diff --git a/Build.PL b/Build.PL index 4c7fa412af..98920731c6 100644 --- a/Build.PL +++ b/Build.PL @@ -30,20 +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; - } -} my @missing_prereqs = (); if ($ENV{SLIC3R_NO_AUTO}) { @@ -129,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..fbad4ca239 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,15 @@ 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) if (MSVC AND SLIC3R_MSVC_COMPILE_PARALLEL) add_compile_options(/MP) 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 +39,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) @@ -75,26 +70,174 @@ if(WIN32) endif() endif() -add_subdirectory(xs) +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 -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. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -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. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=return-type" ) +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) +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) +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 () 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/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/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..b02575617e --- /dev/null +++ b/deps/CMakeLists.txt @@ -0,0 +1,55 @@ +# +# 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 +) + +# 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..a9ae1ea6a9 --- /dev/null +++ b/deps/deps-unix-static.cmake @@ -0,0 +1,168 @@ + +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/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index c7d4260d26..d2a7a23b11 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -41,6 +41,7 @@ our $PROCESS_COMPLETED_EVENT = Wx::NewEventType; my $PreventListEvents = 0; our $appController; +# XXX: VK: done, except callback handling and timer sub new { my ($class, $parent, %params) = @_; my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); @@ -593,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); @@ -812,12 +814,14 @@ sub new { 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) = @_; @@ -854,6 +858,7 @@ 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); @@ -872,11 +877,13 @@ sub on_layer_editing_toggled { $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 @@ -888,6 +895,7 @@ sub update_ui_from_settings } } +# 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 @@ -933,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) = @_; @@ -960,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]; @@ -996,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 @@ -1029,6 +1039,7 @@ sub load_files { return @obj_idx; } +# XXX: VK: done, except a few todos sub load_model_objects { my ($self, @model_objects) = @_; @@ -1111,6 +1122,7 @@ sub load_model_objects { return @obj_idx; } +# XXX: Removed, replaced with bed_shape_bb() sub bed_centerf { my ($self) = @_; @@ -1119,6 +1131,7 @@ sub bed_centerf { return Slic3r::Pointf->new(unscale($bed_center->x), unscale($bed_center->y)); #) } +# XXX: VK: done sub remove { my ($self, $obj_idx) = @_; @@ -1145,6 +1158,7 @@ sub remove { $self->update; } +# XXX: VK: done sub reset { my ($self) = @_; @@ -1165,6 +1179,7 @@ sub reset { $self->update; } +# XXX: VK: done sub increase { my ($self, $copies) = @_; $copies //= 1; @@ -1196,6 +1211,7 @@ sub increase { $self->schedule_background_process; } +# XXX: VK: done sub decrease { my ($self, $copies_asked) = @_; my $copies = $copies_asked // 1; @@ -1223,6 +1239,7 @@ sub decrease { $self->update; } +# XXX: VK: done sub set_number_of_copies { my ($self) = @_; # get current number of copies @@ -1241,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 (;;) { @@ -1263,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 @@ -1333,6 +1352,7 @@ sub rotate { $self->update; } +# XXX: not done sub mirror { my ($self, $axis) = @_; @@ -1362,6 +1382,7 @@ sub mirror { $self->update; } +# XXX: not done, renamed as Plater::priv::scale() sub changescale { my ($self, $axis, $tosize) = @_; @@ -1436,6 +1457,7 @@ sub changescale { $self->update; } +# XXX: VK: WIP sub arrange { my ($self) = @_; @@ -1453,6 +1475,7 @@ sub arrange { $self->update(0); } +# XXX: not done sub split_object { my $self = shift; @@ -1484,6 +1507,7 @@ sub split_object { } } +# 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 { @@ -1491,6 +1515,7 @@ sub schedule_background_process { $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 { @@ -1525,6 +1550,7 @@ sub async_apply_config { } } +# 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) = @_; @@ -1545,6 +1571,7 @@ sub start_background_process { $self->{background_slicing_process}->start; } +# XXX: not done # Stop the background processing sub stop_background_process { my ($self) = @_; @@ -1553,6 +1580,7 @@ sub stop_background_process { # $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. @@ -1573,6 +1601,7 @@ sub reslice { } } +# XXX: VK: done sub export_gcode { my ($self, $output_file) = @_; @@ -1653,6 +1682,7 @@ sub export_gcode { return $self->{export_gcode_output_file}; } +# XXX: not done # This message should be called by the background process synchronously. sub on_update_print_preview { my ($self) = @_; @@ -1665,6 +1695,7 @@ sub on_update_print_preview { Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1); } +# XXX: not done # This gets called also if we have no threads. sub on_progress_event { my ($self, $percent, $message) = @_; @@ -1675,6 +1706,7 @@ sub on_progress_event { $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_process_completed { @@ -1742,6 +1774,7 @@ sub on_process_completed { # $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) = @_; @@ -1818,6 +1851,7 @@ sub print_info_box_show { $panel->Refresh; } +# XXX: not done - to be removed sub do_print { my ($self) = @_; @@ -1831,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}}; @@ -1841,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) = @_; @@ -1872,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; @@ -1883,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; @@ -1911,6 +1949,7 @@ sub fix_through_netfabb { $self->remove($obj_idx); } +# XXX: VK: done sub export_amf { my ($self) = @_; return if !@{$self->{objects}}; @@ -1927,6 +1966,7 @@ sub export_amf { } } +# XXX: VK: done sub export_3mf { my ($self) = @_; return if !@{$self->{objects}}; @@ -1943,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 { @@ -1992,6 +2033,7 @@ 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 { @@ -2015,6 +2057,7 @@ sub update { $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" @@ -2033,6 +2076,7 @@ sub show_preset_comboboxes{ $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. @@ -2077,6 +2121,7 @@ sub on_extruders_change { $self->Layout; } +# XXX: not done sub on_config_change { my ($self, $config) = @_; @@ -2143,6 +2188,7 @@ sub on_config_change { $self->schedule_background_process; } +# XXX: YS: WIP sub item_changed_selection { my ($self, $obj_idx) = @_; @@ -2158,6 +2204,7 @@ sub item_changed_selection { } } +# XXX: VK: done sub collect_selections { my ($self) = @_; my $selections = []; @@ -2167,6 +2214,7 @@ sub collect_selections { return $selections; } +# 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 @@ -2220,6 +2268,7 @@ sub filament_color_box_lmouse_down # } #} +# XXX: YS: done sub changed_object_settings { my ($self, $obj_idx, $parts_changed, $part_settings_changed) = @_; @@ -2245,6 +2294,7 @@ sub changed_object_settings { } } +# 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 { @@ -2275,6 +2325,7 @@ 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) = @_; @@ -2392,6 +2443,7 @@ sub selection_changed { $self->{right_panel}->Thaw; } +# XXX: VK: done sub select_object { my ($self, $obj_idx, $child) = @_; @@ -2412,6 +2464,7 @@ sub select_object { $self->selection_changed(1); } +# XXX: YS: WIP sub select_object_from_cpp { my ($self, $obj_idx, $vol_idx) = @_; @@ -2456,16 +2509,19 @@ sub select_object_from_cpp { $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) = @_; @@ -2576,6 +2632,7 @@ sub object_menu { return $menu; } +# XXX: not done # Set a camera direction, zoom to all objects. sub select_view { my ($self, $direction) = @_; @@ -2593,6 +2650,8 @@ sub select_view { } } + +# XXX: VK: done, in PlaterDropTarget package Slic3r::GUI::Plater::DropTarget; use Wx::DND; use base 'Wx::FileDropTarget'; 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/serial.txt b/serial.txt deleted file mode 100644 index 7c6816d430..0000000000 --- a/serial.txt +++ /dev/null @@ -1,642 +0,0 @@ -<< start -<< echo: 3.1.1-RC5-150z -<< echo: Last Updated: Feb 7 2018 15:28:23 | Author: (none, default config) -<< Compiled: Feb 7 2018 -<< echo: Free Memory: 1777 PlannerBufferBytes: 1312 -<< echo:Hardcoded Default Settings Loaded -<< adc_init ->> N0 M105*39 -<< CrashDetect ENABLED! -<< tmc2130_init(), mode=NORMAL -<< PAT9125_init:1 -<< FSensor -<< ENABLED -<< echo:SD card ok -<< echo:busy: processing -<< Error:Line Number is not Last Line Number+1, Last Line: 0 -<< Resend: 1 -<< ok ->> N1 M107*36 -<< ok ->> N2 M115 U3.1.1-RC5*107 -<< ok ->> N3 M201 X1000 Y1000 Z200 E5000*10 -<< ok ->> N4 M203 X200 Y200 Z12 E120*8 -<< ok ->> N5 M204 S1250 T1250*39 -<< ok ->> N6 M205 X10 Y10 Z0.4 E2.5*63 -<< ok ->> N7 M205 S0 T0*36 -<< ok ->> N8 M83*16 -<< ok ->> N9 M104 S215*106 -<< ok ->> N10 M140 S60*98 -<< ok ->> N11 M190 S60*110 -<< T:158.08 E:0 B:57.1 -<< T:157.04 E:0 B:57.1 -<< T:156.77 E:0 B:56.9 -<< T:156.97 E:0 B:57.0 -<< T:158.14 E:0 B:57.0 -<< T:159.62 E:0 B:56.9 -<< T:161.25 E:0 B:56.8 -<< T:163.64 E:0 B:56.8 -<< T:165.94 E:0 B:56.7 -<< T:168.40 E:0 B:56.8 -<< T:170.79 E:0 B:56.7 -<< T:173.68 E:0 B:56.7 -<< T:175.53 E:0 B:56.6 -<< T:178.40 E:0 B:56.6 -<< T:180.94 E:0 B:56.5 -<< T:183.92 E:0 B:56.4 -<< T:186.73 E:0 B:56.4 -<< T:189.20 E:0 B:56.4 -<< T:191.32 E:0 B:56.3 -<< T:193.91 E:0 B:56.3 -<< T:196.38 E:0 B:56.2 -<< T:198.75 E:0 B:56.2 -<< T:201.65 E:0 B:56.3 -<< T:203.57 E:0 B:56.4 -<< T:206.38 E:0 B:56.5 -<< T:208.71 E:0 B:56.6 -<< T:211.04 E:0 B:56.6 -<< T:212.86 E:0 B:56.8 -<< T:214.84 E:0 B:57.0 -<< T:215.52 E:0 B:57.2 -<< T:215.78 E:0 B:57.4 -<< T:216.30 E:0 B:57.6 -<< T:216.51 E:0 B:57.7 -<< T:215.73 E:0 B:58.0 -<< T:215.47 E:0 B:58.2 -<< T:214.95 E:0 B:58.5 -<< T:214.22 E:0 B:58.7 -<< T:213.65 E:0 B:59.0 -<< T:212.24 E:0 B:59.2 -<< T:212.14 E:0 B:59.4 -<< T:212.03 E:0 B:59.7 -<< T:211.51 E:0 B:59.8 -<< ok ->> N12 M105*20 -<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2 ->> N13 M105*21 -<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2 ->> N14 M105*18 -<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2 ->> N15 M105*19 -<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2 ->> N16 M105*16 -<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2 ->> N17 M105*17 -<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2 ->> N18 M105*30 -<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2 ->> N19 M105*31 -<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2 ->> N20 M105*21 -<< ok T:211.0 /215.0 B:60.0 /60.0 T0:211.0 /215.0 @:60 B@:0 P:46.3 A:36.2 ->> N21 M109 S215*93 -<< T:211.3 E:0 W:? -<< T:211.8 E:0 W:? -<< T:211.8 E:0 W:? -<< T:212.1 E:0 W:? -<< T:212.4 E:0 W:? -<< T:213.3 E:0 W:? -<< T:213.3 E:0 W:? -<< T:213.8 E:0 W:? -<< T:214.1 E:0 W:2 -<< T:214.1 E:0 W:1 -<< T:214.2 E:0 W:0 -<< ok ->> N22 M105*23 -<< ok T:214.3 /215.0 B:60.8 /60.0 T0:214.3 /215.0 @:20 B@:7 P:46.4 A:36.0 ->> N23 M105*22 -<< ok T:214.3 /215.0 B:60.8 /60.0 T0:214.3 /215.0 @:20 B@:7 P:46.4 A:36.0 ->> N24 G28 W*82 -<< 0 step=62 mscnt= 993 -<< tmc2130_goto_step 0 0 2 1000 -<< step 61 mscnt = 984 -<< dir=0 steps=-61 -<< dir=1 steps=61 -<< dir=0 steps=3 -<< cnt 2 step 61 mscnt = 986 -<< cnt 1 step 62 mscnt = 1005 -<< cnt 0 step 63 mscnt = 1021 -<< echo:busy: processing -<< echo:busy: processing -<< 0 step=34 mscnt= 547 -<< tmc2130_goto_step 1 0 2 1000 -<< step 34 mscnt = 552 -<< dir=1 steps=-34 -<< dir=0 steps=34 -<< dir=1 steps=30 -<< cnt 29 step 34 mscnt = 554 -<< cnt 28 step 35 mscnt = 572 -<< cnt 27 step 36 mscnt = 588 -<< cnt 26 step 37 mscnt = 604 -<< cnt 25 step 38 mscnt = 620 -<< cnt 24 step 39 mscnt = 637 -<< cnt 23 step 40 mscnt = 653 -<< cnt 22 step 41 mscnt = 668 -<< cnt 21 step 42 mscnt = 684 -<< cnt 20 step 43 mscnt = 701 -<< cnt 19 step 44 mscnt = 717 -<< cnt 18 step 45 mscnt = 733 -<< cnt 17 step 46 mscnt = 748 -<< cnt 16 step 47 mscnt = 765 -<< cnt 15 step 48 mscnt = 780 -<< cnt 14 step 49 mscnt = 796 -<< cnt 13 step 50 mscnt = 812 -<< cnt 12 step 51 mscnt = 828 -<< cnt 11 step 52 mscnt = 844 -<< cnt 10 step 53 mscnt = 860 -<< cnt 9 step 54 mscnt = 876 -<< cnt 8 step 55 mscnt = 893 -<< cnt 7 step 56 mscnt = 909 -<< cnt 6 step 57 mscnt = 925 -<< cnt 5 step 58 mscnt = 941 -<< cnt 4 step 59 mscnt = 956 -<< cnt 3 step 60 mscnt = 972 -<< cnt 2 step 61 mscnt = 988 -<< cnt 1 step 62 mscnt = 1005 -<< cnt 0 step 63 mscnt = 1021 -<< echo:busy: processing -<< ok ->> N25 M105*16 -<< ok T:213.1 /215.0 B:60.8 /60.0 T0:213.1 /215.0 @:44 B@:35 P:46.5 A:35.6 ->> N26 G80*37 -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< ok ->> N27 M105*18 -<< ok T:214.8 /215.0 B:60.9 /60.0 T0:214.8 /215.0 @:31 B@:25 P:46.3 A:35.7 ->> N28 M105*29 -<< ok T:214.8 /215.0 B:60.9 /60.0 T0:214.8 /215.0 @:31 B@:25 P:46.3 A:35.7 ->> N29 M105*28 -<< ok T:214.8 /215.0 B:60.9 /60.0 T0:214.8 /215.0 @:31 B@:25 P:46.3 A:35.7 ->> N30 M105*20 -<< ok T:214.8 /215.0 B:60.9 /60.0 T0:214.8 /215.0 @:31 B@:25 P:46.3 A:35.7 ->> N31 M105*21 -<< ok T:214.8 /215.0 B:60.9 /60.0 T0:214.8 /215.0 @:31 B@:25 P:46.3 A:35.7 ->> N32 M105*22 -<< ok T:214.8 /215.0 B:60.9 /60.0 T0:214.8 /215.0 @:31 B@:25 P:46.3 A:35.7 ->> N33 G1 Y-3.0 F1000.0*24 -<< ok ->> N34 G92 E0.0*110 -<< ok ->> N35 G1 X60.0 E9.0 F1000.0*101 -<< ok ->> N36 G1 X100.0 E12.5 F1000.0*110 -<< ok ->> N37 G92 E0.0*109 -<< ok ->> N38 M221 S95*102 -<< ok ->> N39 M900 K30*120 -<< Invalid M code. -<< ok ->> N40 G21*46 -<< ok ->> N41 G90*37 -<< ok ->> N42 M83*46 -<< ok ->> N43 G1 E-0.80000 F2100.00000*10 -<< ok ->> N44 G1 Z0.600 F10200.000*1 -<< ok ->> N45 G1 X112.437 Y93.991 F10200.000*106 -<< ok ->> N46 G1 Z0.200 F10200.000*7 -<< ok ->> N47 G1 E0.80000 F2100.00000*35 -<< ok ->> N48 M204 S1000*107 -<< ok ->> N49 G1 F1800*122 -<< ok ->> N50 G1 X112.930 Y93.183 E0.02968*106 -<< ok ->> N51 G1 X113.335 Y92.806 E0.01733*99 -<< ok ->> N52 G1 X113.810 Y92.516 E0.01745*97 -<< ok ->> N53 G1 X114.334 Y92.328 E0.01745*97 -<< ok ->> N54 G1 X114.885 Y92.248 E0.01745*96 -<< ok ->> N55 M105*23 -<< ok T:214.9 /215.0 B:60.8 /60.0 T0:214.9 /215.0 @:30 B@:28 P:46.2 A:35.7 ->> N56 G1 X135.004 Y92.246 E0.63084*96 -<< ok ->> N57 G1 X136.005 Y92.436 E0.03195*101 -<< ok ->> N58 G1 X136.866 Y92.974 E0.03183*107 -<< ok ->> N59 G1 X137.473 Y93.788 E0.03183*111 -<< ok ->> N60 G1 X137.745 Y94.770 E0.03195*100 -<< ok ->> N61 G1 X137.753 Y115.086 E0.63700*88 -<< ok ->> N62 G1 X137.563 Y116.009 E0.02955*87 -<< ok ->> N63 G1 X137.070 Y116.817 E0.02968*88 -<< ok ->> N64 G1 X136.646 Y117.208 E0.01809*93 -<< ok ->> N65 G1 X136.149 Y117.503 E0.01809*88 -<< ok ->> N66 G1 X135.603 Y117.687 E0.01809*94 -<< ok ->> N67 G1 X135.029 Y117.754 E0.01809*94 -<< ok ->> N68 G1 X114.914 Y117.753 E0.63071*81 -<< ok ->> N69 G1 X113.991 Y117.563 E0.02955*83 -<< ok ->> N70 G1 X113.183 Y117.070 E0.02968*89 -<< ok ->> N71 G1 X112.792 Y116.646 E0.01809*88 -<< ok ->> N72 M105*18 -<< ok T:214.0 /215.0 B:60.8 /60.0 T0:214.0 /215.0 @:45 B@:16 P:46.2 A:35.5 ->> N73 G1 X112.497 Y116.149 E0.01809*84 -<< ok ->> N74 G1 X112.313 Y115.603 E0.01809*82 -<< ok ->> N75 G1 X112.246 Y115.029 E0.01809*92 -<< ok ->> N76 G1 X112.247 Y94.914 E0.63071*98 -<< ok ->> N77 G1 X112.425 Y94.050 E0.02767*111 -<< ok ->> N78 G1 F8160*126 -<< ok ->> N79 G1 X112.930 Y93.183 E-0.24526*78 -<< ok ->> N80 G1 F8160*121 -<< ok ->> N81 G1 X113.335 Y92.806 E-0.13510*67 -<< ok ->> N82 G1 F8160*123 -<< ok ->> N83 G1 X113.810 Y92.516 E-0.13609*74 -<< ok ->> N84 G1 F8160*125 -<< ok ->> N85 G1 X114.334 Y92.328 E-0.13609*77 -<< ok ->> N86 G1 F8160*127 -<< ok ->> N87 G1 X114.769 Y92.265 E-0.10746*66 -<< ok ->> N88 G1 E-0.04000 F2100.00000*1 -<< ok ->> N89 G1 Z0.800 F10200.000*14 -<< ok ->> N90 G1 X113.989 Y92.849 F10200.000*110 -<< ok ->> N91 G1 Z0.200 F10200.000*13 -<< ok ->> N92 G1 E0.80000 F2100.00000*43 -<< ok ->> N93 G1 F1800*125 -<< ok ->> N94 G1 X114.911 Y92.625 E0.02977*99 -<< ok ->> N95 G1 X135.004 Y92.623 E0.62999*108 -<< ok ->> N96 G1 X135.871 Y92.788 E0.02767*108 -<< ok ->> N97 G1 X136.617 Y93.258 E0.02767*105 -<< ok ->> N98 G1 X137.141 Y93.968 E0.02767*107 -<< ok ->> N99 G1 X137.371 Y94.824 E0.02778*107 -<< ok ->> N100 G1 X137.376 Y115.065 E0.63464*97 -<< ok ->> N101 G1 X137.209 Y115.878 E0.02602*104 -<< ok ->> N102 G1 X136.773 Y116.584 E0.02602*111 -<< ok ->> N103 G1 X136.407 Y116.916 E0.01550*110 -<< ok ->> N104 G1 X135.980 Y117.166 E0.01550*102 -<< ok ->> N105 G1 X135.511 Y117.321 E0.01550*98 -<< ok ->> N106 G1 X135.020 Y117.377 E0.01550*101 -<< ok ->> N107 G1 X114.935 Y117.376 E0.62975*101 -<< ok ->> N108 G1 X114.122 Y117.209 E0.02602*100 -<< ok ->> N109 G1 X113.416 Y116.773 E0.02602*105 -<< ok ->> N110 G1 X113.084 Y116.407 E0.01550*105 -<< ok ->> N111 G1 X112.834 Y115.980 E0.01550*107 -<< ok ->> N112 G1 X112.679 Y115.511 E0.01550*107 -<< ok ->> N113 G1 X112.623 Y115.020 E0.01550*98 -<< ok ->> N114 G1 X112.624 Y94.935 E0.62975*89 -<< ok ->> N115 G1 X112.791 Y94.122 E0.02602*80 -<< ok ->> N116 G1 X113.227 Y93.416 E0.02602*95 -<< ok ->> N117 G1 X113.940 Y92.885 E0.02789*81 -<< ok ->> N118 G1 F8160*73 -<< ok ->> N119 G1 X114.911 Y92.625 E-0.24574*113 -<< ok ->> N120 G1 F8160*66 -<< ok ->> N121 G1 X117.015 Y92.624 E-0.51426*113 -<< ok ->> N122 G1 E-0.04000 F2100.00000*48 -<< ok ->> N123 G1 Z0.800 F10200.000*63 -<< ok ->> N124 G1 X115.587 Y95.587 F10200.000*92 -<< ok ->> N125 M105*33 -<< ok T:214.2 /215.0 B:60.7 /60.0 T0:214.2 /215.0 @:41 B@:24 P:46.2 A:36.0 ->> N126 G1 Z0.200 F10200.000*48 -<< ok ->> N127 G1 E0.80000 F2100.00000*20 -<< ok ->> N128 G1 F1800*76 -<< ok ->> N129 G1 X134.413 Y95.587 E0.59027*87 -<< ok ->> N130 G1 X134.413 Y114.413 E0.59027*107 -<< ok ->> N131 G1 X115.587 Y114.413 E0.59027*101 -<< ok ->> N132 G1 X115.587 Y95.647 E0.58839*91 -<< ok ->> N133 G1 X115.210 Y95.210 F10200.000*90 -<< ok ->> N134 G1 F1800*65 -<< ok ->> N135 G1 X134.790 Y95.210 E0.61392*93 -<< ok ->> N136 G1 X134.790 Y114.790 E0.61392*107 -<< ok ->> N137 G1 X115.210 Y114.790 E0.61392*100 -<< ok ->> N138 G1 X115.210 Y95.270 E0.61204*86 -<< ok ->> N139 G1 X115.596 Y95.314 F10200.000*92 -<< ok ->> N140 G1 F8160*68 -<< ok ->> N141 G1 X118.319 Y95.260 E-0.76000*113 -<< ok ->> N142 G1 E-0.04000 F2100.00000*54 -<< ok ->> N143 G1 Z0.800 F10200.000*57 -<< ok ->> N144 G1 X115.700 Y113.527 F10200.000*98 -<< ok ->> N145 G1 Z0.200 F10200.000*53 -<< ok ->> N146 G1 E0.80000 F2100.00000*19 -<< ok ->> N147 G1 F1800*69 -<< ok ->> N148 G1 X116.303 Y114.130 E0.02708*98 -<< ok ->> N149 G1 X116.843 Y114.130 E0.01716*96 -<< ok ->> N150 G1 X115.870 Y113.157 E0.04372*110 -<< ok ->> N151 G1 X115.870 Y112.617 E0.01716*110 -<< ok ->> N152 G1 X117.383 Y114.130 E0.06799*108 -<< ok ->> N153 G1 X117.924 Y114.130 E0.01716*106 -<< ok ->> N154 M105*39 -<< ok T:215.1 /215.0 B:60.7 /60.0 T0:215.1 /215.0 @:29 B@:12 P:46.2 A:35.7 ->> N155 G1 X115.870 Y112.076 E0.09225*102 -<< ok ->> N156 G1 X115.870 Y111.536 E0.01716*106 -<< ok ->> N157 G1 X118.464 Y114.130 E0.11652*104 -<< ok ->> N158 G1 X119.004 Y114.130 E0.01716*100 -<< ok ->> N159 G1 X115.870 Y110.996 E0.14079*104 -<< ok ->> N160 G1 X115.870 Y110.456 E0.01716*105 -<< ok ->> N161 G1 X119.544 Y114.130 E0.16505*105 -<< ok ->> N162 G1 X120.085 Y114.130 E0.01716*110 -<< ok ->> N163 G1 X115.870 Y109.915 E0.18932*104 -<< ok ->> N164 G1 X115.870 Y109.375 E0.01716*99 -<< ok ->> N165 G1 X120.625 Y114.130 E0.21358*105 -<< ok ->> N166 G1 X121.165 Y114.130 E0.01716*100 -<< ok ->> N167 G1 X115.870 Y108.835 E0.23785*100 -<< ok ->> N168 G1 X115.870 Y108.295 E0.01716*97 -<< ok ->> N169 G1 X121.705 Y114.130 E0.26212*111 -<< ok ->> N170 G1 X122.245 Y114.130 E0.01716*97 -<< ok ->> N171 G1 X115.870 Y107.755 E0.28638*105 -<< ok ->> N172 G1 X115.870 Y107.214 E0.01716*108 -<< ok ->> N173 G1 X122.786 Y114.130 E0.31065*104 -<< ok ->> N174 G1 X123.326 Y114.130 E0.01716*96 -<< ok ->> N175 G1 X115.870 Y106.674 E0.33491*101 -<< ok ->> N176 G1 X115.870 Y106.134 E0.01716*104 -<< ok ->> N177 G1 X123.866 Y114.130 E0.35918*107 -<< ok ->> N178 G1 X124.406 Y114.130 E0.01716*110 -<< ok ->> N179 G1 X115.870 Y105.594 E0.38344*99 -<< ok ->> N180 G1 X115.870 Y105.054 E0.01716*101 -<< ok ->> N181 G1 X124.946 Y114.130 E0.40771*101 -<< ok ->> N182 G1 X125.487 Y114.130 E0.01716*99 -<< ok ->> N183 G1 X115.870 Y104.513 E0.43198*103 -<< ok ->> N184 G1 X115.870 Y103.973 E0.01716*107 -<< ok ->> N185 G1 X126.027 Y114.130 E0.45624*105 -<< ok ->> N186 G1 X126.567 Y114.130 E0.01716*107 -<< ok ->> N187 G1 X115.870 Y103.433 E0.48051*104 -<< ok ->> N188 G1 X115.870 Y102.893 E0.01716*105 -<< ok ->> N189 G1 X127.107 Y114.130 E0.50477*103 -<< ok ->> N190 M105*47 -<< ok T:215.3 /215.0 B:60.7 /60.0 T0:215.3 /215.0 @:28 B@:18 P:46.3 A:35.5 ->> N191 G1 X127.648 Y114.130 E0.01716*98 -<< ok ->> N192 G1 X115.870 Y102.352 E0.52904*111 -<< ok ->> N193 G1 X115.870 Y101.812 E0.01716*105 -<< ok ->> N194 G1 X128.188 Y114.130 E0.55330*98 -<< ok ->> N195 G1 X128.728 Y114.130 E0.01716*110 -<< ok ->> N196 G1 X115.870 Y101.272 E0.57757*102 -<< ok ->> N197 G1 X115.870 Y100.732 E0.01716*97 -<< ok ->> N198 G1 X129.268 Y114.130 E0.60184*105 -<< ok ->> N199 G1 X129.808 Y114.130 E0.01716*110 -<< ok ->> N200 G1 X115.870 Y100.192 E0.62610*98 -<< ok ->> N201 G1 X115.870 Y99.651 E0.01716*88 -<< ok ->> N202 G1 X130.349 Y114.130 E0.65037*111 -<< ok ->> N203 G1 X130.889 Y114.130 E0.01716*111 -<< ok ->> N204 G1 X115.870 Y99.111 E0.67463*95 -<< ok ->> N205 G1 X115.870 Y98.571 E0.01716*92 -<< ok ->> N206 G1 X131.429 Y114.130 E0.69890*98 -<< ok ->> N207 G1 X131.969 Y114.130 E0.01716*101 -<< ok ->> N208 M105*45 -<< ok T:214.7 /215.0 B:60.6 /60.0 T0:214.7 /215.0 @:38 B@:12 P:46.2 A:35.9 ->> N209 G1 X115.870 Y98.031 E0.72316*81 -<< ok ->> N210 G1 X115.870 Y97.491 E0.01716*88 -<< ok ->> N211 G1 X132.509 Y114.130 E0.74743*105 -<< ok ->> N212 G1 X133.050 Y114.130 E0.01716*96 -<< ok ->> N213 M105*39 -<< ok T:215.1 /215.0 B:60.4 /60.0 T0:215.1 /215.0 @:32 B@:39 P:46.3 A:35.8 ->> N214 M105*32 -<< ok T:214.6 /215.0 B:60.5 /60.0 T0:214.6 /215.0 @:39 B@:14 P:46.3 A:36.3 ->> N215 G28*21 -<< echo:busy: processing -<< 0 step=61 mscnt= 989 -<< tmc2130_goto_step 0 0 2 1000 -<< step 61 mscnt = 984 -<< dir=0 steps=-61 -<< dir=1 steps=61 -<< dir=0 steps=3 -<< cnt 2 step 61 mscnt = 986 -<< cnt 1 step 62 mscnt = 1004 -<< cnt 0 step 63 mscnt = 1021 -<< echo:busy: processing -<< echo:busy: processing -<< 0 step=34 mscnt= 547 -<< tmc2130_goto_step 1 0 2 1000 -<< step 34 mscnt = 552 -<< dir=1 steps=-34 -<< dir=0 steps=34 -<< dir=1 steps=30 -<< cnt 29 step 34 mscnt = 554 -<< cnt 28 step 35 mscnt = 573 -<< cnt 27 step 36 mscnt = 589 -<< cnt 26 step 37 mscnt = 604 -<< cnt 25 step 38 mscnt = 621 -<< cnt 24 step 39 mscnt = 636 -<< cnt 23 step 40 mscnt = 652 -<< cnt 22 step 41 mscnt = 668 -<< cnt 21 step 42 mscnt = 684 -<< cnt 20 step 43 mscnt = 701 -<< cnt 19 step 44 mscnt = 717 -<< cnt 18 step 45 mscnt = 733 -<< cnt 17 step 46 mscnt = 749 -<< cnt 16 step 47 mscnt = 765 -<< cnt 15 step 48 mscnt = 781 -<< cnt 14 step 49 mscnt = 796 -<< cnt 13 step 50 mscnt = 812 -<< cnt 12 step 51 mscnt = 829 -<< cnt 11 step 52 mscnt = 844 -<< cnt 10 step 53 mscnt = 860 -<< cnt 9 step 54 mscnt = 876 -<< cnt 8 step 55 mscnt = 893 -<< cnt 7 step 56 mscnt = 909 -<< cnt 6 step 57 mscnt = 925 -<< cnt 5 step 58 mscnt = 941 -<< cnt 4 step 59 mscnt = 957 -<< cnt 3 step 60 mscnt = 973 -<< cnt 2 step 61 mscnt = 989 -<< cnt 1 step 62 mscnt = 1005 -<< cnt 0 step 63 mscnt = 1021 -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< echo:busy: processing -<< ok ->> N216 M105*34 -<< ok T:214.8 /215.0 B:60.2 /60.0 T0:214.8 /215.0 @:33 B@:27 P:46.0 A:36.1 ->> N217 M105*35 -<< ok T:214.8 /215.0 B:60.2 /60.0 T0:214.8 /215.0 @:33 B@:27 P:46.0 A:36.1 ->> N218 M105*44 -<< ok T:214.9 /215.0 B:60.1 /60.0 T0:214.9 /215.0 @:31 B@:33 P:45.9 A:36.1 ->> N219 M105*45 -<< ok T:214.9 /215.0 B:60.1 /60.0 T0:214.9 /215.0 @:31 B@:33 P:45.9 A:36.1 ->> N220 M105*39 -<< ok T:214.9 /215.0 B:60.1 /60.0 T0:214.9 /215.0 @:31 B@:33 P:45.9 A:36.1 ->> N221 M105*38 -<< ok T:214.9 /215.0 B:60.1 /60.0 T0:214.9 /215.0 @:31 B@:33 P:45.9 A:36.1 ->> N222 M105*37 -<< ok T:214.9 /215.0 B:60.1 /60.0 T0:214.9 /215.0 @:31 B@:33 P:45.9 A:36.1 ->> N223 M105*36 -<< ok T:214.5 /215.0 B:60.1 /60.0 T0:214.5 /215.0 @:38 B@:31 P:46.0 A:36.3 -DISCONNECTED diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000000..dbc4ecd214 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,146 @@ +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) +add_executable(slic3r slic3r.cpp ${CMAKE_CURRENT_BINARY_DIR}/slic3r.rc) +if(SLIC3R_GUI) + set_target_properties(slic3r PROPERTIES OUTPUT_NAME "slic3r-gui") +else() + set_target_properties(slic3r PROPERTIES OUTPUT_NAME "slic3r-console") +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 /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 () + +# 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 96% rename from xs/src/admesh/connect.cpp rename to src/admesh/connect.cpp index da5b667209..166eec3302 100644 --- a/xs/src/admesh/connect.cpp +++ b/src/admesh/connect.cpp @@ -25,6 +25,9 @@ #include #include +#include +#include + #include #include "stl.h" @@ -125,11 +128,11 @@ stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, 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[sizeof(stl_vertex)], b->data(), sizeof(stl_vertex)); + 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 = edge->key + i * 4; + 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. @@ -142,6 +145,16 @@ stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, } } +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 stl_initialize_facet_check_exact(stl_file *stl) { int i; @@ -152,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; @@ -296,11 +308,11 @@ static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, stl_vertex * ((vertex1[1] != vertex2[1]) ? (vertex1[1] < vertex2[1]) : (vertex1[2] < vertex2[2]))) { - memcpy(&edge->key[0], vertex1.data(), sizeof(stl_vertex)); - memcpy(&edge->key[sizeof(stl_vertex)], vertex2.data(), 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.data(), sizeof(stl_vertex)); - memcpy(&edge->key[sizeof(stl_vertex)], vertex1.data(), 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; @@ -338,7 +350,7 @@ static void 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"); diff --git a/xs/src/admesh/normals.cpp b/src/admesh/normals.cpp similarity index 100% rename from xs/src/admesh/normals.cpp rename to src/admesh/normals.cpp diff --git a/xs/src/admesh/shared.cpp b/src/admesh/shared.cpp similarity index 100% rename from xs/src/admesh/shared.cpp rename to src/admesh/shared.cpp diff --git a/xs/src/admesh/stl.h b/src/admesh/stl.h similarity index 98% rename from xs/src/admesh/stl.h rename to src/admesh/stl.h index 096430d150..9898f3e643 100644 --- a/xs/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -65,11 +65,11 @@ typedef struct { typedef struct stl_hash_edge { // Key of a hash edge: sorted vertices of the edge. - unsigned char key[2 * sizeof(stl_vertex)]; + 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] / 23 + key[1] / 19 + key[2] / 17 + key[3] /13 + key[4] / 11 + key[5] / 7 ) % M); } + 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. diff --git a/xs/src/admesh/stl_io.cpp b/src/admesh/stl_io.cpp similarity index 100% rename from xs/src/admesh/stl_io.cpp rename to src/admesh/stl_io.cpp diff --git a/xs/src/admesh/stlinit.cpp b/src/admesh/stlinit.cpp similarity index 100% rename from xs/src/admesh/stlinit.cpp rename to src/admesh/stlinit.cpp diff --git a/xs/src/admesh/util.cpp b/src/admesh/util.cpp similarity index 100% rename from xs/src/admesh/util.cpp rename to src/admesh/util.cpp diff --git a/xs/src/agg/AUTHORS b/src/agg/AUTHORS similarity index 100% rename from xs/src/agg/AUTHORS rename to src/agg/AUTHORS diff --git a/xs/src/agg/VERSION b/src/agg/VERSION similarity index 100% rename from xs/src/agg/VERSION rename to src/agg/VERSION diff --git a/xs/src/agg/agg_array.h b/src/agg/agg_array.h similarity index 100% rename from xs/src/agg/agg_array.h rename to src/agg/agg_array.h diff --git a/xs/src/agg/agg_basics.h b/src/agg/agg_basics.h similarity index 100% rename from xs/src/agg/agg_basics.h rename to src/agg/agg_basics.h diff --git a/xs/src/agg/agg_bezier_arc.h b/src/agg/agg_bezier_arc.h similarity index 100% rename from xs/src/agg/agg_bezier_arc.h rename to src/agg/agg_bezier_arc.h diff --git a/xs/src/agg/agg_clip_liang_barsky.h b/src/agg/agg_clip_liang_barsky.h similarity index 100% rename from xs/src/agg/agg_clip_liang_barsky.h rename to src/agg/agg_clip_liang_barsky.h diff --git a/xs/src/agg/agg_color_gray.h b/src/agg/agg_color_gray.h similarity index 100% rename from xs/src/agg/agg_color_gray.h rename to src/agg/agg_color_gray.h diff --git a/xs/src/agg/agg_color_rgba.h b/src/agg/agg_color_rgba.h similarity index 100% rename from xs/src/agg/agg_color_rgba.h rename to src/agg/agg_color_rgba.h diff --git a/xs/src/agg/agg_config.h b/src/agg/agg_config.h similarity index 100% rename from xs/src/agg/agg_config.h rename to src/agg/agg_config.h diff --git a/xs/src/agg/agg_conv_transform.h b/src/agg/agg_conv_transform.h similarity index 100% rename from xs/src/agg/agg_conv_transform.h rename to src/agg/agg_conv_transform.h diff --git a/xs/src/agg/agg_gamma_functions.h b/src/agg/agg_gamma_functions.h similarity index 100% rename from xs/src/agg/agg_gamma_functions.h rename to src/agg/agg_gamma_functions.h diff --git a/xs/src/agg/agg_gamma_lut.h b/src/agg/agg_gamma_lut.h similarity index 100% rename from xs/src/agg/agg_gamma_lut.h rename to src/agg/agg_gamma_lut.h diff --git a/xs/src/agg/agg_math.h b/src/agg/agg_math.h similarity index 100% rename from xs/src/agg/agg_math.h rename to src/agg/agg_math.h diff --git a/xs/src/agg/agg_path_storage.h b/src/agg/agg_path_storage.h similarity index 100% rename from xs/src/agg/agg_path_storage.h rename to src/agg/agg_path_storage.h diff --git a/xs/src/agg/agg_pixfmt_base.h b/src/agg/agg_pixfmt_base.h similarity index 100% rename from xs/src/agg/agg_pixfmt_base.h rename to src/agg/agg_pixfmt_base.h diff --git a/xs/src/agg/agg_pixfmt_gray.h b/src/agg/agg_pixfmt_gray.h similarity index 100% rename from xs/src/agg/agg_pixfmt_gray.h rename to src/agg/agg_pixfmt_gray.h diff --git a/xs/src/agg/agg_pixfmt_rgb.h b/src/agg/agg_pixfmt_rgb.h similarity index 100% rename from xs/src/agg/agg_pixfmt_rgb.h rename to src/agg/agg_pixfmt_rgb.h diff --git a/xs/src/agg/agg_rasterizer_cells_aa.h b/src/agg/agg_rasterizer_cells_aa.h similarity index 100% rename from xs/src/agg/agg_rasterizer_cells_aa.h rename to src/agg/agg_rasterizer_cells_aa.h diff --git a/xs/src/agg/agg_rasterizer_scanline_aa.h b/src/agg/agg_rasterizer_scanline_aa.h similarity index 100% rename from xs/src/agg/agg_rasterizer_scanline_aa.h rename to src/agg/agg_rasterizer_scanline_aa.h diff --git a/xs/src/agg/agg_rasterizer_scanline_aa_nogamma.h b/src/agg/agg_rasterizer_scanline_aa_nogamma.h similarity index 100% rename from xs/src/agg/agg_rasterizer_scanline_aa_nogamma.h rename to src/agg/agg_rasterizer_scanline_aa_nogamma.h diff --git a/xs/src/agg/agg_rasterizer_sl_clip.h b/src/agg/agg_rasterizer_sl_clip.h similarity index 100% rename from xs/src/agg/agg_rasterizer_sl_clip.h rename to src/agg/agg_rasterizer_sl_clip.h diff --git a/xs/src/agg/agg_renderer_base.h b/src/agg/agg_renderer_base.h similarity index 100% rename from xs/src/agg/agg_renderer_base.h rename to src/agg/agg_renderer_base.h diff --git a/xs/src/agg/agg_renderer_scanline.h b/src/agg/agg_renderer_scanline.h similarity index 100% rename from xs/src/agg/agg_renderer_scanline.h rename to src/agg/agg_renderer_scanline.h diff --git a/xs/src/agg/agg_rendering_buffer.h b/src/agg/agg_rendering_buffer.h similarity index 100% rename from xs/src/agg/agg_rendering_buffer.h rename to src/agg/agg_rendering_buffer.h diff --git a/xs/src/agg/agg_scanline_p.h b/src/agg/agg_scanline_p.h similarity index 100% rename from xs/src/agg/agg_scanline_p.h rename to src/agg/agg_scanline_p.h diff --git a/xs/src/agg/agg_trans_affine.h b/src/agg/agg_trans_affine.h similarity index 100% rename from xs/src/agg/agg_trans_affine.h rename to src/agg/agg_trans_affine.h diff --git a/xs/src/agg/copying b/src/agg/copying similarity index 100% rename from xs/src/agg/copying rename to src/agg/copying 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 100% rename from xs/src/eigen/Eigen/Cholesky rename to src/eigen/Eigen/Cholesky 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 100% rename from xs/src/eigen/Eigen/Core rename to src/eigen/Eigen/Core 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 100% rename from xs/src/eigen/Eigen/Eigenvalues rename to src/eigen/Eigen/Eigenvalues 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 100% rename from xs/src/eigen/Eigen/LU rename to src/eigen/Eigen/LU 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 100% rename from xs/src/eigen/Eigen/QR rename to src/eigen/Eigen/QR diff --git a/xs/src/eigen/Eigen/QtAlignedMalloc b/src/eigen/Eigen/QtAlignedMalloc similarity index 100% rename from xs/src/eigen/Eigen/QtAlignedMalloc rename to src/eigen/Eigen/QtAlignedMalloc 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 100% rename from xs/src/eigen/Eigen/SVD rename to src/eigen/Eigen/SVD 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 100% rename from xs/src/eigen/Eigen/src/Cholesky/LDLT.h rename to src/eigen/Eigen/src/Cholesky/LDLT.h diff --git a/xs/src/eigen/Eigen/src/Cholesky/LLT.h b/src/eigen/Eigen/src/Cholesky/LLT.h similarity index 100% rename from xs/src/eigen/Eigen/src/Cholesky/LLT.h rename to src/eigen/Eigen/src/Cholesky/LLT.h 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 100% rename from xs/src/eigen/Eigen/src/Core/Array.h rename to src/eigen/Eigen/src/Core/Array.h diff --git a/xs/src/eigen/Eigen/src/Core/ArrayBase.h b/src/eigen/Eigen/src/Core/ArrayBase.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/ArrayBase.h rename to src/eigen/Eigen/src/Core/ArrayBase.h diff --git a/xs/src/eigen/Eigen/src/Core/ArrayWrapper.h b/src/eigen/Eigen/src/Core/ArrayWrapper.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/ArrayWrapper.h rename to src/eigen/Eigen/src/Core/ArrayWrapper.h 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 100% rename from xs/src/eigen/Eigen/src/Core/AssignEvaluator.h rename to src/eigen/Eigen/src/Core/AssignEvaluator.h diff --git a/xs/src/eigen/Eigen/src/Core/Assign_MKL.h b/src/eigen/Eigen/src/Core/Assign_MKL.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Assign_MKL.h rename to src/eigen/Eigen/src/Core/Assign_MKL.h 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 100% rename from xs/src/eigen/Eigen/src/Core/CoreEvaluators.h rename to src/eigen/Eigen/src/Core/CoreEvaluators.h 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 100% rename from xs/src/eigen/Eigen/src/Core/CwiseNullaryOp.h rename to src/eigen/Eigen/src/Core/CwiseNullaryOp.h 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 100% rename from xs/src/eigen/Eigen/src/Core/DenseBase.h rename to src/eigen/Eigen/src/Core/DenseBase.h 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 100% rename from xs/src/eigen/Eigen/src/Core/Diagonal.h rename to src/eigen/Eigen/src/Core/Diagonal.h 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 100% rename from xs/src/eigen/Eigen/src/Core/Dot.h rename to src/eigen/Eigen/src/Core/Dot.h diff --git a/xs/src/eigen/Eigen/src/Core/EigenBase.h b/src/eigen/Eigen/src/Core/EigenBase.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/EigenBase.h rename to src/eigen/Eigen/src/Core/EigenBase.h 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 100% rename from xs/src/eigen/Eigen/src/Core/GeneralProduct.h rename to src/eigen/Eigen/src/Core/GeneralProduct.h diff --git a/xs/src/eigen/Eigen/src/Core/GenericPacketMath.h b/src/eigen/Eigen/src/Core/GenericPacketMath.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/GenericPacketMath.h rename to src/eigen/Eigen/src/Core/GenericPacketMath.h 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 100% rename from xs/src/eigen/Eigen/src/Core/Map.h rename to src/eigen/Eigen/src/Core/Map.h 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 100% rename from xs/src/eigen/Eigen/src/Core/MathFunctions.h rename to src/eigen/Eigen/src/Core/MathFunctions.h diff --git a/xs/src/eigen/Eigen/src/Core/MathFunctionsImpl.h b/src/eigen/Eigen/src/Core/MathFunctionsImpl.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/MathFunctionsImpl.h rename to src/eigen/Eigen/src/Core/MathFunctionsImpl.h 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 100% rename from xs/src/eigen/Eigen/src/Core/MatrixBase.h rename to src/eigen/Eigen/src/Core/MatrixBase.h 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 100% rename from xs/src/eigen/Eigen/src/Core/NumTraits.h rename to src/eigen/Eigen/src/Core/NumTraits.h 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 100% rename from xs/src/eigen/Eigen/src/Core/PlainObjectBase.h rename to src/eigen/Eigen/src/Core/PlainObjectBase.h diff --git a/xs/src/eigen/Eigen/src/Core/Product.h b/src/eigen/Eigen/src/Core/Product.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Product.h rename to src/eigen/Eigen/src/Core/Product.h diff --git a/xs/src/eigen/Eigen/src/Core/ProductEvaluators.h b/src/eigen/Eigen/src/Core/ProductEvaluators.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/ProductEvaluators.h rename to src/eigen/Eigen/src/Core/ProductEvaluators.h 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 100% rename from xs/src/eigen/Eigen/src/Core/Redux.h rename to src/eigen/Eigen/src/Core/Redux.h diff --git a/xs/src/eigen/Eigen/src/Core/Ref.h b/src/eigen/Eigen/src/Core/Ref.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Ref.h rename to src/eigen/Eigen/src/Core/Ref.h 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 100% rename from xs/src/eigen/Eigen/src/Core/SelfAdjointView.h rename to src/eigen/Eigen/src/Core/SelfAdjointView.h diff --git a/xs/src/eigen/Eigen/src/Core/SelfCwiseBinaryOp.h b/src/eigen/Eigen/src/Core/SelfCwiseBinaryOp.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/SelfCwiseBinaryOp.h rename to src/eigen/Eigen/src/Core/SelfCwiseBinaryOp.h diff --git a/xs/src/eigen/Eigen/src/Core/Solve.h b/src/eigen/Eigen/src/Core/Solve.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/Solve.h rename to src/eigen/Eigen/src/Core/Solve.h 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 100% rename from xs/src/eigen/Eigen/src/Core/StableNorm.h rename to src/eigen/Eigen/src/Core/StableNorm.h 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 100% rename from xs/src/eigen/Eigen/src/Core/Transpositions.h rename to src/eigen/Eigen/src/Core/Transpositions.h 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 100% rename from xs/src/eigen/Eigen/src/Core/arch/AVX/Complex.h rename to src/eigen/Eigen/src/Core/arch/AVX/Complex.h 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 100% rename from xs/src/eigen/Eigen/src/Core/arch/AVX/PacketMath.h rename to src/eigen/Eigen/src/Core/arch/AVX/PacketMath.h 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 100% rename from xs/src/eigen/Eigen/src/Core/arch/AVX512/MathFunctions.h rename to src/eigen/Eigen/src/Core/arch/AVX512/MathFunctions.h 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 100% rename from xs/src/eigen/Eigen/src/Core/arch/AVX512/PacketMath.h rename to src/eigen/Eigen/src/Core/arch/AVX512/PacketMath.h 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 100% rename from xs/src/eigen/Eigen/src/Core/arch/AltiVec/Complex.h rename to src/eigen/Eigen/src/Core/arch/AltiVec/Complex.h 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 100% rename from xs/src/eigen/Eigen/src/Core/arch/AltiVec/PacketMath.h rename to src/eigen/Eigen/src/Core/arch/AltiVec/PacketMath.h 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 100% rename from xs/src/eigen/Eigen/src/Core/arch/CUDA/Half.h rename to src/eigen/Eigen/src/Core/arch/CUDA/Half.h 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 100% rename from xs/src/eigen/Eigen/src/Core/arch/CUDA/PacketMath.h rename to src/eigen/Eigen/src/Core/arch/CUDA/PacketMath.h 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 100% rename from xs/src/eigen/Eigen/src/Core/arch/CUDA/PacketMathHalf.h rename to src/eigen/Eigen/src/Core/arch/CUDA/PacketMathHalf.h 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/xs/src/eigen/Eigen/src/Core/arch/Default/ConjHelper.h b/src/eigen/Eigen/src/Core/arch/Default/ConjHelper.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/arch/Default/ConjHelper.h rename to src/eigen/Eigen/src/Core/arch/Default/ConjHelper.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 100% rename from xs/src/eigen/Eigen/src/Core/arch/NEON/Complex.h rename to src/eigen/Eigen/src/Core/arch/NEON/Complex.h 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 100% rename from xs/src/eigen/Eigen/src/Core/arch/NEON/PacketMath.h rename to src/eigen/Eigen/src/Core/arch/NEON/PacketMath.h 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 100% rename from xs/src/eigen/Eigen/src/Core/arch/SSE/Complex.h rename to src/eigen/Eigen/src/Core/arch/SSE/Complex.h 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 100% rename from xs/src/eigen/Eigen/src/Core/arch/SSE/PacketMath.h rename to src/eigen/Eigen/src/Core/arch/SSE/PacketMath.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 100% rename from xs/src/eigen/Eigen/src/Core/arch/SSE/TypeCasting.h rename to src/eigen/Eigen/src/Core/arch/SSE/TypeCasting.h 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 100% rename from xs/src/eigen/Eigen/src/Core/arch/ZVector/Complex.h rename to src/eigen/Eigen/src/Core/arch/ZVector/Complex.h 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 100% rename from xs/src/eigen/Eigen/src/Core/functors/BinaryFunctors.h rename to src/eigen/Eigen/src/Core/functors/BinaryFunctors.h diff --git a/xs/src/eigen/Eigen/src/Core/functors/NullaryFunctors.h b/src/eigen/Eigen/src/Core/functors/NullaryFunctors.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/functors/NullaryFunctors.h rename to src/eigen/Eigen/src/Core/functors/NullaryFunctors.h diff --git a/xs/src/eigen/Eigen/src/Core/functors/StlFunctors.h b/src/eigen/Eigen/src/Core/functors/StlFunctors.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/functors/StlFunctors.h rename to src/eigen/Eigen/src/Core/functors/StlFunctors.h 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 100% rename from xs/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular.h rename to src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular.h 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 100% rename from xs/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular_BLAS.h rename to src/eigen/Eigen/src/Core/products/GeneralMatrixMatrixTriangular_BLAS.h diff --git a/xs/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrix_BLAS.h b/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrix_BLAS.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/products/GeneralMatrixMatrix_BLAS.h rename to src/eigen/Eigen/src/Core/products/GeneralMatrixMatrix_BLAS.h diff --git a/xs/src/eigen/Eigen/src/Core/products/GeneralMatrixVector.h b/src/eigen/Eigen/src/Core/products/GeneralMatrixVector.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/products/GeneralMatrixVector.h rename to src/eigen/Eigen/src/Core/products/GeneralMatrixVector.h diff --git a/xs/src/eigen/Eigen/src/Core/products/GeneralMatrixVector_BLAS.h b/src/eigen/Eigen/src/Core/products/GeneralMatrixVector_BLAS.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/products/GeneralMatrixVector_BLAS.h rename to src/eigen/Eigen/src/Core/products/GeneralMatrixVector_BLAS.h 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 100% rename from xs/src/eigen/Eigen/src/Core/products/SelfadjointMatrixMatrix_BLAS.h rename to src/eigen/Eigen/src/Core/products/SelfadjointMatrixMatrix_BLAS.h 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 100% rename from xs/src/eigen/Eigen/src/Core/products/SelfadjointMatrixVector_BLAS.h rename to src/eigen/Eigen/src/Core/products/SelfadjointMatrixVector_BLAS.h 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 100% rename from xs/src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix.h rename to src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix.h 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 100% rename from xs/src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix_BLAS.h rename to src/eigen/Eigen/src/Core/products/TriangularMatrixMatrix_BLAS.h diff --git a/xs/src/eigen/Eigen/src/Core/products/TriangularMatrixVector.h b/src/eigen/Eigen/src/Core/products/TriangularMatrixVector.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/products/TriangularMatrixVector.h rename to src/eigen/Eigen/src/Core/products/TriangularMatrixVector.h 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 100% rename from xs/src/eigen/Eigen/src/Core/products/TriangularMatrixVector_BLAS.h rename to src/eigen/Eigen/src/Core/products/TriangularMatrixVector_BLAS.h 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 100% rename from xs/src/eigen/Eigen/src/Core/products/TriangularSolverMatrix_BLAS.h rename to src/eigen/Eigen/src/Core/products/TriangularSolverMatrix_BLAS.h diff --git a/xs/src/eigen/Eigen/src/Core/products/TriangularSolverVector.h b/src/eigen/Eigen/src/Core/products/TriangularSolverVector.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/products/TriangularSolverVector.h rename to src/eigen/Eigen/src/Core/products/TriangularSolverVector.h diff --git a/xs/src/eigen/Eigen/src/Core/util/BlasUtil.h b/src/eigen/Eigen/src/Core/util/BlasUtil.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/util/BlasUtil.h rename to src/eigen/Eigen/src/Core/util/BlasUtil.h diff --git a/xs/src/eigen/Eigen/src/Core/util/Constants.h b/src/eigen/Eigen/src/Core/util/Constants.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/util/Constants.h rename to src/eigen/Eigen/src/Core/util/Constants.h diff --git a/xs/src/eigen/Eigen/src/Core/util/DisableStupidWarnings.h b/src/eigen/Eigen/src/Core/util/DisableStupidWarnings.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/util/DisableStupidWarnings.h rename to src/eigen/Eigen/src/Core/util/DisableStupidWarnings.h diff --git a/xs/src/eigen/Eigen/src/Core/util/ForwardDeclarations.h b/src/eigen/Eigen/src/Core/util/ForwardDeclarations.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/util/ForwardDeclarations.h rename to src/eigen/Eigen/src/Core/util/ForwardDeclarations.h diff --git a/xs/src/eigen/Eigen/src/Core/util/MKL_support.h b/src/eigen/Eigen/src/Core/util/MKL_support.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/util/MKL_support.h rename to src/eigen/Eigen/src/Core/util/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 100% rename from xs/src/eigen/Eigen/src/Core/util/Macros.h rename to src/eigen/Eigen/src/Core/util/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 100% rename from xs/src/eigen/Eigen/src/Core/util/Memory.h rename to src/eigen/Eigen/src/Core/util/Memory.h diff --git a/xs/src/eigen/Eigen/src/Core/util/Meta.h b/src/eigen/Eigen/src/Core/util/Meta.h similarity index 100% rename from xs/src/eigen/Eigen/src/Core/util/Meta.h rename to src/eigen/Eigen/src/Core/util/Meta.h 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 100% rename from xs/src/eigen/Eigen/src/Core/util/StaticAssert.h rename to src/eigen/Eigen/src/Core/util/StaticAssert.h 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 100% rename from xs/src/eigen/Eigen/src/Eigenvalues/GeneralizedEigenSolver.h rename to src/eigen/Eigen/src/Eigenvalues/GeneralizedEigenSolver.h 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 100% rename from xs/src/eigen/Eigen/src/Eigenvalues/RealSchur.h rename to src/eigen/Eigen/src/Eigenvalues/RealSchur.h 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 100% rename from xs/src/eigen/Eigen/src/Eigenvalues/SelfAdjointEigenSolver_LAPACKE.h rename to src/eigen/Eigen/src/Eigenvalues/SelfAdjointEigenSolver_LAPACKE.h 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 100% rename from xs/src/eigen/Eigen/src/Geometry/AngleAxis.h rename to src/eigen/Eigen/src/Geometry/AngleAxis.h 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 100% rename from xs/src/eigen/Eigen/src/Geometry/Quaternion.h rename to src/eigen/Eigen/src/Geometry/Quaternion.h 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 100% rename from xs/src/eigen/Eigen/src/Geometry/arch/Geometry_SSE.h rename to src/eigen/Eigen/src/Geometry/arch/Geometry_SSE.h 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 100% rename from xs/src/eigen/Eigen/src/IterativeLinearSolvers/BasicPreconditioners.h rename to src/eigen/Eigen/src/IterativeLinearSolvers/BasicPreconditioners.h 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 100% rename from xs/src/eigen/Eigen/src/Jacobi/Jacobi.h rename to src/eigen/Eigen/src/Jacobi/Jacobi.h 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 100% rename from xs/src/eigen/Eigen/src/LU/InverseImpl.h rename to src/eigen/Eigen/src/LU/InverseImpl.h 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 100% rename from xs/src/eigen/Eigen/src/OrderingMethods/Eigen_Colamd.h rename to src/eigen/Eigen/src/OrderingMethods/Eigen_Colamd.h 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 100% rename from xs/src/eigen/Eigen/src/PaStiXSupport/PaStiXSupport.h rename to src/eigen/Eigen/src/PaStiXSupport/PaStiXSupport.h 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 100% rename from xs/src/eigen/Eigen/src/QR/ColPivHouseholderQR.h rename to src/eigen/Eigen/src/QR/ColPivHouseholderQR.h 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 100% rename from xs/src/eigen/Eigen/src/SVD/BDCSVD.h rename to src/eigen/Eigen/src/SVD/BDCSVD.h 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 100% rename from xs/src/eigen/Eigen/src/SVD/JacobiSVD_LAPACKE.h rename to src/eigen/Eigen/src/SVD/JacobiSVD_LAPACKE.h 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 100% rename from xs/src/eigen/Eigen/src/SVD/UpperBidiagonalization.h rename to src/eigen/Eigen/src/SVD/UpperBidiagonalization.h 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 100% rename from xs/src/eigen/Eigen/src/SparseCore/AmbiVector.h rename to src/eigen/Eigen/src/SparseCore/AmbiVector.h 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 100% rename from xs/src/eigen/Eigen/src/SparseCore/ConservativeSparseSparseProduct.h rename to src/eigen/Eigen/src/SparseCore/ConservativeSparseSparseProduct.h 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 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseSelfAdjointView.h rename to src/eigen/Eigen/src/SparseCore/SparseSelfAdjointView.h 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 100% rename from xs/src/eigen/Eigen/src/SparseCore/SparseSparseProductWithPruning.h rename to src/eigen/Eigen/src/SparseCore/SparseSparseProductWithPruning.h 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 100% rename from xs/src/eigen/Eigen/src/SparseLU/SparseLU_panel_bmod.h rename to src/eigen/Eigen/src/SparseLU/SparseLU_panel_bmod.h 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 100% rename from xs/src/eigen/Eigen/src/SparseQR/SparseQR.h rename to src/eigen/Eigen/src/SparseQR/SparseQR.h 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 100% rename from xs/src/eigen/Eigen/src/UmfPackSupport/UmfPackSupport.h rename to src/eigen/Eigen/src/UmfPackSupport/UmfPackSupport.h 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 100% rename from xs/src/eigen/README.md rename to src/eigen/README.md 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..0953733541 --- /dev/null +++ b/src/igl/AABB.cpp @@ -0,0 +1,1075 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2013 Alec Jacobson +// +// This Source Code Form is subject to the terms of the Mozilla Public License +// v. 2.0. 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 +{ +#if false + // 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..cabc9bdfaa --- /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..995afcc76d 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 99% rename from xs/src/libnest2d/libnest2d/libnest2d.hpp rename to src/libnest2d/include/libnest2d/libnest2d.hpp index 8841d1b735..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; @@ -805,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; } 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..5559ad6453 --- /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 100% rename from xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp rename to src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp diff --git a/xs/src/libnest2d/libnest2d/selections/filler.hpp b/src/libnest2d/include/libnest2d/selections/filler.hpp similarity index 100% rename from xs/src/libnest2d/libnest2d/selections/filler.hpp rename to src/libnest2d/include/libnest2d/selections/filler.hpp diff --git a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp b/src/libnest2d/include/libnest2d/selections/firstfit.hpp similarity index 99% rename from xs/src/libnest2d/libnest2d/selections/firstfit.hpp rename to src/libnest2d/include/libnest2d/selections/firstfit.hpp index 6bb9c60e45..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 { diff --git a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp similarity index 96% rename from xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp rename to src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp index cfb98a9c8e..8351a99e7c 100644 --- a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp +++ b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp @@ -1,8 +1,8 @@ #ifndef SELECTION_BOILERPLATE_HPP #define SELECTION_BOILERPLATE_HPP -#include "../libnest2d.hpp" #include +#include namespace libnest2d { namespace selections { 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/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/xs/src/libslic3r/BoundingBox.cpp b/src/libslic3r/BoundingBox.cpp similarity index 100% rename from xs/src/libslic3r/BoundingBox.cpp rename to src/libslic3r/BoundingBox.cpp diff --git a/xs/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp similarity index 100% rename from xs/src/libslic3r/BoundingBox.hpp rename to src/libslic3r/BoundingBox.hpp diff --git a/xs/src/libslic3r/BridgeDetector.cpp b/src/libslic3r/BridgeDetector.cpp similarity index 100% rename from xs/src/libslic3r/BridgeDetector.cpp rename to src/libslic3r/BridgeDetector.cpp 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..7e41a158ae --- /dev/null +++ b/src/libslic3r/CMakeLists.txt @@ -0,0 +1,175 @@ +project(libslic3r) +cmake_minimum_required(VERSION 2.6) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libslic3r_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h @ONLY) + +add_library(libslic3r STATIC + 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 + 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 + PrintExport.hpp + PrintConfig.cpp + PrintConfig.hpp + PrintObject.cpp + PrintRegion.cpp + Rasterizer/Rasterizer.hpp + Rasterizer/Rasterizer.cpp + 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 + SLABasePool.hpp + SLABasePool.cpp + utils.cpp + Utils.hpp +) + +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 100% rename from xs/src/libslic3r/ClipperUtils.cpp rename to src/libslic3r/ClipperUtils.cpp 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 84% rename from xs/src/libslic3r/Config.cpp rename to src/libslic3r/Config.cpp index e7442d7730..bf8be7feb4 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -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; diff --git a/xs/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp similarity index 96% rename from xs/src/libslic3r/Config.hpp rename to src/libslic3r/Config.hpp index e3cd14335a..43871b0f80 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -61,6 +61,12 @@ enum ConfigOptionType { coEnum = 8, }; +enum ConfigOptionMode { + comSimple, + comAdvanced, + comExpert +}; + // A generic value of a configuration option. class ConfigOption { public: @@ -810,6 +816,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 { @@ -879,6 +901,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) @@ -951,6 +988,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; @@ -976,7 +1014,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, @@ -984,18 +1022,27 @@ 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; - } + 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. @@ -1219,6 +1266,10 @@ public: 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; } + // 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); + private: typedef std::map t_options_map; t_options_map options; diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/src/libslic3r/EdgeGrid.cpp similarity index 100% rename from xs/src/libslic3r/EdgeGrid.cpp rename to src/libslic3r/EdgeGrid.cpp 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 100% rename from xs/src/libslic3r/ExPolygon.cpp rename to src/libslic3r/ExPolygon.cpp diff --git a/xs/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp similarity index 100% rename from xs/src/libslic3r/ExPolygon.hpp rename to src/libslic3r/ExPolygon.hpp 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 100% rename from xs/src/libslic3r/ExtrusionEntity.cpp rename to src/libslic3r/ExtrusionEntity.cpp diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp similarity index 100% rename from xs/src/libslic3r/ExtrusionEntity.hpp rename to src/libslic3r/ExtrusionEntity.hpp 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 100% rename from xs/src/libslic3r/ExtrusionEntityCollection.hpp rename to src/libslic3r/ExtrusionEntityCollection.hpp diff --git a/xs/src/libslic3r/ExtrusionSimulator.cpp b/src/libslic3r/ExtrusionSimulator.cpp similarity index 100% rename from xs/src/libslic3r/ExtrusionSimulator.cpp rename to src/libslic3r/ExtrusionSimulator.cpp 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 100% rename from xs/src/libslic3r/Fill/Fill.cpp rename to src/libslic3r/Fill/Fill.cpp 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 100% rename from xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp rename to src/libslic3r/Fill/Fill3DHoneycomb.cpp 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 100% rename from xs/src/libslic3r/Fill/FillBase.cpp rename to src/libslic3r/Fill/FillBase.cpp diff --git a/xs/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp similarity index 100% rename from xs/src/libslic3r/Fill/FillBase.hpp rename to src/libslic3r/Fill/FillBase.hpp diff --git a/xs/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp similarity index 100% rename from xs/src/libslic3r/Fill/FillConcentric.cpp rename to src/libslic3r/Fill/FillConcentric.cpp 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 100% rename from xs/src/libslic3r/Fill/FillGyroid.cpp rename to src/libslic3r/Fill/FillGyroid.cpp 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 100% rename from xs/src/libslic3r/Fill/FillHoneycomb.cpp rename to src/libslic3r/Fill/FillHoneycomb.cpp 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 100% rename from xs/src/libslic3r/Fill/FillPlanePath.cpp rename to src/libslic3r/Fill/FillPlanePath.cpp 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 100% rename from xs/src/libslic3r/Fill/FillRectilinear.cpp rename to src/libslic3r/Fill/FillRectilinear.cpp 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 100% rename from xs/src/libslic3r/Fill/FillRectilinear2.cpp rename to src/libslic3r/Fill/FillRectilinear2.cpp 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 100% rename from xs/src/libslic3r/Fill/FillRectilinear3.cpp rename to src/libslic3r/Fill/FillRectilinear3.cpp 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 100% rename from xs/src/libslic3r/Flow.cpp rename to src/libslic3r/Flow.cpp 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 95% rename from xs/src/libslic3r/Format/3mf.cpp rename to src/libslic3r/Format/3mf.cpp index 0aa38ac391..bd67a2fce1 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" @@ -347,17 +347,18 @@ namespace Slic3r { _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_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, PresetBundle& bundle, const std::string& archive_filename); + + 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 @@ -451,7 +452,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; @@ -470,7 +471,7 @@ namespace Slic3r { 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() @@ -488,7 +489,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); @@ -546,7 +547,7 @@ namespace Slic3r { 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)) { @@ -670,7 +671,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) { @@ -681,7 +682,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()); } } @@ -1328,64 +1329,44 @@ namespace Slic3r { 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 - // translation -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - Vec3d offset(transform(0, 3), transform(1, 3), transform(2, 3)); -#else - double offset_x = transform(0, 3); - double offset_y = transform(1, 3); - double offset_z = transform(2, 3); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + Vec3d offset = transform.matrix().block(0, 3, 3, 1); + + Eigen::Matrix m3x3 = transform.matrix().block(0, 0, 3, 3); +#if ENABLE_MIRROR + // 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(transform(0, 0)) + sqr(transform(1, 0)) + sqr(transform(2, 0))); - double sy = ::sqrt(sqr(transform(0, 1)) + sqr(transform(1, 1)) + sqr(transform(2, 1))); - double sz = ::sqrt(sqr(transform(0, 2)) + sqr(transform(1, 2)) + sqr(transform(2, 2))); +#endif // ENABLE_MIRROR + 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; -#if !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - // non-uniform scale value, return - if ((std::abs(sx - sy) > 0.00001) || (std::abs(sx - sz) > 0.00001)) - return; -#endif // !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + // remove scale + m3x3.col(0).normalize(); + m3x3.col(1).normalize(); + m3x3.col(2).normalize(); - double inv_sx = 1.0 / sx; - double inv_sy = 1.0 / sy; - double inv_sz = 1.0 / sz; - - Eigen::Matrix m3x3; - m3x3 << transform(0, 0) * inv_sx, transform(0, 1) * inv_sy, transform(0, 2) * inv_sz, - transform(1, 0) * inv_sx, transform(1, 1) * inv_sy, transform(1, 2) * inv_sz, - transform(2, 0) * inv_sx, transform(2, 1) * inv_sy, transform(2, 2) * inv_sz; - -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - Vec3d angles = m3x3.eulerAngles(2, 1, 0); - Vec3d rotation(angles(2), angles(1), angles(0)); + Vec3d rotation = Slic3r::Geometry::extract_euler_angles(m3x3); instance.set_offset(offset); - instance.set_scaling_factor(Vec3d(sx, sy, sz)); + instance.set_scaling_factor(scale); instance.set_rotation(rotation); -#else - Eigen::AngleAxisd rotation; - rotation.fromRotationMatrix(m3x3); - - // invalid rotation axis, we currently handle only rotations around Z axis - if ((rotation.angle() != 0.0) && (rotation.axis() != Vec3d::UnitZ()) && (rotation.axis() != -Vec3d::UnitZ())) - return; - - double angle_z = (rotation.axis() == Vec3d::UnitZ()) ? rotation.angle() : -rotation.angle(); - - instance.offset(0) = offset_x; - instance.offset(1) = offset_y; - instance.scaling_factor = sx; - instance.rotation = angle_z; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_MIRROR + instance.set_mirror(mirror); +#endif // ENABLE_MIRROR } bool _3MF_Importer::_handle_start_config(const char** attributes, unsigned int num_attributes) @@ -1430,7 +1411,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); @@ -1488,7 +1469,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) { @@ -1509,7 +1490,7 @@ 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]; @@ -2126,13 +2107,13 @@ 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; } diff --git a/xs/src/libslic3r/Format/3mf.hpp b/src/libslic3r/Format/3mf.hpp similarity index 84% rename from xs/src/libslic3r/Format/3mf.hpp rename to src/libslic3r/Format/3mf.hpp index 85bc812e38..cfab1c6007 100644 --- a/xs/src/libslic3r/Format/3mf.hpp +++ b/src/libslic3r/Format/3mf.hpp @@ -5,10 +5,9 @@ namespace Slic3r { class Model; class Print; - class PresetBundle; // 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 diff --git a/xs/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp similarity index 88% rename from xs/src/libslic3r/Format/AMF.cpp rename to src/libslic3r/Format/AMF.cpp index 41e6941ee4..615dff0017 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,14 +29,13 @@ // 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. -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM // 2 : Added z component of offset // Added x and y components of rotation // Added x, y and z components of scale +#if ENABLE_MIRROR +// Added x, y and z components of mirror +#endif // ENABLE_MIRROR const unsigned int VERSION_AMF = 2; -#else -const unsigned int VERSION_AMF = 1; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM const char* SLIC3RPE_AMF_VERSION = "slic3rpe_amf_version"; const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config"; @@ -46,7 +45,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), @@ -54,8 +53,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); } @@ -126,34 +124,38 @@ struct AMFParserContext NODE_TYPE_INSTANCE, // amf/constellation/instance NODE_TYPE_DELTAX, // amf/constellation/instance/deltax NODE_TYPE_DELTAY, // amf/constellation/instance/deltay -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM NODE_TYPE_DELTAZ, // amf/constellation/instance/deltaz NODE_TYPE_RX, // amf/constellation/instance/rx NODE_TYPE_RY, // amf/constellation/instance/ry -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM NODE_TYPE_RZ, // amf/constellation/instance/rz NODE_TYPE_SCALE, // amf/constellation/instance/scale -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - NODE_TYPE_SCALEX, // amf/constellation/instance/scalex - NODE_TYPE_SCALEY, // amf/constellation/instance/scaley - NODE_TYPE_SCALEZ, // amf/constellation/instance/scalez -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + NODE_TYPE_SCALEX, // amf/constellation/instance/scalex + NODE_TYPE_SCALEY, // amf/constellation/instance/scaley + NODE_TYPE_SCALEZ, // amf/constellation/instance/scalez +#if ENABLE_MIRROR + NODE_TYPE_MIRRORX, // amf/constellation/instance/mirrorx + NODE_TYPE_MIRRORY, // amf/constellation/instance/mirrory + NODE_TYPE_MIRRORZ, // amf/constellation/instance/mirrorz +#endif // ENABLE_MIRROR NODE_TYPE_METADATA, // anywhere under amf/*/metadata }; struct Instance { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - 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) {} +#if ENABLE_MIRROR + 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) {} #else - Instance() : deltax_set(false), deltay_set(false), rz_set(false), scale_set(false) {} -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + 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) {} +#endif // ENABLE_MIRROR // Shift in the X axis. float deltax; bool deltax_set; // Shift in the Y axis. float deltay; bool deltay_set; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM // Shift in the Z axis. float deltaz; bool deltaz_set; @@ -163,11 +165,9 @@ struct AMFParserContext // Rotation around the Y axis. float ry; bool ry_set; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM // Rotation around the Z axis. float rz; bool rz_set; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM // Scaling factors float scalex; bool scalex_set; @@ -175,11 +175,15 @@ struct AMFParserContext bool scaley_set; float scalez; bool scalez_set; -#else - // Scaling factor - float scale; - bool scale_set; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_MIRROR + // Mirroring factors + float mirrorx; + bool mirrorx_set; + float mirrory; + bool mirrory_set; + float mirrorz; + bool mirrorz_set; +#endif // ENABLE_MIRROR }; struct Object { @@ -212,10 +216,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&); @@ -296,26 +298,30 @@ 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; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM 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; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM else if (strcmp(name, "rz") == 0) node_type_new = NODE_TYPE_RZ; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM 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; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM else if (strcmp(name, "scale") == 0) node_type_new = NODE_TYPE_SCALE; +#if ENABLE_MIRROR + 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; +#endif // ENABLE_MIRROR } break; case 4: @@ -372,20 +378,23 @@ void AMFParserContext::characters(const XML_Char *s, int len) { switch (m_path.size()) { case 4: -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - if (m_path.back() == NODE_TYPE_DELTAX || - m_path.back() == NODE_TYPE_DELTAY || - m_path.back() == NODE_TYPE_DELTAZ || + 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) +#if ENABLE_MIRROR + m_path.back() == NODE_TYPE_SCALE || + m_path.back() == NODE_TYPE_MIRRORX || + m_path.back() == NODE_TYPE_MIRRORY || + m_path.back() == NODE_TYPE_MIRRORZ) #else - 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) -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + m_path.back() == NODE_TYPE_SCALE) +#endif // ENABLE_MIRROR m_value[0].append(s, len); break; case 6: @@ -425,7 +434,6 @@ void AMFParserContext::endElement(const char * /* name */) m_instance->deltay_set = true; m_value[0].clear(); break; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM case NODE_TYPE_DELTAZ: assert(m_instance); m_instance->deltaz = float(atof(m_value[0].c_str())); @@ -444,7 +452,6 @@ void AMFParserContext::endElement(const char * /* name */) m_instance->ry_set = true; m_value[0].clear(); break; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM case NODE_TYPE_RZ: assert(m_instance); m_instance->rz = float(atof(m_value[0].c_str())); @@ -453,20 +460,14 @@ void AMFParserContext::endElement(const char * /* name */) break; case NODE_TYPE_SCALE: assert(m_instance); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM 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; -#else - m_instance->scale = float(atof(m_value[0].c_str())); - m_instance->scale_set = true; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM m_value[0].clear(); break; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM case NODE_TYPE_SCALEX: assert(m_instance); m_instance->scalex = float(atof(m_value[0].c_str())); @@ -485,7 +486,26 @@ void AMFParserContext::endElement(const char * /* name */) m_instance->scalez_set = true; m_value[0].clear(); break; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_MIRROR + 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; +#endif // ENABLE_MIRROR // Object vertices: case NODE_TYPE_VERTEX: @@ -549,9 +569,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()) { @@ -643,22 +662,18 @@ 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(); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM 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)); -#else - mi->offset(0) = instance.deltax; - mi->offset(1) = instance.deltay; - mi->rotation = instance.rz_set ? instance.rz : 0.f; - mi->scaling_factor = instance.scale_set ? instance.scale : 1.f; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_MIRROR + 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)); +#endif // ENABLE_MIRROR } } } // 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; @@ -675,7 +690,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); @@ -710,7 +725,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) { @@ -726,7 +741,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); @@ -762,7 +777,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; @@ -789,7 +804,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"); @@ -817,12 +832,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); @@ -833,7 +848,7 @@ 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; @@ -948,7 +963,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"; @@ -965,22 +980,20 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c " \n" " %lf\n" " %lf\n" -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM " %lf\n" " %lf\n" " %lf\n" -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM " %lf\n" -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM " %lf\n" " %lf\n" " %lf\n" -#else - " %lf\n" -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_MIRROR + " %lf\n" + " %lf\n" + " %lf\n" +#endif // ENABLE_MIRROR " \n", object_id, -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM instance->get_offset(X), instance->get_offset(Y), instance->get_offset(Z), @@ -989,13 +1002,14 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c instance->get_rotation(Z), instance->get_scaling_factor(X), instance->get_scaling_factor(Y), - instance->get_scaling_factor(Z)); +#if ENABLE_MIRROR + instance->get_scaling_factor(Z), + instance->get_mirror(X), + instance->get_mirror(Y), + instance->get_mirror(Z)); #else - instance->offset(0), - instance->offset(1), - instance->rotation, - instance->scaling_factor); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + instance->get_scaling_factor(Z)); +#endif // ENABLE_MIRROR //FIXME missing instance->scaling_factor instances.append(buf); diff --git a/xs/src/libslic3r/Format/AMF.hpp b/src/libslic3r/Format/AMF.hpp similarity index 67% rename from xs/src/libslic3r/Format/AMF.hpp rename to src/libslic3r/Format/AMF.hpp index 4779e9a51c..ae8863e02f 100644 --- a/xs/src/libslic3r/Format/AMF.hpp +++ b/src/libslic3r/Format/AMF.hpp @@ -5,10 +5,9 @@ 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); +// 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 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 @@ -16,4 +15,4 @@ extern bool store_amf(const char *path, Model *model, Print* print, bool export_ }; // namespace Slic3r -#endif /* slic3r_Format_AMF_hpp_ */ \ No newline at end of file +#endif /* slic3r_Format_AMF_hpp_ */ diff --git a/xs/src/libslic3r/Format/OBJ.cpp b/src/libslic3r/Format/OBJ.cpp similarity index 100% rename from xs/src/libslic3r/Format/OBJ.cpp rename to src/libslic3r/Format/OBJ.cpp 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 99% rename from xs/src/libslic3r/GCode.cpp rename to src/libslic3r/GCode.cpp index d10705c183..c4d0c1bd93 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -455,9 +455,12 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ fclose(file); 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 (! m_placeholder_parser_failed_templates.empty()) { @@ -1033,9 +1036,11 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) } 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) @@ -1233,7 +1238,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); diff --git a/xs/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp similarity index 100% rename from xs/src/libslic3r/GCode.hpp rename to src/libslic3r/GCode.hpp diff --git a/xs/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp similarity index 99% rename from xs/src/libslic3r/GCode/Analyzer.cpp rename to src/libslic3r/GCode/Analyzer.cpp index 51d5b1a067..e3cd67e7cc 100644 --- a/xs/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -698,7 +698,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ // 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); diff --git a/xs/src/libslic3r/GCode/Analyzer.hpp b/src/libslic3r/GCode/Analyzer.hpp similarity index 100% rename from xs/src/libslic3r/GCode/Analyzer.hpp rename to src/libslic3r/GCode/Analyzer.hpp diff --git a/xs/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp similarity index 100% rename from xs/src/libslic3r/GCode/CoolingBuffer.cpp rename to src/libslic3r/GCode/CoolingBuffer.cpp 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/xs/src/libslic3r/GCode/PostProcessor.cpp b/src/libslic3r/GCode/PostProcessor.cpp similarity index 100% rename from xs/src/libslic3r/GCode/PostProcessor.cpp rename to src/libslic3r/GCode/PostProcessor.cpp diff --git a/xs/src/libslic3r/GCode/PostProcessor.hpp b/src/libslic3r/GCode/PostProcessor.hpp similarity index 100% rename from xs/src/libslic3r/GCode/PostProcessor.hpp rename to src/libslic3r/GCode/PostProcessor.hpp diff --git a/xs/src/libslic3r/GCode/PressureEqualizer.cpp b/src/libslic3r/GCode/PressureEqualizer.cpp similarity index 100% rename from xs/src/libslic3r/GCode/PressureEqualizer.cpp rename to src/libslic3r/GCode/PressureEqualizer.cpp 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 9cf9716e0a..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 diff --git a/xs/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp similarity index 100% rename from xs/src/libslic3r/GCode/PreviewData.hpp rename to src/libslic3r/GCode/PreviewData.hpp diff --git a/xs/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp similarity index 100% rename from xs/src/libslic3r/GCode/PrintExtents.cpp rename to src/libslic3r/GCode/PrintExtents.cpp 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 100% rename from xs/src/libslic3r/GCode/ToolOrdering.cpp rename to src/libslic3r/GCode/ToolOrdering.cpp 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 100% rename from xs/src/libslic3r/GCode/WipeTower.hpp rename to src/libslic3r/GCode/WipeTower.hpp diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp similarity index 100% rename from xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp rename to src/libslic3r/GCode/WipeTowerPrusaMM.cpp 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 100% rename from xs/src/libslic3r/GCodeReader.cpp rename to src/libslic3r/GCodeReader.cpp 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 f97265ee3c..4bfd5d63fe 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -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 100% rename from xs/src/libslic3r/GCodeWriter.cpp rename to src/libslic3r/GCodeWriter.cpp diff --git a/xs/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp similarity index 100% rename from xs/src/libslic3r/GCodeWriter.hpp rename to src/libslic3r/GCodeWriter.hpp diff --git a/xs/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp similarity index 95% rename from xs/src/libslic3r/Geometry.cpp rename to src/libslic3r/Geometry.cpp index 87b4e223d4..a100f520a9 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -1166,4 +1166,70 @@ MedialAxis::retrieve_endpoint(const VD::cell_type* cell) const } } +#if ENABLE_MIRROR +void assemble_transform(Transform3d& transform, const Vec3d& translation, const Vec3d& rotation, const Vec3d& scale, const Vec3d& mirror) +#else +void assemble_transform(Transform3d& transform, const Vec3d& translation, const Vec3d& rotation, const Vec3d& scale) +#endif // ENABLE_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); +#if ENABLE_MIRROR + transform.scale(mirror); +#endif // ENABLE_MIRROR +} + +#if ENABLE_MIRROR +Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation, const Vec3d& scale, const Vec3d& mirror) +#else +Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation, const Vec3d& scale) +#endif // ENABLE_MIRROR +{ + Transform3d transform; +#if ENABLE_MIRROR + assemble_transform(transform, translation, rotation, scale, mirror); +#else + assemble_transform(transform, translation, rotation, scale); +#endif // ENABLE_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); +} + } } diff --git a/xs/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp similarity index 78% rename from xs/src/libslic3r/Geometry.hpp rename to src/libslic3r/Geometry.hpp index 3698b996fd..9046add258 100644 --- a/xs/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -157,6 +157,49 @@ 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: +#if ENABLE_MIRROR +// 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()); +#else +// 1) scale +// 2) rotate X +// 3) rotate Y +// 4) rotate Z +// 5) translate +void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones()); +#endif // ENABLE_MIRROR + +// Returns the transform obtained by assembling the given transformations in the following order: +#if ENABLE_MIRROR +// 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()); +#else +// 1) scale +// 2) rotate X +// 3) rotate Y +// 4) rotate Z +// 5) translate +Transform3d assemble_transform(const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones()); +#endif // ENABLE_MIRROR + +// 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); } } #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 99% rename from xs/src/libslic3r/Layer.cpp rename to src/libslic3r/Layer.cpp index 6c2bd0da90..afdde1852d 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -154,7 +154,7 @@ void Layer::make_fills() layerm->fills.clear(); make_fill(*layerm, layerm->fills); #ifndef NDEBUG - for (size_t i = 0; i < layerm.fills.entities.size(); ++ i) + for (size_t i = 0; i < layerm->fills.entities.size(); ++ i) assert(dynamic_cast(layerm->fills.entities[i]) != NULL); #endif } diff --git a/xs/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp similarity index 100% rename from xs/src/libslic3r/Layer.hpp rename to src/libslic3r/Layer.hpp diff --git a/xs/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp similarity index 100% rename from xs/src/libslic3r/LayerRegion.cpp rename to src/libslic3r/LayerRegion.cpp diff --git a/xs/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp similarity index 100% rename from xs/src/libslic3r/Line.cpp rename to src/libslic3r/Line.cpp diff --git a/xs/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp similarity index 100% rename from xs/src/libslic3r/Line.hpp rename to src/libslic3r/Line.hpp diff --git a/xs/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp similarity index 84% rename from xs/src/libslic3r/Model.cpp rename to src/libslic3r/Model.cpp index e8df86eb7c..2efaf58ebb 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -19,35 +19,36 @@ namespace Slic3r { - unsigned int Model::s_auto_extruder_id = 1; +unsigned int Model::s_auto_extruder_id = 1; -Model::Model(const Model &other) +ModelID ModelBase::s_last_id = 0; + +Model::Model(const Model &rhs) { - // 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); + *this = rhs; } -Model& Model::operator=(Model other) +Model& Model::operator=(const Model &rhs) { - this->swap(other); + m_id = rhs.m_id; + // copy materials + for (const auto &m : rhs.materials) + this->add_material(m.first, *m.second); + // copy objects + this->objects.reserve(rhs.objects.size()); + for (const ModelObject *o : rhs.objects) + this->add_object(*o, true); 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::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); @@ -55,11 +56,11 @@ Model Model::read_from_file(const std::string &input_file, bool add_default_inst 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 + 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); -#endif /* SLIC3R_PRUS */ else throw std::runtime_error("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension."); @@ -78,15 +79,15 @@ Model Model::read_from_file(const std::string &input_file, bool add_default_inst return model; } -Model Model::read_from_archive(const std::string &input_file, PresetBundle* bundle, bool add_default_instances) +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(), bundle, &model); + 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(), bundle, &model); + 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."); @@ -113,6 +114,12 @@ Model Model::read_from_archive(const std::string &input_file, PresetBundle* bund return model; } +void Model::repair() +{ + for (ModelObject *o : this->objects) + o->repair(); +} + ModelObject* Model::add_object() { this->objects.emplace_back(new ModelObject(this)); @@ -235,6 +242,14 @@ BoundingBoxf3 Model::bounding_box() const 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; +} + void Model::center_instances_around_point(const Vec2d &point) { BoundingBoxf3 bb; @@ -242,19 +257,11 @@ void Model::center_instances_around_point(const Vec2d &point) for (size_t i = 0; i < o->instances.size(); ++ i) bb.merge(o->instance_bounding_box(i, false)); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM Vec2d shift2 = point - to_2d(bb.center()); Vec3d shift3 = Vec3d(shift2(0), shift2(1), 0.0); -#else - Vec2d shift = point - 0.5 * to_2d(bb.size()) - to_2d(bb.min); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM for (ModelObject *o : this->objects) { for (ModelInstance *i : o->instances) -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM i->set_offset(i->get_offset() + shift3); -#else - i->offset += shift; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM o->invalidate_bounding_box(); } } @@ -320,12 +327,8 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) size_t idx = 0; for (ModelObject *o : this->objects) { for (ModelInstance *i : o->instances) { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM Vec2d offset_xy = positions[idx] - instance_centers[idx]; i->set_offset(Vec3d(offset_xy(0), offset_xy(1), i->get_offset(Z))); -#else - i->offset = positions[idx] - instance_centers[idx]; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM ++idx; } o->invalidate_bounding_box(); @@ -350,11 +353,7 @@ void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb) for (const ModelInstance *i : instances) { for (const Vec2d &pos : positions) { ModelInstance *instance = o->add_instance(*i); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM instance->set_offset(instance->get_offset() + Vec3d(pos(0), pos(1), 0.0)); -#else - instance->offset += pos; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM } } o->invalidate_bounding_box(); @@ -384,21 +383,12 @@ void Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist) ModelObject* object = this->objects.front(); object->clear_instances(); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM Vec3d ext_size = object->bounding_box().size() + dist * Vec3d::Ones(); -#else - Vec3d size = object->bounding_box().size(); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM 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(); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM instance->set_offset(Vec3d(ext_size(0) * (double)(x_copy - 1), ext_size(1) * (double)(y_copy - 1), 0.0)); -#else - instance->offset(0) = (size(0) + dist) * (x_copy - 1); - instance->offset(1) = (size(1) + dist) * (y_copy - 1); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM } } } @@ -430,6 +420,7 @@ void Model::convert_multipart_object(unsigned int max_extruders) ModelObject* object = new ModelObject(this); object->input_file = this->objects.front()->input_file; + object->name = this->objects.front()->name; reset_auto_extruder_id(); @@ -493,6 +484,7 @@ void Model::reset_auto_extruder_id() } ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volumes) : + ModelBase(other), // copy the id name(other.name), input_file(other.input_file), instances(), @@ -509,33 +501,12 @@ ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volum { 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); + for (ModelVolume *model_volume : other.volumes) + this->add_volume(*model_volume); } - 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); + for (const ModelInstance *model_instance : other.instances) + this->add_instance(*model_instance); } ModelObject::~ModelObject() @@ -544,6 +515,13 @@ ModelObject::~ModelObject() this->clear_instances(); } +// 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); @@ -600,6 +578,15 @@ ModelInstance* ModelObject::add_instance(const ModelInstance &other) 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; @@ -698,28 +685,32 @@ void ModelObject::center_around_origin() if (v->is_model_part()) bb.merge(v->mesh.bounding_box()); +#if ENABLE_EXTENDED_SELECTION + // Shift is the vector from the center of the bounding box to the origin + Vec3d shift = -bb.center(); +#else // Shift is the vector from the center of the bottom face of the bounding box to the origin Vec3d shift = -bb.center(); shift(2) = -bb.min(2); +#endif // ENABLE_EXTENDED_SELECTION this->translate(shift); this->origin_translation += shift; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if !ENABLE_EXTENDED_SELECTION // set z to zero, translation in z has already been done within the mesh shift(2) = 0.0; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#endif // !ENABLE_EXTENDED_SELECTION if (!this->instances.empty()) { for (ModelInstance *i : this->instances) { +#if ENABLE_MIRROR + i->set_offset(i->get_offset() - shift); +#else // apply rotation and scaling to vector as well before translating instance, // in order to leave final position unaltered -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM i->set_offset(i->get_offset() + i->transform_vector(-shift, true)); -#else - Vec3d i_shift = i->world_matrix(true) * shift; - i->offset -= to_2d(i_shift); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#endif // ENABLE_MIRROR } this->invalidate_bounding_box(); } @@ -881,7 +872,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects) new_volume->name = volume->name; new_volume->config = volume->config; new_volume->set_type(volume->type()); - new_volume->material_id(volume->material_id()); + new_volume->set_material_id(volume->material_id()); new_objects->push_back(new_object); delete mesh; @@ -890,26 +881,38 @@ void ModelObject::split(ModelObjectPtrs* new_objects) return; } -// Called by Print::validate() from the UI thread. -void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume) +void ModelObject::repair() { - for (const ModelVolume* vol : this->volumes) - { - if (vol->is_model_part()) - { - for (ModelInstance* inst : this->instances) - { - BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(inst->world_matrix()); + for (ModelVolume *v : this->volumes) + v->mesh.repair(); +} +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()) { + BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->world_matrix()); if (print_volume.contains(bb)) - inst->print_volume_state = ModelInstance::PVS_Inside; + inside_outside |= INSIDE; else if (print_volume.intersects(bb)) - inst->print_volume_state = ModelInstance::PVS_Partly_Outside; + inside_outside |= INSIDE | OUTSIDE; else - inst->print_volume_state = ModelInstance::PVS_Fully_Outside; + 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 @@ -954,22 +957,22 @@ void ModelObject::print_info() const cout << "volume = " << mesh.volume() << endl; } -void ModelVolume::material_id(t_model_material_id material_id) +void ModelVolume::set_material_id(t_model_material_id material_id) { - this->_material_id = material_id; + m_material_id = material_id; - // ensure this->_material_id references an existing material + // ensure m_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); + return this->object->get_model()->get_material(m_material_id); } void ModelVolume::set_material(t_model_material_id material_id, const ModelMaterial &material) { - this->_material_id = material_id; + m_material_id = material_id; (void)this->object->get_model()->add_material(material_id, material); } @@ -978,8 +981,8 @@ 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); + m_material_id = 1 + model->materials.size(); // watchout for implicit cast + return model->add_material(m_material_id); } void ModelVolume::calculate_convex_hull() @@ -1063,7 +1066,6 @@ size_t ModelVolume::split(unsigned int max_extruders) return idx; } -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM void ModelInstance::set_rotation(const Vec3d& rotation) { set_rotation(X, rotation(0)); @@ -1084,7 +1086,38 @@ void ModelInstance::set_rotation(Axis axis, double rotation) } m_rotation(axis) = rotation; } -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + +#if ENABLE_MIRROR +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_MIRROR void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const { @@ -1099,7 +1132,6 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes BoundingBoxf3 bbox = copy.bounding_box(); if (!empty(bbox)) { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM // Scale the bounding box along the three axes. for (unsigned int i = 0; i < 3; ++i) { @@ -1115,19 +1147,6 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes bbox.min += this->m_offset; bbox.max += this->m_offset; } -#else - // Scale the bounding box uniformly. - if (std::abs(this->scaling_factor - 1.) > EPSILON) { - bbox.min *= this->scaling_factor; - bbox.max *= this->scaling_factor; - } - - // Translate the bounding box. - if (!dont_translate) { - Eigen::Map(bbox.min.data()) += this->offset; - Eigen::Map(bbox.max.data()) += this->offset; - } -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM } return bbox; } @@ -1144,46 +1163,27 @@ Vec3d ModelInstance::transform_vector(const Vec3d& v, bool dont_translate) const void ModelInstance::transform_polygon(Polygon* polygon) const { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM // 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 -#else - polygon->rotate(this->rotation); // rotate around polygon origin - polygon->scale(this->scaling_factor); // scale around polygon origin -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM } +#if ENABLE_MIRROR +Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const +#else Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, bool dont_scale) const +#endif // ENABLE_MIRROR { - Transform3d m = Transform3d::Identity(); - -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - if (!dont_translate) - m.translate(m_offset); - - if (!dont_rotate) - { - m.rotate(Eigen::AngleAxisd(m_rotation(2), Vec3d::UnitZ())); - m.rotate(Eigen::AngleAxisd(m_rotation(1), Vec3d::UnitY())); - m.rotate(Eigen::AngleAxisd(m_rotation(0), Vec3d::UnitX())); - } - - if (!dont_scale) - m.scale(m_scaling_factor); + 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; +#if ENABLE_MIRROR + Vec3d mirror = dont_mirror ? Vec3d::Ones() : m_mirror; + return Geometry::assemble_transform(translation, rotation, scale, mirror); #else - if (!dont_translate) - m.translate(Vec3d(offset(0), offset(1), 0.0)); - - if (!dont_rotate) - m.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ())); - - if (!dont_scale) - m.scale(scaling_factor); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - - return m; + return Geometry::assemble_transform(translation, rotation, scale); +#endif // ENABLE_MIRROR } } diff --git a/xs/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp similarity index 73% rename from xs/src/libslic3r/Model.hpp rename to src/libslic3r/Model.hpp index a3619ac6fe..ceb7a8a8f2 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -20,6 +20,7 @@ class ModelMaterial; class ModelObject; class ModelVolume; class PresetBundle; +class Print; typedef std::string t_model_material_id; typedef std::string t_model_material_attribute; @@ -30,8 +31,29 @@ 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) +typedef size_t ModelID; + +// 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: + ModelID m_id = generate_new_id(); + +private: + static inline ModelID generate_new_id() { return s_last_id ++; } + static ModelID s_last_id; +}; + // Material, which may be shared across multiple ModelObjects of a single Model. -class ModelMaterial +class ModelMaterial : public ModelBase { friend class Model; public: @@ -56,12 +78,12 @@ private: // 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 +class ModelObject : public ModelBase { friend class Model; public: std::string name; - std::string input_file; + 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; @@ -89,20 +111,22 @@ public: to new volumes before adding them to this object in order to preserve alignment when user expects that. */ Vec3d 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(); + 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(); + 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. @@ -124,6 +148,7 @@ public: void translate(const Vec3d &vector) { this->translate(vector(0), vector(1), vector(2)); } void translate(coordf_t x, coordf_t y, coordf_t z); void scale(const Vec3d &versor); + void scale(const double s) { this->scale(Vec3d(s, s, s)); } void rotate(float angle, const Axis &axis); void rotate(float angle, const Vec3d& axis); void mirror(const Axis &axis); @@ -132,22 +157,28 @@ public: bool needed_repair() const; void cut(coordf_t z, Model* model) const; void split(ModelObjectPtrs* new_objects); + void repair(); // Called by Print::validate() from the UI thread. - void check_instances_print_volume_state(const BoundingBoxf3& print_volume); + unsigned int check_instances_print_volume_state(const BoundingBoxf3& print_volume); // Print object statistics to console. void print_info() const; - -private: + +protected: + friend class Print; + // 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* clone(Model *parent); + 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(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; + Model *m_model; // Bounding box, cached. mutable BoundingBoxf3 m_bounding_box; @@ -156,20 +187,20 @@ private: // 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 +class ModelVolume : public ModelBase { friend class ModelObject; // The convex hull of this model's mesh. - TriangleMesh m_convex_hull; + TriangleMesh m_convex_hull; public: - std::string name; + std::string name; // The triangular model. - TriangleMesh mesh; + 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; + DynamicPrintConfig config; enum Type { MODEL_TYPE_INVALID = -1, @@ -179,6 +210,9 @@ public: SUPPORT_BLOCKER, }; + // Clone this ModelVolume, keep the ID identical, set the parent to the cloned volume. + ModelVolume* clone(ModelObject *parent) { return new ModelVolume(parent, *this); } + // A parent object owning this modifier volume. ModelObject* get_object() const { return this->object; }; Type type() const { return m_type; } @@ -187,20 +221,21 @@ public: 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); + 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); + size_t split(unsigned int max_extruders); - ModelMaterial* assign_unique_material(); + ModelMaterial* assign_unique_material(); - void calculate_convex_hull(); + void calculate_convex_hull(); const TriangleMesh& get_convex_hull() const; - TriangleMesh& get_convex_hull(); + TriangleMesh& get_convex_hull(); // Helpers for loading / storing into AMF / 3MF files. static Type type_from_string(const std::string &s); @@ -211,23 +246,26 @@ private: ModelObject* object; // Is it an object to be printed, or a modifier volume? Type m_type; - t_model_material_id _material_id; + t_model_material_id m_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, 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) : + 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) { - this->material_id(other.material_id()); + this->set_material_id(other.material_id()); } ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : + ModelBase(other), // copy the ID name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object) { - this->material_id(other.material_id()); + this->set_material_id(other.material_id()); if (mesh.stl.stats.number_of_facets > 1) calculate_convex_hull(); } @@ -235,7 +273,7 @@ private: // A single instance of a ModelObject. // Knows the affine transformation of an object. -class ModelInstance +class ModelInstance : public ModelBase { public: enum EPrintVolumeState : unsigned char @@ -248,25 +286,20 @@ public: friend class ModelObject; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM private: 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 +#if ENABLE_MIRROR + Vec3d m_mirror; // Mirroring along the three axes +#endif // ENABLE_MIRROR public: -#else - double rotation; // Rotation around the Z axis, in radians around mesh center point - double scaling_factor; - Vec2d offset; // in unscaled coordinates -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - // 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_MODELINSTANCE_3D_FULL_TRANSFORM const Vec3d& get_offset() const { return m_offset; } double get_offset(Axis axis) const { return m_offset(axis); } @@ -282,9 +315,21 @@ public: Vec3d get_scaling_factor() const { return m_scaling_factor; } double get_scaling_factor(Axis axis) const { return m_scaling_factor(axis); } +#if ENABLE_MIRROR + void set_scaling_factor(const Vec3d& scaling_factor); + void set_scaling_factor(Axis axis, double scaling_factor); +#else void set_scaling_factor(const Vec3d& scaling_factor) { m_scaling_factor = scaling_factor; } void set_scaling_factor(Axis axis, double scaling_factor) { m_scaling_factor(axis) = scaling_factor; } -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#endif // ENABLE_MIRROR + +#if ENABLE_MIRROR + 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_MIRROR // To be called on an external mesh void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; @@ -297,7 +342,11 @@ public: // 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_MIRROR + Transform3d world_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const; +#else Transform3d world_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false) const; +#endif // ENABLE_MIRROR bool is_printable() const { return print_volume_state == PVS_Inside; } @@ -305,15 +354,15 @@ private: // Parent object, owning this instance. ModelObject* object; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_MIRROR + 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) {} + 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) {} +#else ModelInstance(ModelObject *object) : m_rotation(Vec3d::Zero()), m_scaling_factor(Vec3d::Ones()), m_offset(Vec3d::Zero()), object(object), print_volume_state(PVS_Inside) {} ModelInstance(ModelObject *object, const ModelInstance &other) : m_rotation(other.m_rotation), m_scaling_factor(other.m_scaling_factor), m_offset(other.m_offset), object(object), print_volume_state(PVS_Inside) {} -#else - ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), offset(Vec2d::Zero()), 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) {} -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#endif // ENABLE_MIRROR }; @@ -322,7 +371,7 @@ private: // and with multiple modifier meshes. // A model groups multiple objects, each object having possibly multiple instances, // all objects may share mutliple materials. -class Model +class Model : public ModelBase { static unsigned int s_auto_extruder_id; @@ -334,13 +383,17 @@ public: ModelObjectPtrs objects; Model() {} - Model(const Model &other); - Model& operator= (Model other); - void swap(Model &other); + Model(const Model &rhs); + Model& operator=(const Model &rhs); ~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); + // XXX: use fs::path ? + 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(); ModelObject* add_object(); ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh); @@ -362,6 +415,9 @@ public: 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); void 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; diff --git a/xs/src/libslic3r/ModelArrange.hpp b/src/libslic3r/ModelArrange.hpp similarity index 95% rename from xs/src/libslic3r/ModelArrange.hpp rename to src/libslic3r/ModelArrange.hpp index f8beb668ae..dffb951ddf 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/src/libslic3r/ModelArrange.hpp @@ -29,12 +29,8 @@ std::string toString(const Model& model, bool holes = true) { if(!objinst) continue; Slic3r::TriangleMesh tmpmesh = rmesh; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM // CHECK_ME -> Is the following correct ? tmpmesh.scale(objinst->get_scaling_factor()); -#else - tmpmesh.scale(objinst->scaling_factor); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM objinst->transform_mesh(&tmpmesh); ExPolygons expolys = tmpmesh.horizontal_projection(); for(auto& expoly_complex : expolys) { @@ -92,11 +88,7 @@ void toSVG(SVG& svg, const Model& model) { if(!objinst) continue; Slic3r::TriangleMesh tmpmesh = rmesh; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM tmpmesh.scale(objinst->get_scaling_factor()); -#else - tmpmesh.scale(objinst->scaling_factor); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM objinst->transform_mesh(&tmpmesh); ExPolygons expolys = tmpmesh.horizontal_projection(); svg.draw(expolys); @@ -522,12 +514,8 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { Slic3r::TriangleMesh tmpmesh = rmesh; ClipperLib::PolygonImpl pn; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM // CHECK_ME -> is the following correct ? tmpmesh.scale(objinst->get_scaling_factor()); -#else - tmpmesh.scale(objinst->scaling_factor); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM // TODO export the exact 2D projection auto p = tmpmesh.convex_hull(); @@ -541,20 +529,11 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { // Invalid geometries would throw exceptions when arranging if(item.vertexCount() > 3) { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM // CHECK_ME -> is the following correct or it should take in account all three rotations ? item.rotation(objinst->get_rotation(Z)); -#else - item.rotation(objinst->rotation); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM item.translation({ -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM ClipperLib::cInt(objinst->get_offset(X)/SCALING_FACTOR), ClipperLib::cInt(objinst->get_offset(Y)/SCALING_FACTOR) -#else - ClipperLib::cInt(objinst->offset(0)/SCALING_FACTOR), - ClipperLib::cInt(objinst->offset(1)/SCALING_FACTOR) -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM }); ret.emplace_back(objinst, item); } @@ -691,7 +670,6 @@ void applyResult( // appropriately auto off = item.translation(); Radians rot = item.rotation(); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM Vec3d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR, 0.0); @@ -699,13 +677,6 @@ void applyResult( // write the transformation data into the model instance inst_ptr->set_rotation(Z, rot); inst_ptr->set_offset(foff); -#else - Vec2d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR); - - // write the transformation data into the model instance - inst_ptr->rotation = rot; - inst_ptr->offset = foff; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM } } diff --git a/xs/src/libslic3r/MotionPlanner.cpp b/src/libslic3r/MotionPlanner.cpp similarity index 99% rename from xs/src/libslic3r/MotionPlanner.cpp rename to src/libslic3r/MotionPlanner.cpp index ff3475ed8f..198c39f311 100644 --- a/xs/src/libslic3r/MotionPlanner.cpp +++ b/src/libslic3r/MotionPlanner.cpp @@ -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 98% rename from xs/src/libslic3r/MultiPoint.cpp rename to src/libslic3r/MultiPoint.cpp index 45b354d63a..6e49be958f 100644 --- a/xs/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -14,7 +14,6 @@ void MultiPoint::scale(double factor) pt *= factor; } -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM void MultiPoint::scale(double factor_x, double factor_y) { for (Point &pt : points) @@ -23,7 +22,6 @@ void MultiPoint::scale(double factor_x, double factor_y) pt(1) *= factor_y; } } -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM void MultiPoint::translate(double x, double y) { diff --git a/xs/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp similarity index 97% rename from xs/src/libslic3r/MultiPoint.hpp rename to src/libslic3r/MultiPoint.hpp index 288b3137ea..9a7bd83e56 100644 --- a/xs/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -26,9 +26,7 @@ public: 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); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM void scale(double factor_x, double factor_y); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM void translate(double x, double y); void translate(const Point &vector); void rotate(double angle) { this->rotate(cos(angle), sin(angle)); } 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 100% rename from xs/src/libslic3r/PerimeterGenerator.cpp rename to src/libslic3r/PerimeterGenerator.cpp diff --git a/xs/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp similarity index 100% rename from xs/src/libslic3r/PerimeterGenerator.hpp rename to src/libslic3r/PerimeterGenerator.hpp diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp similarity index 98% rename from xs/src/libslic3r/PlaceholderParser.cpp rename to src/libslic3r/PlaceholderParser.cpp index cc6f1c75e4..331a42614c 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -100,22 +100,33 @@ void PlaceholderParser::update_timestamp(DynamicConfig &config) // 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() diff --git a/xs/src/libslic3r/PlaceholderParser.hpp b/src/libslic3r/PlaceholderParser.hpp similarity index 96% rename from xs/src/libslic3r/PlaceholderParser.hpp rename to src/libslic3r/PlaceholderParser.hpp index 49d53ec9ec..d833969fca 100644 --- a/xs/src/libslic3r/PlaceholderParser.hpp +++ b/src/libslic3r/PlaceholderParser.hpp @@ -14,7 +14,8 @@ class PlaceholderParser public: PlaceholderParser(); - 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. diff --git a/xs/src/libslic3r/Point.cpp b/src/libslic3r/Point.cpp similarity index 100% rename from xs/src/libslic3r/Point.cpp rename to src/libslic3r/Point.cpp diff --git a/xs/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp similarity index 97% rename from xs/src/libslic3r/Point.hpp rename to src/libslic3r/Point.hpp index 6d9d82d25a..1001781080 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -56,6 +56,11 @@ 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(v(0), v(1), 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))); } diff --git a/xs/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp similarity index 100% rename from xs/src/libslic3r/Polygon.cpp rename to src/libslic3r/Polygon.cpp diff --git a/xs/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp similarity index 100% rename from xs/src/libslic3r/Polygon.hpp rename to src/libslic3r/Polygon.hpp diff --git a/xs/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp similarity index 100% rename from xs/src/libslic3r/Polyline.cpp rename to src/libslic3r/Polyline.cpp diff --git a/xs/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp similarity index 100% rename from xs/src/libslic3r/Polyline.hpp rename to src/libslic3r/Polyline.hpp diff --git a/xs/src/libslic3r/PolylineCollection.cpp b/src/libslic3r/PolylineCollection.cpp similarity index 100% rename from xs/src/libslic3r/PolylineCollection.cpp rename to src/libslic3r/PolylineCollection.cpp 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/xs/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp similarity index 65% rename from xs/src/libslic3r/Print.cpp rename to src/libslic3r/Print.cpp index 37b55f87df..f87d7d309a 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -357,6 +357,14 @@ std::vector Print::extruders() const 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) { @@ -376,6 +384,16 @@ double Print::max_allowed_layer_height() const 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) @@ -389,15 +407,18 @@ void Print::add_model_object(ModelObject* model_object, int idx) } else m_objects.emplace_back(object); // Invalidate all print steps. - //FIXME lock mutex! this->invalidate_all_steps(); + // Set the transformation matrix without translation from the first instance. + if (! model_object->instances.empty()) + object->set_trafo(model_object->instances.front()->world_matrix(true)); + 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); + 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) @@ -424,35 +445,7 @@ void Print::add_model_object(ModelObject* model_object, int idx) object->config_apply(src_normalized, true); } - // update 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(); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - // 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) + "%"); -#else - v_scale.push_back(boost::lexical_cast(mobj.instances[0]->scaling_factor * 100) + "%"); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - 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); - } - } + this->update_object_placeholders(); } bool Print::apply_config(DynamicPrintConfig config) @@ -514,12 +507,12 @@ bool Print::apply_config(DynamicPrintConfig config) // 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))) { + 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 = this->_region_config_from_model_volume(volume); + 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) { @@ -571,6 +564,578 @@ exit_for_rearrange_regions: 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_old]; + 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! + //FIXME test for the transformation matrices! + ++ 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; +} + +static inline void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src) +{ + // 1) Delete the support volumes from model_object_dst. + { + std::vector dst; + dst.reserve(model_object_dst.volumes.size()); + for (ModelVolume *vol : model_object_dst.volumes) { + if (vol->is_support_modifier()) + dst.emplace_back(vol); + else + delete vol; + } + model_object_dst.volumes = std::move(dst); + } + // 2) Copy the support volumes from model_object_src to the end of model_object_dst. + for (ModelVolume *vol : model_object_src.volumes) { + if (vol->is_support_modifier()) + model_object_dst.volumes.emplace_back(vol->clone(&model_object_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()) { + const Vec3d &offst = model_instance->get_offset(); + trafo.trafo = model_instance->world_matrix(true); + trafo.copies.front() = Point::new_scale(offst(0), offst(1)); + 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()); +} + +Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in) +{ + // 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(m_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; + t_config_option_keys object_config_diff; + // 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. + m_cancel_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 = 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(model.objects[i]->clone(&m_model)); + } + } else { + // Reorder the objects, add new objects. + // First stop background processing before shuffling or deleting the PrintObjects in the object list. + m_cancel_callback(); + 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((*it)->clone(&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, + 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, reset the parent. + model_object = model_object_new; + model_object.set_model(&m_model); + } else if (support_blockers_differ || support_enforcers_differ) { + // First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list. + m_cancel_callback(); + // 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 the remaining data of ModelVolumes (name, config, m_type, m_material_id) + // Synchronize Object's config. + t_config_option_keys &this_object_config_diff = const_cast(*it_status).object_config_diff; + this_object_config_diff = model_object.config.diff(model_object_new.config); + if (! this_object_config_diff.empty()) + model_object.config.apply_only(model_object_new.config, this_object_config_diff, true); + if (! object_diff.empty() || ! this_object_config_diff.empty()) { + 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); + } + } + } + model_object.name = model_object_new.name; + model_object.input_file = model_object_new.input_file; + model_object.clear_instances(); + for (const ModelInstance *model_instance : model_object_new.instances) + model_object.add_instance(*model_instance); + } + } + + // 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())); + // 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, model_object->raw_bounding_box()); + 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)); + } + 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, model_object->raw_bounding_box()); + 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)); + } else if ((*it_old)->print_object->copies() != new_instances.copies) { + // 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)); + update_apply_status(this->invalidate_step(psSkirt) || this->invalidate_step(psBrim) || this->invalidate_step(psGCodeExport)); + (*it_old)->print_object->set_copies(new_instances.copies); + print_objects_new.emplace_back((*it_old)->print_object); + } + } + } + if (m_objects != print_objects_new) { + m_cancel_callback(); + m_objects = print_objects_new; + update_apply_status(false); + } + } + + // 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]; + ++ 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. + for (int i = 0; i < (int)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)) { + for (region_id = 0; region_id < m_regions.size(); ++ region_id) + if (m_regions[region_id]->m_refcnt == 0) { + // An empty slot was found. + m_regions[region_id]->set_config(std::move(config)); + break; + } + if (region_id == m_regions.size()) + this->add_region(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 (print_object.region_volumes.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(); + 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) @@ -585,23 +1150,7 @@ bool Print::has_skirt() const std::string Print::validate() const { - BoundingBox bed_box_2D = get_extents(Polygon::new_scale(m_config.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_(m_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(2) = -1e10; - unsigned int printable_count = 0; - { - // Lock due to the po->reload_model_instances() - tbb::mutex::scoped_lock lock(m_mutex); - for (PrintObject *po : m_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) + if (m_objects.empty()) return L("All objects are outside of the print volume."); if (m_config.complete_objects) { @@ -875,16 +1424,6 @@ Flow Print::skirt_flow() const ); } -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 : m_objects) diff --git a/xs/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp similarity index 89% rename from xs/src/libslic3r/Print.hpp rename to src/libslic3r/Print.hpp index 7f88110bc3..767106336a 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -86,7 +86,7 @@ public: // 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 invalidate(StepType step, tbb::mutex &mtx, CancelationCallback cancel) { bool invalidated = m_state[step].load(std::memory_order_relaxed) != INVALID; if (invalidated) { #if 0 @@ -94,8 +94,11 @@ public: 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; @@ -106,7 +109,7 @@ public: // 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 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) { @@ -139,19 +142,25 @@ public: // 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; + coordf_t bridging_height_avg(const PrintConfig &print_config) const; // Methods modifying the PrintRegion's state: public: Print* print() { return m_print; } - 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_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_print(print) {} - PrintRegion(Print* print, const PrintRegionConfig &config) : m_print(print), m_config(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() {} }; @@ -195,12 +204,16 @@ public: const LayerPtrs& layers() const { return m_layers; } const SupportLayerPtrs& support_layers() const { return m_support_layers; } - const Points& copies() const { return m_copies; } - bool add_copy(const Vec2d &point); - bool delete_last_copy(); - bool delete_all_copies() { return this->set_copies(Points()); } - bool set_copies(const Points &points); - bool reload_model_instances(); + const Transform3d& trafo() const { return m_trafo; } + void set_trafo(const Transform3d& trafo) { m_trafo = trafo; } + + const Points& copies() const { return m_copies; } + bool add_copy(const Vec2d &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), to_2d(this->size)); } @@ -285,6 +298,8 @@ private: Print *m_print; 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 @@ -382,6 +397,17 @@ public: bool reload_model_instances(); void add_model_object(ModelObject* model_object, int idx = -1); bool apply_config(DynamicPrintConfig config); + 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, + }; + ApplyStatus apply(const Model &model, const DynamicPrintConfig &config); + void process(); void export_gcode(const std::string &path_template, GCodePreviewData *preview_data); // SLA export, temporary. @@ -416,9 +442,14 @@ public: 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; } - const PrintObject* get_object(int idx) const { return m_objects[idx]; } + 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); @@ -475,8 +506,10 @@ protected: PrintRegion* add_region(const PrintRegionConfig &config); 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); - PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume); // 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. @@ -501,6 +534,7 @@ private: // Callback to be evoked to stop the background processing before a state is updated. cancel_callback_type m_cancel_callback = [](){}; + Model m_model; PrintConfig m_config; PrintObjectConfig m_default_object_config; PrintRegionConfig m_default_region_config; diff --git a/xs/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp similarity index 92% rename from xs/src/libslic3r/PrintConfig.cpp rename to src/libslic3r/PrintConfig.cpp index ed02f6d439..4e1de09919 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -39,6 +39,7 @@ void PrintConfigDef::init_common_params() 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); @@ -56,6 +57,7 @@ void PrintConfigDef::init_common_params() 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); } @@ -73,6 +75,7 @@ void PrintConfigDef::init_fff_params() "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_temperature", coInts); @@ -94,6 +97,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -103,6 +107,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -121,6 +126,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -132,6 +138,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -141,6 +148,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -153,6 +161,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -171,6 +180,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -179,10 +189,12 @@ void PrintConfigDef::init_fff_params() "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("compatible_printers", coStrings); def->label = L("Compatible printers"); + def->mode = comAdvanced; def->default_value = new ConfigOptionStrings(); def = this->add("compatible_printers_condition", coString); @@ -190,6 +202,7 @@ void PrintConfigDef::init_fff_params() 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 ...) @@ -204,6 +217,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -219,6 +233,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -227,6 +242,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -237,6 +253,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -259,6 +276,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -267,6 +285,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -286,6 +305,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -296,6 +316,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -307,6 +328,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -315,6 +337,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -346,6 +369,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -366,6 +390,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -375,6 +400,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -401,6 +427,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -412,6 +439,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -430,6 +458,7 @@ void PrintConfigDef::init_fff_params() "from the XY coordinate)."); def->sidetext = L("mm"); def->cli = "extruder-offset=s@"; + def->mode = comAdvanced; def->default_value = new ConfigOptionPoints { Vec2d(0,0) }; def = this->add("extrusion_axis", coString); @@ -446,6 +475,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -457,6 +487,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -475,6 +506,7 @@ void PrintConfigDef::init_fff_params() def->width = 60; def->min = 0; def->max = 1000; + def->mode = comExpert; def->default_value = new ConfigOptionInts { 60 }; def = this->add("filament_colour", coStrings); @@ -491,6 +523,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -501,6 +534,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -509,6 +543,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -517,6 +552,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -526,6 +562,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -534,6 +571,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -544,6 +582,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -553,6 +592,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -561,6 +601,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -572,6 +613,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -580,6 +622,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -588,12 +631,14 @@ void PrintConfigDef::init_fff_params() 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" }; @@ -603,6 +648,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -668,6 +714,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -751,6 +798,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -772,6 +820,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -794,6 +843,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -821,6 +871,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -850,6 +901,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -859,6 +911,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -870,6 +923,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -878,6 +932,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -889,12 +944,14 @@ void PrintConfigDef::init_fff_params() "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); @@ -904,6 +961,7 @@ void PrintConfigDef::init_fff_params() "(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); @@ -915,6 +973,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -946,6 +1005,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -957,6 +1017,7 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 50; + def->mode = comExpert; def->default_value = new ConfigOptionString(""); def = this->add("remaining_times", coBool); @@ -965,11 +1026,13 @@ void PrintConfigDef::init_fff_params() " 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; @@ -997,6 +1060,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1006,6 +1070,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1015,6 +1080,7 @@ void PrintConfigDef::init_fff_params() 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); } } @@ -1027,6 +1093,7 @@ void PrintConfigDef::init_fff_params() 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] @@ -1037,6 +1104,7 @@ void PrintConfigDef::init_fff_params() 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] @@ -1047,6 +1115,7 @@ void PrintConfigDef::init_fff_params() 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] @@ -1057,6 +1126,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1066,6 +1136,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1077,6 +1148,7 @@ void PrintConfigDef::init_fff_params() 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_speed", coFloat); @@ -1087,6 +1159,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1096,6 +1169,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1107,6 +1181,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1118,6 +1193,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1127,6 +1203,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1136,6 +1213,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1144,6 +1222,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1154,6 +1233,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1164,6 +1244,7 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 130; + def->mode = comAdvanced; def->default_value = new ConfigOptionString(""); def = this->add("nozzle_diameter", coFloats); @@ -1183,6 +1264,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1190,6 +1272,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1204,6 +1287,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1211,6 +1295,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1219,6 +1304,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -1229,6 +1315,7 @@ void PrintConfigDef::init_fff_params() "[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); @@ -1237,6 +1324,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1246,6 +1334,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1255,6 +1344,7 @@ void PrintConfigDef::init_fff_params() " 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); @@ -1264,6 +1354,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -1273,6 +1364,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1285,6 +1377,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1321,6 +1414,7 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 60; + def->mode = comExpert; def->default_value = new ConfigOptionStrings(); def = this->add("printer_model", coString); @@ -1335,6 +1429,7 @@ void PrintConfigDef::init_fff_params() def->multiline = true; def->full_width = true; def->height = 130; + def->mode = comAdvanced; def->default_value = new ConfigOptionString(""); def = this->add("printer_vendor", coString); @@ -1361,6 +1456,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1372,6 +1468,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1379,6 +1476,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1387,12 +1485,14 @@ void PrintConfigDef::init_fff_params() "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); @@ -1412,6 +1512,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -1430,6 +1531,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -1440,6 +1542,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -1448,6 +1551,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -1456,6 +1560,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -1464,6 +1569,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1473,6 +1579,7 @@ void PrintConfigDef::init_fff_params() "(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); @@ -1489,6 +1596,7 @@ void PrintConfigDef::init_fff_params() 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 @@ -1551,6 +1659,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -1561,6 +1670,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -1572,6 +1682,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1593,6 +1704,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1601,6 +1713,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1613,6 +1726,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1623,6 +1737,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -1664,6 +1779,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1679,6 +1795,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1695,18 +1812,21 @@ void PrintConfigDef::init_fff_params() 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); @@ -1722,6 +1842,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1733,6 +1854,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1744,6 +1866,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1751,6 +1874,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1767,6 +1891,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1780,6 +1905,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1789,6 +1915,7 @@ void PrintConfigDef::init_fff_params() "(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); @@ -1799,6 +1926,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -1806,6 +1934,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1815,6 +1944,7 @@ void PrintConfigDef::init_fff_params() "(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); @@ -1824,6 +1954,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1833,6 +1964,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1858,6 +1990,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1867,6 +2000,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1884,6 +2018,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1898,6 +2033,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1906,6 +2042,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1924,6 +2061,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1947,6 +2085,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1958,6 +2097,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -1990,6 +2130,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -1997,6 +2138,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -2004,6 +2146,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -2015,6 +2158,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -2022,6 +2166,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -2029,6 +2174,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -2036,6 +2182,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -2062,6 +2209,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -2069,6 +2217,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -2076,6 +2225,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -2083,6 +2233,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -2108,6 +2259,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -2118,6 +2270,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -2128,6 +2281,7 @@ void PrintConfigDef::init_fff_params() "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); @@ -2150,6 +2304,7 @@ void PrintConfigDef::init_fff_params() 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); @@ -2262,6 +2417,7 @@ void PrintConfigDef::init_sla_params() 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); @@ -2345,7 +2501,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() { @@ -2601,4 +2757,158 @@ StaticPrintConfig::StaticCache SLAMaterialConf 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 93% rename from xs/src/libslic3r/PrintConfig.hpp rename to src/libslic3r/PrintConfig.hpp index bc3b0ef490..c03ed25724 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -165,7 +165,7 @@ private: // 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. @@ -968,6 +968,95 @@ protected: #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; } + + 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; } + +private: + class PrintAndCLIConfigDef : public PrintConfigDef + { + public: + PrintAndCLIConfigDef() { this->options.insert(cli_config_def.options.begin(), cli_config_def.options.end()); } + }; + 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/xs/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp similarity index 100% rename from xs/src/libslic3r/PrintExport.hpp rename to src/libslic3r/PrintExport.hpp diff --git a/xs/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp similarity index 99% rename from xs/src/libslic3r/PrintObject.cpp rename to src/libslic3r/PrintObject.cpp index 4e72871fc1..b7b33087f4 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -87,23 +87,25 @@ bool PrintObject::delete_last_copy() bool PrintObject::set_copies(const Points &points) { - bool copies_num_changed = m_copies.size() != points.size(); - - // order copies with a nearest neighbor search and translate them by _copies_shift - m_copies.clear(); - m_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) - m_copies.push_back(points[point_idx] + m_copies_shift); - - bool invalidated = m_print->invalidate_step(psSkirt); - invalidated |= m_print->invalidate_step(psBrim); - if (copies_num_changed) - invalidated |= m_print->invalidate_step(psWipeTower); + // 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_step(psSkirt); + invalidated |= m_print->invalidate_step(psBrim); + if (copies.size() != m_copies.size()) + invalidated |= m_print->invalidate_step(psWipeTower); + invalidated |= m_print->invalidate_step(psGCodeExport); + m_copies = copies; + } return invalidated; } @@ -113,16 +115,11 @@ bool PrintObject::reload_model_instances() copies.reserve(m_model_object->instances.size()); for (const ModelInstance *mi : m_model_object->instances) { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM if (mi->is_printable()) { const Vec3d& offset = mi->get_offset(); copies.emplace_back(Point::new_scale(offset(0), offset(1))); } -#else - if (mi->is_printable()) - copies.emplace_back(Point::new_scale(mi->offset(0), mi->offset(1))); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM } return this->set_copies(copies); } @@ -1591,7 +1588,7 @@ std::vector PrintObject::slice_support_enforcers() const std::vector zs; zs.reserve(this->layers().size()); for (const Layer *l : this->layers()) - zs.emplace_back(l->slice_z); + zs.emplace_back((float)l->slice_z); return this->_slice_volumes(zs, volumes); } @@ -1604,7 +1601,7 @@ std::vector PrintObject::slice_support_blockers() const std::vector zs; zs.reserve(this->layers().size()); for (const Layer *l : this->layers()) - zs.emplace_back(l->slice_z); + zs.emplace_back((float)l->slice_z); return this->_slice_volumes(zs, volumes); } @@ -1618,10 +1615,7 @@ std::vector PrintObject::_slice_volumes(const std::vector &z, for (const ModelVolume *v : volumes) mesh.merge(v->mesh); 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); + mesh.transform(m_trafo.cast()); // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift mesh.translate(- unscale(m_copies_shift(0)), - unscale(m_copies_shift(1)), - float(this->model_object()->bounding_box().min(2))); // perform actual slicing diff --git a/xs/src/libslic3r/PrintRegion.cpp b/src/libslic3r/PrintRegion.cpp similarity index 100% rename from xs/src/libslic3r/PrintRegion.cpp rename to src/libslic3r/PrintRegion.cpp diff --git a/xs/src/libslic3r/Rasterizer/Rasterizer.cpp b/src/libslic3r/Rasterizer/Rasterizer.cpp similarity index 100% rename from xs/src/libslic3r/Rasterizer/Rasterizer.cpp rename to src/libslic3r/Rasterizer/Rasterizer.cpp diff --git a/xs/src/libslic3r/Rasterizer/Rasterizer.hpp b/src/libslic3r/Rasterizer/Rasterizer.hpp similarity index 100% rename from xs/src/libslic3r/Rasterizer/Rasterizer.hpp rename to src/libslic3r/Rasterizer/Rasterizer.hpp diff --git a/xs/src/libslic3r/SLABasePool.cpp b/src/libslic3r/SLABasePool.cpp similarity index 100% rename from xs/src/libslic3r/SLABasePool.cpp rename to src/libslic3r/SLABasePool.cpp diff --git a/xs/src/libslic3r/SLABasePool.hpp b/src/libslic3r/SLABasePool.hpp similarity index 100% rename from xs/src/libslic3r/SLABasePool.hpp rename to src/libslic3r/SLABasePool.hpp diff --git a/xs/src/libslic3r/SVG.cpp b/src/libslic3r/SVG.cpp similarity index 100% rename from xs/src/libslic3r/SVG.cpp rename to src/libslic3r/SVG.cpp 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 100% rename from xs/src/libslic3r/Slicing.cpp rename to src/libslic3r/Slicing.cpp 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 100% rename from xs/src/libslic3r/SlicingAdaptive.cpp rename to src/libslic3r/SlicingAdaptive.cpp 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 100% rename from xs/src/libslic3r/SupportMaterial.cpp rename to src/libslic3r/SupportMaterial.cpp diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/src/libslic3r/SupportMaterial.hpp similarity index 100% rename from xs/src/libslic3r/SupportMaterial.hpp rename to src/libslic3r/SupportMaterial.hpp diff --git a/xs/src/libslic3r/Surface.cpp b/src/libslic3r/Surface.cpp similarity index 100% rename from xs/src/libslic3r/Surface.cpp rename to src/libslic3r/Surface.cpp 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 100% rename from xs/src/libslic3r/SurfaceCollection.cpp rename to src/libslic3r/SurfaceCollection.cpp 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..ec775742b2 --- /dev/null +++ b/src/libslic3r/Technologies.hpp @@ -0,0 +1,29 @@ +#ifndef _technologies_h_ +#define _technologies_h_ + +//============== +// debug techs +//============== + +// Shows camera target in the 3D scene +#define ENABLE_SHOW_CAMERA_TARGET 1 + +//============== +// 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) +// New selections +#define ENABLE_EXTENDED_SELECTION (1 && ENABLE_1_42_0) +// Add mirror components along the three axes in ModelInstance and GLVolume +#define ENABLE_MIRROR (1 && ENABLE_1_42_0) +// Modified camera target behavior +#define ENABLE_MODIFIED_CAMERA_TARGET (1 && ENABLE_1_42_0) + +#endif // _technologies_h_ + + diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp similarity index 99% rename from xs/src/libslic3r/TriangleMesh.cpp rename to src/libslic3r/TriangleMesh.cpp index 4bf13330f6..c8a6e2130b 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -108,21 +108,23 @@ void 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; @@ -134,25 +136,31 @@ void 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 (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); } // 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; @@ -850,12 +858,12 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, 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) { diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp similarity index 100% rename from xs/src/libslic3r/TriangleMesh.hpp rename to src/libslic3r/TriangleMesh.hpp diff --git a/xs/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp similarity index 77% rename from xs/src/libslic3r/Utils.hpp rename to src/libslic3r/Utils.hpp index 313f93094f..b33ab6c248 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -90,22 +90,55 @@ Real round_nearest(Real value, unsigned int decimals) // 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; +} + +#ifdef __clang__ +// On clang, the size_t is a type of its own, so we need to overload for size_t. +// On MSC, the size_t type aliases to uint64_t / uint32_t, so the following code +// gives a duplicate symbol error. +inline size_t next_highest_power_of_2(size_t v) +{ +#if SSIZE_MAX == 9223372036854775807 + static_assert(sizeof(size_t) == sizeof(uint64_t), "sizeof(size_t) == sizeof(uint64_t)"); + return next_highest_power_of_2(uint64_t(v)); +#else + static_assert(sizeof(size_t) == sizeof(uint32_t), "sizeof(size_t) == sizeof(uint32_t)"); + return next_highest_power_of_2(uint32_t(v)); +#endif +} +#endif extern std::string xml_escape(std::string text); diff --git a/xs/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h similarity index 97% rename from xs/src/libslic3r/libslic3r.h rename to src/libslic3r/libslic3r.h index 5f2f3fba17..a2ee3fc9fa 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 @@ -15,10 +17,6 @@ #include "Technologies.hpp" -#define SLIC3R_FORK_NAME "Slic3r Prusa Edition" -#define SLIC3R_VERSION "1.41.0" -#define SLIC3R_BUILD "UNKNOWN" - typedef int32_t coord_t; typedef double coordf_t; 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/xs/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp similarity index 100% rename from xs/src/libslic3r/utils.cpp rename to src/libslic3r/utils.cpp 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/xs/src/png/AUTHORS b/src/png/AUTHORS similarity index 100% rename from xs/src/png/AUTHORS rename to src/png/AUTHORS diff --git a/xs/src/png/COPYING b/src/png/COPYING similarity index 100% rename from xs/src/png/COPYING rename to src/png/COPYING diff --git a/xs/src/png/NEWS b/src/png/NEWS similarity index 100% rename from xs/src/png/NEWS rename to src/png/NEWS diff --git a/xs/src/png/color.hpp b/src/png/color.hpp similarity index 100% rename from xs/src/png/color.hpp rename to src/png/color.hpp diff --git a/xs/src/png/end_info.hpp b/src/png/end_info.hpp similarity index 100% rename from xs/src/png/end_info.hpp rename to src/png/end_info.hpp diff --git a/xs/src/png/error.hpp b/src/png/error.hpp similarity index 100% rename from xs/src/png/error.hpp rename to src/png/error.hpp diff --git a/xs/src/png/image_info.hpp b/src/png/image_info.hpp similarity index 100% rename from xs/src/png/image_info.hpp rename to src/png/image_info.hpp diff --git a/xs/src/png/info.hpp b/src/png/info.hpp similarity index 100% rename from xs/src/png/info.hpp rename to src/png/info.hpp diff --git a/xs/src/png/info_base.hpp b/src/png/info_base.hpp similarity index 100% rename from xs/src/png/info_base.hpp rename to src/png/info_base.hpp diff --git a/xs/src/png/io_base.hpp b/src/png/io_base.hpp similarity index 100% rename from xs/src/png/io_base.hpp rename to src/png/io_base.hpp diff --git a/xs/src/png/libpng/ANNOUNCE b/src/png/libpng/ANNOUNCE similarity index 100% rename from xs/src/png/libpng/ANNOUNCE rename to src/png/libpng/ANNOUNCE diff --git a/xs/src/png/libpng/CMakeLists.txt b/src/png/libpng/CMakeLists.txt similarity index 98% rename from xs/src/png/libpng/CMakeLists.txt rename to src/png/libpng/CMakeLists.txt index 266b67d0e2..7697110c72 100644 --- a/xs/src/png/libpng/CMakeLists.txt +++ b/src/png/libpng/CMakeLists.txt @@ -107,7 +107,7 @@ endif() # set definitions and sources for powerpc if(CMAKE_SYSTEM_PROCESSOR MATCHES "^powerpc*" OR - CMAKE_SYSTEM_PROCESSOR MATCHES "^ppc64*" ) + 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.") @@ -131,7 +131,7 @@ endif() # set definitions and sources for intel if(CMAKE_SYSTEM_PROCESSOR MATCHES "^i?86" OR - CMAKE_SYSTEM_PROCESSOR MATCHES "^x86_64*" ) + 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") @@ -155,7 +155,7 @@ endif() # set definitions and sources for MIPS if(CMAKE_SYSTEM_PROCESSOR MATCHES "mipsel*" OR - CMAKE_SYSTEM_PROCESSOR MATCHES "mips64el*" ) + 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") @@ -770,14 +770,14 @@ function(CREATE_SYMLINK DEST_FILE) 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}" - ) + 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}" - ) + 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) @@ -788,12 +788,12 @@ function(CREATE_SYMLINK DEST_FILE) 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} - ) + 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} - ) + COMMAND "${CMAKE_COMMAND}" -E create_symlink $ $/${DEST_FILE} + ) endif(CMAKE_HOST_WIN32 AND NOT CYGWIN AND NOT MSYS) endif(S_TARGET) diff --git a/xs/src/png/libpng/LICENSE b/src/png/libpng/LICENSE similarity index 100% rename from xs/src/png/libpng/LICENSE rename to src/png/libpng/LICENSE diff --git a/xs/src/png/libpng/arm/arm_init.c b/src/png/libpng/arm/arm_init.c similarity index 100% rename from xs/src/png/libpng/arm/arm_init.c rename to src/png/libpng/arm/arm_init.c diff --git a/xs/src/png/libpng/arm/filter_neon.S b/src/png/libpng/arm/filter_neon.S similarity index 100% rename from xs/src/png/libpng/arm/filter_neon.S rename to src/png/libpng/arm/filter_neon.S diff --git a/xs/src/png/libpng/arm/filter_neon_intrinsics.c b/src/png/libpng/arm/filter_neon_intrinsics.c similarity index 100% rename from xs/src/png/libpng/arm/filter_neon_intrinsics.c rename to src/png/libpng/arm/filter_neon_intrinsics.c diff --git a/xs/src/png/libpng/intel/filter_sse2_intrinsics.c b/src/png/libpng/intel/filter_sse2_intrinsics.c similarity index 100% rename from xs/src/png/libpng/intel/filter_sse2_intrinsics.c rename to src/png/libpng/intel/filter_sse2_intrinsics.c diff --git a/xs/src/png/libpng/intel/intel_init.c b/src/png/libpng/intel/intel_init.c similarity index 100% rename from xs/src/png/libpng/intel/intel_init.c rename to src/png/libpng/intel/intel_init.c diff --git a/xs/src/png/libpng/libpng-config.in b/src/png/libpng/libpng-config.in similarity index 100% rename from xs/src/png/libpng/libpng-config.in rename to src/png/libpng/libpng-config.in diff --git a/xs/src/png/libpng/libpng.pc.in b/src/png/libpng/libpng.pc.in similarity index 100% rename from xs/src/png/libpng/libpng.pc.in rename to src/png/libpng/libpng.pc.in diff --git a/xs/src/png/libpng/mips/filter_msa_intrinsics.c b/src/png/libpng/mips/filter_msa_intrinsics.c similarity index 100% rename from xs/src/png/libpng/mips/filter_msa_intrinsics.c rename to src/png/libpng/mips/filter_msa_intrinsics.c diff --git a/xs/src/png/libpng/mips/mips_init.c b/src/png/libpng/mips/mips_init.c similarity index 100% rename from xs/src/png/libpng/mips/mips_init.c rename to src/png/libpng/mips/mips_init.c diff --git a/xs/src/png/libpng/png.c b/src/png/libpng/png.c similarity index 100% rename from xs/src/png/libpng/png.c rename to src/png/libpng/png.c diff --git a/xs/src/png/libpng/png.h b/src/png/libpng/png.h similarity index 100% rename from xs/src/png/libpng/png.h rename to src/png/libpng/png.h diff --git a/xs/src/png/libpng/pngconf.h b/src/png/libpng/pngconf.h similarity index 100% rename from xs/src/png/libpng/pngconf.h rename to src/png/libpng/pngconf.h diff --git a/xs/src/png/libpng/pngdebug.h b/src/png/libpng/pngdebug.h similarity index 100% rename from xs/src/png/libpng/pngdebug.h rename to src/png/libpng/pngdebug.h diff --git a/xs/src/png/libpng/pngerror.c b/src/png/libpng/pngerror.c similarity index 100% rename from xs/src/png/libpng/pngerror.c rename to src/png/libpng/pngerror.c diff --git a/xs/src/png/libpng/pngget.c b/src/png/libpng/pngget.c similarity index 100% rename from xs/src/png/libpng/pngget.c rename to src/png/libpng/pngget.c diff --git a/xs/src/png/libpng/pnginfo.h b/src/png/libpng/pnginfo.h similarity index 100% rename from xs/src/png/libpng/pnginfo.h rename to src/png/libpng/pnginfo.h diff --git a/xs/src/png/libpng/pngmem.c b/src/png/libpng/pngmem.c similarity index 100% rename from xs/src/png/libpng/pngmem.c rename to src/png/libpng/pngmem.c diff --git a/xs/src/png/libpng/pngpread.c b/src/png/libpng/pngpread.c similarity index 100% rename from xs/src/png/libpng/pngpread.c rename to src/png/libpng/pngpread.c diff --git a/xs/src/png/libpng/pngpriv.h b/src/png/libpng/pngpriv.h similarity index 100% rename from xs/src/png/libpng/pngpriv.h rename to src/png/libpng/pngpriv.h diff --git a/xs/src/png/libpng/pngread.c b/src/png/libpng/pngread.c similarity index 100% rename from xs/src/png/libpng/pngread.c rename to src/png/libpng/pngread.c diff --git a/xs/src/png/libpng/pngrio.c b/src/png/libpng/pngrio.c similarity index 100% rename from xs/src/png/libpng/pngrio.c rename to src/png/libpng/pngrio.c diff --git a/xs/src/png/libpng/pngrtran.c b/src/png/libpng/pngrtran.c similarity index 100% rename from xs/src/png/libpng/pngrtran.c rename to src/png/libpng/pngrtran.c diff --git a/xs/src/png/libpng/pngrutil.c b/src/png/libpng/pngrutil.c similarity index 100% rename from xs/src/png/libpng/pngrutil.c rename to src/png/libpng/pngrutil.c diff --git a/xs/src/png/libpng/pngset.c b/src/png/libpng/pngset.c similarity index 100% rename from xs/src/png/libpng/pngset.c rename to src/png/libpng/pngset.c diff --git a/xs/src/png/libpng/pngstruct.h b/src/png/libpng/pngstruct.h similarity index 100% rename from xs/src/png/libpng/pngstruct.h rename to src/png/libpng/pngstruct.h diff --git a/xs/src/png/libpng/pngtest.c b/src/png/libpng/pngtest.c similarity index 100% rename from xs/src/png/libpng/pngtest.c rename to src/png/libpng/pngtest.c diff --git a/xs/src/png/libpng/pngtrans.c b/src/png/libpng/pngtrans.c similarity index 100% rename from xs/src/png/libpng/pngtrans.c rename to src/png/libpng/pngtrans.c diff --git a/xs/src/png/libpng/pngusr.dfa b/src/png/libpng/pngusr.dfa similarity index 100% rename from xs/src/png/libpng/pngusr.dfa rename to src/png/libpng/pngusr.dfa diff --git a/xs/src/png/libpng/pngwio.c b/src/png/libpng/pngwio.c similarity index 100% rename from xs/src/png/libpng/pngwio.c rename to src/png/libpng/pngwio.c diff --git a/xs/src/png/libpng/pngwrite.c b/src/png/libpng/pngwrite.c similarity index 100% rename from xs/src/png/libpng/pngwrite.c rename to src/png/libpng/pngwrite.c diff --git a/xs/src/png/libpng/pngwtran.c b/src/png/libpng/pngwtran.c similarity index 100% rename from xs/src/png/libpng/pngwtran.c rename to src/png/libpng/pngwtran.c diff --git a/xs/src/png/libpng/pngwutil.c b/src/png/libpng/pngwutil.c similarity index 100% rename from xs/src/png/libpng/pngwutil.c rename to src/png/libpng/pngwutil.c diff --git a/xs/src/png/libpng/powerpc/filter_vsx_intrinsics.c b/src/png/libpng/powerpc/filter_vsx_intrinsics.c similarity index 100% rename from xs/src/png/libpng/powerpc/filter_vsx_intrinsics.c rename to src/png/libpng/powerpc/filter_vsx_intrinsics.c diff --git a/xs/src/png/libpng/powerpc/powerpc_init.c b/src/png/libpng/powerpc/powerpc_init.c similarity index 100% rename from xs/src/png/libpng/powerpc/powerpc_init.c rename to src/png/libpng/powerpc/powerpc_init.c diff --git a/xs/src/png/libpng/scripts/checksym.awk b/src/png/libpng/scripts/checksym.awk old mode 100755 new mode 100644 similarity index 100% rename from xs/src/png/libpng/scripts/checksym.awk rename to src/png/libpng/scripts/checksym.awk diff --git a/xs/src/png/libpng/scripts/def.c b/src/png/libpng/scripts/def.c similarity index 100% rename from xs/src/png/libpng/scripts/def.c rename to src/png/libpng/scripts/def.c diff --git a/xs/src/png/libpng/scripts/dfn.awk b/src/png/libpng/scripts/dfn.awk old mode 100755 new mode 100644 similarity index 100% rename from xs/src/png/libpng/scripts/dfn.awk rename to src/png/libpng/scripts/dfn.awk diff --git a/xs/src/png/libpng/scripts/genchk.cmake.in b/src/png/libpng/scripts/genchk.cmake.in similarity index 100% rename from xs/src/png/libpng/scripts/genchk.cmake.in rename to src/png/libpng/scripts/genchk.cmake.in diff --git a/xs/src/png/libpng/scripts/genout.cmake.in b/src/png/libpng/scripts/genout.cmake.in similarity index 100% rename from xs/src/png/libpng/scripts/genout.cmake.in rename to src/png/libpng/scripts/genout.cmake.in diff --git a/xs/src/png/libpng/scripts/gensrc.cmake.in b/src/png/libpng/scripts/gensrc.cmake.in similarity index 100% rename from xs/src/png/libpng/scripts/gensrc.cmake.in rename to src/png/libpng/scripts/gensrc.cmake.in diff --git a/xs/src/png/libpng/scripts/intprefix.c b/src/png/libpng/scripts/intprefix.c similarity index 100% rename from xs/src/png/libpng/scripts/intprefix.c rename to src/png/libpng/scripts/intprefix.c diff --git a/xs/src/png/libpng/scripts/libpng-config-body.in b/src/png/libpng/scripts/libpng-config-body.in similarity index 100% rename from xs/src/png/libpng/scripts/libpng-config-body.in rename to src/png/libpng/scripts/libpng-config-body.in diff --git a/xs/src/png/libpng/scripts/libpng-config-head.in b/src/png/libpng/scripts/libpng-config-head.in similarity index 100% rename from xs/src/png/libpng/scripts/libpng-config-head.in rename to src/png/libpng/scripts/libpng-config-head.in diff --git a/xs/src/png/libpng/scripts/libpng.pc.in b/src/png/libpng/scripts/libpng.pc.in similarity index 100% rename from xs/src/png/libpng/scripts/libpng.pc.in rename to src/png/libpng/scripts/libpng.pc.in diff --git a/xs/src/png/libpng/scripts/options.awk b/src/png/libpng/scripts/options.awk old mode 100755 new mode 100644 similarity index 100% rename from xs/src/png/libpng/scripts/options.awk rename to src/png/libpng/scripts/options.awk diff --git a/xs/src/png/libpng/scripts/pnglibconf.dfa b/src/png/libpng/scripts/pnglibconf.dfa similarity index 100% rename from xs/src/png/libpng/scripts/pnglibconf.dfa rename to src/png/libpng/scripts/pnglibconf.dfa diff --git a/xs/src/png/libpng/scripts/pnglibconf.h.prebuilt b/src/png/libpng/scripts/pnglibconf.h.prebuilt similarity index 100% rename from xs/src/png/libpng/scripts/pnglibconf.h.prebuilt rename to src/png/libpng/scripts/pnglibconf.h.prebuilt diff --git a/xs/src/png/libpng/scripts/prefix.c b/src/png/libpng/scripts/prefix.c similarity index 100% rename from xs/src/png/libpng/scripts/prefix.c rename to src/png/libpng/scripts/prefix.c diff --git a/xs/src/png/libpng/scripts/sym.c b/src/png/libpng/scripts/sym.c similarity index 100% rename from xs/src/png/libpng/scripts/sym.c rename to src/png/libpng/scripts/sym.c diff --git a/xs/src/png/libpng/scripts/symbols.c b/src/png/libpng/scripts/symbols.c similarity index 100% rename from xs/src/png/libpng/scripts/symbols.c rename to src/png/libpng/scripts/symbols.c diff --git a/xs/src/png/libpng/scripts/symbols.def b/src/png/libpng/scripts/symbols.def similarity index 100% rename from xs/src/png/libpng/scripts/symbols.def rename to src/png/libpng/scripts/symbols.def diff --git a/xs/src/png/libpng/scripts/test.cmake.in b/src/png/libpng/scripts/test.cmake.in similarity index 100% rename from xs/src/png/libpng/scripts/test.cmake.in rename to src/png/libpng/scripts/test.cmake.in diff --git a/xs/src/png/libpng/scripts/vers.c b/src/png/libpng/scripts/vers.c similarity index 100% rename from xs/src/png/libpng/scripts/vers.c rename to src/png/libpng/scripts/vers.c diff --git a/xs/src/png/palette.hpp b/src/png/palette.hpp similarity index 100% rename from xs/src/png/palette.hpp rename to src/png/palette.hpp diff --git a/xs/src/png/pixel_traits.hpp b/src/png/pixel_traits.hpp similarity index 100% rename from xs/src/png/pixel_traits.hpp rename to src/png/pixel_traits.hpp diff --git a/xs/src/png/tRNS.hpp b/src/png/tRNS.hpp similarity index 100% rename from xs/src/png/tRNS.hpp rename to src/png/tRNS.hpp diff --git a/xs/src/png/types.hpp b/src/png/types.hpp similarity index 100% rename from xs/src/png/types.hpp rename to src/png/types.hpp diff --git a/xs/src/png/writer.hpp b/src/png/writer.hpp similarity index 100% rename from xs/src/png/writer.hpp rename to src/png/writer.hpp diff --git a/xs/src/png/zlib/CMakeLists.txt b/src/png/zlib/CMakeLists.txt similarity index 100% rename from xs/src/png/zlib/CMakeLists.txt rename to src/png/zlib/CMakeLists.txt diff --git a/xs/src/png/zlib/ChangeLog b/src/png/zlib/ChangeLog similarity index 100% rename from xs/src/png/zlib/ChangeLog rename to src/png/zlib/ChangeLog diff --git a/xs/src/png/zlib/FAQ b/src/png/zlib/FAQ similarity index 100% rename from xs/src/png/zlib/FAQ rename to src/png/zlib/FAQ diff --git a/xs/src/png/zlib/INDEX b/src/png/zlib/INDEX similarity index 100% rename from xs/src/png/zlib/INDEX rename to src/png/zlib/INDEX diff --git a/xs/src/png/zlib/Makefile b/src/png/zlib/Makefile similarity index 100% rename from xs/src/png/zlib/Makefile rename to src/png/zlib/Makefile diff --git a/xs/src/png/zlib/Makefile.in b/src/png/zlib/Makefile.in similarity index 100% rename from xs/src/png/zlib/Makefile.in rename to src/png/zlib/Makefile.in diff --git a/xs/src/png/zlib/README b/src/png/zlib/README similarity index 100% rename from xs/src/png/zlib/README rename to src/png/zlib/README diff --git a/xs/src/png/zlib/adler32.c b/src/png/zlib/adler32.c similarity index 100% rename from xs/src/png/zlib/adler32.c rename to src/png/zlib/adler32.c diff --git a/xs/src/png/zlib/amiga/Makefile.pup b/src/png/zlib/amiga/Makefile.pup similarity index 100% rename from xs/src/png/zlib/amiga/Makefile.pup rename to src/png/zlib/amiga/Makefile.pup diff --git a/xs/src/png/zlib/amiga/Makefile.sas b/src/png/zlib/amiga/Makefile.sas similarity index 100% rename from xs/src/png/zlib/amiga/Makefile.sas rename to src/png/zlib/amiga/Makefile.sas diff --git a/xs/src/png/zlib/compress.c b/src/png/zlib/compress.c similarity index 100% rename from xs/src/png/zlib/compress.c rename to src/png/zlib/compress.c diff --git a/xs/src/png/zlib/configure b/src/png/zlib/configure similarity index 100% rename from xs/src/png/zlib/configure rename to src/png/zlib/configure diff --git a/xs/src/png/zlib/contrib/README.contrib b/src/png/zlib/contrib/README.contrib similarity index 100% rename from xs/src/png/zlib/contrib/README.contrib rename to src/png/zlib/contrib/README.contrib diff --git a/xs/src/png/zlib/contrib/ada/buffer_demo.adb b/src/png/zlib/contrib/ada/buffer_demo.adb similarity index 100% rename from xs/src/png/zlib/contrib/ada/buffer_demo.adb rename to src/png/zlib/contrib/ada/buffer_demo.adb diff --git a/xs/src/png/zlib/contrib/ada/mtest.adb b/src/png/zlib/contrib/ada/mtest.adb similarity index 100% rename from xs/src/png/zlib/contrib/ada/mtest.adb rename to src/png/zlib/contrib/ada/mtest.adb diff --git a/xs/src/png/zlib/contrib/ada/read.adb b/src/png/zlib/contrib/ada/read.adb similarity index 100% rename from xs/src/png/zlib/contrib/ada/read.adb rename to src/png/zlib/contrib/ada/read.adb diff --git a/xs/src/png/zlib/contrib/ada/readme.txt b/src/png/zlib/contrib/ada/readme.txt similarity index 100% rename from xs/src/png/zlib/contrib/ada/readme.txt rename to src/png/zlib/contrib/ada/readme.txt diff --git a/xs/src/png/zlib/contrib/ada/test.adb b/src/png/zlib/contrib/ada/test.adb similarity index 100% rename from xs/src/png/zlib/contrib/ada/test.adb rename to src/png/zlib/contrib/ada/test.adb diff --git a/xs/src/png/zlib/contrib/ada/zlib-streams.adb b/src/png/zlib/contrib/ada/zlib-streams.adb similarity index 100% rename from xs/src/png/zlib/contrib/ada/zlib-streams.adb rename to src/png/zlib/contrib/ada/zlib-streams.adb diff --git a/xs/src/png/zlib/contrib/ada/zlib-streams.ads b/src/png/zlib/contrib/ada/zlib-streams.ads similarity index 100% rename from xs/src/png/zlib/contrib/ada/zlib-streams.ads rename to src/png/zlib/contrib/ada/zlib-streams.ads diff --git a/xs/src/png/zlib/contrib/ada/zlib-thin.adb b/src/png/zlib/contrib/ada/zlib-thin.adb similarity index 100% rename from xs/src/png/zlib/contrib/ada/zlib-thin.adb rename to src/png/zlib/contrib/ada/zlib-thin.adb diff --git a/xs/src/png/zlib/contrib/ada/zlib-thin.ads b/src/png/zlib/contrib/ada/zlib-thin.ads similarity index 100% rename from xs/src/png/zlib/contrib/ada/zlib-thin.ads rename to src/png/zlib/contrib/ada/zlib-thin.ads diff --git a/xs/src/png/zlib/contrib/ada/zlib.adb b/src/png/zlib/contrib/ada/zlib.adb similarity index 100% rename from xs/src/png/zlib/contrib/ada/zlib.adb rename to src/png/zlib/contrib/ada/zlib.adb diff --git a/xs/src/png/zlib/contrib/ada/zlib.ads b/src/png/zlib/contrib/ada/zlib.ads similarity index 100% rename from xs/src/png/zlib/contrib/ada/zlib.ads rename to src/png/zlib/contrib/ada/zlib.ads diff --git a/xs/src/png/zlib/contrib/ada/zlib.gpr b/src/png/zlib/contrib/ada/zlib.gpr similarity index 100% rename from xs/src/png/zlib/contrib/ada/zlib.gpr rename to src/png/zlib/contrib/ada/zlib.gpr diff --git a/xs/src/png/zlib/contrib/amd64/amd64-match.S b/src/png/zlib/contrib/amd64/amd64-match.S similarity index 100% rename from xs/src/png/zlib/contrib/amd64/amd64-match.S rename to src/png/zlib/contrib/amd64/amd64-match.S diff --git a/xs/src/png/zlib/contrib/asm686/README.686 b/src/png/zlib/contrib/asm686/README.686 similarity index 100% rename from xs/src/png/zlib/contrib/asm686/README.686 rename to src/png/zlib/contrib/asm686/README.686 diff --git a/xs/src/png/zlib/contrib/asm686/match.S b/src/png/zlib/contrib/asm686/match.S similarity index 100% rename from xs/src/png/zlib/contrib/asm686/match.S rename to src/png/zlib/contrib/asm686/match.S diff --git a/xs/src/png/zlib/contrib/blast/Makefile b/src/png/zlib/contrib/blast/Makefile similarity index 100% rename from xs/src/png/zlib/contrib/blast/Makefile rename to src/png/zlib/contrib/blast/Makefile diff --git a/xs/src/png/zlib/contrib/blast/README b/src/png/zlib/contrib/blast/README similarity index 100% rename from xs/src/png/zlib/contrib/blast/README rename to src/png/zlib/contrib/blast/README diff --git a/xs/src/png/zlib/contrib/blast/blast.c b/src/png/zlib/contrib/blast/blast.c similarity index 100% rename from xs/src/png/zlib/contrib/blast/blast.c rename to src/png/zlib/contrib/blast/blast.c diff --git a/xs/src/png/zlib/contrib/blast/blast.h b/src/png/zlib/contrib/blast/blast.h similarity index 100% rename from xs/src/png/zlib/contrib/blast/blast.h rename to src/png/zlib/contrib/blast/blast.h diff --git a/xs/src/png/zlib/contrib/blast/test.pk b/src/png/zlib/contrib/blast/test.pk similarity index 100% rename from xs/src/png/zlib/contrib/blast/test.pk rename to src/png/zlib/contrib/blast/test.pk diff --git a/xs/src/png/zlib/contrib/blast/test.txt b/src/png/zlib/contrib/blast/test.txt similarity index 100% rename from xs/src/png/zlib/contrib/blast/test.txt rename to src/png/zlib/contrib/blast/test.txt diff --git a/xs/src/png/zlib/contrib/delphi/ZLib.pas b/src/png/zlib/contrib/delphi/ZLib.pas similarity index 100% rename from xs/src/png/zlib/contrib/delphi/ZLib.pas rename to src/png/zlib/contrib/delphi/ZLib.pas diff --git a/xs/src/png/zlib/contrib/delphi/ZLibConst.pas b/src/png/zlib/contrib/delphi/ZLibConst.pas similarity index 100% rename from xs/src/png/zlib/contrib/delphi/ZLibConst.pas rename to src/png/zlib/contrib/delphi/ZLibConst.pas diff --git a/xs/src/png/zlib/contrib/delphi/readme.txt b/src/png/zlib/contrib/delphi/readme.txt similarity index 100% rename from xs/src/png/zlib/contrib/delphi/readme.txt rename to src/png/zlib/contrib/delphi/readme.txt diff --git a/xs/src/png/zlib/contrib/delphi/zlibd32.mak b/src/png/zlib/contrib/delphi/zlibd32.mak similarity index 100% rename from xs/src/png/zlib/contrib/delphi/zlibd32.mak rename to src/png/zlib/contrib/delphi/zlibd32.mak diff --git a/xs/src/png/zlib/contrib/dotzlib/DotZLib.build b/src/png/zlib/contrib/dotzlib/DotZLib.build similarity index 100% rename from xs/src/png/zlib/contrib/dotzlib/DotZLib.build rename to src/png/zlib/contrib/dotzlib/DotZLib.build diff --git a/xs/src/png/zlib/contrib/dotzlib/DotZLib.chm b/src/png/zlib/contrib/dotzlib/DotZLib.chm similarity index 100% rename from xs/src/png/zlib/contrib/dotzlib/DotZLib.chm rename to src/png/zlib/contrib/dotzlib/DotZLib.chm diff --git a/xs/src/png/zlib/contrib/dotzlib/DotZLib.sln b/src/png/zlib/contrib/dotzlib/DotZLib.sln similarity index 100% rename from xs/src/png/zlib/contrib/dotzlib/DotZLib.sln rename to src/png/zlib/contrib/dotzlib/DotZLib.sln diff --git a/xs/src/png/zlib/contrib/dotzlib/DotZLib/AssemblyInfo.cs b/src/png/zlib/contrib/dotzlib/DotZLib/AssemblyInfo.cs similarity index 100% rename from xs/src/png/zlib/contrib/dotzlib/DotZLib/AssemblyInfo.cs rename to src/png/zlib/contrib/dotzlib/DotZLib/AssemblyInfo.cs diff --git a/xs/src/png/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs b/src/png/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs similarity index 100% rename from xs/src/png/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs rename to src/png/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs diff --git a/xs/src/png/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs b/src/png/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs similarity index 100% rename from xs/src/png/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs rename to src/png/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs diff --git a/xs/src/png/zlib/contrib/dotzlib/DotZLib/CodecBase.cs b/src/png/zlib/contrib/dotzlib/DotZLib/CodecBase.cs similarity index 100% rename from xs/src/png/zlib/contrib/dotzlib/DotZLib/CodecBase.cs rename to src/png/zlib/contrib/dotzlib/DotZLib/CodecBase.cs diff --git a/xs/src/png/zlib/contrib/dotzlib/DotZLib/Deflater.cs b/src/png/zlib/contrib/dotzlib/DotZLib/Deflater.cs similarity index 100% rename from xs/src/png/zlib/contrib/dotzlib/DotZLib/Deflater.cs rename to src/png/zlib/contrib/dotzlib/DotZLib/Deflater.cs diff --git a/xs/src/png/zlib/contrib/dotzlib/DotZLib/DotZLib.cs b/src/png/zlib/contrib/dotzlib/DotZLib/DotZLib.cs similarity index 100% rename from xs/src/png/zlib/contrib/dotzlib/DotZLib/DotZLib.cs rename to src/png/zlib/contrib/dotzlib/DotZLib/DotZLib.cs diff --git a/xs/src/png/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj b/src/png/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj similarity index 100% rename from xs/src/png/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj rename to src/png/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj diff --git a/xs/src/png/zlib/contrib/dotzlib/DotZLib/GZipStream.cs b/src/png/zlib/contrib/dotzlib/DotZLib/GZipStream.cs similarity index 100% rename from xs/src/png/zlib/contrib/dotzlib/DotZLib/GZipStream.cs rename to src/png/zlib/contrib/dotzlib/DotZLib/GZipStream.cs diff --git a/xs/src/png/zlib/contrib/dotzlib/DotZLib/Inflater.cs b/src/png/zlib/contrib/dotzlib/DotZLib/Inflater.cs similarity index 100% rename from xs/src/png/zlib/contrib/dotzlib/DotZLib/Inflater.cs rename to src/png/zlib/contrib/dotzlib/DotZLib/Inflater.cs diff --git a/xs/src/png/zlib/contrib/dotzlib/DotZLib/UnitTests.cs b/src/png/zlib/contrib/dotzlib/DotZLib/UnitTests.cs similarity index 100% rename from xs/src/png/zlib/contrib/dotzlib/DotZLib/UnitTests.cs rename to src/png/zlib/contrib/dotzlib/DotZLib/UnitTests.cs diff --git a/xs/src/png/zlib/contrib/dotzlib/LICENSE_1_0.txt b/src/png/zlib/contrib/dotzlib/LICENSE_1_0.txt similarity index 100% rename from xs/src/png/zlib/contrib/dotzlib/LICENSE_1_0.txt rename to src/png/zlib/contrib/dotzlib/LICENSE_1_0.txt diff --git a/xs/src/png/zlib/contrib/dotzlib/readme.txt b/src/png/zlib/contrib/dotzlib/readme.txt similarity index 100% rename from xs/src/png/zlib/contrib/dotzlib/readme.txt rename to src/png/zlib/contrib/dotzlib/readme.txt diff --git a/xs/src/png/zlib/contrib/gcc_gvmat64/gvmat64.S b/src/png/zlib/contrib/gcc_gvmat64/gvmat64.S similarity index 100% rename from xs/src/png/zlib/contrib/gcc_gvmat64/gvmat64.S rename to src/png/zlib/contrib/gcc_gvmat64/gvmat64.S diff --git a/xs/src/png/zlib/contrib/infback9/README b/src/png/zlib/contrib/infback9/README similarity index 100% rename from xs/src/png/zlib/contrib/infback9/README rename to src/png/zlib/contrib/infback9/README diff --git a/xs/src/png/zlib/contrib/infback9/infback9.c b/src/png/zlib/contrib/infback9/infback9.c similarity index 100% rename from xs/src/png/zlib/contrib/infback9/infback9.c rename to src/png/zlib/contrib/infback9/infback9.c diff --git a/xs/src/png/zlib/contrib/infback9/infback9.h b/src/png/zlib/contrib/infback9/infback9.h similarity index 100% rename from xs/src/png/zlib/contrib/infback9/infback9.h rename to src/png/zlib/contrib/infback9/infback9.h diff --git a/xs/src/png/zlib/contrib/infback9/inffix9.h b/src/png/zlib/contrib/infback9/inffix9.h similarity index 100% rename from xs/src/png/zlib/contrib/infback9/inffix9.h rename to src/png/zlib/contrib/infback9/inffix9.h diff --git a/xs/src/png/zlib/contrib/infback9/inflate9.h b/src/png/zlib/contrib/infback9/inflate9.h similarity index 100% rename from xs/src/png/zlib/contrib/infback9/inflate9.h rename to src/png/zlib/contrib/infback9/inflate9.h diff --git a/xs/src/png/zlib/contrib/infback9/inftree9.c b/src/png/zlib/contrib/infback9/inftree9.c similarity index 100% rename from xs/src/png/zlib/contrib/infback9/inftree9.c rename to src/png/zlib/contrib/infback9/inftree9.c diff --git a/xs/src/png/zlib/contrib/infback9/inftree9.h b/src/png/zlib/contrib/infback9/inftree9.h similarity index 100% rename from xs/src/png/zlib/contrib/infback9/inftree9.h rename to src/png/zlib/contrib/infback9/inftree9.h diff --git a/xs/src/png/zlib/contrib/inflate86/inffas86.c b/src/png/zlib/contrib/inflate86/inffas86.c similarity index 100% rename from xs/src/png/zlib/contrib/inflate86/inffas86.c rename to src/png/zlib/contrib/inflate86/inffas86.c diff --git a/xs/src/png/zlib/contrib/inflate86/inffast.S b/src/png/zlib/contrib/inflate86/inffast.S similarity index 100% rename from xs/src/png/zlib/contrib/inflate86/inffast.S rename to src/png/zlib/contrib/inflate86/inffast.S diff --git a/xs/src/png/zlib/contrib/iostream/test.cpp b/src/png/zlib/contrib/iostream/test.cpp similarity index 100% rename from xs/src/png/zlib/contrib/iostream/test.cpp rename to src/png/zlib/contrib/iostream/test.cpp diff --git a/xs/src/png/zlib/contrib/iostream/zfstream.cpp b/src/png/zlib/contrib/iostream/zfstream.cpp similarity index 100% rename from xs/src/png/zlib/contrib/iostream/zfstream.cpp rename to src/png/zlib/contrib/iostream/zfstream.cpp diff --git a/xs/src/png/zlib/contrib/iostream/zfstream.h b/src/png/zlib/contrib/iostream/zfstream.h similarity index 100% rename from xs/src/png/zlib/contrib/iostream/zfstream.h rename to src/png/zlib/contrib/iostream/zfstream.h diff --git a/xs/src/png/zlib/contrib/iostream2/zstream.h b/src/png/zlib/contrib/iostream2/zstream.h similarity index 100% rename from xs/src/png/zlib/contrib/iostream2/zstream.h rename to src/png/zlib/contrib/iostream2/zstream.h diff --git a/xs/src/png/zlib/contrib/iostream2/zstream_test.cpp b/src/png/zlib/contrib/iostream2/zstream_test.cpp similarity index 100% rename from xs/src/png/zlib/contrib/iostream2/zstream_test.cpp rename to src/png/zlib/contrib/iostream2/zstream_test.cpp diff --git a/xs/src/png/zlib/contrib/iostream3/README b/src/png/zlib/contrib/iostream3/README similarity index 100% rename from xs/src/png/zlib/contrib/iostream3/README rename to src/png/zlib/contrib/iostream3/README diff --git a/xs/src/png/zlib/contrib/iostream3/TODO b/src/png/zlib/contrib/iostream3/TODO similarity index 100% rename from xs/src/png/zlib/contrib/iostream3/TODO rename to src/png/zlib/contrib/iostream3/TODO diff --git a/xs/src/png/zlib/contrib/iostream3/test.cc b/src/png/zlib/contrib/iostream3/test.cc similarity index 100% rename from xs/src/png/zlib/contrib/iostream3/test.cc rename to src/png/zlib/contrib/iostream3/test.cc diff --git a/xs/src/png/zlib/contrib/iostream3/zfstream.cc b/src/png/zlib/contrib/iostream3/zfstream.cc similarity index 100% rename from xs/src/png/zlib/contrib/iostream3/zfstream.cc rename to src/png/zlib/contrib/iostream3/zfstream.cc diff --git a/xs/src/png/zlib/contrib/iostream3/zfstream.h b/src/png/zlib/contrib/iostream3/zfstream.h similarity index 100% rename from xs/src/png/zlib/contrib/iostream3/zfstream.h rename to src/png/zlib/contrib/iostream3/zfstream.h diff --git a/xs/src/png/zlib/contrib/masmx64/bld_ml64.bat b/src/png/zlib/contrib/masmx64/bld_ml64.bat similarity index 100% rename from xs/src/png/zlib/contrib/masmx64/bld_ml64.bat rename to src/png/zlib/contrib/masmx64/bld_ml64.bat diff --git a/xs/src/png/zlib/contrib/masmx64/gvmat64.asm b/src/png/zlib/contrib/masmx64/gvmat64.asm similarity index 100% rename from xs/src/png/zlib/contrib/masmx64/gvmat64.asm rename to src/png/zlib/contrib/masmx64/gvmat64.asm diff --git a/xs/src/png/zlib/contrib/masmx64/inffas8664.c b/src/png/zlib/contrib/masmx64/inffas8664.c similarity index 100% rename from xs/src/png/zlib/contrib/masmx64/inffas8664.c rename to src/png/zlib/contrib/masmx64/inffas8664.c diff --git a/xs/src/png/zlib/contrib/masmx64/inffasx64.asm b/src/png/zlib/contrib/masmx64/inffasx64.asm similarity index 100% rename from xs/src/png/zlib/contrib/masmx64/inffasx64.asm rename to src/png/zlib/contrib/masmx64/inffasx64.asm diff --git a/xs/src/png/zlib/contrib/masmx64/readme.txt b/src/png/zlib/contrib/masmx64/readme.txt similarity index 100% rename from xs/src/png/zlib/contrib/masmx64/readme.txt rename to src/png/zlib/contrib/masmx64/readme.txt diff --git a/xs/src/png/zlib/contrib/masmx86/bld_ml32.bat b/src/png/zlib/contrib/masmx86/bld_ml32.bat similarity index 100% rename from xs/src/png/zlib/contrib/masmx86/bld_ml32.bat rename to src/png/zlib/contrib/masmx86/bld_ml32.bat diff --git a/xs/src/png/zlib/contrib/masmx86/inffas32.asm b/src/png/zlib/contrib/masmx86/inffas32.asm similarity index 100% rename from xs/src/png/zlib/contrib/masmx86/inffas32.asm rename to src/png/zlib/contrib/masmx86/inffas32.asm diff --git a/xs/src/png/zlib/contrib/masmx86/match686.asm b/src/png/zlib/contrib/masmx86/match686.asm similarity index 100% rename from xs/src/png/zlib/contrib/masmx86/match686.asm rename to src/png/zlib/contrib/masmx86/match686.asm diff --git a/xs/src/png/zlib/contrib/masmx86/readme.txt b/src/png/zlib/contrib/masmx86/readme.txt similarity index 100% rename from xs/src/png/zlib/contrib/masmx86/readme.txt rename to src/png/zlib/contrib/masmx86/readme.txt diff --git a/xs/src/png/zlib/contrib/minizip/Makefile b/src/png/zlib/contrib/minizip/Makefile similarity index 100% rename from xs/src/png/zlib/contrib/minizip/Makefile rename to src/png/zlib/contrib/minizip/Makefile diff --git a/xs/src/png/zlib/contrib/minizip/Makefile.am b/src/png/zlib/contrib/minizip/Makefile.am similarity index 100% rename from xs/src/png/zlib/contrib/minizip/Makefile.am rename to src/png/zlib/contrib/minizip/Makefile.am diff --git a/xs/src/png/zlib/contrib/minizip/MiniZip64_Changes.txt b/src/png/zlib/contrib/minizip/MiniZip64_Changes.txt similarity index 100% rename from xs/src/png/zlib/contrib/minizip/MiniZip64_Changes.txt rename to src/png/zlib/contrib/minizip/MiniZip64_Changes.txt diff --git a/xs/src/png/zlib/contrib/minizip/MiniZip64_info.txt b/src/png/zlib/contrib/minizip/MiniZip64_info.txt similarity index 100% rename from xs/src/png/zlib/contrib/minizip/MiniZip64_info.txt rename to src/png/zlib/contrib/minizip/MiniZip64_info.txt diff --git a/xs/src/png/zlib/contrib/minizip/configure.ac b/src/png/zlib/contrib/minizip/configure.ac similarity index 100% rename from xs/src/png/zlib/contrib/minizip/configure.ac rename to src/png/zlib/contrib/minizip/configure.ac diff --git a/xs/src/png/zlib/contrib/minizip/crypt.h b/src/png/zlib/contrib/minizip/crypt.h similarity index 100% rename from xs/src/png/zlib/contrib/minizip/crypt.h rename to src/png/zlib/contrib/minizip/crypt.h diff --git a/xs/src/png/zlib/contrib/minizip/ioapi.c b/src/png/zlib/contrib/minizip/ioapi.c similarity index 100% rename from xs/src/png/zlib/contrib/minizip/ioapi.c rename to src/png/zlib/contrib/minizip/ioapi.c diff --git a/xs/src/png/zlib/contrib/minizip/ioapi.h b/src/png/zlib/contrib/minizip/ioapi.h similarity index 100% rename from xs/src/png/zlib/contrib/minizip/ioapi.h rename to src/png/zlib/contrib/minizip/ioapi.h diff --git a/xs/src/png/zlib/contrib/minizip/iowin32.c b/src/png/zlib/contrib/minizip/iowin32.c similarity index 100% rename from xs/src/png/zlib/contrib/minizip/iowin32.c rename to src/png/zlib/contrib/minizip/iowin32.c diff --git a/xs/src/png/zlib/contrib/minizip/iowin32.h b/src/png/zlib/contrib/minizip/iowin32.h similarity index 100% rename from xs/src/png/zlib/contrib/minizip/iowin32.h rename to src/png/zlib/contrib/minizip/iowin32.h diff --git a/xs/src/png/zlib/contrib/minizip/make_vms.com b/src/png/zlib/contrib/minizip/make_vms.com similarity index 100% rename from xs/src/png/zlib/contrib/minizip/make_vms.com rename to src/png/zlib/contrib/minizip/make_vms.com diff --git a/xs/src/png/zlib/contrib/minizip/miniunz.c b/src/png/zlib/contrib/minizip/miniunz.c similarity index 100% rename from xs/src/png/zlib/contrib/minizip/miniunz.c rename to src/png/zlib/contrib/minizip/miniunz.c diff --git a/xs/src/png/zlib/contrib/minizip/miniunzip.1 b/src/png/zlib/contrib/minizip/miniunzip.1 similarity index 100% rename from xs/src/png/zlib/contrib/minizip/miniunzip.1 rename to src/png/zlib/contrib/minizip/miniunzip.1 diff --git a/xs/src/png/zlib/contrib/minizip/minizip.1 b/src/png/zlib/contrib/minizip/minizip.1 similarity index 100% rename from xs/src/png/zlib/contrib/minizip/minizip.1 rename to src/png/zlib/contrib/minizip/minizip.1 diff --git a/xs/src/png/zlib/contrib/minizip/minizip.c b/src/png/zlib/contrib/minizip/minizip.c similarity index 100% rename from xs/src/png/zlib/contrib/minizip/minizip.c rename to src/png/zlib/contrib/minizip/minizip.c diff --git a/xs/src/png/zlib/contrib/minizip/minizip.pc.in b/src/png/zlib/contrib/minizip/minizip.pc.in similarity index 100% rename from xs/src/png/zlib/contrib/minizip/minizip.pc.in rename to src/png/zlib/contrib/minizip/minizip.pc.in diff --git a/xs/src/png/zlib/contrib/minizip/mztools.c b/src/png/zlib/contrib/minizip/mztools.c similarity index 100% rename from xs/src/png/zlib/contrib/minizip/mztools.c rename to src/png/zlib/contrib/minizip/mztools.c diff --git a/xs/src/png/zlib/contrib/minizip/mztools.h b/src/png/zlib/contrib/minizip/mztools.h similarity index 100% rename from xs/src/png/zlib/contrib/minizip/mztools.h rename to src/png/zlib/contrib/minizip/mztools.h diff --git a/xs/src/png/zlib/contrib/minizip/unzip.c b/src/png/zlib/contrib/minizip/unzip.c similarity index 100% rename from xs/src/png/zlib/contrib/minizip/unzip.c rename to src/png/zlib/contrib/minizip/unzip.c diff --git a/xs/src/png/zlib/contrib/minizip/unzip.h b/src/png/zlib/contrib/minizip/unzip.h similarity index 100% rename from xs/src/png/zlib/contrib/minizip/unzip.h rename to src/png/zlib/contrib/minizip/unzip.h diff --git a/xs/src/png/zlib/contrib/minizip/zip.c b/src/png/zlib/contrib/minizip/zip.c similarity index 100% rename from xs/src/png/zlib/contrib/minizip/zip.c rename to src/png/zlib/contrib/minizip/zip.c diff --git a/xs/src/png/zlib/contrib/minizip/zip.h b/src/png/zlib/contrib/minizip/zip.h similarity index 100% rename from xs/src/png/zlib/contrib/minizip/zip.h rename to src/png/zlib/contrib/minizip/zip.h diff --git a/xs/src/png/zlib/contrib/pascal/example.pas b/src/png/zlib/contrib/pascal/example.pas similarity index 100% rename from xs/src/png/zlib/contrib/pascal/example.pas rename to src/png/zlib/contrib/pascal/example.pas diff --git a/xs/src/png/zlib/contrib/pascal/readme.txt b/src/png/zlib/contrib/pascal/readme.txt similarity index 100% rename from xs/src/png/zlib/contrib/pascal/readme.txt rename to src/png/zlib/contrib/pascal/readme.txt diff --git a/xs/src/png/zlib/contrib/pascal/zlibd32.mak b/src/png/zlib/contrib/pascal/zlibd32.mak similarity index 100% rename from xs/src/png/zlib/contrib/pascal/zlibd32.mak rename to src/png/zlib/contrib/pascal/zlibd32.mak diff --git a/xs/src/png/zlib/contrib/pascal/zlibpas.pas b/src/png/zlib/contrib/pascal/zlibpas.pas similarity index 100% rename from xs/src/png/zlib/contrib/pascal/zlibpas.pas rename to src/png/zlib/contrib/pascal/zlibpas.pas diff --git a/xs/src/png/zlib/contrib/puff/Makefile b/src/png/zlib/contrib/puff/Makefile similarity index 100% rename from xs/src/png/zlib/contrib/puff/Makefile rename to src/png/zlib/contrib/puff/Makefile diff --git a/xs/src/png/zlib/contrib/puff/README b/src/png/zlib/contrib/puff/README similarity index 100% rename from xs/src/png/zlib/contrib/puff/README rename to src/png/zlib/contrib/puff/README diff --git a/xs/src/png/zlib/contrib/puff/puff.c b/src/png/zlib/contrib/puff/puff.c similarity index 100% rename from xs/src/png/zlib/contrib/puff/puff.c rename to src/png/zlib/contrib/puff/puff.c diff --git a/xs/src/png/zlib/contrib/puff/puff.h b/src/png/zlib/contrib/puff/puff.h similarity index 100% rename from xs/src/png/zlib/contrib/puff/puff.h rename to src/png/zlib/contrib/puff/puff.h diff --git a/xs/src/png/zlib/contrib/puff/pufftest.c b/src/png/zlib/contrib/puff/pufftest.c similarity index 100% rename from xs/src/png/zlib/contrib/puff/pufftest.c rename to src/png/zlib/contrib/puff/pufftest.c diff --git a/xs/src/png/zlib/contrib/puff/zeros.raw b/src/png/zlib/contrib/puff/zeros.raw similarity index 100% rename from xs/src/png/zlib/contrib/puff/zeros.raw rename to src/png/zlib/contrib/puff/zeros.raw diff --git a/xs/src/png/zlib/contrib/testzlib/testzlib.c b/src/png/zlib/contrib/testzlib/testzlib.c similarity index 100% rename from xs/src/png/zlib/contrib/testzlib/testzlib.c rename to src/png/zlib/contrib/testzlib/testzlib.c diff --git a/xs/src/png/zlib/contrib/testzlib/testzlib.txt b/src/png/zlib/contrib/testzlib/testzlib.txt similarity index 100% rename from xs/src/png/zlib/contrib/testzlib/testzlib.txt rename to src/png/zlib/contrib/testzlib/testzlib.txt diff --git a/xs/src/png/zlib/contrib/untgz/Makefile b/src/png/zlib/contrib/untgz/Makefile similarity index 100% rename from xs/src/png/zlib/contrib/untgz/Makefile rename to src/png/zlib/contrib/untgz/Makefile diff --git a/xs/src/png/zlib/contrib/untgz/Makefile.msc b/src/png/zlib/contrib/untgz/Makefile.msc similarity index 100% rename from xs/src/png/zlib/contrib/untgz/Makefile.msc rename to src/png/zlib/contrib/untgz/Makefile.msc diff --git a/xs/src/png/zlib/contrib/untgz/untgz.c b/src/png/zlib/contrib/untgz/untgz.c similarity index 100% rename from xs/src/png/zlib/contrib/untgz/untgz.c rename to src/png/zlib/contrib/untgz/untgz.c diff --git a/xs/src/png/zlib/contrib/vstudio/readme.txt b/src/png/zlib/contrib/vstudio/readme.txt similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/readme.txt rename to src/png/zlib/contrib/vstudio/readme.txt diff --git a/xs/src/png/zlib/contrib/vstudio/vc10/miniunz.vcxproj b/src/png/zlib/contrib/vstudio/vc10/miniunz.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc10/miniunz.vcxproj rename to src/png/zlib/contrib/vstudio/vc10/miniunz.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters b/src/png/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters rename to src/png/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters diff --git a/xs/src/png/zlib/contrib/vstudio/vc10/minizip.vcxproj b/src/png/zlib/contrib/vstudio/vc10/minizip.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc10/minizip.vcxproj rename to src/png/zlib/contrib/vstudio/vc10/minizip.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc10/minizip.vcxproj.filters b/src/png/zlib/contrib/vstudio/vc10/minizip.vcxproj.filters similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc10/minizip.vcxproj.filters rename to src/png/zlib/contrib/vstudio/vc10/minizip.vcxproj.filters diff --git a/xs/src/png/zlib/contrib/vstudio/vc10/testzlib.vcxproj b/src/png/zlib/contrib/vstudio/vc10/testzlib.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc10/testzlib.vcxproj rename to src/png/zlib/contrib/vstudio/vc10/testzlib.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc10/testzlib.vcxproj.filters b/src/png/zlib/contrib/vstudio/vc10/testzlib.vcxproj.filters similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc10/testzlib.vcxproj.filters rename to src/png/zlib/contrib/vstudio/vc10/testzlib.vcxproj.filters diff --git a/xs/src/png/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj b/src/png/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj rename to src/png/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.filters b/src/png/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.filters similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.filters rename to src/png/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.filters diff --git a/xs/src/png/zlib/contrib/vstudio/vc10/zlib.rc b/src/png/zlib/contrib/vstudio/vc10/zlib.rc similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc10/zlib.rc rename to src/png/zlib/contrib/vstudio/vc10/zlib.rc diff --git a/xs/src/png/zlib/contrib/vstudio/vc10/zlibstat.vcxproj b/src/png/zlib/contrib/vstudio/vc10/zlibstat.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc10/zlibstat.vcxproj rename to src/png/zlib/contrib/vstudio/vc10/zlibstat.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.filters b/src/png/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.filters similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.filters rename to src/png/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.filters diff --git a/xs/src/png/zlib/contrib/vstudio/vc10/zlibvc.def b/src/png/zlib/contrib/vstudio/vc10/zlibvc.def similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc10/zlibvc.def rename to src/png/zlib/contrib/vstudio/vc10/zlibvc.def diff --git a/xs/src/png/zlib/contrib/vstudio/vc10/zlibvc.sln b/src/png/zlib/contrib/vstudio/vc10/zlibvc.sln similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc10/zlibvc.sln rename to src/png/zlib/contrib/vstudio/vc10/zlibvc.sln diff --git a/xs/src/png/zlib/contrib/vstudio/vc10/zlibvc.vcxproj b/src/png/zlib/contrib/vstudio/vc10/zlibvc.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc10/zlibvc.vcxproj rename to src/png/zlib/contrib/vstudio/vc10/zlibvc.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.filters b/src/png/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.filters similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.filters rename to src/png/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.filters diff --git a/xs/src/png/zlib/contrib/vstudio/vc11/miniunz.vcxproj b/src/png/zlib/contrib/vstudio/vc11/miniunz.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc11/miniunz.vcxproj rename to src/png/zlib/contrib/vstudio/vc11/miniunz.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc11/minizip.vcxproj b/src/png/zlib/contrib/vstudio/vc11/minizip.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc11/minizip.vcxproj rename to src/png/zlib/contrib/vstudio/vc11/minizip.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc11/testzlib.vcxproj b/src/png/zlib/contrib/vstudio/vc11/testzlib.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc11/testzlib.vcxproj rename to src/png/zlib/contrib/vstudio/vc11/testzlib.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc11/testzlibdll.vcxproj b/src/png/zlib/contrib/vstudio/vc11/testzlibdll.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc11/testzlibdll.vcxproj rename to src/png/zlib/contrib/vstudio/vc11/testzlibdll.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc11/zlib.rc b/src/png/zlib/contrib/vstudio/vc11/zlib.rc similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc11/zlib.rc rename to src/png/zlib/contrib/vstudio/vc11/zlib.rc diff --git a/xs/src/png/zlib/contrib/vstudio/vc11/zlibstat.vcxproj b/src/png/zlib/contrib/vstudio/vc11/zlibstat.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc11/zlibstat.vcxproj rename to src/png/zlib/contrib/vstudio/vc11/zlibstat.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc11/zlibvc.def b/src/png/zlib/contrib/vstudio/vc11/zlibvc.def similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc11/zlibvc.def rename to src/png/zlib/contrib/vstudio/vc11/zlibvc.def diff --git a/xs/src/png/zlib/contrib/vstudio/vc11/zlibvc.sln b/src/png/zlib/contrib/vstudio/vc11/zlibvc.sln similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc11/zlibvc.sln rename to src/png/zlib/contrib/vstudio/vc11/zlibvc.sln diff --git a/xs/src/png/zlib/contrib/vstudio/vc11/zlibvc.vcxproj b/src/png/zlib/contrib/vstudio/vc11/zlibvc.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc11/zlibvc.vcxproj rename to src/png/zlib/contrib/vstudio/vc11/zlibvc.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc12/miniunz.vcxproj b/src/png/zlib/contrib/vstudio/vc12/miniunz.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc12/miniunz.vcxproj rename to src/png/zlib/contrib/vstudio/vc12/miniunz.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc12/minizip.vcxproj b/src/png/zlib/contrib/vstudio/vc12/minizip.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc12/minizip.vcxproj rename to src/png/zlib/contrib/vstudio/vc12/minizip.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc12/testzlib.vcxproj b/src/png/zlib/contrib/vstudio/vc12/testzlib.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc12/testzlib.vcxproj rename to src/png/zlib/contrib/vstudio/vc12/testzlib.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc12/testzlibdll.vcxproj b/src/png/zlib/contrib/vstudio/vc12/testzlibdll.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc12/testzlibdll.vcxproj rename to src/png/zlib/contrib/vstudio/vc12/testzlibdll.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc12/zlib.rc b/src/png/zlib/contrib/vstudio/vc12/zlib.rc similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc12/zlib.rc rename to src/png/zlib/contrib/vstudio/vc12/zlib.rc diff --git a/xs/src/png/zlib/contrib/vstudio/vc12/zlibstat.vcxproj b/src/png/zlib/contrib/vstudio/vc12/zlibstat.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc12/zlibstat.vcxproj rename to src/png/zlib/contrib/vstudio/vc12/zlibstat.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc12/zlibvc.def b/src/png/zlib/contrib/vstudio/vc12/zlibvc.def similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc12/zlibvc.def rename to src/png/zlib/contrib/vstudio/vc12/zlibvc.def diff --git a/xs/src/png/zlib/contrib/vstudio/vc12/zlibvc.sln b/src/png/zlib/contrib/vstudio/vc12/zlibvc.sln similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc12/zlibvc.sln rename to src/png/zlib/contrib/vstudio/vc12/zlibvc.sln diff --git a/xs/src/png/zlib/contrib/vstudio/vc12/zlibvc.vcxproj b/src/png/zlib/contrib/vstudio/vc12/zlibvc.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc12/zlibvc.vcxproj rename to src/png/zlib/contrib/vstudio/vc12/zlibvc.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc14/miniunz.vcxproj b/src/png/zlib/contrib/vstudio/vc14/miniunz.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc14/miniunz.vcxproj rename to src/png/zlib/contrib/vstudio/vc14/miniunz.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc14/minizip.vcxproj b/src/png/zlib/contrib/vstudio/vc14/minizip.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc14/minizip.vcxproj rename to src/png/zlib/contrib/vstudio/vc14/minizip.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc14/testzlib.vcxproj b/src/png/zlib/contrib/vstudio/vc14/testzlib.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc14/testzlib.vcxproj rename to src/png/zlib/contrib/vstudio/vc14/testzlib.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc14/testzlibdll.vcxproj b/src/png/zlib/contrib/vstudio/vc14/testzlibdll.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc14/testzlibdll.vcxproj rename to src/png/zlib/contrib/vstudio/vc14/testzlibdll.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc14/zlib.rc b/src/png/zlib/contrib/vstudio/vc14/zlib.rc similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc14/zlib.rc rename to src/png/zlib/contrib/vstudio/vc14/zlib.rc diff --git a/xs/src/png/zlib/contrib/vstudio/vc14/zlibstat.vcxproj b/src/png/zlib/contrib/vstudio/vc14/zlibstat.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc14/zlibstat.vcxproj rename to src/png/zlib/contrib/vstudio/vc14/zlibstat.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc14/zlibvc.def b/src/png/zlib/contrib/vstudio/vc14/zlibvc.def similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc14/zlibvc.def rename to src/png/zlib/contrib/vstudio/vc14/zlibvc.def diff --git a/xs/src/png/zlib/contrib/vstudio/vc14/zlibvc.sln b/src/png/zlib/contrib/vstudio/vc14/zlibvc.sln similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc14/zlibvc.sln rename to src/png/zlib/contrib/vstudio/vc14/zlibvc.sln diff --git a/xs/src/png/zlib/contrib/vstudio/vc14/zlibvc.vcxproj b/src/png/zlib/contrib/vstudio/vc14/zlibvc.vcxproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc14/zlibvc.vcxproj rename to src/png/zlib/contrib/vstudio/vc14/zlibvc.vcxproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc9/miniunz.vcproj b/src/png/zlib/contrib/vstudio/vc9/miniunz.vcproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc9/miniunz.vcproj rename to src/png/zlib/contrib/vstudio/vc9/miniunz.vcproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc9/minizip.vcproj b/src/png/zlib/contrib/vstudio/vc9/minizip.vcproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc9/minizip.vcproj rename to src/png/zlib/contrib/vstudio/vc9/minizip.vcproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc9/testzlib.vcproj b/src/png/zlib/contrib/vstudio/vc9/testzlib.vcproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc9/testzlib.vcproj rename to src/png/zlib/contrib/vstudio/vc9/testzlib.vcproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc9/testzlibdll.vcproj b/src/png/zlib/contrib/vstudio/vc9/testzlibdll.vcproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc9/testzlibdll.vcproj rename to src/png/zlib/contrib/vstudio/vc9/testzlibdll.vcproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc9/zlib.rc b/src/png/zlib/contrib/vstudio/vc9/zlib.rc similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc9/zlib.rc rename to src/png/zlib/contrib/vstudio/vc9/zlib.rc diff --git a/xs/src/png/zlib/contrib/vstudio/vc9/zlibstat.vcproj b/src/png/zlib/contrib/vstudio/vc9/zlibstat.vcproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc9/zlibstat.vcproj rename to src/png/zlib/contrib/vstudio/vc9/zlibstat.vcproj diff --git a/xs/src/png/zlib/contrib/vstudio/vc9/zlibvc.def b/src/png/zlib/contrib/vstudio/vc9/zlibvc.def similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc9/zlibvc.def rename to src/png/zlib/contrib/vstudio/vc9/zlibvc.def diff --git a/xs/src/png/zlib/contrib/vstudio/vc9/zlibvc.sln b/src/png/zlib/contrib/vstudio/vc9/zlibvc.sln similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc9/zlibvc.sln rename to src/png/zlib/contrib/vstudio/vc9/zlibvc.sln diff --git a/xs/src/png/zlib/contrib/vstudio/vc9/zlibvc.vcproj b/src/png/zlib/contrib/vstudio/vc9/zlibvc.vcproj similarity index 100% rename from xs/src/png/zlib/contrib/vstudio/vc9/zlibvc.vcproj rename to src/png/zlib/contrib/vstudio/vc9/zlibvc.vcproj diff --git a/xs/src/png/zlib/crc32.c b/src/png/zlib/crc32.c similarity index 100% rename from xs/src/png/zlib/crc32.c rename to src/png/zlib/crc32.c diff --git a/xs/src/png/zlib/crc32.h b/src/png/zlib/crc32.h similarity index 100% rename from xs/src/png/zlib/crc32.h rename to src/png/zlib/crc32.h diff --git a/xs/src/png/zlib/deflate.c b/src/png/zlib/deflate.c similarity index 100% rename from xs/src/png/zlib/deflate.c rename to src/png/zlib/deflate.c diff --git a/xs/src/png/zlib/deflate.h b/src/png/zlib/deflate.h similarity index 100% rename from xs/src/png/zlib/deflate.h rename to src/png/zlib/deflate.h diff --git a/xs/src/png/zlib/gzclose.c b/src/png/zlib/gzclose.c similarity index 100% rename from xs/src/png/zlib/gzclose.c rename to src/png/zlib/gzclose.c diff --git a/xs/src/png/zlib/gzguts.h b/src/png/zlib/gzguts.h similarity index 100% rename from xs/src/png/zlib/gzguts.h rename to src/png/zlib/gzguts.h diff --git a/xs/src/png/zlib/gzlib.c b/src/png/zlib/gzlib.c similarity index 100% rename from xs/src/png/zlib/gzlib.c rename to src/png/zlib/gzlib.c diff --git a/xs/src/png/zlib/gzread.c b/src/png/zlib/gzread.c similarity index 100% rename from xs/src/png/zlib/gzread.c rename to src/png/zlib/gzread.c diff --git a/xs/src/png/zlib/gzwrite.c b/src/png/zlib/gzwrite.c similarity index 100% rename from xs/src/png/zlib/gzwrite.c rename to src/png/zlib/gzwrite.c diff --git a/xs/src/png/zlib/infback.c b/src/png/zlib/infback.c similarity index 100% rename from xs/src/png/zlib/infback.c rename to src/png/zlib/infback.c diff --git a/xs/src/png/zlib/inffast.c b/src/png/zlib/inffast.c similarity index 100% rename from xs/src/png/zlib/inffast.c rename to src/png/zlib/inffast.c diff --git a/xs/src/png/zlib/inffast.h b/src/png/zlib/inffast.h similarity index 100% rename from xs/src/png/zlib/inffast.h rename to src/png/zlib/inffast.h diff --git a/xs/src/png/zlib/inffixed.h b/src/png/zlib/inffixed.h similarity index 100% rename from xs/src/png/zlib/inffixed.h rename to src/png/zlib/inffixed.h diff --git a/xs/src/png/zlib/inflate.c b/src/png/zlib/inflate.c similarity index 100% rename from xs/src/png/zlib/inflate.c rename to src/png/zlib/inflate.c diff --git a/xs/src/png/zlib/inflate.h b/src/png/zlib/inflate.h similarity index 100% rename from xs/src/png/zlib/inflate.h rename to src/png/zlib/inflate.h diff --git a/xs/src/png/zlib/inftrees.c b/src/png/zlib/inftrees.c similarity index 100% rename from xs/src/png/zlib/inftrees.c rename to src/png/zlib/inftrees.c diff --git a/xs/src/png/zlib/inftrees.h b/src/png/zlib/inftrees.h similarity index 100% rename from xs/src/png/zlib/inftrees.h rename to src/png/zlib/inftrees.h diff --git a/xs/src/png/zlib/make_vms.com b/src/png/zlib/make_vms.com similarity index 100% rename from xs/src/png/zlib/make_vms.com rename to src/png/zlib/make_vms.com diff --git a/xs/src/png/zlib/msdos/Makefile.bor b/src/png/zlib/msdos/Makefile.bor similarity index 100% rename from xs/src/png/zlib/msdos/Makefile.bor rename to src/png/zlib/msdos/Makefile.bor diff --git a/xs/src/png/zlib/msdos/Makefile.dj2 b/src/png/zlib/msdos/Makefile.dj2 similarity index 100% rename from xs/src/png/zlib/msdos/Makefile.dj2 rename to src/png/zlib/msdos/Makefile.dj2 diff --git a/xs/src/png/zlib/msdos/Makefile.emx b/src/png/zlib/msdos/Makefile.emx similarity index 100% rename from xs/src/png/zlib/msdos/Makefile.emx rename to src/png/zlib/msdos/Makefile.emx diff --git a/xs/src/png/zlib/msdos/Makefile.msc b/src/png/zlib/msdos/Makefile.msc similarity index 100% rename from xs/src/png/zlib/msdos/Makefile.msc rename to src/png/zlib/msdos/Makefile.msc diff --git a/xs/src/png/zlib/msdos/Makefile.tc b/src/png/zlib/msdos/Makefile.tc similarity index 100% rename from xs/src/png/zlib/msdos/Makefile.tc rename to src/png/zlib/msdos/Makefile.tc diff --git a/xs/src/png/zlib/nintendods/Makefile b/src/png/zlib/nintendods/Makefile similarity index 100% rename from xs/src/png/zlib/nintendods/Makefile rename to src/png/zlib/nintendods/Makefile diff --git a/xs/src/png/zlib/nintendods/README b/src/png/zlib/nintendods/README similarity index 100% rename from xs/src/png/zlib/nintendods/README rename to src/png/zlib/nintendods/README diff --git a/xs/src/png/zlib/old/Makefile.emx b/src/png/zlib/old/Makefile.emx similarity index 100% rename from xs/src/png/zlib/old/Makefile.emx rename to src/png/zlib/old/Makefile.emx diff --git a/xs/src/png/zlib/old/Makefile.riscos b/src/png/zlib/old/Makefile.riscos similarity index 100% rename from xs/src/png/zlib/old/Makefile.riscos rename to src/png/zlib/old/Makefile.riscos diff --git a/xs/src/png/zlib/old/README b/src/png/zlib/old/README similarity index 100% rename from xs/src/png/zlib/old/README rename to src/png/zlib/old/README diff --git a/xs/src/png/zlib/old/descrip.mms b/src/png/zlib/old/descrip.mms similarity index 100% rename from xs/src/png/zlib/old/descrip.mms rename to src/png/zlib/old/descrip.mms diff --git a/xs/src/png/zlib/old/os2/Makefile.os2 b/src/png/zlib/old/os2/Makefile.os2 similarity index 100% rename from xs/src/png/zlib/old/os2/Makefile.os2 rename to src/png/zlib/old/os2/Makefile.os2 diff --git a/xs/src/png/zlib/old/os2/zlib.def b/src/png/zlib/old/os2/zlib.def similarity index 100% rename from xs/src/png/zlib/old/os2/zlib.def rename to src/png/zlib/old/os2/zlib.def diff --git a/xs/src/png/zlib/old/visual-basic.txt b/src/png/zlib/old/visual-basic.txt similarity index 100% rename from xs/src/png/zlib/old/visual-basic.txt rename to src/png/zlib/old/visual-basic.txt diff --git a/xs/src/png/zlib/os400/README400 b/src/png/zlib/os400/README400 similarity index 100% rename from xs/src/png/zlib/os400/README400 rename to src/png/zlib/os400/README400 diff --git a/xs/src/png/zlib/os400/bndsrc b/src/png/zlib/os400/bndsrc similarity index 100% rename from xs/src/png/zlib/os400/bndsrc rename to src/png/zlib/os400/bndsrc diff --git a/xs/src/png/zlib/os400/make.sh b/src/png/zlib/os400/make.sh similarity index 100% rename from xs/src/png/zlib/os400/make.sh rename to src/png/zlib/os400/make.sh diff --git a/xs/src/png/zlib/os400/zlib.inc b/src/png/zlib/os400/zlib.inc similarity index 100% rename from xs/src/png/zlib/os400/zlib.inc rename to src/png/zlib/os400/zlib.inc diff --git a/xs/src/png/zlib/qnx/package.qpg b/src/png/zlib/qnx/package.qpg similarity index 100% rename from xs/src/png/zlib/qnx/package.qpg rename to src/png/zlib/qnx/package.qpg diff --git a/xs/src/png/zlib/treebuild.xml b/src/png/zlib/treebuild.xml similarity index 100% rename from xs/src/png/zlib/treebuild.xml rename to src/png/zlib/treebuild.xml diff --git a/xs/src/png/zlib/trees.c b/src/png/zlib/trees.c similarity index 100% rename from xs/src/png/zlib/trees.c rename to src/png/zlib/trees.c diff --git a/xs/src/png/zlib/trees.h b/src/png/zlib/trees.h similarity index 100% rename from xs/src/png/zlib/trees.h rename to src/png/zlib/trees.h diff --git a/xs/src/png/zlib/uncompr.c b/src/png/zlib/uncompr.c similarity index 100% rename from xs/src/png/zlib/uncompr.c rename to src/png/zlib/uncompr.c diff --git a/xs/src/png/zlib/watcom/watcom_f.mak b/src/png/zlib/watcom/watcom_f.mak similarity index 100% rename from xs/src/png/zlib/watcom/watcom_f.mak rename to src/png/zlib/watcom/watcom_f.mak diff --git a/xs/src/png/zlib/watcom/watcom_l.mak b/src/png/zlib/watcom/watcom_l.mak similarity index 100% rename from xs/src/png/zlib/watcom/watcom_l.mak rename to src/png/zlib/watcom/watcom_l.mak diff --git a/xs/src/png/zlib/win32/DLL_FAQ.txt b/src/png/zlib/win32/DLL_FAQ.txt similarity index 100% rename from xs/src/png/zlib/win32/DLL_FAQ.txt rename to src/png/zlib/win32/DLL_FAQ.txt diff --git a/xs/src/png/zlib/win32/Makefile.bor b/src/png/zlib/win32/Makefile.bor similarity index 100% rename from xs/src/png/zlib/win32/Makefile.bor rename to src/png/zlib/win32/Makefile.bor diff --git a/xs/src/png/zlib/win32/Makefile.gcc b/src/png/zlib/win32/Makefile.gcc similarity index 100% rename from xs/src/png/zlib/win32/Makefile.gcc rename to src/png/zlib/win32/Makefile.gcc diff --git a/xs/src/png/zlib/win32/Makefile.msc b/src/png/zlib/win32/Makefile.msc similarity index 100% rename from xs/src/png/zlib/win32/Makefile.msc rename to src/png/zlib/win32/Makefile.msc diff --git a/xs/src/png/zlib/win32/README-WIN32.txt b/src/png/zlib/win32/README-WIN32.txt similarity index 100% rename from xs/src/png/zlib/win32/README-WIN32.txt rename to src/png/zlib/win32/README-WIN32.txt diff --git a/xs/src/png/zlib/win32/VisualC.txt b/src/png/zlib/win32/VisualC.txt similarity index 100% rename from xs/src/png/zlib/win32/VisualC.txt rename to src/png/zlib/win32/VisualC.txt diff --git a/xs/src/png/zlib/win32/zlib.def b/src/png/zlib/win32/zlib.def similarity index 100% rename from xs/src/png/zlib/win32/zlib.def rename to src/png/zlib/win32/zlib.def diff --git a/xs/src/png/zlib/win32/zlib1.rc b/src/png/zlib/win32/zlib1.rc similarity index 100% rename from xs/src/png/zlib/win32/zlib1.rc rename to src/png/zlib/win32/zlib1.rc diff --git a/xs/src/png/zlib/zconf.h.cmakein b/src/png/zlib/zconf.h.cmakein similarity index 100% rename from xs/src/png/zlib/zconf.h.cmakein rename to src/png/zlib/zconf.h.cmakein diff --git a/xs/src/png/zlib/zconf.h.in b/src/png/zlib/zconf.h.in similarity index 100% rename from xs/src/png/zlib/zconf.h.in rename to src/png/zlib/zconf.h.in diff --git a/xs/src/png/zlib/zconf.h.included b/src/png/zlib/zconf.h.included similarity index 100% rename from xs/src/png/zlib/zconf.h.included rename to src/png/zlib/zconf.h.included diff --git a/xs/src/png/zlib/zlib.3 b/src/png/zlib/zlib.3 similarity index 100% rename from xs/src/png/zlib/zlib.3 rename to src/png/zlib/zlib.3 diff --git a/xs/src/png/zlib/zlib.3.pdf b/src/png/zlib/zlib.3.pdf similarity index 100% rename from xs/src/png/zlib/zlib.3.pdf rename to src/png/zlib/zlib.3.pdf diff --git a/xs/src/png/zlib/zlib.h b/src/png/zlib/zlib.h similarity index 100% rename from xs/src/png/zlib/zlib.h rename to src/png/zlib/zlib.h diff --git a/xs/src/png/zlib/zlib.map b/src/png/zlib/zlib.map similarity index 100% rename from xs/src/png/zlib/zlib.map rename to src/png/zlib/zlib.map diff --git a/xs/src/png/zlib/zlib.pc.cmakein b/src/png/zlib/zlib.pc.cmakein similarity index 100% rename from xs/src/png/zlib/zlib.pc.cmakein rename to src/png/zlib/zlib.pc.cmakein diff --git a/xs/src/png/zlib/zlib.pc.in b/src/png/zlib/zlib.pc.in similarity index 100% rename from xs/src/png/zlib/zlib.pc.in rename to src/png/zlib/zlib.pc.in diff --git a/xs/src/png/zlib/zlib2ansi b/src/png/zlib/zlib2ansi similarity index 100% rename from xs/src/png/zlib/zlib2ansi rename to src/png/zlib/zlib2ansi diff --git a/xs/src/png/zlib/zutil.c b/src/png/zlib/zutil.c similarity index 100% rename from xs/src/png/zlib/zutil.c rename to src/png/zlib/zutil.c diff --git a/xs/src/png/zlib/zutil.h b/src/png/zlib/zutil.h similarity index 100% rename from xs/src/png/zlib/zutil.h rename to src/png/zlib/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 100% rename from xs/src/poly2tri/common/utils.h rename to src/poly2tri/common/utils.h 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 085b396862..03e337db56 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,39 +28,92 @@ #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; -int -main(int argc, char **argv) +/// utility function for displaying CLI usage +void printUsage(); + +using namespace Slic3r; + +int main(int argc, char **argv) { - // Convert arguments to UTF-8 (needed on Windows). - // argv then points to memory owned by a. + // Convert arguments to UTF-8 (needed on Windows). argv then points to memory owned by a. + //FIXME On Windows, we want to receive the arguments as 16bit characters! 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); @@ -57,124 +128,128 @@ 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, false); + 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.translate(0, 0, - model.bounding_box().min(2)); + if (! model.objects.empty()) { 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); - } - + model.objects.front()->cut(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"); - } + // 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.add_model_object(mo); + } + print_config.normalize(); + print.apply_config(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"; +} diff --git a/xs/src/slic3r/AppController.cpp b/src/slic3r/AppController.cpp similarity index 63% rename from xs/src/slic3r/AppController.cpp rename to src/slic3r/AppController.cpp index 2bb79cec1a..4cc87bb7ec 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/src/slic3r/AppController.cpp @@ -1,5 +1,7 @@ #include "AppController.hpp" +#include + #include #include #include @@ -7,16 +9,12 @@ #include #include -#include -#include -#include - #include #include #include #include #include -#include +#include namespace Slic3r { @@ -41,9 +39,9 @@ bool AppControllerGui::is_main_thread() const return m_pri_data->ui_thread == std::this_thread::get_id(); } -namespace GUI { -PresetBundle* get_preset_bundle(); -} +// namespace GUI { +// PresetBundle* get_preset_bundle(); +// } static const PrintObjectStep STEP_SLICE = posSlice; static const PrintObjectStep STEP_PERIMETERS = posPerimeters; @@ -139,119 +137,119 @@ public: void PrintController::slice_to_png() { - using Pointf3 = Vec3d; +// using Pointf3 = Vec3d; - auto ctl = GUI::get_appctl(); - auto presetbundle = GUI::get_preset_bundle(); +// auto ctl = GUI::get_appctl(); +// auto presetbundle = GUI::wxGetApp().preset_bundle; - assert(presetbundle); +// assert(presetbundle); - // FIXME: this crashes in command line mode - auto pt = presetbundle->printers.get_selected_preset().printer_technology(); - if(pt != ptSLA) { - ctl->report_issue(IssueType::ERR, L("Printer technology is not SLA!"), - L("Error")); - return; - } +// // FIXME: this crashes in command line mode +// auto pt = presetbundle->printers.get_selected_preset().printer_technology(); +// if(pt != ptSLA) { +// ctl->report_issue(IssueType::ERR, L("Printer technology is not SLA!"), +// L("Error")); +// return; +// } - auto conf = presetbundle->full_config(); - conf.validate(); +// auto conf = presetbundle->full_config(); +// conf.validate(); - auto exd = query_png_export_data(conf); - if(exd.zippath.empty()) return; +// auto exd = query_png_export_data(conf); +// if(exd.zippath.empty()) return; - Print *print = m_print; +// Print *print = m_print; - try { - print->apply_config(conf); - print->validate(); - } catch(std::exception& e) { - ctl->report_issue(IssueType::ERR, e.what(), "Error"); - return; - } +// try { +// print->apply_config(conf); +// print->validate(); +// } catch(std::exception& e) { +// ctl->report_issue(IssueType::ERR, e.what(), "Error"); +// return; +// } - // TODO: copy the model and work with the copy only - bool correction = false; - if(exd.corr_x != 1.0 || exd.corr_y != 1.0 || exd.corr_z != 1.0) { - correction = true; -// print->invalidate_all_steps(); +// // TODO: copy the model and work with the copy only +// bool correction = false; +// if(exd.corr_x != 1.0 || exd.corr_y != 1.0 || exd.corr_z != 1.0) { +// correction = true; +//// print->invalidate_all_steps(); -// for(auto po : print->objects) { -// po->model_object()->scale( -// Pointf3(exd.corr_x, exd.corr_y, exd.corr_z) -// ); -// po->model_object()->invalidate_bounding_box(); -// po->reload_model_instances(); -// po->invalidate_all_steps(); +//// for(auto po : print->objects) { +//// po->model_object()->scale( +//// Pointf3(exd.corr_x, exd.corr_y, exd.corr_z) +//// ); +//// po->model_object()->invalidate_bounding_box(); +//// po->reload_model_instances(); +//// po->invalidate_all_steps(); +//// } +// } + +// // Turn back the correction scaling on the model. +// auto scale_back = [this, print, correction, exd]() { +// if(correction) { // scale the model back +//// print->invalidate_all_steps(); +//// for(auto po : print->objects) { +//// po->model_object()->scale( +//// Pointf3(1.0/exd.corr_x, 1.0/exd.corr_y, 1.0/exd.corr_z) +//// ); +//// po->model_object()->invalidate_bounding_box(); +//// po->reload_model_instances(); +//// po->invalidate_all_steps(); +//// } // } - } +// }; - // Turn back the correction scaling on the model. - auto scale_back = [this, print, correction, exd]() { - if(correction) { // scale the model back -// print->invalidate_all_steps(); -// for(auto po : print->objects) { -// po->model_object()->scale( -// Pointf3(1.0/exd.corr_x, 1.0/exd.corr_y, 1.0/exd.corr_z) -// ); -// po->model_object()->invalidate_bounding_box(); -// po->reload_model_instances(); -// po->invalidate_all_steps(); -// } - } - }; +// auto print_bb = print->bounding_box(); +// Vec2d punsc = unscale(print_bb.size()); - 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) > exd.width_mm || py(punsc) > exd.height_mm) { +// std::stringstream ss; - // If the print does not fit into the print area we should cry about it. - if(px(punsc) > exd.width_mm || py(punsc) > exd.height_mm) { - std::stringstream ss; +// ss << L("Print will not fit and will be truncated!") << "\n" +// << L("Width needed: ") << px(punsc) << " mm\n" +// << L("Height needed: ") << py(punsc) << " mm\n"; - ss << L("Print will not fit and will be truncated!") << "\n" - << L("Width needed: ") << px(punsc) << " mm\n" - << L("Height needed: ") << py(punsc) << " mm\n"; +// if(!ctl->report_issue(IssueType::WARN_Q, ss.str(), L("Warning"))) { +// scale_back(); +// return; +// } +// } - if(!ctl->report_issue(IssueType::WARN_Q, ss.str(), L("Warning"))) { - scale_back(); - return; - } - } +// auto pri = ctl->create_progress_indicator( +// 200, L("Slicing to zipped png files...")); - auto pri = ctl->create_progress_indicator( - 200, L("Slicing to zipped png files...")); +// pri->on_cancel([&print](){ print->cancel(); }); - pri->on_cancel([&print](){ print->cancel(); }); +// try { +// pri->update(0, L("Slicing...")); +// slice(pri); +// } catch (std::exception& e) { +// ctl->report_issue(IssueType::ERR, e.what(), L("Exception occurred")); +// scale_back(); +// if(print->canceled()) print->restart(); +// return; +// } - try { - pri->update(0, L("Slicing...")); - slice(pri); - } catch (std::exception& e) { - ctl->report_issue(IssueType::ERR, e.what(), L("Exception occurred")); - scale_back(); - if(print->canceled()) print->restart(); - return; - } +// auto initstate = unsigned(pri->state()); +// print->set_status_callback([pri, initstate](int st, const std::string& msg) +// { +// pri->update(initstate + unsigned(st), msg); +// }); - auto initstate = unsigned(pri->state()); - print->set_status_callback([pri, initstate](int st, const std::string& msg) - { - pri->update(initstate + unsigned(st), msg); - }); +// try { +// print_to( *print, exd.zippath, +// exd.width_mm, exd.height_mm, +// exd.width_px, exd.height_px, +// exd.exp_time_s, exd.exp_time_first_s); - try { - print_to( *print, exd.zippath, - exd.width_mm, exd.height_mm, - exd.width_px, exd.height_px, - exd.exp_time_s, exd.exp_time_first_s); +// } catch (std::exception& e) { +// ctl->report_issue(IssueType::ERR, e.what(), L("Exception occurred")); +// } - } catch (std::exception& e) { - ctl->report_issue(IssueType::ERR, e.what(), L("Exception occurred")); - } - - scale_back(); - if(print->canceled()) print->restart(); - print->set_status_default(); +// scale_back(); +// if(print->canceled()) print->restart(); +// print->set_status_default(); } const PrintConfig &PrintController::config() const diff --git a/xs/src/slic3r/AppController.hpp b/src/slic3r/AppController.hpp similarity index 100% rename from xs/src/slic3r/AppController.hpp rename to src/slic3r/AppController.hpp diff --git a/xs/src/slic3r/AppControllerWx.cpp b/src/slic3r/AppControllerWx.cpp similarity index 100% rename from xs/src/slic3r/AppControllerWx.cpp rename to src/slic3r/AppControllerWx.cpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt new file mode 100644 index 0000000000..7837ae2b33 --- /dev/null +++ b/src/slic3r/CMakeLists.txt @@ -0,0 +1,122 @@ +add_library(libslic3r_gui STATIC + ${LIBDIR}/slic3r/GUI/AboutDialog.cpp + ${LIBDIR}/slic3r/GUI/AboutDialog.hpp + ${LIBDIR}/slic3r/GUI/SysInfoDialog.cpp + ${LIBDIR}/slic3r/GUI/SysInfoDialog.hpp + ${LIBDIR}/slic3r/GUI/AppConfig.cpp + ${LIBDIR}/slic3r/GUI/AppConfig.hpp + ${LIBDIR}/slic3r/GUI/BackgroundSlicingProcess.cpp + ${LIBDIR}/slic3r/GUI/BackgroundSlicingProcess.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/GLToolbar.hpp + ${LIBDIR}/slic3r/GUI/GLToolbar.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/GUI_Preview.cpp + ${LIBDIR}/slic3r/GUI/GUI_Preview.hpp + ${LIBDIR}/slic3r/GUI/GUI_PreviewIface.cpp + ${LIBDIR}/slic3r/GUI/GUI_PreviewIface.hpp + ${LIBDIR}/slic3r/GUI/GUI_App.cpp + ${LIBDIR}/slic3r/GUI/GUI_App.hpp + ${LIBDIR}/slic3r/GUI/GUI_Utils.cpp + ${LIBDIR}/slic3r/GUI/GUI_Utils.hpp + ${LIBDIR}/slic3r/GUI/MainFrame.cpp + ${LIBDIR}/slic3r/GUI/MainFrame.hpp + ${LIBDIR}/slic3r/GUI/Plater.cpp + ${LIBDIR}/slic3r/GUI/Plater.hpp + ${LIBDIR}/slic3r/GUI/GUI_ObjectList.cpp + ${LIBDIR}/slic3r/GUI/GUI_ObjectList.hpp + ${LIBDIR}/slic3r/GUI/GUI_ObjectManipulation.cpp + ${LIBDIR}/slic3r/GUI/GUI_ObjectManipulation.hpp + ${LIBDIR}/slic3r/GUI/LambdaObjectDialog.cpp + ${LIBDIR}/slic3r/GUI/LambdaObjectDialog.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/GUI/ProgressIndicator.hpp + ${LIBDIR}/slic3r/GUI/ProgressStatusBar.hpp + ${LIBDIR}/slic3r/GUI/ProgressStatusBar.cpp + ${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/AppController.hpp + ${LIBDIR}/slic3r/AppController.cpp + ${LIBDIR}/slic3r/AppControllerWx.cpp +) + +target_link_libraries(libslic3r_gui libslic3r avrdude) diff --git a/xs/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp similarity index 100% rename from xs/src/slic3r/Config/Snapshot.cpp rename to src/slic3r/Config/Snapshot.cpp 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 100% rename from xs/src/slic3r/GUI/2DBed.cpp rename to src/slic3r/GUI/2DBed.cpp diff --git a/xs/src/slic3r/GUI/2DBed.hpp b/src/slic3r/GUI/2DBed.hpp similarity index 100% rename from xs/src/slic3r/GUI/2DBed.hpp rename to src/slic3r/GUI/2DBed.hpp diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp similarity index 88% rename from xs/src/slic3r/GUI/3DScene.cpp rename to src/slic3r/GUI/3DScene.cpp index 3eb1746941..91f60947c5 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -36,7 +36,7 @@ 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](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2)); @@ -52,7 +52,7 @@ 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](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2)); @@ -196,21 +196,21 @@ const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f }; GLVolume::GLVolume(float r, float g, float b, float a) : m_offset(Vec3d::Zero()) -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM , m_rotation(Vec3d::Zero()) , m_scaling_factor(Vec3d::Ones()) -#else - , m_rotation(0.0) - , m_scaling_factor(1.0) -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_MIRROR + , m_mirror(Vec3d::Ones()) +#endif // ENABLE_MIRROR , m_world_matrix(Transform3f::Identity()) , m_world_matrix_dirty(true) , m_transformed_bounding_box_dirty(true) , m_transformed_convex_hull_bounding_box_dirty(true) , m_convex_hull(nullptr) , composite_id(-1) +#if !ENABLE_EXTENDED_SELECTION , select_group_id(-1) , drag_group_id(-1) +#endif // !ENABLE_EXTENDED_SELECTION , extruder_id(0) , selected(false) , is_active(true) @@ -242,7 +242,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]; } @@ -260,7 +260,6 @@ void GLVolume::set_render_color() set_render_color(color, 4); } -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM const Vec3d& GLVolume::get_rotation() const { return m_rotation; @@ -268,31 +267,31 @@ const Vec3d& GLVolume::get_rotation() const void GLVolume::set_rotation(const Vec3d& rotation) { - if (m_rotation != rotation) - { - m_rotation = rotation; - m_world_matrix_dirty = true; - m_transformed_bounding_box_dirty = true; - m_transformed_convex_hull_bounding_box_dirty = true; - } -} -#else -double GLVolume::get_rotation() const -{ - return m_rotation; -} +#if ENABLE_EXTENDED_SELECTION + static const double TWO_PI = 2.0 * (double)PI; +#endif // ENABLE_EXTENDED_SELECTION -void GLVolume::set_rotation(double rotation) -{ if (m_rotation != rotation) { m_rotation = rotation; +#if ENABLE_EXTENDED_SELECTION + 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; + } + } +#endif // ENABLE_EXTENDED_SELECTION m_world_matrix_dirty = true; m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; } } -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM const Vec3d& GLVolume::get_offset() const { @@ -310,7 +309,13 @@ void GLVolume::set_offset(const Vec3d& offset) } } -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_EXTENDED_SELECTION +const Vec3d& GLVolume::get_scaling_factor() const +{ + return m_scaling_factor; +} +#endif // ENABLE_EXTENDED_SELECTION + void GLVolume::set_scaling_factor(const Vec3d& scaling_factor) { if (m_scaling_factor != scaling_factor) @@ -321,24 +326,47 @@ void GLVolume::set_scaling_factor(const Vec3d& scaling_factor) m_transformed_convex_hull_bounding_box_dirty = true; } } -#else -void GLVolume::set_scaling_factor(double factor) + +#if ENABLE_MIRROR +const Vec3d& GLVolume::get_mirror() const { - if (m_scaling_factor != factor) + 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_scaling_factor = factor; + m_mirror = mirror; m_world_matrix_dirty = true; m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; } } -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + +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_MIRROR void GLVolume::set_convex_hull(const TriangleMesh& convex_hull) { m_convex_hull = &convex_hull; } +#if !ENABLE_EXTENDED_SELECTION void GLVolume::set_select_group_id(const std::string& select_by) { if (select_by == "object") @@ -356,22 +384,17 @@ void GLVolume::set_drag_group_id(const std::string& drag_by) else if (drag_by == "instance") drag_group_id = object_idx() * 1000 + instance_idx(); } +#endif // !ENABLE_EXTENDED_SELECTION const Transform3f& GLVolume::world_matrix() const { if (m_world_matrix_dirty) { - m_world_matrix = Transform3f::Identity(); - m_world_matrix.translate(m_offset.cast()); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation(2), Vec3f::UnitZ())); - m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation(1), Vec3f::UnitY())); - m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation(0), Vec3f::UnitX())); - m_world_matrix.scale(m_scaling_factor.cast()); +#if ENABLE_MIRROR + m_world_matrix = Geometry::assemble_transform(m_offset, m_rotation, m_scaling_factor, m_mirror).cast(); #else - m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation, Vec3f::UnitZ())); - m_world_matrix.scale((float)m_scaling_factor); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + m_world_matrix = Geometry::assemble_transform(m_offset, m_rotation, m_scaling_factor).cast(); +#endif // ENABLE_MIRROR m_world_matrix_dirty = false; } return m_world_matrix; @@ -446,13 +469,7 @@ void GLVolume::render() const ::glCullFace(GL_BACK); ::glPushMatrix(); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM ::glMultMatrixf(world_matrix().data()); -#else - ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); - ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); - ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM if (this->indexed_vertex_array.indexed()) this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); else @@ -544,7 +561,7 @@ 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); @@ -563,7 +580,7 @@ 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); @@ -577,13 +594,7 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c ::glPushMatrix(); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM ::glMultMatrixf(world_matrix().data()); -#else - ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); - ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); - ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM if (n_triangles > 0) { @@ -612,7 +623,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); @@ -621,19 +632,13 @@ 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(); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM ::glMultMatrixf(world_matrix().data()); -#else - ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); - ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); - ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM if (n_triangles > 0) ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first); @@ -682,6 +687,14 @@ void GLVolume::generate_layer_height_texture(const PrintObject *print_object, bo #define LAYER_HEIGHT_TEXTURE_WIDTH 1024 #define LAYER_HEIGHT_TEXTURE_HEIGHT 1024 +#if ENABLE_EXTENDED_SELECTION +std::vector GLVolumeCollection::load_object( + const ModelObject *model_object, + int obj_idx, + const std::vector &instance_idxs, + const std::string &color_by, + bool use_VBOs) +#else std::vector GLVolumeCollection::load_object( const ModelObject *model_object, int obj_idx, @@ -690,6 +703,7 @@ std::vector GLVolumeCollection::load_object( const std::string &select_by, const std::string &drag_by, bool use_VBOs) +#endif // ENABLE_EXTENDED_SELECTION { static float colors[4][4] = { { 1.0f, 1.0f, 0.0f, 1.f }, @@ -740,8 +754,10 @@ std::vector GLVolumeCollection::load_object( 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 !ENABLE_EXTENDED_SELECTION v.set_select_group_id(select_by); v.set_drag_group_id(drag_by); +#endif // !ENABLE_EXTENDED_SELECTION if (model_volume->is_model_part()) { v.set_convex_hull(model_volume->get_convex_hull()); @@ -751,15 +767,12 @@ std::vector GLVolumeCollection::load_object( } v.is_modifier = ! model_volume->is_model_part(); v.shader_outside_printer_detection_enabled = model_volume->is_model_part(); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM v.set_offset(instance->get_offset()); v.set_rotation(instance->get_rotation()); v.set_scaling_factor(instance->get_scaling_factor()); -#else - v.set_offset(Vec3d(instance->offset(0), instance->offset(1), 0.0)); - v.set_rotation(instance->rotation); - v.set_scaling_factor(instance->scaling_factor); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_MIRROR + v.set_mirror(instance->get_mirror()); +#endif // ENABLE_MIRROR } } @@ -789,9 +802,9 @@ int GLVolumeCollection::load_wipe_tower_preview( // 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}, + 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) @@ -834,8 +847,10 @@ int GLVolumeCollection::load_wipe_tower_preview( v.bounding_box = v.indexed_vertex_array.bounding_box(); v.indexed_vertex_array.finalize_geometry(use_VBOs); v.composite_id = obj_idx * 1000000; +#if !ENABLE_EXTENDED_SELECTION v.select_group_id = obj_idx * 1000000; v.drag_group_id = obj_idx * 1000; +#endif // !ENABLE_EXTENDED_SELECTION v.is_wipe_tower = true; v.shader_outside_printer_detection_enabled = ! size_unknown; return int(this->volumes.size() - 1); @@ -1017,7 +1032,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]; @@ -1031,6 +1046,7 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con } } +#if !ENABLE_EXTENDED_SELECTION void GLVolumeCollection::set_select_by(const std::string& select_by) { for (GLVolume *vol : this->volumes) @@ -1048,6 +1064,7 @@ void GLVolumeCollection::set_drag_by(const std::string& drag_by) vol->set_drag_group_id(drag_by); } } +#endif // !ENABLE_EXTENDED_SELECTION std::vector GLVolumeCollection::get_current_print_zs(bool active_only) const { @@ -1820,6 +1837,7 @@ void _3DScene::reset_volumes(wxGLCanvas* canvas) s_canvas_mgr.reset_volumes(canvas); } +#if !ENABLE_EXTENDED_SELECTION void _3DScene::deselect_volumes(wxGLCanvas* canvas) { s_canvas_mgr.deselect_volumes(canvas); @@ -1834,6 +1852,7 @@ void _3DScene::update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections) { s_canvas_mgr.set_objects_selections(canvas, selections); } +#endif // ENABLE_EXTENDED_SELECTION void _3DScene::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config) { @@ -1900,6 +1926,7 @@ void _3DScene::set_color_by(wxGLCanvas* canvas, const std::string& value) s_canvas_mgr.set_color_by(canvas, value); } +#if !ENABLE_EXTENDED_SELECTION void _3DScene::set_select_by(wxGLCanvas* canvas, const std::string& value) { s_canvas_mgr.set_select_by(canvas, value); @@ -1914,6 +1941,7 @@ std::string _3DScene::get_select_by(wxGLCanvas* canvas) { return s_canvas_mgr.get_select_by(canvas); } +#endif // !ENABLE_EXTENDED_SELECTION bool _3DScene::is_layers_editing_enabled(wxGLCanvas* canvas) { @@ -2045,192 +2073,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) -{ -#if !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - s_canvas_mgr.register_on_gizmo_scale_uniformly_callback(canvas, callback); -#endif // !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -} - -void _3DScene::register_on_gizmo_scale_3D_callback(wxGLCanvas* canvas, void* callback) -{ -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - s_canvas_mgr.register_on_gizmo_scale_3D_callback(canvas, callback); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -} - -void _3DScene::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback) -{ -#if !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - s_canvas_mgr.register_on_gizmo_rotate_callback(canvas, callback); -#endif // !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -} - -void _3DScene::register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback) -{ -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - s_canvas_mgr.register_on_gizmo_rotate_3D_callback(canvas, callback); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -} - -void _3DScene::register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback) -{ -#if !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - s_canvas_mgr.register_on_gizmo_flatten_callback(canvas, callback); -#endif // !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -} - -void _3DScene::register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback) -{ -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - s_canvas_mgr.register_on_gizmo_flatten_3D_callback(canvas, callback); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -} - -void _3DScene::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback) -{ -#if !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - s_canvas_mgr.register_on_update_geometry_info_callback(canvas, callback); -#endif // !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -} - -void _3DScene::register_on_update_geometry_3D_info_callback(wxGLCanvas* canvas, void* callback) -{ -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - s_canvas_mgr.register_on_update_geometry_3D_info_callback(canvas, callback); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -} - -void _3DScene::register_action_add_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_add_callback(canvas, callback); -} - -void _3DScene::register_action_delete_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_delete_callback(canvas, callback); -} - -void _3DScene::register_action_deleteall_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_deleteall_callback(canvas, callback); -} - -void _3DScene::register_action_arrange_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_arrange_callback(canvas, callback); -} - -void _3DScene::register_action_more_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_more_callback(canvas, callback); -} - -void _3DScene::register_action_fewer_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_fewer_callback(canvas, callback); -} - -void _3DScene::register_action_split_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_split_callback(canvas, callback); -} - -void _3DScene::register_action_cut_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_cut_callback(canvas, callback); -} - -void _3DScene::register_action_settings_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_settings_callback(canvas, callback); -} - -void _3DScene::register_action_layersediting_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_layersediting_callback(canvas, callback); -} - -void _3DScene::register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_selectbyparts_callback(canvas, callback); -} - static inline int hex_digit_to_int(const char c) { return @@ -2278,6 +2120,15 @@ int _3DScene::get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx) return s_canvas_mgr.get_in_object_volume_id(canvas, scene_vol_idx); } +#if ENABLE_MIRROR +#if ENABLE_EXTENDED_SELECTION +void _3DScene::mirror_selection(wxGLCanvas* canvas, Axis axis) +{ + s_canvas_mgr.mirror_selection(canvas, axis); +} +#endif // ENABLE_EXTENDED_SELECTION +#endif // ENABLE_MIRROR + void _3DScene::reload_scene(wxGLCanvas* canvas, bool force) { s_canvas_mgr.reload_scene(canvas, force); @@ -2293,9 +2144,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 88% rename from xs/src/slic3r/GUI/3DScene.hpp rename to src/slic3r/GUI/3DScene.hpp index 484d6028d1..7e74ff92f2 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -256,17 +256,14 @@ public: private: // Offset of the volume to be rendered. Vec3d m_offset; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM // 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; -#else - // Rotation around Z axis of the volume to be rendered. - double m_rotation; - // Scale factor of the volume to be rendered. - double m_scaling_factor; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_MIRROR + // Mirroring along the three axes of the volume to be rendered. + Vec3d m_mirror; +#endif // ENABLE_MIRROR // World matrix of the volume to be rendered. mutable Transform3f m_world_matrix; // Whether or not is needed to recalculate the world matrix. @@ -292,10 +289,12 @@ public: float render_color[4]; // An ID containing the object ID, volume ID and instance ID. int composite_id; +#if !ENABLE_EXTENDED_SELECTION // 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; +#endif // !ENABLE_EXTENDED_SELECTION // An ID containing the extruder ID (used to select color). int extruder_id; // Is this object selected? @@ -334,25 +333,30 @@ public: // Sets render color in dependence of current state void set_render_color(); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM const Vec3d& get_rotation() const; void set_rotation(const Vec3d& rotation); +#if ENABLE_EXTENDED_SELECTION + const Vec3d& get_scaling_factor() const; +#endif // ENABLE_EXTENDED_SELECTION void set_scaling_factor(const Vec3d& scaling_factor); -#else - double get_rotation() const; - void set_rotation(double rotation); - void set_scaling_factor(double factor); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_MIRROR + const Vec3d& get_mirror() const; + double get_mirror(Axis axis) const; + void set_mirror(const Vec3d& mirror); + void set_mirror(Axis axis, double mirror); +#endif // ENABLE_MIRROR const Vec3d& get_offset() const; void set_offset(const Vec3d& offset); void set_convex_hull(const TriangleMesh& convex_hull); +#if !ENABLE_EXTENDED_SELECTION void set_select_group_id(const std::string& select_by); void set_drag_group_id(const std::string& drag_by); +#endif // !ENABLE_EXTENDED_SELECTION int object_idx() const { return this->composite_id / 1000000; } int volume_idx() const { return (this->composite_id / 1000) % 1000; } @@ -410,6 +414,10 @@ public: void reset_layer_height_texture_data() { layer_height_texture_data.reset(); } }; +#if ENABLE_EXTENDED_SELECTION +typedef std::vector GLVolumePtrs; +#endif // ENABLE_EXTENDED_SELECTION + class GLVolumeCollection { // min and max vertex of the print box volume @@ -417,11 +425,23 @@ class GLVolumeCollection float print_box_max[3]; public: +#if ENABLE_EXTENDED_SELECTION + GLVolumePtrs volumes; +#else std::vector volumes; - +#endif // ENABLE_EXTENDED_SELECTION + GLVolumeCollection() {}; ~GLVolumeCollection() { clear(); }; +#if ENABLE_EXTENDED_SELECTION + std::vector load_object( + const ModelObject *model_object, + int obj_idx, + const std::vector &instance_idxs, + const std::string &color_by, + bool use_VBOs); +#else std::vector load_object( const ModelObject *model_object, int obj_idx, @@ -430,6 +450,7 @@ public: const std::string &select_by, const std::string &drag_by, bool use_VBOs); +#endif // ENABLE_EXTENDED_SELECTION int 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); @@ -463,8 +484,10 @@ public: void update_colors_by_extruder(const DynamicPrintConfig* config); +#if !ENABLE_EXTENDED_SELECTION void set_select_by(const std::string& select_by); void set_drag_by(const std::string& drag_by); +#endif // !ENABLE_EXTENDED_SELECTION // Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection std::vector get_current_print_zs(bool active_only) const; @@ -493,14 +516,20 @@ public: static unsigned int get_volumes_count(wxGLCanvas* canvas); static void reset_volumes(wxGLCanvas* canvas); +#if !ENABLE_EXTENDED_SELECTION 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); +#endif // !ENABLE_EXTENDED_SELECTION 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); +#if ENABLE_EXTENDED_SELECTION + static GUI::GLCanvas3D* get_canvas(wxGLCanvas* canvas); +#else static void set_objects_selections(wxGLCanvas* canvas, const std::vector& selections); +#endif // ENABLE_EXTENDED_SELECTION static void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); static void set_print(wxGLCanvas* canvas, Print* print); @@ -516,10 +545,12 @@ public: static void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); static void set_color_by(wxGLCanvas* canvas, const std::string& value); +#if !ENABLE_EXTENDED_SELECTION static void set_select_by(wxGLCanvas* canvas, const std::string& value); static void set_drag_by(wxGLCanvas* canvas, const std::string& value); static std::string get_select_by(wxGLCanvas* canvas); +#endif // !ENABLE_EXTENDED_SELECTION static bool is_layers_editing_enabled(wxGLCanvas* canvas); static bool is_layers_editing_allowed(wxGLCanvas* canvas); @@ -555,54 +586,24 @@ public: 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_scale_3D_callback(wxGLCanvas* canvas, void* callback); - static void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback); - static void register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback); - static void register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback); - static void register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback); - static void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback); - static void register_on_update_geometry_3D_info_callback(wxGLCanvas* canvas, void* callback); - - static void register_action_add_callback(wxGLCanvas* canvas, void* callback); - static void register_action_delete_callback(wxGLCanvas* canvas, void* callback); - static void register_action_deleteall_callback(wxGLCanvas* canvas, void* callback); - static void register_action_arrange_callback(wxGLCanvas* canvas, void* callback); - static void register_action_more_callback(wxGLCanvas* canvas, void* callback); - static void register_action_fewer_callback(wxGLCanvas* canvas, void* callback); - static void register_action_split_callback(wxGLCanvas* canvas, void* callback); - static void register_action_cut_callback(wxGLCanvas* canvas, void* callback); - static void register_action_settings_callback(wxGLCanvas* canvas, void* callback); - static void register_action_layersediting_callback(wxGLCanvas* canvas, void* callback); - static void register_action_selectbyparts_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 int get_first_volume_id(wxGLCanvas* canvas, int obj_idx); static int get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx); +#if ENABLE_MIRROR +#if ENABLE_EXTENDED_SELECTION + static void mirror_selection(wxGLCanvas* canvas, Axis axis); +#endif // ENABLE_EXTENDED_SELECTION +#endif // ENABLE_MIRROR + 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); 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 100% rename from xs/src/slic3r/GUI/AppConfig.cpp rename to src/slic3r/GUI/AppConfig.cpp 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/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp similarity index 58% rename from xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp rename to src/slic3r/GUI/BackgroundSlicingProcess.cpp index 99997e3905..17d290db73 100644 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -1,5 +1,5 @@ #include "BackgroundSlicingProcess.hpp" -#include "GUI.hpp" +#include "GUI_App.hpp" #include #include @@ -15,18 +15,16 @@ #include #include +#include #include namespace Slic3r { -namespace GUI { - extern wxPanel *g_wxPlater; -}; - BackgroundSlicingProcess::BackgroundSlicingProcess() { - m_temp_output_path = wxStandardPaths::Get().GetTempDir().utf8_str().data(); - m_temp_output_path += (boost::format(".%1%.gcode") % get_current_pid()).str(); + 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() @@ -59,14 +57,22 @@ void BackgroundSlicingProcess::thread_proc() assert(m_print != nullptr); m_print->process(); if (! m_print->canceled()) { - wxQueueEvent(GUI::g_wxPlater, new wxCommandEvent(m_event_sliced_id)); + wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_sliced_id)); m_print->export_gcode(m_temp_output_path, m_gcode_preview_data); - if (! m_print->canceled() && ! m_output_path.empty()) { - if (copy_file(m_temp_output_path, m_output_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_output_path, m_print->config()); - } + 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_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); + } } } catch (CanceledException &ex) { // Canceled, this is all right. @@ -81,7 +87,7 @@ void BackgroundSlicingProcess::thread_proc() wxCommandEvent evt(m_event_finished_id); evt.SetString(error); evt.SetInt(m_print->canceled() ? -1 : (error.empty() ? 1 : 0)); - wxQueueEvent(GUI::g_wxPlater, evt.Clone()); + 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. @@ -128,6 +134,7 @@ bool BackgroundSlicingProcess::start() 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(); }); lck.unlock(); m_condition.notify_one(); return true; @@ -137,21 +144,23 @@ bool BackgroundSlicingProcess::stop() { std::unique_lock lck(m_mutex); if (m_state == STATE_INITIAL) { - this->m_output_path.clear(); +// this->m_export_path.clear(); return false; } - assert(this->running()); +// 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_output_path.clear(); +// this->m_export_path.clear(); return true; } @@ -164,4 +173,62 @@ bool BackgroundSlicingProcess::apply_config(const DynamicPrintConfig &config) return invalidated; } +// 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) +{ +// this->stop(); + 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/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp similarity index 71% rename from xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp rename to src/slic3r/GUI/BackgroundSlicingProcess.hpp index cc7a6db307..dab40d3a8b 100644 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -6,11 +6,18 @@ #include #include +#include "Print.hpp" + namespace Slic3r { class DynamicPrintConfig; class GCodePreviewData; -class Print; +class Model; + +// 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. @@ -31,8 +38,6 @@ public: // 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; } - // Set the output path of the G-code. - void set_output_path(const std::string &path) { m_output_path = path; } // 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. @@ -42,6 +47,16 @@ public: // 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. bool apply_config(const DynamicPrintConfig &config); + // 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); + // 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). @@ -70,8 +85,11 @@ private: Print *m_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; - std::string m_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; @@ -80,6 +98,14 @@ private: 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. diff --git a/xs/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp similarity index 96% rename from xs/src/slic3r/GUI/BedShapeDialog.cpp rename to src/slic3r/GUI/BedShapeDialog.cpp index e04f2b3700..407da7ae39 100644 --- a/xs/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -291,14 +291,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; diff --git a/xs/src/slic3r/GUI/BedShapeDialog.hpp b/src/slic3r/GUI/BedShapeDialog.hpp similarity index 100% rename from xs/src/slic3r/GUI/BedShapeDialog.hpp rename to src/slic3r/GUI/BedShapeDialog.hpp 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 100% rename from xs/src/slic3r/GUI/BonjourDialog.cpp rename to src/slic3r/GUI/BonjourDialog.cpp 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 85% rename from xs/src/slic3r/GUI/ButtonsDescription.cpp rename to src/slic3r/GUI/ButtonsDescription.cpp index 5739fc90e3..8018382169 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,8 +37,8 @@ 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()); @@ -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 100% rename from xs/src/slic3r/GUI/ButtonsDescription.hpp rename to src/slic3r/GUI/ButtonsDescription.hpp 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..e7b1868d01 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); diff --git a/xs/src/slic3r/GUI/ConfigWizard.hpp b/src/slic3r/GUI/ConfigWizard.hpp similarity index 100% rename from xs/src/slic3r/GUI/ConfigWizard.hpp rename to src/slic3r/GUI/ConfigWizard.hpp 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 98% rename from xs/src/slic3r/GUI/Field.cpp rename to src/slic3r/GUI/Field.cpp index f143e8bc6a..29d23eee58 100644 --- a/xs/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -7,6 +7,7 @@ #include #include "PrintConfig.hpp" #include +#include "GUI_App.hpp" namespace Slic3r { namespace GUI { @@ -609,15 +610,21 @@ 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(){ @@ -707,7 +714,7 @@ void StaticText::BUILD() 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); diff --git a/xs/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp similarity index 99% rename from xs/src/slic3r/GUI/Field.hpp rename to src/slic3r/GUI/Field.hpp index c38658e2b9..58c6afebe0 100644 --- a/xs/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -54,10 +54,13 @@ public: virtual bool AcceptsFocusFromKeyboard() const { return false; } + void set_as_hidden() { + Hide(); + hidden = true; + } + virtual bool Show(bool show = true) override { - if (!show) - hidden = true; - return wxButton::Show(!hidden); + return wxButton::Show(hidden ? false : show); } }; 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 75% rename from xs/src/slic3r/GUI/GLCanvas3D.cpp rename to src/slic3r/GUI/GLCanvas3D.cpp index cd1dde8f48..c83a5a0389 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -10,15 +10,19 @@ #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" @@ -316,6 +320,7 @@ 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; @@ -412,7 +417,7 @@ void GLCanvas3D::Bed::_calc_gridlines(const ExPolygon& poly, const BoundingBox& } // 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); @@ -426,7 +431,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(); @@ -598,7 +603,8 @@ bool GLCanvas3D::Bed::_are_equal(const Pointfs& bed_1, const Pointfs& bed_2) } GLCanvas3D::Axes::Axes() - : origin(0, 0, 0), length(0.0f) + : origin(Vec3d::Zero()) + , length(0.0f) { } @@ -613,11 +619,11 @@ 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(0), (GLfloat)origin(1), (GLfloat)origin(2)); + ::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(0), (GLfloat)origin(1), (GLfloat)origin(2)); + ::glVertex3dv(origin.data()); ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1) + length, (GLfloat)origin(2)); ::glEnd(); // draw line for Z axis @@ -627,7 +633,7 @@ void GLCanvas3D::Axes::render(bool depth_test) const ::glBegin(GL_LINES); ::glColor3f(0.0f, 0.0f, 1.0f); - ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2)); + ::glVertex3dv(origin.data()); ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2) + length); ::glEnd(); } @@ -642,7 +648,7 @@ 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)); + ExPolygons expolygons = offset_ex(polygons, (float)scale_(0.1)); Lines lines = to_lines(expolygons); return m_lines.set_from_lines(lines, m_z); } @@ -1087,10 +1093,14 @@ 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) +#if !ENABLE_EXTENDED_SELECTION , volume_center_offset(0, 0, 0) , move_with_shift(false) +#endif // !ENABLE_EXTENDED_SELECTION , move_volume_idx(-1) +#if !ENABLE_EXTENDED_SELECTION , gizmo_volume_idx(-1) +#endif // !ENABLE_EXTENDED_SELECTION { } @@ -1123,6 +1133,703 @@ bool GLCanvas3D::Mouse::is_start_position_3D_defined() const return (drag.start_position_3D != Drag::Invalid_3D_Point); } +#if ENABLE_EXTENDED_SELECTION +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); +} + +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; + + // resets the current list if needed + const GLVolume* volume = (*m_volumes)[volume_idx]; + if (as_single_selection || volume->is_wipe_tower || is_wipe_tower() || volume->is_modifier || is_modifier()) + clear(); + + switch (m_mode) + { + case Volume: + { + _add_volume(volume_idx); + break; + } + case Instance: + { + _add_instance(volume->object_idx(), volume->instance_idx()); + break; + } + case Object: + { + _add_object(volume->object_idx()); + break; + } + } + + _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; + } + case Object: + { + _remove_object(volume->object_idx()); + break; + } + } + + _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(); + + _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(); + + _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, bool as_single_selection) +{ + if (!m_valid) + return; + + // resets the current list if needed + if (as_single_selection) + clear(); + + 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)) + _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 (object_idx != -1) + { + if ((object_idx != -1) && (object_idx < 1000)) + 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 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) + { + (*m_volumes)[i]->set_offset(m_cache.volumes_data[i].get_position() + displacement); + } + + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::rotate(const Vec3d& rotation) +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + if (is_single_full_instance()) + (*m_volumes)[i]->set_rotation(rotation); + else + { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + // 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); + } + } + + if (m_mode == Instance) + _synchronize_unselected_instances(); + + 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()) + (*m_volumes)[i]->set_scaling_factor(scale); + else + { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); + 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); + } + } + + if (m_mode == Instance) + _synchronize_unselected_instances(); + + m_bounding_box_dirty = true; +} + +#if ENABLE_MIRROR +void GLCanvas3D::Selection::mirror(Axis axis) +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + if (is_single_full_instance()) + (*m_volumes)[i]->set_mirror(axis, -(*m_volumes)[i]->get_mirror(axis)); + } + + if (m_mode == Instance) + _synchronize_unselected_instances(); + + m_bounding_box_dirty = true; +} +#endif // ENABLE_MIRROR + +void GLCanvas3D::Selection::render(bool show_indirect_selection) const +{ + if (is_empty()) + return; + + // render cumulative bounding box of selected volumes + _render_selected_volumes(); + + // render bounding boxes of indirectly selected instances + if (show_indirect_selection && (m_mode == Instance)) + _render_unselected_instances(); +} + +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); + } + + 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 = Modifier; + 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 + { + 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(); + if (volumes_count * instances_count == (unsigned int)m_list.size()) + m_type = SingleFullObject; + else if ((m_cache.content.begin()->second.size() == 1) && (volumes_count == (unsigned int)m_list.size())) + m_type = SingleFullInstance; + } + } + } + + 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 Modifier: + { + std::cout << "selection type: Modifier" << std::endl; + break; + } + case SingleFullObject: + { + std::cout << "selection type: SingleFullObject" << std::endl; + break; + } + case SingleFullInstance: + { + std::cout << "selection type: SingleFullInstance" << 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]; + m_cache.volumes_data.emplace(i, VolumeCache(v->get_offset(), v->get_rotation(), v->get_scaling_factor())); + } + 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_unselected_instances() const +{ + std::set done; // prevent processing volumes twice + done.insert(m_list.begin(), m_list.end()); + + typedef std::map, BoundingBoxf3> InstanceToBoxMap; + InstanceToBoxMap boxes; + 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(); + + 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]; + int i_idx = v->instance_idx(); + if ((v->object_idx() != object_idx) || (i_idx == instance_idx)) + continue; + + std::pair box_id(object_idx, i_idx); + InstanceToBoxMap::iterator it = boxes.find(box_id); + if (it == boxes.end()) + it = boxes.insert(InstanceToBoxMap::value_type(box_id, BoundingBoxf3())).first; + + it->second.merge(v->transformed_convex_hull_bounding_box()); + + done.insert(j); + } + } + + float color[3] = { 1.0f, 1.0f, 0.0f }; + for (const InstanceToBoxMap::value_type& box : boxes) + { + _render_bounding_box(box.second, 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.25f * box.size().cast(); + + ::glEnable(GL_DEPTH_TEST); + + ::glColor3fv(color); + + ::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(); + const Vec3d& rotation = volume->get_rotation(); + const Vec3d& scaling_factor = volume->get_scaling_factor(); +#if ENABLE_MIRROR + const Vec3d& mirror = volume->get_mirror(); +#endif // ENABLE_MIRROR + + // 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; + + v->set_rotation(rotation); + v->set_scaling_factor(scaling_factor); +#if ENABLE_MIRROR + v->set_mirror(mirror); +#endif // ENABLE_MIRROR + + done.insert(j); + } + } +} +#endif // ENABLE_EXTENDED_SELECTION + const float GLCanvas3D::Gizmos::OverlayTexturesScale = 0.75f; const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f * OverlayTexturesScale; const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale; @@ -1147,11 +1854,6 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) if (!gizmo->init()) return false; -#if !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - // temporary disable z grabber - gizmo->disable_grabber(2); -#endif // !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - m_gizmos.insert(GizmosMap::value_type(Move, gizmo)); gizmo = new GLGizmoScale3D(parent); @@ -1161,18 +1863,6 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) if (!gizmo->init()) return false; -#if !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - // temporary disable x grabbers - gizmo->disable_grabber(0); - gizmo->disable_grabber(1); - // temporary disable y grabbers - gizmo->disable_grabber(2); - gizmo->disable_grabber(3); - // temporary disable z grabbers - gizmo->disable_grabber(4); - gizmo->disable_grabber(5); -#endif // !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - m_gizmos.insert(GizmosMap::value_type(Scale, gizmo)); gizmo = new GLGizmoRotate3D(parent); @@ -1188,12 +1878,6 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) return false; } -#if !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - // temporary disable x and y grabbers - gizmo->disable_grabber(0); - gizmo->disable_grabber(1); -#endif // !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo)); gizmo = new GLGizmoFlatten(parent); @@ -1232,15 +1916,27 @@ void GLCanvas3D::Gizmos::set_enabled(bool enable) m_enabled = enable; } +#if ENABLE_EXTENDED_SELECTION +std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const GLCanvas3D::Selection& selection) +#else void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos) +#endif // ENABLE_EXTENDED_SELECTION { +#if ENABLE_EXTENDED_SELECTION + std::string name = ""; +#endif // ENABLE_EXTENDED_SELECTION + if (!m_enabled) +#if ENABLE_EXTENDED_SELECTION + return name; +#else return; +#endif // ENABLE_EXTENDED_SELECTION 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) continue; @@ -1249,16 +1945,32 @@ void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Vec2 float half_tex_size = 0.5f * tex_size; // we currently use circular icons for gizmo, so we check the radius +#if ENABLE_EXTENDED_SELECTION + if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On)) +#else if (it->second->get_state() != GLGizmoBase::On) +#endif // ENABLE_EXTENDED_SELECTION { 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 ENABLE_EXTENDED_SELECTION + if (inside) + name = it->second->get_name(); +#endif // ENABLE_EXTENDED_SELECTION } top_y += (tex_size + OverlayGapY); } + +#if ENABLE_EXTENDED_SELECTION + return name; +#endif // ENABLE_EXTENDED_SELECTION } +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const GLCanvas3D::Selection& selection) +#else void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos) +#endif // ENABLE_EXTENDED_SELECTION { if (!m_enabled) return; @@ -1266,7 +1978,7 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec 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) continue; @@ -1275,14 +1987,18 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec float half_tex_size = 0.5f * tex_size; // we currently use circular icons for gizmo, so we check the radius +#if ENABLE_EXTENDED_SELECTION + if (it->second->is_activable(selection) && ((mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size)) +#else if ((mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size) +#endif // ENABLE_EXTENDED_SELECTION { 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; @@ -1293,8 +2009,27 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec 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); } +#if ENABLE_EXTENDED_SELECTION +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; + } + } +} +#endif // ENABLE_EXTENDED_SELECTION + void GLCanvas3D::Gizmos::reset_all_states() { if (!m_enabled) @@ -1324,6 +2059,23 @@ void GLCanvas3D::Gizmos::set_hover_id(int id) } } +#if ENABLE_EXTENDED_SELECTION +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); + } +} +#endif // ENABLE_EXTENDED_SELECTION + bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const { if (!m_enabled) @@ -1397,24 +2149,55 @@ bool GLCanvas3D::Gizmos::is_running() const bool GLCanvas3D::Gizmos::is_dragging() const { + if (!m_enabled) + return false; + GLGizmoBase* curr = _get_current(); return (curr != nullptr) ? curr->is_dragging() : false; } +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3D::Gizmos::start_dragging(const GLCanvas3D::Selection& selection) +{ + if (!m_enabled) + return; + + GLGizmoBase* curr = _get_current(); + if (curr != nullptr) + curr->start_dragging(selection); +} +#else void GLCanvas3D::Gizmos::start_dragging(const BoundingBoxf3& box) { + if (!m_enabled) + return; + GLGizmoBase* curr = _get_current(); if (curr != nullptr) curr->start_dragging(box); } +#endif // ENABLE_EXTENDED_SELECTION void GLCanvas3D::Gizmos::stop_dragging() { + if (!m_enabled) + return; + GLGizmoBase* curr = _get_current(); if (curr != nullptr) curr->stop_dragging(); } +#if ENABLE_EXTENDED_SELECTION +Vec3d GLCanvas3D::Gizmos::get_displacement() const +{ + if (!m_enabled) + return Vec3d::Zero(); + + GizmosMap::const_iterator it = m_gizmos.find(Move); + return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_displacement() : Vec3d::Zero(); +} +#else Vec3d GLCanvas3D::Gizmos::get_position() const { if (!m_enabled) @@ -1433,8 +2216,8 @@ void GLCanvas3D::Gizmos::set_position(const Vec3d& position) if (it != m_gizmos.end()) reinterpret_cast(it->second)->set_position(position); } +#endif // ENABLE_EXTENDED_SELECTION -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM Vec3d GLCanvas3D::Gizmos::get_scale() const { if (!m_enabled) @@ -1481,54 +2264,6 @@ Vec3d GLCanvas3D::Gizmos::get_flattening_rotation() const GizmosMap::const_iterator it = m_gizmos.find(Flatten); return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_flattening_rotation() : Vec3d::Zero(); } -#else -float GLCanvas3D::Gizmos::get_scale() const -{ - if (!m_enabled) - return 1.0f; - - GizmosMap::const_iterator it = m_gizmos.find(Scale); - return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_scale_x() : 1.0f; -} - -void GLCanvas3D::Gizmos::set_scale(float 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); -} - -float GLCanvas3D::Gizmos::get_angle_z() const -{ - if (!m_enabled) - return 0.0f; - - GizmosMap::const_iterator it = m_gizmos.find(Rotate); - return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_angle_z() : 0.0f; -} - -void GLCanvas3D::Gizmos::set_angle_z(float angle_z) -{ - 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); -} - -Vec3d GLCanvas3D::Gizmos::get_flattening_normal() const -{ - if (!m_enabled) - return Vec3d::Zero(); - - GizmosMap::const_iterator it = m_gizmos.find(Flatten); - return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_flattening_normal() : Vec3d::Zero(); -} -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object) { @@ -1570,17 +2305,36 @@ void GLCanvas3D::Gizmos::delete_current_grabber(bool delete_all) reinterpret_cast(it->second)->delete_current_grabber(delete_all); } +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3D::Gizmos::render_current_gizmo(const GLCanvas3D::Selection& selection) const +#else void GLCanvas3D::Gizmos::render_current_gizmo(const BoundingBoxf3& box) const +#endif // ENABLE_EXTENDED_SELECTION { if (!m_enabled) return; ::glDisable(GL_DEPTH_TEST); +#if ENABLE_EXTENDED_SELECTION + _render_current_gizmo(selection); +#else if (box.radius() > 0.0) _render_current_gizmo(box); +#endif // ENABLE_EXTENDED_SELECTION } +#if ENABLE_EXTENDED_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(selection); +} +#else void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const { if (!m_enabled) @@ -1590,6 +2344,7 @@ void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const BoundingBox if (curr != nullptr) curr->render_for_picking(box); } +#endif // ENABLE_EXTENDED_SELECTION void GLCanvas3D::Gizmos::render_overlay(const GLCanvas3D& canvas) const { @@ -1640,12 +2395,21 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas) const } } +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3D::Gizmos::_render_current_gizmo(const GLCanvas3D::Selection& selection) const +{ + GLGizmoBase* curr = _get_current(); + if (curr != nullptr) + curr->render(selection); +} +#else void GLCanvas3D::Gizmos::_render_current_gizmo(const BoundingBoxf3& box) const { GLGizmoBase* curr = _get_current(); if (curr != nullptr) curr->render(box); } +#endif // ENABLE_EXTENDED_SELECTION float GLCanvas3D::Gizmos::_get_total_overlay_height() const { @@ -1661,6 +2425,12 @@ float GLCanvas3D::Gizmos::_get_total_overlay_height() const return height; } +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; @@ -1699,17 +2469,12 @@ bool GLCanvas3D::WarningTexture::generate(const std::string& msg) // 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); @@ -1828,7 +2593,7 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c if (items_count > 1) m_original_height += (items_count - 1) * Px_Square_Contour; - int pow_of_two_size = next_highest_power_of_2(std::max(m_original_width, m_original_height)); + 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; @@ -1836,17 +2601,12 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c // 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); @@ -1974,16 +2734,39 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const } } -GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const -{ - GizmosMap::const_iterator it = m_gizmos.find(m_current); - return (it != m_gizmos.end()) ? it->second : nullptr; -} +wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); +#if ENABLE_EXTENDED_SELECTION +wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); +#else +wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, ObjectSelectEvent); +#endif // ENABLE_EXTENDED_SELECTION +wxDEFINE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent); +#if !ENABLE_EXTENDED_SELECTION +wxDEFINE_EVENT(EVT_GLCANVAS_DOUBLE_CLICK, SimpleEvent); +#endif // !ENABLE_EXTENDED_SELECTION +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); +#if !ENABLE_EXTENDED_SELECTION +wxDEFINE_EVENT(EVT_GLCANVAS_ROTATE_OBJECT, Event); +wxDEFINE_EVENT(EVT_GLCANVAS_SCALE_UNIFORMLY, SimpleEvent); +#endif // !ENABLE_EXTENDED_SELECTION +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>); + +#if !ENABLE_EXTENDED_SELECTION +wxDEFINE_EVENT(EVT_GIZMO_SCALE, Vec3dEvent); +wxDEFINE_EVENT(EVT_GIZMO_ROTATE, Vec3dEvent); +wxDEFINE_EVENT(EVT_GIZMO_FLATTEN, Vec3dEvent); +#endif // !ENABLE_EXTENDED_SELECTION GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) : m_canvas(canvas) , m_context(nullptr) - , m_timer(nullptr) , m_toolbar(*this) , m_config(nullptr) , m_print(nullptr) @@ -2002,35 +2785,51 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_shader_enabled(false) , m_dynamic_background_enabled(false) , m_multisample_allowed(false) +#if ENABLE_EXTENDED_SELECTION + , m_regenerate_volumes(true) +#endif // ENABLE_EXTENDED_SELECTION , m_color_by("volume") +#if !ENABLE_EXTENDED_SELECTION , m_select_by("object") , m_drag_by("instance") +#endif // !ENABLE_EXTENDED_SELECTION , m_reload_delayed(false) { 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); } + +#if ENABLE_EXTENDED_SELECTION + m_selection.set_volumes(&m_volumes.volumes); +#endif // ENABLE_EXTENDED_SELECTION } 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) @@ -2109,6 +2908,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)) @@ -2116,6 +2916,7 @@ bool GLCanvas3D::set_current() return false; } +#endif // !ENABLE_USE_UNIQUE_GLCONTEXT void GLCanvas3D::set_as_dirty() { @@ -2131,10 +2932,15 @@ 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 +#if ENABLE_EXTENDED_SELECTION + m_selection.clear(); +#endif // ENABLE_EXTENDED_SELECTION m_volumes.release_geometry(); m_volumes.clear(); m_dirty = true; @@ -2144,6 +2950,7 @@ void GLCanvas3D::reset_volumes() _reset_warning_texture(); } +#if !ENABLE_EXTENDED_SELECTION void GLCanvas3D::deselect_volumes() { for (GLVolume* vol : m_volumes.volumes) @@ -2183,6 +2990,7 @@ void GLCanvas3D::update_volumes_selection(const std::vector& selections) } } } +#endif // !ENABLE_EXTENDED_SELECTION int GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) const { @@ -2197,8 +3005,10 @@ bool GLCanvas3D::move_volume_up(unsigned int id) { 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); +#if !ENABLE_EXTENDED_SELECTION 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); +#endif // !ENABLE_EXTENDED_SELECTION return true; } @@ -2211,18 +3021,22 @@ bool GLCanvas3D::move_volume_down(unsigned int id) { 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); +#if !ENABLE_EXTENDED_SELECTION 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); +#endif // !ENABLE_EXTENDED_SELECTION return true; } return false; } +#if !ENABLE_EXTENDED_SELECTION void GLCanvas3D::set_objects_selections(const std::vector& selections) { m_objects_selections = selections; } +#endif // !ENABLE_EXTENDED_SELECTION void GLCanvas3D::set_config(DynamicPrintConfig* config) { @@ -2237,6 +3051,9 @@ void GLCanvas3D::set_print(Print* print) void GLCanvas3D::set_model(Model* model) { m_model = model; +#if ENABLE_EXTENDED_SELECTION + m_selection.set_model(m_model); +#endif // ENABLE_EXTENDED_SELECTION } void GLCanvas3D::set_bed_shape(const Pointfs& shape) @@ -2294,6 +3111,7 @@ void GLCanvas3D::set_color_by(const std::string& value) m_color_by = value; } +#if !ENABLE_EXTENDED_SELECTION void GLCanvas3D::set_select_by(const std::string& value) { m_select_by = value; @@ -2315,6 +3133,7 @@ const std::string& GLCanvas3D::get_drag_by() const { return m_drag_by; } +#endif // !ENABLE_EXTENDED_SELECTION float GLCanvas3D::get_camera_zoom() const { @@ -2370,6 +3189,9 @@ void GLCanvas3D::enable_legend_texture(bool enable) void GLCanvas3D::enable_picking(bool enable) { m_picking_enabled = enable; +#if ENABLE_EXTENDED_SELECTION + m_selection.set_mode(Selection::Instance); +#endif // ENABLE_EXTENDED_SELECTION } void GLCanvas3D::enable_moving(bool enable) @@ -2432,6 +3254,16 @@ void GLCanvas3D::zoom_to_volumes() m_apply_zoom_to_volumes_filter = false; } +#if ENABLE_MODIFIED_CAMERA_TARGET +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3D::zoom_to_selection() +{ + if (!m_selection.is_empty()) + _zoom_to_bounding_box(m_selection.get_bounding_box()); +} +#endif // ENABLE_EXTENDED_SELECTION +#endif // ENABLE_MODIFIED_CAMERA_TARGET + void GLCanvas3D::select_view(const std::string& direction) { const float* dir_vec = nullptr; @@ -2451,12 +3283,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(); @@ -2483,6 +3315,31 @@ void GLCanvas3D::update_gizmos_data() if (!m_gizmos.is_enabled()) return; +#if ENABLE_EXTENDED_SELECTION + 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(); + for (int i = 0; i < 6; ++i) + { + m_gizmos.enable_grabber(Gizmos::Scale, i, enable_scale_xyz); + } + + if (m_selection.is_single_full_instance()) + { + 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); + } + else + { + m_gizmos.set_scale(Vec3d::Ones()); + m_gizmos.set_rotation(Vec3d::Zero()); + m_gizmos.set_flattening_data(nullptr); + } +#else int id = _get_first_selected_object_id(); if ((id != -1) && (m_model != nullptr)) { @@ -2492,15 +3349,9 @@ void GLCanvas3D::update_gizmos_data() ModelInstance* model_instance = model_object->instances[0]; if (model_instance != nullptr) { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM m_gizmos.set_position(model_instance->get_offset()); m_gizmos.set_scale(model_instance->get_scaling_factor()); m_gizmos.set_rotation(model_instance->get_rotation()); -#else - m_gizmos.set_position(Vec3d(model_instance->offset(0), model_instance->offset(1), 0.0)); - m_gizmos.set_scale(model_instance->scaling_factor); - m_gizmos.set_angle_z(model_instance->rotation); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM m_gizmos.set_flattening_data(model_object); m_gizmos.set_model_object_ptr(model_object); } @@ -2509,15 +3360,11 @@ void GLCanvas3D::update_gizmos_data() else { m_gizmos.set_position(Vec3d::Zero()); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM m_gizmos.set_scale(Vec3d::Ones()); m_gizmos.set_rotation(Vec3d::Zero()); -#else - m_gizmos.set_scale(1.0f); - m_gizmos.set_angle_z(0.0f); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM m_gizmos.set_flattening_data(nullptr); } +#endif // ENABLE_EXTENDED_SELECTION } // Returns a Rect object denoting size and position of the Reset button used by a gizmo. @@ -2549,7 +3396,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) @@ -2565,6 +3416,8 @@ void GLCanvas3D::render() float theta = m_camera.get_theta(); bool is_custom_bed = m_bed.is_custom(); + set_tooltip(""); + // picking pass _picking_pass(); @@ -2579,6 +3432,10 @@ void GLCanvas3D::render() } _render_objects(); +#if ENABLE_EXTENDED_SELECTION + _render_selection(); +#endif // ENABLE_EXTENDED_SELECTION + if (!is_custom_bed) // textured bed needs to be rendered after objects { _render_axes(true); @@ -2587,6 +3444,9 @@ void GLCanvas3D::render() _render_current_gizmo(); _render_cutting_plane(); +#if ENABLE_SHOW_CAMERA_TARGET + _render_camera_target(); +#endif // ENABLE_SHOW_CAMERA_TARGET // draw overlays _render_gizmos_overlay(); @@ -2617,7 +3477,11 @@ std::vector GLCanvas3D::load_object(const ModelObject& model_object, int ob instance_idxs.push_back(i); } } +#if ENABLE_EXTENDED_SELECTION + return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_use_VBOs && m_initialized); +#else 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); +#endif // ENABLE_EXTENDED_SELECTION } std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) @@ -2648,16 +3512,41 @@ int GLCanvas3D::get_in_object_volume_id(int scene_vol_idx) const return ((0 <= scene_vol_idx) && (scene_vol_idx < (int)m_volumes.volumes.size())) ? m_volumes.volumes[scene_vol_idx]->volume_idx() : -1; } +#if ENABLE_MIRROR +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3D::mirror_selection(Axis axis) +{ + m_selection.mirror(axis); + _on_mirror(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); +} +#endif // ENABLE_EXTENDED_SELECTION +#endif // ENABLE_MIRROR + void GLCanvas3D::reload_scene(bool force) { if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr)) return; +#if !ENABLE_EXTENDED_SELECTION reset_volumes(); +#endif // !ENABLE_EXTENDED_SELECTION +#if !ENABLE_USE_UNIQUE_GLCONTEXT // ensures this canvas is current if (!set_current()) return; +#endif // !ENABLE_USE_UNIQUE_GLCONTEXT + +#if ENABLE_EXTENDED_SELECTION + if (m_regenerate_volumes) + { + reset_volumes(); + + // to update the toolbar + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); + } +#endif // ENABLE_EXTENDED_SELECTION set_bed_shape(dynamic_cast(m_config->option("bed_shape"))->values); @@ -2669,6 +3558,17 @@ void GLCanvas3D::reload_scene(bool force) m_reload_delayed = false; +#if ENABLE_EXTENDED_SELECTION + if (m_regenerate_volumes) + { + for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) + { + load_object(*m_model, obj_idx); + } + } + + update_gizmos_data(); +#else m_objects_volumes_idxs.clear(); for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) @@ -2682,36 +3582,44 @@ void GLCanvas3D::reload_scene(bool force) // 2nd call to restore selection, if any if (!m_objects_selections.empty()) update_gizmos_data(); +#endif // ENABLE_EXTENDED_SELECTION - if (m_config->has("nozzle_diameter")) +#if ENABLE_EXTENDED_SELECTION + if (m_regenerate_volumes) { - // 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) +#endif // ENABLE_EXTENDED_SELECTION + if (m_config->has("nozzle_diameter")) { - // Height of a print (Show at least a slab) - double height = std::max(m_model->bounding_box().max(2), 10.0); + // Should the wipe tower be visualized ? + unsigned int extruders_count = (unsigned int)dynamic_cast(m_config->option("nozzle_diameter"))->values.size(); - 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; + 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; - float depth = m_print->get_wipe_tower_depth(); - if (!m_print->is_step_done(psWipeTower)) - depth = (900.f/w) * (float)(extruders_count - 1) ; + 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); - 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); + 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(); + update_volumes_colors_by_extruder(); +#if ENABLE_EXTENDED_SELECTION + } +#endif // ENABLE_EXTENDED_SELECTION // checks for geometry outside the print volume to render it accordingly if (!m_volumes.empty()) @@ -2723,31 +3631,38 @@ 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)); } + +#if ENABLE_EXTENDED_SELECTION + // restore to default value + m_regenerate_volumes = true; +#endif // ENABLE_EXTENDED_SELECTION } 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()) { @@ -2803,212 +3718,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); -} - -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -void GLCanvas3D::register_on_gizmo_scale_3D_callback(void* callback) -{ - if (callback != nullptr) - m_on_gizmo_scale_3D_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_gizmo_rotate_3D_callback(void* callback) -{ - if (callback != nullptr) - m_on_gizmo_rotate_3D_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_gizmo_flatten_3D_callback(void* callback) -{ - if (callback != nullptr) - m_on_gizmo_flatten_3D_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_update_geometry_3D_info_callback(void* callback) -{ - if (callback != nullptr) - m_on_update_geometry_3D_info_callback.register_callback(callback); -} -#else -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_gizmo_flatten_callback(void* callback) -{ - if (callback != nullptr) - m_on_gizmo_flatten_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); -} -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - -void GLCanvas3D::register_action_add_callback(void* callback) -{ - if (callback != nullptr) - m_action_add_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_delete_callback(void* callback) -{ - if (callback != nullptr) - m_action_delete_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_deleteall_callback(void* callback) -{ - if (callback != nullptr) - m_action_deleteall_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_arrange_callback(void* callback) -{ - if (callback != nullptr) - m_action_arrange_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_more_callback(void* callback) -{ - if (callback != nullptr) - m_action_more_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_fewer_callback(void* callback) -{ - if (callback != nullptr) - m_action_fewer_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_split_callback(void* callback) -{ - if (callback != nullptr) - m_action_split_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_cut_callback(void* callback) -{ - if (callback != nullptr) - m_action_cut_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_settings_callback(void* callback) -{ - if (callback != nullptr) - m_action_settings_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_layersediting_callback(void* callback) -{ - if (callback != nullptr) - m_action_layersediting_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_selectbyparts_callback(void* callback) -{ - if (callback != nullptr) - m_action_selectbyparts_callback.register_callback(callback); -} - void GLCanvas3D::bind_event_handlers() { if (m_canvas != nullptr) @@ -3097,27 +3806,49 @@ 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; } +#if !ENABLE_EXTENDED_SELECTION // key L/l case 76: - case 108: { m_on_rotate_object_left_callback.call(); break; } - // key R/r + case 108: { post_event(Event(EVT_GLCANVAS_ROTATE_OBJECT, -1)); break; } + // key R/r case 82: - case 114: { m_on_rotate_object_right_callback.call(); break; } - // key S/s + case 114: { post_event(Event(EVT_GLCANVAS_ROTATE_OBJECT, +1)); break; } + // key S/s case 83: - case 115: { m_on_scale_object_uniformly_callback.call(); break; } + case 115: { post_event(SimpleEvent(EVT_GLCANVAS_SCALE_UNIFORMLY)); break; } +#endif // !ENABLE_EXTENDED_SELECTION +#if ENABLE_MODIFIED_CAMERA_TARGET +#if ENABLE_EXTENDED_SELECTION + // 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_EXTENDED_SELECTION +#else + // key Z/z + case 90: + case 122: { zoom_to_volumes(); break; } +#endif // ENABLE_MODIFIED_CAMERA_TARGET default: { evt.Skip(); @@ -3138,7 +3869,11 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) // Performs layers editing updates, if enabled if (is_layers_editing_enabled()) { +#if ENABLE_EXTENDED_SELECTION + int object_idx_selected = m_selection.get_object_idx(); +#else int object_idx_selected = _get_first_selected_object_id(); +#endif // ENABLE_EXTENDED_SELECTION if (object_idx_selected != -1) { // A volume is selected. Test, whether hovering over a layer thickness bar. @@ -3165,7 +3900,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(); } @@ -3182,8 +3917,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { Point pos(evt.GetX(), evt.GetY()); +#if ENABLE_EXTENDED_SELECTION + int selected_object_idx = m_selection.get_object_idx(); + int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1; +#else int selected_object_idx = _get_first_selected_object_id(); int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1; +#endif // ENABLE_EXTENDED_SELECTION 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); @@ -3204,8 +3944,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.position = Vec2d(-1.0, -1.0); m_dirty = true; } +#if !ENABLE_EXTENDED_SELECTION else if (evt.LeftDClick() && (m_hover_volume_id != -1) && !gizmos_overlay_contains_mouse && (toolbar_contains_mouse == -1)) - m_on_double_click_callback.call(); + post_event(SimpleEvent(EVT_GLCANVAS_DOUBLE_CLICK)); +#endif // !ENABLE_EXTENDED_SELECTION else if (evt.LeftDClick() && (toolbar_contains_mouse != -1)) { m_toolbar_action_running = true; @@ -3220,25 +3962,33 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { case Gizmos::Scale: { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - const Vec3d& scale = m_gizmos.get_scale(); - m_on_gizmo_scale_3D_callback.call(scale(0), scale(1), scale(2)); +#if ENABLE_EXTENDED_SELECTION + m_selection.scale(m_gizmos.get_scale()); + _on_scale(); #else - m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale()); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - update_scale_values(); + post_event(Vec3dEvent(EVT_GIZMO_SCALE, m_gizmos.get_scale())); +#endif // ENABLE_EXTENDED_SELECTION +#if ENABLE_EXTENDED_SELECTION + wxGetApp().obj_manipul()->update_settings_value(m_selection); +#else + wxGetApp().obj_manipul()->update_scale_values(); +#endif // ENABLE_EXTENDED_SELECTION m_dirty = true; break; } case Gizmos::Rotate: { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - const Vec3d& rotation = m_gizmos.get_rotation(); - m_on_gizmo_rotate_3D_callback.call(rotation(0), rotation(1), rotation(2)); +#if ENABLE_EXTENDED_SELECTION + m_selection.rotate(m_gizmos.get_rotation()); + _on_rotate(); #else - m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z()); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - update_rotation_values(); + post_event(Vec3dEvent(EVT_GIZMO_ROTATE, std::move(m_gizmos.get_rotation()))); +#endif // ENABLE_EXTENDED_SELECTION +#if ENABLE_EXTENDED_SELECTION + wxGetApp().obj_manipul()->update_settings_value(m_selection); +#else + wxGetApp().obj_manipul()->update_rotation_values(); +#endif // ENABLE_EXTENDED_SELECTION m_dirty = true; break; } @@ -3253,7 +4003,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { // If user pressed left or right button we first check whether this happened // on a volume or not. +#if !ENABLE_EXTENDED_SELECTION int volume_idx = m_hover_volume_id; +#endif // !ENABLE_EXTENDED_SELECTION m_layers_editing.state = LayersEditing::Unknown; if ((layer_editing_object_idx != -1) && m_layers_editing.bar_rect_contains(*this, pos(0), pos(1))) { @@ -3287,32 +4039,45 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } } +#if ENABLE_EXTENDED_SELECTION + 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 else if ((selected_object_idx != -1) && gizmos_overlay_contains_mouse) { update_gizmos_data(); m_gizmos.update_on_off_state(*this, m_mouse.position); m_dirty = true; } - else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse() && evt.LeftDown()) +#endif // ENABLE_EXTENDED_SELECTION +#if ENABLE_EXTENDED_SELECTION + else if (evt.LeftDown() && !m_selection.is_empty() && m_gizmos.grabber_contains_mouse()) +#else + else if (evt.LeftDown() && (selected_object_idx != -1) && m_gizmos.grabber_contains_mouse()) +#endif // ENABLE_EXTENDED_SELECTION { update_gizmos_data(); +#if ENABLE_EXTENDED_SELECTION + m_selection.start_dragging(); + m_gizmos.start_dragging(m_selection); +#else m_gizmos.start_dragging(_selected_volumes_bounding_box()); m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id(selected_object_idx); +#endif // ENABLE_EXTENDED_SELECTION if (m_gizmos.get_current_type() == Gizmos::Flatten) { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM // Rotate the object so the normal points downward: - const Vec3d& rotation = m_gizmos.get_flattening_rotation(); - m_on_gizmo_flatten_3D_callback.call(rotation(0), rotation(1), rotation(2)); +#if ENABLE_EXTENDED_SELECTION + m_selection.rotate(m_gizmos.get_flattening_rotation()); + _on_flatten(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); #else - // Rotate the object so the normal points downward: - Vec3d normal = m_gizmos.get_flattening_normal(); - if (normal(0) != 0.0 || normal(1) != 0.0 || normal(2) != 0.0) { - Vec3d axis = normal(2) > 0.999 ? Vec3d::UnitX() : normal.cross(-Vec3d::UnitZ()).normalized(); - float angle = acos(clamp(-1.0, 1.0, -normal(2))); - m_on_gizmo_flatten_callback.call(angle, (float)axis(0), (float)axis(1), (float)axis(2)); - } -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + post_event(Vec3dEvent(EVT_GIZMO_FLATTEN, m_gizmos.get_flattening_rotation())); +#endif // ENABLE_EXTENDED_SELECTION } m_dirty = true; @@ -3332,8 +4097,27 @@ 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 ENABLE_EXTENDED_SELECTION + if (m_picking_enabled && ((m_hover_volume_id != -1) || !is_layers_editing_enabled())) +#else if (m_picking_enabled && ((volume_idx != -1) || !is_layers_editing_enabled())) +#endif // ENABLE_EXTENDED_SELECTION { +#if ENABLE_EXTENDED_SELECTION + if (evt.LeftDown() && (m_hover_volume_id != -1)) + { + if (!evt.ShiftDown() || !m_selection.contains_volume(m_hover_volume_id)) + m_selection.add(m_hover_volume_id, !evt.ShiftDown()); + else + m_selection.remove(m_hover_volume_id); + + 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; + } +#else if (volume_idx != -1) { deselect_volumes(); @@ -3351,32 +4135,58 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) update_gizmos_data(); m_dirty = true; } +#endif // ENABLE_EXTENDED_SELECTION } // propagate event through callback +#if !ENABLE_EXTENDED_SELECTION if (m_picking_enabled && (volume_idx != -1)) _on_select(volume_idx, selected_object_idx); +#endif // !ENABLE_EXTENDED_SELECTION +#if ENABLE_EXTENDED_SELECTION + if (m_hover_volume_id != -1) +#else if (volume_idx != -1) +#endif // ENABLE_EXTENDED_SELECTION { +#if ENABLE_EXTENDED_SELECTION + if (evt.LeftDown() && m_moving_enabled && (m_mouse.drag.move_volume_idx == -1)) +#else if (evt.LeftDown() && m_moving_enabled) +#endif // ENABLE_EXTENDED_SELECTION { // 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. +#if ENABLE_EXTENDED_SELECTION + Vec3d pos3d = _mouse_to_3d(pos); +#else Vec3d pos3d = (volume_idx == -1) ? Vec3d(DBL_MAX, DBL_MAX, DBL_MAX) : _mouse_to_3d(pos); - +#endif + // Only accept the initial position, if it is inside the volume bounding box. +#if ENABLE_EXTENDED_SELECTION + BoundingBoxf3 volume_bbox = m_volumes.volumes[m_hover_volume_id]->transformed_bounding_box(); +#else BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box(); +#endif // ENABLE_EXTENDED_SELECTION volume_bbox.offset(1.0); if (volume_bbox.contains(pos3d)) { // The dragging operation is initiated. +#if ENABLE_EXTENDED_SELECTION + m_mouse.drag.move_volume_idx = m_hover_volume_id; + m_selection.start_dragging(); +#else m_mouse.drag.move_with_shift = evt.ShiftDown(); m_mouse.drag.move_volume_idx = volume_idx; +#endif // ENABLE_EXTENDED_SELECTION m_mouse.drag.start_position_3D = pos3d; +#if !ENABLE_EXTENDED_SELECTION // 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 = volume_bbox.center() - pos3d; +#endif // !ENABLE_EXTENDED_SELECTION } } else if (evt.RightDown()) @@ -3387,9 +4197,25 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) render(); if (m_hover_volume_id != -1) { +#if ENABLE_EXTENDED_SELECTION + // 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())); + } +#else // if right clicking on volume, propagate event through callback (shows context menu) if (m_volumes.volumes[volume_idx]->hover) - m_on_right_click_callback.call(pos(0), pos(1)); + post_event(Vec2dEvent(EVT_GLCANVAS_RIGHT_CLICK, pos.cast())); +#endif // ENABLE_EXTENDED_SELECTION } } } @@ -3402,8 +4228,14 @@ 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; +#if ENABLE_EXTENDED_SELECTION + // we do not want to translate objects if the user just clicked on an object while pressing shift (to add/remove it from the selection) and then drag + Vec3d cur_pos = evt.ShiftDown() ? m_mouse.drag.start_position_3D : Linef3(_mouse_to_3d(pos, &z0), _mouse_to_3d(pos, &z1)).intersect_plane(m_mouse.drag.start_position_3D(2)); +#else Vec3d cur_pos = Linef3(_mouse_to_3d(pos, &z0), _mouse_to_3d(pos, &z1)).intersect_plane(m_mouse.drag.start_position_3D(2)); +#endif // ENABLE_EXTENDED_SELECTION +#if !ENABLE_EXTENDED_SELECTION // Clip the new position, so the object center remains close to the bed. cur_pos += m_mouse.drag.volume_center_offset; Point cur_pos2(scale_(cur_pos(0)), scale_(cur_pos(1))); @@ -3414,9 +4246,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) cur_pos(1) = unscale(ip(1)); } cur_pos -= m_mouse.drag.volume_center_offset; +#endif // !ENABLE_EXTENDED_SELECTION // Calculate the translation vector. - Vec3d vector = cur_pos - m_mouse.drag.start_position_3D; + Vec3d displacement = cur_pos - m_mouse.drag.start_position_3D; +#if ENABLE_EXTENDED_SELECTION + m_selection.translate(displacement); + wxGetApp().obj_manipul()->update_settings_value(m_selection); +#else // 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. @@ -3439,11 +4276,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Apply new temporary volume origin and ignore Z. for (GLVolume* v : volumes) { - v->set_offset(v->get_offset() + Vec3d(vector(0), vector(1), 0.0)); + v->set_offset(v->get_offset() + Vec3d(displacement(0), displacement(1), 0.0)); } - update_position_values(volume->get_offset()); + wxGetApp().obj_manipul()->update_position_value(volume->get_offset()); m_mouse.drag.start_position_3D = cur_pos; +#endif // ENABLE_EXTENDED_SELECTION m_dirty = true; } @@ -3455,6 +4293,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.dragging = true; m_gizmos.update(mouse_ray(pos), &pos); +#if !ENABLE_EXTENDED_SELECTION std::vector volumes; if (m_mouse.drag.gizmo_volume_idx != -1) { @@ -3471,67 +4310,66 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } } +#endif // !ENABLE_EXTENDED_SELECTION switch (m_gizmos.get_current_type()) { case Gizmos::Move: { // Apply new temporary offset +#if ENABLE_EXTENDED_SELECTION + m_selection.translate(m_gizmos.get_displacement()); + wxGetApp().obj_manipul()->update_settings_value(m_selection); +#else GLVolume* volume = m_volumes.volumes[m_mouse.drag.gizmo_volume_idx]; Vec3d offset = m_gizmos.get_position() - volume->get_offset(); for (GLVolume* v : volumes) { v->set_offset(v->get_offset() + offset); } - update_position_values(volume->get_offset()); + wxGetApp().obj_manipul()->update_position_value(volume->get_offset()); +#endif // ENABLE_EXTENDED_SELECTION break; } case Gizmos::Scale: { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM // Apply new temporary scale factors +#if ENABLE_EXTENDED_SELECTION + m_selection.scale(m_gizmos.get_scale()); + wxGetApp().obj_manipul()->update_settings_value(m_selection); +#else const Vec3d& scale = m_gizmos.get_scale(); for (GLVolume* v : volumes) { v->set_scaling_factor(scale); } - update_scale_values(scale); -#else - // Apply new temporary scale factor - float scale_factor = m_gizmos.get_scale(); - for (GLVolume* v : volumes) - { - v->set_scaling_factor((double)scale_factor); - } - update_scale_values((double)scale_factor); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + wxGetApp().obj_manipul()->update_scale_value(scale); +#endif // ENABLE_EXTENDED_SELECTION break; } case Gizmos::Rotate: { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + // Apply new temporary rotations +#if ENABLE_EXTENDED_SELECTION + m_selection.rotate(m_gizmos.get_rotation()); + wxGetApp().obj_manipul()->update_settings_value(m_selection); +#else // Apply new temporary rotation const Vec3d& rotation = m_gizmos.get_rotation(); for (GLVolume* v : volumes) { v->set_rotation(rotation); } - update_rotation_value(rotation); -#else - // Apply new temporary angle_z - float angle_z = m_gizmos.get_angle_z(); - for (GLVolume* v : volumes) - { - v->set_rotation((double)angle_z); - } - update_rotation_value((double)angle_z, Z); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + wxGetApp().obj_manipul()->update_rotation_value(rotation); +#endif // ENABLE_EXTENDED_SELECTION break; } default: break; } +#if ENABLE_EXTENDED_SELECTION +#else if (!volumes.empty()) { BoundingBoxf3 bb; @@ -3540,13 +4378,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) bb.merge(volume->transformed_bounding_box()); } const Vec3d& size = bb.size(); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM const Vec3d& scale = m_gizmos.get_scale(); - m_on_update_geometry_3D_info_callback.call(size(0), size(1), size(2), scale(0), scale(1), scale(2)); -#else - m_on_update_geometry_info_callback.call(size(0), size(1), size(2), m_gizmos.get_scale()); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + post_event(Vec3dsEvent<2>(EVT_GLCANVAS_UPDATE_GEOMETRY, {size, scale})); } +#endif // ENABLE_EXTENDED_SELECTION m_dirty = true; } @@ -3568,7 +4403,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) 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; } @@ -3585,7 +4420,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) 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; } @@ -3601,10 +4436,15 @@ 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) { +#if ENABLE_EXTENDED_SELECTION + m_regenerate_volumes = false; + _on_move(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); +#else // get all volumes belonging to the same group, if any std::vector volume_idxs; int vol_id = m_mouse.drag.move_volume_idx; @@ -3625,10 +4465,16 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // 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]); +#endif // ENABLE_EXTENDED_SELECTION } else if (m_gizmos.get_current_type() == Gizmos::SlaSupports && m_hover_volume_id != -1) { +#if ENABLE_EXTENDED_SELECTION + int id = m_selection.get_object_idx(); +#else int id = _get_first_selected_object_id(); +#endif // ENABLE_EXTENDED_SELECTION + if ((id != -1) && (m_model != nullptr)) { m_gizmos.clicked_on_object(Vec2d(pos(0), pos(1))); } @@ -3642,8 +4488,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_picking_enabled && !m_toolbar_action_running) #endif // ENABLE_GIZMOS_RESET { +#if ENABLE_EXTENDED_SELECTION + m_selection.clear(); + wxGetApp().obj_manipul()->update_settings_value(m_selection); + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); +#else deselect_volumes(); _on_select(-1, -1); +#endif // ENABLE_EXTENDED_SELECTION update_gizmos_data(); } #if ENABLE_GIZMOS_RESET @@ -3657,6 +4509,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { case Gizmos::Move: { +#if ENABLE_EXTENDED_SELECTION + m_regenerate_volumes = false; + _on_move(); +#else // get all volumes belonging to the same group, if any std::vector volume_idxs; int vol_id = m_mouse.drag.gizmo_volume_idx; @@ -3673,38 +4529,41 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } _on_move(volume_idxs); +#endif // ENABLE_EXTENDED_SELECTION break; } case Gizmos::Scale: { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - const Vec3d& scale = m_gizmos.get_scale(); - m_on_gizmo_scale_3D_callback.call(scale(0), scale(1), scale(2)); -#else - m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale()); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_EXTENDED_SELECTION + _on_scale(); +#endif // ENABLE_EXTENDED_SELECTION break; } case Gizmos::Rotate: { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - const Vec3d& rotation = m_gizmos.get_rotation(); - m_on_gizmo_rotate_3D_callback.call(rotation(0), rotation(1), rotation(2)); +#if ENABLE_EXTENDED_SELECTION + _on_rotate(); #else - m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z()); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + post_event(Vec3dEvent(EVT_GIZMO_ROTATE, m_gizmos.get_rotation())); +#endif // ENABLE_EXTENDED_SELECTION break; } default: break; } m_gizmos.stop_dragging(); - update_settings_value(); +#if ENABLE_EXTENDED_SELECTION + wxGetApp().obj_manipul()->update_settings_value(m_selection); +#else + wxGetApp().obj_manipul()->update_values(); +#endif // ENABLE_EXTENDED_SELECTION } m_mouse.drag.move_volume_idx = -1; +#if !ENABLE_EXTENDED_SELECTION m_mouse.drag.gizmo_volume_idx = -1; +#endif // !ENABLE_EXTENDED_SELECTION m_mouse.set_start_position_3D_as_invalid(); m_mouse.set_start_position_2D_as_invalid(); m_mouse.dragging = false; @@ -3738,12 +4597,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(); } @@ -3772,16 +4631,27 @@ 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) +void GLCanvas3D::set_tooltip(const std::string& tooltip) const { if (m_canvas != nullptr) - m_canvas->SetToolTip(tooltip); + { + wxToolTip* t = m_canvas->GetToolTip(); + if (t != nullptr) + { + if (t->GetTip() != tooltip) + t->SetTip(tooltip); + } + else + m_canvas->SetToolTip(tooltip); + } } bool GLCanvas3D::_is_shown_on_screen() const @@ -3818,7 +4688,7 @@ bool GLCanvas3D::_init_toolbar() item.tooltip = GUI::L_str("Add..."); item.sprite_id = 0; item.is_toggable = false; - item.action_callback = &m_action_add_callback; + item.action_event = EVT_GLTOOLBAR_ADD; if (!m_toolbar.add_item(item)) return false; @@ -3826,7 +4696,7 @@ bool GLCanvas3D::_init_toolbar() item.tooltip = GUI::L_str("Delete"); item.sprite_id = 1; item.is_toggable = false; - item.action_callback = &m_action_delete_callback; + item.action_event = EVT_GLTOOLBAR_DELETE; if (!m_toolbar.add_item(item)) return false; @@ -3834,7 +4704,7 @@ bool GLCanvas3D::_init_toolbar() item.tooltip = GUI::L_str("Delete all"); item.sprite_id = 2; item.is_toggable = false; - item.action_callback = &m_action_deleteall_callback; + item.action_event = EVT_GLTOOLBAR_DELETE_ALL; if (!m_toolbar.add_item(item)) return false; @@ -3842,7 +4712,7 @@ bool GLCanvas3D::_init_toolbar() item.tooltip = GUI::L_str("Arrange"); item.sprite_id = 3; item.is_toggable = false; - item.action_callback = &m_action_arrange_callback; + item.action_event = EVT_GLTOOLBAR_ARRANGE; if (!m_toolbar.add_item(item)) return false; @@ -3853,7 +4723,7 @@ bool GLCanvas3D::_init_toolbar() item.tooltip = GUI::L_str("Add instance"); item.sprite_id = 4; item.is_toggable = false; - item.action_callback = &m_action_more_callback; + item.action_event = EVT_GLTOOLBAR_MORE; if (!m_toolbar.add_item(item)) return false; @@ -3861,18 +4731,26 @@ bool GLCanvas3D::_init_toolbar() item.tooltip = GUI::L_str("Remove instance"); item.sprite_id = 5; item.is_toggable = false; - item.action_callback = &m_action_fewer_callback; + item.action_event = EVT_GLTOOLBAR_FEWER; if (!m_toolbar.add_item(item)) return false; if (!m_toolbar.add_separator()) return false; - item.name = "split"; - item.tooltip = GUI::L_str("Split"); + item.name = "splitobjects"; + item.tooltip = GUI::L_str("Split to objects"); item.sprite_id = 6; item.is_toggable = false; - item.action_callback = &m_action_split_callback; + 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 = 6; + item.is_toggable = false; + item.action_event = EVT_GLTOOLBAR_SPLIT_VOLUMES; if (!m_toolbar.add_item(item)) return false; @@ -3880,52 +4758,70 @@ bool GLCanvas3D::_init_toolbar() item.tooltip = GUI::L_str("Cut..."); item.sprite_id = 7; item.is_toggable = false; - item.action_callback = &m_action_cut_callback; + item.action_event = EVT_GLTOOLBAR_CUT; if (!m_toolbar.add_item(item)) return false; if (!m_toolbar.add_separator()) return false; +#if !ENABLE_EXTENDED_SELECTION item.name = "settings"; item.tooltip = GUI::L_str("Settings..."); item.sprite_id = 8; item.is_toggable = false; - item.action_callback = &m_action_settings_callback; + item.action_event = EVT_GLTOOLBAR_SETTINGS; if (!m_toolbar.add_item(item)) return false; +#endif // !ENABLE_EXTENDED_SELECTION item.name = "layersediting"; item.tooltip = GUI::L_str("Layers editing"); item.sprite_id = 9; item.is_toggable = true; - item.action_callback = &m_action_layersediting_callback; + item.action_event = EVT_GLTOOLBAR_LAYERSEDITING; if (!m_toolbar.add_item(item)) return false; if (!m_toolbar.add_separator()) return false; +#if !ENABLE_EXTENDED_SELECTION item.name = "selectbyparts"; item.tooltip = GUI::L_str("Select by parts"); item.sprite_id = 10; item.is_toggable = true; - item.action_callback = &m_action_selectbyparts_callback; + item.action_event = EVT_GLTOOLBAR_SELECTBYPARTS; if (!m_toolbar.add_item(item)) return false; +#endif // !ENABLE_EXTENDED_SELECTION 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); @@ -3994,6 +4890,7 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box() const return bb; } +#if !ENABLE_EXTENDED_SELECTION BoundingBoxf3 GLCanvas3D::_selected_volumes_bounding_box() const { BoundingBoxf3 bb; @@ -4038,6 +4935,7 @@ BoundingBoxf3 GLCanvas3D::_selected_volumes_bounding_box() const return bb; } +#endif // !ENABLE_EXTENDED_SELECTION void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) { @@ -4049,7 +4947,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(); } @@ -4122,48 +5020,6 @@ float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) co return (float)std::min((double)cnv_size.get_width() / max_x, (double)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(); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - m_on_gizmo_scale_3D_callback.deregister_callback(); - m_on_gizmo_rotate_3D_callback.deregister_callback(); - m_on_gizmo_flatten_3D_callback.deregister_callback(); - m_on_update_geometry_3D_info_callback.deregister_callback(); -#else - m_on_gizmo_scale_uniformly_callback.deregister_callback(); - m_on_gizmo_rotate_callback.deregister_callback(); - m_on_gizmo_flatten_callback.deregister_callback(); - m_on_update_geometry_info_callback.deregister_callback(); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - - m_action_add_callback.deregister_callback(); - m_action_delete_callback.deregister_callback(); - m_action_deleteall_callback.deregister_callback(); - m_action_arrange_callback.deregister_callback(); - m_action_more_callback.deregister_callback(); - m_action_fewer_callback.deregister_callback(); - m_action_split_callback.deregister_callback(); - m_action_cut_callback.deregister_callback(); - m_action_settings_callback.deregister_callback(); - m_action_layersediting_callback.deregister_callback(); - m_action_selectbyparts_callback.deregister_callback(); -} - void GLCanvas3D::_mark_volumes_for_layer_height() const { if (m_print == nullptr) @@ -4171,7 +5027,11 @@ void GLCanvas3D::_mark_volumes_for_layer_height() const for (GLVolume* vol : m_volumes.volumes) { +#if ENABLE_EXTENDED_SELECTION + int object_id = vol->object_idx(); +#else int object_id = int(vol->select_group_id / 1000000); +#endif // ENABLE_EXTENDED_SELECTION int shader_id = m_layers_editing.get_shader_program_id(); if (is_layers_editing_enabled() && (shader_id != -1) && vol->selected && @@ -4204,8 +5064,7 @@ 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 - Vec3d neg_target = - m_camera.target; - ::glTranslatef((GLfloat)neg_target(0), (GLfloat)neg_target(1), (GLfloat)neg_target(2)); + ::glTranslated(-m_camera.target(0), -m_camera.target(1), -m_camera.target(2)); } void GLCanvas3D::_picking_pass() const @@ -4227,16 +5086,22 @@ void GLCanvas3D::_picking_pass() const ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); _render_volumes(true); +#if ENABLE_EXTENDED_SELECTION + m_gizmos.render_current_gizmo_for_picking_pass(m_selection); +#else m_gizmos.render_current_gizmo_for_picking_pass(_selected_volumes_bounding_box()); +#endif // ENABLE_EXTENDED_SELECTION if (m_multisample_allowed) ::glEnable(GL_MULTISAMPLE); int volume_id = -1; +#if !ENABLE_EXTENDED_SELECTION for (GLVolume* vol : m_volumes.volumes) { vol->hover = false; } +#endif // !ENABLE_EXTENDED_SELECTION GLubyte color[4] = { 0, 0, 0, 0 }; const Size& cnv_size = get_canvas_size(); @@ -4250,6 +5115,7 @@ void GLCanvas3D::_picking_pass() const if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size())) { m_hover_volume_id = volume_id; +#if !ENABLE_EXTENDED_SELECTION m_volumes.volumes[volume_id]->hover = true; int group_id = m_volumes.volumes[volume_id]->select_group_id; if (group_id != -1) @@ -4260,6 +5126,7 @@ void GLCanvas3D::_picking_pass() const vol->hover = true; } } +#endif // !ENABLE_EXTENDED_SELECTION m_gizmos.set_hover_id(-1); } else @@ -4268,9 +5135,22 @@ void GLCanvas3D::_picking_pass() const m_gizmos.set_hover_id(inside ? (254 - (int)color[2]) : -1); } +#if ENABLE_EXTENDED_SELECTION + _update_volumes_hover_state(); +#endif // ENABLE_EXTENDED_SELECTION + // updates gizmos overlay +#if ENABLE_EXTENDED_SELECTION + if (!m_selection.is_empty()) + { + std::string name = m_gizmos.update_hover_state(*this, pos, m_selection); + if (!name.empty()) + set_tooltip(name); + } +#else if (_get_first_selected_object_id() != -1) m_gizmos.update_hover_state(*this, pos); +#endif // ENABLE_EXTENDED_SELECTION else m_gizmos.reset_all_states(); @@ -4297,9 +5177,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); @@ -4370,6 +5250,15 @@ void GLCanvas3D::_render_objects() const ::glDisable(GL_LIGHTING); } +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3D::_render_selection() const +{ + Gizmos::EType type = m_gizmos.get_current_type(); + bool show_indirect_selection = m_gizmos.is_running() && ((type == Gizmos::Rotate) || (type == Gizmos::Scale) || (type == Gizmos::Flatten)); + m_selection.render(show_indirect_selection); +} +#endif // ENABLE_EXTENDED_SELECTION + void GLCanvas3D::_render_cutting_plane() const { m_cutting_plane.render(volumes_bounding_box()); @@ -4393,7 +5282,7 @@ void GLCanvas3D::_render_legend_texture() const void GLCanvas3D::_render_layer_editing_overlay() const { - if (m_print == nullptr) + if ((m_print == nullptr) || m_print->objects().empty()) return; GLVolume* volume = nullptr; @@ -4412,8 +5301,12 @@ 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. +#if ENABLE_EXTENDED_SELECTION + int object_idx = volume->object_idx(); +#else int object_idx = int(volume->select_group_id / 1000000); - if ((int)m_print->objects().size() < object_idx) +#endif // ENABLE_EXTENDED_SELECTION + if ((int)m_print->objects().size() <= object_idx) return; const PrintObject* print_object = m_print->get_object(object_idx); @@ -4453,7 +5346,7 @@ 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(); @@ -4472,7 +5365,11 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const void GLCanvas3D::_render_current_gizmo() const { +#if ENABLE_EXTENDED_SELECTION + m_gizmos.render_current_gizmo(m_selection); +#else m_gizmos.render_current_gizmo(_selected_volumes_bounding_box()); +#endif // ENABLE_EXTENDED_SELECTION } void GLCanvas3D::_render_gizmos_overlay() const @@ -4486,6 +5383,70 @@ void GLCanvas3D::_render_toolbar() const 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 + +#if ENABLE_EXTENDED_SELECTION +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; + } + } +} +#endif // ENABLE_EXTENDED_SELECTION + float GLCanvas3D::_get_layers_editing_cursor_z_relative() const { return m_layers_editing.get_cursor_z_relative(*this); @@ -4580,16 +5541,15 @@ Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos) 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(); + m_timer.Stop(); } +#if !ENABLE_EXTENDED_SELECTION int GLCanvas3D::_get_first_selected_object_id() const { if (m_print != nullptr) @@ -4623,12 +5583,15 @@ int GLCanvas3D::_get_first_selected_volume_id(int object_id) const return -1; } +#endif // !ENABLE_EXTENDED_SELECTION 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; @@ -4749,7 +5712,11 @@ 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) { +#if ENABLE_EXTENDED_SELECTION + GLVolumePtrs vols; +#else std::vector vols; +#endif // ENABLE_EXTENDED_SELECTION if (ctxt.color_by_tool()) { for (size_t i = 0; i < ctxt.number_tools(); ++i) vols.emplace_back(new_volume(ctxt.color_tool(i))); @@ -4908,7 +5875,11 @@ 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. +#if ENABLE_EXTENDED_SELECTION + GLVolumePtrs vols; +#else std::vector vols; +#endif // ENABLE_EXTENDED_SELECTION if (ctxt.color_by_tool()) { for (size_t i = 0; i < ctxt.number_tools(); ++i) vols.emplace_back(new_volume(ctxt.color_tool(i))); @@ -5134,9 +6105,15 @@ 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()) { +#if ENABLE_EXTENDED_SELECTION + 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) +#else 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) +#endif // ENABLE_EXTENDED_SELECTION { GLVolume* volume = *it; delete volume; @@ -5207,9 +6184,15 @@ 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()) { +#if ENABLE_EXTENDED_SELECTION + 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) +#else 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) +#endif // ENABLE_EXTENDED_SELECTION { GLVolume* volume = *it; delete volume; @@ -5513,7 +6496,11 @@ void GLCanvas3D::_load_shells() instance_ids[i] = i; } +#if ENABLE_EXTENDED_SELECTION + m_volumes.load_object(model_obj, object_id, instance_ids, "object", m_use_VBOs && m_initialized); +#else m_volumes.load_object(model_obj, object_id, instance_ids, "object", "object", "object", m_use_VBOs && m_initialized); +#endif // ENABLE_EXTENDED_SELECTION ++object_id; } @@ -5536,10 +6523,17 @@ 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) { +#if ENABLE_EXTENDED_SELECTION + 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 (GLVolumePtrs::iterator it = begin; it != end; ++it) +#else 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(); for (std::vector::iterator it = begin; it != end; ++it) +#endif // ENABLE_EXTENDED_SELECTION { GLVolume* volume = *it; @@ -5628,6 +6622,160 @@ void GLCanvas3D::_show_warning_texture_if_needed() } } +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3D::_on_move() +{ + if (m_model == nullptr) + return; + + std::set> done; // prevent moving instances twice + bool object_moved = false; + Vec3d wipe_tower_origin = Vec3d::Zero(); + + for (const GLVolume* v : m_volumes.volumes) + { + int object_idx = v->object_idx(); + int instance_idx = v->instance_idx(); + + // prevent moving instances twice + std::pair done_id(object_idx, instance_idx); + if (done.find(done_id) != done.end()) + continue; + + done.insert(done_id); + + if (object_idx < 1000) + { + // Move instances. + ModelObject* model_object = m_model->objects[object_idx]; + if (model_object != nullptr) + { + model_object->instances[instance_idx]->set_offset(v->get_offset()); + model_object->invalidate_bounding_box(); + object_moved = true; + } + } + else if (object_idx == 1000) + // Move a wipe tower proxy. + wipe_tower_origin = v->get_offset(); + } + + if (object_moved) + post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED)); + + if (wipe_tower_origin != Vec3d::Zero()) + post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin))); +} + +void GLCanvas3D::_on_rotate() +{ + if (m_model == nullptr) + return; + + std::set> done; // prevent rotating instances twice + + for (const GLVolume* v : m_volumes.volumes) + { + int object_idx = v->object_idx(); + if (object_idx >= 1000) + continue; + + int instance_idx = v->instance_idx(); + + // prevent rotating instances twice + std::pair done_id(object_idx, instance_idx); + if (done.find(done_id) != done.end()) + continue; + + done.insert(done_id); + + // Rotate instances. + ModelObject* model_object = m_model->objects[object_idx]; + if (model_object != nullptr) + { + model_object->instances[instance_idx]->set_rotation(v->get_rotation()); + model_object->invalidate_bounding_box(); + } + } + + post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} + +void GLCanvas3D::_on_scale() +{ + if (m_model == nullptr) + return; + + std::set> done; // prevent scaling instances twice + + for (const GLVolume* v : m_volumes.volumes) + { + int object_idx = v->object_idx(); + if (object_idx >= 1000) + continue; + + int instance_idx = v->instance_idx(); + + // prevent scaling instances twice + std::pair done_id(object_idx, instance_idx); + if (done.find(done_id) != done.end()) + continue; + + done.insert(done_id); + + // Rotate instances. + ModelObject* model_object = m_model->objects[object_idx]; + if (model_object != nullptr) + { + model_object->instances[instance_idx]->set_scaling_factor(v->get_scaling_factor()); + model_object->invalidate_bounding_box(); + } + } + + post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} + +void GLCanvas3D::_on_flatten() +{ + _on_rotate(); +} + +#if ENABLE_MIRROR +void GLCanvas3D::_on_mirror() +{ + if (m_model == nullptr) + return; + + std::set> done; // prevent mirroring instances twice + + for (const GLVolume* v : m_volumes.volumes) + { + int object_idx = v->object_idx(); + if (object_idx >= 1000) + continue; + + int instance_idx = v->instance_idx(); + + // prevent mirroring instances twice + std::pair done_id(object_idx, instance_idx); + if (done.find(done_id) != done.end()) + continue; + + done.insert(done_id); + + // Mirror instances. + ModelObject* model_object = m_model->objects[object_idx]; + if (model_object != nullptr) + { + model_object->instances[instance_idx]->set_mirror(v->get_mirror()); + model_object->invalidate_bounding_box(); + } + } + + post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} +#endif // ENABLE_MIRROR +#else void GLCanvas3D::_on_move(const std::vector& volume_idxs) { if (m_model == nullptr) @@ -5656,14 +6804,9 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) ModelObject* model_object = m_model->objects[obj_idx]; if (model_object != nullptr) { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM model_object->instances[instance_idx]->set_offset(volume->get_offset()); -#else - const Vec3d& offset = volume->get_offset(); - model_object->instances[instance_idx]->offset = Vec2d(offset(0), offset(1)); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM model_object->invalidate_bounding_box(); - update_position_values(); + wxGetApp().obj_manipul()->update_position_values(); object_moved = true; } } @@ -5673,12 +6816,14 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) } if (object_moved) - m_on_instance_moved_callback.call(); + post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED)); if (wipe_tower_origin != Vec3d::Zero()) - m_on_wipe_tower_moved_callback.call(wipe_tower_origin(0), wipe_tower_origin(1)); + post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin))); } +#endif // ENABLE_EXTENDED_SELECTION +#if !ENABLE_EXTENDED_SELECTION void GLCanvas3D::_on_select(int volume_idx, int object_idx) { int vol_id = -1; @@ -5707,9 +6852,10 @@ void GLCanvas3D::_on_select(int volume_idx, int object_idx) } } - m_on_select_object_callback.call(obj_id, vol_id); - Slic3r::GUI::select_current_volume(obj_id, vol_id); + post_event(ObjectSelectEvent(obj_id, vol_id)); + wxGetApp().obj_list()->select_current_volume(obj_id, vol_id); } +#endif // !ENABLE_EXTENDED_SELECTION std::vector GLCanvas3D::_parse_colors(const std::vector& colors) { @@ -5738,24 +6884,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(); } diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp similarity index 65% rename from xs/src/slic3r/GUI/GLCanvas3D.hpp rename to src/slic3r/GUI/GLCanvas3D.hpp index 7d9b830ca8..b47a11a1d8 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -1,10 +1,15 @@ #ifndef slic3r_GLCanvas3D_hpp_ #define slic3r_GLCanvas3D_hpp_ -#include "../../slic3r/GUI/3DScene.hpp" -#include "../../slic3r/GUI/GLToolbar.hpp" +#include -class wxTimer; +#include "3DScene.hpp" +#include "GLToolbar.hpp" +#include "Event.hpp" + +#include + +class wxWindow; class wxSizeEvent; class wxIdleEvent; class wxKeyEvent; @@ -12,6 +17,7 @@ class wxMouseEvent; class wxTimerEvent; class wxPaintEvent; + namespace Slic3r { class GLShader; @@ -76,6 +82,54 @@ public: void set_bottom(float bottom); }; +#if ENABLE_EXTENDED_SELECTION +wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); +#else +struct ObjectSelectEvent; +wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, ObjectSelectEvent); +struct ObjectSelectEvent : public ArrayEvent +{ + ObjectSelectEvent(ptrdiff_t object_id, ptrdiff_t volume_id, wxObject *origin = nullptr) + : ArrayEvent(EVT_GLCANVAS_OBJECT_SELECT, {object_id, volume_id}, origin) + {} + + ptrdiff_t object_id() const { return data[0]; } + ptrdiff_t volume_id() const { return data[1]; } +}; +#endif // ENABLE_EXTENDED_SELECTION + +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); +#if !ENABLE_EXTENDED_SELECTION +wxDECLARE_EVENT(EVT_GLCANVAS_DOUBLE_CLICK, SimpleEvent); +#endif // !ENABLE_EXTENDED_SELECTION +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); +#if !ENABLE_EXTENDED_SELECTION +wxDECLARE_EVENT(EVT_GLCANVAS_ROTATE_OBJECT, Event); // data: -1 => rotate left, +1 => rotate right +wxDECLARE_EVENT(EVT_GLCANVAS_SCALE_UNIFORMLY, SimpleEvent); +#endif // !ENABLE_EXTENDED_SELECTION +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>); + +#if !ENABLE_EXTENDED_SELECTION +wxDECLARE_EVENT(EVT_GIZMO_SCALE, Vec3dEvent); +wxDECLARE_EVENT(EVT_GIZMO_ROTATE, Vec3dEvent); +wxDECLARE_EVENT(EVT_GIZMO_FLATTEN, Vec3dEvent); +#endif // !ENABLE_EXTENDED_SELECTION + + class GLCanvas3D { struct GCodePreviewVolumeIndex @@ -302,11 +356,15 @@ class GLCanvas3D Point start_position_2D; Vec3d start_position_3D; +#if !ENABLE_EXTENDED_SELECTION Vec3d volume_center_offset; bool move_with_shift; +#endif // !ENABLE_EXTENDED_SELECTION int move_volume_idx; +#if !ENABLE_EXTENDED_SELECTION int gizmo_volume_idx; +#endif // !ENABLE_EXTENDED_SELECTION public: Drag(); @@ -328,6 +386,150 @@ class GLCanvas3D bool is_start_position_3D_defined() const; }; +#if ENABLE_EXTENDED_SELECTION +public: + class Selection + { + public: + typedef std::set IndicesList; + + enum EMode : unsigned char + { + Volume, + Instance, + Object + }; + + enum EType : unsigned char + { + Invalid, + Empty, + WipeTower, + Modifier, + SingleFullObject, + SingleFullInstance, + Mixed + }; + + private: + struct VolumeCache + { + private: + Vec3d m_position; + Vec3d m_rotation; + Vec3d m_scaling_factor; + Transform3d m_rotation_matrix; + Transform3d m_scale_matrix; + + public: + VolumeCache(); + VolumeCache(const Vec3d& position, const Vec3d& rotation, const Vec3d& scaling_factor); + + 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; } + }; + + typedef std::map VolumesCache; + typedef std::set InstanceIdxsList; + typedef std::map ObjectIdxsToInstanceIdxsMap; + + struct Cache + { + VolumesCache volumes_data; + Vec3d dragging_center; + ObjectIdxsToInstanceIdxsMap content; + }; + + GLVolumePtrs* m_volumes; + Model* m_model; + + bool m_valid; + EMode m_mode; + EType m_type; + IndicesList m_list; + Cache m_cache; + mutable BoundingBoxf3 m_bounding_box; + mutable bool m_bounding_box_dirty; + + public: + Selection(); + + void set_volumes(GLVolumePtrs* volumes); + 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, 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 == Modifier; } + bool is_single_full_instance() const; + bool is_single_full_object() const { return m_type == SingleFullObject; } + 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; + + 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); + void scale(const Vec3d& scale); +#if ENABLE_MIRROR + void mirror(Axis axis); +#endif // ENABLE_MIRROR + + void render(bool show_indirect_selection) 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_unselected_instances() const; + void _render_bounding_box(const BoundingBoxf3& box, float* color) const; + void _synchronize_unselected_instances(); + }; + +private: +#endif // ENABLE_EXTENDED_SELECTION + class Gizmos { static const float OverlayTexturesScale; @@ -361,11 +563,20 @@ class GLCanvas3D bool is_enabled() const; void set_enabled(bool enable); +#if ENABLE_EXTENDED_SELECTION + 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); +#else void update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos); void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos); +#endif // ENABLE_EXTENDED_SELECTION void reset_all_states(); void set_hover_id(int id); +#if ENABLE_EXTENDED_SELECTION + void enable_grabber(EType type, unsigned int id, bool enable); +#endif // ENABLE_EXTENDED_SELECTION bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const; bool grabber_contains_mouse() const; @@ -379,13 +590,20 @@ class GLCanvas3D bool is_running() const; bool is_dragging() const; +#if ENABLE_EXTENDED_SELECTION + void start_dragging(const Selection& selection); +#else void start_dragging(const BoundingBoxf3& box); +#endif // ENABLE_EXTENDED_SELECTION void stop_dragging(); +#if ENABLE_EXTENDED_SELECTION + Vec3d get_displacement() const; +#else Vec3d get_position() const; void set_position(const Vec3d& position); +#endif // ENABLE_EXTENDED_SELECTION -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM Vec3d get_scale() const; void set_scale(const Vec3d& scale); @@ -393,32 +611,32 @@ class GLCanvas3D void set_rotation(const Vec3d& rotation); Vec3d get_flattening_rotation() const; -#else - float get_scale() const; - void set_scale(float scale); - - float get_angle_z() const; - void set_angle_z(float angle_z); - - Vec3d get_flattening_normal() const; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM void set_flattening_data(const ModelObject* model_object); - + void set_model_object_ptr(ModelObject* model_object); void clicked_on_object(const Vec2d& mouse_position); void delete_current_grabber(bool delete_all = false); +#if ENABLE_EXTENDED_SELECTION + void render_current_gizmo(const Selection& selection) const; + void render_current_gizmo_for_picking_pass(const Selection& selection) const; +#else void render_current_gizmo(const BoundingBoxf3& box) const; - void render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const; +#endif // ENABLE_EXTENDED_SELECTION + void render_overlay(const GLCanvas3D& canvas) const; private: void _reset(); void _render_overlay(const GLCanvas3D& canvas) const; +#if ENABLE_EXTENDED_SELECTION + void _render_current_gizmo(const Selection& selection) const; +#else void _render_current_gizmo(const BoundingBoxf3& box) const; +#endif // ENABLE_EXTENDED_SELECTION float _get_total_overlay_height() const; GLGizmoBase* _get_current() const; @@ -466,7 +684,7 @@ class GLCanvas3D 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; @@ -478,6 +696,9 @@ class GLCanvas3D mutable GLToolbar m_toolbar; mutable GLVolumeCollection m_volumes; +#if ENABLE_EXTENDED_SELECTION + Selection m_selection; +#endif // ENABLE_EXTENDED_SELECTION DynamicPrintConfig* m_config; Print* m_print; Model* m_model; @@ -496,81 +717,69 @@ class GLCanvas3D bool m_shader_enabled; bool m_dynamic_background_enabled; bool m_multisample_allowed; +#if ENABLE_EXTENDED_SELECTION + bool m_regenerate_volumes; +#endif // ENABLE_EXTENDED_SELECTION std::string m_color_by; +#if !ENABLE_EXTENDED_SELECTION std::string m_select_by; std::string m_drag_by; +#endif // !ENABLE_EXTENDED_SELECTION bool m_reload_delayed; +#if !ENABLE_EXTENDED_SELECTION std::vector> m_objects_volumes_idxs; std::vector m_objects_selections; +#endif // !ENABLE_EXTENDED_SELECTION 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; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - PerlCallback m_on_gizmo_rotate_3D_callback; - PerlCallback m_on_gizmo_flatten_3D_callback; - PerlCallback m_on_gizmo_scale_3D_callback; - PerlCallback m_on_update_geometry_3D_info_callback; -#else - PerlCallback m_on_gizmo_scale_uniformly_callback; - PerlCallback m_on_gizmo_rotate_callback; - PerlCallback m_on_gizmo_flatten_callback; - PerlCallback m_on_update_geometry_info_callback; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - - PerlCallback m_action_add_callback; - PerlCallback m_action_delete_callback; - PerlCallback m_action_deleteall_callback; - PerlCallback m_action_arrange_callback; - PerlCallback m_action_more_callback; - PerlCallback m_action_fewer_callback; - PerlCallback m_action_split_callback; - PerlCallback m_action_cut_callback; - PerlCallback m_action_settings_callback; - PerlCallback m_action_layersediting_callback; - PerlCallback m_action_selectbyparts_callback; + 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(); +#if !ENABLE_EXTENDED_SELECTION void deselect_volumes(); void select_volume(unsigned int id); void update_volumes_selection(const std::vector& selections); +#endif // !ENABLE_EXTENDED_SELECTION int check_volumes_outside_state(const DynamicPrintConfig* config) const; bool move_volume_up(unsigned int id); bool move_volume_down(unsigned int id); +#if !ENABLE_EXTENDED_SELECTION void set_objects_selections(const std::vector& selections); +#endif // !ENABLE_EXTENDED_SELECTION void set_config(DynamicPrintConfig* config); void set_print(Print* print); void set_model(Model* model); +#if ENABLE_EXTENDED_SELECTION + const Selection& get_selection() const { return m_selection; } + Selection& get_selection() { return m_selection; } +#endif // ENABLE_EXTENDED_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. @@ -584,11 +793,13 @@ public: void set_cutting_plane(float z, const ExPolygons& polygons); void set_color_by(const std::string& value); +#if !ENABLE_EXTENDED_SELECTION void set_select_by(const std::string& value); void set_drag_by(const std::string& value); const std::string& get_select_by() const; const std::string& get_drag_by() const; +#endif // !ENABLE_EXTENDED_SELECTION float get_camera_zoom() const; @@ -617,6 +828,11 @@ public: void zoom_to_bed(); void zoom_to_volumes(); +#if ENABLE_MODIFIED_CAMERA_TARGET +#if ENABLE_EXTENDED_SELECTION + void zoom_to_selection(); +#endif // ENABLE_EXTENDED_SELECTION +#endif // ENABLE_MODIFIED_CAMERA_TARGET void select_view(const std::string& direction); void set_viewport_from_scene(const GLCanvas3D& other); @@ -636,50 +852,17 @@ public: int get_first_volume_id(int obj_idx) const; int get_in_object_volume_id(int scene_vol_idx) const; +#if ENABLE_MIRROR +#if ENABLE_EXTENDED_SELECTION + void mirror_selection(Axis axis); +#endif // ENABLE_EXTENDED_SELECTION +#endif // ENABLE_MIRROR + 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); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - void register_on_gizmo_scale_3D_callback(void* callback); - void register_on_gizmo_rotate_3D_callback(void* callback); - void register_on_gizmo_flatten_3D_callback(void* callback); - void register_on_update_geometry_3D_info_callback(void* callback); -#else - void register_on_gizmo_scale_uniformly_callback(void* callback); - void register_on_gizmo_rotate_callback(void* callback); - void register_on_gizmo_flatten_callback(void* callback); - void register_on_update_geometry_info_callback(void* callback); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - - void register_action_add_callback(void* callback); - void register_action_delete_callback(void* callback); - void register_action_deleteall_callback(void* callback); - void register_action_arrange_callback(void* callback); - void register_action_more_callback(void* callback); - void register_action_fewer_callback(void* callback); - void register_action_split_callback(void* callback); - void register_action_cut_callback(void* callback); - void register_action_settings_callback(void* callback); - void register_action_layersediting_callback(void* callback); - void register_action_selectbyparts_callback(void* callback); - void bind_event_handlers(); void unbind_event_handlers(); @@ -697,7 +880,7 @@ public: void reset_legend_texture(); - void set_tooltip(const std::string& tooltip); + void set_tooltip(const std::string& tooltip) const; private: bool _is_shown_on_screen() const; @@ -705,16 +888,19 @@ private: 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; +#if !ENABLE_EXTENDED_SELECTION BoundingBoxf3 _selected_volumes_bounding_box() const; +#endif // !ENABLE_EXTENDED_SELECTION 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(); @@ -724,6 +910,9 @@ private: void _render_bed(float theta) const; void _render_axes(bool depth_test) const; void _render_objects() const; +#if ENABLE_EXTENDED_SELECTION + void _render_selection() const; +#endif // ENABLE_EXTENDED_SELECTION void _render_cutting_plane() const; void _render_warning_texture() const; void _render_legend_texture() const; @@ -732,6 +921,13 @@ private: 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 + +#if ENABLE_EXTENDED_SELECTION + void _update_volumes_hover_state() const; +#endif // ENABLE_EXTENDED_SELECTION float _get_layers_editing_cursor_z_relative() const; void _perform_layer_editing_action(wxMouseEvent* evt = nullptr); @@ -749,8 +945,10 @@ private: void _start_timer(); void _stop_timer(); +#if !ENABLE_EXTENDED_SELECTION int _get_first_selected_object_id() const; int _get_first_selected_volume_id(int object_id) const; +#endif // !ENABLE_EXTENDED_SELECTION // Create 3D thick extrusion lines for a skirt and brim. // Adds a new Slic3r::GUI::3DScene::Volume to volumes. @@ -780,8 +978,20 @@ private: void _update_toolpath_volumes_outside_state(); void _show_warning_texture_if_needed(); +#if ENABLE_EXTENDED_SELECTION + void _on_move(); + void _on_rotate(); + void _on_scale(); + void _on_flatten(); +#if ENABLE_MIRROR + void _on_mirror(); +#endif // ENABLE_MIRROR +#else void _on_move(const std::vector& volume_idxs); +#endif // ENABLE_EXTENDED_SELECTION +#if !ENABLE_EXTENDED_SELECTION void _on_select(int volume_idx, int object_idx); +#endif // !ENABLE_EXTENDED_SELECTION // 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); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/src/slic3r/GUI/GLCanvas3DManager.cpp similarity index 68% rename from xs/src/slic3r/GUI/GLCanvas3DManager.cpp rename to src/slic3r/GUI/GLCanvas3DManager.cpp index 1fef7ffe99..1d963e3b7b 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,6 +246,7 @@ void GLCanvas3DManager::reset_volumes(wxGLCanvas* canvas) it->second->reset_volumes(); } +#if !ENABLE_EXTENDED_SELECTION void GLCanvas3DManager::deselect_volumes(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -236,6 +267,7 @@ void GLCanvas3DManager::update_volumes_selection(wxGLCanvas* canvas, const std:: if (it != m_canvases.end()) it->second->update_volumes_selection(selections); } +#endif // !ENABLE_EXTENDED_SELECTION int GLCanvas3DManager::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const { @@ -255,12 +287,20 @@ bool GLCanvas3DManager::move_volume_down(wxGLCanvas* canvas, unsigned int id) return (it != m_canvases.end()) ? it->second->move_volume_down(id) : false; } +#if ENABLE_EXTENDED_SELECTION +GLCanvas3D* GLCanvas3DManager::get_canvas(wxGLCanvas* canvas) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second : nullptr; +} +#else 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); } +#endif // ENABLE_EXTENDED_SELECTION void GLCanvas3DManager::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config) { @@ -324,6 +364,7 @@ void GLCanvas3DManager::set_color_by(wxGLCanvas* canvas, const std::string& valu it->second->set_color_by(value); } +#if !ENABLE_EXTENDED_SELECTION void GLCanvas3DManager::set_select_by(wxGLCanvas* canvas, const std::string& value) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -343,6 +384,7 @@ std::string GLCanvas3DManager::get_select_by(wxGLCanvas* canvas) const CanvasesMap::const_iterator it = _get_canvas(canvas); return (it != m_canvases.end()) ? it->second->get_select_by() : ""; } +#endif // !ENABLE_EXTENDED_SELECTION bool GLCanvas3DManager::is_layers_editing_enabled(wxGLCanvas* canvas) const { @@ -554,6 +596,17 @@ int GLCanvas3DManager::get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol return (it != m_canvases.end()) ? it->second->get_in_object_volume_id(scene_vol_idx) : -1; } +#if ENABLE_MIRROR +#if ENABLE_EXTENDED_SELECTION +void GLCanvas3DManager::mirror_selection(wxGLCanvas* canvas, Axis axis) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->mirror_selection(axis); +} +#endif // ENABLE_EXTENDED_SELECTION +#endif // ENABLE_MIRROR + void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -578,253 +631,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); -} - -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -void GLCanvas3DManager::register_on_gizmo_scale_3D_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_gizmo_scale_3D_callback(callback); -} - -void GLCanvas3DManager::register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_gizmo_rotate_3D_callback(callback); -} - -void GLCanvas3DManager::register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_gizmo_flatten_3D_callback(callback); -} - -void GLCanvas3DManager::register_on_update_geometry_3D_info_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_update_geometry_3D_info_callback(callback); -} -#else -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_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_gizmo_flatten_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); -} -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - -void GLCanvas3DManager::register_action_add_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_add_callback(callback); -} - -void GLCanvas3DManager::register_action_delete_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_delete_callback(callback); -} - -void GLCanvas3DManager::register_action_deleteall_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_deleteall_callback(callback); -} - -void GLCanvas3DManager::register_action_arrange_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_arrange_callback(callback); -} - -void GLCanvas3DManager::register_action_more_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_more_callback(callback); -} - -void GLCanvas3DManager::register_action_fewer_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_fewer_callback(callback); -} - -void GLCanvas3DManager::register_action_split_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_split_callback(callback); -} - -void GLCanvas3DManager::register_action_cut_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_cut_callback(callback); -} - -void GLCanvas3DManager::register_action_settings_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_settings_callback(callback); -} - -void GLCanvas3DManager::register_action_layersediting_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_layersediting_callback(callback); -} - -void GLCanvas3DManager::register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_selectbyparts_callback(callback); + return new wxGLCanvas(parent, wxID_ANY, attribList); } GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) @@ -845,5 +674,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 64% rename from xs/src/slic3r/GUI/GLCanvas3DManager.hpp rename to src/slic3r/GUI/GLCanvas3DManager.hpp index 1c376cb293..89cb09e7f0 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; @@ -19,7 +20,7 @@ typedef std::vector ExPolygons; class ModelObject; class PrintObject; class GCodePreviewData; - + namespace GUI { class GLCanvas3D; @@ -41,17 +42,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,14 +87,20 @@ public: unsigned int get_volumes_count(wxGLCanvas* canvas) const; void reset_volumes(wxGLCanvas* canvas); +#if !ENABLE_EXTENDED_SELECTION void deselect_volumes(wxGLCanvas* canvas); void select_volume(wxGLCanvas* canvas, unsigned int id); void update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections); +#endif // !ENABLE_EXTENDED_SELECTION 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); +#if ENABLE_EXTENDED_SELECTION + GLCanvas3D* get_canvas(wxGLCanvas* canvas); +#else void set_objects_selections(wxGLCanvas* canvas, const std::vector& selections); +#endif // ENABLE_EXTENDED_SELECTION void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); void set_print(wxGLCanvas* canvas, Print* print); @@ -95,10 +116,12 @@ public: void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); void set_color_by(wxGLCanvas* canvas, const std::string& value); +#if !ENABLE_EXTENDED_SELECTION void set_select_by(wxGLCanvas* canvas, const std::string& value); void set_drag_by(wxGLCanvas* canvas, const std::string& value); std::string get_select_by(wxGLCanvas* canvas) const; +#endif // !ENABLE_EXTENDED_SELECTION bool is_layers_editing_enabled(wxGLCanvas* canvas) const; bool is_layers_editing_allowed(wxGLCanvas* canvas) const; @@ -140,57 +163,28 @@ public: int get_first_volume_id(wxGLCanvas* canvas, int obj_idx) const; int get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx) const; +#if ENABLE_MIRROR +#if ENABLE_EXTENDED_SELECTION + void mirror_selection(wxGLCanvas* canvas, Axis axis); +#endif // ENABLE_EXTENDED_SELECTION +#endif // ENABLE_MIRROR + 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); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - void register_on_gizmo_scale_3D_callback(wxGLCanvas* canvas, void* callback); - void register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback); - void register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback); - void register_on_update_geometry_3D_info_callback(wxGLCanvas* canvas, void* callback); -#else - void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback); - void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback); - void register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback); - void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - - void register_action_add_callback(wxGLCanvas* canvas, void* callback); - void register_action_delete_callback(wxGLCanvas* canvas, void* callback); - void register_action_deleteall_callback(wxGLCanvas* canvas, void* callback); - void register_action_arrange_callback(wxGLCanvas* canvas, void* callback); - void register_action_more_callback(wxGLCanvas* canvas, void* callback); - void register_action_fewer_callback(wxGLCanvas* canvas, void* callback); - void register_action_split_callback(wxGLCanvas* canvas, void* callback); - void register_action_cut_callback(wxGLCanvas* canvas, void* callback); - void register_action_settings_callback(wxGLCanvas* canvas, void* callback); - void register_action_layersediting_callback(wxGLCanvas* canvas, void* callback); - void register_action_selectbyparts_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/xs/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp similarity index 73% rename from xs/src/slic3r/GUI/GLGizmo.cpp rename to src/slic3r/GUI/GLGizmo.cpp index d91730d266..e190b1b3ab 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1,7 +1,12 @@ +#include "../../libslic3r/libslic3r.h" #include "GLGizmo.hpp" +#include "GUI.hpp" + #include "../../libslic3r/Utils.hpp" +#if !ENABLE_EXTENDED_SELECTION #include "../../slic3r/GUI/GLCanvas3D.hpp" +#endif // !ENABLE_EXTENDED_SELECTION #include #include "../../libslic3r/Geometry.hpp" @@ -21,6 +26,7 @@ static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f namespace Slic3r { namespace GUI { +#if !ENABLE_EXTENDED_SELECTION // returns the intersection of the given ray with the plane parallel to plane XY and passing through the given center // coordinates are local to the plane Vec3d intersection_on_plane_xy(const Linef3& ray, const Vec3d& center) @@ -103,7 +109,8 @@ unsigned int select_best_plane(const Vec3d& unit_vector, unsigned int preferred_ return ret; } - +#endif // !ENABLE_EXTENDED_SELECTION + const float GLGizmoBase::Grabber::SizeFactor = 0.025f; const float GLGizmoBase::Grabber::MinHalfSize = 1.5f; const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f; @@ -119,7 +126,7 @@ GLGizmoBase::Grabber::Grabber() color[2] = 1.0f; } -void GLGizmoBase::Grabber::render(bool hover, const BoundingBoxf3& box) const +void GLGizmoBase::Grabber::render(bool hover, float size) const { float render_color[3]; if (hover) @@ -131,27 +138,25 @@ void GLGizmoBase::Grabber::render(bool hover, const BoundingBoxf3& box) const else ::memcpy((void*)render_color, (const void*)color, 3 * sizeof(float)); - render(box, render_color, true); + render(size, render_color, true); } -void GLGizmoBase::Grabber::render(const BoundingBoxf3& box, const float* render_color, bool use_lighting) const +void GLGizmoBase::Grabber::render(float size, const float* render_color, bool use_lighting) const { - float max_size = (float)box.max_size(); - float half_size = dragging ? max_size * SizeFactor * DraggingScaleFactor : max_size * SizeFactor; + float half_size = dragging ? size * SizeFactor * DraggingScaleFactor : size * SizeFactor; half_size = std::max(half_size, MinHalfSize); if (use_lighting) ::glEnable(GL_LIGHTING); - ::glColor3f((GLfloat)render_color[0], (GLfloat)render_color[1], (GLfloat)render_color[2]); + ::glColor3fv(render_color); ::glPushMatrix(); - ::glTranslatef((GLfloat)center(0), (GLfloat)center(1), (GLfloat)center(2)); + ::glTranslated(center(0), center(1), center(2)); - float rad_to_deg = 180.0f / (GLfloat)PI; - ::glRotatef((GLfloat)angles(0) * rad_to_deg, 1.0f, 0.0f, 0.0f); - ::glRotatef((GLfloat)angles(1) * rad_to_deg, 0.0f, 1.0f, 0.0f); - ::glRotatef((GLfloat)angles(2) * rad_to_deg, 0.0f, 0.0f, 1.0f); + ::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 @@ -257,7 +262,11 @@ void GLGizmoBase::disable_grabber(unsigned int id) on_disable_grabber(id); } +#if ENABLE_EXTENDED_SELECTION +void GLGizmoBase::start_dragging(const GLCanvas3D::Selection& selection) +#else void GLGizmoBase::start_dragging(const BoundingBoxf3& box) +#endif // ENABLE_EXTENDED_SELECTION { m_dragging = true; @@ -266,13 +275,16 @@ void GLGizmoBase::start_dragging(const BoundingBoxf3& box) m_grabbers[i].dragging = (m_hover_id == i); } +#if ENABLE_EXTENDED_SELECTION + on_start_dragging(selection); +#else on_start_dragging(box); +#endif // ENABLE_EXTENDED_SELECTION } void GLGizmoBase::stop_dragging() { m_dragging = false; - set_tooltip(""); for (int i = 0; i < (int)m_grabbers.size(); ++i) { @@ -299,15 +311,19 @@ float GLGizmoBase::picking_color_component(unsigned int id) const 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), box); + 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) @@ -315,7 +331,7 @@ void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const 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(box); + m_grabbers[i].render_for_picking(size); } } } @@ -369,8 +385,15 @@ bool GLGizmoRotate::on_init() return true; } +#if ENABLE_EXTENDED_SELECTION +void GLGizmoRotate::on_start_dragging(const GLCanvas3D::Selection& selection) +#else void GLGizmoRotate::on_start_dragging(const BoundingBoxf3& box) +#endif // ENABLE_EXTENDED_SELECTION { +#if ENABLE_EXTENDED_SELECTION + const BoundingBoxf3& box = selection.get_bounding_box(); +#endif // ENABLE_EXTENDED_SELECTION m_center = box.center(); m_radius = Offset + box.radius(); m_snap_coarse_in_radius = m_radius / 3.0f; @@ -414,13 +437,33 @@ void GLGizmoRotate::on_update(const Linef3& mouse_ray, const Point* mouse_positi m_angle = theta; } +#if ENABLE_EXTENDED_SELECTION +void GLGizmoRotate::on_render(const GLCanvas3D::Selection& selection) const +#else void GLGizmoRotate::on_render(const BoundingBoxf3& box) const +#endif // ENABLE_EXTENDED_SELECTION { if (!m_grabbers[0].enabled) return; +#if ENABLE_EXTENDED_SELECTION + const BoundingBoxf3& box = selection.get_bounding_box(); + bool single_instance = selection.is_single_full_instance(); + + std::string axis; + switch (m_axis) + { + case X: { axis = "X: "; break; } + case Y: { axis = "Y: "; break; } + case Z: { axis = "Z: "; break; } + } + + if ((single_instance && (m_hover_id == 0)) || m_dragging) + set_tooltip(axis + format((float)Geometry::rad2deg(m_angle), 4) + "\u00B0"); +#else if (m_dragging) set_tooltip(format(m_angle * 180.0f / (float)PI, 4)); +#endif // ENABLE_EXTENDED_SELECTION else { m_center = box.center(); @@ -458,14 +501,22 @@ void GLGizmoRotate::on_render(const BoundingBoxf3& box) const ::glPopMatrix(); } +#if ENABLE_EXTENDED_SELECTION +void GLGizmoRotate::on_render_for_picking(const GLCanvas3D::Selection& selection) const +#else void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const +#endif // ENABLE_EXTENDED_SELECTION { ::glDisable(GL_DEPTH_TEST); ::glPushMatrix(); transform_to_local(); +#if ENABLE_EXTENDED_SELECTION + render_grabbers_for_picking(selection.get_bounding_box()); +#else render_grabbers_for_picking(box); +#endif // ENABLE_EXTENDED_SELECTION ::glPopMatrix(); } @@ -567,7 +618,7 @@ void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const ::glBegin(GL_LINES); ::glVertex3f(0.0f, 0.0f, 0.0f); - ::glVertex3f((GLfloat)m_grabbers[0].center(0), (GLfloat)m_grabbers[0].center(1), (GLfloat)m_grabbers[0].center(2)); + ::glVertex3dv(m_grabbers[0].center.data()); ::glEnd(); ::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 3 * sizeof(float)); @@ -576,7 +627,7 @@ void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const void GLGizmoRotate::transform_to_local() const { - ::glTranslatef((GLfloat)m_center(0), (GLfloat)m_center(1), (GLfloat)m_center(2)); + ::glTranslated(m_center(0), m_center(1), m_center(2)); switch (m_axis) { @@ -677,11 +728,24 @@ bool GLGizmoRotate3D::on_init() return true; } +std::string GLGizmoRotate3D::on_get_name() const +{ + return L("Rotate"); +} + +#if ENABLE_EXTENDED_SELECTION +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); +} +#else void GLGizmoRotate3D::on_start_dragging(const BoundingBoxf3& box) { if ((0 <= m_hover_id) && (m_hover_id < 3)) m_gizmos[m_hover_id].start_dragging(box); } +#endif // ENABLE_EXTENDED_SELECTION void GLGizmoRotate3D::on_stop_dragging() { @@ -689,6 +753,19 @@ void GLGizmoRotate3D::on_stop_dragging() m_gizmos[m_hover_id].stop_dragging(); } +#if ENABLE_EXTENDED_SELECTION +void GLGizmoRotate3D::on_render(const GLCanvas3D::Selection& selection) const +{ + 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); +} +#else void GLGizmoRotate3D::on_render(const BoundingBoxf3& box) const { if ((m_hover_id == -1) || (m_hover_id == 0)) @@ -700,15 +777,17 @@ void GLGizmoRotate3D::on_render(const BoundingBoxf3& box) const if ((m_hover_id == -1) || (m_hover_id == 2)) m_gizmos[Z].render(box); } +#endif // ENABLE_EXTENDED_SELECTION const float GLGizmoScale3D::Offset = 5.0f; +#if !ENABLE_EXTENDED_SELECTION const Vec3d GLGizmoScale3D::OffsetVec = (double)GLGizmoScale3D::Offset * Vec3d::Ones(); +#endif // !ENABLE_EXTENDED_SELECTION GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent) : GLGizmoBase(parent) , m_scale(Vec3d::Ones()) , m_starting_scale(Vec3d::Ones()) - , m_show_starting_box(false) { } @@ -746,13 +825,25 @@ bool GLGizmoScale3D::on_init() return true; } +std::string GLGizmoScale3D::on_get_name() const +{ + return L("Scale"); +} + +#if ENABLE_EXTENDED_SELECTION +void GLGizmoScale3D::on_start_dragging(const GLCanvas3D::Selection& selection) +#else void GLGizmoScale3D::on_start_dragging(const BoundingBoxf3& box) +#endif // ENABLE_EXTENDED_SELECTION { if (m_hover_id != -1) { m_starting_drag_position = m_grabbers[m_hover_id].center; - m_show_starting_box = true; +#if ENABLE_EXTENDED_SELECTION + m_starting_box = selection.get_bounding_box(); +#else m_starting_box = BoundingBoxf3(box.min - OffsetVec, box.max + OffsetVec); +#endif // ENABLE_EXTENDED_SELECTION } } @@ -776,8 +867,31 @@ void GLGizmoScale3D::on_process_double_click() } #endif // ENABLE_GIZMOS_RESET +#if ENABLE_EXTENDED_SELECTION +void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const +#else void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const +#endif // ENABLE_EXTENDED_SELECTION { +#if ENABLE_EXTENDED_SELECTION + bool single_instance = selection.is_single_full_instance(); + Vec3f scale = single_instance ? 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_scaling_factor().cast() : 100.0f * m_scale.cast(); + + if ((single_instance && ((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_instance && ((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_instance && ((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_instance && ((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); + } +#else if (m_grabbers[0].dragging || m_grabbers[1].dragging) set_tooltip("X: " + format(100.0f * m_scale(0), 4) + "%"); else if (m_grabbers[2].dragging || m_grabbers[3].dragging) @@ -791,47 +905,119 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const tooltip += "Z: " + format(100.0f * m_scale(2), 4) + "%"; set_tooltip(tooltip); } +#endif // ENABLE_EXTENDED_SELECTION ::glEnable(GL_DEPTH_TEST); +#if ENABLE_EXTENDED_SELECTION + 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()); + transform = v->world_matrix().cast(); + + // gets angles from first selected volume + angles = v->get_rotation(); + +#if ENABLE_MIRROR + // consider rotation+mirror only components of the transform for offsets + offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_mirror()); +#else + // set rotation-only component of transform + offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles); +#endif // ENABLE_MIRROR + } + else + box = selection.get_bounding_box(); + + m_box = box; +#else m_box = BoundingBoxf3(box.min - OffsetVec, box.max + OffsetVec); +#endif // ENABLE_EXTENDED_SELECTION + const Vec3d& center = m_box.center(); +#if ENABLE_EXTENDED_SELECTION + 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); +#endif // ENABLE_EXTENDED_SELECTION // x axis +#if ENABLE_EXTENDED_SELECTION + 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; +#else m_grabbers[0].center = Vec3d(m_box.min(0), center(1), center(2)); m_grabbers[1].center = Vec3d(m_box.max(0), center(1), center(2)); +#endif // ENABLE_EXTENDED_SELECTION ::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 +#if ENABLE_EXTENDED_SELECTION + 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; +#else m_grabbers[2].center = Vec3d(center(0), m_box.min(1), center(2)); m_grabbers[3].center = Vec3d(center(0), m_box.max(1), center(2)); +#endif // ENABLE_EXTENDED_SELECTION ::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 +#if ENABLE_EXTENDED_SELECTION + 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; +#else m_grabbers[4].center = Vec3d(center(0), center(1), m_box.min(2)); m_grabbers[5].center = Vec3d(center(0), center(1), m_box.max(2)); +#endif // ENABLE_EXTENDED_SELECTION ::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 = Vec3d(m_box.min(0), m_box.min(1), m_box.min(2)); - m_grabbers[7].center = Vec3d(m_box.max(0), m_box.min(1), m_box.min(2)); - m_grabbers[8].center = Vec3d(m_box.max(0), m_box.max(1), m_box.min(2)); - m_grabbers[9].center = Vec3d(m_box.min(0), m_box.max(1), m_box.min(2)); +#if ENABLE_EXTENDED_SELECTION + 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; +#else + m_grabbers[6].center = Vec3d(m_box.min(0), m_box.min(1), center(2)); + m_grabbers[7].center = Vec3d(m_box.max(0), m_box.min(1), center(2)); + m_grabbers[8].center = Vec3d(m_box.max(0), m_box.max(1), center(2)); + m_grabbers[9].center = Vec3d(m_box.min(0), m_box.max(1), center(2)); +#endif // ENABLE_EXTENDED_SELECTION for (int i = 6; i < 10; ++i) { ::memcpy((void*)m_grabbers[i].color, (const void*)m_highlight_color, 3 * sizeof(float)); } +#if ENABLE_EXTENDED_SELECTION + // sets grabbers orientation + for (int i = 0; i < 10; ++i) + { + m_grabbers[i].angles = angles; + } +#endif // ENABLE_EXTENDED_SELECTION + ::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f); + float box_max_size = (float)m_box.max_size(); + if (m_hover_id == -1) { - // draw box - ::glColor3fv(m_base_color); - render_box(m_box); // draw connections if (m_grabbers[0].enabled && m_grabbers[1].enabled) { @@ -848,115 +1034,72 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const ::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 starting box - if (m_show_starting_box) - { - ::glColor3fv(m_base_color); - render_box(m_starting_box); - } - // draw current box - ::glColor3fv(m_drag_color); - render_box(m_box); // draw connection ::glColor3fv(m_grabbers[0].color); render_grabbers_connection(0, 1); // draw grabbers - m_grabbers[0].render(true, m_box); - m_grabbers[1].render(true, m_box); + 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 starting box - if (m_show_starting_box) - { - ::glColor3fv(m_base_color); - render_box(m_starting_box); - } - // draw current box - ::glColor3fv(m_drag_color); - render_box(m_box); // draw connection ::glColor3fv(m_grabbers[2].color); render_grabbers_connection(2, 3); // draw grabbers - m_grabbers[2].render(true, m_box); - m_grabbers[3].render(true, m_box); + 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 starting box - if (m_show_starting_box) - { - ::glColor3fv(m_base_color); - render_box(m_starting_box); - } - // draw current box - ::glColor3fv(m_drag_color); - render_box(m_box); // draw connection ::glColor3fv(m_grabbers[4].color); render_grabbers_connection(4, 5); // draw grabbers - m_grabbers[4].render(true, m_box); - m_grabbers[5].render(true, m_box); + m_grabbers[4].render(true, box_max_size); + m_grabbers[5].render(true, box_max_size); } else if (m_hover_id >= 6) { - // draw starting box - if (m_show_starting_box) - { - ::glColor3fv(m_base_color); - render_box(m_starting_box); - } - // draw current box + // draw connection ::glColor3fv(m_drag_color); - render_box(m_box); + 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, m_box); + m_grabbers[i].render(true, box_max_size); } } } +#if ENABLE_EXTENDED_SELECTION +void GLGizmoScale3D::on_render_for_picking(const GLCanvas3D::Selection& selection) const +{ + ::glDisable(GL_DEPTH_TEST); + + render_grabbers_for_picking(selection.get_bounding_box()); +} +#else void GLGizmoScale3D::on_render_for_picking(const BoundingBoxf3& box) const { ::glDisable(GL_DEPTH_TEST); render_grabbers_for_picking(box); } - -void GLGizmoScale3D::render_box(const BoundingBoxf3& box) const -{ - // bottom face - ::glBegin(GL_LINE_LOOP); - ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.min(2)); - ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.min(2)); - ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.min(2)); - ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.min(2)); - ::glEnd(); - - // top face - ::glBegin(GL_LINE_LOOP); - ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.max(2)); - ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.max(2)); - ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.max(2)); - ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.max(2)); - ::glEnd(); - - // vertical edges - ::glBegin(GL_LINES); - ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.max(2)); - ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.max(2)); - ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.max(2)); - ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.max(2)); - ::glEnd(); -} +#endif // ENABLE_EXTENDED_SELECTION void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int id_2) const { @@ -964,15 +1107,19 @@ void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int if ((id_1 < grabbers_count) && (id_2 < grabbers_count)) { ::glBegin(GL_LINES); - ::glVertex3f((GLfloat)m_grabbers[id_1].center(0), (GLfloat)m_grabbers[id_1].center(1), (GLfloat)m_grabbers[id_1].center(2)); - ::glVertex3f((GLfloat)m_grabbers[id_2].center(0), (GLfloat)m_grabbers[id_2].center(1), (GLfloat)m_grabbers[id_2].center(2)); + ::glVertex3dv(m_grabbers[id_1].center.data()); + ::glVertex3dv(m_grabbers[id_2].center.data()); ::glEnd(); } } void GLGizmoScale3D::do_scale_x(const Linef3& mouse_ray) { +#if ENABLE_EXTENDED_SELECTION + double ratio = calc_ratio(mouse_ray); +#else double ratio = calc_ratio(1, mouse_ray, m_starting_box.center()); +#endif // ENABLE_EXTENDED_SELECTION if (ratio > 0.0) m_scale(0) = m_starting_scale(0) * ratio; @@ -980,38 +1127,70 @@ void GLGizmoScale3D::do_scale_x(const Linef3& mouse_ray) void GLGizmoScale3D::do_scale_y(const Linef3& mouse_ray) { +#if ENABLE_EXTENDED_SELECTION + double ratio = calc_ratio(mouse_ray); +#else double ratio = calc_ratio(2, mouse_ray, m_starting_box.center()); +#endif // ENABLE_EXTENDED_SELECTION if (ratio > 0.0) -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM m_scale(1) = m_starting_scale(1) * ratio; -#else - m_scale(0) = m_starting_scale(1) * ratio; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM } void GLGizmoScale3D::do_scale_z(const Linef3& mouse_ray) { +#if ENABLE_EXTENDED_SELECTION + double ratio = calc_ratio(mouse_ray); +#else double ratio = calc_ratio(1, mouse_ray, m_starting_box.center()); +#endif // ENABLE_EXTENDED_SELECTION if (ratio > 0.0) -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM m_scale(2) = m_starting_scale(2) * ratio; -#else - m_scale(0) = m_starting_scale(2) * ratio; // << this is temporary -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM } void GLGizmoScale3D::do_scale_uniform(const Linef3& mouse_ray) { +#if ENABLE_EXTENDED_SELECTION + double ratio = calc_ratio(mouse_ray); +#else Vec3d center = m_starting_box.center(); center(2) = m_box.min(2); double ratio = calc_ratio(0, mouse_ray, center); +#endif // ENABLE_EXTENDED_SELECTION if (ratio > 0.0) m_scale = m_starting_scale * ratio; } +#if ENABLE_EXTENDED_SELECTION +double GLGizmoScale3D::calc_ratio(const Linef3& mouse_ray) 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 = 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_starting_drag_position - 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()); + + return (len_starting_vec + proj) / len_starting_vec; + } + + return ratio; +} +#else double GLGizmoScale3D::calc_ratio(unsigned int preferred_plane_id, const Linef3& mouse_ray, const Vec3d& center) const { double ratio = 0.0; @@ -1047,12 +1226,17 @@ double GLGizmoScale3D::calc_ratio(unsigned int preferred_plane_id, const Linef3& return ratio; } +#endif // ENABLE_EXTENDED_SELECTION const double GLGizmoMove3D::Offset = 10.0; GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent) : GLGizmoBase(parent) +#if ENABLE_EXTENDED_SELECTION + , m_displacement(Vec3d::Zero()) +#else , m_position(Vec3d::Zero()) +#endif // ENABLE_EXTENDED_SELECTION , m_starting_drag_position(Vec3d::Zero()) , m_starting_box_center(Vec3d::Zero()) , m_starting_box_bottom_center(Vec3d::Zero()) @@ -1083,10 +1267,23 @@ bool GLGizmoMove3D::on_init() return true; } +std::string GLGizmoMove3D::on_get_name() const +{ + return L("Move"); +} + +#if ENABLE_EXTENDED_SELECTION +void GLGizmoMove3D::on_start_dragging(const GLCanvas3D::Selection& selection) +#else void GLGizmoMove3D::on_start_dragging(const BoundingBoxf3& box) +#endif // ENABLE_EXTENDED_SELECTION { if (m_hover_id != -1) { +#if ENABLE_EXTENDED_SELECTION + m_displacement = Vec3d::Zero(); + const BoundingBoxf3& box = selection.get_bounding_box(); +#endif // ENABLE_EXTENDED_SELECTION m_starting_drag_position = m_grabbers[m_hover_id].center; m_starting_box_center = box.center(); m_starting_box_bottom_center = box.center(); @@ -1094,27 +1291,62 @@ void GLGizmoMove3D::on_start_dragging(const BoundingBoxf3& box) } } +#if ENABLE_EXTENDED_SELECTION +void GLGizmoMove3D::on_stop_dragging() +{ + m_displacement = Vec3d::Zero(); +} +#endif // ENABLE_EXTENDED_SELECTION + void GLGizmoMove3D::on_update(const Linef3& mouse_ray, const Point* mouse_pos) { +#if ENABLE_EXTENDED_SELECTION + if (m_hover_id == 0) + m_displacement(0) = calc_projection(mouse_ray); + else if (m_hover_id == 1) + m_displacement(1) = calc_projection(mouse_ray); + else if (m_hover_id == 2) + m_displacement(2) = calc_projection(mouse_ray); +#else if (m_hover_id == 0) m_position(0) = 2.0 * m_starting_box_center(0) + calc_projection(X, 1, mouse_ray) - m_starting_drag_position(0); else if (m_hover_id == 1) m_position(1) = 2.0 * m_starting_box_center(1) + calc_projection(Y, 2, mouse_ray) - m_starting_drag_position(1); else if (m_hover_id == 2) m_position(2) = 2.0 * m_starting_box_bottom_center(2) + calc_projection(Z, 1, mouse_ray) - m_starting_drag_position(2); +#endif // ENABLE_EXTENDED_SELECTION } +#if ENABLE_EXTENDED_SELECTION +void GLGizmoMove3D::on_render(const GLCanvas3D::Selection& selection) const +#else void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const +#endif // ENABLE_EXTENDED_SELECTION { +#if ENABLE_EXTENDED_SELECTION + 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)); +#else if (m_grabbers[0].dragging) set_tooltip("X: " + format(m_position(0), 2)); else if (m_grabbers[1].dragging) set_tooltip("Y: " + format(m_position(1), 2)); else if (m_grabbers[2].dragging) set_tooltip("Z: " + format(m_position(2), 2)); +#endif // ENABLE_EXTENDED_SELECTION ::glEnable(GL_DEPTH_TEST); +#if ENABLE_EXTENDED_SELECTION + const BoundingBoxf3& box = selection.get_bounding_box(); +#endif // ENABLE_EXTENDED_SELECTION const Vec3d& center = box.center(); // x axis @@ -1140,8 +1372,8 @@ void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const { ::glColor3fv(AXES_COLOR[i]); ::glBegin(GL_LINES); - ::glVertex3f(center(0), center(1), center(2)); - ::glVertex3f((GLfloat)m_grabbers[i].center(0), (GLfloat)m_grabbers[i].center(1), (GLfloat)m_grabbers[i].center(2)); + ::glVertex3dv(center.data()); + ::glVertex3dv(m_grabbers[i].center.data()); ::glEnd(); } } @@ -1154,22 +1386,55 @@ void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const // draw axis ::glColor3fv(AXES_COLOR[m_hover_id]); ::glBegin(GL_LINES); - ::glVertex3f(center(0), center(1), center(2)); - ::glVertex3f((GLfloat)m_grabbers[m_hover_id].center(0), (GLfloat)m_grabbers[m_hover_id].center(1), (GLfloat)m_grabbers[m_hover_id].center(2)); + ::glVertex3dv(center.data()); + ::glVertex3dv(m_grabbers[m_hover_id].center.data()); ::glEnd(); // draw grabber - m_grabbers[m_hover_id].render(true, box); + m_grabbers[m_hover_id].render(true, box.max_size()); } } +#if ENABLE_EXTENDED_SELECTION +void GLGizmoMove3D::on_render_for_picking(const GLCanvas3D::Selection& selection) const +{ + ::glDisable(GL_DEPTH_TEST); + + render_grabbers_for_picking(selection.get_bounding_box()); +} +#else void GLGizmoMove3D::on_render_for_picking(const BoundingBoxf3& box) const { ::glDisable(GL_DEPTH_TEST); render_grabbers_for_picking(box); } +#endif // ENABLE_EXTENDED_SELECTION +#if ENABLE_EXTENDED_SELECTION +double GLGizmoMove3D::calc_projection(const Linef3& mouse_ray) 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 = 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_starting_drag_position - 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()); + } + return projection; +} +#else double GLGizmoMove3D::calc_projection(Axis axis, unsigned int preferred_plane_id, const Linef3& mouse_ray) const { double projection = 0.0; @@ -1205,6 +1470,7 @@ double GLGizmoMove3D::calc_projection(Axis axis, unsigned int preferred_plane_id return projection; } +#endif // ENABLE_EXTENDED_SELECTION GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent) : GLGizmoBase(parent) @@ -1232,26 +1498,51 @@ bool GLGizmoFlatten::on_init() return true; } +std::string GLGizmoFlatten::on_get_name() const +{ + return L("Flatten"); +} + +#if ENABLE_EXTENDED_SELECTION +void GLGizmoFlatten::on_start_dragging(const GLCanvas3D::Selection& selection) +#else void GLGizmoFlatten::on_start_dragging(const BoundingBoxf3& box) +#endif // ENABLE_EXTENDED_SELECTION { if (m_hover_id != -1) { m_normal = m_planes[m_hover_id].normal; +#if ENABLE_EXTENDED_SELECTION + m_starting_center = selection.get_bounding_box().center(); +#else m_starting_center = box.center(); +#endif // ENABLE_EXTENDED_SELECTION } } +#if ENABLE_EXTENDED_SELECTION +void GLGizmoFlatten::on_render(const GLCanvas3D::Selection& selection) const +#else void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const +#endif // ENABLE_EXTENDED_SELECTION { // 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 = box.center(); - Vec3d dragged_offset(box.center() - m_starting_center); + Vec3d dragged_offset(Vec3d::Zero()); +#if ENABLE_EXTENDED_SELECTION + if (m_starting_center == Vec3d::Zero()) + m_starting_center = selection.get_bounding_box().center(); + dragged_offset = selection.get_bounding_box().center() - m_starting_center; +#else + if (m_starting_center == Vec3d::Zero()) + m_starting_center = box.center(); + dragged_offset(box.center() - m_starting_center); +#endif // ENABLE_EXTENDED_SELECTION ::glEnable(GL_BLEND); ::glEnable(GL_DEPTH_TEST); + ::glDisable(GL_CULL_FACE); for (int i=0; i<(int)m_planes.size(); ++i) { if (i == m_hover_id) @@ -1259,52 +1550,81 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const else ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if ENABLE_EXTENDED_SELECTION + int instance_idx = selection.get_instance_idx(); + if ((instance_idx != -1) && (m_model_object != nullptr)) + { + Transform3d m = m_model_object->instances[instance_idx]->world_matrix(); + m.pretranslate(dragged_offset); + ::glPushMatrix(); + ::glMultMatrixd(m.data()); + ::glBegin(GL_POLYGON); + for (const Vec3d& vertex : m_planes[i].vertices) + { + ::glVertex3dv(vertex.data()); + } + ::glEnd(); + ::glPopMatrix(); + } +#else for (const InstanceData& inst : m_instances) { Transform3d m = inst.matrix; m.pretranslate(dragged_offset); ::glPushMatrix(); ::glMultMatrixd(m.data()); -#else - for (Vec2d offset : m_instances_positions) { - offset += to_2d(dragged_offset); - ::glPushMatrix(); - ::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM ::glBegin(GL_POLYGON); for (const Vec3d& vertex : m_planes[i].vertices) - ::glVertex3f((GLfloat)vertex(0), (GLfloat)vertex(1), (GLfloat)vertex(2)); + ::glVertex3dv(vertex.data()); ::glEnd(); ::glPopMatrix(); } +#endif // ENABLE_EXTENDED_SELECTION } + ::glEnable(GL_CULL_FACE); ::glDisable(GL_BLEND); } +#if ENABLE_EXTENDED_SELECTION +void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selection) const +#else void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const +#endif // ENABLE_EXTENDED_SELECTION { ::glEnable(GL_DEPTH_TEST); + ::glDisable(GL_CULL_FACE); for (unsigned int i = 0; i < m_planes.size(); ++i) { ::glColor3f(1.0f, 1.0f, picking_color_component(i)); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - for (const InstanceData& inst : m_instances) { +#if ENABLE_EXTENDED_SELECTION + int instance_idx = selection.get_instance_idx(); + if ((instance_idx != -1) && (m_model_object != nullptr)) + { ::glPushMatrix(); - ::glMultMatrixd(inst.matrix.data()); -#else - for (const Vec2d& offset : m_instances_positions) { - ::glPushMatrix(); - ::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + ::glMultMatrixd(m_model_object->instances[instance_idx]->world_matrix().data()); ::glBegin(GL_POLYGON); for (const Vec3d& vertex : m_planes[i].vertices) - ::glVertex3f((GLfloat)vertex(0), (GLfloat)vertex(1), (GLfloat)vertex(2)); + { + ::glVertex3dv(vertex.data()); + } ::glEnd(); ::glPopMatrix(); } +#else + for (const InstanceData& inst : m_instances) { + ::glPushMatrix(); + ::glMultMatrixd(inst.matrix.data()); + ::glBegin(GL_POLYGON); + for (const Vec3d& vertex : m_planes[i].vertices) + ::glVertex3dv(vertex.data()); + ::glEnd(); + ::glPopMatrix(); + } +#endif // ENABLE_EXTENDED_SELECTION } + + ::glEnable(GL_CULL_FACE); } void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) @@ -1312,20 +1632,14 @@ void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) m_starting_center = Vec3d::Zero(); m_model_object = model_object; +#if !ENABLE_EXTENDED_SELECTION // ...and save the updated positions of the object instances: if (m_model_object && !m_model_object->instances.empty()) { -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM m_instances.clear(); -#else - m_instances_positions.clear(); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM for (const auto* instance : m_model_object->instances) -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM m_instances.emplace_back(instance->world_matrix()); -#else - m_instances_positions.emplace_back(instance->offset); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM } +#endif // !ENABLE_EXTENDED_SELECTION if (is_plane_update_necessary()) update_planes(); @@ -1338,10 +1652,6 @@ void GLGizmoFlatten::update_planes() ch.merge(vol->get_convex_hull()); ch = ch.convex_hull_3d(); -#if !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - ch.scale(m_model_object->instances.front()->scaling_factor); - ch.rotate_z(m_model_object->instances.front()->rotation); -#endif // !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM 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))); @@ -1507,10 +1817,6 @@ void GLGizmoFlatten::update_planes() m_source_data.bounding_boxes.clear(); for (const auto& vol : m_model_object->volumes) m_source_data.bounding_boxes.push_back(vol->get_convex_hull().bounding_box()); -#if !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - m_source_data.scaling_factor = m_model_object->instances.front()->scaling_factor; - m_source_data.rotation = m_model_object->instances.front()->rotation; -#endif // !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM 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]); } @@ -1522,13 +1828,7 @@ bool GLGizmoFlatten::is_plane_update_necessary() const if (m_state != On || !m_model_object || m_model_object->instances.empty()) return false; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM if (m_model_object->volumes.size() != m_source_data.bounding_boxes.size()) -#else - 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) -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM return true; // now compare the bounding boxes: @@ -1544,23 +1844,16 @@ bool GLGizmoFlatten::is_plane_update_necessary() const return false; } -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM Vec3d GLGizmoFlatten::get_flattening_rotation() const { // calculates the rotations in model space, taking in account the scaling factors Eigen::Matrix m = m_model_object->instances.front()->world_matrix(true, true).matrix().block(0, 0, 3, 3).inverse().transpose(); Eigen::Quaterniond q; - Vec3d angles = q.setFromTwoVectors(m * m_normal, -Vec3d::UnitZ()).toRotationMatrix().eulerAngles(2, 1, 0); + Vec3d angles = Geometry::extract_euler_angles(q.setFromTwoVectors(m * m_normal, -Vec3d::UnitZ()).toRotationMatrix()); m_normal = Vec3d::Zero(); - return Vec3d(angles(2), angles(1), angles(0)); + m_starting_center = Vec3d::Zero(); + return angles; } -#else -Vec3d GLGizmoFlatten::get_flattening_normal() const { - Vec3d normal = m_model_object->instances.front()->world_matrix(true).matrix().block(0, 0, 3, 3).inverse() * m_normal; - m_normal = Vec3d::Zero(); - return normal.normalized(); -} -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM @@ -1598,7 +1891,11 @@ void GLGizmoSlaSupports::set_model_object_ptr(ModelObject* model_object) update_mesh(); } +#if ENABLE_EXTENDED_SELECTION +void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const +#else void GLGizmoSlaSupports::on_render(const BoundingBoxf3& box) const +#endif { ::glEnable(GL_BLEND); ::glEnable(GL_DEPTH_TEST); @@ -1606,9 +1903,16 @@ void GLGizmoSlaSupports::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 ENABLE_EXTENDED_SELECTION + 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; +#else if (m_starting_center == Vec3d::Zero()) m_starting_center = box.center(); Vec3d dragged_offset(box.center() - m_starting_center); +#endif // ENABLE_EXTENDED_SELECTION for (auto& g : m_grabbers) { g.color[0] = 1.f; @@ -1625,7 +1929,12 @@ void GLGizmoSlaSupports::on_render(const BoundingBoxf3& box) const ::glDisable(GL_BLEND); } + +#if ENABLE_EXTENDED_SELECTION +void GLGizmoSlaSupports::on_render_for_picking(const GLCanvas3D::Selection& selection) const +#else void GLGizmoSlaSupports::on_render_for_picking(const BoundingBoxf3& box) const +#endif { ::glEnable(GL_DEPTH_TEST); for (unsigned int i=0; ivolumes.size() != m_source_data.bounding_boxes.size() - || (m_model_object->instances.front()->world_matrix() * m_source_data.matrix.inverse() * Vec3d(1., 1., 1.) - Vec3d(1., 1., 1.)).norm() > 0.001 ) + if ((m_model_object->instances.front()->world_matrix() * m_source_data.matrix.inverse() * Vec3d(1., 1., 1.) - Vec3d(1., 1., 1.)).norm() > 0.001 ) #else - 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) + if (m_model_object->instances.front()->get_scaling_factor() != m_source_data.scaling_factor + || m_model_object->instances.front()->get_rotation() != m_source_data.rotation + || m_model_object->instances.front()->get_offset() != m_source_data.offset) #endif // ENABLE_MODELINSTANCE_3D_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; - // 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(); + /*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 true;*/ return false; } @@ -1701,15 +2004,7 @@ void GLGizmoSlaSupports::update_mesh() { Eigen::MatrixXf& V = m_V; Eigen::MatrixXi& F = m_F; - - TriangleMesh combined_mesh; - for (const ModelVolume* vol : m_model_object->volumes) - combined_mesh.merge(vol->mesh); - //combined_mesh.scale(m_model_object->instances.front()->scaling_factor); - //combined_mesh.rotate_z(m_model_object->instances.front()->rotation); - //const stl_file& stl = combined_mesh.stl; const stl_file& stl = m_model_object->mesh().stl; - V.resize(3*stl.stats.number_of_facets, 3); F.resize(stl.stats.number_of_facets, 3); for (unsigned int i=0; ivolumes) - m_source_data.bounding_boxes.push_back(vol->get_convex_hull().bounding_box()); #if !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - m_source_data.scaling_factor = m_model_object->instances.front()->scaling_factor; - m_source_data.rotation = m_model_object->instances.front()->rotation; + m_source_data.scaling_factor = m_model_object->instances.front()->get_scaling_factor(); + m_source_data.rotation = m_model_object->instances.front()->get_rotation(); + m_source_data.offset = m_model_object->instances.front()->get_offset(); #else m_source_data.matrix = m_model_object->instances.front()->world_matrix(); #endif 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) { @@ -1846,5 +2136,10 @@ void GLGizmoSlaSupports::render_tooltip_texture() const { } +std::string GLGizmoSlaSupports::on_get_name() const +{ + return L("SLA Support Points"); +} + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp similarity index 67% rename from xs/src/slic3r/GUI/GLGizmo.hpp rename to src/slic3r/GUI/GLGizmo.hpp index 488e5c02e0..14eac77e72 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -2,6 +2,9 @@ #define slic3r_GLGizmo_hpp_ #include "../../slic3r/GUI/GLTexture.hpp" +#if ENABLE_EXTENDED_SELECTION +#include "../../slic3r/GUI/GLCanvas3D.hpp" +#endif // ENABLE_EXTENDED_SELECTION #include "../../libslic3r/Point.hpp" #include "../../libslic3r/BoundingBox.hpp" @@ -34,11 +37,11 @@ protected: Grabber(); - void render(bool hover, const BoundingBoxf3& box) const; - void render_for_picking(const BoundingBoxf3& box) const { render(box, color, false); } + void render(bool hover, float size) const; + void render_for_picking(float size) const { render(size, color, false); } private: - void render(const BoundingBoxf3& box, const float* render_color, bool use_lighting) const; + void render(float size, const float* render_color, bool use_lighting) const; void render_face(float half_size) const; }; @@ -71,12 +74,18 @@ public: 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(); } +#if ENABLE_EXTENDED_SELECTION + bool is_activable(const GLCanvas3D::Selection& selection) const { return on_is_activable(selection); } +#endif // ENABLE_EXTENDED_SELECTION + unsigned int get_texture_id() const { return m_textures[m_state].get_id(); } int get_textures_size() const { return m_textures[Off].get_width(); } @@ -88,7 +97,11 @@ public: void enable_grabber(unsigned int id); void disable_grabber(unsigned int id); +#if ENABLE_EXTENDED_SELECTION + void start_dragging(const GLCanvas3D::Selection& selection); +#else void start_dragging(const BoundingBoxf3& box); +#endif // ENABLE_EXTENDED_SELECTION void stop_dragging(); bool is_dragging() const { return m_dragging; } @@ -98,23 +111,41 @@ public: void process_double_click() { on_process_double_click(); } #endif // ENABLE_GIZMOS_RESET +#if ENABLE_EXTENDED_SELECTION + void render(const GLCanvas3D::Selection& selection) const { on_render(selection); } + void render_for_picking(const GLCanvas3D::Selection& selection) const { on_render_for_picking(selection); } +#else void render(const BoundingBoxf3& box) const { on_render(box); } void render_for_picking(const BoundingBoxf3& box) const { on_render_for_picking(box); } +#endif // ENABLE_EXTENDED_SELECTION 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() {} +#if ENABLE_EXTENDED_SELECTION + virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return true; } +#endif // ENABLE_EXTENDED_SELECTION virtual void on_enable_grabber(unsigned int id) {} virtual void on_disable_grabber(unsigned int id) {} +#if ENABLE_EXTENDED_SELECTION + virtual void on_start_dragging(const GLCanvas3D::Selection& selection) {} +#else virtual void on_start_dragging(const BoundingBoxf3& box) {} +#endif // ENABLE_EXTENDED_SELECTION virtual void on_stop_dragging() {} virtual void on_update(const Linef3& mouse_ray, const Point* mouse_pos) = 0; #if ENABLE_GIZMOS_RESET virtual void on_process_double_click() {} #endif // ENABLE_GIZMOS_RESET +#if ENABLE_EXTENDED_SELECTION + virtual void on_render(const GLCanvas3D::Selection& selection) const = 0; + virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const = 0; +#else virtual void on_render(const BoundingBoxf3& box) const = 0; virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0; +#endif // ENABLE_EXTENDED_SELECTION float picking_color_component(unsigned int id) const; void render_grabbers(const BoundingBoxf3& box) const; @@ -164,13 +195,23 @@ public: protected: virtual bool on_init(); + virtual std::string on_get_name() const { return ""; } +#if ENABLE_EXTENDED_SELECTION + virtual void on_start_dragging(const GLCanvas3D::Selection& selection); +#else virtual void on_start_dragging(const BoundingBoxf3& box); +#endif // ENABLE_EXTENDED_SELECTION virtual void on_update(const Linef3& mouse_ray, const Point* mouse_pos); #if ENABLE_GIZMOS_RESET virtual void on_process_double_click() { m_angle = 0.0; } #endif // ENABLE_GIZMOS_RESET +#if ENABLE_EXTENDED_SELECTION + virtual void on_render(const GLCanvas3D::Selection& selection) const; + virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; +#else virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const; +#endif // ENABLE_EXTENDED_SELECTION private: void render_circle() const; @@ -192,22 +233,12 @@ class GLGizmoRotate3D : public GLGizmoBase public: explicit GLGizmoRotate3D(GLCanvas3D& parent); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM 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)); } -#else - double get_angle_x() const { return m_gizmos[X].get_angle(); } - void set_angle_x(double angle) { m_gizmos[X].set_angle(angle); } - - double get_angle_y() const { return m_gizmos[Y].get_angle(); } - void set_angle_y(double angle) { m_gizmos[Y].set_angle(angle); } - - double get_angle_z() const { return m_gizmos[Z].get_angle(); } - void set_angle_z(double angle) { m_gizmos[Z].set_angle(angle); } -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM protected: virtual bool on_init(); + virtual std::string on_get_name() const; virtual void on_set_state() { for (GLGizmoRotate& g : m_gizmos) @@ -222,6 +253,9 @@ protected: m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1); } } +#if ENABLE_EXTENDED_SELECTION + virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return !selection.is_wipe_tower(); } +#endif // ENABLE_EXTENDED_SELECTION virtual void on_enable_grabber(unsigned int id) { if ((0 <= id) && (id < 3)) @@ -232,7 +266,11 @@ protected: if ((0 <= id) && (id < 3)) m_gizmos[id].disable_grabber(0); } +#if ENABLE_EXTENDED_SELECTION + virtual void on_start_dragging(const GLCanvas3D::Selection& selection); +#else virtual void on_start_dragging(const BoundingBoxf3& box); +#endif // ENABLE_EXTENDED_SELECTION virtual void on_stop_dragging(); virtual void on_update(const Linef3& mouse_ray, const Point* mouse_pos) { @@ -248,6 +286,16 @@ protected: m_gizmos[m_hover_id].process_double_click(); } #endif // ENABLE_GIZMOS_RESET +#if ENABLE_EXTENDED_SELECTION + 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); + } + } +#else virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const { @@ -256,12 +304,15 @@ protected: g.render_for_picking(box); } } +#endif // ENABLE_EXTENDED_SELECTION }; class GLGizmoScale3D : public GLGizmoBase { static const float Offset; +#if !ENABLE_EXTENDED_SELECTION static const Vec3d OffsetVec; +#endif // !ENABLE_EXTENDED_SELECTION mutable BoundingBoxf3 m_box; @@ -269,41 +320,44 @@ class GLGizmoScale3D : public GLGizmoBase Vec3d m_starting_scale; Vec3d m_starting_drag_position; - bool m_show_starting_box; BoundingBoxf3 m_starting_box; public: explicit GLGizmoScale3D(GLCanvas3D& parent); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM const Vec3d& get_scale() const { return m_scale; } - void set_scale(const Vec3d& scale) { m_starting_scale = scale; } +#if ENABLE_EXTENDED_SELECTION + void set_scale(const Vec3d& scale) { m_starting_scale = scale; m_scale = scale; } #else - double get_scale_x() const { return m_scale(0); } - void set_scale_x(double scale) { m_starting_scale(0) = scale; } - - double get_scale_y() const { return m_scale(1); } - void set_scale_y(double scale) { m_starting_scale(1) = scale; } - - double get_scale_z() const { return m_scale(2); } - void set_scale_z(double scale) { m_starting_scale(2) = scale; } - - void set_scale(double scale) { m_starting_scale = scale * Vec3d::Ones(); } -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM + void set_scale(const Vec3d& scale) { m_starting_scale = scale; } +#endif // ENABLE_EXTENDED_SELECTION protected: virtual bool on_init(); + virtual std::string on_get_name() const; +#if ENABLE_EXTENDED_SELECTION + virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return !selection.is_wipe_tower(); } +#endif // ENABLE_EXTENDED_SELECTION +#if ENABLE_EXTENDED_SELECTION + virtual void on_start_dragging(const GLCanvas3D::Selection& selection); +#else virtual void on_start_dragging(const BoundingBoxf3& box); virtual void on_stop_dragging() { m_show_starting_box = false; } + +#endif // ENABLE_EXTENDED_SELECTION virtual void on_update(const Linef3& mouse_ray, const Point* mouse_pos); #if ENABLE_GIZMOS_RESET virtual void on_process_double_click(); #endif // ENABLE_GIZMOS_RESET +#if ENABLE_EXTENDED_SELECTION + virtual void on_render(const GLCanvas3D::Selection& selection) const; + virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; +#else virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const; +#endif // ENABLE_EXTENDED_SELECTION private: - void render_box(const BoundingBoxf3& box) const; void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const; void do_scale_x(const Linef3& mouse_ray); @@ -311,14 +365,22 @@ private: void do_scale_z(const Linef3& mouse_ray); void do_scale_uniform(const Linef3& mouse_ray); +#if ENABLE_EXTENDED_SELECTION + double calc_ratio(const Linef3& mouse_ray) const; +#else double calc_ratio(unsigned int preferred_plane_id, const Linef3& mouse_ray, const Vec3d& center) const; +#endif // ENABLE_EXTENDED_SELECTION }; class GLGizmoMove3D : public GLGizmoBase { static const double Offset; +#if ENABLE_EXTENDED_SELECTION + Vec3d m_displacement; +#else Vec3d m_position; +#endif // ENABLE_EXTENDED_SELECTION Vec3d m_starting_drag_position; Vec3d m_starting_box_center; Vec3d m_starting_box_bottom_center; @@ -326,18 +388,37 @@ class GLGizmoMove3D : public GLGizmoBase public: explicit GLGizmoMove3D(GLCanvas3D& parent); +#if ENABLE_EXTENDED_SELECTION + const Vec3d& get_displacement() const { return m_displacement; } +#else const Vec3d& get_position() const { return m_position; } void set_position(const Vec3d& position) { m_position = position; } +#endif // ENABLE_EXTENDED_SELECTION protected: virtual bool on_init(); + virtual std::string on_get_name() const; +#if ENABLE_EXTENDED_SELECTION + virtual void on_start_dragging(const GLCanvas3D::Selection& selection); + virtual void on_stop_dragging(); +#else virtual void on_start_dragging(const BoundingBoxf3& box); +#endif // ENABLE_EXTENDED_SELECTION virtual void on_update(const Linef3& mouse_ray, const Point* mouse_pos); +#if ENABLE_EXTENDED_SELECTION + virtual void on_render(const GLCanvas3D::Selection& selection) const; + virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; +#else virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const; +#endif // ENABLE_EXTENDED_SELECTION private: +#if ENABLE_EXTENDED_SELECTION + double calc_projection(const Linef3& mouse_ray) const; +#else double calc_projection(Axis axis, unsigned int preferred_plane_id, const Linef3& mouse_ray) const; +#endif // ENABLE_EXTENDED_SELECTION }; class GLGizmoFlatten : public GLGizmoBase @@ -354,10 +435,6 @@ private: }; struct SourceDataSummary { std::vector bounding_boxes; // bounding boxes of convex hulls of individual volumes -#if !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - float scaling_factor; - float rotation; -#endif // !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM Vec3d mesh_first_point; }; @@ -365,16 +442,14 @@ private: SourceDataSummary m_source_data; std::vector m_planes; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#if !ENABLE_EXTENDED_SELECTION struct InstanceData { Transform3d matrix; InstanceData(const Transform3d& matrix) : matrix(matrix) {} }; std::vector m_instances; -#else - std::vector m_instances_positions; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM +#endif // !ENABLE_EXTENDED_SELECTION mutable Vec3d m_starting_center; const ModelObject* m_model_object = nullptr; @@ -385,18 +460,27 @@ public: explicit GLGizmoFlatten(GLCanvas3D& parent); void set_flattening_data(const ModelObject* model_object); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM Vec3d get_flattening_rotation() const; -#else - Vec3d get_flattening_normal() const; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM protected: virtual bool on_init(); + virtual std::string on_get_name() const; +#if ENABLE_EXTENDED_SELECTION + virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return selection.is_single_full_instance(); } +#endif // ENABLE_EXTENDED_SELECTION +#if ENABLE_EXTENDED_SELECTION + virtual void on_start_dragging(const GLCanvas3D::Selection& selection); +#else virtual void on_start_dragging(const BoundingBoxf3& box); +#endif // ENABLE_EXTENDED_SELECTION virtual void on_update(const Linef3& mouse_ray, const Point* mouse_pos) {} +#if ENABLE_EXTENDED_SELECTION + virtual void on_render(const GLCanvas3D::Selection& selection) const; + virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; +#else virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const; +#endif // ENABLE_EXTENDED_SELECTION virtual void on_set_state() { if (m_state == On && is_plane_update_necessary()) @@ -414,10 +498,11 @@ private: Eigen::MatrixXf m_V; // vertices Eigen::MatrixXi m_F; // facets indices struct SourceDataSummary { - std::vector bounding_boxes; // bounding boxes of convex hulls of individual volumes + BoundingBoxf3 bounding_box; #if !ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - float scaling_factor; - float rotation; + Vec3d scaling_factor; + Vec3d rotation; + Vec3d offset; #else Transform3d matrix; #endif @@ -438,8 +523,14 @@ public: private: bool on_init(); void on_update(const Linef3& mouse_ray, const Point* mouse_pos); - void on_render(const BoundingBoxf3& box) const; - void on_render_for_picking(const BoundingBoxf3& box) const; +#if ENABLE_EXTENDED_SELECTION + virtual void on_render(const GLCanvas3D::Selection& selection) const; + virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; +#else + virtual void on_render(const BoundingBoxf3& box) const; + virtual void on_render_for_picking(const BoundingBoxf3& box) const; +#endif // ENABLE_EXTENDED_SELECTION + void render_grabbers(bool picking = false) const; void render_tooltip_texture() const; bool is_mesh_update_necessary() const; @@ -453,6 +544,7 @@ protected: if (m_state == On && is_mesh_update_necessary()) update_mesh(); } + std::string on_get_name() const override; }; } // namespace GUI 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 100% rename from xs/src/slic3r/GUI/GLTexture.cpp rename to src/slic3r/GUI/GLTexture.cpp diff --git a/xs/src/slic3r/GUI/GLTexture.hpp b/src/slic3r/GUI/GLTexture.hpp similarity index 100% rename from xs/src/slic3r/GUI/GLTexture.hpp rename to src/slic3r/GUI/GLTexture.hpp diff --git a/xs/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp similarity index 93% rename from xs/src/slic3r/GUI/GLToolbar.cpp rename to src/slic3r/GUI/GLToolbar.cpp index 388868b12f..265fc20426 100644 --- a/xs/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -6,19 +6,39 @@ #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_CUT, SimpleEvent); +#if !ENABLE_EXTENDED_SELECTION +wxDEFINE_EVENT(EVT_GLTOOLBAR_SETTINGS, SimpleEvent); +#endif // !ENABLE_EXTENDED_SELECTION +wxDEFINE_EVENT(EVT_GLTOOLBAR_LAYERSEDITING, SimpleEvent); +#if !ENABLE_EXTENDED_SELECTION +wxDEFINE_EVENT(EVT_GLTOOLBAR_SELECTBYPARTS, SimpleEvent); +#endif // !ENABLE_EXTENDED_SELECTION + + GLToolbarItem::Data::Data() : name("") , tooltip("") , sprite_id(-1) , is_toggable(false) - , action_callback(nullptr) { } @@ -49,10 +69,9 @@ const std::string& GLToolbarItem::get_tooltip() const return m_data.tooltip; } -void GLToolbarItem::do_action() +void GLToolbarItem::do_action(wxEvtHandler *target) { - if (m_data.action_callback != nullptr) - m_data.action_callback->call(); + wxPostEvent(target, SimpleEvent(m_data.action_event)); } bool GLToolbarItem::is_enabled() const @@ -131,6 +150,14 @@ GLToolbar::GLToolbar(GLCanvas3D& parent) { } +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/"; @@ -325,13 +352,13 @@ void GLToolbar::do_action(unsigned int item_id) item->set_state(GLToolbarItem::Hover); m_parent.render(); - item->do_action(); + item->do_action(m_parent.get_wxglcanvas()); } else { item->set_state(GLToolbarItem::HoverPressed); m_parent.render(); - item->do_action(); + 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 @@ -485,7 +512,8 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) } } - m_parent.set_tooltip(tooltip); + if (!tooltip.empty()) + m_parent.set_tooltip(tooltip); } void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) diff --git a/xs/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp similarity index 81% rename from xs/src/slic3r/GUI/GLToolbar.hpp rename to src/slic3r/GUI/GLToolbar.hpp index 65d6748ffe..9f3853530c 100644 --- a/xs/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -1,17 +1,38 @@ #ifndef slic3r_GLToolbar_hpp_ #define slic3r_GLToolbar_hpp_ -#include "../../slic3r/GUI/GLTexture.hpp" -#include "../../callback.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_CUT, SimpleEvent); +#if !ENABLE_EXTENDED_SELECTION +wxDECLARE_EVENT(EVT_GLTOOLBAR_SETTINGS, SimpleEvent); +#endif // !ENABLE_EXTENDED_SELECTION +wxDECLARE_EVENT(EVT_GLTOOLBAR_LAYERSEDITING, SimpleEvent); +#if !ENABLE_EXTENDED_SELECTION +wxDECLARE_EVENT(EVT_GLTOOLBAR_SELECTBYPARTS, SimpleEvent); +#endif // !ENABLE_EXTENDED_SELECTION + class GLToolbarItem { public: @@ -38,7 +59,7 @@ public: std::string tooltip; unsigned int sprite_id; bool is_toggable; - PerlCallback* action_callback; + wxEventType action_event; Data(); }; @@ -57,7 +78,7 @@ public: const std::string& get_name() const; const std::string& get_tooltip() const; - void do_action(); + void do_action(wxEvtHandler *target); bool is_enabled() const; bool is_hovered() const; @@ -121,6 +142,7 @@ private: 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); diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp new file mode 100644 index 0000000000..9e18cac020 --- /dev/null +++ b/src/slic3r/GUI/GUI.cpp @@ -0,0 +1,471 @@ +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "../AppController.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(); + #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{ (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 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 +} + +namespace { +AppControllerPtr g_appctl; +} + +AppControllerPtr get_appctl() +{ + return g_appctl; +} + +void set_cli_appctl() +{ + g_appctl = std::make_shared(); +} + +void set_gui_appctl() +{ + g_appctl = std::make_shared(); +} + +} } diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp new file mode 100644 index 0000000000..cbc20c3c1a --- /dev/null +++ b/src/slic3r/GUI/GUI.hpp @@ -0,0 +1,118 @@ +#ifndef slic3r_GUI_hpp_ +#define slic3r_GUI_hpp_ + +#include "Config.hpp" +#include "callback.hpp" + +#include + +class wxWindow; +class wxMenuBar; +class wxNotebook; +class wxComboCtrl; +class wxFileDialog; +class wxTopLevelWindow; + +namespace Slic3r { + +class AppConfig; +class DynamicPrintConfig; +class PreviewIface; +class Print; +class GCodePreviewData; +class AppControllerBase; + +using AppControllerPtr = std::shared_ptr; + +#define _(s) Slic3r::GUI::I18N::translate((s)) + +namespace GUI { namespace I18N { + inline wxString translate(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)); } + inline wxString translate(const wchar_t *s) { return wxGetTranslation(s); } + inline wxString translate(const std::string &s) { return wxGetTranslation(wxString(s.c_str(), wxConvUTF8)); } + inline wxString translate(const std::wstring &s) { return wxGetTranslation(s.c_str()); } +} } + +// !!! If you needed to translate some wxString, +// !!! please use _(L(string)) +// !!! _() - is a standard wxWidgets macro to translate +// !!! L() is used only for marking localizable string +// !!! It will be used in "xgettext" to create a Locating Message Catalog. +#define L(s) s + +//! macro used to localization, return wxScopedCharBuffer +//! With wxConvUTF8 explicitly specify that the source string is already in UTF-8 encoding +#define _CHB(s) wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str() + +// Minimal buffer length for translated string (char buf[MIN_BUF_LENGTH_FOR_L]) +#define MIN_BUF_LENGTH_FOR_L 512 + +namespace GUI { + +void disable_screensaver(); +void enable_screensaver(); +bool debugged(); +void break_to_debugger(); + +AppConfig* get_app_config(); + +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); + +// 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); + +// Opens the configuration wizard, returns true if wizard is finished & accepted. +// The run_reason argument is actually ConfigWizard::RunReason, but int is used here because of Perl. +extern void config_wizard(int run_reason); + +PreviewIface* create_preview_iface(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data); + +// 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); + +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); + +// 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. +void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value); + +// Returns the current state of the items listed in the wxCheckListBoxComboPopup contained in the given wxComboCtrl, +// encoded inside an int. +int combochecklist_get_flags(wxComboCtrl* comboCtrl); + +// Return translated std::string as a wxString +wxString L_str(const std::string &str); +// Return wxString from std::string in UTF8 +wxString from_u8(const std::string &str); +// 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; + +// Returns the dimensions of the screen on which the main frame is displayed +bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height); + +// Save window size and maximized status into AppConfig +void save_window_size(wxTopLevelWindow *window, const std::string &name); +// Restore the above +void restore_window_size(wxTopLevelWindow *window, const std::string &name); + +// Display an About dialog +extern void about(); +// Ask the destop to open the datadir using the default file explorer. +extern void desktop_open_datadir_folder(); + +} // namespace GUI +} // namespace Slic3r + +#endif diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp new file mode 100644 index 0000000000..0bdaa2ceeb --- /dev/null +++ b/src/slic3r/GUI/GUI_App.cpp @@ -0,0 +1,706 @@ +#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", +}; + + +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"); + 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").empty() ? false : true); + // eval{ + preset_bundle->load_presets(*app_config); + // }; + // if ($@) { + // 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); + + // 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(); +} + + +void GUI_App::open_model(wxWindow *parent, wxArrayString& input_files) +{ + auto dialog = new wxFileDialog(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->Destroy(); + return; + } + + dialog->GetPaths(input_files); + dialog->Destroy(); +} + +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(mainframe->m_plater); + + ConfigMenuIDs mode = wxGetApp().get_view_mode(); + + obj_list()->get_sizer()->Show(mode == ConfigMenuModeExpert); + sidebar().show_info_sizers(mode == ConfigMenuModeExpert); + sidebar().show_buttons(mode == ConfigMenuModeExpert); + obj_manipul()->show_object_name(mode == ConfigMenuModeSimple); + obj_list()->update_manipulation_sizer(mode == ConfigMenuModeSimple); + + sidebar().Layout(); + mainframe->m_plater->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; + for (Tab *tab : 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(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() +{ + for (Tab *tab : tabs_list) { + tab->load_current_preset(); + } +} + +Sidebar& GUI_App::sidebar() +{ + return plater_->sidebar(); +} + +ObjectManipulation* GUI_App::obj_manipul() +{ + return sidebar().obj_manipul(); +} + +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 \ No newline at end of file diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp new file mode 100644 index 0000000000..ccc7026eed --- /dev/null +++ b/src/slic3r/GUI/GUI_App.hpp @@ -0,0 +1,154 @@ +#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_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(); + void open_model(wxWindow *parent, wxArrayString& input_files); + 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(); + 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_ \ No newline at end of file diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp new file mode 100644 index 0000000000..6c9077badf --- /dev/null +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -0,0 +1,1327 @@ +#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 +{ + +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); + } + + init_icons(); + + // create control + create_objects_ctrl(); + + // 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); }); +} + +ObjectList::~ObjectList() +{ + if (m_default_config) + delete m_default_config; +} + +void ObjectList::create_objects_ctrl() +{ + SetMinSize(wxSize(-1, 150)); // TODO - Set correct height according to the opened/closed objects + + m_sizer = new wxBoxSizer(wxVERTICAL); + m_sizer->Add(this, 1, wxGROW | wxLEFT, 20); + + m_objects_model = new PrusaObjectDataViewModel; + AssociateModel(m_objects_model); +#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, 250, 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) { + auto item = GetSelection(); + return m_objects_model->GetIdByItem(item); + } + 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(Slic3r::GUI::from_u8(Slic3r::var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG); + m_bmp_solidmesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG); + + // init icon for manifold warning + m_bmp_manifold_warning = wxBitmap(Slic3r::GUI::from_u8(Slic3r::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(Slic3r::GUI::from_u8(Slic3r::var("split.png")), wxBITMAP_TYPE_PNG); + + // init bitmap for "Add Settings" context menu + m_bmp_cog = wxBitmap(Slic3r::GUI::from_u8(Slic3r::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(); + + 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()) + /*fix_through_netfabb()*/;// #ys_FIXME + } +#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_all(); + 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 (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 + wxGetApp().obj_manipul()->update_settings_list(); +} + +void ObjectList::menu_item_add_generic(wxMenuItem* &menu, int id) { + auto sub_menu = new wxMenu; + + 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, [this, sub_menu](wxEvent &event) { + load_lambda(sub_menu->GetLabel(event.GetId()).ToStdString()); + }); +#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; + std::vector menu_items = { L("Add part"), L("Add modifier"), L("Add generic") }; + + wxWindowID config_id_base = wxWindow::NewControlId(menu_items.size() + 4 + 2); + + int i = 0; + for (auto& item : menu_items) { + auto menu_item = new wxMenuItem(menu, config_id_base + i, _(item)); + menu_item->SetBitmap(i == 0 ? m_bmp_solidmesh : m_bmp_modifiermesh); + if (item == "Add generic") + menu_item_add_generic(menu_item, config_id_base + i); + menu->Append(menu_item); + i++; + } + + menu->AppendSeparator(); + auto menu_item = menu_item_split(menu, config_id_base + i + 4); + menu->Append(menu_item); + menu_item->Enable(is_splittable_object(false)); + + menu->AppendSeparator(); + // Append settings popupmenu + menu->Append(menu_item_settings(menu, config_id_base + i + 5, false)); + + menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event){ + switch (event.GetId() - config_id_base) { + case 0: + load_subobject(); + break; + case 1: + load_subobject(true); + break; + case 2: + case 3: + case 4: + case 5: + case 6: +#ifdef __WXMSW__ + load_lambda(menu->GetLabel(event.GetId()).ToStdString()); +#endif // __WXMSW__ + break; + case 7: //3: + split(false); + break; + default: +#ifdef __WXMSW__ + get_settings_choice(menu, event.GetId(), false); +#endif // __WXMSW__ + break; + } + }); + + return menu; +} + +wxMenu* ObjectList::create_part_settings_popupmenu() +{ + wxMenu *menu = new wxMenu; + wxWindowID config_id_base = wxWindow::NewControlId(2); + + auto menu_item = menu_item_split(menu, config_id_base); + menu->Append(menu_item); + menu_item->Enable(is_splittable_object(true)); + + menu->AppendSeparator(); + // Append settings popupmenu + menu->Append(menu_item_settings(menu, config_id_base + 1, true)); + + menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event){ + switch (event.GetId() - config_id_base) { + case 0: + split(true); + 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; +} + + +// Load SubObjects (parts and modifiers) +void ObjectList::load_subobject(bool is_modifier /*= false*/, bool is_lambda/* = false*/) +{ + auto item = GetSelection(); + if (!item) + return; + int obj_idx = -1; + if (m_objects_model->GetParent(item) == wxDataViewItem(0)) + obj_idx = m_objects_model->GetIdByItem(item); + else + return; + + if (obj_idx < 0) return; + wxArrayString part_names; + if (is_lambda) + load_lambda((*m_objects)[obj_idx], part_names, is_modifier); + else + load_part((*m_objects)[obj_idx], part_names, is_modifier); + + 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), + is_modifier ? m_bmp_modifiermesh : m_bmp_solidmesh); + + 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, + const bool is_modifier) +{ + wxWindow* parent = wxGetApp().tab_panel()->GetPage(0); + + wxArrayString input_files; + wxGetApp().open_model(parent, input_files); + 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(); + delta = model_object->origin_translation - object->origin_translation; + } + for (auto volume : object->volumes) { + auto new_volume = model_object->add_volume(*volume); + new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); + boost::filesystem::path(input_file).filename().string(); + new_volume->name = boost::filesystem::path(input_file).filename().string(); + + part_names.Add(new_volume->name); + + if (delta != Vec3d::Zero()) + { + new_volume->mesh.translate((float)delta(0), (float)delta(1), (float)delta(2)); + new_volume->get_convex_hull().translate((float)delta(0), (float)delta(1), (float)delta(2)); + } + + // 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_lambda( ModelObject* model_object, + wxArrayString& part_names, + const bool is_modifier) +{ + auto dlg = new LambdaObjectDialog(GetMainWindow()); + if (dlg->ShowModal() == wxID_CANCEL) { + return; + } + + std::string name = "lambda-"; + TriangleMesh mesh; + + auto params = dlg->ObjectParameters(); + switch (params.type) + { + case LambdaTypeBox:{ + mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]); + name += "Box"; + break; } + case LambdaTypeCylinder:{ + mesh = make_cylinder(params.cyl_r, params.cyl_h); + name += "Cylinder"; + break; } + case LambdaTypeSphere:{ + mesh = make_sphere(params.sph_rho); + name += "Sphere"; + break; } + case LambdaTypeSlab:{ + const auto& size = model_object->bounding_box().size(); + mesh = make_cube(size(0)*1.5, size(1)*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(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z); + name += "Slab"; + break; } + default: + break; + } + mesh.repair(); + + auto new_volume = model_object->add_volume(mesh); + new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); + + 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)); + + part_names.Add(name); + + m_parts_changed = true; +} + +void ObjectList::load_lambda(const std::string& type_name) +{ + if (m_selected_object_id < 0) return; + + auto dlg = new LambdaObjectDialog(GetMainWindow(), type_name); + if (dlg->ShowModal() == wxID_CANCEL) + return; + + const std::string name = "lambda-" + type_name; + TriangleMesh mesh; + + const auto params = dlg->ObjectParameters(); + if (type_name == _("Box")) + mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]); + else if (type_name == _("Cylinder")) + mesh = make_cylinder(params.cyl_r, params.cyl_h); + else if (type_name == _("Sphere")) + mesh = make_sphere(params.sph_rho); + else if (type_name == _("Slab")){ + const auto& size = (*m_objects)[m_selected_object_id]->bounding_box().size(); + mesh = make_cube(size(0)*1.5, size(1)*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(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z); + } + mesh.repair(); + + auto new_volume = (*m_objects)[m_selected_object_id]->add_volume(mesh); + new_volume->set_type(ModelVolume::PARAMETER_MODIFIER); + + 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(m_selected_object_id); + + select_item(m_objects_model->AddVolumeChild(GetSelection(), + name, m_bmp_modifiermesh)); +#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME + selection_changed(); +#endif //no __WXOSX__ //__WXMSW__ +} + + +// 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(); + if (!item || m_selected_object_id < 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 auto nozzle_dmrs_cnt = config.option("nozzle_diameter")->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)[m_selected_object_id]; + + if (split_part) { + auto parent = m_objects_model->GetParent(item); + m_objects_model->DeleteChildren(parent); + + for (auto id = 0; id < model_object->volumes.size(); id++) + m_objects_model->AddVolumeChild(parent, model_object->volumes[id]->name, + model_object->volumes[id]->is_modifier() ? m_bmp_modifiermesh : m_bmp_solidmesh, + model_object->volumes[id]->config.has("extruder") ? + model_object->volumes[id]->config.option("extruder")->value : 0, + false); + + Expand(parent); + } + else { + for (auto id = 0; id < model_object->volumes.size(); id++) + m_objects_model->AddVolumeChild(item, model_object->volumes[id]->name, + m_bmp_solidmesh, + model_object->volumes[id]->config.has("extruder") ? + model_object->volumes[id]->config.option("extruder")->value : 0, + false); + Expand(item); + } + + m_parts_changed = true; + parts_changed(m_selected_object_id); +} + +bool ObjectList::get_volume_by_item(const bool split_part, const wxDataViewItem& item, ModelVolume*& volume) +{ + if (!item || m_selected_object_id < 0) + return false; + const auto volume_id = m_objects_model->GetVolumeIdByItem(item); + if (volume_id < 0) { + if (split_part) return false; + volume = (*m_objects)[m_selected_object_id]->volumes[0]; + } + else + volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; + if (volume) + return true; + return false; +} + +bool ObjectList::is_splittable_object(const bool split_part) +{ + const wxDataViewItem item = GetSelection(); + if (!item) return false; + + wxDataViewItemArray children; + if (!split_part && m_objects_model->GetChildren(item, children) > 0) + 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::parts_changed(int obj_idx) +{ + wxGetApp().mainframe->m_plater->changed_object_settings(obj_idx); +} + +void ObjectList::part_selection_changed() +{ + auto item = GetSelection(); + int obj_idx = -1; + ConfigOptionsGroup* og = wxGetApp().obj_manipul()->get_og(); + m_config = nullptr; + wxString object_name = wxEmptyString; + if (item) + { + const bool is_settings_item = m_objects_model->IsSettingsItem(item); + bool is_part = false; + wxString og_name = wxEmptyString; + 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; + } + 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 (is_settings_item) { + 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; + } + } + 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; + } + } + + og->set_name(" " + og_name + " "); + object_name = m_objects_model->GetName(item); + if (m_default_config) delete m_default_config; + m_default_config = DynamicPrintConfig::new_from_defaults_keys(get_options(is_part)); + } + og->set_value("object_name", object_name); + + wxGetApp().obj_manipul()->update_settings_list(); + + m_selected_object_id = obj_idx; + +#if ENABLE_EXTENDED_SELECTION + wxGetApp().obj_manipul()->update_settings_value(_3DScene::get_canvas(wxGetApp().canvas3D())->get_selection()); +#else + wxGetApp().obj_manipul()->update_values(); +#endif // ENABLE_EXTENDED_SELECTION +} + +void ObjectList::update_manipulation_sizer(const bool is_simple_mode) +{ + auto item = GetSelection(); /// #ys_FIXME_to_multi_sel + if (!item || !is_simple_mode) + return; + + if (m_objects_model->IsSettingsItem(item)) { + select_item(m_objects_model->GetParent(item)); + } +} + +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); +#if !ENABLE_EXTENDED_SELECTION + /*Select*/select_item(item); +#endif // !ENABLE_EXTENDED_SELECTION + + // 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); + } + + 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, + m_bmp_solidmesh, + model_object->volumes[id]->config.option("extruder")->value, + false); + Expand(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_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() +{ +#if ENABLE_EXTENDED_SELECTION + auto& selection = _3DScene::get_canvas(wxGetApp().canvas3D())->get_selection(); + wxDataViewItemArray sels; + + for (auto idx: selection.get_volume_idxs()) + { + const auto gl_vol = selection.get_volume(idx); + sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); + } + select_items(sels); + +#endif // ENABLE_EXTENDED_SELECTION +} + +void ObjectList::update_selections_on_canvas() +{ +#if ENABLE_EXTENDED_SELECTION + 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, 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()); + +#endif // ENABLE_EXTENDED_SELECTION +} + +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::fix_multiselection_conflicts() +{ + if (GetSelectedItemsCount() <= 1) + return; + + m_prevent_list_events = true; + + wxDataViewItemArray sels; + GetSelections(sels); + + for (auto item : sels) { + if (m_objects_model->IsSettingsItem(item)) + Unselect(item); + else if (m_objects_model->GetParent(item) != wxDataViewItem(0)) + Unselect(m_objects_model->GetParent(item)); + } + + m_prevent_list_events = false; +} + +} //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..56d01b7f50 --- /dev/null +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -0,0 +1,146 @@ +#ifndef slic3r_GUI_ObjectList_hpp_ +#define slic3r_GUI_ObjectList_hpp_ + +#include +#include +#include +#include + +class wxBoxSizer; +class PrusaObjectDataViewModel; + +namespace Slic3r { +class ConfigOptionsGroup; +class DynamicPrintConfig; +class ModelObject; +class ModelVolume; + +namespace GUI { + +class ObjectList : public wxDataViewCtrl +{ + wxBoxSizer *m_sizer {nullptr}; + + DynamicPrintConfig *m_default_config {nullptr}; + + wxBitmap m_bmp_modifiermesh; + wxBitmap m_bmp_solidmesh; + wxBitmap m_bmp_manifold_warning; + wxBitmap m_bmp_cog; + wxBitmap m_bmp_split; + + 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); + 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(bool is_modifier = false, bool is_lambda = false); + void load_part(ModelObject* model_object, wxArrayString& part_names, const bool is_modifier); + void load_lambda(ModelObject* model_object, wxArrayString& part_names, const bool is_modifier); + void load_lambda(const std::string& type_name); + 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 parts_changed(int obj_idx); + void part_selection_changed(); + + void update_manipulation_sizer(const bool is_simple_mode); + + // 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); + // 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(); + // correct current selections to avoid of the possible conflicts + void fix_multiselection_conflicts(); +}; + + +}} + +#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..ca35871187 --- /dev/null +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -0,0 +1,468 @@ +#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 +{ + +OG_Settings::OG_Settings(wxWindow* parent, const bool staticbox) +{ + wxString title = staticbox ? " " : ""; // temporary workaround - #ys_FIXME + m_og = std::make_shared(parent, title); +} + +wxSizer* OG_Settings::get_sizer() +{ + return m_og->sizer; +} + +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->m_on_change = [this](t_config_option_key opt_key, boost::any value){ + if (opt_key == "scale_unit"){ + const wxString& selection = boost::any_cast(value); + std::vector axes{ "x", "y", "z" }; + for (auto axis : axes) { + std::string key = "scale_" + axis; + m_og->set_side_text(key, selection); + } + + m_is_percent_scale = selection == _("%"); + update_scale_values(); + } + }; + + 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 = 55; + + 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) + { + int def_value = 0; + 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 = coInt; + def.default_value = new ConfigOptionInt(def_value); + def.width = 55; + + 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")); + + + 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")); + + m_settings_list_sizer = new wxBoxSizer(wxVERTICAL); + m_og->sizer->Add(m_settings_list_sizer, 1, wxEXPAND | wxLEFT, 5); + + m_og->disable(); +} + +int ObjectManipulation::ol_selection() +{ + return wxGetApp().obj_list()->get_selected_obj_idx(); +} + +void ObjectManipulation::update_settings_list() +{ +#ifdef __WXGTK__ + auto parent = m_og->get_parent(); +#else + auto parent = m_og->parent(); +#endif /* __WXGTK__ */ + +// There is a bug related to Ubuntu overlay scrollbars, see https://github.com/prusa3d/Slic3r/issues/898 and https://github.com/prusa3d/Slic3r/issues/952. +// The issue apparently manifests when Show()ing a window with overlay scrollbars while the UI is frozen. For this reason, +// we will Thaw the UI prematurely on Linux. This means destroing the no_updates object prematurely. +#ifdef __linux__ + std::unique_ptr no_updates(new wxWindowUpdateLocker(parent)); +#else + wxWindowUpdateLocker noUpdates(parent); +#endif + + m_settings_list_sizer->Clear(true); + bool show_manipulations = 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 (!objects_ctrl->multiple_selection() && + config && objects_model->IsSettingsItem(item)) + { + auto extra_column = [config](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](wxEvent &event){ + config->erase(opt_key); + wxTheApp->CallAfter([]() { wxGetApp().obj_manipul()->update_settings_list(); }); + }); + 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(parent, cat.first, config, false, extra_column); + optgroup->label_width = 150; + optgroup->sidetext_width = 70; + + 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(objects_model->Delete(item)); + wxGetApp().obj_list()->part_selection_changed(); + } + else { + if (!categories.empty()) + objects_model->UpdateSettingsDigest(item, categories); + show_manipulations = false; + } + } + + show_manipulation_og(show_manipulations); + wxGetApp().sidebar().show_info_sizers(show_manipulations && item && objects_model->GetParent(item) == wxDataViewItem(0)); + +#ifdef __linux__ + no_updates.reset(nullptr); +#endif + + parent->Layout(); + parent->GetParent()->Layout(); +} + +#if ENABLE_EXTENDED_SELECTION +void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection) +{ + if (selection.is_single_full_object()) + { + if (wxGetApp().mainframe->m_plater->model().objects[selection.get_object_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()) + { + // 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 if (selection.is_wipe_tower()) + { + // the selection contains a single volume + 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 if (selection.is_modifier()) + { + // the selection contains a single volume + 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(); +} + +void ObjectManipulation::reset_settings_value() +{ + reset_position_value(); + reset_rotation_value(); + reset_scale_value(); + m_og->disable(); +} + +void ObjectManipulation::reset_position_value() +{ + m_og->set_value("position_x", 0); + m_og->set_value("position_y", 0); + m_og->set_value("position_z", 0); +} + +void ObjectManipulation::reset_rotation_value() +{ + m_og->set_value("rotation_x", 0); + m_og->set_value("rotation_y", 0); + m_og->set_value("rotation_z", 0); +} + +void ObjectManipulation::reset_scale_value() +{ + m_is_percent_scale = true; + m_og->set_value("scale_unit", _("%")); + m_og->set_value("scale_x", 100); + m_og->set_value("scale_y", 100); + m_og->set_value("scale_z", 100); +} +#endif // ENABLE_EXTENDED_SELECTION + +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", 0); + m_og->set_value("position_y", 0); + m_og->set_value("position_z", 0); + m_og->set_value("scale_x", 0); + m_og->set_value("scale_y", 0); + m_og->set_value("scale_z", 0); + m_og->set_value("rotation_x", 0); + m_og->set_value("rotation_y", 0); + m_og->set_value("rotation_z", 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", int(instance->get_scaling_factor(X) * 100)); + m_og->set_value("scale_y", int(instance->get_scaling_factor(Y) * 100)); + m_og->set_value("scale_z", int(instance->get_scaling_factor(Z) * 100)); + } + else { + m_og->set_value("scale_x", int(instance->get_scaling_factor(X) * size(0) + 0.5)); + m_og->set_value("scale_y", int(instance->get_scaling_factor(Y) * size(1) + 0.5)); + m_og->set_value("scale_z", int(instance->get_scaling_factor(Z) * size(2) + 0.5)); + } +} + +void ObjectManipulation::update_position_values() +{ + auto instance = wxGetApp().mainframe->m_plater->model().objects[ol_selection()]->instances.front(); + + m_og->set_value("position_x", int(instance->get_offset(X))); + m_og->set_value("position_y", int(instance->get_offset(Y))); + m_og->set_value("position_z", int(instance->get_offset(Z))); +} + +void ObjectManipulation::update_position_value(const Vec3d& position) +{ + m_og->set_value("position_x", int(position(0))); + m_og->set_value("position_y", int(position(1))); + m_og->set_value("position_z", int(position(2))); +} + +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_og->set_value("scale_unit", _("%")); + + auto scale = scaling_factor * 100.0; + m_og->set_value("scale_x", int(scale(0))); + m_og->set_value("scale_y", int(scale(1))); + m_og->set_value("scale_z", int(scale(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", int(round_nearest(Geometry::rad2deg(rotation(0)), 0))); + m_og->set_value("rotation_y", int(round_nearest(Geometry::rad2deg(rotation(1)), 0))); + m_og->set_value("rotation_z", int(round_nearest(Geometry::rad2deg(rotation(2)), 0))); +} + +void ObjectManipulation::show_object_name(bool show) +{ + wxGridSizer* grid_sizer = m_og->get_grid_sizer(); + grid_sizer->Show(static_cast(0), show); + grid_sizer->Show(static_cast(1), show); +} + +void ObjectManipulation::show_manipulation_og(const bool show) +{ + wxGridSizer* grid_sizer = m_og->get_grid_sizer(); + if (show == grid_sizer->IsShown(2)) + return; + for (size_t id = 2; id < 12; id++) + grid_sizer->Show(id, show); +} + +} //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..13b9290a0a --- /dev/null +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -0,0 +1,79 @@ +#ifndef slic3r_GUI_ObjectManipulation_hpp_ +#define slic3r_GUI_ObjectManipulation_hpp_ + +#include + +#include + +#include "Preset.hpp" +#if ENABLE_EXTENDED_SELECTION +#include "GLCanvas3D.hpp" +#endif // ENABLE_EXTENDED_SELECTION + +class wxBoxSizer; + +namespace Slic3r { +namespace GUI { +class ConfigOptionsGroup; + + +class OG_Settings +{ +protected: + std::shared_ptr m_og; +public: + OG_Settings(wxWindow* parent, const bool staticbox); + ~OG_Settings() {} + + wxSizer* get_sizer(); + ConfigOptionsGroup* get_og() { return m_og.get(); } +}; + + +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 + // sizer for extra Object/Part's settings + wxBoxSizer* m_settings_list_sizer{ nullptr }; + // option groups for settings + std::vector > m_og_settings; + +public: + ObjectManipulation(wxWindow* parent); + ~ObjectManipulation() {} + + int ol_selection(); + void update_settings_list(); + +#if ENABLE_EXTENDED_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(); +#endif // ENABLE_EXTENDED_SELECTION + + 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; } + + void show_object_name(bool show); + void show_manipulation_og(const bool show); +}; + +}} + +#endif // slic3r_GUI_ObjectManipulation_hpp_ diff --git a/xs/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp similarity index 79% rename from xs/src/slic3r/GUI/GUI_Preview.cpp rename to src/slic3r/GUI/GUI_Preview.cpp index c66d1fd1f2..5618eab043 100644 --- a/xs/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -3,8 +3,10 @@ #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 @@ -57,24 +59,10 @@ bool Preview::init(wxNotebook* notebook, DynamicPrintConfig* config, Print* prin if (!Create(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize)) return false; - int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24, WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0 }; - - 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); - - // if multisample is not enabled or supported by the graphic card, remove it from the attributes list - bool can_multisample = enable_multisample && wxGLCanvas::IsExtensionSupported("WGL_ARB_multisample"); -// bool can_multisample = enable_multisample && wxGLCanvas::IsDisplaySupported(attribList); // <<< Alternative method: but IsDisplaySupported() seems not to work - if (!can_multisample) - attribList[4] = 0; - - m_canvas = new wxGLCanvas(this, wxID_ANY, attribList); - if (m_canvas == nullptr) - return false; + m_canvas = GLCanvas3DManager::create_wxglcanvas(this); _3DScene::add_canvas(m_canvas); - _3DScene::allow_multisample(m_canvas, can_multisample); + _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); @@ -82,7 +70,7 @@ bool Preview::init(wxNotebook* notebook, DynamicPrintConfig* config, Print* prin _3DScene::enable_dynamic_background(m_canvas, true); m_double_slider_sizer = new wxBoxSizer(wxHORIZONTAL); - create_double_slider(this, m_double_slider_sizer, m_canvas); + create_double_slider(); m_label_view_type = new wxStaticText(this, wxID_ANY, _(L("View"))); @@ -128,18 +116,18 @@ bool Preview::init(wxNotebook* notebook, DynamicPrintConfig* config, Print* prin 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 | 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 | 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 | wxALIGN_CENTER_VERTICAL, 5); + bottom_sizer->Add(m_checkbox_travel, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(10); - bottom_sizer->Add(m_checkbox_retractions, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + bottom_sizer->Add(m_checkbox_retractions, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(10); - bottom_sizer->Add(m_checkbox_unretractions, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + bottom_sizer->Add(m_checkbox_unretractions, 0, wxEXPAND | wxALL, 5); bottom_sizer->AddSpacer(10); - bottom_sizer->Add(m_checkbox_shells, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + 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); @@ -183,12 +171,6 @@ Preview::~Preview() } } -void Preview::register_on_viewport_changed_callback(void* callback) -{ - if ((m_canvas != nullptr) && (callback != nullptr)) - _3DScene::register_on_viewport_changed_callback(m_canvas, callback); -} - void Preview::set_number_extruders(unsigned int number_extruders) { if (m_number_extruders != number_extruders) @@ -211,7 +193,8 @@ void Preview::set_number_extruders(unsigned int number_extruders) void Preview::reset_gcode_preview_data() { m_gcode_preview_data->reset(); - _3DScene::reset_legend_texture(); + if (m_canvas != nullptr) + _3DScene::reset_legend_texture(m_canvas); } void Preview::set_canvas_as_dirty() @@ -287,10 +270,11 @@ void Preview::load_print() if (n_layers == 0) { reset_sliders(); - _3DScene::reset_legend_texture(); - if (m_canvas) + if (m_canvas != nullptr) + { + _3DScene::reset_legend_texture(m_canvas); m_canvas->Refresh(); - + } return; } @@ -485,5 +469,104 @@ void Preview::on_checkbox_shells(wxCommandEvent& evt) 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(); + }); +} + +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); + + 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; + } + } +} + +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/xs/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp similarity index 74% rename from xs/src/slic3r/GUI/GUI_Preview.hpp rename to src/slic3r/GUI/GUI_Preview.hpp index bdd69a0754..ab7544ed8c 100644 --- a/xs/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -13,6 +13,7 @@ class wxStaticText; class wxChoice; class wxComboCtrl; class wxCheckBox; +class PrusaDoubleSlider; namespace Slic3r { @@ -46,11 +47,14 @@ class Preview : public wxPanel 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); virtual ~Preview(); - void register_on_viewport_changed_callback(void* callback); + wxGLCanvas* get_wxglcanvas() { return m_canvas; } + void set_number_extruders(unsigned int number_extruders); void reset_gcode_preview_data(); void set_canvas_as_dirty(); @@ -64,7 +68,7 @@ public: 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); @@ -83,6 +87,19 @@ private: 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 diff --git a/xs/src/slic3r/GUI/GUI_PreviewIface.cpp b/src/slic3r/GUI/GUI_PreviewIface.cpp similarity index 89% rename from xs/src/slic3r/GUI/GUI_PreviewIface.cpp rename to src/slic3r/GUI/GUI_PreviewIface.cpp index 9048beb338..cbec512059 100644 --- a/xs/src/slic3r/GUI/GUI_PreviewIface.cpp +++ b/src/slic3r/GUI/GUI_PreviewIface.cpp @@ -4,11 +4,6 @@ namespace Slic3r { -void PreviewIface::register_on_viewport_changed_callback(void* callback) -{ - m_preview->register_on_viewport_changed_callback(callback); -} - void PreviewIface::set_number_extruders(unsigned int number_extruders) { m_preview->set_number_extruders(number_extruders); diff --git a/xs/src/slic3r/GUI/GUI_PreviewIface.hpp b/src/slic3r/GUI/GUI_PreviewIface.hpp similarity index 93% rename from xs/src/slic3r/GUI/GUI_PreviewIface.hpp rename to src/slic3r/GUI/GUI_PreviewIface.hpp index 86f155bd5a..df5fccaaf5 100644 --- a/xs/src/slic3r/GUI/GUI_PreviewIface.hpp +++ b/src/slic3r/GUI/GUI_PreviewIface.hpp @@ -19,7 +19,6 @@ class PreviewIface public: explicit PreviewIface(GUI::Preview* preview) : m_preview(preview) {} - void 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); diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp new file mode 100644 index 0000000000..5a7ece5865 --- /dev/null +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -0,0 +1,127 @@ +#include "GUI_Utils.hpp" + +#include +#include +#include + +#include +#include +#include + +#include "libslic3r/Config.hpp" + + +namespace Slic3r { +namespace GUI { + + +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]; + + 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..fe96e5a1b8 --- /dev/null +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -0,0 +1,74 @@ +#ifndef slic3r_GUI_Utils_hpp_ +#define slic3r_GUI_Utils_hpp_ + +#include +#include + +#include + +#include +#include +#include + +class wxCheckBox; +class wxTopLevelWindow; +class wxRect; + + +namespace Slic3r { +namespace GUI { + + +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/xs/src/slic3r/GUI/LambdaObjectDialog.cpp b/src/slic3r/GUI/LambdaObjectDialog.cpp similarity index 99% rename from xs/src/slic3r/GUI/LambdaObjectDialog.cpp rename to src/slic3r/GUI/LambdaObjectDialog.cpp index 7d741be7ff..d1d8e60467 100644 --- a/xs/src/slic3r/GUI/LambdaObjectDialog.cpp +++ b/src/slic3r/GUI/LambdaObjectDialog.cpp @@ -8,7 +8,6 @@ namespace Slic3r { namespace GUI { -static wxString dots("…", wxConvUTF8); LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent, const wxString type_name): diff --git a/xs/src/slic3r/GUI/LambdaObjectDialog.hpp b/src/slic3r/GUI/LambdaObjectDialog.hpp similarity index 74% rename from xs/src/slic3r/GUI/LambdaObjectDialog.hpp rename to src/slic3r/GUI/LambdaObjectDialog.hpp index 8f3e8cd80d..9ee7824fcb 100644 --- a/xs/src/slic3r/GUI/LambdaObjectDialog.hpp +++ b/src/slic3r/GUI/LambdaObjectDialog.hpp @@ -13,6 +13,24 @@ 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 { diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp new file mode 100644 index 0000000000..c15532d709 --- /dev/null +++ b/src/slic3r/GUI/MainFrame.cpp @@ -0,0 +1,777 @@ +#include "MainFrame.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Tab.hpp" +#include "PresetBundle.hpp" +#include "../AppController.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) +{ + m_appController = new Slic3r::AppController(); + + // 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_appController->set_model(&m_plater->model()); + m_appController->set_print(&m_plater->print()); + + GUI::set_gui_appctl(); + + // Make the global status bar and its progress indicator available in C++ + m_appController->set_global_progress_indicator(m_statusbar); + + 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(); + return; +} + +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(); +// panel->OnActivate(); if panel->can('OnActivate'); + + if (panel == nullptr) + return; + + for (auto& tab_name : { "print", "filament", "printer" }) { + if (tab_name == panel->GetName()) { + // On GTK, the wxEVT_NOTEBOOK_PAGE_CHANGED event is triggered + // before the MainFrame is fully set up. + auto it = m_options_tabs.find(tab_name); + assert(it != m_options_tabs.end()); + if (it != m_options_tabs.end()) + it->second->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(); + std::vector tab_names = { "print", "filament", "sla_material", "printer" }; + for (auto tab_name : tab_names) + m_options_tabs[tab_name] = get_preset_tab(tab_name.c_str()); + + 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()); + } + } +} + +std::vector preset_tabs = { + { "print", nullptr, ptFFF }, + { "filament", nullptr, ptFFF }, + { "sla_material", nullptr, ptSLA } +}; + +std::vector& MainFrame::get_preset_tabs() { + return preset_tabs; +} + +Tab* MainFrame::get_tab(const std::string& name) +{ + std::vector::iterator it = std::find_if(preset_tabs.begin(), preset_tabs.end(), + [name](PresetTab& tab){ return name == tab.name; }); + return it != preset_tabs.end() ? it->panel : nullptr; +} + +Tab* MainFrame::get_preset_tab(const std::string& name) +{ + Tab* tab = get_tab(name); + if (tab) return tab; + + for (size_t i = 0; i < m_tabpanel->GetPageCount(); ++i) { + tab = dynamic_cast(m_tabpanel->GetPage(i)); + if (!tab) + continue; + if (tab->name() == name) { + return tab; + } + } + return nullptr; +} + +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 TabSLAMaterial(m_tabpanel)); + add_created_tab(new TabPrinter(m_tabpanel)); +} + +void MainFrame::add_created_tab(Tab* panel) +{ + panel->create_preset_tab(); + + const wxString& tab_name = panel->GetName(); + bool add_panel = true; + + auto it = std::find_if(preset_tabs.begin(), preset_tabs.end(), + [tab_name](PresetTab& tab){return tab.name == tab_name; }); + if (it != preset_tabs.end()) { + it->panel = panel; + add_panel = it->technology == wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); + } + + if (add_panel) + m_tabpanel->AddPage(panel, panel->title()); +} + +void MainFrame::init_menubar() +{ + // File menu + wxMenu* fileMenu = new wxMenu; + { + 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(); + wxMenuItem* repeat = nullptr; + append_menu_item(fileMenu, wxID_ANY, _(L("Q&uick Slice…\tCtrl+U")), _(L("Slice a file into a G-code")), + [this, repeat](wxCommandEvent&){ + wxTheApp->CallAfter([this, repeat](){ + quick_slice(); + 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, repeat](wxCommandEvent&){ + wxTheApp->CallAfter([this, repeat](){ + quick_slice(qsSaveAs); + repeat->Enable(is_last_input_file()); + }); }, "cog_go.png"); + 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"); + repeat->Enable(0); + 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"); + append_menu_item(fileMenu, wxID_ANY, _(L("Slice to PNG…")), _(L("Slice file to a set of PNG files")), + [this](wxCommandEvent&){ slice_to_png(); /*$self->quick_slice(save_as = > 0, export_png = > 1);*/ }, "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); } ); + } + + // 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"); + } + + // 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 (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"); }); + } + + // 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 (m_plater_menu) menubar->Append(m_plater_menu, L("&Plater")) ; + menubar->Append(windowMenu, L("&Window")); + if (m_viewMenu) menubar->Append(m_viewMenu, L("&View")); + // Add additional menus from C++ + wxGetApp().add_config_menu(menubar); + menubar->Append(helpMenu, L("&Help")); + SetMenuBar(menubar); + } +} + +void MainFrame::slice_to_png(){ +// m_plater->stop_background_process(); +// m_plater->async_apply_config(); + m_appController->print_ctl()->slice_to_png(); +} + +// 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; + } + for (auto tab : m_options_tabs ) + tab.second->load_current_preset(); + 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. + for (auto tab : m_options_tabs) + tab.second->load_current_preset(); + + 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 : m_options_tabs) + tab.second->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) { + auto reload_dependent_tabs = tab->get_dependent_tabs(); + + // 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->sidebar().update_presets(preset_type); + + if (preset_type == Slic3r::Preset::TYPE_PRINTER) { + // Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors. + // XXX: Do this in a more C++ way + for (const auto tab_name_other : { "print", "filament", "sla_material" }) { + Tab* cur_tab = m_options_tabs[tab_name_other]; + // 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. + if (reload_dependent_tabs.empty() || + find(reload_dependent_tabs.begin(), reload_dependent_tabs.end(), tab_name_other) == + reload_dependent_tabs.end() ) + cur_tab->update_tab_ui(); + else + cur_tab->load_current_preset(); + } + } + m_plater->on_config_change(*tab->get_config()); + } +} + +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(); + std::vector tab_names = { "print", "filament", "printer" }; + for (auto tab_name: tab_names) + m_options_tabs[tab_name]->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..04201b7092 --- /dev/null +++ b/src/slic3r/GUI/MainFrame.hpp @@ -0,0 +1,113 @@ +#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; +class AppController; + +// #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; + + AppController* m_appController { nullptr }; + std::map m_options_tabs; + + wxMenuItem* m_menu_item_reslice_now { nullptr }; + wxMenu* m_plater_menu { nullptr }; + wxMenu* m_viewMenu{ nullptr }; + + 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&); + Tab* get_tab(const std::string& name); + +public: + MainFrame() {} + MainFrame(const bool no_plater, const bool loaded); + ~MainFrame() {} + + + void init_tabpanel(); + const std::map& options_tabs() const { return m_options_tabs; } + Tab* get_preset_tab(const std::string& name); + 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 slice_to_png(); + 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); + + AppController* app_controller() { return m_appController; } + + std::vector& get_preset_tabs(); + + Plater* m_plater { nullptr }; + wxNotebook* m_tabpanel { nullptr }; + wxProgressDialog* m_progress_dialog { nullptr }; + ProgressStatusBar* m_statusbar { nullptr }; +}; + +} // GUI +} //Slic3r + +#endif // slic3r_MainFrame_hpp_ \ No newline at end of file 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 90% rename from xs/src/slic3r/GUI/OptionsGroup.cpp rename to src/slic3r/GUI/OptionsGroup.cpp index ea22b2cb5a..371747bc13 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -97,8 +97,8 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co void OptionsGroup::add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field) { if (!m_show_modified_btns) { - field->m_Undo_btn->Hide(); - field->m_Undo_to_sys_btn->Hide(); + field->m_Undo_btn->set_as_hidden(); + field->m_Undo_to_sys_btn->set_as_hidden(); return; } @@ -123,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 && @@ -156,16 +160,8 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* #endif /* __WXGTK__ */ // if we have an extra column, build it - if (extra_column) { - if (extra_column) { - grid_sizer->Add(extra_column(parent(), line), 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 3); - } - else { - // if the callback provides no sizer for the extra cell, put a spacer - grid_sizer->AddSpacer(1); - } - } - + 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; @@ -182,16 +178,14 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* label->SetFont(label_font); label->Wrap(label_width); // avoid a Linux/GTK bug if (!line.near_label_widget) - grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | - (m_flag == ogSIDE_OPTIONS_VERTICAL ? wxTOP : wxALIGN_CENTER_VERTICAL), 5); + 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) | - (m_flag == ogSIDE_OPTIONS_VERTICAL ? wxTOP : wxALIGN_CENTER_VERTICAL), 5); + sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5); } if (line.label_tooltip.compare("") != 0) label->SetToolTip(line.label_tooltip); @@ -208,7 +202,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* // 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(m_flag == ogSIDE_OPTIONS_VERTICAL ? wxVERTICAL : wxHORIZONTAL); + auto sizer = new wxBoxSizer(wxHORIZONTAL); 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 && @@ -218,23 +212,16 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* 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(), 1, (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; } for (auto opt : option_set) { ConfigOptionDef option = opt.opt; - wxSizer* sizer_tmp; - if (m_flag == ogSIDE_OPTIONS_VERTICAL){ - auto sz = new wxFlexGridSizer(1, 3, 2, 2); - sz->RemoveGrowableCol(2); - sizer_tmp = sz; - } - else - sizer_tmp = sizer; + wxSizer* sizer_tmp = sizer; // add label if any if (option.label != "") { wxString str_label = _(option.label); @@ -244,7 +231,7 @@ 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_tmp->Add(label, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 0); + sizer_tmp->Add(label, 0, /*wxALIGN_RIGHT |*/ wxALIGN_CENTER_VERTICAL, 0); } // add field @@ -260,7 +247,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* auto sidetext = new wxStaticText( parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition, wxSize(sidetext_width, -1)/*wxDefaultSize*/, wxALIGN_LEFT); sidetext->SetFont(sidetext_font); - sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, m_flag == ogSIDE_OPTIONS_VERTICAL ? 0 : 4); + sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); field->set_side_text_ptr(sidetext); } @@ -269,13 +256,10 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* 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 && m_flag != ogSIDE_OPTIONS_VERTICAL) //! istead of (opt != option_set.back()) + if (opt.opt_id != option_set.back().opt_id) //! istead of (opt != option_set.back()) { sizer_tmp->AddSpacer(6); } - - if (m_flag == ogSIDE_OPTIONS_VERTICAL) - sizer->Add(sizer_tmp, 0, wxALIGN_RIGHT|wxALL, 0); } // add extra sizers if any for (auto extra_widget : line.get_extra_widgets()) { @@ -403,6 +387,39 @@ void ConfigOptionsGroup::reload_config(){ } +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; + + sizer->ShowItems(true); +#ifdef __WXGTK__ + m_panel->Show(true); + m_grid_sizer->Show(true); +#endif /* __WXGTK__ */ + + 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) { diff --git a/xs/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp similarity index 93% rename from xs/src/slic3r/GUI/OptionsGroup.hpp rename to src/slic3r/GUI/OptionsGroup.hpp index 4941e54539..656ae1d722 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__ @@ -26,11 +27,6 @@ namespace Slic3r { namespace GUI { -enum ogDrawFlag{ - ogDEFAULT, - ogSIDE_OPTIONS_VERTICAL -}; - /// 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; @@ -150,7 +146,6 @@ public: inline void enable() { for (auto& field : m_fields) field.second->enable(); } inline void disable() { for (auto& field : m_fields) field.second->disable(); } - void set_flag(ogDrawFlag flag) { m_flag = flag; } void set_grid_vgap(int gap) { m_grid_sizer->SetVGap(gap); } void set_show_modified_btns_val(bool show) { @@ -158,12 +153,13 @@ public: } OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false, - ogDrawFlag flag = ogDEFAULT, column_t extra_clmn = nullptr) : - m_parent(_parent), title(title), m_show_modified_btns(is_tab_opt), - staticbox(title!=""), m_flag(flag), extra_column(extra_clmn){ + 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(bold_font()); + stb->SetFont(wxGetApp().bold_font()); } sizer = (staticbox ? new wxStaticBoxSizer(stb, wxVERTICAL) : new wxBoxSizer(wxVERTICAL)); auto num_columns = 1U; @@ -171,7 +167,7 @@ public: if (extra_column != nullptr) num_columns++; 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); + 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); @@ -186,6 +182,7 @@ public: 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 @@ -196,8 +193,6 @@ protected: // "true" if option is created in preset tabs bool m_show_modified_btns{ false }; - ogDrawFlag m_flag{ ogDEFAULT }; - // This panel is needed for correct showing of the ToolTips for Button, StaticText and CheckBox // Tooltips on GTK doesn't work inside wxStaticBoxSizer unless you insert a panel // inside it before you insert the other controls. @@ -222,14 +217,15 @@ protected: class ConfigOptionsGroup: public OptionsGroup { public: ConfigOptionsGroup( wxWindow* parent, const wxString& title, DynamicPrintConfig* _config = nullptr, - bool is_tab_opt = false, ogDrawFlag flag = ogDEFAULT, column_t extra_clmn = nullptr) : - OptionsGroup(parent, title, is_tab_opt, flag, extra_clmn), m_config(_config) {} + 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); @@ -250,6 +246,8 @@ 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 + 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); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp new file mode 100644 index 0000000000..59ebe10b23 --- /dev/null +++ b/src/slic3r/GUI/Plater.cpp @@ -0,0 +1,2577 @@ +#include "Plater.hpp" + +#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/Print.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 "slic3r/AppController.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 "PrintConfig.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; +private: + wxStaticText *info_size; + wxStaticText *info_volume; + wxStaticText *info_facets; + wxStaticText *info_materials; + wxStaticText *info_manifold; +}; + +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"))); + + 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); + grid_sizer->Add(sizer_manifold, 0, wxEXPAND | wxTOP, 4); + + Add(grid_sizer, 0, wxEXPAND); +} + +class SlicedInfo : public wxStaticBoxSizer +{ +public: + SlicedInfo(wxWindow *parent); + +private: + wxStaticText *info_filament_m; + wxStaticText *info_filament_mm3; + wxStaticText *info_filament_g; + wxStaticText *info_cost; + wxStaticText *info_time_normal; + wxStaticText *info_time_silent; +}; + +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, 5); + grid_sizer->SetFlexibleDirection(wxHORIZONTAL); + grid_sizer->AddGrowableCol(1, 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, "N/A"); + info_label->SetFont(wxGetApp().small_font()); + grid_sizer->Add(text, 0); + grid_sizer->Add(info_label, 0); + }; + + init_info_label(info_filament_m, _(L("Used Filament (m)"))); + init_info_label(info_filament_mm3, _(L("Used Filament (mm³)"))); + init_info_label(info_filament_g, _(L("Used Filament (g)"))); + init_info_label(info_cost, _(L("Cost"))); + init_info_label(info_time_normal, _(L("Estimated printing time (normal mode)"))); + init_info_label(info_time_silent, _(L("Estimated printing time (silent mode)"))); + + Add(grid_sizer, 0, wxEXPAND); +} + +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->sizer->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_material; + PresetComboBox *combo_printer; + + wxBoxSizer *sizer_params; + FreqChangedParams *frequently_changed_parameters; + ObjectList *object_list; + ObjectManipulation *object_manipulation; + 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) {} + + bool show_manifold_warning_icon = false; + bool show_print_info = false; + + + void show_preset_comboboxes(); +}; + +void Sidebar::priv::show_preset_comboboxes() +{ + const bool showSLA = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA; + + wxWindowUpdateLocker noUpdates(wxGetApp().mainframe); + + for (size_t i = 0; i < 4; ++i) + sizer_presets->Show(i, !showSLA); + + sizer_presets->Show(4, showSLA); + sizer_presets->Show(5, showSLA); + + frequently_changed_parameters->Show(!showSLA); + + wxGetApp().plater()->Layout(); + wxGetApp().mainframe->Layout(); +} + + +// Sidebar / public + +Sidebar::Sidebar(Plater *parent) + : wxPanel(parent), p(new priv(parent)) +{ + p->scrolled = new wxScrolledWindow(this); + + // The preset chooser + p->sizer_presets = new wxFlexGridSizer(4, 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_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); + + // Frequently Object Settings + p->object_manipulation = new ObjectManipulation(p->scrolled); + p->sizer_params->Add(p->object_manipulation->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 + auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL); + scrolled_sizer->SetMinSize(320, -1); + p->scrolled->SetSizer(scrolled_sizer); + p->scrolled->SetScrollbars(0, 1, 1, 1); + 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) +{ + switch (preset_type) { + case Preset::TYPE_FILAMENT: + if (p->combos_filament.size() == 1) { + // Single filament printer, synchronize the filament presets. + const std::string &name = wxGetApp().preset_bundle->filaments.get_selected_preset().name; + wxGetApp().preset_bundle->set_filament_preset(0, name); + } + + for (size_t i = 0; i < p->combos_filament.size(); i++) { + wxGetApp().preset_bundle->update_platter_filament_ui(i, p->combos_filament[i]); + } + + break; + + case Preset::TYPE_PRINT: + wxGetApp().preset_bundle->prints.update_platter_ui(p->combo_print); + break; + + case Preset::TYPE_SLA_MATERIAL: + wxGetApp().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. + wxGetApp().preset_bundle->prints.update_platter_ui(p->combo_print); + // Update the printer choosers, update the dirty flags. + wxGetApp().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. + for (size_t i = 0; i < p->combos_filament.size(); i++) { + wxGetApp().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; +} + +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_sizers(const bool show) +{ + p->object_info->Show(show); + p->object_info->manifold_warning_icon->Show(show && p->show_manifold_warning_icon); // where is g_show_manifold_warning_icon updating? #ys_FIXME + p->sliced_info->Show(show && p->show_print_info); // where is g_show_print_info updating? #ys_FIXME +} + +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 (wxGetApp().preset_bundle->printers.get_selected_preset().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() > 0; +} + + +std::vector& Sidebar::combos_filament() +{ + return p->combos_filament; +} + +#if !ENABLE_EXTENDED_SELECTION +// Plater::Object + +struct PlaterObject +{ + std::string name; + bool selected; + + PlaterObject(std::string name) : name(std::move(name)), selected(false) {} +}; +#endif // !ENABLE_EXTENDED_SELECTION + +// 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::Model model; + Slic3r::GCodePreviewData gcode_preview_data; +#if !ENABLE_EXTENDED_SELECTION + std::vector objects; +#endif // !ENABLE_EXTENDED_SELECTION + + // GUI elements + wxNotebook *notebook; + Sidebar *sidebar; + wxGLCanvas *canvas3D; // TODO: Use GLCanvas3D when we can + Preview *preview; + BackgroundSlicingProcess background_process; + 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); + +#if !ENABLE_EXTENDED_SELECTION + std::vector collect_selections(); +#endif // !ENABLE_EXTENDED_SELECTION + 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; + std::vector load_files(const std::vector &input_files); + std::vector load_model_objects(const ModelObjectPtrs &model_objects); + std::unique_ptr get_export_file(GUI::FileType file_type); + +#if ENABLE_EXTENDED_SELECTION + const GLCanvas3D::Selection& get_selection() const; + GLCanvas3D::Selection& get_selection(); + int get_selected_object_idx() const; +#else + void select_object(optional obj_idx); + void select_object_from_cpp(); + optional selected_object() const; +#endif // ENABLE_EXTENDED_SELECTION + void selection_changed(); + void object_list_changed(); +#if !ENABLE_EXTENDED_SELECTION + void select_view(); +#endif // !ENABLE_EXTENDED_SELECTION + + void remove(size_t obj_idx); + void reset(); +#if !ENABLE_EXTENDED_SELECTION + void rotate(); +#endif // !ENABLE_EXTENDED_SELECTION + void mirror(Axis axis); +#if !ENABLE_EXTENDED_SELECTION + void scale(); +#endif // !ENABLE_EXTENDED_SELECTION + void arrange(); + void split_object(); + void split_volume(); + void schedule_background_process(); + void async_apply_config(); + void start_background_process(); + void reload_from_disk(); + void export_object_stl(); + void fix_through_netfabb(); + + 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_cut(SimpleEvent&); +#if !ENABLE_EXTENDED_SELECTION + void on_action_settings(SimpleEvent&); +#endif // !ENABLE_EXTENDED_SELECTION + void on_action_layersediting(SimpleEvent&); +#if !ENABLE_EXTENDED_SELECTION + void on_action_selectbyparts(SimpleEvent&); +#endif // !ENABLE_EXTENDED_SELECTION + +#if ENABLE_EXTENDED_SELECTION + void on_object_select(SimpleEvent&); +#else + void on_object_select(ObjectSelectEvent&); +#endif // ENABLE_EXTENDED_SELECTION + void on_viewport_changed(SimpleEvent&); + void on_right_click(Vec2dEvent&); + void on_model_update(SimpleEvent&); +#if !ENABLE_EXTENDED_SELECTION + void on_scale_uniformly(SimpleEvent&); +#endif // !ENABLE_EXTENDED_SELECTION + void on_wipetower_moved(Vec3dEvent&); + void on_enable_action_buttons(Event&); + void on_update_geometry(Vec3dsEvent<2>&); + +private: + bool init_object_menu(); + +#if ENABLE_EXTENDED_SELECTION + 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 can_cut_object() const; + bool layers_height_allowed() const; + bool can_delete_all() const; + bool can_arrange() const; +#if ENABLE_MIRROR + bool can_mirror() const; +#endif // ENABLE_MIRROR +#endif // ENABLE_EXTENDED_SELECTION +}; + +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" + })), + notebook(new wxNotebook(q, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_BOTTOM)), + sidebar(new Sidebar(q)), + canvas3D(GLCanvas3DManager::create_wxglcanvas(notebook)) +{ + background_process.set_print(&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); + // Register progress callback from the Print class to the Platter. + print.set_status_callback([this](int percent, const std::string &message){ + wxCommandEvent event(EVT_PROGRESS_BAR); + event.SetInt(percent); + event.SetString(message); + wxQueueEvent(this->q, event.Clone()); + }); + this->q->Bind(EVT_PROGRESS_BAR, &priv::on_progress_event, this); + + _3DScene::add_canvas(canvas3D); + _3DScene::allow_multisample(canvas3D, GLCanvas3DManager::can_multisample()); + notebook->AddPage(canvas3D, _(L("3D"))); + preview = new GUI::Preview(notebook, config, &print, &gcode_preview_data); + + // XXX: If have OpenGL + _3DScene::enable_picking(canvas3D, true); + _3DScene::enable_moving(canvas3D, true); + // XXX: more config from 3D.pm +#if !ENABLE_EXTENDED_SELECTION + _3DScene::set_select_by(canvas3D, "object"); + _3DScene::set_drag_by(canvas3D, "instance"); +#endif // !ENABLE_EXTENDED_SELECTION + _3DScene::set_model(canvas3D, &model); + _3DScene::set_print(canvas3D, &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); + + // 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); +#if !ENABLE_EXTENDED_SELECTION + canvas3D->Bind(EVT_GLCANVAS_DOUBLE_CLICK, [](SimpleEvent&) {}); // XXX: remove? +#endif // !ENABLE_EXTENDED_SELECTION + 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(); }); +#if !ENABLE_EXTENDED_SELECTION + canvas3D->Bind(EVT_GLCANVAS_ROTATE_OBJECT, [this](Event &evt) { /*TODO: call rotate */ }); + canvas3D->Bind(EVT_GLCANVAS_SCALE_UNIFORMLY, [this](SimpleEvent&) { this->scale(); }); +#endif // !ENABLE_EXTENDED_SELECTION + 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_CUT, &priv::on_action_cut, this); +#if !ENABLE_EXTENDED_SELECTION + canvas3D->Bind(EVT_GLTOOLBAR_SETTINGS, &priv::on_action_settings, this); +#endif // !ENABLE_EXTENDED_SELECTION + canvas3D->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this); +#if !ENABLE_EXTENDED_SELECTION + canvas3D->Bind(EVT_GLTOOLBAR_SELECTBYPARTS, &priv::on_action_selectbyparts, this); +#endif // !ENABLE_EXTENDED_SELECTION + + // 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(); +} + +#if !ENABLE_EXTENDED_SELECTION +std::vector Plater::priv::collect_selections() +{ + std::vector res; + for (const auto &obj : objects) { + res.push_back(obj.selected); + } + return res; +} +#endif // !ENABLE_EXTENDED_SELECTION + +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 !ENABLE_EXTENDED_SELECTION + const auto selections = collect_selections(); + _3DScene::set_objects_selections(canvas3D, selections); +#endif // !ENABLE_EXTENDED_SELECTION + _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(); +} + +std::vector Plater::priv::load_files(const std::vector &input_files) +{ + 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(); + + auto *new_model = one_by_one ? nullptr : new Slic3r::Model(); + 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; + config.apply(FullPrintConfig::defaults()); + model = Slic3r::Model::read_from_archive(path.string(), &config, false); + Preset::normalize(config); + wxGetApp().preset_bundle->load_config_model(filename.string(), std::move(config)); + for (const auto &kv : main_frame->options_tabs()) { kv.second->load_current_preset(); } + wxGetApp().app_config->update_config_dir(path.parent_path().string()); + // forces the update of the config here, or it will invalidate the imported layer heights profile if done using the timer + // and if the config contains a "layer_height" different from the current defined one + // TODO: + // $self->async_apply_config; + } 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; + } + + // 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(); + } + } + + 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 (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()); + } + + 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"))); + return obj_idxs; +} + +std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects) +{ + const BoundingBoxf bed_shape = bed_shape_bb(); + const Vec3d bed_center = Slic3r::to_3d(bed_shape.center().cast(), 0.0); + 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; +#if ENABLE_EXTENDED_SELECTION + unsigned int obj_count = model.objects.size(); +#endif // ENABLE_EXTENDED_SELECTION + + 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; +#if ENABLE_EXTENDED_SELECTION + obj_idxs.push_back(obj_count++); +#else + objects.emplace_back(std::move(object_name)); + obj_idxs.push_back(objects.size() - 1); +#endif // ENABLE_EXTENDED_SELECTION + + 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 + auto *instance = object->add_instance(); + instance->set_offset(bed_center); + } + + 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); + } + } + + // print.auto_assign_extruders(object); + // print.add_model_object(object); + } + + // if user turned autocentering off, automatic arranging would disappoint them + if (get_config("autocenter") != "1") { + need_arrange = false; + } + + 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); + } + + if (need_arrange) { + // arrange(); // TODO + } + + 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, + _(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; +} + +#if ENABLE_EXTENDED_SELECTION +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; +} +#else +void Plater::priv::select_object(optional obj_idx) +{ + for (auto &obj : objects) { + obj.selected = false; + } + + if (obj_idx) { + objects[*obj_idx].selected = true; + } + + selection_changed(); +} + +void Plater::priv::select_object_from_cpp() +{ + // TODO +} + +optional Plater::priv::selected_object() const +{ + for (size_t i = 0; i < objects.size(); i++) { + if (objects[i].selected) { return i; } + } + + return boost::none; +} +#endif // ENABLE_EXTENDED_SELECTION + +void Plater::priv::selection_changed() +{ +#if !ENABLE_EXTENDED_SELECTION + const auto obj_idx = selected_object(); + const bool have_sel = !!obj_idx; +#endif // !ENABLE_EXTENDED_SELECTION + +#if ENABLE_EXTENDED_SELECTION + _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, "cut", can_cut_object()); + _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); +#else + _3DScene::enable_toolbar_item(canvas3D, "fewer", have_sel); + _3DScene::enable_toolbar_item(canvas3D, "splitobjects", have_sel); + _3DScene::enable_toolbar_item(canvas3D, "splitvolumes", have_sel); + _3DScene::enable_toolbar_item(canvas3D, "cut", have_sel); + _3DScene::enable_toolbar_item(canvas3D, "settings", have_sel); + _3DScene::enable_toolbar_item(canvas3D, "layersediting", have_sel && config->opt_bool("variable_layer_height") && _3DScene::is_layers_editing_allowed(canvas3D)); +#endif // ENABLE_EXTENDED_SELECTION + +#if ENABLE_EXTENDED_SELECTION + int obj_idx = get_selected_object_idx(); + bool have_sel = (obj_idx != -1); +#else + bool can_select_by_parts = false; + + if (have_sel) { + const auto *model_object = model.objects[*obj_idx]; + // XXX: ? + can_select_by_parts = *obj_idx < 1000 && model_object->volumes.size() > 1; + _3DScene::enable_toolbar_item(canvas3D, "fewer", model_object->instances.size() > 1); + } + + if (can_select_by_parts) { + // first disable to let the item in the toolbar to switch to the unpressed state // XXX: ? + _3DScene::enable_toolbar_item(canvas3D, "selectbyparts", false); + _3DScene::enable_toolbar_item(canvas3D, "selectbyparts", true); + } else { + _3DScene::enable_toolbar_item(canvas3D, "selectbyparts", false); + _3DScene::set_select_by(canvas3D, "object"); + } +#endif // ENABLE_EXTENDED_SELECTION + + wxWindowUpdateLocker freeze_guard(sidebar); + if (have_sel) { +#if ENABLE_EXTENDED_SELECTION + const ModelObject* model_object = model.objects[obj_idx]; +#else + const auto *model_object = model.objects[*obj_idx]; +#endif // ENABLE_EXTENDED_SELECTION + // FIXME print_info runs model fixing in two rounds, it is very slow, it should not be performed here! + // # $model_object->print_info; + + const ModelInstance* model_instance = !model_object->instances.empty() ? model_object->instances.front() : nullptr; + // TODO + // $self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size})); + // $self->{object_info_materials}->SetLabel($model_object->materials_count); + + // if (my $stats = $model_object->mesh_stats) { + // $self->{object_info_volume}->SetLabel(sprintf('%.2f', $stats->{volume} * ($model_instance->scaling_factor**3))); + // $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"}->(1); + + // # we don't show normals_fixed because we never provide normals + // # to admesh, so it generates normals for all facets + // my $message = sprintf L('%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d facets reversed, %d backwards edges'), + // @$stats{qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges)}; + // $self->{object_info_manifold}->SetToolTipString($message); + // $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_show"}->(0); + // $self->{object_info_manifold}->SetToolTipString(""); + // $self->{object_info_manifold_warning_icon}->SetToolTipString(""); + // } + // } else { + // $self->{object_info_facets}->SetLabel($object->facets); + // } + } else { + // $self->{"object_info_$_"}->SetLabel("") for qw(size volume facets materials manifold); + // $self->{"object_info_manifold_warning_icon_show"}->(0); + // $self->{object_info_manifold}->SetToolTipString(""); + // $self->{object_info_manifold_warning_icon}->SetToolTipString(""); + } + + q->Layout(); +} + +void Plater::priv::object_list_changed() +{ + // Enable/disable buttons depending on whether there are any objects on the platter. +#if ENABLE_EXTENDED_SELECTION + _3DScene::enable_toolbar_item(canvas3D, "deleteall", can_delete_all()); + _3DScene::enable_toolbar_item(canvas3D, "arrange", can_arrange()); +#else + const bool have_objects = !objects.empty(); + _3DScene::enable_toolbar_item(canvas3D, "deleteall", have_objects); + _3DScene::enable_toolbar_item(canvas3D, "arrange", have_objects); +#endif // ENABLE_EXTENDED_SELECTION + + 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; + +#if ENABLE_EXTENDED_SELECTION + sidebar->enable_buttons(!model.objects.empty() && !export_in_progress && model_fits); +#else + sidebar->enable_buttons(have_objects && !export_in_progress && model_fits); +#endif // ENABLE_EXTENDED_SELECTION +} + +#if !ENABLE_EXTENDED_SELECTION +void Plater::priv::select_view() +{ + // TODO +} +#endif // !ENABLE_EXTENDED_SELECTION + +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); + +#if !ENABLE_EXTENDED_SELECTION + objects.erase(objects.begin() + obj_idx); +#endif // !ENABLE_EXTENDED_SELECTION + 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(); + +#if !ENABLE_EXTENDED_SELECTION + select_object(boost::none); +#endif // !ENABLE_EXTENDED_SELECTION + update(); +} + +void Plater::priv::reset() +{ + // 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); + +#if !ENABLE_EXTENDED_SELECTION + objects.clear(); +#endif // !ENABLE_EXTENDED_SELECTION + model.clear_objects(); +// print.clear_objects(); + + // Delete all objects from list on c++ side + sidebar->obj_list()->delete_all_objects_from_list(); + object_list_changed(); + +#if !ENABLE_EXTENDED_SELECTION + select_object(boost::none); +#endif // !ENABLE_EXTENDED_SELECTION + update(); +} + +#if !ENABLE_EXTENDED_SELECTION +void Plater::priv::rotate() +{ + // TODO +} +#endif // !ENABLE_EXTENDED_SELECTION + +void Plater::priv::mirror(Axis axis) +{ +#if ENABLE_MIRROR +#if ENABLE_EXTENDED_SELECTION + _3DScene::mirror_selection(canvas3D, axis); +#endif // ENABLE_EXTENDED_SELECTION +#else +#if ENABLE_EXTENDED_SELECTION + int obj_idx = get_selected_object_idx(); + if (obj_idx == -1) + return; + + ModelObject* model_object = model.objects[obj_idx]; + ModelInstance* model_instance = model_object->instances.front(); +#else + const auto obj_idx = selected_object(); + if (! obj_idx) { return; } + + auto *model_object = model.objects[*obj_idx]; + auto *model_instance = model_object->instances[0]; +#endif // ENABLE_EXTENDED_SELECTION + + // XXX: ? + // # apply Z rotation before mirroring + // if ($model_instance->rotation != 0) { + // $model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, 1)); + // $_->set_rotation(0) for @{ $model_object->instances }; + // } + + model_object->mirror(axis); + + selection_changed(); + update(); +#endif // ENABLE_MIRROR +} + +#if !ENABLE_EXTENDED_SELECTION +void Plater::priv::scale() +{ + // TODO +} +#endif // !ENABLE_EXTENDED_SELECTION + +void Plater::priv::arrange() +{ + main_frame->app_controller()->arrange_model(); + + // 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 + + update(); +} + +void Plater::priv::split_object() +{ +#if ENABLE_EXTENDED_SELECTION + 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++); + m->center_around_origin(); + } + + 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 + load_model_objects(new_objects); + } +#endif // ENABLE_EXTENDED_SELECTION +} + +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); +} + +void Plater::priv::async_apply_config() +{ + 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_info_sizers(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(); + if (this->preview != nullptr) + 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")) { +#if !ENABLE_EXTENDED_SELECTION + std::vector selections = this->collect_selections(); + Slic3r::_3DScene::set_objects_selections(this->canvas3D, selections); + Slic3r::_3DScene::reload_scene(this->canvas3D, 1); +#endif /* !ENABLE_EXTENDED_SELECTION */ + } + } + if (invalidated != Print::APPLY_STATUS_UNCHANGED && this->get_config("background_processing") == "1" && + this->print.num_object_instances() > 0 && this->background_process.start()) + this->statusbar()->set_cancel_callback([this](){ + this->statusbar()->set_status_text(L("Cancelling")); + this->background_process.stop(); + }); +} + +void Plater::priv::start_background_process() +{ + if (this->background_process.running()) + return; + // return if ! @{$self->{objects}} || $self->{background_slicing_process}->running; + // Don't start process thread if Print is not valid. + std::string err = this->q->print().validate(); + if (! err.empty()) { + this->statusbar()->set_status_text(err); + } else { + // Copy the names of active presets into the placeholder parser. + wxGetApp().preset_bundle->export_selections(this->q->print().placeholder_parser()); + // Start the background process. + this->background_process.start(); + } +} + +void Plater::priv::reload_from_disk() +{ + // TODO +} + +void Plater::priv::export_object_stl() +{ + // TODO +} + +void Plater::priv::fix_through_netfabb() +{ +/* + my ($self) = @_; + my ($obj_idx, $object) = $self->selected_object; + return if !defined $obj_idx; + my $model_object = $self->{model}->objects->[$obj_idx]; + my $model_fixed = Slic3r::Model->new; + Slic3r::GUI::fix_model_by_win10_sdk_gui($model_object, $self->{print}, $model_fixed); + + my @new_obj_idx = $self->load_model_objects(@{$model_fixed->objects}); + return if !@new_obj_idx; + + foreach my $new_obj_idx (@new_obj_idx) { + my $o = $self->{model}->objects->[$new_obj_idx]; + $o->clear_instances; + $o->add_instance($_) for @{$model_object->instances}; + #$o->invalidate_bounding_box; + + if ($o->volumes_count == $model_object->volumes_count) { + for my $i (0..($o->volumes_count-1)) { + $o->get_volume($i)->config->apply($model_object->get_volume($i)->config); + } + } + #FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid, + } + + $self->remove($obj_idx); +*/ +} + +void Plater::priv::on_notebook_changed(wxBookCtrlEvent&) +{ + const auto current_id = notebook->GetCurrentPage()->GetId(); + if (current_id == canvas3D->GetId()) { + if (_3DScene::is_reload_delayed(canvas3D)) { +#if !ENABLE_EXTENDED_SELECTION + _3DScene::set_objects_selections(canvas3D, collect_selections()); +#endif // !ENABLE_EXTENDED_SELECTION + _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; + } + } + } + + // Synchronize config.ini with the current selections. + wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); + // 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: +#if !ENABLE_EXTENDED_SELECTION + auto selections = collect_selections(); + _3DScene::set_objects_selections(canvas3D, selections); + if (canvas3D) + _3DScene::reload_scene(canvas3D, true); +#endif // !ENABLE_EXTENDED_SELECTION +} + +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_info_sizers(success); + + // this updates buttons status + //$self->object_list_changed; + + // refresh preview + if (this->preview != nullptr) + this->preview->reload_print(); +} + +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) + q->add(); +} + +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_cut(SimpleEvent&) +{ + // TODO +} + +#if !ENABLE_EXTENDED_SELECTION +void Plater::priv::on_action_settings(SimpleEvent&) +{ + // TODO +} +#endif // !ENABLE_EXTENDED_SELECTION + +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); +} + +#if !ENABLE_EXTENDED_SELECTION +void Plater::priv::on_action_selectbyparts(SimpleEvent&) +{ + // TODO +} +#endif // !ENABLE_EXTENDED_SELECTION + +#if ENABLE_EXTENDED_SELECTION +void Plater::priv::on_object_select(SimpleEvent& evt) +{ + selection_changed(); + wxGetApp().obj_list()->update_selections(); +} +#else +void Plater::priv::on_object_select(ObjectSelectEvent &evt) +{ + const auto obj_idx = evt.object_id(); + const auto vol_idx = evt.volume_id(); + + if (obj_idx >= 0 && obj_idx < 1000 && vol_idx == -1) { + // Ignore the special objects (the wipe tower proxy and such). + select_object(obj_idx); +} +} +#endif // ENABLE_EXTENDED_SELECTION + +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) +{ +#if ENABLE_EXTENDED_SELECTION + 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()); +#else + // TODO +#endif // ENABLE_EXTENDED_SELECTION +} + +void Plater::priv::on_model_update(SimpleEvent&) +{ + // TODO +} + +#if !ENABLE_EXTENDED_SELECTION +void Plater::priv::on_scale_uniformly(SimpleEvent&) +{ +// my ($scale) = @_; + +// my ($obj_idx, $object) = $self->selected_object; + const auto obj_idx = selected_object(); + if (! obj_idx) { return; } +// return if !defined $obj_idx; + +// my $model_object = $self->{model}->objects->[$obj_idx]; +// my $model_instance = $model_object->instances->[0]; + +// my $variation = $scale / $model_instance->scaling_factor; +// #FIXME Scale the layer height profile? +// foreach my $range (@{ $model_object->layer_height_ranges }) { +// $range->[0] *= $variation; +// $range->[1] *= $variation; +// } +// $_->set_scaling_factor($scale) for @{ $model_object->instances }; + +// # 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 +// $self->{print}->add_model_object($model_object, $obj_idx); + +// $self->selection_changed(1); # refresh info (size, volume etc.) +// $self->update; + + this->schedule_background_process(); +} +#endif // !ENABLE_EXTENDED_SELECTION + +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); + main_frame->get_preset_tab("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(); + +#if ENABLE_MIRROR + 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"))); +#endif // ENABLE_MIRROR + + 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.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.png", &object_menu); + + wxMenuItem* item_split = append_submenu(&object_menu, split_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object")), "shape_ungroup.png"); + +#if ENABLE_EXTENDED_SELECTION + // ui updates needs to be binded to the parent panel + if (q != nullptr) + { +#if ENABLE_MIRROR + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_mirror()); }, item_mirror->GetId()); +#endif // ENABLE_MIRROR + 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()); + } +#endif // ENABLE_EXTENDED_SELECTION + + return true; +} + +#if ENABLE_EXTENDED_SELECTION +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::can_cut_object() const +{ + int obj_idx = get_selected_object_idx(); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()); +} + +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(); +} + +#if ENABLE_MIRROR +bool Plater::priv::can_mirror() const +{ + return get_selection().is_from_single_instance(); +} +#endif // ENABLE_MIRROR +#endif // ENABLE_EXTENDED_SELECTION + +// 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; } + +void Plater::add() +{ + wxArrayString input_files; + wxGetApp().open_model(this, input_files); + + std::vector input_paths; + for (const auto &file : input_files) { + input_paths.push_back(file.wx_str()); + } + load_files(input_paths); +} + +void Plater::load_files(const std::vector &input_files) { p->load_files(input_files); } + +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::remove_selected() +{ +#if ENABLE_EXTENDED_SELECTION + int obj_idx = p->get_selected_object_idx(); + if (obj_idx != -1) + remove((size_t)obj_idx); +#else + const auto selected = p->selected_object(); + if (selected) { + remove(*selected); + } +#endif // ENABLE_EXTENDED_SELECTION +} + +void Plater::increase_instances(size_t num) +{ +#if ENABLE_EXTENDED_SELECTION + 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(); +#else + const auto obj_idx = p->selected_object(); + if (! obj_idx) { return; } + + auto *model_object = p->model.objects[*obj_idx]; + auto *model_instance = model_object->instances[model_object->instances.size() - 1]; +#endif // ENABLE_EXTENDED_SELECTION + + 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()); +#if ENABLE_EXTENDED_SELECTION +// p->print.get_object(obj_idx)->add_copy(Slic3r::to_2d(offset_vec)); +#else +// p->print.get_object(*obj_idx)->add_copy(Slic3r::to_2d(offset_vec)); +#endif // ENABLE_EXTENDED_SELECTION + } + +#if ENABLE_EXTENDED_SELECTION + sidebar().obj_list()->increase_object_instances(obj_idx, num); +#else + sidebar().obj_list()->increase_object_instances(*obj_idx, num); +#endif // ENABLE_EXTENDED_SELECTION + + if (p->get_config("autocenter") == "1") { + p->arrange(); + } else { + p->update(); + } + +#if ENABLE_EXTENDED_SELECTION + p->get_selection().add_instance(obj_idx, (int)model_object->instances.size() - 1); +#endif // ENABLE_EXTENDED_SELECTION + + p->selection_changed(); + + this->p->schedule_background_process(); +} + +void Plater::decrease_instances(size_t num) +{ +#if ENABLE_EXTENDED_SELECTION + int obj_idx = p->get_selected_object_idx(); + if (obj_idx == -1) + return; + + ModelObject* model_object = p->model.objects[obj_idx]; +#else + const auto obj_idx = p->selected_object(); + if (! obj_idx) { return; } + + auto *model_object = p->model.objects[*obj_idx]; +#endif // ENABLE_EXTENDED_SELECTION + if (model_object->instances.size() > num) { + for (size_t i = 0; i < num; i++) { + model_object->delete_last_instance(); +#if ENABLE_EXTENDED_SELECTION +// p->print.get_object(obj_idx)->delete_last_copy(); +#else +// p->print.get_object(*obj_idx)->delete_last_copy(); +#endif // ENABLE_EXTENDED_SELECTION + } +#if ENABLE_EXTENDED_SELECTION + sidebar().obj_list()->decrease_object_instances(obj_idx, num); +#else + sidebar().obj_list()->decrease_object_instances(*obj_idx, num); +#endif // ENABLE_EXTENDED_SELECTION + } + else { +#if ENABLE_EXTENDED_SELECTION + remove(obj_idx); +#else + remove(*obj_idx); +#endif // ENABLE_EXTENDED_SELECTION + } + + p->update(); + +#if ENABLE_EXTENDED_SELECTION + if (!model_object->instances.empty()) + p->get_selection().add_instance(obj_idx, (int)model_object->instances.size() - 1); +#endif // ENABLE_EXTENDED_SELECTION + + p->selection_changed(); + this->p->schedule_background_process(); +} + +void Plater::set_number_of_copies(/*size_t num*/) +{ +#if ENABLE_EXTENDED_SELECTION + int obj_idx = p->get_selected_object_idx(); + if (obj_idx == -1) + return; + + ModelObject* model_object = p->model.objects[obj_idx]; +#else + const auto obj_idx = p->selected_object(); + if (! obj_idx) { return; } + + auto *model_object = p->model.objects[*obj_idx]; +#endif // ENABLE_EXTENDED_SELECTION + + 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::export_gcode(fs::path output_path) +{ +#if ENABLE_EXTENDED_SELECTION + if (p->model.objects.empty()) + return; +#else + if (p->objects.empty()) + return; +#endif // ENABLE_EXTENDED_SELECTION + + 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->print.validate(); + if (! err.empty()) { + // The config is not valid + GUI::show_error(this, _(err)); + return; + } + + // 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; + } + } + + if (! output_path.empty()) { + this->p->background_process.schedule_export(p->print.output_filepath(output_path.string())); + this->p->background_process.start(); + } +} + +void Plater::export_stl() +{ +#if ENABLE_EXTENDED_SELECTION + if (p->model.objects.empty()) { return; } +#else + if (p->objects.empty()) { return; } +#endif // ENABLE_EXTENDED_SELECTION + + 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 ENABLE_EXTENDED_SELECTION + if (p->model.objects.empty()) { return; } +#else + if (p->objects.empty()) { return; } +#endif // ENABLE_EXTENDED_SELECTION + + auto dialog = p->get_export_file(FT_AMF); + if (! dialog) { return; } + + wxString path = dialog->GetPath(); + auto path_cstr = path.c_str(); + + if (Slic3r::store_amf(path_cstr, &p->model, &p->print, dialog->get_checkbox_value())) { + // 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)); + } +} + +void Plater::export_3mf() +{ +#if ENABLE_EXTENDED_SELECTION + if (p->model.objects.empty()) { return; } +#else + if (p->objects.empty()) { return; } +#endif // ENABLE_EXTENDED_SELECTION + + auto dialog = p->get_export_file(FT_3MF); + if (! dialog) { return; } + + wxString path = dialog->GetPath(); + auto path_cstr = path.c_str(); + + if (Slic3r::store_3mf(path_cstr, &p->model, &p->print, dialog->get_checkbox_value())) { + // 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() +{ + // explicitly cancel a previous thread and start a new one. + // Don't reslice if export of G-code or sending to OctoPrint is running. +// if (! defined($self->{export_gcode_output_file}) && ! defined($self->{send_gcode_file})) { + // Stop the background processing threads, stop the async update timer. +// this->p->stop_background_process(); + // Rather perform one additional unnecessary update of the print object instead of skipping a pending async update. + this->p->async_apply_config(); + this->p->statusbar()->set_cancel_callback([this](){ + this->p->statusbar()->set_status_text(L("Cancelling")); + this->p->background_process.stop(); + }); + this->p->start_background_process(); +} + +void Plater::send_gcode() +{ +// p->send_gcode_file = export_gcode(); +} + +void Plater::on_extruders_change(int num_extruders) +{ + auto& choices = sidebar().combos_filament(); + + 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(); + GetParent()->Layout(); +} + +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 == "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(); +} + +wxGLCanvas* Plater::canvas3D() +{ + return p->canvas3D; +} + +void Plater::changed_object_settings(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]; + model_object->center_around_origin(); + } + + // update print + if (list->is_parts_changed() || list->is_part_settings_changed()) { + this->p->schedule_background_process(); +#if !ENABLE_EXTENDED_SELECTION + if (p->canvas3D) _3DScene::reload_scene(p->canvas3D, true); + auto selections = p->collect_selections(); + _3DScene::set_objects_selections(p->canvas3D, selections); +#endif // !ENABLE_EXTENDED_SELECTION + _3DScene::reload_scene(p->canvas3D, false); +#if !ENABLE_MODIFIED_CAMERA_TARGET + _3DScene::zoom_to_volumes(p->canvas3D); +#endif // !ENABLE_MODIFIED_CAMERA_TARGET + } + else { + this->p->schedule_background_process(); + } + +} + + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp new file mode 100644 index 0000000000..4c6d0ff302 --- /dev/null +++ b/src/slic3r/GUI/Plater.hpp @@ -0,0 +1,140 @@ +#ifndef slic3r_Plater_hpp_ +#define slic3r_Plater_hpp_ + +#include +#include +#include + +#include +#include + +#include "Preset.hpp" + +class wxButton; +class wxBoxSizer; +class wxGLCanvas; + +namespace Slic3r { + +class Model; +class Print; + +namespace GUI { + +class MainFrame; +class ConfigOptionsGroup; +class ObjectManipulation; +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 +{ +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(); + + ConfigOptionsGroup* og_freq_chng_params(); + wxButton* get_wiping_dialog_button(); + void update_objects_list_extruder_column(int extruders_count); + void show_info_sizers(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(); + + 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(); + + void add(); + + void load_files(const std::vector &input_files); + + void update(bool force_autocenter = false); + void select_view(const std::string& direction); + + void remove(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*/); + + // Note: empty path means "use the default" + void export_gcode(boost::filesystem::path output_path = boost::filesystem::path()); + void export_stl(); + void export_amf(); + void export_3mf(); + void reslice(); + void changed_object_settings(int obj_idx); + void send_gcode(); + + void on_extruders_change(int extruders_count); + void on_config_change(const DynamicPrintConfig &config); + + wxGLCanvas* canvas3D(); +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 95% rename from xs/src/slic3r/GUI/Preferences.cpp rename to src/slic3r/GUI/Preferences.cpp index 89a8ead925..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() { @@ -124,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 83% rename from xs/src/slic3r/GUI/Preferences.hpp rename to src/slic3r/GUI/Preferences.hpp index d01d78b70b..0b1dd57308 100644 --- a/xs/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -15,9 +15,8 @@ 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(wxWindow* parent); ~PreferencesDialog(){ } void build(); diff --git a/xs/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp similarity index 98% rename from xs/src/slic3r/GUI/Preset.cpp rename to src/slic3r/GUI/Preset.cpp index 9911caa5b8..07410e325f 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; @@ -734,7 +735,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; @@ -751,7 +752,7 @@ 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); + 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)) @@ -791,11 +792,11 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()); } if (i + 1 == m_num_default_presets) - 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) @@ -883,7 +884,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) diff --git a/xs/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp similarity index 99% rename from xs/src/slic3r/GUI/Preset.hpp rename to src/slic3r/GUI/Preset.hpp index 821d7dc543..2d56e96c2d 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 @@ -177,11 +178,11 @@ public: static const std::vector& sla_material_options(); static void update_suffix_modified(); + static void normalize(DynamicPrintConfig &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(); @@ -355,7 +356,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. diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp similarity index 99% rename from xs/src/slic3r/GUI/PresetBundle.cpp rename to src/slic3r/GUI/PresetBundle.cpp index cd3924dd0a..f0f53ff6cc 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 @@ -77,7 +78,8 @@ PresetBundle::PresetBundle() : 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) { - Preset &preset = this->printers.preset(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 = (i == 0) ? this->printers.default_preset() : this->printers.preset(1); preset.config.optptr("printer_settings_id", true); preset.config.optptr("printer_vendor", true); preset.config.optptr("printer_model", true); @@ -1297,7 +1299,7 @@ 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 || this->printers.get_edited_preset().printer_technology() == ptSLA) return; @@ -1320,7 +1322,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; @@ -1373,12 +1375,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) diff --git a/xs/src/slic3r/GUI/PresetBundle.hpp b/src/slic3r/GUI/PresetBundle.hpp similarity index 95% rename from xs/src/slic3r/GUI/PresetBundle.hpp rename to src/slic3r/GUI/PresetBundle.hpp index 68ec534dae..e116b0ff90 100644 --- a/xs/src/slic3r/GUI/PresetBundle.hpp +++ b/src/slic3r/GUI/PresetBundle.hpp @@ -73,6 +73,11 @@ 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. @@ -105,7 +110,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); 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/GUI/ProgressIndicator.hpp b/src/slic3r/GUI/ProgressIndicator.hpp similarity index 100% rename from xs/src/slic3r/GUI/ProgressIndicator.hpp rename to src/slic3r/GUI/ProgressIndicator.hpp diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.cpp b/src/slic3r/GUI/ProgressStatusBar.cpp similarity index 87% rename from xs/src/slic3r/GUI/ProgressStatusBar.cpp rename to src/slic3r/GUI/ProgressStatusBar.cpp index 0ca86084b0..44a7b06c51 100644 --- a/xs/src/slic3r/GUI/ProgressStatusBar.cpp +++ b/src/slic3r/GUI/ProgressStatusBar.cpp @@ -5,14 +5,14 @@ #include #include #include -#include "GUI.hpp" +#include "GUI_App.hpp" #include namespace Slic3r { ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id): - self(new wxStatusBar(parent ? parent : GUI::get_main_frame(), + self(new wxStatusBar(parent ? parent : GUI::wxGetApp().mainframe, id == -1? wxID_ANY : id)), m_timer(new wxTimer(self)), m_prog (new wxGauge(self, @@ -53,8 +53,8 @@ ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id): }); m_cancelbutton->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) { - if(m_cancel_cb) m_cancel_cb(); - m_perl_cancel_callback.call(); + if (m_cancel_cb) + m_cancel_cb(); m_cancelbutton->Hide(); }); } @@ -130,13 +130,23 @@ void ProgressStatusBar::run(int rate) void ProgressStatusBar::embed(wxFrame *frame) { - wxFrame* mf = frame? frame : GUI::get_main_frame(); + wxFrame* mf = frame ? frame : GUI::wxGetApp().mainframe; mf->SetStatusBar(self); } void ProgressStatusBar::set_status_text(const wxString& txt) { - self->SetStatusText(wxString::FromUTF8(txt.c_str())); + 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() diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.hpp b/src/slic3r/GUI/ProgressStatusBar.hpp similarity index 59% rename from xs/src/slic3r/GUI/ProgressStatusBar.hpp rename to src/slic3r/GUI/ProgressStatusBar.hpp index 0d8560d3bb..f33a70ed30 100644 --- a/xs/src/slic3r/GUI/ProgressStatusBar.hpp +++ b/src/slic3r/GUI/ProgressStatusBar.hpp @@ -4,8 +4,6 @@ #include #include -#include "../../callback.hpp" - class wxTimer; class wxGauge; class wxButton; @@ -22,7 +20,8 @@ namespace Slic3r { * 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 { +class ProgressStatusBar +{ wxStatusBar *self; // we cheat! It should be the base class but: perl! wxTimer *m_timer; wxGauge *m_prog; @@ -35,25 +34,26 @@ public: 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(); + 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 remove_cancel_callback() { set_cancel_callback(); } - void run(int rate); - void embed(wxFrame *frame = nullptr); - void set_status_text(const wxString& txt); + 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(); + void show_cancel_button(); + void hide_cancel_button(); - PerlCallback m_perl_cancel_callback; private: bool m_busy = false; CancelFn m_cancel_cb; diff --git a/xs/src/slic3r/GUI/RammingChart.cpp b/src/slic3r/GUI/RammingChart.cpp similarity index 100% rename from xs/src/slic3r/GUI/RammingChart.cpp rename to src/slic3r/GUI/RammingChart.cpp 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 91% rename from xs/src/slic3r/GUI/Tab.cpp rename to src/slic3r/GUI/Tab.cpp index e0db63803d..bba5161cc3 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -28,16 +28,29 @@ #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 == "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); @@ -121,18 +134,18 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) auto dlg = new ButtonsDescription(this, &m_icon_descriptions); 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); @@ -203,7 +215,9 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) // 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() @@ -222,9 +236,9 @@ Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::str 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; } } @@ -464,12 +478,14 @@ 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){ 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; @@ -513,8 +529,8 @@ void Tab::update_changed_tree_ui() } 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(); } @@ -586,14 +602,11 @@ 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. @@ -621,6 +634,25 @@ void Tab::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; @@ -661,34 +693,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); - } - - if (opt_key == "printer_technology") - { - int val = boost::any_cast(value); - event.SetInt(val); - g_wxMainFrame->ProcessWindowEvent(event); - return; - } - - 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(ogFrequentlyChangingParameters)->get_config_value(*m_config, opt_key); - get_optgroup(ogFrequentlyChangingParameters)->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") { @@ -697,12 +720,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(ogFrequentlyChangingParameters)->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(ogFrequentlyChangingParameters)->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" ) @@ -713,15 +736,17 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) // Show/hide the 'purging volumes' button void Tab::update_wiping_button_visibility() { - if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA) + 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; - get_wiping_dialog_button()->Show(wipe_tower_enabled && multiple_extruders && single_extruder_mm); - - (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(); + } } @@ -732,11 +757,9 @@ 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); - } + wxCommandEvent event(EVT_TAB_PRESETS_CHANGED); + event.SetEventObject(this); + wxPostEvent(this, event); update_preset_description_line(); } @@ -787,18 +810,20 @@ void Tab::update_preset_description_line() void Tab::update_frequently_changed_parameters() { - boost::any value = get_optgroup(ogFrequentlyChangingParameters)->get_config_value(*m_config, "fill_density"); - get_optgroup(ogFrequentlyChangingParameters)->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(ogFrequentlyChangingParameters)->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(ogFrequentlyChangingParameters)->set_value("brim", val); + og_freq_chng_params->set_value("brim", val); update_wiping_button_visibility(); } @@ -1015,7 +1040,7 @@ void TabPrint::build() page = add_options_page(_(L("Dependencies")), "wrench.png"); optgroup = page->new_optgroup(_(L("Profile dependencies"))); - line = { _(L("Compatible printers")), "" }; + 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); }; @@ -1041,7 +1066,7 @@ void TabPrint::reload_config(){ void TabPrint::update() { - if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA) + if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) return; // ys_FIXME Freeze(); @@ -1341,7 +1366,7 @@ 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 = 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); @@ -1381,7 +1406,7 @@ void TabFilament::build() page = add_options_page(_(L("Dependencies")), "wrench.png"); optgroup = page->new_optgroup(_(L("Profile dependencies"))); - line = { _(L("Compatible printers")), "" }; + 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); }; @@ -1407,7 +1432,7 @@ void TabFilament::reload_config(){ void TabFilament::update() { - if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA) + if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) return; // ys_FIXME Freeze(); @@ -1458,8 +1483,6 @@ void TabPrinter::build() m_printer_technology = m_presets->get_selected_preset().printer_technology(); m_presets->get_selected_preset().printer_technology() == ptSLA ? build_sla() : build_fff(); - -// on_value_change("printer_technology", m_printer_technology); // to update show/hide preset ComboBoxes } void TabPrinter::build_fff() @@ -1478,10 +1501,10 @@ void TabPrinter::build_fff() 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 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); @@ -1510,6 +1533,7 @@ void TabPrinter::build_fff() 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"); @@ -1763,7 +1787,7 @@ void TabPrinter::build_sla() 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 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); @@ -1839,7 +1863,7 @@ void TabPrinter::extruders_count_changed(size_t extruders_count){ build_extruder_pages(); reload_config(); on_value_change("extruders_count", extruders_count); - update_objects_list_extruder_column(extruders_count); + wxGetApp().sidebar().update_objects_list_extruder_column(extruders_count); } void TabPrinter::append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key) @@ -2039,8 +2063,6 @@ void TabPrinter::update_pages() m_pages_sla.empty() ? build_sla() : m_pages.swap(m_pages_sla); rebuild_page_tree(true); - - on_value_change("printer_technology", m_presets->get_edited_preset().printer_technology()); // to update show/hide preset ComboBoxes } void TabPrinter::update() @@ -2180,7 +2202,7 @@ 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(); @@ -2189,15 +2211,15 @@ void Tab::load_current_preset() PrinterTechnology& printer_technology = m_presets->get_edited_preset().printer_technology(); if (printer_technology != static_cast(this)->m_printer_technology) { - for (auto& tab : get_preset_tabs()){ + for (auto& tab : wxGetApp().mainframe->get_preset_tabs()){ if (tab.technology != printer_technology) { - int page_id = get_tab_panel()->FindPage(tab.panel); - get_tab_panel()->GetPage(page_id)->Show(false); - get_tab_panel()->RemovePage(page_id); + int page_id = wxGetApp().tab_panel()->FindPage(tab.panel); + wxGetApp().tab_panel()->GetPage(page_id)->Show(false); + wxGetApp().tab_panel()->RemovePage(page_id); } else - get_tab_panel()->InsertPage(get_tab_panel()->FindPage(this), tab.panel, tab.panel->title()); + wxGetApp().tab_panel()->InsertPage(wxGetApp().tab_panel()->FindPage(this), tab.panel, tab.panel->title()); } static_cast(this)->m_printer_technology = printer_technology; @@ -2225,8 +2247,9 @@ 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); @@ -2250,6 +2273,33 @@ void Tab::rebuild_page_tree(bool tree_sel_change_event /*= false*/) 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. @@ -2374,9 +2424,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(); @@ -2517,7 +2571,7 @@ 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; 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. @@ -2600,173 +2654,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;" @@ -2842,6 +2729,15 @@ 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; @@ -2865,8 +2761,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; @@ -3005,7 +2915,7 @@ void TabSLAMaterial::build() page = add_options_page(_(L("Dependencies")), "wrench.png"); optgroup = page->new_optgroup(_(L("Profile dependencies"))); - auto line = Line { _(L("Compatible printers")), "" }; + 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); }; @@ -3025,7 +2935,7 @@ void TabSLAMaterial::build() void TabSLAMaterial::update() { - if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptFFF) + if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF) return; // ys_FIXME } diff --git a/xs/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp similarity index 92% rename from xs/src/slic3r/GUI/Tab.hpp rename to src/slic3r/GUI/Tab.hpp index e4e37d4eb8..1961142563 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,7 +56,7 @@ 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(){} @@ -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. @@ -178,16 +181,14 @@ protected: 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; @@ -201,28 +202,26 @@ public: 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); + 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; } - // 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(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(); @@ -254,6 +253,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); @@ -271,7 +271,6 @@ protected: void on_presets_changed(); void update_preset_description_line(); void update_frequently_changed_parameters(); - void update_tab_presets(wxComboCtrl* ui, bool show_incompatible); void fill_icon_descriptions(); void set_tooltips_text(); }; 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 100% rename from xs/src/slic3r/GUI/WipeTowerDialog.cpp rename to src/slic3r/GUI/WipeTowerDialog.cpp 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/xs/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp similarity index 84% rename from xs/src/slic3r/GUI/wxExtensions.cpp rename to src/slic3r/GUI/wxExtensions.cpp index 13730a497a..f9eb032c46 100644 --- a/xs/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -1,6 +1,5 @@ #include "wxExtensions.hpp" -#include "GUI.hpp" #include "../../libslic3r/Utils.hpp" #include "BitmapCache.hpp" @@ -8,6 +7,41 @@ #include #include #include +#include "GUI_App.hpp" +#include "GUI_ObjectList.hpp" + +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; @@ -361,14 +395,15 @@ void PrusaObjectDataViewModelNode::set_part_action_icon() { Slic3r::GUI::BitmapCache *m_bitmap_cache = nullptr; bool PrusaObjectDataViewModelNode::update_settings_digest(const std::vector& categories) { - if (m_type != "settings" || m_opt_categories == categories) + if (m_type != itSettings || m_opt_categories == categories) return false; m_opt_categories = categories; m_name = wxEmptyString; - m_icon = m_empty_icon; +// m_icon = m_empty_icon; + m_bmp = m_empty_bmp; - auto categories_icon = Slic3r::GUI::get_category_icon(); + std::map& categories_icon = Slic3r::GUI::wxGetApp().obj_list()->CATEGORY_ICON;//Slic3r::GUI::get_category_icon(); for (auto& cat : m_opt_categories) m_name += cat + "; "; @@ -416,18 +451,7 @@ wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name) return child; } -wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name, const int instances_count/*, int scale*/) -{ - auto root = new PrusaObjectDataViewModelNode(name, instances_count); - m_objects.push_back(root); - // notify control - wxDataViewItem child((void*)root); - wxDataViewItem parent((void*)NULL); - ItemAdded(parent, child); - return child; -} - -wxDataViewItem PrusaObjectDataViewModel::AddChild( const wxDataViewItem &parent_item, +wxDataViewItem PrusaObjectDataViewModel::AddVolumeChild(const wxDataViewItem &parent_item, const wxString &name, const wxBitmap& icon, const int extruder/* = 0*/, @@ -438,25 +462,25 @@ wxDataViewItem PrusaObjectDataViewModel::AddChild( const wxDataViewItem &parent_ const wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder); - if (create_frst_child && (root->GetChildren().Count() == 0 || - (root->GetChildren().Count() == 1 && root->GetNthChild(0)->m_type == "settings"))) + if (create_frst_child && root->m_volumes_cnt == 0) { - const auto icon_solid_mesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG); - const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, icon_solid_mesh, extruder_str, 0); + const auto bmp_solid_mesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG); + const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, bmp_solid_mesh, extruder_str, 0); root->Append(node); // notify control const wxDataViewItem child((void*)node); ItemAdded(parent_item, child); + + root->m_volumes_cnt++; } - const auto volume_id = root->GetChildCount() > 0 && root->GetNthChild(0)->m_type == "settings" ? - root->GetChildCount() - 1 : root->GetChildCount(); - - const auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, volume_id); + const auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, root->m_volumes_cnt); root->Append(node); // notify control const wxDataViewItem child((void*)node); - ItemAdded(parent_item, child); + ItemAdded(parent_item, child); + root->m_volumes_cnt++; + return child; } @@ -465,7 +489,7 @@ wxDataViewItem PrusaObjectDataViewModel::AddSettingsChild(const wxDataViewItem & PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID(); if (!root) return wxDataViewItem(0); - const auto node = new PrusaObjectDataViewModelNode(root); + const auto node = new PrusaObjectDataViewModelNode(root, itSettings); root->Insert(node, 0); // notify control const wxDataViewItem child((void*)node); @@ -473,6 +497,55 @@ wxDataViewItem PrusaObjectDataViewModel::AddSettingsChild(const wxDataViewItem & return child; } +int get_istances_root_idx(PrusaObjectDataViewModelNode *parent_node) +{ + int inst_root_id = -1; + int stop_search_i = parent_node->GetChildCount(); + if (stop_search_i > 2) stop_search_i = 2; + for (int i = 0; i < stop_search_i; ++i) + if (parent_node->GetNthChild(i)->m_type & itInstanceRoot) { + inst_root_id = i; + break; + } + return inst_root_id; +} + +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) { + const unsigned insert_pos = parent_node->GetChildCount() == 0 || parent_node->GetNthChild(0)->m_type != itSettings ? 0 : 1; + parent_node->Insert(inst_root_node, insert_pos); + // notify control + ItemAdded(parent_item, inst_root_item); + 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); @@ -488,21 +561,45 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item) // thus removing the node from it doesn't result in freeing it if (node_parent){ auto id = node_parent->GetChildren().Index(node); - auto v_id = node->GetVolumeId(); + auto idx = node->GetIdx(); node_parent->GetChildren().Remove(node); if (id > 0){ if(id == node_parent->GetChildCount()) id--; ret_item = wxDataViewItem(node_parent->GetChildren().Item(id)); } - //update volume_id value for remaining child-nodes + //update idx value for remaining child-nodes auto children = node_parent->GetChildren(); - for (size_t i = 0; i < node_parent->GetChildCount() && v_id>=0; i++) + for (size_t i = 0; i < node_parent->GetChildCount() && idx>=0; i++) { - auto volume_id = children[i]->GetVolumeId(); - if (volume_id > v_id) - children[i]->SetVolumeId(volume_id-1); + 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); + ItemDeleted(ret_item, wxDataViewItem(node_parent)); + +#ifndef __WXGTK__ + if (obj_node->GetChildCount() == 0) + obj_node->m_container = false; +#endif //__WXGTK__ + return ret_item; + } } else { @@ -532,6 +629,43 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &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()) @@ -590,19 +724,23 @@ wxDataViewItem PrusaObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volu } auto parent = m_objects[obj_idx]; - if (parent->GetChildCount() == 0) { + 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_volume_id == volume_idx) + if (parent->GetNthChild(i)->m_idx == volume_idx && parent->GetNthChild(0)->GetType() & itVolume) return wxDataViewItem(parent->GetNthChild(i)); return wxDataViewItem(0); } -int PrusaObjectDataViewModel::GetIdByItem(wxDataViewItem& item) +int PrusaObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) { wxASSERT(item.IsOk()); @@ -614,14 +752,49 @@ int PrusaObjectDataViewModel::GetIdByItem(wxDataViewItem& item) return it - m_objects.begin(); } -int PrusaObjectDataViewModel::GetVolumeIdByItem(const wxDataViewItem& item) +int PrusaObjectDataViewModel::GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const { wxASSERT(item.IsOk()); PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - if (!node) // happens if item.IsOk()==false + if (!node || node->m_type != type) return -1; - return node->GetVolumeId(); + 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() < 0 && !(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 @@ -633,21 +806,6 @@ wxString PrusaObjectDataViewModel::GetName(const wxDataViewItem &item) const return node->m_name; } -wxString PrusaObjectDataViewModel::GetCopy(const wxDataViewItem &item) const -{ - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - if (!node) // happens if item.IsOk()==false - return wxEmptyString; - - return node->m_copy; -} - -wxIcon& PrusaObjectDataViewModel::GetIcon(const wxDataViewItem &item) const -{ - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - return node->m_icon; -} - wxBitmap& PrusaObjectDataViewModel::GetBitmap(const wxDataViewItem &item) const { PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); @@ -661,17 +819,13 @@ void PrusaObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); switch (col) { - case 0:{ - const PrusaDataViewBitmapText data(node->m_name, node->m_bmp); - variant << data; - break;} - case 1: - variant = node->m_copy; + case 0: + variant << PrusaDataViewBitmapText(node->m_name, node->m_bmp); break; - case 2: + case 1: variant = node->m_extruder; break; - case 3: + case 2: variant << node->m_action_icon; break; default: @@ -694,7 +848,7 @@ bool PrusaObjectDataViewModel::SetValue(const wxVariant &variant, const int item return m_objects[item_idx]->SetValue(variant, col); } - +/* wxDataViewItem PrusaObjectDataViewModel::MoveChildUp(const wxDataViewItem &item) { auto ret_item = wxDataViewItem(0); @@ -742,7 +896,7 @@ wxDataViewItem PrusaObjectDataViewModel::MoveChildDown(const wxDataViewItem &ite 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); @@ -753,14 +907,14 @@ wxDataViewItem PrusaObjectDataViewModel::ReorganizeChildren(int current_volume_i if (!node_parent) // happens if item.IsOk()==false return ret_item; - const size_t shift = node_parent->GetChildren().Item(0)->m_type == "settings" ? 1 : 0; + 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 = HasSettings(wxDataViewItem(deleted_node)); + const auto settings_item = GetSettingsItem(wxDataViewItem(deleted_node)); if (settings_item) ItemAdded(wxDataViewItem(deleted_node), settings_item); @@ -769,7 +923,7 @@ wxDataViewItem PrusaObjectDataViewModel::ReorganizeChildren(int current_volume_i 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]->SetVolumeId(id); + children[id+shift]->SetIdx(id); return wxDataViewItem(node_parent->GetNthChild(new_volume_id+shift)); } @@ -792,12 +946,32 @@ wxDataViewItem PrusaObjectDataViewModel::GetParent(const wxDataViewItem &item) c PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); // objects nodes has no parent too - if (find(m_objects.begin(), m_objects.end(),node) != m_objects.end()) + 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 @@ -833,7 +1007,15 @@ unsigned int PrusaObjectDataViewModel::GetChildren(const wxDataViewItem &parent, return count; } -wxDataViewItem PrusaObjectDataViewModel::HasSettings(const wxDataViewItem &item) const +ItemType PrusaObjectDataViewModel::GetItemType(const wxDataViewItem &item) const +{ + if (!item.IsOk()) + return itUndef; + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + return node->m_type; +} + +wxDataViewItem PrusaObjectDataViewModel::GetSettingsItem(const wxDataViewItem &item) const { if (!item.IsOk()) return wxDataViewItem(0); @@ -842,9 +1024,8 @@ wxDataViewItem PrusaObjectDataViewModel::HasSettings(const wxDataViewItem &item) if (node->GetChildCount() == 0) return wxDataViewItem(0); - auto& children = node->GetChildren(); - if (children[0]->m_type == "settings") - return wxDataViewItem((void*)children[0]);; + if (node->GetNthChild(0)->m_type == itSettings) + return wxDataViewItem((void*)node->GetNthChild(0)); return wxDataViewItem(0); } @@ -854,7 +1035,7 @@ bool PrusaObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const if (!item.IsOk()) return false; PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - return node->m_type == "settings"; + return node->m_type == itSettings; } @@ -869,7 +1050,14 @@ void PrusaObjectDataViewModel::UpdateSettingsDigest(const wxDataViewItem &item, ItemChanged(item); } +//----------------------------------------------------------------------------- +// PrusaDataViewBitmapText +//----------------------------------------------------------------------------- + +wxIMPLEMENT_DYNAMIC_CLASS(PrusaDataViewBitmapText, wxObject) + IMPLEMENT_VARIANT_OBJECT(PrusaDataViewBitmapText) + // --------------------------------------------------------- // PrusaIconTextRenderer // --------------------------------------------------------- diff --git a/xs/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp similarity index 87% rename from xs/src/slic3r/GUI/wxExtensions.hpp rename to src/slic3r/GUI/wxExtensions.hpp index 51c02035c0..80a564fd0f 100644 --- a/xs/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -12,6 +12,12 @@ #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 { @@ -111,6 +117,7 @@ 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, @@ -165,7 +172,7 @@ public: void SetText(const wxString &text) { m_text = text; } wxString GetText() const { return m_text; } - void SetBitmap(const wxIcon &icon) { m_bmp = icon; } + void SetBitmap(const wxBitmap &bmp) { m_bmp = bmp; } const wxBitmap &GetBitmap() const { return m_bmp; } bool IsSameAs(const PrusaDataViewBitmapText& other) const { @@ -183,6 +190,8 @@ public: private: wxString m_text; wxBitmap m_bmp; + + wxDECLARE_DYNAMIC_CLASS(PrusaDataViewBitmapText); }; DECLARE_VARIANT_OBJECT(PrusaDataViewBitmapText) @@ -191,6 +200,15 @@ 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); @@ -198,16 +216,14 @@ class PrusaObjectDataViewModelNode { PrusaObjectDataViewModelNode* m_parent; MyObjectTreeModelNodePtrArray m_children; - wxIcon m_empty_icon; wxBitmap m_empty_bmp; + size_t m_volumes_cnt = 0; std::vector< std::string > m_opt_categories; public: - PrusaObjectDataViewModelNode(const wxString &name, const int instances_count=1) { + PrusaObjectDataViewModelNode(const wxString &name) { m_parent = NULL; m_name = name; - m_copy = wxString::Format("%d", instances_count); - m_type = "object"; - m_volume_id = -1; + 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 @@ -221,13 +237,12 @@ public: const wxString& sub_obj_name, const wxBitmap& bmp, const wxString& extruder, - const int volume_id=-1) { + const int idx = -1 ) { m_parent = parent; m_name = sub_obj_name; - m_copy = wxEmptyString; m_bmp = bmp; - m_type = "volume"; - m_volume_id = volume_id; + 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 @@ -238,12 +253,25 @@ public: set_part_action_icon(); } - PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent) : + PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent, const ItemType type) : m_parent(parent), - m_name("Settings to modified"), - m_copy(wxEmptyString), - m_type("settings"), - m_extruder(wxEmptyString) {} + 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() { @@ -257,11 +285,9 @@ public: } wxString m_name; - wxIcon& m_icon = m_empty_icon; wxBitmap& m_bmp = m_empty_bmp; - wxString m_copy; - std::string m_type; - int m_volume_id = -2; + ItemType m_type; + int m_idx = -1; bool m_container = false; wxString m_extruder = "default"; wxBitmap m_action_icon; @@ -325,12 +351,9 @@ public: m_name = data.GetText(); return true;} case 1: - m_copy = variant.GetString(); - return true; - case 2: m_extruder = variant.GetString(); return true; - case 3: + case 2: m_action_icon << variant; return true; default: @@ -338,28 +361,25 @@ public: } return false; } - void SetIcon(const wxIcon &icon) - { - m_icon = icon; - } void SetBitmap(const wxBitmap &icon) { m_bmp = icon; } - - void SetType(const std::string& type){ - m_type = type; - } - const std::string& GetType(){ - return m_type; + + 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); } - void SetVolumeId(const int& volume_id){ - m_volume_id = volume_id; - } - const int& GetVolumeId(){ - return m_volume_id; + int GetIdx() const { + return m_idx; } // use this function only for childrens @@ -367,9 +387,10 @@ public: { // ! Don't overwrite other values because of equality of this values for all children -- m_name = from_node.m_name; - m_icon = from_node.m_icon; - m_volume_id = from_node.m_volume_id; - m_extruder = from_node.m_extruder; + 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) { @@ -381,8 +402,8 @@ public: PrusaObjectDataViewModelNode new_scnd = *GetNthChild(frst_id); PrusaObjectDataViewModelNode new_frst = *GetNthChild(scnd_id); - new_scnd.m_volume_id = m_children.Item(scnd_id)->m_volume_id; - new_frst.m_volume_id = m_children.Item(frst_id)->m_volume_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); @@ -393,6 +414,8 @@ public: void set_object_action_icon(); void set_part_action_icon(); bool update_settings_digest(const std::vector& categories); +private: + friend class PrusaObjectDataViewModel; }; // ---------------------------------------------------------------------------- @@ -407,27 +430,29 @@ public: ~PrusaObjectDataViewModel(); wxDataViewItem Add(const wxString &name); - wxDataViewItem Add(const wxString &name, const int instances_count); - wxDataViewItem AddChild(const wxDataViewItem &parent_item, + wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item, const wxString &name, const wxBitmap& icon, 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); wxDataViewItem GetItemById(int obj_idx); wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx); - int GetIdByItem(wxDataViewItem& item); - int GetVolumeIdByItem(const wxDataViewItem& item); - bool IsEmpty() { return m_objects.empty(); } + 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; - wxString GetCopy(const wxDataViewItem &item) const; - wxIcon& GetIcon(const wxDataViewItem &item) const; wxBitmap& GetBitmap(const wxDataViewItem &item) const; // helper methods to change the model @@ -441,8 +466,8 @@ public: 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); +// 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, @@ -452,6 +477,8 @@ public: 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; @@ -460,7 +487,8 @@ public: // In our case it is an item with all columns virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } - wxDataViewItem HasSettings(const wxDataViewItem &item) const; + ItemType GetItemType(const wxDataViewItem &item) const ; + wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const; bool IsSettingsItem(const wxDataViewItem &item) const; void UpdateSettingsDigest(const wxDataViewItem &item, const std::vector& categories); }; @@ -474,7 +502,7 @@ class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer public: PrusaBitmapTextRenderer( wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int align = wxDVR_DEFAULT_ALIGNMENT): - wxDataViewCustomRenderer(wxT("wxObject"), mode, align) {} + wxDataViewCustomRenderer(wxT("PrusaDataViewBitmapText"), mode, align) {} bool SetValue(const wxVariant &value); bool GetValue(wxVariant &value) const; @@ -485,7 +513,6 @@ public: virtual bool HasEditorCtrl() const { return false; } private: -// wxDataViewIconText m_value; PrusaDataViewBitmapText m_value; }; 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 100% rename from xs/src/slic3r/Utils/Bonjour.cpp rename to src/slic3r/Utils/Bonjour.cpp 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 100% rename from xs/src/slic3r/Utils/FixModelByWin10.cpp rename to src/slic3r/Utils/FixModelByWin10.cpp 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 100% rename from xs/src/slic3r/Utils/HexFile.cpp rename to src/slic3r/Utils/HexFile.cpp 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 95% rename from xs/src/slic3r/Utils/PresetUpdater.cpp rename to src/slic3r/Utils/PresetUpdater.cpp index 2e423dc5ee..026e340da5 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(); @@ -466,8 +467,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()) {} @@ -485,7 +486,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 @@ -509,7 +510,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); @@ -569,10 +570,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; @@ -601,9 +602,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 100% rename from xs/src/slic3r/Utils/Semver.hpp rename to src/slic3r/Utils/Semver.hpp 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/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/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 7d4c324f4e..27228dacff 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -1,441 +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 -Wno-reorder" ) - find_package(PkgConfig REQUIRED) -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() - -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. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=return-type" ) -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/PostProcessor.cpp - ${LIBDIR}/libslic3r/GCode/PostProcessor.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/PrintExport.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/Technologies.hpp - ${LIBDIR}/libslic3r/TriangleMesh.cpp - ${LIBDIR}/libslic3r/TriangleMesh.hpp - ${LIBDIR}/libslic3r/SLABasePool.hpp - ${LIBDIR}/libslic3r/SLABasePool.cpp -# ${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/BackgroundSlicingProcess.cpp - ${LIBDIR}/slic3r/GUI/BackgroundSlicingProcess.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/GLToolbar.hpp - ${LIBDIR}/slic3r/GUI/GLToolbar.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/GUI_ObjectParts.cpp - ${LIBDIR}/slic3r/GUI/GUI_ObjectParts.hpp - ${LIBDIR}/slic3r/GUI/GUI_Preview.cpp - ${LIBDIR}/slic3r/GUI/GUI_Preview.hpp - ${LIBDIR}/slic3r/GUI/GUI_PreviewIface.cpp - ${LIBDIR}/slic3r/GUI/GUI_PreviewIface.hpp - ${LIBDIR}/slic3r/GUI/LambdaObjectDialog.cpp - ${LIBDIR}/slic3r/GUI/LambdaObjectDialog.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/GUI/ProgressIndicator.hpp - ${LIBDIR}/slic3r/GUI/ProgressStatusBar.hpp - ${LIBDIR}/slic3r/GUI/ProgressStatusBar.cpp - ${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/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 -) - -# ############################################################################## -# Configure rasterizer target -# ############################################################################## - -find_package(PNG QUIET) - -option(RASTERIZER_FORCE_BUILTIN_LIBPNG "Force the usage of builting libpng instead of the system version." OFF) - -add_library(rasterizer STATIC - ${LIBDIR}/libslic3r/Rasterizer/Rasterizer.hpp - ${LIBDIR}/libslic3r/Rasterizer/Rasterizer.cpp -) - -if(PNG_FOUND AND NOT RASTERIZER_FORCE_BUILTIN_LIBPNG) - message(STATUS "Using system libpng.") - target_link_libraries(rasterizer PRIVATE ${PNG_LIBRARIES}) - target_include_directories(rasterizer PRIVATE ${PNG_INCLUDE_DIRS}) - target_compile_definitions(rasterizer PRIVATE ${PNG_DEFINITIONS}) +# 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(ZLIB_LIBRARY "") - message(WARNING "Using builtin libpng. This can cause crashes on some platforms.") - add_subdirectory( ${LIBDIR}/png/zlib) - - set(ZLIB_INCLUDE_DIR - ${LIBDIR}/png/zlib - ${CMAKE_CURRENT_BINARY_DIR}/src/png/zlib - ) - - add_subdirectory( ${LIBDIR}/png/libpng ) - set_target_properties(zlibstatic PROPERTIES POSITION_INDEPENDENT_CODE ON) - set_target_properties(png_static PROPERTIES POSITION_INDEPENDENT_CODE ON) - - target_include_directories(rasterizer PRIVATE - ${LIBDIR}/png/libpng - ${CMAKE_CURRENT_BINARY_DIR}/src/png/libpng - ) - target_link_libraries(rasterizer PRIVATE png_static zlibstatic) - + 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() -target_link_libraries(libslic3r rasterizer ) - -# ############################################################################## +# 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) @@ -467,13 +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_BackgroundSlicingProcess.xsp - ${XSP_DIR}/GUI_3DScene.xsp - ${XSP_DIR}/GUI_Preset.xsp - ${XSP_DIR}/GUI_Tab.xsp - ${XSP_DIR}/GUI_Preview.xsp ${XSP_DIR}/Layer.xsp ${XSP_DIR}/Line.xsp ${XSP_DIR}/Model.xsp @@ -488,10 +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}/ProgressStatusBar.xsp ${XSP_DIR}/XS.xsp ) foreach (file ${XS_XSP_FILES}) @@ -522,50 +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}/callback.cpp - ${LIBDIR}/callback.hpp - ${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 () @@ -585,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 /DTBB_USE_ASSERT") - set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") - set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") - set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") -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) @@ -782,23 +185,9 @@ if(APPLE) ) endif() -# Create a slic3r executable -add_executable(slic3r EXCLUDE_FROM_ALL ${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}) - -add_executable(slabasebed EXCLUDE_FROM_ALL ${PROJECT_SOURCE_DIR}/src/slabasebed.cpp) -target_include_directories(slabasebed PRIVATE src src/libslic3r) -target_link_libraries(slabasebed libslic3r libslic3r_gui qhull admesh miniz ${Boost_LIBRARIES} clipper ${EXPAT_LIBRARIES} ${GLEW_LIBRARIES} polypartition poly2tri ${TBB_LIBRARIES} ${wxWidgets_LIBRARIES} ${CMAKE_DL_LIBS}) - 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. @@ -806,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/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

JavaScript is disabled on your browser. Please enable JavaScript to enjoy all the features of this site.
\ No newline at end of file diff --git a/xs/src/benchmark.h b/xs/src/benchmark.h deleted file mode 100644 index 19870b37b1..0000000000 --- a/xs/src/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/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 a78a03b3a5..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(0); - } - - template - inline TCoord y(const RawPoint& p) - { - return p(1); - } - - template - inline TCoord& x(RawPoint& p) - { - return p(0); - } - - template - inline TCoord& y(RawPoint& p) - { - return p(1); - } - - 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/Format/PRUS.cpp b/xs/src/libslic3r/Format/PRUS.cpp deleted file mode 100644 index 5b27583316..0000000000 --- a/xs/src/libslic3r/Format/PRUS.cpp +++ /dev/null @@ -1,410 +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 }; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - Vec3d instance_rotation = Vec3d::Zero(); - Vec3d instance_scaling_factor = Vec3d::Ones(); - Vec3d instance_offset = Vec3d::Zero(); -#else - double instance_rotation = 0.; - double instance_scaling_factor = 1.f; - Vec2d instance_offset(0., 0.); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - 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 ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - 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]); -#else - 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.; - } -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - 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); - } -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - 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] / instance_scaling_factor(2); -#else - instance_offset(0) = position[0] - zero[0]; - instance_offset(1) = position[1] - zero[1]; - trafo[2][3] = position[2] / instance_scaling_factor; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - 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(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(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](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 = 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(); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - instance->set_rotation(instance_rotation); - instance->set_scaling_factor(instance_scaling_factor); - instance->set_offset(instance_offset); -#else - instance->rotation = instance_rotation; - instance->scaling_factor = instance_scaling_factor; - instance->offset = instance_offset; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - ++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/Technologies.hpp b/xs/src/libslic3r/Technologies.hpp deleted file mode 100644 index 14e2a8bde3..0000000000 --- a/xs/src/libslic3r/Technologies.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _technologies_h_ -#define _technologies_h_ - -// 1.42.0 techs -#define ENABLE_1_42_0 1 - -// Add z coordinate to model instances' offset -// Add x and y rotation components to model instances' offset -// Add scaling factors for all the three axes to model instances -#define ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM (1 && ENABLE_1_42_0) -// Add double click on gizmo grabbers to reset transformation components to their default value -#define ENABLE_GIZMOS_RESET (1 && ENABLE_1_42_0) - -#endif // _technologies_h_ - - diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp deleted file mode 100644 index 2ad155b7d7..0000000000 --- a/xs/src/slic3r/GUI/GUI.cpp +++ /dev/null @@ -1,1428 +0,0 @@ -#include "GUI.hpp" -#include "../AppController.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 -#include - -#include "wxExtensions.hpp" - -#include "Tab.hpp" -#include "TabIface.hpp" -#include "GUI_Preview.hpp" -#include "GUI_PreviewIface.hpp" -#include "AboutDialog.hpp" -#include "AppConfig.hpp" -#include "ConfigSnapshotDialog.hpp" -#include "ProgressStatusBar.hpp" -#include "Utils.hpp" -#include "MsgDialog.hpp" -#include "ConfigWizard.hpp" -#include "Preferences.hpp" -#include "PresetBundle.hpp" -#include "UpdateDialogs.hpp" -#include "FirmwareDialog.hpp" -#include "GUI_ObjectParts.hpp" - -#include "../Utils/PresetUpdater.hpp" -#include "../Config/Snapshot.hpp" - -#include "3DScene.hpp" -#include "libslic3r/I18N.hpp" -#include "Model.hpp" -#include "LambdaObjectDialog.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(); - #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; -ProgressStatusBar *g_progress_status_bar = nullptr; -wxNotebook *g_wxTabPanel = nullptr; -wxPanel *g_wxPlater = nullptr; -AppConfig *g_AppConfig = nullptr; -PresetBundle *g_PresetBundle= nullptr; -PresetUpdater *g_PresetUpdater = 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::vector > m_optgroups; -double m_brim_width = 0.0; -size_t m_label_width = 100; -wxButton* g_wiping_dialog_button = nullptr; - -//showed/hided controls according to the view mode -wxWindow *g_right_panel = nullptr; -wxBoxSizer *g_frequently_changed_parameters_sizer = nullptr; -wxBoxSizer *g_info_sizer = nullptr; -wxBoxSizer *g_object_list_sizer = nullptr; -std::vector g_buttons; -wxStaticBitmap *g_manifold_warning_icon = nullptr; -bool g_show_print_info = false; -bool g_show_manifold_warning_icon = false; - -PreviewIface* g_preview = 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; -} - -wxFrame* get_main_frame() { return g_wxMainFrame; } - -void set_progress_status_bar(ProgressStatusBar *prsb) -{ - g_progress_status_bar = prsb; -} - -ProgressStatusBar* get_progress_status_bar() { return g_progress_status_bar; } - -void set_tab_panel(wxNotebook *tab_panel) -{ - g_wxTabPanel = tab_panel; -} - -void set_plater(wxPanel *plater) -{ - g_wxPlater = plater; -} - -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; -} - -enum ActionButtons -{ - abExportGCode, - abReslice, - abPrint, - abSendGCode, -}; - -void set_objects_from_perl( wxWindow* parent, - wxBoxSizer *frequently_changed_parameters_sizer, - wxBoxSizer *info_sizer, - wxButton *btn_export_gcode, - wxButton *btn_reslice, - wxButton *btn_print, - wxButton *btn_send_gcode, - wxStaticBitmap *manifold_warning_icon) -{ - g_right_panel = parent->GetParent(); - g_frequently_changed_parameters_sizer = frequently_changed_parameters_sizer; - g_info_sizer = info_sizer; - - g_buttons.push_back(btn_export_gcode); - g_buttons.push_back(btn_reslice); - g_buttons.push_back(btn_print); - g_buttons.push_back(btn_send_gcode); - - // Update font style for buttons - for (auto btn : g_buttons) - btn->SetFont(bold_font()); - - g_manifold_warning_icon = manifold_warning_icon; -} - -void set_show_print_info(bool show) -{ - g_show_print_info = show; -} - -void set_show_manifold_warning_icon(bool show) -{ - g_show_manifold_warning_icon = show; - if (!g_manifold_warning_icon) - return; - - // update manifold_warning_icon showing - if (show && !g_info_sizer->IsShown(static_cast(0))) - g_show_manifold_warning_icon = false; - - g_manifold_warning_icon->Show(g_show_manifold_warning_icon); - g_manifold_warning_icon->GetParent()->Layout(); -} - -void set_objects_list_sizer(wxBoxSizer *objects_list_sizer){ - g_object_list_sizer = objects_list_sizer; -} - -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, - ConfigMenuModeSimple, - ConfigMenuModeExpert, - ConfigMenuLanguage, - ConfigMenuFlashFirmware, - ConfigMenuCnt, -}; - -ConfigMenuIDs get_view_mode() -{ - if (!g_AppConfig->has("view_mode")) - return ConfigMenuModeSimple; - - const auto mode = g_AppConfig->get("view_mode"); - return mode == "expert" ? ConfigMenuModeExpert : ConfigMenuModeSimple; -} - -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->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 + 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, [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; - } - }); - mode_menu->Bind(wxEVT_MENU, [config_id_base](wxEvent& event) { - std::string mode = event.GetId() - config_id_base == ConfigMenuModeExpert ? - "expert" : "simple"; - g_AppConfig->set("view_mode", mode); - g_AppConfig->save(); - update_mode(); - }); - 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); -} - -void open_model(wxWindow *parent, wxArrayString& input_files){ - 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 dlg_title = _(L("Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):")); - auto dialog = new wxFileDialog(parent /*? parent : GetTopWindow(g_wxMainFrame)*/, dlg_title, - g_AppConfig->get_last_dir(), "", - MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST); - if (dialog->ShowModal() != wxID_OK) { - dialog->Destroy(); - return ; - } - - dialog->GetPaths(input_files); - dialog->Destroy(); -} - -// 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(int event_value_change, int event_presets_changed) -{ - update_label_colours_from_appconfig(); - add_created_tab(new TabPrint (g_wxTabPanel), event_value_change, event_presets_changed); - add_created_tab(new TabFilament (g_wxTabPanel), event_value_change, event_presets_changed); - add_created_tab(new TabSLAMaterial (g_wxTabPanel), event_value_change, event_presets_changed); - add_created_tab(new TabPrinter (g_wxTabPanel), event_value_change, event_presets_changed); -} - -std::vector preset_tabs = { - { "print", nullptr, ptFFF }, - { "filament", nullptr, ptFFF }, - { "sla_material", nullptr, ptSLA } -}; -const std::vector& get_preset_tabs() { - return preset_tabs; -} - -Tab* get_tab(const std::string& name) -{ - std::vector::iterator it = std::find_if(preset_tabs.begin(), preset_tabs.end(), - [name](PresetTab& tab){ return name == tab.name; }); - return it != preset_tabs.end() ? it->panel : nullptr; -} - -TabIface* get_preset_tab_iface(char *name) -{ - Tab* tab = get_tab(name); - if (tab) return new TabIface(tab); - - 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); -} - -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{ (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, int event_value_change, int event_presets_changed) -{ - panel->create_preset_tab(g_PresetBundle); - - // Load the currently selected preset into the GUI, update the preset selection box. - panel->load_current_preset(); - - panel->set_event_value_change(wxEventType(event_value_change)); - panel->set_event_presets_changed(wxEventType(event_presets_changed)); - - const wxString& tab_name = panel->GetName(); - bool add_panel = true; - - auto it = std::find_if( preset_tabs.begin(), preset_tabs.end(), - [tab_name](PresetTab& tab){return tab.name == tab_name; }); - if (it != preset_tabs.end()) { - it->panel = panel; - add_panel = it->technology == g_PresetBundle->printers.get_edited_preset().printer_technology(); - } - - if (add_panel) - 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(); -} - -// Assign a Lambda to the print object to emit a wxWidgets Command with the provided ID -// to deliver a progress status message. -void set_print_callback_event(Print *print, int id) -{ - print->set_status_callback([id](int percent, const std::string &message){ - wxCommandEvent event(id); - event.SetInt(percent); - event.SetString(message); - wxQueueEvent(g_wxMainFrame, event.Clone()); - }); -} - -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 - )); -} - -wxWindow* get_right_panel(){ - return g_right_panel; -} - -wxNotebook * get_tab_panel() { - return g_wxTabPanel; -} - -const size_t& label_width(){ - return m_label_width; -} - -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()); -} - -std::string into_u8(const wxString &str) -{ - auto buffer_utf8 = str.utf8_str(); - return std::string(buffer_utf8.data()); -} - -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) -{ - set_event_object_selection_changed(event_object_selection_changed); - set_event_object_settings_changed(event_object_settings_changed); - set_event_remove_object(event_remove_object); - set_event_update_scene(event_update_scene); - set_objects_from_model(model); - init_mesh_icons(); - -// wxWindowUpdateLocker noUpdates(parent); - -// add_objects_list(parent, sizer); - -// add_collapsible_panes(parent, sizer); -} - -void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer) -{ - DynamicPrintConfig* config = &g_PresetBundle->prints.get_edited_preset().config; - std::shared_ptr optgroup = std::make_shared(parent, "", config); - const wxArrayInt& ar = preset_sizer->GetColWidths(); - m_label_width = ar.IsEmpty() ? 100 : ar.front()-4; - optgroup->label_width = m_label_width; - - //Frequently changed parameters - 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_optgroups[ogFrequentlyChangingParameters]->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 = optgroup->get_option("fill_density"); - option.opt.sidetext = ""; - option.opt.full_width = true; - 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; - 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"); - 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; - 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; - }; - optgroup->append_line(line); - - sizer->Add(optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT, 2); - - m_optgroups.push_back(optgroup);// ogFrequentlyChangingParameters - - // Object List - add_objects_list(parent, sizer); - - // Frequently Object Settings - add_object_settings(parent, sizer); -} - -void show_frequently_changed_parameters(bool show) -{ - g_frequently_changed_parameters_sizer->Show(show); - if (!show) return; - - for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) { - Tab *tab = dynamic_cast(g_wxTabPanel->GetPage(i)); - if (!tab) - continue; - tab->update_wiping_button_visibility(); - break; - } -} - -void show_buttons(bool show) -{ - g_buttons[abReslice]->Show(show); - for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) { - TabPrinter *tab = dynamic_cast(g_wxTabPanel->GetPage(i)); - if (!tab) - continue; - if (g_PresetBundle->printers.get_selected_preset().printer_technology() == ptFFF) { - g_buttons[abPrint]->Show(show && !tab->m_config->opt_string("serial_port").empty()); - g_buttons[abSendGCode]->Show(show && !tab->m_config->opt_string("print_host").empty()); - } - break; - } -} - -void show_info_sizer(const bool show) -{ - g_info_sizer->Show(static_cast(0), show); - g_info_sizer->Show(1, show && g_show_print_info); - g_manifold_warning_icon->Show(show && g_show_manifold_warning_icon); -} - -void show_object_name(bool show) -{ - wxGridSizer* grid_sizer = get_optgroup(ogFrequentlyObjectSettings)->get_grid_sizer(); - grid_sizer->Show(static_cast(0), show); - grid_sizer->Show(static_cast(1), show); -} - -void update_mode() -{ - wxWindowUpdateLocker noUpdates(g_right_panel->GetParent()); - - ConfigMenuIDs mode = get_view_mode(); - - g_object_list_sizer->Show(mode == ConfigMenuModeExpert); - show_info_sizer(mode == ConfigMenuModeExpert); - show_buttons(mode == ConfigMenuModeExpert); - show_object_name(mode == ConfigMenuModeSimple); - show_manipulation_sizer(mode == ConfigMenuModeSimple); - - // TODO There is a not the best place of it! - // *** Update showing of the collpane_settings -// show_collpane_settings(mode == ConfigMenuModeExpert); - // ************************* - g_right_panel->Layout(); - g_right_panel->GetParent()->Layout(); -} - -bool is_expert_mode(){ - return get_view_mode() == ConfigMenuModeExpert; -} - -ConfigOptionsGroup* get_optgroup(size_t i) -{ - return m_optgroups[i].get(); -} - -std::vector >& get_optgroups() { - return m_optgroups; -} - -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 enable_action_buttons(bool enable) -{ - if (g_buttons.empty()) - return; - - // Update background colour for buttons - const wxColour bgrd_color = enable ? wxColour(224, 224, 224/*255, 96, 0*/) : wxColour(204, 204, 204); - - for (auto btn : g_buttons) { - btn->Enable(enable); - btn->SetBackgroundColour(bgrd_color); - } -} - -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 -} - -namespace { -AppControllerPtr g_appctl; -} - -AppControllerPtr get_appctl() -{ - return g_appctl; -} - -void set_cli_appctl() -{ - g_appctl = std::make_shared(); -} - -void set_gui_appctl() -{ - g_appctl = std::make_shared(); -} - -} } diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp deleted file mode 100644 index 04b4a42179..0000000000 --- a/xs/src/slic3r/GUI/GUI.hpp +++ /dev/null @@ -1,269 +0,0 @@ -#ifndef slic3r_GUI_hpp_ -#define slic3r_GUI_hpp_ - -#include -#include -#include "PrintConfig.hpp" -#include "../../callback.hpp" -#include "GUI_ObjectParts.hpp" - -#include -#include - -class wxApp; -class wxWindow; -class wxFrame; -class wxMenuBar; -class wxNotebook; -class wxPanel; -class wxComboCtrl; -class wxString; -class wxArrayString; -class wxArrayLong; -class wxColour; -class wxBoxSizer; -class wxFlexGridSizer; -class wxButton; -class wxFileDialog; -class wxStaticBitmap; -class wxFont; -class wxTopLevelWindow; - -namespace Slic3r { - -class PresetBundle; -class PresetCollection; -class Print; -class ProgressStatusBar; -class AppConfig; -class PresetUpdater; -class DynamicPrintConfig; -class TabIface; -class PreviewIface; -class Print; -class GCodePreviewData; -class AppControllerBase; - -using AppControllerPtr = std::shared_ptr; - -#define _(s) Slic3r::GUI::I18N::translate((s)) - -namespace GUI { namespace I18N { - inline wxString translate(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)); } - inline wxString translate(const wchar_t *s) { return wxGetTranslation(s); } - inline wxString translate(const std::string &s) { return wxGetTranslation(wxString(s.c_str(), wxConvUTF8)); } - inline wxString translate(const std::wstring &s) { return wxGetTranslation(s.c_str()); } -} } - -// !!! If you needed to translate some wxString, -// !!! please use _(L(string)) -// !!! _() - is a standard wxWidgets macro to translate -// !!! L() is used only for marking localizable string -// !!! It will be used in "xgettext" to create a Locating Message Catalog. -#define L(s) s - -//! macro used to localization, return wxScopedCharBuffer -//! With wxConvUTF8 explicitly specify that the source string is already in UTF-8 encoding -#define _CHB(s) wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str() - -// Minimal buffer length for translated string (char buf[MIN_BUF_LENGTH_FOR_L]) -#define MIN_BUF_LENGTH_FOR_L 512 - -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; -} - -struct PresetTab { - std::string name; - Tab* panel; - PrinterTechnology technology; -}; - - -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_progress_status_bar(ProgressStatusBar *prsb); -void set_tab_panel(wxNotebook *tab_panel); -void set_plater(wxPanel *plater); -void set_app_config(AppConfig *app_config); -void set_preset_bundle(PresetBundle *preset_bundle); -void set_preset_updater(PresetUpdater *updater); -void set_objects_from_perl( wxWindow* parent, - wxBoxSizer *frequently_changed_parameters_sizer, - wxBoxSizer *info_sizer, - wxButton *btn_export_gcode, - wxButton *btn_reslice, - wxButton *btn_print, - wxButton *btn_send_gcode, - wxStaticBitmap *manifold_warning_icon); -void set_show_print_info(bool show); -void set_show_manifold_warning_icon(bool show); -void set_objects_list_sizer(wxBoxSizer *objects_list_sizer); - -AppConfig* get_app_config(); -wxApp* get_app(); -PresetBundle* get_preset_bundle(); -wxFrame* get_main_frame(); -ProgressStatusBar* get_progress_status_bar(); -wxNotebook * get_tab_panel(); -wxNotebook* get_tab_panel(); - -AppControllerPtr get_appctl(); -void set_cli_appctl(); -void set_gui_appctl(); - -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(); - -void open_model(wxWindow *parent, wxArrayString& input_files); - -wxWindow* get_right_panel(); -const size_t& label_width(); - -Tab* get_tab(const std::string& name); -const std::vector& get_preset_tabs(); - -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); - -// Opens the configuration wizard, returns true if wizard is finished & accepted. -// 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); - -// Create a new preset tab (print, filament and printer), -void create_preset_tabs(int event_value_change, int event_presets_changed); -TabIface* get_preset_tab_iface(char *name); - -PreviewIface* create_preview_iface(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data); - -// add it at the end of the tab panel. -void add_created_tab(Tab* panel, int event_value_change, int event_presets_changed); -// 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); - -// Assign a Lambda to the print object to emit a wxWidgets Command with the provided ID -// to deliver a progress status message. -void set_print_callback_event(Print *print, int id); - -// 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); -// update right panel of the Plater according to view mode -void update_mode(); - -void show_info_sizer(const bool show); - -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. -void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value); - -// Returns the current state of the items listed in the wxCheckListBoxComboPopup contained in the given wxComboCtrl, -// encoded inside an int. -int combochecklist_get_flags(wxComboCtrl* comboCtrl); - -// Return translated std::string as a wxString -wxString L_str(const std::string &str); -// Return wxString from std::string in UTF8 -wxString from_u8(const std::string &str); -// Return std::string in UTF8 from wxString -std::string into_u8(const wxString &str); - -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); -void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer); -// Update view mode according to selected menu -void update_mode(); -bool is_expert_mode(); - -// Callback to trigger a configuration update timer on the Plater. -static PerlCallback g_on_request_update_callback; - -ConfigOptionsGroup* get_optgroup(size_t i); -std::vector >& get_optgroups(); -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); - -// Save window size and maximized status into AppConfig -void save_window_size(wxTopLevelWindow *window, const std::string &name); -// Restore the above -void restore_window_size(wxTopLevelWindow *window, const std::string &name); - -// Update buttons view according to enable/disable -void enable_action_buttons(bool enable); - -// Display an About dialog -extern void about(); -// Ask the destop to open the datadir using the default file explorer. -extern void desktop_open_datadir_folder(); - -} // namespace GUI -} // namespace Slic3r - -#endif diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp deleted file mode 100644 index 0c7eb62850..0000000000 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp +++ /dev/null @@ -1,2095 +0,0 @@ -#include "GUI.hpp" -#include "OptionsGroup.hpp" -#include "PresetBundle.hpp" -#include "GUI_ObjectParts.hpp" -#include "Model.hpp" -#include "wxExtensions.hpp" -#include "LambdaObjectDialog.hpp" -#include "../../libslic3r/Utils.hpp" - -#include -#include -#include -#include "Geometry.hpp" -#include "slic3r/Utils/FixModelByWin10.hpp" - -#include -#include "3DScene.hpp" - -namespace Slic3r -{ -namespace GUI -{ -wxSizer *m_sizer_object_buttons = nullptr; -wxSizer *m_sizer_part_buttons = nullptr; -wxSizer *m_sizer_object_movers = nullptr; -wxDataViewCtrl *m_objects_ctrl = nullptr; -PrusaObjectDataViewModel *m_objects_model = nullptr; -wxCollapsiblePane *m_collpane_settings = nullptr; -PrusaDoubleSlider *m_slider = nullptr; -wxGLCanvas *m_preview_canvas = nullptr; - -wxBitmap m_icon_modifiermesh; -wxBitmap m_icon_solidmesh; -wxBitmap m_icon_manifold_warning; -wxBitmap m_bmp_cog; -wxBitmap m_bmp_split; - -wxSlider* m_mover_x = nullptr; -wxSlider* m_mover_y = nullptr; -wxSlider* m_mover_z = nullptr; -wxButton* m_btn_move_up = nullptr; -wxButton* m_btn_move_down = nullptr; -Vec3d m_move_options; -Vec3d m_last_coords; -int m_selected_object_id = -1; - -bool g_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 -bool g_is_percent_scale = false; // It indicates if scale unit is percentage -bool g_is_uniform_scale = false; // It indicates if scale is uniform -ModelObjectPtrs* m_objects; -std::shared_ptr m_config; -std::shared_ptr m_default_config; -wxBoxSizer* m_option_sizer = nullptr; - -// option groups for settings -std::vector > m_og_settings; - -int m_event_object_selection_changed = 0; -int m_event_object_settings_changed = 0; -int m_event_remove_object = 0; -int m_event_update_scene = 0; - -bool m_parts_changed = false; -bool m_part_settings_changed = false; - -#ifdef __WXOSX__ - wxString g_selected_extruder = ""; -#endif //__WXOSX__ - -inline t_category_icon& get_category_icon() { - static t_category_icon CATEGORY_ICON; - if (CATEGORY_ICON.empty()){ - CATEGORY_ICON[L("Layers and Perimeters")] = wxBitmap(from_u8(Slic3r::var("layers.png")), wxBITMAP_TYPE_PNG); - CATEGORY_ICON[L("Infill")] = wxBitmap(from_u8(Slic3r::var("infill.png")), wxBITMAP_TYPE_PNG); - CATEGORY_ICON[L("Support material")] = wxBitmap(from_u8(Slic3r::var("building.png")), wxBITMAP_TYPE_PNG); - CATEGORY_ICON[L("Speed")] = wxBitmap(from_u8(Slic3r::var("time.png")), wxBITMAP_TYPE_PNG); - CATEGORY_ICON[L("Extruders")] = wxBitmap(from_u8(Slic3r::var("funnel.png")), wxBITMAP_TYPE_PNG); - CATEGORY_ICON[L("Extrusion Width")] = wxBitmap(from_u8(Slic3r::var("funnel.png")), wxBITMAP_TYPE_PNG); -// CATEGORY_ICON[L("Skirt and brim")] = wxBitmap(from_u8(Slic3r::var("box.png")), wxBITMAP_TYPE_PNG); -// CATEGORY_ICON[L("Speed > Acceleration")] = wxBitmap(from_u8(Slic3r::var("time.png")), wxBITMAP_TYPE_PNG); - CATEGORY_ICON[L("Advanced")] = wxBitmap(from_u8(Slic3r::var("wand.png")), wxBITMAP_TYPE_PNG); - } - return CATEGORY_ICON; -} - -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 = get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA ? 1 : - get_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 set_event_object_selection_changed(const int& event){ - m_event_object_selection_changed = event; -} -void set_event_object_settings_changed(const int& event){ - m_event_object_settings_changed = event; -} -void set_event_remove_object(const int& event){ - m_event_remove_object = event; -} -void set_event_update_scene(const int& event){ - m_event_update_scene = event; -} - -void set_objects_from_model(Model &model) { - m_objects = &(model.objects); -} - -void init_mesh_icons(){ - m_icon_modifiermesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG); - m_icon_solidmesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG); - - // init icon for manifold warning - m_icon_manifold_warning = wxBitmap(Slic3r::GUI::from_u8(Slic3r::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(Slic3r::GUI::from_u8(Slic3r::var("split.png")), wxBITMAP_TYPE_PNG); - - // init bitmap for "Add Settings" context menu - m_bmp_cog = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG); -} - -bool is_parts_changed(){return m_parts_changed;} -bool is_part_settings_changed(){ return m_part_settings_changed; } - -static wxString dots("…", wxConvUTF8); - -void set_tooltip_for_item(const wxPoint& pt) -{ - wxDataViewItem item; - wxDataViewColumn* col; - m_objects_ctrl->HitTest(pt, item, col); - if (!item) return; - - if (col->GetTitle() == " ") - m_objects_ctrl->GetMainWindow()->SetToolTip(_(L("Right button click the icon to change the object settings"))); - else if (col->GetTitle() == _("Name") && - m_objects_model->GetIcon(item).GetRefData() == m_icon_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")); - - m_objects_ctrl->GetMainWindow()->SetToolTip(tooltip); - } - else - m_objects_ctrl->GetMainWindow()->SetToolTip(""); // hide tooltip -} - -wxPoint get_mouse_position_in_control() { - const wxPoint& pt = wxGetMousePosition(); - wxWindow* win = m_objects_ctrl->GetMainWindow(); - return wxPoint(pt.x - win->GetScreenPosition().x, - pt.y - win->GetScreenPosition().y); -} - -bool is_mouse_position_in_control(wxPoint& pt) { - pt = get_mouse_position_in_control(); - const wxSize& cz = m_objects_ctrl->GetSize(); - if (pt.x > 0 && pt.x < cz.x && - pt.y > 0 && pt.y < cz.y) - return true; - return false; -} - -wxDataViewColumn* object_ctrl_create_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, 2, 60, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); - return column; -} - -void create_objects_ctrl(wxWindow* win, wxBoxSizer*& objects_sz) -{ - m_objects_ctrl = new wxDataViewCtrl(win, wxID_ANY, wxDefaultPosition, wxDefaultSize); - m_objects_ctrl->SetMinSize(wxSize(-1, 150)); // TODO - Set correct height according to the opened/closed objects - - objects_sz = new wxBoxSizer(wxVERTICAL); - objects_sz->Add(m_objects_ctrl, 1, wxGROW | wxLEFT, 20); - - m_objects_model = new PrusaObjectDataViewModel; - m_objects_ctrl->AssociateModel(m_objects_model); -#if wxUSE_DRAG_AND_DROP && wxUSE_UNICODE - m_objects_ctrl->EnableDragSource(wxDF_UNICODETEXT); - m_objects_ctrl->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 - m_objects_ctrl->AppendColumn(new wxDataViewColumn(_(L("Name")), new PrusaBitmapTextRenderer(), - 0, 200, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE)); - - // column 1 of the view control: - m_objects_ctrl->AppendTextColumn(_(L("Copy")), 1, wxDATAVIEW_CELL_INERT, 45, - wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); - - // column 2 of the view control: - m_objects_ctrl->AppendColumn(object_ctrl_create_extruder_column(4)); - - // column 3 of the view control: - m_objects_ctrl->AppendBitmapColumn(" ", 3, wxDATAVIEW_CELL_INERT, 25, - wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); -} - -// ****** from GUI.cpp -wxBoxSizer* create_objects_list(wxWindow *win) -{ - wxBoxSizer* objects_sz; - // create control - create_objects_ctrl(win, objects_sz); - - // describe control behavior - m_objects_ctrl->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [](wxEvent& event) { - object_ctrl_selection_changed(); -#ifndef __WXMSW__ - set_tooltip_for_item(get_mouse_position_in_control()); -#endif //__WXMSW__ - }); - - m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, [](wxDataViewEvent& event) { - object_ctrl_context_menu(); -// event.Skip(); - }); - - m_objects_ctrl->Bind(wxEVT_CHAR, [](wxKeyEvent& event) { object_ctrl_key_event(event); }); // doesn't work on OSX - -#ifdef __WXMSW__ - // Extruder value changed - m_objects_ctrl->Bind(wxEVT_CHOICE, [](wxCommandEvent& event) { update_extruder_in_config(event.GetString()); }); - - m_objects_ctrl->GetMainWindow()->Bind(wxEVT_MOTION, [](wxMouseEvent& event) { - set_tooltip_for_item(event.GetPosition()); - event.Skip(); - }); -#else - // equivalent to wxEVT_CHOICE on __WXMSW__ - m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, [](wxDataViewEvent& event) { object_ctrl_item_value_change(event); }); -#endif //__WXMSW__ - - m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_BEGIN_DRAG, [](wxDataViewEvent& e) {on_begin_drag(e);}); - m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, [](wxDataViewEvent& e) {on_drop_possible(e); }); - m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_DROP, [](wxDataViewEvent& e) {on_drop(e);}); - return objects_sz; -} - -wxBoxSizer* create_edit_object_buttons(wxWindow* win) -{ - auto sizer = new wxBoxSizer(wxVERTICAL); - - auto btn_load_part = new wxButton(win, wxID_ANY, /*Load */"part" + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER/*wxBU_LEFT*/); - auto btn_load_modifier = new wxButton(win, wxID_ANY, /*Load */"modifier" + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER/*wxBU_LEFT*/); - auto btn_load_lambda_modifier = new wxButton(win, wxID_ANY, /*Load */"generic" + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER/*wxBU_LEFT*/); - auto btn_delete = new wxButton(win, wxID_ANY, "Delete"/*" part"*/, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER/*wxBU_LEFT*/); - auto btn_split = new wxButton(win, wxID_ANY, "Split"/*" part"*/, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER/*wxBU_LEFT*/); - m_btn_move_up = new wxButton(win, wxID_ANY, "", wxDefaultPosition, wxDefaultSize/*wxSize(30, -1)*/, wxBU_LEFT); - m_btn_move_down = new wxButton(win, wxID_ANY, "", wxDefaultPosition, wxDefaultSize/*wxSize(30, -1)*/, wxBU_LEFT); - - //*** button's functions - btn_load_part->Bind(wxEVT_BUTTON, [win](wxEvent&) { -// on_btn_load(win); - }); - - btn_load_modifier->Bind(wxEVT_BUTTON, [win](wxEvent&) { -// on_btn_load(win, true); - }); - - btn_load_lambda_modifier->Bind(wxEVT_BUTTON, [win](wxEvent&) { -// on_btn_load(win, true, true); - }); - - btn_delete ->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_del(); }); - btn_split ->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_split(true); }); - m_btn_move_up ->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_move_up(); }); - m_btn_move_down ->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_move_down(); }); - //*** - - m_btn_move_up->SetMinSize(wxSize(20, -1)); - m_btn_move_down->SetMinSize(wxSize(20, -1)); - btn_load_part->SetBitmap(wxBitmap(from_u8(Slic3r::var("brick_add.png")), wxBITMAP_TYPE_PNG)); - btn_load_modifier->SetBitmap(wxBitmap(from_u8(Slic3r::var("brick_add.png")), wxBITMAP_TYPE_PNG)); - btn_load_lambda_modifier->SetBitmap(wxBitmap(from_u8(Slic3r::var("brick_add.png")), wxBITMAP_TYPE_PNG)); - btn_delete->SetBitmap(wxBitmap(from_u8(Slic3r::var("brick_delete.png")), wxBITMAP_TYPE_PNG)); - btn_split->SetBitmap(wxBitmap(from_u8(Slic3r::var("shape_ungroup.png")), wxBITMAP_TYPE_PNG)); - m_btn_move_up->SetBitmap(wxBitmap(from_u8(Slic3r::var("bullet_arrow_up.png")), wxBITMAP_TYPE_PNG)); - m_btn_move_down->SetBitmap(wxBitmap(from_u8(Slic3r::var("bullet_arrow_down.png")), wxBITMAP_TYPE_PNG)); - - m_sizer_object_buttons = new wxGridSizer(1, 3, 0, 0); - m_sizer_object_buttons->Add(btn_load_part, 0, wxEXPAND); - m_sizer_object_buttons->Add(btn_load_modifier, 0, wxEXPAND); - m_sizer_object_buttons->Add(btn_load_lambda_modifier, 0, wxEXPAND); - m_sizer_object_buttons->Show(false); - - m_sizer_part_buttons = new wxGridSizer(1, 3, 0, 0); - m_sizer_part_buttons->Add(btn_delete, 0, wxEXPAND); - m_sizer_part_buttons->Add(btn_split, 0, wxEXPAND); - { - auto up_down_sizer = new wxGridSizer(1, 2, 0, 0); - up_down_sizer->Add(m_btn_move_up, 1, wxEXPAND); - up_down_sizer->Add(m_btn_move_down, 1, wxEXPAND); - m_sizer_part_buttons->Add(up_down_sizer, 0, wxEXPAND); - } - m_sizer_part_buttons->Show(false); - - btn_load_part->SetFont(Slic3r::GUI::small_font()); - btn_load_modifier->SetFont(Slic3r::GUI::small_font()); - btn_load_lambda_modifier->SetFont(Slic3r::GUI::small_font()); - btn_delete->SetFont(Slic3r::GUI::small_font()); - btn_split->SetFont(Slic3r::GUI::small_font()); - m_btn_move_up->SetFont(Slic3r::GUI::small_font()); - m_btn_move_down->SetFont(Slic3r::GUI::small_font()); - - sizer->Add(m_sizer_object_buttons, 0, wxEXPAND | wxLEFT, 20); - sizer->Add(m_sizer_part_buttons, 0, wxEXPAND | wxLEFT, 20); - return sizer; -} - -void update_after_moving() -{ - auto item = m_objects_ctrl->GetSelection(); - if (!item || m_selected_object_id<0) - return; - - auto volume_id = m_objects_model->GetVolumeIdByItem(item); - if (volume_id < 0) - return; - - auto d = m_move_options - m_last_coords; - auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; - volume->mesh.translate(d(0), d(1), d(2)); - m_last_coords = m_move_options; - - m_parts_changed = true; - parts_changed(m_selected_object_id); -} - -wxSizer* object_movers(wxWindow *win) -{ -// DynamicPrintConfig* config = &get_preset_bundle()->/*full_config();//*/printers.get_edited_preset().config; // TODO get config from Model_volume - std::shared_ptr optgroup = std::make_shared(win, "Move"/*, config*/); - optgroup->label_width = 20; - optgroup->m_on_change = [](t_config_option_key opt_key, boost::any value){ - int val = boost::any_cast(value); - bool update = false; - if (opt_key == "x" && m_move_options(0) != val){ - update = true; - m_move_options(0) = val; - } - else if (opt_key == "y" && m_move_options(1) != val){ - update = true; - m_move_options(1) = val; - } - else if (opt_key == "z" && m_move_options(2) != val){ - update = true; - m_move_options(2) = val; - } - if (update) update_after_moving(); - }; - - ConfigOptionDef def; - def.label = L("X"); - def.type = coInt; - def.gui_type = "slider"; - def.default_value = new ConfigOptionInt(0); - - Option option = Option(def, "x"); - option.opt.full_width = true; - optgroup->append_single_option_line(option); - m_mover_x = dynamic_cast(optgroup->get_field("x")->getWindow()); - - def.label = L("Y"); - option = Option(def, "y"); - optgroup->append_single_option_line(option); - m_mover_y = dynamic_cast(optgroup->get_field("y")->getWindow()); - - def.label = L("Z"); - option = Option(def, "z"); - optgroup->append_single_option_line(option); - m_mover_z = dynamic_cast(optgroup->get_field("z")->getWindow()); - - get_optgroups().push_back(optgroup); // ogObjectMovers - - m_sizer_object_movers = optgroup->sizer; - m_sizer_object_movers->Show(false); - - m_move_options = Vec3d(0, 0, 0); - m_last_coords = Vec3d(0, 0, 0); - - return optgroup->sizer; -} - -wxBoxSizer* content_settings(wxWindow *win) -{ - DynamicPrintConfig* config = &get_preset_bundle()->/*full_config();//*/printers.get_edited_preset().config; // TODO get config from Model_volume - std::shared_ptr optgroup = std::make_shared(win, "Extruders", config); - optgroup->label_width = label_width(); - - Option option = optgroup->get_option("extruder"); - option.opt.default_value = new ConfigOptionInt(1); - optgroup->append_single_option_line(option); - - get_optgroups().push_back(optgroup); // ogObjectSettings - - auto sizer = new wxBoxSizer(wxVERTICAL); - sizer->Add(create_edit_object_buttons(win), 0, wxEXPAND, 0); // *** Edit Object Buttons*** - - sizer->Add(optgroup->sizer, 1, wxEXPAND | wxLEFT, 20); - - auto add_btn = new wxButton(win, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); - if (wxMSW) add_btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - add_btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("add.png")), wxBITMAP_TYPE_PNG)); - sizer->Add(add_btn, 0, wxALIGN_LEFT | wxLEFT, 20); - - sizer->Add(object_movers(win), 0, wxEXPAND | wxLEFT, 20); - - return sizer; -} - -void add_objects_list(wxWindow* parent, wxBoxSizer* sizer) -{ - const auto ol_sizer = create_objects_list(parent); - sizer->Add(ol_sizer, 1, wxEXPAND | wxTOP, 20); - set_objects_list_sizer(ol_sizer); -} - -Line add_og_to_object_settings(const std::string& option_name, const std::string& sidetext, int def_value = 0) -{ - 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]() { set_uniform_scaling(btn->IsLocked()); }); - }); - return btn; - }; - } - - ConfigOptionDef def; - def.type = coInt; - def.default_value = new ConfigOptionInt(def_value); - def.width = 55; - - 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; -} - -void add_object_settings(wxWindow* parent, wxBoxSizer* sizer) -{ - auto optgroup = std::make_shared(parent, _(L("Object Settings"))); - optgroup->label_width = 100; - optgroup->set_grid_vgap(5); - - optgroup->m_on_change = [](t_config_option_key opt_key, boost::any value){ - if (opt_key == "scale_unit"){ - const wxString& selection = boost::any_cast(value); - std::vector axes{ "x", "y", "z" }; - for (auto axis : axes) { - std::string key = "scale_" + axis; - get_optgroup(ogFrequentlyObjectSettings)->set_side_text(key, selection); - } - - g_is_percent_scale = selection == _("%"); - update_scale_values(); - } - }; - - 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{ " " }; - optgroup->append_single_option_line(Option(def, "object_name")); - - - // Legend for object modification - auto line = Line{ "", "" }; - def.label = ""; - def.type = coString; - def.width = 55; - - 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); - } - optgroup->append_line(line); - - - // Settings table - optgroup->append_line(add_og_to_object_settings(L("Position"), L("mm"))); - optgroup->append_line(add_og_to_object_settings(L("Rotation"), "°")); - optgroup->append_line(add_og_to_object_settings(L("Scale"), "mm")); - - - 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 }; - optgroup->append_single_option_line(Option(def, "place_on_bed")); - - m_option_sizer = new wxBoxSizer(wxVERTICAL); - optgroup->sizer->Add(m_option_sizer, 1, wxEXPAND | wxLEFT, 5); - - sizer->Add(optgroup->sizer, 0, wxEXPAND | wxLEFT | wxTOP, 20); - - optgroup->disable(); - - get_optgroups().push_back(optgroup); // ogFrequentlyObjectSettings -} - - -// add Collapsible Pane to sizer -wxCollapsiblePane* add_collapsible_pane(wxWindow* parent, wxBoxSizer* sizer_parent, const wxString& name, std::function content_function) -{ -#ifdef __WXMSW__ - auto *collpane = new PrusaCollapsiblePaneMSW(parent, wxID_ANY, name); -#else - auto *collpane = new PrusaCollapsiblePane/*wxCollapsiblePane*/(parent, wxID_ANY, name); -#endif // __WXMSW__ - // add the pane with a zero proportion value to the sizer which contains it - sizer_parent->Add(collpane, 0, wxGROW | wxALL, 0); - - wxWindow *win = collpane->GetPane(); - - wxSizer *sizer = content_function(win); - - wxSizer *sizer_pane = new wxBoxSizer(wxVERTICAL); - sizer_pane->Add(sizer, 1, wxGROW | wxEXPAND | wxBOTTOM, 2); - win->SetSizer(sizer_pane); - // sizer_pane->SetSizeHints(win); - return collpane; -} - -void add_collapsible_panes(wxWindow* parent, wxBoxSizer* sizer) -{ - // *** Objects List *** - auto collpane = add_collapsible_pane(parent, sizer, "Objects List:", create_objects_list); - collpane->Bind(wxEVT_COLLAPSIBLEPANE_CHANGED, ([collpane](wxCommandEvent& e){ - // wxWindowUpdateLocker noUpdates(g_right_panel); - if (collpane->IsCollapsed()) { - m_sizer_object_buttons->Show(false); - m_sizer_part_buttons->Show(false); - m_sizer_object_movers->Show(false); - if (!m_objects_ctrl->HasSelection()) - m_collpane_settings->Show(false); - } - })); - - // *** Object/Part Settings *** - m_collpane_settings = add_collapsible_pane(parent, sizer, "Object Settings", content_settings); -} - -void show_collpane_settings(bool expert_mode) -{ - m_collpane_settings->Show(expert_mode && !m_objects_model->IsEmpty()); -} - -void add_object_to_list(const std::string &name, ModelObject* model_object) -{ - wxString item_name = name; - auto item = m_objects_model->Add(item_name, model_object->instances.size()); - m_objects_ctrl->Select(item); - - // 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) { - const PrusaDataViewBitmapText data(item_name, m_icon_manifold_warning); - wxVariant variant; - variant << data; - m_objects_model->SetValue(variant, item, 0); - } - - if (model_object->volumes.size() > 1) { - for (auto id = 0; id < model_object->volumes.size(); id++) - m_objects_model->AddChild(item, - model_object->volumes[id]->name, - m_icon_solidmesh, - model_object->volumes[id]->config.option("extruder")->value, - false); - m_objects_ctrl->Expand(item); - } - -#ifndef __WXOSX__ - object_ctrl_selection_changed(); -#endif //__WXMSW__ -} - -void delete_object_from_list() -{ - auto item = m_objects_ctrl->GetSelection(); - if (!item || m_objects_model->GetParent(item) != wxDataViewItem(0)) - return; -// m_objects_ctrl->Select(m_objects_model->Delete(item)); - m_objects_model->Delete(item); - - part_selection_changed(); - -// if (m_objects_model->IsEmpty()) -// m_collpane_settings->Show(false); -} - -void delete_all_objects_from_list() -{ - m_objects_model->DeleteAll(); - - part_selection_changed(); -// m_collpane_settings->Show(false); -} - -void set_object_count(int idx, int count) -{ - m_objects_model->SetValue(wxString::Format("%d", count), idx, 1); - m_objects_ctrl->Refresh(); -} - -void unselect_objects() -{ - if (!m_objects_ctrl->GetSelection()) - return; - - g_prevent_list_events = true; - m_objects_ctrl->UnselectAll(); - part_selection_changed(); - g_prevent_list_events = false; -} - -void select_current_object(int idx) -{ - g_prevent_list_events = true; - m_objects_ctrl->UnselectAll(); - if (idx>=0) - m_objects_ctrl->Select(m_objects_model->GetItemById(idx)); - part_selection_changed(); - g_prevent_list_events = false; -} - -void select_current_volume(int idx, int vol_idx) -{ - if (vol_idx < 0) { - select_current_object(idx); - return; - } - g_prevent_list_events = true; - m_objects_ctrl->UnselectAll(); - if (idx >= 0) - m_objects_ctrl->Select(m_objects_model->GetItemByVolumeId(idx, vol_idx)); - part_selection_changed(); - g_prevent_list_events = false; -} - -void remove() -{ - auto item = m_objects_ctrl->GetSelection(); - if (!item) - return; - - if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { - if (m_event_remove_object > 0) { - wxCommandEvent event(m_event_remove_object); - get_main_frame()->ProcessWindowEvent(event); - } -// delete_object_from_list(); - } - else - on_btn_del(); -} - -void object_ctrl_selection_changed() -{ - if (g_prevent_list_events) return; - - part_selection_changed(); - - if (m_event_object_selection_changed > 0) { - wxCommandEvent event(m_event_object_selection_changed); - event.SetId(m_selected_object_id); // set $obj_idx - const wxDataViewItem item = m_objects_ctrl->GetSelection(); - if (!item || m_objects_model->GetParent(item) == wxDataViewItem(0)) - event.SetInt(-1); // set $vol_idx - else { - const int vol_idx = m_objects_model->GetVolumeIdByItem(item); - if (vol_idx == -2) // is settings item - event.SetInt(m_objects_model->GetVolumeIdByItem(m_objects_model->GetParent(item))); // set $vol_idx - else - event.SetInt(vol_idx); - } - get_main_frame()->ProcessWindowEvent(event); - } - -#ifdef __WXOSX__ - update_extruder_in_config(g_selected_extruder); -#endif //__WXOSX__ -} - -void object_ctrl_context_menu() -{ - wxDataViewItem item; - wxDataViewColumn* col; -// printf("object_ctrl_context_menu\n"); - const wxPoint pt = get_mouse_position_in_control(); -// printf("mouse_position_in_control: x = %d, y = %d\n", pt.x, pt.y); - m_objects_ctrl->HitTest(pt, item, col); - if (!item) -#ifdef __WXOSX__ // #ys_FIXME temporary workaround for OSX - // after Yosemite OS X version, HitTest return undefined item - item = m_objects_ctrl->GetSelection(); - if (item) - show_context_menu(); - else - printf("undefined item\n"); - return; -#else - return; -#endif // __WXOSX__ -// printf("item exists\n"); - const wxString title = col->GetTitle(); -// printf("title = *%s*\n", title.data().AsChar()); - - if (title == " ") - show_context_menu(); -// #ys_FIXME -// else if (title == _("Name") && pt.x >15 && -// m_objects_model->GetIcon(item).GetRefData() == m_icon_manifold_warning.GetRefData()) -// { -// if (is_windows10()) -// fix_through_netfabb(); -// } -#ifndef __WXMSW__ - m_objects_ctrl->GetMainWindow()->SetToolTip(""); // hide tooltip -#endif //__WXMSW__ -} - -void object_ctrl_key_event(wxKeyEvent& event) -{ - if (event.GetKeyCode() == WXK_TAB) - m_objects_ctrl->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 - event.Skip(); -} - -void object_ctrl_item_value_change(wxDataViewEvent& event) -{ - if (event.GetColumn() == 2) - { - wxVariant variant; - m_objects_model->GetValue(variant, event.GetItem(), 2); -#ifdef __WXOSX__ - g_selected_extruder = variant.GetString(); -#else // --> for Linux - update_extruder_in_config(variant.GetString()); -#endif //__WXOSX__ - } -} - -void show_manipulation_og(const bool show) -{ - wxGridSizer* grid_sizer = get_optgroup(ogFrequentlyObjectSettings)->get_grid_sizer(); - if (show == grid_sizer->IsShown(2)) - return; - for (size_t id = 2; id < 12; id++) - grid_sizer->Show(id, show); -} - -//update_optgroup -void update_settings_list() -{ -#ifdef __WXGTK__ - auto parent = get_optgroup(ogFrequentlyObjectSettings)->get_parent(); -#else - auto parent = get_optgroup(ogFrequentlyObjectSettings)->parent(); -#endif /* __WXGTK__ */ - -// There is a bug related to Ubuntu overlay scrollbars, see https://github.com/prusa3d/Slic3r/issues/898 and https://github.com/prusa3d/Slic3r/issues/952. -// The issue apparently manifests when Show()ing a window with overlay scrollbars while the UI is frozen. For this reason, -// we will Thaw the UI prematurely on Linux. This means destroing the no_updates object prematurely. -#ifdef __linux__ - std::unique_ptr no_updates(new wxWindowUpdateLocker(parent)); -#else - wxWindowUpdateLocker noUpdates(parent); -#endif - - m_option_sizer->Clear(true); - - bool show_manipulations = true; - const auto item = m_objects_ctrl->GetSelection(); - if (m_config && m_objects_model->IsSettingsItem(item)) - { - auto extra_column = [](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](wxEvent &event){ - (*m_config)->erase(opt_key); - wxTheApp->CallAfter([]() { update_settings_list(); }); - }); - return btn; - }; - - std::map> cat_options; - auto opt_keys = (*m_config)->keys(); - m_og_settings.resize(0); - std::vector categories; - if (!(opt_keys.size() == 1 && opt_keys[0] == "extruder"))// return; - { - auto extruders_cnt = get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA ? 1 : - get_preset_bundle()->printers.get_edited_preset().config.option("nozzle_diameter")->values.size(); - - for (auto& opt_key : opt_keys) { - auto category = (*m_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(parent, cat.first, *m_config, false, ogDEFAULT, extra_column); - optgroup->label_width = 150; - optgroup->sidetext_width = 70; - - 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_option_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0); - m_og_settings.push_back(optgroup); - - categories.push_back(cat.first); - } - } - - if (m_og_settings.empty()) { - m_objects_ctrl->Select(m_objects_model->Delete(item)); - part_selection_changed(); - } - else { - if (!categories.empty()) - m_objects_model->UpdateSettingsDigest(item, categories); - show_manipulations = false; - } - } - - show_manipulation_og(show_manipulations); - show_info_sizer(show_manipulations && item && m_objects_model->GetParent(item) == wxDataViewItem(0)); - -#ifdef __linux__ - no_updates.reset(nullptr); -#endif - - parent->Layout(); - get_right_panel()->GetParent()->Layout(); -} - -void 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 (wxGetMultipleChoices(selections, _(L("Select showing settings")), category_name, names) ==0 ) - 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.get()->option(opt_key)->clone()); - } - - - // Add settings item for object - const auto item = m_objects_ctrl->GetSelection(); - if (item) { - const auto settings_item = m_objects_model->HasSettings(item); - m_objects_ctrl->Select(settings_item ? settings_item : - m_objects_model->AddSettingsChild(item)); -#ifndef __WXOSX__ - part_selection_changed(); -#endif //no __WXOSX__ - } - else - update_settings_list(); -} - -void menu_item_add_generic(wxMenuItem* &menu, int id) { - auto sub_menu = new wxMenu; - - 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](wxEvent &event) { - load_lambda(sub_menu->GetLabel(event.GetId()).ToStdString()); - }); -#endif //no __WXMSW__ - - menu->SetSubMenu(sub_menu); -} - -wxMenuItem* 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* 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 *create_add_part_popupmenu() -{ - wxMenu *menu = new wxMenu; - std::vector menu_items = { L("Add part"), L("Add modifier"), L("Add generic") }; - - wxWindowID config_id_base = wxWindow::NewControlId(menu_items.size()+4+2); - - int i = 0; - for (auto& item : menu_items) { - auto menu_item = new wxMenuItem(menu, config_id_base + i, _(item)); - menu_item->SetBitmap(i == 0 ? m_icon_solidmesh : m_icon_modifiermesh); - if (item == "Add generic") - menu_item_add_generic(menu_item, config_id_base + i); - menu->Append(menu_item); - i++; - } - - menu->AppendSeparator(); - auto menu_item = menu_item_split(menu, config_id_base + i + 4); - menu->Append(menu_item); - menu_item->Enable(is_splittable_object(false)); - - menu->AppendSeparator(); - // Append settings popupmenu - menu->Append(menu_item_settings(menu, config_id_base + i + 5, false)); - - menu->Bind(wxEVT_MENU, [config_id_base, menu](wxEvent &event){ - switch (event.GetId() - config_id_base) { - case 0: - on_btn_load(); - break; - case 1: - on_btn_load(true); - break; - case 2: -// on_btn_load(true, true); - break; - case 3: - case 4: - case 5: - case 6: -#ifdef __WXMSW__ - load_lambda(menu->GetLabel(event.GetId()).ToStdString()); -#endif // __WXMSW__ - break; - case 7: //3: - on_btn_split(false); - break; - default: -#ifdef __WXMSW__ - get_settings_choice(menu, event.GetId(), false); -#endif // __WXMSW__ - break; - } - }); - - return menu; -} - -wxMenu *create_part_settings_popupmenu() -{ - wxMenu *menu = new wxMenu; - wxWindowID config_id_base = wxWindow::NewControlId(2); - - auto menu_item = menu_item_split(menu, config_id_base); - menu->Append(menu_item); - menu_item->Enable(is_splittable_object(true)); - - menu->AppendSeparator(); - // Append settings popupmenu - menu->Append(menu_item_settings(menu, config_id_base + 1, true)); - - menu->Bind(wxEVT_MENU, [config_id_base, menu](wxEvent &event){ - switch (event.GetId() - config_id_base) { - case 0: - on_btn_split(true); - break; - default:{ - get_settings_choice(menu, event.GetId(), true); - break; } - } - }); - - return menu; -} - -wxMenu *create_add_settings_popupmenu(bool is_part) -{ - wxMenu *menu = new wxMenu; - - auto categories = get_category_icon(); - - 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(categories.find(cat.first) == categories.end() ? - wxNullBitmap : categories.at(cat.first)); - menu->Append(menu_item); - } -#ifndef __WXMSW__ - menu->Bind(wxEVT_MENU, [menu,is_part](wxEvent &event) { - get_settings_choice(menu, event.GetId(), is_part); - }); -#endif //no __WXMSW__ - return menu; -} - -void show_context_menu() -{ - const auto item = m_objects_ctrl->GetSelection(); - if (item) - { - if (m_objects_model->IsSettingsItem(item)) - return; - const auto menu = m_objects_model->GetParent(item) == wxDataViewItem(0) ? - create_add_part_popupmenu() : - create_part_settings_popupmenu(); - get_tab_panel()->GetPage(0)->PopupMenu(menu); - } -} - -// ****** - -void load_part( ModelObject* model_object, - wxArrayString& part_names, const bool is_modifier) -{ - wxWindow* parent = get_tab_panel()->GetPage(0); - - wxArrayString input_files; - open_model(parent, input_files); - 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(); - delta = model_object->origin_translation - object->origin_translation; - } - for (auto volume : object->volumes) { - auto new_volume = model_object->add_volume(*volume); - new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); - boost::filesystem::path(input_file).filename().string(); - new_volume->name = boost::filesystem::path(input_file).filename().string(); - - part_names.Add(new_volume->name); - - if (delta != Vec3d::Zero()) - { - new_volume->mesh.translate((float)delta(0), (float)delta(1), (float)delta(2)); - new_volume->get_convex_hull().translate((float)delta(0), (float)delta(1), (float)delta(2)); - } - - // 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 load_lambda( ModelObject* model_object, - wxArrayString& part_names, const bool is_modifier) -{ - auto dlg = new LambdaObjectDialog(m_objects_ctrl->GetMainWindow()); - if (dlg->ShowModal() == wxID_CANCEL) { - return; - } - - std::string name = "lambda-"; - TriangleMesh mesh; - - auto params = dlg->ObjectParameters(); - switch (params.type) - { - case LambdaTypeBox:{ - mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]); - name += "Box"; - break;} - case LambdaTypeCylinder:{ - mesh = make_cylinder(params.cyl_r, params.cyl_h); - name += "Cylinder"; - break;} - case LambdaTypeSphere:{ - mesh = make_sphere(params.sph_rho); - name += "Sphere"; - break;} - case LambdaTypeSlab:{ - const auto& size = model_object->bounding_box().size(); - mesh = make_cube(size(0)*1.5, size(1)*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(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z); - name += "Slab"; - break; } - default: - break; - } - mesh.repair(); - - auto new_volume = model_object->add_volume(mesh); - new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); - - 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)); - - part_names.Add(name); - - m_parts_changed = true; -} - -void load_lambda(const std::string& type_name) -{ - if (m_selected_object_id < 0) return; - - auto dlg = new LambdaObjectDialog(m_objects_ctrl->GetMainWindow(), type_name); - if (dlg->ShowModal() == wxID_CANCEL) - return; - - const std::string name = "lambda-"+type_name; - TriangleMesh mesh; - - const auto params = dlg->ObjectParameters(); - if (type_name == _("Box")) - mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]); - else if (type_name == _("Cylinder")) - mesh = make_cylinder(params.cyl_r, params.cyl_h); - else if (type_name == _("Sphere")) - mesh = make_sphere(params.sph_rho); - else if (type_name == _("Slab")){ - const auto& size = (*m_objects)[m_selected_object_id]->bounding_box().size(); - mesh = make_cube(size(0)*1.5, size(1)*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(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z); - } - mesh.repair(); - - auto new_volume = (*m_objects)[m_selected_object_id]->add_volume(mesh); - new_volume->set_type(ModelVolume::PARAMETER_MODIFIER); - - 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(m_selected_object_id); - - m_objects_ctrl->Select(m_objects_model->AddChild(m_objects_ctrl->GetSelection(), - name, m_icon_modifiermesh)); -#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME - object_ctrl_selection_changed(); -#endif //no __WXOSX__ //__WXMSW__ -} - -void on_btn_load(bool is_modifier /*= false*/, bool is_lambda/* = false*/) -{ - auto item = m_objects_ctrl->GetSelection(); - if (!item) - return; - int obj_idx = -1; - if (m_objects_model->GetParent(item) == wxDataViewItem(0)) - obj_idx = m_objects_model->GetIdByItem(item); - else - return; - - if (obj_idx < 0) return; - wxArrayString part_names; - if (is_lambda) - load_lambda((*m_objects)[obj_idx], part_names, is_modifier); - else - load_part((*m_objects)[obj_idx], part_names, is_modifier); - - parts_changed(obj_idx); - - for (int i = 0; i < part_names.size(); ++i) - m_objects_ctrl->Select( m_objects_model->AddChild(item, part_names.Item(i), - is_modifier ? m_icon_modifiermesh : m_icon_solidmesh)); -#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME - object_ctrl_selection_changed(); -#endif //no __WXOSX__//__WXMSW__ -} - -void remove_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)); -} - -bool remove_subobject_from_object(const int volume_id) -{ - const auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; - - // if user is deleting the last solid part, throw error - int solid_cnt = 0; - for (auto vol : (*m_objects)[m_selected_object_id]->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 this object."))); - return false; - } - - (*m_objects)[m_selected_object_id]->delete_volume(volume_id); - m_parts_changed = true; - - parts_changed(m_selected_object_id); - return true; -} - -void on_btn_del() -{ - auto item = m_objects_ctrl->GetSelection(); - if (!item) return; - - const auto volume_id = m_objects_model->GetVolumeIdByItem(item); - if (volume_id ==-1) - return; - - if (volume_id ==-2) - remove_settings_from_config(); - else if (!remove_subobject_from_object(volume_id)) - return; - - m_objects_ctrl->Select(m_objects_model->Delete(item)); - part_selection_changed(); -} - -bool get_volume_by_item(const bool split_part, const wxDataViewItem& item, ModelVolume*& volume) -{ - if (!item || m_selected_object_id < 0) - return false; - const auto volume_id = m_objects_model->GetVolumeIdByItem(item); - if (volume_id < 0) { - if (split_part) return false; - volume = (*m_objects)[m_selected_object_id]->volumes[0]; - } - else - volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; - if (volume) - return true; - return false; -} - -bool is_splittable_object(const bool split_part) -{ - const wxDataViewItem item = m_objects_ctrl->GetSelection(); - if (!item) return false; - - wxDataViewItemArray children; - if (!split_part && m_objects_model->GetChildren(item, children) > 0) - 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 on_btn_split(const bool split_part) -{ - const auto item = m_objects_ctrl->GetSelection(); - if (!item || m_selected_object_id<0) - return; - ModelVolume* volume; - if (!get_volume_by_item(split_part, item, volume)) return; - DynamicPrintConfig& config = get_preset_bundle()->printers.get_edited_preset().config; - const auto nozzle_dmrs_cnt = config.option("nozzle_diameter")->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)[m_selected_object_id]; - - if (split_part) { - auto parent = m_objects_model->GetParent(item); - m_objects_model->DeleteChildren(parent); - - for (auto id = 0; id < model_object->volumes.size(); id++) - m_objects_model->AddChild(parent, model_object->volumes[id]->name, - model_object->volumes[id]->is_modifier() ? m_icon_modifiermesh : m_icon_solidmesh, - model_object->volumes[id]->config.has("extruder") ? - model_object->volumes[id]->config.option("extruder")->value : 0, - false); - - m_objects_ctrl->Expand(parent); - } - else { - for (auto id = 0; id < model_object->volumes.size(); id++) - m_objects_model->AddChild(item, model_object->volumes[id]->name, - m_icon_solidmesh, - model_object->volumes[id]->config.has("extruder") ? - model_object->volumes[id]->config.option("extruder")->value : 0, - false); - m_objects_ctrl->Expand(item); - } - - m_parts_changed = true; - parts_changed(m_selected_object_id); -} - -void on_btn_move_up(){ - auto item = m_objects_ctrl->GetSelection(); - if (!item) - return; - auto volume_id = m_objects_model->GetVolumeIdByItem(item); - if (volume_id < 0) - return; - auto& volumes = (*m_objects)[m_selected_object_id]->volumes; - if (0 < volume_id && volume_id < volumes.size()) { - std::swap(volumes[volume_id - 1], volumes[volume_id]); - m_parts_changed = true; - m_objects_ctrl->Select(m_objects_model->MoveChildUp(item)); - part_selection_changed(); -// #ifdef __WXMSW__ -// object_ctrl_selection_changed(); -// #endif //__WXMSW__ - } -} - -void on_btn_move_down(){ - auto item = m_objects_ctrl->GetSelection(); - if (!item) - return; - auto volume_id = m_objects_model->GetVolumeIdByItem(item); - if (volume_id < 0) - return; - auto& volumes = (*m_objects)[m_selected_object_id]->volumes; - if (0 <= volume_id && volume_id+1 < volumes.size()) { - std::swap(volumes[volume_id + 1], volumes[volume_id]); - m_parts_changed = true; - m_objects_ctrl->Select(m_objects_model->MoveChildDown(item)); - part_selection_changed(); -// #ifdef __WXMSW__ -// object_ctrl_selection_changed(); -// #endif //__WXMSW__ - } -} - -void parts_changed(int obj_idx) -{ - if (m_event_object_settings_changed <= 0) return; - - wxCommandEvent e(m_event_object_settings_changed); - auto event_str = wxString::Format("%d %d %d", obj_idx, - is_parts_changed() ? 1 : 0, - is_part_settings_changed() ? 1 : 0); - e.SetString(event_str); - get_main_frame()->ProcessWindowEvent(e); -} - -void update_settings_value() -{ - auto og = get_optgroup(ogFrequentlyObjectSettings); - if (m_selected_object_id < 0 || m_objects->size() <= m_selected_object_id) { - og->set_value("position_x", 0); - og->set_value("position_y", 0); - og->set_value("position_z", 0); - og->set_value("scale_x", 0); - og->set_value("scale_y", 0); - og->set_value("scale_z", 0); - og->set_value("rotation_x", 0); - og->set_value("rotation_y", 0); - og->set_value("rotation_z", 0); - og->disable(); - return; - } - g_is_percent_scale = boost::any_cast(og->get_value("scale_unit")) == _("%"); - update_position_values(); - update_scale_values(); - update_rotation_values(); - og->enable(); -} - -void part_selection_changed() -{ - auto item = m_objects_ctrl->GetSelection(); - int obj_idx = -1; - auto og = get_optgroup(ogFrequentlyObjectSettings); - m_config = nullptr; - wxString object_name = wxEmptyString; - if (item) - { - const bool is_settings_item = m_objects_model->IsSettingsItem(item); - bool is_part = false; - wxString og_name = wxEmptyString; - if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { - obj_idx = m_objects_model->GetIdByItem(item); - og_name = _(L("Object manipulation")); - m_config = std::make_shared(&(*m_objects)[obj_idx]->config); - } - 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 (is_settings_item) { - if (m_objects_model->GetParent(parent) == wxDataViewItem(0)) { - og_name = _(L("Object Settings to modify")); - m_config = std::make_shared(&(*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 = std::make_shared(&(*m_objects)[obj_idx]->volumes[volume_id]->config); - } - } - else { - og_name = _(L("Part manipulation")); - is_part = true; - const auto volume_id = m_objects_model->GetVolumeIdByItem(item); - m_config = std::make_shared(&(*m_objects)[obj_idx]->volumes[volume_id]->config); - } - } - - og->set_name(" " + og_name + " "); - object_name = m_objects_model->GetName(item); - m_default_config = std::make_shared(*DynamicPrintConfig::new_from_defaults_keys(get_options(is_part))); - } - og->set_value("object_name", object_name); - - update_settings_list(); - - m_selected_object_id = obj_idx; - - update_settings_value(); - -/* wxWindowUpdateLocker noUpdates(get_right_panel()); - - m_move_options = Point3(0, 0, 0); - m_last_coords = Point3(0, 0, 0); - // reset move sliders - std::vector opt_keys = {"x", "y", "z"}; - auto og = get_optgroup(ogObjectMovers); - for (auto opt_key: opt_keys) - og->set_value(opt_key, int(0)); - -// if (!item || m_selected_object_id < 0){ - if (m_selected_object_id < 0){ - m_sizer_object_buttons->Show(false); - m_sizer_part_buttons->Show(false); - m_sizer_object_movers->Show(false); - m_collpane_settings->Show(false); - return; - } - - m_collpane_settings->Show(true); - - auto volume_id = m_objects_model->GetVolumeIdByItem(item); - if (volume_id < 0){ - m_sizer_object_buttons->Show(true); - m_sizer_part_buttons->Show(false); - m_sizer_object_movers->Show(false); - m_collpane_settings->SetLabelText(_(L("Object Settings")) + ":"); - -// 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; -// } - - return; - } - - m_collpane_settings->SetLabelText(_(L("Part Settings")) + ":"); - - m_sizer_object_buttons->Show(false); - m_sizer_part_buttons->Show(true); - m_sizer_object_movers->Show(true); - - auto bb_size = m_objects[m_selected_object_id]->bounding_box().size(); - int scale = 10; //?? - - m_mover_x->SetMin(-bb_size.x * 4 * scale); - m_mover_x->SetMax(bb_size.x * 4 * scale); - - m_mover_y->SetMin(-bb_size.y * 4 * scale); - m_mover_y->SetMax(bb_size.y * 4 * scale); - - m_mover_z->SetMin(-bb_size.z * 4 * scale); - m_mover_z->SetMax(bb_size.z * 4 * scale); - - - -// my ($config, @opt_keys); - m_btn_move_up->Enable(volume_id > 0); - m_btn_move_down->Enable(volume_id + 1 < m_objects[m_selected_object_id]->volumes.size()); - - // attach volume config to settings panel - auto volume = m_objects[m_selected_object_id]->volumes[volume_id]; - - if (volume->modifier) - og->enable(); - else - og->disable(); - -// auto config = volume->config; - - // get default values -// @opt_keys = @{Slic3r::Config::PrintRegion->new->get_keys}; -// } -/* - # get default values - my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys); - - # append default extruder - push @opt_keys, 'extruder'; - $default_config->set('extruder', 0); - $config->set_ifndef('extruder', 0); - $self->{settings_panel}->set_default_config($default_config); - $self->{settings_panel}->set_config($config); - $self->{settings_panel}->set_opt_keys(\@opt_keys); - $self->{settings_panel}->set_fixed_options([qw(extruder)]); - $self->{settings_panel}->enable; - } - */ -} - -void set_extruder_column_hidden(bool hide) -{ - m_objects_ctrl->GetColumn(2)->SetHidden(hide); -} - -void 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)); - - if (m_event_update_scene > 0) { - wxCommandEvent e(m_event_update_scene); - get_main_frame()->ProcessWindowEvent(e); - } -} - -void update_scale_values() -{ - auto og = get_optgroup(ogFrequentlyObjectSettings); - auto instance = (*m_objects)[m_selected_object_id]->instances.front(); - auto size = (*m_objects)[m_selected_object_id]->instance_bounding_box(0).size(); - -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - if (g_is_percent_scale) { - og->set_value("scale_x", int(instance->get_scaling_factor(X) * 100)); - og->set_value("scale_y", int(instance->get_scaling_factor(Y) * 100)); - og->set_value("scale_z", int(instance->get_scaling_factor(Z) * 100)); - } - else { - og->set_value("scale_x", int(instance->get_scaling_factor(X) * size(0) + 0.5)); - og->set_value("scale_y", int(instance->get_scaling_factor(Y) * size(1) + 0.5)); - og->set_value("scale_z", int(instance->get_scaling_factor(Z) * size(2) + 0.5)); - } -#else - if (g_is_percent_scale) { - auto scale = instance->scaling_factor * 100.0; - og->set_value("scale_x", int(scale)); - og->set_value("scale_y", int(scale)); - og->set_value("scale_z", int(scale)); - } - else { - og->set_value("scale_x", int(instance->scaling_factor * size(0) + 0.5)); - og->set_value("scale_y", int(instance->scaling_factor * size(1) + 0.5)); - og->set_value("scale_z", int(instance->scaling_factor * size(2) + 0.5)); - } -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -} - -void update_position_values() -{ - auto og = get_optgroup(ogFrequentlyObjectSettings); - auto instance = (*m_objects)[m_selected_object_id]->instances.front(); - -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - og->set_value("position_x", int(instance->get_offset(X))); - og->set_value("position_y", int(instance->get_offset(Y))); - og->set_value("position_z", int(instance->get_offset(Z))); -#else - og->set_value("position_x", int(instance->offset(0))); - og->set_value("position_y", int(instance->offset(1))); - og->set_value("position_z", 0); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -} - -void update_position_values(const Vec3d& position) -{ - auto og = get_optgroup(ogFrequentlyObjectSettings); - - og->set_value("position_x", int(position(0))); - og->set_value("position_y", int(position(1))); - og->set_value("position_z", int(position(2))); -} - -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -void update_scale_values(const Vec3d& scaling_factor) -{ - auto og = get_optgroup(ogFrequentlyObjectSettings); - - // 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 (!g_is_percent_scale) - og->set_value("scale_unit", _("%")); - - auto scale = scaling_factor * 100.0; - og->set_value("scale_x", int(scale(0))); - og->set_value("scale_y", int(scale(1))); - og->set_value("scale_z", int(scale(2))); -} -#else -void update_scale_values(double scaling_factor) -{ - auto og = get_optgroup(ogFrequentlyObjectSettings); - - // 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 (!g_is_percent_scale) - og->set_value("scale_unit", _("%")); - - auto scale = scaling_factor * 100.0; - og->set_value("scale_x", int(scale)); - og->set_value("scale_y", int(scale)); - og->set_value("scale_z", int(scale)); -} -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - -void update_rotation_values() -{ -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - update_rotation_value((*m_objects)[m_selected_object_id]->instances.front()->get_rotation()); -#else - auto og = get_optgroup(ogFrequentlyObjectSettings); - auto instance = (*m_objects)[m_selected_object_id]->instances.front(); - og->set_value("rotation_x", 0); - og->set_value("rotation_y", 0); - og->set_value("rotation_z", int(Geometry::rad2deg(instance->rotation))); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -} - -void update_rotation_value(double angle, Axis axis) -{ - auto og = get_optgroup(ogFrequentlyObjectSettings); - - 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; - } - } - - og->set_value(axis_str, round_nearest(int(Geometry::rad2deg(angle)), 0)); -} - -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -void update_rotation_value(const Vec3d& rotation) -{ - auto og = get_optgroup(ogFrequentlyObjectSettings); - og->set_value("rotation_x", int(round_nearest(Geometry::rad2deg(rotation(0)), 0))); - og->set_value("rotation_y", int(round_nearest(Geometry::rad2deg(rotation(1)), 0))); - og->set_value("rotation_z", int(round_nearest(Geometry::rad2deg(rotation(2)), 0))); -} -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM - -void set_uniform_scaling(const bool uniform_scale) -{ - g_is_uniform_scale = uniform_scale; -} - -void on_begin_drag(wxDataViewEvent &event) -{ - wxDataViewItem item(event.GetItem()); - - // only allow drags for item, not containers - if (m_objects_model->GetParent(item) == wxDataViewItem(0) || m_objects_model->IsSettingsItem(item)) { - 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 - **/ - g_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 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->IsSettingsItem(item)) - event.Veto(); -} - -void on_drop(wxDataViewEvent &event) -{ - wxDataViewItem item(event.GetItem()); - - // only allow drops for item, not containers - if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) || - event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->IsSettingsItem(item)) { - 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__ - - m_objects_ctrl->Select(m_objects_model->ReorganizeChildren(from_volume_id, to_volume_id, - m_objects_model->GetParent(item))); - - 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]); - - m_parts_changed = true; - parts_changed(m_selected_object_id); - - g_prevent_list_events = false; -} - -void update_objects_list_extruder_column(int extruders_count) -{ - if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA) - extruders_count = 1; - - // delete old 3rd column - m_objects_ctrl->DeleteColumn(m_objects_ctrl->GetColumn(2)); - // insert new created 3rd column - m_objects_ctrl->InsertColumn(2, object_ctrl_create_extruder_column(extruders_count)); - // set show/hide for this column - set_extruder_column_hidden(extruders_count <= 1); -} - -void create_double_slider(wxWindow* parent, wxBoxSizer* sizer, wxGLCanvas* canvas) -{ - m_slider = new PrusaDoubleSlider(parent, wxID_ANY, 0, 0, 0, 100); - sizer->Add(m_slider, 0, wxEXPAND, 0); - - m_preview_canvas = canvas; - m_preview_canvas->Bind(wxEVT_KEY_DOWN, update_double_slider_from_canvas); - - m_slider->Bind(wxEVT_SCROLL_CHANGED, [parent](wxEvent& event) { - _3DScene::set_toolpaths_range(m_preview_canvas, m_slider->GetLowerValueD() - 1e-6, m_slider->GetHigherValueD() + 1e-6); - if (parent->IsShown()) - m_preview_canvas->Refresh(); - }); -} - -void fill_slider_values(std::vector> &values, - const std::vector &layers_z) -{ - std::vector layers_all_z = _3DScene::get_current_print_zs(m_preview_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; - } - } -} - -void 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 update_double_slider(bool force_sliders_full_range) -{ - std::vector> values; - std::vector layers_z = _3DScene::get_current_print_zs(m_preview_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); - - set_double_slider_thumbs(force_sliders_full_range, layers_z, z_low, z_high); -} - -void reset_double_slider() -{ - m_slider->SetHigherValue(0); - m_slider->SetLowerValue(0); -} - -void 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(); -} - -void show_manipulation_sizer(const bool is_simple_mode) -{ - auto item = m_objects_ctrl->GetSelection(); - if (!item || !is_simple_mode) - return; - - if (m_objects_model->IsSettingsItem(item)) { - m_objects_ctrl->Select(m_objects_model->GetParent(item)); - part_selection_changed(); - } -} - -} //namespace GUI -} //namespace Slic3r \ No newline at end of file diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.hpp b/xs/src/slic3r/GUI/GUI_ObjectParts.hpp deleted file mode 100644 index 8189238f02..0000000000 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.hpp +++ /dev/null @@ -1,154 +0,0 @@ -#ifndef slic3r_GUI_ObjectParts_hpp_ -#define slic3r_GUI_ObjectParts_hpp_ - -class wxWindow; -class wxSizer; -class wxBoxSizer; -class wxString; -class wxArrayString; -class wxMenu; -class wxDataViewEvent; -class wxKeyEvent; -class wxGLCanvas; -class wxBitmap; - -namespace Slic3r { -class ModelObject; -class Model; - -namespace GUI { -//class wxGLCanvas; - -enum ogGroup{ - ogFrequentlyChangingParameters, - ogFrequentlyObjectSettings, - ogCurrentSettings -// ogObjectSettings, -// ogObjectMovers, -// ogPartSettings -}; - -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; -}; - -typedef std::map t_category_icon; -inline t_category_icon& get_category_icon(); - -void add_collapsible_panes(wxWindow* parent, wxBoxSizer* sizer); -void add_objects_list(wxWindow* parent, wxBoxSizer* sizer); -void add_object_settings(wxWindow* parent, wxBoxSizer* sizer); -void show_collpane_settings(bool expert_mode); - -wxMenu *create_add_settings_popupmenu(bool is_part); -wxMenu *create_add_part_popupmenu(); -wxMenu *create_part_settings_popupmenu(); - -// Add object to the list -//void add_object(const std::string &name); -void add_object_to_list(const std::string &name, ModelObject* model_object); -// Delete object from the list -void delete_object_from_list(); -// Delete all objects from the list -void delete_all_objects_from_list(); -// Set count of object on c++ side -void set_object_count(int idx, int count); -// 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 object_ctrl_selection_changed(); -void object_ctrl_context_menu(); -void object_ctrl_key_event(wxKeyEvent& event); -void object_ctrl_item_value_change(wxDataViewEvent& event); -void show_context_menu(); -bool is_splittable_object(const bool split_part); - -void init_mesh_icons(); -void set_event_object_selection_changed(const int& event); -void set_event_object_settings_changed(const int& event); -void set_event_remove_object(const int& event); -void set_event_update_scene(const int& event); -void set_objects_from_model(Model &model); - -bool is_parts_changed(); -bool is_part_settings_changed(); - -void load_part( ModelObject* model_object, - wxArrayString& part_names, const bool is_modifier); - -void load_lambda( ModelObject* model_object, - wxArrayString& part_names, const bool is_modifier); -void load_lambda( const std::string& type_name); - -void on_btn_load(bool is_modifier = false, bool is_lambda = false); -void on_btn_del(); -void on_btn_split(const bool split_part); -void on_btn_move_up(); -void on_btn_move_down(); - -void parts_changed(int obj_idx); -void part_selection_changed(); - -void update_settings_value(); -// 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); -// update position values displacements or "gizmos" -void update_position_values(); -void update_position_values(const Vec3d& position); -// update scale values after scale unit changing or "gizmos" -void update_scale_values(); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -void update_scale_values(const Vec3d& scaling_factor); -#else -void update_scale_values(double scaling_factor); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -// update rotation values object selection changing -void update_rotation_values(); -// update rotation value after "gizmos" -void update_rotation_value(double angle, Axis axis); -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -void update_rotation_value(const Vec3d& rotation); -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM -void set_uniform_scaling(const bool uniform_scale); - -void on_begin_drag(wxDataViewEvent &event); -void on_drop_possible(wxDataViewEvent &event); -void on_drop(wxDataViewEvent &event); - -// update extruder column for objects_ctrl according to extruders count -void update_objects_list_extruder_column(int extruders_count); - -// Create/Update/Reset double slider on 3dPreview -void create_double_slider(wxWindow* parent, wxBoxSizer* sizer, wxGLCanvas* canvas); -void update_double_slider(bool force_sliders_full_range); -void reset_double_slider(); -// update DoubleSlider after keyDown in canvas -void update_double_slider_from_canvas(wxKeyEvent& event); - -void show_manipulation_sizer(const bool is_simple_mode); - -} //namespace GUI -} //namespace Slic3r -#endif //slic3r_GUI_ObjectParts_hpp_ \ No newline at end of file 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/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/Model.xsp b/xs/xsp/Model.xsp index 58e69693b7..180e052fff 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -21,21 +21,12 @@ %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; %}; @@ -122,67 +113,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 { @@ -331,7 +262,7 @@ 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() @@ -364,7 +295,6 @@ ModelMaterial::attributes() Ref object() %code%{ RETVAL = THIS->get_object(); %}; -#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM Vec3d* rotation() %code%{ RETVAL = new Vec3d(THIS->get_rotation(X), THIS->get_rotation(Y), THIS->get_rotation(Z)); %}; @@ -391,25 +321,6 @@ ModelMaterial::attributes() THIS->set_offset(X, (*offset)(0)); THIS->set_offset(Y, (*offset)(1)); %}; -#else - double rotation() - %code%{ RETVAL = THIS->rotation; %}; - - double scaling_factor() - %code%{ RETVAL = THIS->scaling_factor; %}; - - Ref offset() - %code%{ RETVAL = &THIS->offset; %}; - - void set_rotation(double val) - %code%{ THIS->rotation = val; 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(Vec2d *offset) - %code%{ THIS->offset = *offset; %}; -#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; void transform_polygon(Polygon* polygon) const; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 0424d8b7b2..b34444b231 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -185,14 +185,6 @@ _constant() } %}; - void export_gcode_with_preview_data(char *path_template, GCodePreviewData *preview_data) %code%{ - try { - THIS->export_gcode(path_template, preview_data); - } catch (std::exception& e) { - croak(e.what()); - } - %}; - void export_gcode(char *path_template) %code%{ try { THIS->export_gcode(path_template, nullptr);