Compare commits
103
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3283e29b7 | ||
|
|
261fec1d05 | ||
|
|
ab6229c77a | ||
|
|
b1fe6ba49c | ||
|
|
8d940367bf | ||
|
|
fb13b11a19 | ||
|
|
7c58cb041d | ||
|
|
10e95bb93b | ||
|
|
00100b60db | ||
|
|
3f8c5426b8 | ||
|
|
fd16e24391 | ||
|
|
44785345e5 | ||
|
|
71676a111e | ||
|
|
e5e7d8a76a | ||
|
|
10002b6cf1 | ||
|
|
418dd43cfe | ||
|
|
9162fb7d79 | ||
|
|
dd38f82afd | ||
|
|
215368a07b | ||
|
|
17787c86d6 | ||
|
|
f3f7879364 | ||
|
|
e2a345a43d | ||
|
|
e45a7e0868 | ||
|
|
c410e80608 | ||
|
|
7765697eb6 | ||
|
|
83922780e1 | ||
|
|
02e0a1ba83 | ||
|
|
97fb89c955 | ||
|
|
62fe771ae1 | ||
|
|
a183e28109 | ||
|
|
a69c8c96f3 | ||
|
|
724ff6b05a | ||
|
|
3cf08b7f68 | ||
|
|
5ded7da086 | ||
|
|
2cc372f70d | ||
|
|
b1bd5b1216 | ||
|
|
598b0b194a | ||
|
|
4de0a0569a | ||
|
|
afafc7cf5a | ||
|
|
514271af51 | ||
|
|
282388c68a | ||
|
|
7b6cc27284 | ||
|
|
a05865e265 | ||
|
|
1d007f4252 | ||
|
|
3374c1ad71 | ||
|
|
90464ffbc3 | ||
|
|
7e5512ce71 | ||
|
|
46f0860c18 | ||
|
|
67b406d60d | ||
|
|
c01718d0b4 | ||
|
|
78015d4da1 | ||
|
|
4a91b88f81 | ||
|
|
3e3c96d0fd | ||
|
|
ee7a7a0aa1 | ||
|
|
70dad85a27 | ||
|
|
610a495be4 | ||
|
|
f1981ff873 | ||
|
|
83dbca4ffd | ||
|
|
a3b0fe4a20 | ||
|
|
50ac379c53 | ||
|
|
dcd1d45d4f | ||
|
|
e4b8bfb28d | ||
|
|
e9c9c5d228 | ||
|
|
bd85c8998f | ||
|
|
af06a10939 | ||
|
|
2f625e66fd | ||
|
|
d91f4edeb0 | ||
|
|
1284a39f13 | ||
|
|
0c77359c89 | ||
|
|
87a550af22 | ||
|
|
574ab1a3a8 | ||
|
|
d6db1cf8c2 | ||
|
|
3ea7f94809 | ||
|
|
3370a3e9ca | ||
|
|
33ed4e010b | ||
|
|
b4406f2397 | ||
|
|
fda700937b | ||
|
|
30019acb17 | ||
|
|
2869f98629 | ||
|
|
04f06d7837 | ||
|
|
91f6c503f6 | ||
|
|
639378c1f4 | ||
|
|
dbecea2bc4 | ||
|
|
238a607032 | ||
|
|
4fff4670d3 | ||
|
|
1c3d487d8e | ||
|
|
c35f0fee39 | ||
|
|
4498aa8dfc | ||
|
|
e83a560347 | ||
|
|
322db89acd | ||
|
|
d444bb173f | ||
|
|
7174a7ac94 | ||
|
|
6f1571bacd | ||
|
|
85db4b17e3 | ||
|
|
142fd6026a | ||
|
|
c98c375270 | ||
|
|
fc42e420cd | ||
|
|
a95612e2c9 | ||
|
|
5c2aa0bed8 | ||
|
|
068a28f431 | ||
|
|
6a8e81f800 | ||
|
|
92f790b48a | ||
|
|
3479ca7ab1 |
@@ -21,5 +21,6 @@ assignees: ''
|
||||
<!-- Check all that apply (change to `[x]`) -->
|
||||
|
||||
- [ ] Windows
|
||||
- [ ] macOS
|
||||
- [ ] macOS Intel
|
||||
- [ ] macOS Arm (e.g., M1, M2)
|
||||
- [ ] Linux
|
||||
|
||||
@@ -20,19 +20,14 @@ jobs:
|
||||
####################
|
||||
|
||||
Unix:
|
||||
name: ${{ matrix.name }} (${{ matrix.config }}, ${{ fromJSON('["HeaderOnly", "Static"]')[matrix.static == 'ON'] }})
|
||||
name: ${{ matrix.os }} ${{ fromJSON('["Header-Only", "Static"]')[matrix.build-params.static == 'ON'] }} ${{ matrix.build-params.tutorials == 'ON' && 'tutorial' || ''}} ${{ matrix.build-params.tests == 'ON' && 'tests' || ''}} ${{ matrix.config }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-latest]
|
||||
config: [Release]
|
||||
static: [ON, OFF]
|
||||
include:
|
||||
- os: macos-latest
|
||||
name: macOS
|
||||
- os: ubuntu-20.04
|
||||
name: Linux
|
||||
build-params: [ {static: ON, tutorials: ON, tests: ON }, {static: OFF, tutorials: OFF, tests: ON }, {static: OFF, tutorials: ON, tests: OFF }]
|
||||
env:
|
||||
IGL_NUM_THREADS: 1 # See https://github.com/libigl/libigl/pull/996
|
||||
steps:
|
||||
@@ -55,15 +50,14 @@ jobs:
|
||||
- name: Dependencies (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew update
|
||||
brew install ccache
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install ccache
|
||||
|
||||
- name: Cache Build
|
||||
id: cache-build
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.static }}-cache
|
||||
key: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.build-params.static }}-cache
|
||||
|
||||
- name: Prepare ccache
|
||||
run: |
|
||||
@@ -77,8 +71,10 @@ jobs:
|
||||
cmake .. \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_BUILD_TYPE=${{ matrix.config }} \
|
||||
-DLIBIGL_USE_STATIC_LIBRARY=${{ matrix.static }} \
|
||||
-DLIBIGL_COPYLEFT_CGAL=ON
|
||||
-DLIBIGL_USE_STATIC_LIBRARY=${{ matrix.build-params.static }} \
|
||||
-DLIBIGL_BUILD_TUTORIALS=${{ matrix.build-params.tutorials }} \
|
||||
-DLIBIGL_BUILD_TESTS=${{ matrix.build-params.tests }} \
|
||||
-DLIBIGL_COPYLEFT_CGAL=ON
|
||||
|
||||
- name: Build
|
||||
run: cd build; make -j2; ccache --show-stats
|
||||
@@ -91,24 +87,35 @@ jobs:
|
||||
####################
|
||||
|
||||
Windows:
|
||||
name: Windows (${{ matrix.config }}, ${{ fromJSON('["HeaderOnly", "Static"]')[matrix.static == 'ON'] }})
|
||||
name: Windows ${{ fromJSON('["Header-Only", "Static"]')[matrix.build-params.static == 'ON'] }} ${{ matrix.build-params.tutorials == 'ON' && 'tutorial' || ''}} ${{ matrix.build-params.selected_tutorial != 'NONE' && matrix.build-params.selected_tutorial || '' }} ${{ matrix.build-params.tests == 'ON' && 'tests' || ''}} ${{ matrix.config }}
|
||||
runs-on: windows-2022
|
||||
env:
|
||||
CC: cl.exe
|
||||
CXX: cl.exe
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config: [Release]
|
||||
static: [ON, OFF]
|
||||
build-params: [
|
||||
{static: ON, tutorials: ON, tests: ON, selected_tutorial: NONE},
|
||||
{static: OFF, tutorials: OFF, tests: ON, selected_tutorial: NONE},
|
||||
{static: OFF, tutorials: ON, tests: OFF, selected_tutorial: 1},
|
||||
{static: OFF, tutorials: ON, tests: OFF, selected_tutorial: 2},
|
||||
{static: OFF, tutorials: ON, tests: OFF, selected_tutorial: 3},
|
||||
{static: OFF, tutorials: ON, tests: OFF, selected_tutorial: 4},
|
||||
{static: OFF, tutorials: ON, tests: OFF, selected_tutorial: 5},
|
||||
{static: OFF, tutorials: ON, tests: OFF, selected_tutorial: 6},
|
||||
{static: OFF, tutorials: ON, tests: OFF, selected_tutorial: 7},
|
||||
{static: OFF, tutorials: ON, tests: OFF, selected_tutorial: 8},
|
||||
{static: OFF, tutorials: ON, tests: OFF, selected_tutorial: 9},
|
||||
]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 10
|
||||
- uses: seanmiddleditch/gha-setup-ninja@master
|
||||
|
||||
- name: Set env
|
||||
- name: Install Ninja
|
||||
uses: seanmiddleditch/gha-setup-ninja@master
|
||||
|
||||
- name: Set env variable for sccache
|
||||
run: |
|
||||
echo "appdata=$env:LOCALAPPDATA" >> ${env:GITHUB_ENV}
|
||||
|
||||
@@ -117,18 +124,16 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.appdata }}\Mozilla\sccache
|
||||
key: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.static }}-cache
|
||||
|
||||
key: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.build-params.static }}-cache
|
||||
|
||||
- name: Prepare sccache
|
||||
run: |
|
||||
Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
|
||||
iwr -useb 'https://raw.githubusercontent.com/scoopinstaller/install/master/install.ps1' -outfile 'install.ps1'
|
||||
.\install.ps1 -RunAsAdmin
|
||||
scoop install sccache --global
|
||||
# Scoop modifies the PATH so we make it available for the next steps of the job
|
||||
echo "${env:PATH}" >> ${env:GITHUB_PATH}
|
||||
|
||||
# We run configure + build in the same step, since they both need to call VsDevCmd
|
||||
# Also, cmd uses ^ to break commands into multiple lines (in powershell this is `)
|
||||
- name: Configure and build
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -136,14 +141,23 @@ jobs:
|
||||
cmake -G Ninja ^
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache ^
|
||||
-DCMAKE_BUILD_TYPE=${{ matrix.config }} ^
|
||||
-DLIBIGL_USE_STATIC_LIBRARY=${{ matrix.static }} ^
|
||||
-DLIBIGL_USE_STATIC_LIBRARY=${{ matrix.build-params.static }} ^
|
||||
-DLIBIGL_COPYLEFT_CGAL=ON ^
|
||||
-DCMAKE_JOB_POOLS=pool-linking=1;pool-compilation=1 ^
|
||||
-DCMAKE_JOB_POOL_COMPILE:STRING=pool-compilation ^
|
||||
-DCMAKE_JOB_POOL_LINK:STRING=pool-linking ^
|
||||
-DLIBIGL_BUILD_TUTORIALS=${{ matrix.build-params.tutorials }} ^
|
||||
-DLIBIGL_BUILD_TESTS=${{ matrix.build-params.tests }} ^
|
||||
-DLIBIGL_TUTORIALS_CHAPTER1=${{ (matrix.build-params.selected_tutorial == 'NONE' || matrix.build-params.selected_tutorial == '1') && 'ON' || 'OFF' }} ^
|
||||
-DLIBIGL_TUTORIALS_CHAPTER2=${{ (matrix.build-params.selected_tutorial == 'NONE' || matrix.build-params.selected_tutorial == '2') && 'ON' || 'OFF' }} ^
|
||||
-DLIBIGL_TUTORIALS_CHAPTER3=${{ (matrix.build-params.selected_tutorial == 'NONE' || matrix.build-params.selected_tutorial == '3') && 'ON' || 'OFF' }} ^
|
||||
-DLIBIGL_TUTORIALS_CHAPTER4=${{ (matrix.build-params.selected_tutorial == 'NONE' || matrix.build-params.selected_tutorial == '4') && 'ON' || 'OFF' }} ^
|
||||
-DLIBIGL_TUTORIALS_CHAPTER5=${{ (matrix.build-params.selected_tutorial == 'NONE' || matrix.build-params.selected_tutorial == '5') && 'ON' || 'OFF' }} ^
|
||||
-DLIBIGL_TUTORIALS_CHAPTER6=${{ (matrix.build-params.selected_tutorial == 'NONE' || matrix.build-params.selected_tutorial == '6') && 'ON' || 'OFF' }} ^
|
||||
-DLIBIGL_TUTORIALS_CHAPTER7=${{ (matrix.build-params.selected_tutorial == 'NONE' || matrix.build-params.selected_tutorial == '7') && 'ON' || 'OFF' }} ^
|
||||
-DLIBIGL_TUTORIALS_CHAPTER8=${{ (matrix.build-params.selected_tutorial == 'NONE' || matrix.build-params.selected_tutorial == '8') && 'ON' || 'OFF' }} ^
|
||||
-DLIBIGL_TUTORIALS_CHAPTER9=${{ (matrix.build-params.selected_tutorial == 'NONE' || matrix.build-params.selected_tutorial == '9') && 'ON' || 'OFF' }} ^
|
||||
-B build ^
|
||||
-S .
|
||||
cmake --build build
|
||||
cmake --build build -j2
|
||||
|
||||
- name: Tests
|
||||
run: cd build; ctest --verbose
|
||||
run: cd build; ctest --verbose -j2
|
||||
|
||||
|
||||
@@ -47,3 +47,4 @@ LibiglOptions.cmake
|
||||
|
||||
# macos debris
|
||||
.DS_Store
|
||||
*~
|
||||
|
||||
+15
-3
@@ -29,8 +29,8 @@ option(HUNTER_ENABLED "Enable Hunter package manager support" OFF)
|
||||
if(HUNTER_ENABLED)
|
||||
include("cmake/misc/HunterGate.cmake")
|
||||
HunterGate(
|
||||
URL "https://github.com/cpp-pm/hunter/archive/v0.23.300.tar.gz"
|
||||
SHA1 "1151d539465d9cdbc880ee30f794864aec11c448"
|
||||
URL "https://github.com/cpp-pm/hunter/archive/v0.24.8.tar.gz"
|
||||
SHA1 "ca7838dded9a1811b04ffd56175f629e0af82d3d"
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -55,6 +55,8 @@ set_property(GLOBAL PROPERTY __igl_module_path ${CMAKE_MODULE_PATH})
|
||||
|
||||
set(LIBIGL_DEFAULT_CGAL ${LIBIGL_TOPLEVEL_PROJECT})
|
||||
set(MATLAB_ADDITIONAL_VERSIONS
|
||||
"R2023b=10.4"
|
||||
"R2023a=10.4"
|
||||
"R2022b=10.3"
|
||||
"R2022a=10.2"
|
||||
"R2021b=10.1"
|
||||
@@ -74,6 +76,14 @@ if(LIBIGL_TOPLEVEL_PROJECT)
|
||||
message(WARNING "Mosek not found, disabling igl_restricted::mosek module.")
|
||||
endif()
|
||||
endif()
|
||||
set(LIBIGL_DEFAULT_COMISO ${LIBIGL_TOPLEVEL_PROJECT})
|
||||
if(LIBIGL_TOPLEVEL_PROJECT AND (NOT APPLE) AND UNIX)
|
||||
find_package(BLAS QUIET)
|
||||
if(NOT BLAS_FOUND)
|
||||
set(LIBIGL_DEFAULT_COMISO OFF)
|
||||
message(WARNING "BLAS not found, disabling igl_copyleft::comiso module.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Build tests and tutorials
|
||||
option(LIBIGL_BUILD_TESTS "Build libigl unit test" ${LIBIGL_TOPLEVEL_PROJECT})
|
||||
@@ -91,8 +101,9 @@ option(LIBIGL_EMBREE "Build target igl::embree" ${LIBIGL_TOP
|
||||
option(LIBIGL_GLFW "Build target igl::glfw" ${LIBIGL_TOPLEVEL_PROJECT})
|
||||
option(LIBIGL_IMGUI "Build target igl::imgui" ${LIBIGL_TOPLEVEL_PROJECT})
|
||||
option(LIBIGL_OPENGL "Build target igl::opengl" ${LIBIGL_TOPLEVEL_PROJECT})
|
||||
option(LIBIGL_PNG "Build target igl::png" ${LIBIGL_TOPLEVEL_PROJECT})
|
||||
option(LIBIGL_STB "Build target igl::stb" ${LIBIGL_TOPLEVEL_PROJECT})
|
||||
option(LIBIGL_PREDICATES "Build target igl::predicates" ${LIBIGL_TOPLEVEL_PROJECT})
|
||||
option(LIBIGL_SPECTRA "Build target igl::spectra" ${LIBIGL_TOPLEVEL_PROJECT})
|
||||
option(LIBIGL_XML "Build target igl::xml" ${LIBIGL_TOPLEVEL_PROJECT})
|
||||
|
||||
# Copyleft modules. These modules are available under GPL license, and their dependencies are
|
||||
@@ -167,3 +178,4 @@ if(LIBIGL_INSTALL)
|
||||
write_basic_package_version_file("${version_config_file}" COMPATIBILITY SameMajorVersion)
|
||||
install(FILES "${project_config_out}" "${version_config_file}" DESTINATION "${export_dest_dir}")
|
||||
endif()
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
# option(LIBIGL_GLFW "Build target igl::glfw" ON)
|
||||
# option(LIBIGL_IMGUI "Build target igl::imgui" ON)
|
||||
# option(LIBIGL_OPENGL "Build target igl::opengl" ON)
|
||||
# option(LIBIGL_PNG "Build target igl::png" ON)
|
||||
# option(LIBIGL_STB "Build target igl::stb" ON)
|
||||
# option(LIBIGL_PREDICATES "Build target igl::predicates" ON)
|
||||
# option(LIBIGL_XML "Build target igl::xml" ON)
|
||||
# option(LIBIGL_COPYLEFT_CGAL "Build target igl_copyleft::cgal" ON)
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
Documentation, tutorial, and instructions at <https://libigl.github.io>.
|
||||
|
||||
|
||||
| 🚨 Important |
|
||||
| 🆕 Doxygen Documentation |
|
||||
|:---|
|
||||
| The latest version of libigl (v2.4.0) introduces some **breaking changes** to its CMake build system. Please read our [changelog](https://libigl.github.io/changelog/) page for instructions on how to update your project accordingly. |
|
||||
| The latest version of libigl (v2.5.0) introduces [doxygen generated detailed documentation](https://libigl.github.io/dox/index.html) |
|
||||
|
||||
@@ -1,51 +1,2 @@
|
||||
function(igl_add_test module_name)
|
||||
if(NOT LIBIGL_BUILD_TESTS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT TARGET ${module_name})
|
||||
message(FATAL_ERROR "'${module_name}' is not a CMake target")
|
||||
endif()
|
||||
|
||||
# Check if category is `copyleft` or `restricted`
|
||||
if(${module_name} MATCHES "^igl_copyleft")
|
||||
set(suffix "_copyleft")
|
||||
elseif(${module_name} MATCHES "^igl_restricted")
|
||||
set(suffix "_restricted")
|
||||
else()
|
||||
set(suffix "")
|
||||
endif()
|
||||
|
||||
# Create test executable
|
||||
add_executable(test_${module_name}
|
||||
${libigl_SOURCE_DIR}/tests/main.cpp
|
||||
${libigl_SOURCE_DIR}/tests/test_common.h
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
# Include headers
|
||||
target_include_directories(test_${module_name} PUBLIC ${libigl_SOURCE_DIR}/tests)
|
||||
|
||||
# Compile definitions
|
||||
target_compile_definitions(test_${module_name} PUBLIC CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
|
||||
# Dependencies
|
||||
include(catch2)
|
||||
include(libigl_tests_data)
|
||||
target_link_libraries(test_${module_name} PUBLIC
|
||||
${module_name}
|
||||
igl::tests_data
|
||||
Catch2::Catch2
|
||||
)
|
||||
|
||||
# IDE Folder
|
||||
set_target_properties(test_${module_name} PROPERTIES FOLDER Libigl_Tests)
|
||||
|
||||
# Output directory
|
||||
set_target_properties(test_${module_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests")
|
||||
|
||||
# Register tests
|
||||
FetchContent_GetProperties(catch2)
|
||||
include("${catch2_SOURCE_DIR}/contrib/Catch.cmake")
|
||||
catch_discover_tests(test_${module_name})
|
||||
endfunction()
|
||||
|
||||
@@ -7,7 +7,9 @@ function(igl_add_tutorial name)
|
||||
endforeach()
|
||||
|
||||
message(STATUS "Creating libigl tutorial: ${name}")
|
||||
add_executable(${name} ${CMAKE_CURRENT_SOURCE_DIR}/${name}/main.cpp)
|
||||
# get all cpp files in ${CMAKE_CURRENT_SOURCE_DIR}/${name}/
|
||||
file(GLOB SRCFILES ${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.cpp)
|
||||
add_executable(${name} ${SRCFILES})
|
||||
target_link_libraries(${name} PRIVATE
|
||||
igl::core
|
||||
igl::tutorial_data
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(Eigen3 REQUIRED)
|
||||
find_dependency(Threads REQUIRED)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/LibiglConfigTargets.cmake")
|
||||
check_required_components(Libigl)
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# 1. Define module
|
||||
igl_add_library(igl_png)
|
||||
|
||||
# 2. Include headers
|
||||
include(GNUInstallDirs)
|
||||
target_include_directories(igl_png ${IGL_SCOPE}
|
||||
$<BUILD_INTERFACE:${libigl_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
)
|
||||
|
||||
# 3. Target sources
|
||||
file(GLOB INC_FILES "${libigl_SOURCE_DIR}/include/igl/png/*.h")
|
||||
file(GLOB SRC_FILES "${libigl_SOURCE_DIR}/include/igl/png/*.cpp")
|
||||
igl_target_sources(igl_png ${INC_FILES} ${SRC_FILES})
|
||||
|
||||
# 4. Dependencies
|
||||
include(stb)
|
||||
igl_include(opengl)
|
||||
target_link_libraries(igl_png ${IGL_SCOPE}
|
||||
igl::core
|
||||
igl::opengl
|
||||
stb::stb
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
# 1. Define module
|
||||
igl_add_library(igl_spectra)
|
||||
|
||||
# 2. Include headers
|
||||
include(GNUInstallDirs)
|
||||
target_include_directories(igl_spectra ${IGL_SCOPE}
|
||||
$<BUILD_INTERFACE:${libigl_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
)
|
||||
|
||||
# 3. Target sources
|
||||
file(GLOB INC_FILES "${libigl_SOURCE_DIR}/include/igl/spectra/*.h")
|
||||
file(GLOB SRC_FILES "${libigl_SOURCE_DIR}/include/igl/spectra/*.cpp")
|
||||
igl_target_sources(igl_spectra ${INC_FILES} ${SRC_FILES})
|
||||
|
||||
# 4. Dependencies
|
||||
include(spectra)
|
||||
target_link_libraries(igl_spectra ${IGL_SCOPE}
|
||||
igl::core
|
||||
spectra::spectra
|
||||
)
|
||||
|
||||
# 5. Unit tests
|
||||
file(GLOB SRC_FILES "${libigl_SOURCE_DIR}/tests/include/igl/spectra/*.cpp")
|
||||
igl_add_test(igl_spectra ${SRC_FILES})
|
||||
@@ -0,0 +1,35 @@
|
||||
# 1. Define module
|
||||
igl_add_library(igl_stb)
|
||||
|
||||
# 2. Include headers
|
||||
include(GNUInstallDirs)
|
||||
target_include_directories(igl_stb ${IGL_SCOPE}
|
||||
$<BUILD_INTERFACE:${libigl_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
)
|
||||
|
||||
# 3. Target sources
|
||||
file(GLOB INC_FILES "${libigl_SOURCE_DIR}/include/igl/stb/*.h")
|
||||
file(GLOB SRC_FILES "${libigl_SOURCE_DIR}/include/igl/stb/*.cpp")
|
||||
if(LIBIGL_OPENGL)
|
||||
message(STATUS "Including igl/opengl/stb support")
|
||||
file(GLOB OPENGL_INC_FILES "${libigl_SOURCE_DIR}/include/igl/opengl/stb/*.h")
|
||||
file(GLOB OPENGL_SRC_FILES "${libigl_SOURCE_DIR}/include/igl/opengl/stb/*.cpp")
|
||||
list(APPEND INC_FILES ${OPENGL_INC_FILES})
|
||||
list(APPEND SRC_FILES ${OPENGL_SRC_FILES})
|
||||
endif()
|
||||
igl_target_sources(igl_stb ${INC_FILES} ${SRC_FILES})
|
||||
|
||||
# 4. Dependencies
|
||||
include(stb)
|
||||
target_link_libraries(igl_stb ${IGL_SCOPE}
|
||||
igl::core
|
||||
stb::stb
|
||||
)
|
||||
|
||||
if(LIBIGL_OPENGL)
|
||||
igl_include(opengl)
|
||||
target_link_libraries(igl_stb ${IGL_SCOPE}
|
||||
igl::opengl
|
||||
)
|
||||
endif()
|
||||
@@ -13,21 +13,3 @@ include(igl_windows)
|
||||
|
||||
# Libigl permissive modules
|
||||
igl_include(core)
|
||||
igl_include_optional(embree)
|
||||
igl_include_optional(opengl)
|
||||
igl_include_optional(glfw)
|
||||
igl_include_optional(imgui)
|
||||
igl_include_optional(predicates)
|
||||
igl_include_optional(png)
|
||||
igl_include_optional(xml)
|
||||
|
||||
# Libigl copyleft modules
|
||||
igl_include_optional(copyleft core)
|
||||
igl_include_optional(copyleft cgal)
|
||||
igl_include_optional(copyleft comiso)
|
||||
igl_include_optional(copyleft tetgen)
|
||||
|
||||
# Libigl restricted modules
|
||||
igl_include_optional(restricted matlab)
|
||||
igl_include_optional(restricted mosek)
|
||||
igl_include_optional(restricted triangle)
|
||||
|
||||
Vendored
+7
-7
@@ -7,12 +7,8 @@ message(STATUS "Third-party: creating target 'CGAL::CGAL'")
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
cgal
|
||||
#GIT_REPOSITORY https://github.com/CGAL/cgal.git
|
||||
#GIT_TAG f7c3c8212b56c0d6dae63787efc99093f4383415
|
||||
URL https://github.com/CGAL/cgal/releases/download/releases%2FCGAL-4.12.2/CGAL-4.12.2.tar.xz
|
||||
URL_MD5 c94a0081c3836fd01ccb4d1e8bdd5d4f
|
||||
# URL https://github.com/CGAL/cgal/releases/download/v5.2.1/CGAL-5.2.1-library.tar.xz
|
||||
# URL_MD5 c1c3a9abe9106b5f3ff8dccaf2ddc0b7
|
||||
URL https://github.com/CGAL/cgal/releases/download/v5.4/CGAL-5.4-library.tar.xz
|
||||
URL_MD5 996f7ee9ba1553edac60debb115699cd
|
||||
)
|
||||
FetchContent_GetProperties(cgal)
|
||||
if(cgal_POPULATED)
|
||||
@@ -38,8 +34,10 @@ function(cgal_import_target)
|
||||
include(boost)
|
||||
|
||||
ignore_package(GMP 5.0.1)
|
||||
set(GMP_INCLUDE_DIR "")
|
||||
set(GMP_INCLUDE_DIR ${gmp_INCLUDE_DIR})
|
||||
set(GMP_LIBRARIES gmp::gmp)
|
||||
set(GMPXX_INCLUDE_DIR ${GMP_INCLUDE_DIR})
|
||||
set(GMPXX_LIBRARIES ${GMP_LIBRARIES})
|
||||
|
||||
ignore_package(MPFR 3.0.0)
|
||||
set(MPFR_INCLUDE_DIR "")
|
||||
@@ -52,6 +50,8 @@ function(cgal_import_target)
|
||||
# Prefer Config mode before Module mode to prevent CGAL from loading its own FindXXX.cmake
|
||||
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE)
|
||||
|
||||
# https://stackoverflow.com/a/71714947/148668
|
||||
set(CGAL_DATA_DIR "unspecified")
|
||||
find_package(CGAL CONFIG COMPONENTS Core PATHS ${cgal_SOURCE_DIR} NO_DEFAULT_PATH)
|
||||
endfunction()
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -8,7 +8,7 @@ include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
eigen
|
||||
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
|
||||
GIT_TAG tags/3.3.7
|
||||
GIT_TAG tags/3.4.0
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
FetchContent_GetProperties(eigen)
|
||||
|
||||
Vendored
+1
-1
@@ -8,7 +8,7 @@ include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
glad
|
||||
GIT_REPOSITORY https://github.com/libigl/libigl-glad.git
|
||||
GIT_TAG 09b4969c56779f7ddf8e6176ec1873184aec890f
|
||||
GIT_TAG ceef55fcd08bdd16e985370a99cfb60e69623221
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(glad)
|
||||
|
||||
Vendored
+47
-5
@@ -10,6 +10,31 @@ if(WIN32)
|
||||
else()
|
||||
message(STATUS "Third-party: creating target 'gmp::gmp'")
|
||||
|
||||
# SERIOUSLY !?! CMAKE and configure use transposed definitions of "build" and
|
||||
# "host"?
|
||||
#
|
||||
# https://cmake.org/cmake/help/latest/variable/CMAKE_SYSTEM_NAME.html#variable:CMAKE_SYSTEM_NAME
|
||||
# https://gcc.gnu.org/onlinedocs/gccint/Configure-Terms.html
|
||||
#
|
||||
# Seems these aren't to be trusted much
|
||||
# https://gitlab.kitware.com/cmake/cmake/-/issues/20989
|
||||
if(APPLE)
|
||||
# https://gmplib.org/list-archives/gmp-discuss/2020-November/006607.html
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
|
||||
set(gmp_BUILD "x86_64-apple-darwin")
|
||||
set(gmp_HOST "arm64-apple-darwin")
|
||||
set(gmp_CFLAGS "--target=arm64-apple-darwin")
|
||||
set(gmp_LDFLAGS "-arch arm64")
|
||||
message(STATUS "GMP Recipe notices building on ${gmp_BUILD} for ${gmp_HOST}")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64" AND CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64")
|
||||
set(gmp_HOST "x86_64-apple-darwin")
|
||||
set(gmp_BUILD "arm64-apple-darwin")
|
||||
set(gmp_CFLAGS "--target=x86_64-apple-darwin13.0.0")
|
||||
set(gmp_LDFLAGS "")
|
||||
message(STATUS "GMP Recipe notices building on ${gmp_HOST} for ${gmp_BUILD}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(FetchContent)
|
||||
include(ProcessorCount)
|
||||
ProcessorCount(Ncpu)
|
||||
@@ -17,21 +42,38 @@ else()
|
||||
set(prefix ${FETCHCONTENT_BASE_DIR}/gmp)
|
||||
set(gmp_INSTALL ${prefix}/install)
|
||||
set(gmp_LIB_DIR ${gmp_INSTALL}/lib)
|
||||
set(gmp_LIBRARY ${gmp_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}gmp${CMAKE_STATIC_LIBRARY_SUFFIX})
|
||||
set(gmp_LIBRARY
|
||||
${gmp_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}gmp${CMAKE_STATIC_LIBRARY_SUFFIX}
|
||||
${gmp_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}gmpxx${CMAKE_STATIC_LIBRARY_SUFFIX}
|
||||
)
|
||||
set(gmp_INCLUDE_DIR ${gmp_INSTALL}/include)
|
||||
|
||||
# Try to use CONFIGURE_HANDLED_BY_BUILD ON to avoid constantly reconfiguring
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.20)
|
||||
# CMake < 3.20, do not use any extra option
|
||||
set(gmp_ExternalProject_Add_extra_options)
|
||||
else()
|
||||
# CMake >= 3.20
|
||||
set(gmp_ExternalProject_Add_extra_options "CONFIGURE_HANDLED_BY_BUILD;ON")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(gmp
|
||||
PREFIX ${prefix}
|
||||
URL https://gmplib.org/download/gmp/gmp-6.2.1.tar.xz
|
||||
URL_MD5 0b82665c4a92fd2ade7440c13fcaa42b
|
||||
URL https://github.com/alisw/GMP/archive/refs/tags/v6.2.1.tar.gz
|
||||
URL_MD5 f060ad4e762ae550d16f1bb477aadba5
|
||||
UPDATE_DISCONNECTED true # need this to avoid constant rebuild
|
||||
PATCH_COMMAND
|
||||
curl "https://gmplib.org/repo/gmp/raw-rev/5f32dbc41afc" "|" git apply -v
|
||||
CONFIGURE_HANDLED_BY_BUILD ON # avoid constant reconfigure
|
||||
curl "https://gist.githubusercontent.com/alecjacobson/d34d9307c17d1b853571699b9786e9d1/raw/8d14fc21cb7654f51c2e8df4deb0f82f9d0e8355/gmp-patch" "|" git apply -v
|
||||
${gmp_ExternalProject_Add_extra_options}
|
||||
CONFIGURE_COMMAND
|
||||
${CMAKE_COMMAND} -E env
|
||||
CFLAGS=${gmp_CFLAGS}
|
||||
LDFLAGS=${gmp_LDFLAGS}
|
||||
${prefix}/src/gmp/configure
|
||||
--disable-debug --disable-dependency-tracking --enable-cxx --with-pic
|
||||
--prefix=${gmp_INSTALL}
|
||||
--build=${gmp_BUILD}
|
||||
--host=${gmp_HOST}
|
||||
--disable-shared
|
||||
BUILD_COMMAND make -j${Ncpu}
|
||||
INSTALL_COMMAND make -j${Ncpu} install
|
||||
|
||||
Vendored
+1
@@ -9,6 +9,7 @@ FetchContent_Declare(
|
||||
imgui
|
||||
GIT_REPOSITORY https://github.com/ocornut/imgui.git
|
||||
GIT_TAG v1.85
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
FetchContent_MakeAvailable(imgui)
|
||||
|
||||
|
||||
Vendored
+33
-3
@@ -14,6 +14,22 @@ if(WIN32)
|
||||
else()
|
||||
message(STATUS "Third-party: creating target 'mpfr::mpfr'")
|
||||
|
||||
# Praying this will work the same as gmp
|
||||
if(APPLE)
|
||||
# https://gmplib.org/list-archives/gmp-discuss/2020-November/006607.html
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
|
||||
set(mpfr_BUILD "x86_64-apple-darwin")
|
||||
set(mpfr_HOST "arm64-apple-darwin")
|
||||
set(mpfr_CFLAGS "--target=arm64-apple-darwin")
|
||||
set(mpfr_LDFLAGS "-arch arm64")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64" AND CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64")
|
||||
set(mpfr_HOST "x86_64-apple-darwin")
|
||||
set(mpfr_BUILD "arm64-apple-darwin")
|
||||
set(mpfr_CFLAGS "--target=x86_64-apple-darwin13.0.0")
|
||||
set(mpfr_LDFLAGS "")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(FetchContent)
|
||||
include(ProcessorCount)
|
||||
ProcessorCount(Ncpu)
|
||||
@@ -23,19 +39,33 @@ else()
|
||||
set(mpfr_LIBRARY ${mpfr_INSTALL}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}mpfr${CMAKE_STATIC_LIBRARY_SUFFIX})
|
||||
set(mpfr_INCLUDE_DIR ${mpfr_INSTALL}/include)
|
||||
|
||||
# Try to use CONFIGURE_HANDLED_BY_BUILD ON to avoid constantly reconfiguring
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.20)
|
||||
# CMake < 3.20, do not use any extra option
|
||||
set(mpfr_ExternalProject_Add_extra_options)
|
||||
else()
|
||||
# CMake >= 3.20
|
||||
set(mpfr_ExternalProject_Add_extra_options "CONFIGURE_HANDLED_BY_BUILD;ON")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(mpfr
|
||||
PREFIX ${prefix}
|
||||
DEPENDS gmp
|
||||
URL https://ftp.gnu.org/gnu/mpfr/mpfr-4.1.0.tar.xz
|
||||
URL_MD5 bdd3d5efba9c17da8d83a35ec552baef
|
||||
URL https://ftp.gnu.org/gnu/mpfr/mpfr-4.2.0.tar.xz
|
||||
URL_MD5 a25091f337f25830c16d2054d74b5af7
|
||||
UPDATE_DISCONNECTED true # need this to avoid constant rebuild
|
||||
CONFIGURE_HANDLED_BY_BUILD ON # avoid constant reconfigure
|
||||
${mpfr_ExternalProject_Add_extra_options} # avoid constant reconfigure
|
||||
CONFIGURE_COMMAND
|
||||
${CMAKE_COMMAND} -E env
|
||||
CFLAGS=${gmp_CFLAGS}
|
||||
LDFLAGS=${gmp_LDFLAGS}
|
||||
${prefix}/src/mpfr/configure
|
||||
--disable-debug --disable-dependency-tracking --disable-silent-rules --enable-cxx --with-pic
|
||||
--with-gmp-include=${gmp_INCLUDE_DIR} --with-gmp-lib=${gmp_LIB_DIR}
|
||||
--disable-shared
|
||||
--prefix=${mpfr_INSTALL}
|
||||
--build=${gmp_BUILD}
|
||||
--host=${gmp_HOST}
|
||||
--disable-shared
|
||||
BUILD_COMMAND make -j${Ncpu}
|
||||
INSTALL_COMMAND make -j${Ncpu} install
|
||||
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
if(TARGET spectra::spectra)
|
||||
return()
|
||||
endif()
|
||||
include(FetchContent)
|
||||
|
||||
message(STATUS "Third-party: creating target 'spectra::spectra'")
|
||||
|
||||
# Use fork because yixuan/spectra struggles to find Eigen3
|
||||
FetchContent_Declare(
|
||||
Spectra
|
||||
GIT_REPOSITORY https://github.com/alecjacobson/spectra/
|
||||
GIT_TAG bbdc521b70a733c52ebfc0ac1484c82e13c3d140
|
||||
)
|
||||
FetchContent_MakeAvailable(Spectra)
|
||||
|
||||
add_library(spectra::spectra ALIAS Spectra)
|
||||
File diff suppressed because it is too large
Load Diff
+2728
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
# libigl - A simple C++ geometry processing library
|
||||
|
||||
This detailed documentation browser is automatically generated from the comments
|
||||
in libigl header (.h) files.
|
||||
|
||||
In general, each function (e.g., `igl::func`) will be defined in a
|
||||
correspondingly named header file (e.g., `#include <igl/func.h>`).
|
||||
|
||||
The _core_ library only depends on the standard template library (`std::`) and
|
||||
Eigen. These functions reside directly the [`igl::` namespace](./namespaceigl.html)
|
||||
|
||||
Functions with further dependencies reside in a corresonding sub-namespace. For
|
||||
example, the function `igl::spectra::lscm` depends on the Spectra library so it
|
||||
resides in the [`igl::spectra::` namespace](./namespaceigl_1_1spectra.html).
|
||||
|
||||
Functions which depend on external code under a copyleft license reside in the
|
||||
[`igl::copyleft::` namepsace](file:///Users/alecjacobson/Repos/libigl/dox/namespaceigl_1_1copyleft.html).
|
||||
|
||||
https://libigl.github.io/
|
||||
|
||||
https://github.com/libigl/libigl/
|
||||
File diff suppressed because it is too large
Load Diff
+160
-119
@@ -15,35 +15,47 @@
|
||||
#include <vector>
|
||||
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).
|
||||
/// 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).
|
||||
///
|
||||
/// @tparam DerivedV Matrix type of vertex positions (e.g., `Eigen::MatrixXd`)
|
||||
/// @tparam DIM Dimension of mesh vertex positions (2 or 3)
|
||||
template <typename DerivedV, int DIM>
|
||||
class AABB
|
||||
{
|
||||
public:
|
||||
/// Scalar type of vertex positions (e.g., `double`)
|
||||
typedef typename DerivedV::Scalar Scalar;
|
||||
/// Fixed-size (`DIM`) RowVector type using `Scalar`
|
||||
typedef Eigen::Matrix<Scalar,1,DIM> RowVectorDIMS;
|
||||
/// Fixed-size (`DIM`) (Column)Vector type using `Scalar`
|
||||
typedef Eigen::Matrix<Scalar,DIM,1> VectorDIMS;
|
||||
/// Fixed-width (`DIM`) Matrix type using `Scalar`
|
||||
typedef Eigen::Matrix<Scalar,Eigen::Dynamic,DIM> MatrixXDIMS;
|
||||
/// Pointer to "left" child node (`nullptr` if leaf)
|
||||
// Shared pointers are slower...
|
||||
AABB * m_left;
|
||||
AABB * m_left;
|
||||
/// Pointer to "right" child node (`nullptr` if leaf)
|
||||
AABB * m_right;
|
||||
/// Axis-Aligned Bounding Box containing this node
|
||||
Eigen::AlignedBox<Scalar,DIM> m_box;
|
||||
// -1 non-leaf
|
||||
/// Index of single primitive in this node if full leaf, otherwise -1 for non-leaf
|
||||
int m_primitive;
|
||||
//Scalar m_low_sqr_d;
|
||||
//int m_depth;
|
||||
/// @private
|
||||
AABB():
|
||||
m_left(NULL), m_right(NULL),
|
||||
m_box(), m_primitive(-1)
|
||||
//m_low_sqr_d(std::numeric_limits<double>::infinity()),
|
||||
//m_depth(0)
|
||||
{}
|
||||
/// @private
|
||||
// http://stackoverflow.com/a/3279550/148668
|
||||
AABB(const AABB& other):
|
||||
m_left(other.m_left ? new AABB(*other.m_left) : NULL),
|
||||
@@ -56,6 +68,7 @@ public:
|
||||
// m_right ? m_right->m_depth + 1 : 0))
|
||||
{
|
||||
}
|
||||
/// @private
|
||||
// copy-swap idiom
|
||||
friend void swap(AABB& first, AABB& second)
|
||||
{
|
||||
@@ -68,18 +81,21 @@ public:
|
||||
//swap(first.m_low_sqr_d,second.m_low_sqr_d);
|
||||
//swap(first.m_depth,second.m_depth);
|
||||
}
|
||||
/// @private
|
||||
// Pass-by-value (aka copy)
|
||||
AABB& operator=(AABB other)
|
||||
{
|
||||
swap(*this,other);
|
||||
return *this;
|
||||
}
|
||||
/// @private
|
||||
AABB(AABB&& other):
|
||||
// initialize via default constructor
|
||||
AABB()
|
||||
{
|
||||
swap(*this,other);
|
||||
}
|
||||
/// @private
|
||||
// Seems like there should have been an elegant solution to this using
|
||||
// the copy-swap idiom above:
|
||||
IGL_INLINE void deinit()
|
||||
@@ -91,20 +107,20 @@ public:
|
||||
delete m_right;
|
||||
m_right = NULL;
|
||||
}
|
||||
/// @private
|
||||
~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}
|
||||
/// Build an Axis-Aligned Bounding Box tree for a given mesh and given
|
||||
/// serialization of a previous AABB tree.
|
||||
///
|
||||
/// @param[in] V #V by dim list of mesh vertex positions.
|
||||
/// @param[in] Ele #Ele by dim+1 list of mesh indices into #V.
|
||||
/// @param[in] bb_mins max_tree by dim list of bounding box min corner positions
|
||||
/// @param[in] bb_maxs max_tree by dim list of bounding box max corner positions
|
||||
/// @param[in] elements max_tree list of element or (not leaf id) indices into Ele
|
||||
/// @param[in] i recursive call index {0}
|
||||
template <
|
||||
typename DerivedEle,
|
||||
typename Derivedbb_mins,
|
||||
@@ -117,43 +133,44 @@ public:
|
||||
const Eigen::MatrixBase<Derivedbb_maxs> & bb_maxs,
|
||||
const Eigen::MatrixBase<Derivedelements> & elements,
|
||||
const int i = 0);
|
||||
// Wrapper for root with empty serialization
|
||||
/// Build an Axis-Aligned Bounding Box tree for a given mesh and given
|
||||
/// serialization of a previous AABB tree.
|
||||
///
|
||||
/// @param[in] V #V by dim list of mesh vertex positions.
|
||||
/// @param[in] Ele #Ele by dim+1 list of mesh indices into #V.
|
||||
template <typename DerivedEle>
|
||||
IGL_INLINE void init(
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
const Eigen::MatrixBase<DerivedEle> & 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)
|
||||
//
|
||||
/// Build an Axis-Aligned Bounding Box tree for a given mesh.
|
||||
///
|
||||
/// @param[in] V #V by dim list of mesh vertex positions.
|
||||
/// @param[in] Ele #Ele by dim+1 list of mesh indices into #V.
|
||||
/// @param[in] 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.
|
||||
/// @param[in] I #I list of indices into Ele of elements to include (for recursive
|
||||
/// calls)
|
||||
///
|
||||
template <typename DerivedEle, typename DerivedSI, typename DerivedI>
|
||||
IGL_INLINE void init(
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
const Eigen::MatrixBase<DerivedEle> & Ele,
|
||||
const Eigen::MatrixBase<DerivedSI> & SI,
|
||||
const Eigen::MatrixBase<DerivedI>& I);
|
||||
// Return whether at leaf node
|
||||
/// 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
|
||||
/// 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).
|
||||
///
|
||||
/// @param[in] V #V by dim list of mesh vertex positions. **Should be same as used to
|
||||
/// construct mesh.**
|
||||
/// @param[in] Ele #Ele by dim+1 list of mesh indices into #V. **Should be same as used to
|
||||
/// construct mesh.**
|
||||
/// @param[in] q dim row-vector query position
|
||||
/// @param[in] first whether to only return first element containing q
|
||||
/// @return list of indices of elements containing q
|
||||
template <typename DerivedEle, typename Derivedq>
|
||||
IGL_INLINE std::vector<int> find(
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
@@ -161,17 +178,18 @@ public:
|
||||
const Eigen::MatrixBase<Derivedq> & 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))
|
||||
/// Number of nodes contained in subtree
|
||||
///
|
||||
/// @return 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}
|
||||
/// Serialize this class into 3 arrays (so we can pass it pack to matlab)
|
||||
///
|
||||
/// @param[out] bb_mins max_tree by dim list of bounding box min corner positions
|
||||
/// @param[out] bb_maxs max_tree by dim list of bounding box max corner positions
|
||||
/// @param[out] elements max_tree list of element or (not leaf id) indices into Ele
|
||||
/// @param[in] i recursive call index into these arrays {0}
|
||||
template <
|
||||
typename Derivedbb_mins,
|
||||
typename Derivedbb_maxs,
|
||||
@@ -181,19 +199,17 @@ public:
|
||||
Eigen::PlainObjectBase<Derivedbb_maxs> & bb_maxs,
|
||||
Eigen::PlainObjectBase<Derivedelements> & 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.
|
||||
/// Compute squared distance to a query point
|
||||
///
|
||||
/// @param[in] V #V by dim list of vertex positions
|
||||
/// @param[in] Ele #Ele by dim list of simplex indices
|
||||
/// @param[in] p dim-long query point
|
||||
/// @param[out] i facet index corresponding to smallest distances
|
||||
/// @param[out] c closest point
|
||||
/// @return squared distance
|
||||
///
|
||||
/// \pre Currently assumes Elements are triangles regardless of
|
||||
/// dimension.
|
||||
template <typename DerivedEle>
|
||||
IGL_INLINE Scalar squared_distance(
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
@@ -201,26 +217,23 @@ public:
|
||||
const RowVectorDIMS & p,
|
||||
int & i,
|
||||
Eigen::PlainObjectBase<RowVectorDIMS> & 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.
|
||||
/// Compute squared distance to a query point if within `low_sqr_d` and
|
||||
/// `up_sqr_d`.
|
||||
///
|
||||
/// @param[in] V #V by dim list of vertex positions
|
||||
/// @param[in] Ele #Ele by dim list of simplex indices
|
||||
/// @param[in] p dim-long query point
|
||||
/// @param[in] low_sqr_d lower bound on squared distance, specified maximum squared
|
||||
/// distance
|
||||
/// @param[in] up_sqr_d current upper bounded on squared distance, current minimum
|
||||
/// squared distance (only consider distances less than this), see
|
||||
/// output.
|
||||
/// @param[out] i facet index corresponding to smallest distances
|
||||
/// @param[out] c closest point
|
||||
/// @return squared distance
|
||||
///
|
||||
/// \pre currently assumes Elements are triangles regardless of
|
||||
/// dimension.
|
||||
template <typename DerivedEle>
|
||||
IGL_INLINE Scalar squared_distance(
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
@@ -230,7 +243,18 @@ public:
|
||||
const Scalar up_sqr_d,
|
||||
int & i,
|
||||
Eigen::PlainObjectBase<RowVectorDIMS> & c) const;
|
||||
// Default low_sqr_d
|
||||
/// Compute squared distance to a query point (default `low_sqr_d`)
|
||||
///
|
||||
/// @param[in] V #V by dim list of vertex positions
|
||||
/// @param[in] Ele #Ele by dim list of simplex indices
|
||||
/// @param[in] p dim-long query point
|
||||
/// @param[in] up_sqr_d current upper bounded on squared distance, current minimum
|
||||
/// squared distance (only consider distances less than this), see
|
||||
/// output.
|
||||
/// @param[out] i facet index corresponding to smallest distances
|
||||
/// @param[out] c closest point
|
||||
/// @return squared distance
|
||||
///
|
||||
template <typename DerivedEle>
|
||||
IGL_INLINE Scalar squared_distance(
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
@@ -239,7 +263,14 @@ public:
|
||||
const Scalar up_sqr_d,
|
||||
int & i,
|
||||
Eigen::PlainObjectBase<RowVectorDIMS> & c) const;
|
||||
// All hits
|
||||
/// Intersect a ray with the mesh return all hits
|
||||
///
|
||||
/// @param[in] V #V by dim list of vertex positions
|
||||
/// @param[in] Ele #Ele by dim list of simplex indices
|
||||
/// @param[in] origin dim-long ray origin
|
||||
/// @param[in] dir dim-long ray direction
|
||||
/// @param[out] hits list of hits
|
||||
/// @return true if any hits
|
||||
template <typename DerivedEle>
|
||||
IGL_INLINE bool intersect_ray(
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
@@ -247,7 +278,14 @@ public:
|
||||
const RowVectorDIMS & origin,
|
||||
const RowVectorDIMS & dir,
|
||||
std::vector<igl::Hit> & hits) const;
|
||||
// First hit
|
||||
/// Intersect a ray with the mesh return first hit
|
||||
///
|
||||
/// @param[in] V #V by dim list of vertex positions
|
||||
/// @param[in] Ele #Ele by dim list of simplex indices
|
||||
/// @param[in] origin dim-long ray origin
|
||||
/// @param[in] dir dim-long ray direction
|
||||
/// @param[out] hit first hit
|
||||
/// @return true if any hit
|
||||
template <typename DerivedEle>
|
||||
IGL_INLINE bool intersect_ray(
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
@@ -255,7 +293,15 @@ public:
|
||||
const RowVectorDIMS & origin,
|
||||
const RowVectorDIMS & dir,
|
||||
igl::Hit & hit) const;
|
||||
//private:
|
||||
/// Intersect a ray with the mesh return first hit farther than `min_t`
|
||||
///
|
||||
/// @param[in] V #V by dim list of vertex positions
|
||||
/// @param[in] Ele #Ele by dim list of simplex indices
|
||||
/// @param[in] origin dim-long ray origin
|
||||
/// @param[in] dir dim-long ray direction
|
||||
/// @param[in] min_t minimum t value to consider
|
||||
/// @param[out] hit first hit
|
||||
/// @return true if any hit
|
||||
template <typename DerivedEle>
|
||||
IGL_INLINE bool intersect_ray(
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
@@ -265,20 +311,17 @@ public:
|
||||
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
|
||||
/// 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).
|
||||
///
|
||||
/// @param[in] V #V by dim list of vertex positions
|
||||
/// @param[in] Ele #Ele by dim list of simplex indices
|
||||
/// @param[in] P #P by dim list of query points
|
||||
/// @param[out] sqrD #P list of squared distances
|
||||
/// @param[out] I #P list of indices into Ele of closest primitives
|
||||
/// @param[out] C #P by dim list of closest points
|
||||
template <
|
||||
typename DerivedEle,
|
||||
typename DerivedP,
|
||||
@@ -293,21 +336,19 @@ public:
|
||||
Eigen::PlainObjectBase<DerivedI> & I,
|
||||
Eigen::PlainObjectBase<DerivedC> & 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
|
||||
/// 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).
|
||||
///
|
||||
/// @param[in] V #V by dim list of vertex positions
|
||||
/// @param[in] Ele #Ele by dim list of simplex indices
|
||||
/// @param[in] other AABB hierarchy of another set of primitives (must be points)
|
||||
/// @param[in] other_V #other_V by dim list of query points
|
||||
/// @param[in] other_Ele #other_Ele by ss list of simplex indices into other_V
|
||||
/// (must be simple list of points: ss == 1)
|
||||
/// @param[out] sqrD #P list of squared distances
|
||||
/// @param[out] I #P list of indices into Ele of closest primitives
|
||||
/// @param[out] C #P by dim list of closest points
|
||||
template <
|
||||
typename DerivedEle,
|
||||
typename Derivedother_V,
|
||||
|
||||
@@ -9,27 +9,24 @@
|
||||
#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 for choosing ARAP energy type
|
||||
enum ARAPEnergyType
|
||||
{
|
||||
/// "As-rigid-as-possible Surface Modeling" by [Sorkine and Alexa 2007],
|
||||
/// rotations defined at vertices affecting incident edges, default
|
||||
ARAP_ENERGY_TYPE_SPOKES = 0,
|
||||
/// 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_SPOKES_AND_RIMS = 1,
|
||||
/// "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_ELEMENTS = 2,
|
||||
/// Choose one automatically: spokes and rims for surfaces, elements for
|
||||
/// planar meshes and tets (not fully supported)
|
||||
ARAP_ENERGY_TYPE_DEFAULT = 3,
|
||||
/// Total number of types
|
||||
NUM_ARAP_ENERGY_TYPES = 4
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2017 Daniele Panozzo <daniele.panozzo@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <iostream>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
template <typename Scalar>
|
||||
IGL_INLINE void igl::AtA_cached_precompute(
|
||||
const Eigen::SparseMatrix<Scalar>& A,
|
||||
igl::AtA_cached_data& data,
|
||||
Eigen::SparseMatrix<Scalar>& AtA)
|
||||
{
|
||||
// 1 Compute At (this could be avoided, but performance-wise it will not make a difference)
|
||||
std::vector<std::vector<int> > Col_RowPtr;
|
||||
std::vector<std::vector<int> > Col_IndexPtr;
|
||||
|
||||
Col_RowPtr.resize(A.cols());
|
||||
Col_IndexPtr.resize(A.cols());
|
||||
|
||||
for (unsigned k=0; k<A.outerSize(); ++k)
|
||||
{
|
||||
unsigned outer_index = *(A.outerIndexPtr()+k);
|
||||
unsigned next_outer_index = (k+1 == A.outerSize()) ? A.nonZeros() : *(A.outerIndexPtr()+k+1);
|
||||
|
||||
for (unsigned l=outer_index; l<next_outer_index; ++l)
|
||||
{
|
||||
int col = k;
|
||||
int row = *(A.innerIndexPtr()+l);
|
||||
int value_index = l;
|
||||
assert(col < A.cols());
|
||||
assert(col >= 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<Scalar> 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<AtA.outerSize(); ++k)
|
||||
{
|
||||
unsigned outer_index = *(AtA.outerIndexPtr()+k);
|
||||
unsigned next_outer_index = (k+1 == AtA.outerSize()) ? AtA.nonZeros() : *(AtA.outerIndexPtr()+k+1);
|
||||
|
||||
for (unsigned l=outer_index; l<next_outer_index; ++l)
|
||||
{
|
||||
int col = k;
|
||||
int row = *(AtA.innerIndexPtr()+l);
|
||||
int value_index = l;
|
||||
assert(col < AtA.cols());
|
||||
assert(col >= 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[row].size() && j<Col_RowPtr[col].size())
|
||||
{
|
||||
if (Col_RowPtr[row][i] == Col_RowPtr[col][j])
|
||||
{
|
||||
data.I_row.push_back(Col_IndexPtr[row][i]);
|
||||
data.I_col.push_back(Col_IndexPtr[col][j]);
|
||||
data.I_w.push_back(Col_RowPtr[col][j]);
|
||||
++i;
|
||||
++j;
|
||||
} else
|
||||
if (Col_RowPtr[row][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 <typename Scalar>
|
||||
IGL_INLINE void igl::AtA_cached(
|
||||
const Eigen::SparseMatrix<Scalar>& A,
|
||||
const igl::AtA_cached_data& data,
|
||||
Eigen::SparseMatrix<Scalar>& AtA)
|
||||
{
|
||||
for (unsigned i=0; i<data.I_outer.size()-1; ++i)
|
||||
{
|
||||
*(AtA.valuePtr() + i) = 0;
|
||||
for (unsigned j=data.I_outer[i]; j<data.I_outer[i+1]; ++j)
|
||||
*(AtA.valuePtr() + i) += *(A.valuePtr() + data.I_row[j]) * data.W[data.I_w[j]] * *(A.valuePtr() + data.I_col[j]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef IGL_STATIC_LIBRARY
|
||||
template void igl::AtA_cached<double>(Eigen::SparseMatrix<double, 0, int> const&, igl::AtA_cached_data const&, Eigen::SparseMatrix<double, 0, int>&);
|
||||
template void igl::AtA_cached_precompute<double>(Eigen::SparseMatrix<double, 0, int> const&, igl::AtA_cached_data&, Eigen::SparseMatrix<double, 0, int>&);
|
||||
#endif
|
||||
+32
-20
@@ -13,40 +13,47 @@
|
||||
#include <Eigen/Sparse>
|
||||
namespace igl
|
||||
{
|
||||
/// Hold precomputed data for AtA_cached
|
||||
struct AtA_cached_data
|
||||
{
|
||||
// Weights
|
||||
/// Weights (diagonal of W)
|
||||
Eigen::VectorXd W;
|
||||
|
||||
// Flatten composition rules
|
||||
/// @private
|
||||
std::vector<int> I_row;
|
||||
/// @private
|
||||
std::vector<int> I_col;
|
||||
/// @private
|
||||
std::vector<int> I_w;
|
||||
|
||||
// For each entry of AtA, points to the beginning
|
||||
// of the composition rules
|
||||
/// @private
|
||||
std::vector<int> 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);
|
||||
/// 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.
|
||||
///
|
||||
/// @param[in] A m x n sparse matrix
|
||||
/// @param[in,out] 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.
|
||||
/// @param[out] AtA m by m matrix computed as AtA * W * A
|
||||
///
|
||||
/// #### Example:
|
||||
///
|
||||
/// \code{cpp}
|
||||
/// 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);
|
||||
/// \endcode
|
||||
template <typename Scalar>
|
||||
IGL_INLINE void AtA_cached_precompute(
|
||||
const Eigen::SparseMatrix<Scalar>& A,
|
||||
@@ -54,6 +61,11 @@ namespace igl
|
||||
Eigen::SparseMatrix<Scalar>& AtA
|
||||
);
|
||||
|
||||
/// Computes At * W * A, where A is sparse and W is diagonal precomputed into data.
|
||||
///
|
||||
/// @param[in] A m x n sparse matrix
|
||||
/// @param[in] 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.
|
||||
/// @param[out] AtA m by m matrix computed as AtA * W * A
|
||||
template <typename Scalar>
|
||||
IGL_INLINE void AtA_cached(
|
||||
const Eigen::SparseMatrix<Scalar>& A,
|
||||
|
||||
+16
-5
@@ -7,12 +7,23 @@
|
||||
// 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 <sstream>
|
||||
#include <string>
|
||||
/// Convert a stream of things to a const char *.
|
||||
///
|
||||
/// Suppose you have a function:
|
||||
/// \code{cpp}
|
||||
/// void func(const char * c);
|
||||
/// \endcode
|
||||
/// Then you can write:
|
||||
/// \code{cpp}
|
||||
/// func(C_STR("foo"<<1<<"bar"));
|
||||
/// \endcode
|
||||
/// which is equivalent to:
|
||||
/// \code{cpp}
|
||||
/// func("foo1bar");
|
||||
/// \endcode
|
||||
///
|
||||
// http://stackoverflow.com/a/2433143/148668
|
||||
#define C_STR(X) static_cast<std::ostringstream&>(std::ostringstream().flush() << X).str().c_str()
|
||||
#endif
|
||||
|
||||
@@ -1,359 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. If 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 <Eigen/Geometry>
|
||||
#include <Eigen/Core>
|
||||
#include "PI.h"
|
||||
|
||||
#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 <cmath>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
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: "<<m_at_dist<<endl;
|
||||
//cout<<"proj_up: "<<proj_up.transpose()<<endl;
|
||||
//cout<<"F: "<<F.transpose()<<endl;
|
||||
//cout<<"eye(): "<<this->eye().transpose()<<endl;
|
||||
//cout<<"at(): "<<this->at().transpose()<<endl;
|
||||
//cout<<"eye()-at(): "<<(this->eye()-this->at()).normalized().transpose()<<endl;
|
||||
//cout<<"eye-this->eye(): "<<(eye-this->eye()).squaredNorm()<<endl;
|
||||
assert( (eye-this->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
|
||||
@@ -1,30 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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
|
||||
+6
-2
@@ -10,13 +10,17 @@
|
||||
#include "igl_inline.h"
|
||||
namespace igl
|
||||
{
|
||||
// Define a standard value for double epsilon
|
||||
/// Standard value for double epsilon
|
||||
const double DOUBLE_EPS = 1.0e-14;
|
||||
/// Standard value for double epsilon²
|
||||
const double DOUBLE_EPS_SQ = 1.0e-28;
|
||||
/// Standard value for single epsilon
|
||||
const float FLOAT_EPS = 1.0e-7f;
|
||||
/// Standard value for single epsilon²
|
||||
const float FLOAT_EPS_SQ = 1.0e-14f;
|
||||
// Function returning EPS for corresponding type
|
||||
/// Function returning EPS for corresponding type
|
||||
template <typename S_type> IGL_INLINE S_type EPS();
|
||||
/// Function returning EPS_SQ for corresponding type
|
||||
template <typename S_type> IGL_INLINE S_type EPS_SQ();
|
||||
// Template specializations for float and double
|
||||
template <> IGL_INLINE float EPS<float>();
|
||||
|
||||
@@ -85,7 +85,9 @@
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace igl { namespace FastWindingNumber {
|
||||
namespace igl {
|
||||
/// @private
|
||||
namespace FastWindingNumber {
|
||||
|
||||
/*
|
||||
* Integer types
|
||||
@@ -242,7 +244,9 @@ typedef union SYS_FPRealUnionT<fpreal64> SYS_FPRealUnionD;
|
||||
#include <limits>
|
||||
#include <math.h>
|
||||
|
||||
namespace igl { namespace FastWindingNumber {
|
||||
namespace igl {
|
||||
/// @private
|
||||
namespace FastWindingNumber {
|
||||
|
||||
// NOTE:
|
||||
// These have been carefully written so that in the case of equality
|
||||
@@ -383,7 +387,9 @@ static inline fpreal64 SYSabs(fpreal64 a) { return ::fabs(a); }
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
namespace igl { namespace FastWindingNumber {
|
||||
namespace igl {
|
||||
/// @private
|
||||
namespace FastWindingNumber {
|
||||
|
||||
typedef __m128 v4sf;
|
||||
typedef __m128i v4si;
|
||||
@@ -745,7 +751,9 @@ vm_allbits(const v4si &a)
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace igl { namespace FastWindingNumber {
|
||||
namespace igl {
|
||||
/// @private
|
||||
namespace FastWindingNumber {
|
||||
|
||||
struct v4si {
|
||||
int32 v[4];
|
||||
@@ -1174,7 +1182,9 @@ int SYS_FORCE_INLINE _mm_movemask_ps(const v4sf& v) {
|
||||
|
||||
|
||||
|
||||
namespace igl { namespace FastWindingNumber {
|
||||
namespace igl {
|
||||
/// @private
|
||||
namespace FastWindingNumber {
|
||||
|
||||
class v4uf;
|
||||
|
||||
@@ -1628,7 +1638,9 @@ typedef v4uu v4ui;
|
||||
#include <type_traits>
|
||||
#include <string.h>
|
||||
|
||||
namespace igl { namespace FastWindingNumber {
|
||||
namespace igl {
|
||||
/// @private
|
||||
namespace FastWindingNumber {
|
||||
|
||||
/// This routine describes how to change the size of an array.
|
||||
/// It must increase the current_size by at least one!
|
||||
@@ -2423,7 +2435,9 @@ private:
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace igl { namespace FastWindingNumber {
|
||||
namespace igl {
|
||||
/// @private
|
||||
namespace FastWindingNumber {
|
||||
|
||||
// Implemented in UT_Array.C
|
||||
extern void ut_ArrayImplFree(void *p);
|
||||
@@ -3086,7 +3100,9 @@ UT_Array<T>::operator!=(const UT_Array<T> &a) const
|
||||
|
||||
#include <utility>
|
||||
#include <stddef.h>
|
||||
namespace igl { namespace FastWindingNumber {
|
||||
namespace igl {
|
||||
/// @private
|
||||
namespace FastWindingNumber {
|
||||
|
||||
/// An array class with the small buffer optimization, making it ideal for
|
||||
/// cases when you know it will only contain a few elements at the expense of
|
||||
@@ -3242,7 +3258,9 @@ private:
|
||||
|
||||
|
||||
|
||||
namespace igl { namespace FastWindingNumber {
|
||||
namespace igl {
|
||||
/// @private
|
||||
namespace FastWindingNumber {
|
||||
|
||||
template<typename T,exint SIZE,bool INSTANTIATED=false>
|
||||
class UT_FixedVector
|
||||
@@ -3646,7 +3664,9 @@ struct UT_FixedVectorTraits<UT_FixedVector<T,SIZE,INSTANTIATED> >
|
||||
|
||||
|
||||
#include <thread> // This is just included for std::thread::hardware_concurrency()
|
||||
namespace igl { namespace FastWindingNumber {
|
||||
namespace igl {
|
||||
/// @private
|
||||
namespace FastWindingNumber {
|
||||
namespace UT_Thread { inline int getNumProcessors() {
|
||||
return std::thread::hardware_concurrency();
|
||||
}}
|
||||
@@ -3879,7 +3899,9 @@ namespace UT_Thread { inline int getNumProcessors() {
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
namespace igl { namespace FastWindingNumber {
|
||||
namespace igl {
|
||||
/// @private
|
||||
namespace FastWindingNumber {
|
||||
|
||||
template<typename T> class UT_Array;
|
||||
class v4uf;
|
||||
@@ -4440,12 +4462,14 @@ using UT_BVH = UT::BVH<N>;
|
||||
|
||||
|
||||
|
||||
#include <igl/parallel_for.h>
|
||||
#include "parallel_for.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace igl { namespace FastWindingNumber {
|
||||
namespace igl {
|
||||
/// @private
|
||||
namespace FastWindingNumber {
|
||||
namespace HDK_Sample {
|
||||
|
||||
namespace UT {
|
||||
@@ -6008,7 +6032,9 @@ void BVH<N>::debugDump() const {
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace igl { namespace FastWindingNumber {
|
||||
namespace igl {
|
||||
/// @private
|
||||
namespace FastWindingNumber {
|
||||
namespace HDK_Sample {
|
||||
|
||||
template<typename T>
|
||||
@@ -6373,7 +6399,9 @@ private:
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace igl { namespace FastWindingNumber {
|
||||
namespace igl {
|
||||
/// @private
|
||||
namespace FastWindingNumber {
|
||||
|
||||
// This needs to be here or else the warning suppression doesn't work because
|
||||
// the templated calling code won't otherwise be compiled until after we've
|
||||
@@ -6423,7 +6451,7 @@ inline void ut_ArrayImplFree(void *p)
|
||||
|
||||
|
||||
|
||||
#include <igl/parallel_for.h>
|
||||
#include "parallel_for.h"
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@@ -6440,7 +6468,9 @@ inline void ut_ArrayImplFree(void *p)
|
||||
|
||||
#define TAYLOR_SERIES_ORDER 2
|
||||
|
||||
namespace igl { namespace FastWindingNumber {
|
||||
namespace igl {
|
||||
/// @private
|
||||
namespace FastWindingNumber {
|
||||
|
||||
namespace HDK_Sample {
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace igl
|
||||
{
|
||||
|
||||
/// File encoding types for writing files.
|
||||
enum class FileEncoding {
|
||||
Binary,
|
||||
Ascii
|
||||
|
||||
@@ -50,6 +50,7 @@ namespace igl {
|
||||
}
|
||||
};
|
||||
|
||||
/// Class to convert a FILE * to an std::istream
|
||||
struct FileMemoryStream : virtual FileMemoryBuffer, public std::istream
|
||||
{
|
||||
FileMemoryStream( char const *first_elem, size_t size)
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <typename DerivedF, typename DerivedFF, typename DerivedFFi>
|
||||
IGL_INLINE igl::HalfEdgeIterator<DerivedF,DerivedFF,DerivedFFi>::HalfEdgeIterator(
|
||||
const Eigen::MatrixBase<DerivedF>& _F,
|
||||
const Eigen::MatrixBase<DerivedFF>& _FF,
|
||||
const Eigen::MatrixBase<DerivedFFi>& _FFi,
|
||||
int _fi,
|
||||
int _ei,
|
||||
bool _reverse
|
||||
)
|
||||
: fi(_fi), ei(_ei), reverse(_reverse), F(_F), FF(_FF), FFi(_FFi)
|
||||
{}
|
||||
|
||||
template <typename DerivedF, typename DerivedFF, typename DerivedFFi>
|
||||
IGL_INLINE void igl::HalfEdgeIterator<DerivedF,DerivedFF,DerivedFFi>::flipF()
|
||||
{
|
||||
if (isBorder())
|
||||
return;
|
||||
|
||||
int fin = (FF)(fi,ei);
|
||||
int ein = (FFi)(fi,ei);
|
||||
|
||||
fi = fin;
|
||||
ei = ein;
|
||||
reverse = !reverse;
|
||||
}
|
||||
|
||||
|
||||
// Change Edge
|
||||
template <typename DerivedF, typename DerivedFF, typename DerivedFFi>
|
||||
IGL_INLINE void igl::HalfEdgeIterator<DerivedF,DerivedFF,DerivedFFi>::flipE()
|
||||
{
|
||||
if (!reverse)
|
||||
ei = (ei+2)%3; // ei-1
|
||||
else
|
||||
ei = (ei+1)%3;
|
||||
|
||||
reverse = !reverse;
|
||||
}
|
||||
|
||||
// Change Vertex
|
||||
template <typename DerivedF, typename DerivedFF, typename DerivedFFi>
|
||||
IGL_INLINE void igl::HalfEdgeIterator<DerivedF,DerivedFF,DerivedFFi>::flipV()
|
||||
{
|
||||
reverse = !reverse;
|
||||
}
|
||||
|
||||
template <typename DerivedF, typename DerivedFF, typename DerivedFFi>
|
||||
IGL_INLINE bool igl::HalfEdgeIterator<DerivedF,DerivedFF,DerivedFFi>::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 <typename DerivedF, typename DerivedFF, typename DerivedFFi>
|
||||
IGL_INLINE bool igl::HalfEdgeIterator<DerivedF,DerivedFF,DerivedFFi>::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 <typename DerivedF, typename DerivedFF, typename DerivedFFi>
|
||||
IGL_INLINE int igl::HalfEdgeIterator<DerivedF,DerivedFF,DerivedFFi>::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 <typename DerivedF, typename DerivedFF, typename DerivedFFi>
|
||||
IGL_INLINE int igl::HalfEdgeIterator<DerivedF,DerivedFF,DerivedFFi>::Fi()
|
||||
{
|
||||
return fi;
|
||||
}
|
||||
|
||||
// Get edge index
|
||||
template <typename DerivedF, typename DerivedFF, typename DerivedFFi>
|
||||
IGL_INLINE int igl::HalfEdgeIterator<DerivedF,DerivedFF,DerivedFFi>::Ei()
|
||||
{
|
||||
return ei;
|
||||
}
|
||||
|
||||
|
||||
template <typename DerivedF, typename DerivedFF, typename DerivedFFi>
|
||||
IGL_INLINE bool igl::HalfEdgeIterator<DerivedF,DerivedFF,DerivedFFi>::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<int, -1, 3, 0, -1, 3> ,Eigen::Matrix<int, -1, 3, 0, -1, 3> ,Eigen::Matrix<int, -1, 3, 0, -1, 3> >::HalfEdgeIterator(Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, int, int, bool);
|
||||
template igl::HalfEdgeIterator<Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >::HalfEdgeIterator(Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, int, int, bool);
|
||||
template bool igl::HalfEdgeIterator<Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1> >::NextFE();
|
||||
template int igl::HalfEdgeIterator<Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1> >::Ei();
|
||||
template int igl::HalfEdgeIterator<Eigen::Matrix<int, -1, 3, 0, -1, 3> ,Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1> >::Ei();
|
||||
template int igl::HalfEdgeIterator<Eigen::Matrix<int, -1, 3, 0, -1, 3> ,Eigen::Matrix<int, -1, 3, 0, -1, 3> ,Eigen::Matrix<int, -1, 3, 0, -1, 3> >::Ei();
|
||||
template int igl::HalfEdgeIterator<Eigen::Matrix<int, -1, 3, 0, -1, 3> ,Eigen::Matrix<int, -1, 3, 0, -1, 3> ,Eigen::Matrix<int, -1, 3, 0, -1, 3> >::Fi();
|
||||
template bool igl::HalfEdgeIterator<Eigen::Matrix<int, -1, 3, 0, -1, 3> ,Eigen::Matrix<int, -1, 3, 0, -1, 3> ,Eigen::Matrix<int, -1, 3, 0, -1, 3> >::NextFE();
|
||||
template int igl::HalfEdgeIterator<Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1> >::Vi();
|
||||
template igl::HalfEdgeIterator<Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1> >::HalfEdgeIterator(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, int, int, bool);
|
||||
template int igl::HalfEdgeIterator<Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1> >::Fi();
|
||||
template void igl::HalfEdgeIterator<Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1> >::flipE();
|
||||
template void igl::HalfEdgeIterator<Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3> >::flipE();
|
||||
template void igl::HalfEdgeIterator<Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1> >::flipF();
|
||||
template void igl::HalfEdgeIterator<Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3> >::flipF();
|
||||
template void igl::HalfEdgeIterator<Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1> >::flipV();
|
||||
template bool igl::HalfEdgeIterator<Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1> >::operator==(igl::HalfEdgeIterator<Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1>,Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
|
||||
template int igl::HalfEdgeIterator<Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >::Fi();
|
||||
template bool igl::HalfEdgeIterator<Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >::NextFE();
|
||||
template bool igl::HalfEdgeIterator<Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3> >::isBorder();
|
||||
template bool igl::HalfEdgeIterator<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >::isBorder();
|
||||
#endif
|
||||
@@ -11,35 +11,24 @@
|
||||
#include <Eigen/Core>
|
||||
|
||||
#include <vector>
|
||||
#include <igl/igl_inline.h>
|
||||
#include "igl_inline.h"
|
||||
|
||||
// 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.
|
||||
//
|
||||
/// 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)
|
||||
template <
|
||||
typename DerivedF,
|
||||
typename DerivedFF,
|
||||
@@ -47,7 +36,15 @@ namespace igl
|
||||
class HalfEdgeIterator
|
||||
{
|
||||
public:
|
||||
// Init the HalfEdgeIterator by specifying Face,Edge Index and Orientation
|
||||
/// Init the HalfEdgeIterator by specifying Face,Edge Index and Orientation
|
||||
///
|
||||
/// @param[in] F #F by 3 list of "faces"
|
||||
/// @param[in] FF #F by 3 list of triangle-triangle adjacency.
|
||||
/// @param[in] FFi #F by 3 list of FF inverse. For FF and FFi, refer to
|
||||
/// "triangle_triangle_adjacency.h"
|
||||
/// @param[in] _fi index of the selected face
|
||||
/// @param[in] _ii index of the selected face
|
||||
/// @param[in] _reverse orientation of the selected face
|
||||
IGL_INLINE HalfEdgeIterator(
|
||||
const Eigen::MatrixBase<DerivedF>& _F,
|
||||
const Eigen::MatrixBase<DerivedFF>& _FF,
|
||||
@@ -57,41 +54,48 @@ namespace igl
|
||||
bool _reverse = false
|
||||
);
|
||||
|
||||
// Change Face
|
||||
/// Change Face
|
||||
IGL_INLINE void flipF();
|
||||
|
||||
// Change Edge
|
||||
/// Change Edge
|
||||
IGL_INLINE void flipE();
|
||||
|
||||
// Change Vertex
|
||||
/// Change Vertex
|
||||
IGL_INLINE void flipV();
|
||||
|
||||
/// Determine if on border.
|
||||
/// @returns true if the current edge is on the border
|
||||
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, ...
|
||||
*/
|
||||
/// Change to 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, ...
|
||||
///
|
||||
/// @returns true if the next edge is not on the border
|
||||
IGL_INLINE bool NextFE();
|
||||
|
||||
// Get vertex index
|
||||
/// Get vertex index
|
||||
/// @return vertex index
|
||||
IGL_INLINE int Vi();
|
||||
|
||||
// Get face index
|
||||
/// Get face index
|
||||
/// @return face index
|
||||
IGL_INLINE int Fi();
|
||||
|
||||
// Get edge index
|
||||
/// Get edge index
|
||||
/// @return edge index
|
||||
IGL_INLINE int Ei();
|
||||
|
||||
/// Check if two HalfEdgeIterator are the same
|
||||
/// @return true if two HalfEdgeIterator are the same
|
||||
IGL_INLINE bool operator==(HalfEdgeIterator& p2);
|
||||
|
||||
private:
|
||||
|
||||
+10
-8
@@ -11,18 +11,20 @@
|
||||
|
||||
namespace igl
|
||||
{
|
||||
// Reimplementation of the embree::Hit struct from embree1.0
|
||||
//
|
||||
/// 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 (not used)
|
||||
// barycentric coordinates so that
|
||||
// pos = V.row(F(id,0))*(1-u-v)+V.row(F(id,1))*u+V.row(F(id,2))*v;
|
||||
/// primitive id
|
||||
int id;
|
||||
/// geometry id (not used)
|
||||
int gid;
|
||||
/// barycentric coordinates so that
|
||||
/// pos = V.row(F(id,0))*(1-u-v)+V.row(F(id,1))*u+V.row(F(id,2))*v;
|
||||
float u,v;
|
||||
// parametric distance so that
|
||||
// pos = origin + t * dir
|
||||
/// parametric distance so that
|
||||
/// pos = origin + t * dir
|
||||
float t;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,10 +8,8 @@
|
||||
#ifndef IGL_INDEXCOMPARISON_H
|
||||
#define IGL_INDEXCOMPARISON_H
|
||||
namespace igl{
|
||||
// Comparison struct used by sort
|
||||
// http://bytes.com/topic/c/answers/132045-sort-get-index
|
||||
|
||||
// For use with functions like std::sort
|
||||
/// Comparison struct used by sort
|
||||
/// http://bytes.com/topic/c/answers/132045-sort-get-index
|
||||
template<class T> struct IndexLessThan
|
||||
{
|
||||
IndexLessThan(const T arr) : arr(arr) {}
|
||||
@@ -22,7 +20,7 @@ namespace igl{
|
||||
const T arr;
|
||||
};
|
||||
|
||||
// For use with functions like std::unique
|
||||
/// Comparison struct used by unique
|
||||
template<class T> struct IndexEquals
|
||||
{
|
||||
IndexEquals(const T arr) : arr(arr) {}
|
||||
@@ -33,7 +31,7 @@ namespace igl{
|
||||
const T arr;
|
||||
};
|
||||
|
||||
// For use with functions like std::sort
|
||||
/// Comparison struct for vectors for use with functions like std::sort
|
||||
template<class T> struct IndexVectorLessThan
|
||||
{
|
||||
IndexVectorLessThan(const T & vec) : vec ( vec) {}
|
||||
@@ -44,7 +42,7 @@ namespace igl{
|
||||
const T & vec;
|
||||
};
|
||||
|
||||
// For use with functions like std::sort
|
||||
/// Comparison struct for use with functions like std::sort
|
||||
template<class T> struct IndexDimLessThan
|
||||
{
|
||||
IndexDimLessThan(const T & mat,const int & dim, const int & j) :
|
||||
@@ -67,7 +65,7 @@ namespace igl{
|
||||
const int & j;
|
||||
};
|
||||
|
||||
// For use with functions like std::sort
|
||||
/// Comparison struct For use with functions like std::sort
|
||||
template<class T> struct IndexRowLessThan
|
||||
{
|
||||
IndexRowLessThan(const T & mat) : mat ( mat) {}
|
||||
@@ -91,7 +89,7 @@ namespace igl{
|
||||
const T & mat;
|
||||
};
|
||||
|
||||
// For use with functions like std::sort
|
||||
/// Comparison struct for use with functions like std::sort
|
||||
template<class T> struct IndexRowEquals
|
||||
{
|
||||
IndexRowEquals(const T & mat) : mat ( mat) {}
|
||||
|
||||
+34
-25
@@ -1,33 +1,42 @@
|
||||
#ifndef IGL_LINSPACED_H
|
||||
#define IGL_LINSPACED_H
|
||||
#include <Eigen/Core>
|
||||
// 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<Eigen::VectorXi>(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<decltype(a) >(size,low,high);
|
||||
//
|
||||
/// @file LinSpaced.h
|
||||
///
|
||||
/// 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<Eigen::VectorXi>(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<decltype(a) >(size,low,high);
|
||||
///
|
||||
namespace igl
|
||||
{
|
||||
/// Replacement for Eigen::DenseBase::LinSpaced
|
||||
/// @param[in] size number of elements
|
||||
/// @param[in] low first element
|
||||
/// @param[in] high last element
|
||||
/// @return vector of size elements linearly spaced between low and
|
||||
///
|
||||
/// \fileinfo
|
||||
template <typename Derived>
|
||||
//inline typename Eigen::DenseBase< Derived >::RandomAccessLinSpacedReturnType
|
||||
inline Derived LinSpaced(
|
||||
|
||||
@@ -9,10 +9,9 @@
|
||||
#define IGL_MAPPINGENERGYTYPE_H
|
||||
namespace igl
|
||||
{
|
||||
// Energy Types used for Parameterization/Mapping.
|
||||
// Refer to SLIM [Rabinovich et al. 2017] for more details
|
||||
/// Energy Types used for Parameterization/Mapping.
|
||||
/// Refer to SLIM [Rabinovich et al. 2017] for more details
|
||||
// Todo: Integrate with ARAPEnergyType
|
||||
|
||||
enum MappingEnergyType
|
||||
{
|
||||
ARAP = 0,
|
||||
|
||||
@@ -9,13 +9,20 @@
|
||||
#define IGL_MESH_BOOLEAN_TYPE_H
|
||||
namespace igl
|
||||
{
|
||||
/// Boolean operation types
|
||||
enum MeshBooleanType
|
||||
{
|
||||
/// A ∪ B
|
||||
MESH_BOOLEAN_TYPE_UNION = 0,
|
||||
/// A ∩ B
|
||||
MESH_BOOLEAN_TYPE_INTERSECT = 1,
|
||||
/// A \ B
|
||||
MESH_BOOLEAN_TYPE_MINUS = 2,
|
||||
/// A ⊕ B
|
||||
MESH_BOOLEAN_TYPE_XOR = 3,
|
||||
/// Resolve intersections without removing any non-coplanar faces
|
||||
MESH_BOOLEAN_TYPE_RESOLVE = 4,
|
||||
/// Total number of Boolean options
|
||||
NUM_MESH_BOOLEAN_TYPES = 5
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,497 +0,0 @@
|
||||
// based on MSH reader from PyMesh
|
||||
|
||||
// Copyright (c) 2015 Qingnan Zhou <qzhou@adobe.com>
|
||||
// Copyright (C) 2020 Vladimir Fonov <vladimir.fonov@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include "MshLoader.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace igl {
|
||||
// helper function
|
||||
void inline _msh_eat_white_space(std::ifstream& fin) {
|
||||
char next = fin.peek();
|
||||
while (next == '\n' || next == ' ' || next == '\t' || next == '\r') {
|
||||
fin.get();
|
||||
next = fin.peek();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IGL_INLINE igl::MshLoader::MshLoader(const std::string &filename) {
|
||||
std::ifstream fin(filename, std::ios::in | std::ios::binary);
|
||||
|
||||
if (!fin.is_open()) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "failed to open file \"" << filename << "\"";
|
||||
throw std::ios_base::failure(err_msg.str());
|
||||
}
|
||||
// Parse header
|
||||
std::string buf;
|
||||
double version;
|
||||
int type;
|
||||
fin >> buf;
|
||||
if (buf != "$MeshFormat") { throw std::runtime_error("Unexpected .msh format"); }
|
||||
|
||||
fin >> version >> type >> m_data_size;
|
||||
m_binary = (type == 1);
|
||||
if(version>2.2 || version<2.0)
|
||||
{
|
||||
// probably unsupported version
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Error: Unsupported file version:" << version << std::endl;
|
||||
throw std::runtime_error(err_msg.str());
|
||||
|
||||
}
|
||||
// Some sanity check.
|
||||
if (m_data_size != 8) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Error: data size must be 8 bytes." << std::endl;
|
||||
throw std::runtime_error(err_msg.str());
|
||||
}
|
||||
if (sizeof(int) != 4) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Error: code must be compiled with int size 4 bytes." << std::endl;
|
||||
throw std::runtime_error(err_msg.str());
|
||||
}
|
||||
|
||||
// Read in extra info from binary header.
|
||||
if (m_binary) {
|
||||
int one;
|
||||
igl::_msh_eat_white_space(fin);
|
||||
fin.read(reinterpret_cast<char*>(&one), sizeof(int));
|
||||
if (one != 1) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Binary msh file " << filename
|
||||
<< " is saved with different endianness than this machine."
|
||||
<< std::endl;
|
||||
throw std::runtime_error(err_msg.str());
|
||||
}
|
||||
}
|
||||
|
||||
fin >> buf;
|
||||
if (buf != "$EndMeshFormat")
|
||||
{
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Unexpected contents in the file header." << std::endl;
|
||||
throw std::runtime_error(err_msg.str());
|
||||
}
|
||||
|
||||
while (!fin.eof()) {
|
||||
buf.clear();
|
||||
fin >> buf;
|
||||
if (buf == "$Nodes") {
|
||||
parse_nodes(fin);
|
||||
fin >> buf;
|
||||
if (buf != "$EndNodes") { throw std::runtime_error("Unexpected tag"); }
|
||||
} else if (buf == "$Elements") {
|
||||
parse_elements(fin);
|
||||
fin >> buf;
|
||||
if (buf != "$EndElements") { throw std::runtime_error("Unexpected tag"); }
|
||||
} else if (buf == "$NodeData") {
|
||||
parse_node_field(fin);
|
||||
fin >> buf;
|
||||
if (buf != "$EndNodeData") { throw std::runtime_error("Unexpected tag"); }
|
||||
} else if (buf == "$ElementData") {
|
||||
parse_element_field(fin);
|
||||
fin >> buf;
|
||||
if (buf != "$EndElementData") { throw std::runtime_error("Unexpected tag"); }
|
||||
} else if (fin.eof()) {
|
||||
break;
|
||||
} else {
|
||||
parse_unknown_field(fin, buf);
|
||||
}
|
||||
}
|
||||
fin.close();
|
||||
}
|
||||
|
||||
IGL_INLINE void igl::MshLoader::parse_nodes(std::ifstream& fin) {
|
||||
size_t num_nodes;
|
||||
fin >> num_nodes;
|
||||
m_nodes.resize(num_nodes*3);
|
||||
|
||||
if (m_binary) {
|
||||
size_t stride = (4+3*m_data_size);
|
||||
size_t num_bytes = stride * num_nodes;
|
||||
char* data = new char[num_bytes];
|
||||
igl::_msh_eat_white_space(fin);
|
||||
fin.read(data, num_bytes);
|
||||
|
||||
for (size_t i=0; i<num_nodes; i++) {
|
||||
int node_idx;
|
||||
memcpy(&node_idx, data+i*stride, sizeof(int));
|
||||
node_idx-=1;
|
||||
// directly move into vector storage
|
||||
// this works only when m_data_size==sizeof(Float)==sizeof(double)
|
||||
memcpy(&m_nodes[node_idx*3], data+i*stride + 4, m_data_size*3);
|
||||
}
|
||||
delete [] data;
|
||||
} else {
|
||||
int node_idx;
|
||||
for (size_t i=0; i<num_nodes; i++) {
|
||||
fin >> node_idx;
|
||||
node_idx -= 1;
|
||||
// here it's 3D node explicitly
|
||||
fin >> m_nodes[node_idx*3]
|
||||
>> m_nodes[node_idx*3+1]
|
||||
>> m_nodes[node_idx*3+2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IGL_INLINE void igl::MshLoader::parse_elements(std::ifstream& fin) {
|
||||
m_elements_tags.resize(2); //hardcoded to have 2 tags
|
||||
size_t num_elements;
|
||||
fin >> num_elements;
|
||||
|
||||
size_t nodes_per_element;
|
||||
|
||||
if (m_binary) {
|
||||
igl::_msh_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);
|
||||
|
||||
// store node info
|
||||
for (size_t i=0; i<num_elems; i++) {
|
||||
int elem_idx;
|
||||
|
||||
// all elements in the segment share the same elem_type and number of nodes per element
|
||||
m_elements_types.push_back(elem_type);
|
||||
m_elements_lengths.push_back(nodes_per_element);
|
||||
|
||||
fin.read((char*)&elem_idx, sizeof(int));
|
||||
elem_idx -= 1;
|
||||
m_elements_ids.push_back(elem_idx);
|
||||
|
||||
// read first two tags
|
||||
for (size_t j=0; j<num_tags; j++) {
|
||||
int tag;
|
||||
fin.read((char*)&tag, sizeof(int));
|
||||
if(j<2) m_elements_tags[j].push_back(tag);
|
||||
}
|
||||
|
||||
for (size_t j=num_tags; j<2; j++)
|
||||
m_elements_tags[j].push_back(-1); // fill up tags if less then 2
|
||||
|
||||
m_elements_nodes_idx.push_back(m_elements.size());
|
||||
// Element values.
|
||||
for (size_t j=0; j<nodes_per_element; j++) {
|
||||
int idx;
|
||||
fin.read((char*)&idx, sizeof(int));
|
||||
|
||||
m_elements.push_back(idx-1);
|
||||
}
|
||||
}
|
||||
elem_read += num_elems;
|
||||
}
|
||||
} else {
|
||||
for (size_t i=0; i<num_elements; i++) {
|
||||
// Parse per element header
|
||||
int elem_num, elem_type, num_tags;
|
||||
fin >> elem_num >> elem_type >> num_tags;
|
||||
|
||||
// read tags.
|
||||
for (size_t j=0; j<num_tags; j++) {
|
||||
int tag;
|
||||
fin >> tag;
|
||||
if(j<2) m_elements_tags[j].push_back(tag);
|
||||
}
|
||||
for (size_t j=num_tags; j<2; j++)
|
||||
m_elements_tags[j].push_back(-1); // fill up tags if less then 2
|
||||
|
||||
nodes_per_element = num_nodes_per_elem_type(elem_type);
|
||||
m_elements_types.push_back(elem_type);
|
||||
m_elements_lengths.push_back(nodes_per_element);
|
||||
|
||||
elem_num -= 1;
|
||||
m_elements_ids.push_back(elem_num);
|
||||
m_elements_nodes_idx.push_back(m_elements.size());
|
||||
// Parse node idx.
|
||||
for (size_t j=0; j<nodes_per_element; j++) {
|
||||
int idx;
|
||||
fin >> idx;
|
||||
m_elements.push_back(idx-1); // msh index starts from 1.
|
||||
}
|
||||
}
|
||||
}
|
||||
// debug
|
||||
assert(m_elements_types.size() == m_elements_ids.size());
|
||||
assert(m_elements_tags[0].size() == m_elements_ids.size());
|
||||
assert(m_elements_tags[1].size() == m_elements_ids.size());
|
||||
assert(m_elements_lengths.size() == m_elements_ids.size());
|
||||
}
|
||||
|
||||
IGL_INLINE void igl::MshLoader::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::vector<std::string> str_tags(num_string_tags);
|
||||
|
||||
for (size_t i=0; i<num_string_tags; i++) {
|
||||
igl::_msh_eat_white_space(fin);
|
||||
if (fin.peek() == '\"') {
|
||||
// Handle field name between quotes.
|
||||
char buf[128];
|
||||
fin.get(); // remove the quote at the beginning.
|
||||
fin.getline(buf, 128, '\"');
|
||||
str_tags[i] = std::string(buf);
|
||||
} else {
|
||||
fin >> str_tags[i];
|
||||
}
|
||||
}
|
||||
|
||||
fin >> num_real_tags;
|
||||
std::vector<Float> real_tags(num_real_tags);
|
||||
for (size_t i=0; i<num_real_tags; i++)
|
||||
fin >> real_tags[i];
|
||||
|
||||
fin >> num_int_tags;
|
||||
std::vector<int> int_tags(num_int_tags);
|
||||
for (size_t i=0; i<num_int_tags; i++)
|
||||
fin >> int_tags[i];
|
||||
|
||||
if (num_string_tags <= 0 || num_int_tags <= 2) {
|
||||
throw std::runtime_error("Unexpected number of field tags");
|
||||
}
|
||||
std::string fieldname = str_tags[0];
|
||||
int num_components = int_tags[1];
|
||||
int num_entries = int_tags[2];
|
||||
|
||||
std::vector<Float> field( num_entries*num_components );
|
||||
|
||||
if (m_binary) {
|
||||
size_t num_bytes = (num_components * m_data_size + 4) * num_entries;
|
||||
char* data = new char[num_bytes];
|
||||
igl::_msh_eat_white_space(fin);
|
||||
fin.read(data, num_bytes);
|
||||
for (size_t i=0; i<num_entries; i++) {
|
||||
int node_idx;
|
||||
memcpy(&node_idx,&data[i*(4+num_components*m_data_size)],4);
|
||||
|
||||
if(node_idx<1) throw std::runtime_error("Negative or zero index");
|
||||
node_idx -= 1;
|
||||
|
||||
if(node_idx>=num_entries) throw std::runtime_error("Index too big");
|
||||
size_t base_idx = i*(4+num_components*m_data_size) + 4;
|
||||
// TODO: make this work when m_data_size != sizeof(double) ?
|
||||
memcpy(&field[node_idx*num_components], &data[base_idx], num_components*m_data_size);
|
||||
}
|
||||
delete [] data;
|
||||
} else {
|
||||
int node_idx;
|
||||
for (size_t i=0; i<num_entries; i++) {
|
||||
fin >> node_idx;
|
||||
node_idx -= 1;
|
||||
for (size_t j=0; j<num_components; j++) {
|
||||
fin >> field[node_idx*num_components+j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_node_fields_names.push_back(fieldname);
|
||||
m_node_fields.push_back(field);
|
||||
m_node_fields_components.push_back(num_components);
|
||||
}
|
||||
|
||||
IGL_INLINE void igl::MshLoader::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::vector<std::string> str_tags(num_string_tags);
|
||||
for (size_t i=0; i<num_string_tags; i++) {
|
||||
igl::_msh_eat_white_space(fin);
|
||||
if (fin.peek() == '\"') {
|
||||
// Handle field name between quoates.
|
||||
char buf[128];
|
||||
fin.get(); // remove the quote at the beginning.
|
||||
fin.getline(buf, 128, '\"');
|
||||
str_tags[i] = buf;
|
||||
} else {
|
||||
fin >> str_tags[i];
|
||||
}
|
||||
}
|
||||
|
||||
fin >> num_real_tags;
|
||||
std::vector<Float> real_tags(num_real_tags);
|
||||
for (size_t i=0; i<num_real_tags; i++)
|
||||
fin >> real_tags[i];
|
||||
|
||||
fin >> num_int_tags;
|
||||
std::vector<int> int_tags(num_int_tags);
|
||||
for (size_t i=0; i<num_int_tags; i++)
|
||||
fin >> int_tags[i];
|
||||
|
||||
if (num_string_tags <= 0 || num_int_tags <= 2) {
|
||||
throw std::runtime_error("Invalid file format");
|
||||
}
|
||||
std::string fieldname = str_tags[0];
|
||||
int num_components = int_tags[1];
|
||||
int num_entries = int_tags[2];
|
||||
std::vector<Float> field(num_entries*num_components);
|
||||
|
||||
if (m_binary) {
|
||||
size_t num_bytes = (num_components * m_data_size + 4) * num_entries;
|
||||
char* data = new char[num_bytes];
|
||||
igl::_msh_eat_white_space(fin);
|
||||
fin.read(data, num_bytes);
|
||||
for (int i=0; i<num_entries; i++) {
|
||||
int elem_idx;
|
||||
// works with sizeof(int)==4
|
||||
memcpy(&elem_idx, &data[i*(4+num_components*m_data_size)],4);
|
||||
elem_idx -= 1;
|
||||
|
||||
// directly copy data into vector storage space
|
||||
memcpy(&field[elem_idx*num_components], &data[i*(4+num_components*m_data_size) + 4], m_data_size*num_components);
|
||||
}
|
||||
delete [] data;
|
||||
} else {
|
||||
int elem_idx;
|
||||
for (size_t i=0; i<num_entries; i++) {
|
||||
fin >> elem_idx;
|
||||
elem_idx -= 1;
|
||||
for (size_t j=0; j<num_components; j++) {
|
||||
fin >> field[elem_idx*num_components+j];
|
||||
}
|
||||
}
|
||||
}
|
||||
m_element_fields_names.push_back(fieldname);
|
||||
m_element_fields.push_back(field);
|
||||
m_element_fields_components.push_back(num_components);
|
||||
}
|
||||
|
||||
IGL_INLINE void igl::MshLoader::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;
|
||||
}
|
||||
}
|
||||
|
||||
IGL_INLINE int igl::MshLoader::num_nodes_per_elem_type(int elem_type) {
|
||||
int nodes_per_element = 0;
|
||||
switch (elem_type) {
|
||||
case ELEMENT_LINE: // 2-node line
|
||||
nodes_per_element = 2;
|
||||
break;
|
||||
case ELEMENT_TRI:
|
||||
nodes_per_element = 3; // 3-node triangle
|
||||
break;
|
||||
case ELEMENT_QUAD:
|
||||
nodes_per_element = 4; // 5-node quad
|
||||
break;
|
||||
case ELEMENT_TET:
|
||||
nodes_per_element = 4; // 4-node tetrahedra
|
||||
break;
|
||||
case ELEMENT_HEX: // 8-node hexahedron
|
||||
nodes_per_element = 8;
|
||||
break;
|
||||
case ELEMENT_PRISM: // 6-node prism
|
||||
nodes_per_element = 6;
|
||||
break;
|
||||
case ELEMENT_LINE_2ND_ORDER:
|
||||
nodes_per_element = 3;
|
||||
break;
|
||||
case ELEMENT_TRI_2ND_ORDER:
|
||||
nodes_per_element = 6;
|
||||
break;
|
||||
case ELEMENT_QUAD_2ND_ORDER:
|
||||
nodes_per_element = 9;
|
||||
break;
|
||||
case ELEMENT_TET_2ND_ORDER:
|
||||
nodes_per_element = 10;
|
||||
break;
|
||||
case ELEMENT_HEX_2ND_ORDER:
|
||||
nodes_per_element = 27;
|
||||
break;
|
||||
case ELEMENT_PRISM_2ND_ORDER:
|
||||
nodes_per_element = 18;
|
||||
break;
|
||||
case ELEMENT_PYRAMID_2ND_ORDER:
|
||||
nodes_per_element = 14;
|
||||
break;
|
||||
case ELEMENT_POINT: // 1-node point
|
||||
nodes_per_element = 1;
|
||||
break;
|
||||
default:
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Element type (" << elem_type << ") is not supported yet."
|
||||
<< std::endl;
|
||||
throw std::runtime_error(err_msg.str());
|
||||
}
|
||||
return nodes_per_element;
|
||||
}
|
||||
|
||||
|
||||
IGL_INLINE bool igl::MshLoader::is_element_map_identity() const
|
||||
{
|
||||
for(int i=0;i<m_elements_ids.size();i++) {
|
||||
int id=m_elements_ids[i];
|
||||
if (id!=i) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
IGL_INLINE void igl::MshLoader::index_structures(int tag_column)
|
||||
{
|
||||
//cleanup
|
||||
m_structure_index.clear();
|
||||
m_structures.clear();
|
||||
m_structure_length.clear();
|
||||
|
||||
//index structure tags
|
||||
for(auto i=0; i != m_elements_tags[tag_column].size(); ++i )
|
||||
{
|
||||
m_structure_index.insert(
|
||||
std::pair<msh_struct,int>(
|
||||
msh_struct( m_elements_tags[tag_column][i],
|
||||
m_elements_types[i]), i)
|
||||
);
|
||||
}
|
||||
|
||||
// identify unique structures
|
||||
std::vector<StructIndex::value_type> _unique_structs;
|
||||
std::unique_copy(std::begin(m_structure_index),
|
||||
std::end(m_structure_index),
|
||||
std::back_inserter(_unique_structs),
|
||||
[](const StructIndex::value_type &c1, const StructIndex::value_type &c2)
|
||||
{ return c1.first == c2.first; });
|
||||
|
||||
std::for_each( _unique_structs.begin(), _unique_structs.end(),
|
||||
[this](const StructIndex::value_type &n){ this->m_structures.push_back(n.first); });
|
||||
|
||||
for(auto t = m_structures.begin(); t != m_structures.end(); ++t)
|
||||
{
|
||||
// identify all elements corresponding to this tag
|
||||
auto structure_range = m_structure_index.equal_range( *t );
|
||||
int cnt=0;
|
||||
|
||||
for(auto i=structure_range.first; i!=structure_range.second; i++)
|
||||
cnt++;
|
||||
|
||||
m_structure_length.insert( std::pair<msh_struct,int>( *t, cnt));
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
namespace igl {
|
||||
|
||||
// Class for loading information from .msh file
|
||||
// depends only on c++stl library
|
||||
/// Class for loading information from .msh file
|
||||
/// depends only on c++stl library
|
||||
class MshLoader {
|
||||
public:
|
||||
|
||||
@@ -60,6 +60,8 @@ class MshLoader {
|
||||
// other elements
|
||||
ELEMENT_POINT=15 };
|
||||
public:
|
||||
/// Load a .msh file from a given path
|
||||
/// @param[in] filename path to .msh
|
||||
MshLoader(const std::string &filename);
|
||||
|
||||
public:
|
||||
@@ -187,4 +189,4 @@ class MshLoader {
|
||||
# include "MshLoader.cpp"
|
||||
#endif
|
||||
|
||||
#endif //IGL_MSH_LOADER_H
|
||||
#endif //IGL_MSH_LOADER_H
|
||||
|
||||
@@ -1,347 +0,0 @@
|
||||
// based on MSH writer from PyMesh
|
||||
|
||||
// Copyright (c) 2015 Qingnan Zhou <qzhou@adobe.com>
|
||||
// Copyright (C) 2020 Vladimir Fonov <vladimir.fonov@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include "MshSaver.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
|
||||
|
||||
IGL_INLINE igl::MshSaver::MshSaver(const std::string& filename, bool binary) :
|
||||
m_binary(binary), m_num_nodes(0), m_num_elements(0) {
|
||||
if (!m_binary) {
|
||||
fout.open(filename.c_str(), std::fstream::out);
|
||||
} else {
|
||||
fout.open(filename.c_str(), std::fstream::binary);
|
||||
}
|
||||
if (!fout) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Error opening " << filename << " to write msh file." << std::endl;
|
||||
throw std::ios_base::failure(err_msg.str());
|
||||
}
|
||||
}
|
||||
|
||||
IGL_INLINE igl::MshSaver::~MshSaver() {
|
||||
fout.close();
|
||||
}
|
||||
|
||||
IGL_INLINE void igl::MshSaver::save_mesh(
|
||||
const FloatVector& nodes,
|
||||
const IndexVector& elements,
|
||||
const IntVector& element_lengths,
|
||||
const IntVector& element_types,
|
||||
const IntVector& element_tags
|
||||
) {
|
||||
|
||||
save_header();
|
||||
|
||||
save_nodes(nodes);
|
||||
|
||||
save_elements(elements, element_lengths, element_types, element_tags );
|
||||
}
|
||||
|
||||
IGL_INLINE void igl::MshSaver::save_header() {
|
||||
if (!m_binary) {
|
||||
fout << "$MeshFormat" << std::endl;
|
||||
fout << "2.2 0 " << sizeof(double) << std::endl;
|
||||
fout << "$EndMeshFormat" << std::endl;
|
||||
fout.precision(17);
|
||||
} else {
|
||||
fout << "$MeshFormat" << std::endl;
|
||||
fout << "2.2 1 " << sizeof(double) << std::endl;
|
||||
int one = 1;
|
||||
fout.write((char*)&one, sizeof(int));
|
||||
fout << "\n$EndMeshFormat" << std::endl;
|
||||
}
|
||||
fout.flush();
|
||||
}
|
||||
|
||||
IGL_INLINE void igl::MshSaver::save_nodes(const FloatVector& nodes) {
|
||||
// Save nodes.
|
||||
// 3D hadrcoded
|
||||
m_num_nodes = nodes.size() / 3;
|
||||
fout << "$Nodes" << std::endl;
|
||||
fout << m_num_nodes << std::endl;
|
||||
if (!m_binary) {
|
||||
for (size_t i=0; i<nodes.size(); i+=3) {
|
||||
//const VectorF& v = nodes.segment(i,m_dim);
|
||||
int node_idx = i/3 + 1;
|
||||
fout << node_idx << " " << nodes[i] << " " << nodes[i+1] << " " << nodes[i+2] << std::endl;
|
||||
}
|
||||
} else {
|
||||
for (size_t i=0; i<nodes.size(); i+=3) {
|
||||
//const VectorF& v = nodes.segment(i,m_dim);
|
||||
int node_idx = i/3 + 1;
|
||||
fout.write((const char*)&node_idx, sizeof(int));
|
||||
fout.write((const char*)&nodes[i], sizeof(Float)*3);
|
||||
}
|
||||
}
|
||||
fout << "$EndNodes" << std::endl;
|
||||
fout.flush();
|
||||
}
|
||||
|
||||
IGL_INLINE void igl::MshSaver::save_elements(const IndexVector& elements,
|
||||
const IntVector& element_lengths,
|
||||
const IntVector& element_types,
|
||||
const IntVector& element_tags)
|
||||
{
|
||||
|
||||
m_num_elements = element_tags.size();
|
||||
assert(element_lengths.size() == element_types.size() );
|
||||
assert(element_lengths.size() == element_tags.size() );
|
||||
// TODO: sum up all lengths
|
||||
// Save elements.
|
||||
// node inxes are 1-based
|
||||
fout << "$Elements" << std::endl;
|
||||
fout << m_num_elements << std::endl;
|
||||
|
||||
if (m_num_elements > 0) {
|
||||
//int elem_type = el_type;
|
||||
int num_elems = m_num_elements;
|
||||
//int tags = 0;
|
||||
if (!m_binary) {
|
||||
size_t el_ptr=0;
|
||||
for (size_t i=0;i<m_num_elements;++i) {
|
||||
|
||||
int elem_num = (int) i + 1;
|
||||
///VectorI elem = elements.segment(i, nodes_per_element) + VectorI::Ones(nodes_per_element);
|
||||
// hardcoded: duplicate tags (I don't know why)
|
||||
fout << elem_num << " " << element_types[i] << " " << 2 << " "<< element_tags[i] << " "<< element_tags[i] << " ";
|
||||
for (size_t j=0; j<element_lengths[i]; j++) {
|
||||
fout << elements[el_ptr + j] + 1 << " ";
|
||||
}
|
||||
fout << std::endl;
|
||||
el_ptr+=element_lengths[i];
|
||||
}
|
||||
} else {
|
||||
size_t el_ptr=0,i=0;
|
||||
while(i<m_num_elements) {
|
||||
|
||||
// write elements in consistent chunks
|
||||
// TODO: refactor this code to be able to specify different elements
|
||||
// more effeciently
|
||||
|
||||
int elem_type=-1;
|
||||
int elem_len=-1;
|
||||
size_t j=i;
|
||||
for(;j<m_num_elements;++j)
|
||||
{
|
||||
if( elem_type==-1 )
|
||||
{
|
||||
elem_type=element_types[j];
|
||||
elem_len=element_lengths[j];
|
||||
} else if( elem_type!=element_types[j] ||
|
||||
elem_len!=element_lengths[j]) {
|
||||
break; // found the edge of the segment
|
||||
}
|
||||
}
|
||||
|
||||
//hardcoded: 2 tags
|
||||
int num_elems=j-i, num_tags=2;
|
||||
|
||||
fout.write((const char*)& elem_type, sizeof(int));
|
||||
fout.write((const char*)& num_elems, sizeof(int));
|
||||
fout.write((const char*)& num_tags, sizeof(int));
|
||||
|
||||
for(int k=0;k<num_elems; ++k,++i){
|
||||
int elem_num = (int )i + 1;
|
||||
fout.write((const char*)&elem_num, sizeof(int));
|
||||
|
||||
// HACK: hardcoded 2 tags
|
||||
fout.write((const char*)& element_tags[i], sizeof(int));
|
||||
fout.write((const char*)& element_tags[i], sizeof(int));
|
||||
|
||||
for (size_t e=0; e<elem_len; e++) {
|
||||
int _elem = static_cast<int>( elements[el_ptr + e] )+1;
|
||||
fout.write((const char*)&_elem, sizeof(int));
|
||||
}
|
||||
el_ptr+=elem_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fout << "$EndElements" << std::endl;
|
||||
fout.flush();
|
||||
}
|
||||
|
||||
IGL_INLINE void igl::MshSaver::save_scalar_field(const std::string& fieldname, const FloatVector& field) {
|
||||
assert(field.size() == m_num_nodes);
|
||||
fout << "$NodeData" << std::endl;
|
||||
fout << "1" << std::endl; // num string tags.
|
||||
fout << "\"" << fieldname << "\"" << std::endl;
|
||||
fout << "1" << std::endl; // num real tags.
|
||||
fout << "0.0" << std::endl; // time value.
|
||||
fout << "3" << std::endl; // num int tags.
|
||||
fout << "0" << std::endl; // the time step
|
||||
fout << "1" << std::endl; // 1-component scalar field.
|
||||
fout << m_num_nodes << std::endl; // number of nodes
|
||||
|
||||
if (m_binary) {
|
||||
for (size_t i=0; i<m_num_nodes; i++) {
|
||||
int node_idx = i+1;
|
||||
fout.write((char*)&node_idx, sizeof(int));
|
||||
fout.write((char*)&field[i], sizeof(Float));
|
||||
}
|
||||
} else {
|
||||
for (size_t i=0; i<m_num_nodes; i++) {
|
||||
int node_idx = i+1;
|
||||
fout << node_idx << " " << field[i] << std::endl;
|
||||
}
|
||||
}
|
||||
fout << "$EndNodeData" << std::endl;
|
||||
fout.flush();
|
||||
}
|
||||
|
||||
IGL_INLINE void igl::MshSaver::save_vector_field(const std::string& fieldname, const FloatVector& field) {
|
||||
assert(field.size() == 3 * m_num_nodes);
|
||||
|
||||
fout << "$NodeData" << std::endl;
|
||||
fout << "1" << std::endl; // num string tags.
|
||||
fout << "\"" << fieldname << "\"" << std::endl;
|
||||
fout << "1" << std::endl; // num real tags.
|
||||
fout << "0.0" << std::endl; // time value.
|
||||
fout << "3" << std::endl; // num int tags.
|
||||
fout << "0" << std::endl; // the time step
|
||||
fout << "3" << std::endl; // 3-component vector field.
|
||||
fout << m_num_nodes << std::endl; // number of nodes
|
||||
|
||||
const Float zero = 0.0;
|
||||
if (m_binary) {
|
||||
for (size_t i=0; i<m_num_nodes; i++) {
|
||||
int node_idx = i+1;
|
||||
fout.write((const char*)&node_idx, sizeof(int));
|
||||
fout.write((const char*)&field[i*3], sizeof(Float)*3);
|
||||
}
|
||||
} else {
|
||||
for (size_t i=0; i<m_num_nodes; i++) {
|
||||
int node_idx = i+1;
|
||||
fout << node_idx
|
||||
<< " " << field[i*3]
|
||||
<< " " << field[i*3+1]
|
||||
<< " " << field[i*3+2]
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
fout << "$EndNodeData" << std::endl;
|
||||
fout.flush();
|
||||
}
|
||||
|
||||
IGL_INLINE void igl::MshSaver::save_elem_scalar_field(const std::string& fieldname, const FloatVector& field) {
|
||||
assert(field.size() == m_num_elements);
|
||||
fout << "$ElementData" << std::endl;
|
||||
fout << 1 << std::endl; // num string tags.
|
||||
fout << "\"" << fieldname << "\"" << std::endl;
|
||||
fout << "1" << std::endl; // num real tags.
|
||||
fout << "0.0" << std::endl; // time value.
|
||||
fout << "3" << std::endl; // num int tags.
|
||||
fout << "0" << std::endl; // the time step
|
||||
fout << "1" << std::endl; // 1-component scalar field.
|
||||
fout << m_num_elements << std::endl; // number of elements
|
||||
|
||||
if (m_binary) {
|
||||
for (size_t i=0; i<m_num_elements; i++) {
|
||||
int elem_idx = i+1;
|
||||
fout.write((const char*)&elem_idx, sizeof(int));
|
||||
fout.write((const char*)&field[i], sizeof(Float));
|
||||
}
|
||||
} else {
|
||||
for (size_t i=0; i<m_num_elements; i++) {
|
||||
int elem_idx = i+1;
|
||||
fout << elem_idx << " " << field[i] << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
fout << "$EndElementData" << std::endl;
|
||||
fout.flush();
|
||||
}
|
||||
|
||||
IGL_INLINE void igl::MshSaver::save_elem_vector_field(const std::string& fieldname, const FloatVector& field) {
|
||||
assert(field.size() == m_num_elements * 3);
|
||||
fout << "$ElementData" << std::endl;
|
||||
fout << 1 << std::endl; // num string tags.
|
||||
fout << "\"" << fieldname << "\"" << std::endl;
|
||||
fout << "1" << std::endl; // num real tags.
|
||||
fout << "0.0" << std::endl; // time value.
|
||||
fout << "3" << std::endl; // num int tags.
|
||||
fout << "0" << std::endl; // the time step
|
||||
fout << "3" << std::endl; // 3-component vector field.
|
||||
fout << m_num_elements << std::endl; // number of elements
|
||||
|
||||
const Float zero = 0.0;
|
||||
if (m_binary) {
|
||||
for (size_t i=0; i<m_num_elements; ++i) {
|
||||
int elem_idx = i+1;
|
||||
fout.write((const char*)&elem_idx, sizeof(int));
|
||||
fout.write((const char*)&field[i*3], sizeof(Float) * 3);
|
||||
}
|
||||
} else {
|
||||
for (size_t i=0; i<m_num_elements; ++i) {
|
||||
int elem_idx = i+1;
|
||||
fout << elem_idx
|
||||
<< " " << field[i*3]
|
||||
<< " " << field[i*3+1]
|
||||
<< " " << field[i*3+2]
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
fout << "$EndElementData" << std::endl;
|
||||
fout.flush();
|
||||
}
|
||||
|
||||
|
||||
IGL_INLINE void igl::MshSaver::save_elem_tensor_field(const std::string& fieldname, const FloatVector& field) {
|
||||
assert(field.size() == m_num_elements * 3 * (3 + 1) / 2);
|
||||
fout << "$ElementData" << std::endl;
|
||||
fout << 1 << std::endl; // num string tags.
|
||||
fout << "\"" << fieldname << "\"" << std::endl;
|
||||
fout << "1" << std::endl; // num real tags.
|
||||
fout << "0.0" << std::endl; // time value.
|
||||
fout << "3" << std::endl; // num int tags.
|
||||
fout << "0" << std::endl; // the time step
|
||||
fout << "9" << std::endl; // 9-component tensor field.
|
||||
fout << m_num_elements << std::endl; // number of elements
|
||||
|
||||
const Float zero = 0.0;
|
||||
|
||||
if (m_binary) {
|
||||
for (size_t i=0; i<m_num_elements; i++) {
|
||||
int elem_idx = i+1;
|
||||
fout.write((char*)&elem_idx, sizeof(int));
|
||||
//const VectorF& val = field.segment(i*6, 6);
|
||||
const Float* val = &field[i*6];
|
||||
Float tensor[9] = {
|
||||
val[0], val[5], val[4],
|
||||
val[5], val[1], val[3],
|
||||
val[4], val[3], val[2] };
|
||||
fout.write((char*)tensor, sizeof(Float) * 9);
|
||||
}
|
||||
} else {
|
||||
for (size_t i=0; i<m_num_elements; i++) {
|
||||
int elem_idx = i+1;
|
||||
const Float* val = &field[i*6];
|
||||
fout << elem_idx
|
||||
<< " " << val[0]
|
||||
<< " " << val[5]
|
||||
<< " " << val[4]
|
||||
<< " " << val[5]
|
||||
<< " " << val[1]
|
||||
<< " " << val[3]
|
||||
<< " " << val[4]
|
||||
<< " " << val[3]
|
||||
<< " " << val[2]
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
fout << "$EndElementData" << std::endl;
|
||||
fout.flush();
|
||||
}
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
namespace igl {
|
||||
|
||||
// Class for dumping information to .msh file
|
||||
// depends only on c++stl library
|
||||
// current implementation works only with 3D information
|
||||
/// Class for dumping information to .msh file
|
||||
/// depends only on c++stl library
|
||||
/// current implementation works only with 3D information
|
||||
class MshSaver {
|
||||
public:
|
||||
typedef double Float;
|
||||
@@ -30,6 +30,9 @@ class MshSaver {
|
||||
typedef std::vector<IntVector> IntField;
|
||||
typedef std::vector<std::string> FieldNames;
|
||||
|
||||
/// Write a .msh to a given path
|
||||
/// @param[in] filename path to output file
|
||||
/// @param[in] binary whether to write in binary format
|
||||
MshSaver(const std::string& filename, bool binary=true);
|
||||
~MshSaver();
|
||||
|
||||
|
||||
@@ -10,14 +10,15 @@
|
||||
|
||||
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
|
||||
/// Type of mesh normal computation method
|
||||
enum NormalType
|
||||
{
|
||||
/// Normals computed per vertex based on incident faces
|
||||
PER_VERTEX_NORMALS,
|
||||
/// Normals computed per face
|
||||
PER_FACE_NORMALS,
|
||||
/// Normals computed per corner (aka wedge) based on incident faces without
|
||||
/// sharp edge
|
||||
PER_CORNER_NORMALS
|
||||
};
|
||||
# define NUM_NORMAL_TYPE 3
|
||||
|
||||
+3
-3
@@ -9,9 +9,9 @@
|
||||
#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);
|
||||
/// 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;
|
||||
|
||||
@@ -11,8 +11,10 @@ namespace igl
|
||||
{
|
||||
// Use standard mathematical constants' M_PI if available
|
||||
#ifdef M_PI
|
||||
/// π
|
||||
constexpr double PI = M_PI;
|
||||
#else
|
||||
/// π
|
||||
constexpr double PI = 3.1415926535897932384626433832795;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -35,9 +35,17 @@
|
||||
|
||||
#else
|
||||
|
||||
/// Bold red colored text
|
||||
/// @param[in] X text to color
|
||||
/// @returns colored text as "stream"
|
||||
/// #### Example:
|
||||
///
|
||||
/// \code{cpp}
|
||||
/// std::cout<<REDRUM("File "<<filename<<" not found.")<<std::endl;
|
||||
/// \endcode
|
||||
#define REDRUM(X) "\e[1m\e[31m"<<X<<"\e[m"
|
||||
// Bold Red, etc.
|
||||
#define NORUM(X) ""<<X<<""
|
||||
#define REDRUM(X) "\e[1m\e[31m"<<X<<"\e[m"
|
||||
#define GREENRUM(X) "\e[1m\e[32m"<<X<<"\e[m"
|
||||
#define YELLOWRUM(X) "\e[1m\e[33m"<<X<<"\e[m"
|
||||
#define BLUERUM(X) "\e[1m\e[34m"<<X<<"\e[m"
|
||||
|
||||
+16
-5
@@ -7,12 +7,23 @@
|
||||
// obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#ifndef IGL_STR_H
|
||||
#define IGL_STR_H
|
||||
// http://stackoverflow.com/a/2433143/148668
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
// Suppose you have a function:
|
||||
// void func(std::string c);
|
||||
// Then you can write:
|
||||
// func(STR("foo"<<1<<"bar"));
|
||||
/// Convert a stream of things to std:;string
|
||||
///
|
||||
/// Suppose you have a function:
|
||||
/// \code{cpp}
|
||||
/// void func(std::string s);
|
||||
/// \endcode
|
||||
/// Then you can write:
|
||||
/// \code{cpp}
|
||||
/// func(C_STR("foo"<<1<<"bar"));
|
||||
/// \endcode
|
||||
/// which is equivalent to:
|
||||
/// \code{cpp}
|
||||
/// func("foo1bar");
|
||||
/// \endcode
|
||||
///
|
||||
// http://stackoverflow.com/a/2433143/148668
|
||||
#define STR(X) static_cast<std::ostringstream&>(std::ostringstream().flush() << X).str()
|
||||
#endif
|
||||
|
||||
@@ -9,14 +9,16 @@
|
||||
#define IGL_SOLVER_STATUS_H
|
||||
namespace igl
|
||||
{
|
||||
/// Solver status type used by min_quad_with_fixed
|
||||
enum SolverStatus
|
||||
{
|
||||
// Good
|
||||
// Good. Solver declared convergence
|
||||
SOLVER_STATUS_CONVERGED = 0,
|
||||
// OK
|
||||
// OK. Solver reached max iterations
|
||||
SOLVER_STATUS_MAX_ITER = 1,
|
||||
// Bad
|
||||
// Bad. Solver reported failure
|
||||
SOLVER_STATUS_ERROR = 2,
|
||||
// Total number of solver types
|
||||
NUM_SOLVER_STATUSES = 3,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -14,16 +14,23 @@
|
||||
|
||||
namespace igl
|
||||
{
|
||||
// Templates:
|
||||
// T should be a matrix that implements .size(), and operator(int i)
|
||||
/// A row of things that can be sorted against other rows
|
||||
/// @tparam T should be a vector/matrix/array that implements .size(), and operator(int i)
|
||||
template <typename T>
|
||||
class SortableRow
|
||||
{
|
||||
public:
|
||||
/// The data
|
||||
T data;
|
||||
public:
|
||||
/// Default constructor
|
||||
SortableRow():data(){};
|
||||
/// Constructor
|
||||
/// @param[in] data the data
|
||||
SortableRow(const T & data):data(data){};
|
||||
/// Less than comparison
|
||||
/// @param[in] that the other row
|
||||
/// @returns true if this row is less than that row
|
||||
bool operator<(const SortableRow & that) const
|
||||
{
|
||||
// Lexicographical
|
||||
@@ -41,6 +48,9 @@ namespace igl
|
||||
// All characters the same, comes done to length
|
||||
return this->data.size()<that.data.size();
|
||||
};
|
||||
/// Equality comparison
|
||||
/// @param[in] that the other row
|
||||
/// @returns true if this row is equal to that row
|
||||
bool operator==(const SortableRow & that) const
|
||||
{
|
||||
if(this->data.size() != that.data.size())
|
||||
@@ -56,6 +66,9 @@ namespace igl
|
||||
}
|
||||
return true;
|
||||
};
|
||||
/// Inequality comparison
|
||||
/// @param[in] that the other row
|
||||
/// @returns true if this row is not equal to that row
|
||||
bool operator!=(const SortableRow & that) const
|
||||
{
|
||||
return !(*this == that);
|
||||
|
||||
+16
-8
@@ -25,10 +25,11 @@
|
||||
|
||||
namespace igl
|
||||
{
|
||||
/// Simple timer class
|
||||
class Timer
|
||||
{
|
||||
public:
|
||||
// default constructor
|
||||
/// default constructor
|
||||
Timer():
|
||||
stopped(0),
|
||||
#ifdef WIN32
|
||||
@@ -64,7 +65,10 @@ namespace igl
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
//Raw mach_absolute_times going in, difference in seconds out
|
||||
/// Raw mach_absolute_times going in, difference in seconds out
|
||||
/// @param[in] endTime end time
|
||||
/// @param[in] startTime start time
|
||||
/// @return time
|
||||
double subtractTimes( uint64_t endTime, uint64_t startTime )
|
||||
{
|
||||
uint64_t difference = endTime - startTime;
|
||||
@@ -84,7 +88,7 @@ namespace igl
|
||||
}
|
||||
#endif
|
||||
|
||||
// start timer
|
||||
/// start timer
|
||||
void start()
|
||||
{
|
||||
stopped = 0; // reset stop flag
|
||||
@@ -98,7 +102,7 @@ namespace igl
|
||||
|
||||
}
|
||||
|
||||
// stop the timer
|
||||
/// stop the timer
|
||||
void stop()
|
||||
{
|
||||
stopped = 1; // set timer stopped flag
|
||||
@@ -112,23 +116,27 @@ namespace igl
|
||||
#endif
|
||||
|
||||
}
|
||||
// get elapsed time in second
|
||||
/// get elapsed time in second
|
||||
/// @return time in seconds
|
||||
double getElapsedTime()
|
||||
{
|
||||
return this->getElapsedTimeInSec();
|
||||
}
|
||||
// get elapsed time in second (same as getElapsedTime)
|
||||
/// get elapsed time in second (same as getElapsedTime)
|
||||
/// @return time
|
||||
double getElapsedTimeInSec()
|
||||
{
|
||||
return this->getElapsedTimeInMicroSec() * 0.000001;
|
||||
}
|
||||
|
||||
// get elapsed time in milli-second
|
||||
/// get elapsed time in milli-second
|
||||
/// @return time
|
||||
double getElapsedTimeInMilliSec()
|
||||
{
|
||||
return this->getElapsedTimeInMicroSec() * 0.001;
|
||||
}
|
||||
// get elapsed time in micro-second
|
||||
/// get elapsed time in micro-second
|
||||
/// @return time
|
||||
double getElapsedTimeInMicroSec()
|
||||
{
|
||||
double startTimeInMicroSec = 0;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
namespace igl
|
||||
{
|
||||
/// @private
|
||||
// Simple Viewport class for an opengl context. Handles reshaping and mouse.
|
||||
struct Viewport
|
||||
{
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
namespace igl
|
||||
{
|
||||
/// Class for building an AABB tree to implement the divide and conquer
|
||||
/// algorithm described in [Jacobson et al. 2013].
|
||||
template <
|
||||
typename Point,
|
||||
typename DerivedV,
|
||||
@@ -38,13 +40,20 @@ namespace igl
|
||||
total_positive_area(std::numeric_limits<typename DerivedV::Scalar>::infinity()),
|
||||
split_method(MEDIAN_ON_LONGEST_AXIS)
|
||||
{}
|
||||
/// Constructor
|
||||
///
|
||||
/// @param[in] V #V by 3 list of vertex positions
|
||||
/// @param[in] F #F by 3 list of triangle indices into V
|
||||
inline WindingNumberAABB(
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
const Eigen::MatrixBase<DerivedF> & F);
|
||||
inline WindingNumberAABB(
|
||||
const WindingNumberTree<Point,DerivedV,DerivedF> & parent,
|
||||
const Eigen::MatrixBase<DerivedF> & F);
|
||||
// Initialize some things
|
||||
/// Initialize the hierarchy to a given mesh
|
||||
///
|
||||
/// @param[in] V #V by 3 list of vertex positions
|
||||
/// @param[in] F #F by 3 list of triangle indices into V
|
||||
inline void set_mesh(
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
const Eigen::MatrixBase<DerivedF> & F);
|
||||
|
||||
@@ -9,14 +9,15 @@
|
||||
#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 hierarchical evaluation
|
||||
EXACT_WINDING_NUMBER_METHOD = 0,
|
||||
// poor approximation
|
||||
APPROX_SIMPLE_WINDING_NUMBER_METHOD = 1,
|
||||
// another poor approximation
|
||||
APPROX_CACHE_WINDING_NUMBER_METHOD = 2,
|
||||
/// Number of winding number methods
|
||||
NUM_WINDING_NUMBER_METHODS = 3
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,10 +14,9 @@
|
||||
|
||||
namespace igl
|
||||
{
|
||||
// Space partitioning tree for computing winding number hierarchically.
|
||||
//
|
||||
// Templates:
|
||||
// Point type for points in space, e.g. Eigen::Vector3d
|
||||
/// Space partitioning tree for computing winding number hierarchically.
|
||||
///
|
||||
/// @tparam Point type for points in space, e.g. Eigen::Vector3d
|
||||
template <
|
||||
typename Point,
|
||||
typename DerivedV,
|
||||
@@ -143,8 +142,8 @@ namespace igl
|
||||
#include "triangle_fan.h"
|
||||
#include "exterior_edges.h"
|
||||
|
||||
#include <igl/PI.h>
|
||||
#include <igl/remove_duplicate_vertices.h>
|
||||
#include "PI.h"
|
||||
#include "remove_duplicate_vertices.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2018 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
// obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#include "accumarray.h"
|
||||
#include <cassert>
|
||||
|
||||
template <
|
||||
typename DerivedS,
|
||||
typename DerivedV,
|
||||
typename DerivedA
|
||||
>
|
||||
void igl::accumarray(
|
||||
const Eigen::MatrixBase<DerivedS> & S,
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
Eigen::PlainObjectBase<DerivedA> & A)
|
||||
{
|
||||
assert(V.size() == S.size() && "S and V should be same size");
|
||||
if(S.size() == 0) { A.resize(0,1); return; }
|
||||
A.setZero(S.maxCoeff()+1,1);
|
||||
for(int s = 0;s<S.size();s++)
|
||||
{
|
||||
A(S(s)) += V(s);
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename DerivedS,
|
||||
typename DerivedA
|
||||
>
|
||||
void igl::accumarray(
|
||||
const Eigen::MatrixBase<DerivedS> & S,
|
||||
const typename DerivedA::Scalar V,
|
||||
Eigen::PlainObjectBase<DerivedA> & A)
|
||||
{
|
||||
if(S.size() == 0) { A.resize(0,1); return; }
|
||||
A.setZero(S.maxCoeff()+1,1);
|
||||
for(int s = 0;s<S.size();s++)
|
||||
{
|
||||
A(S(s)) += V;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef IGL_STATIC_LIBRARY
|
||||
// Explicit template instantiation
|
||||
// generated by autoexplicit.sh
|
||||
template void igl::accumarray<Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::Matrix<int, -1, 1, 0, -1, 1>::Scalar, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
|
||||
template void igl::accumarray<Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
|
||||
#endif
|
||||
+10
-13
@@ -11,14 +11,11 @@
|
||||
#include <Eigen/Core>
|
||||
namespace igl
|
||||
{
|
||||
// ACCUMARRY Like Matlab's accumarray. Accumulate values in V using subscripts
|
||||
// in S.
|
||||
//
|
||||
// Inputs:
|
||||
// S #S list of subscripts
|
||||
// V #V list of values
|
||||
// Outputs:
|
||||
// A max(subs)+1 list of accumulated values
|
||||
/// Accumulate values in V using subscripts in S. Like Matlab's accumarray.
|
||||
///
|
||||
/// @param[in] S #S list of subscripts
|
||||
/// @param[in] V #V list of values
|
||||
/// @param[out] A max(subs)+1 list of accumulated values
|
||||
template <
|
||||
typename DerivedS,
|
||||
typename DerivedV,
|
||||
@@ -28,11 +25,11 @@ namespace igl
|
||||
const Eigen::MatrixBase<DerivedS> & S,
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
Eigen::PlainObjectBase<DerivedA> & A);
|
||||
// Inputs:
|
||||
// S #S list of subscripts
|
||||
// V single value used for all
|
||||
// Outputs:
|
||||
// A max(subs)+1 list of accumulated values
|
||||
/// Accumulate constant value `V` using subscripts in S. Like Matlab's accumarray.
|
||||
///
|
||||
/// @param[in] S #S list of subscripts
|
||||
/// @param[in] V single value used for all
|
||||
/// @param[out] A max(subs)+1 list of accumulated values
|
||||
template <
|
||||
typename DerivedS,
|
||||
typename DerivedA
|
||||
|
||||
@@ -1,370 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <iostream>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
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<AT>& A,
|
||||
const Eigen::PlainObjectBase<DerivedB> & B,
|
||||
const Eigen::PlainObjectBase<Derivedknown> & known,
|
||||
const Eigen::PlainObjectBase<DerivedY> & Y,
|
||||
const Eigen::SparseMatrix<AeqT>& Aeq,
|
||||
const Eigen::PlainObjectBase<DerivedBeq> & Beq,
|
||||
const Eigen::SparseMatrix<AieqT>& Aieq,
|
||||
const Eigen::PlainObjectBase<DerivedBieq> & Bieq,
|
||||
const Eigen::PlainObjectBase<Derivedlx> & p_lx,
|
||||
const Eigen::PlainObjectBase<Derivedux> & p_ux,
|
||||
const igl::active_set_params & params,
|
||||
Eigen::PlainObjectBase<DerivedZ> & 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<typename Derivedlx::Scalar,Eigen::Dynamic,1> lx;
|
||||
Eigen::Matrix<typename Derivedux::Scalar,Eigen::Dynamic,1> ux;
|
||||
if(p_lx.size() == 0)
|
||||
{
|
||||
lx = Derivedlx::Constant(
|
||||
n,1,-numeric_limits<typename Derivedlx::Scalar>::max());
|
||||
}else
|
||||
{
|
||||
lx = p_lx;
|
||||
}
|
||||
if(p_ux.size() == 0)
|
||||
{
|
||||
ux = Derivedux::Constant(
|
||||
n,1,numeric_limits<typename Derivedux::Scalar>::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<BOOL,Dynamic,1> as_lx = Matrix<BOOL,Dynamic,1>::Constant(n,1,FALSE);
|
||||
Matrix<BOOL,Dynamic,1> as_ux = Matrix<BOOL,Dynamic,1>::Constant(n,1,FALSE);
|
||||
Matrix<BOOL,Dynamic,1> as_ieq = Matrix<BOOL,Dynamic,1>::Constant(Aieq.rows(),1,FALSE);
|
||||
|
||||
// Keep track of previous Z for comparison
|
||||
DerivedZ old_Z;
|
||||
old_Z = DerivedZ::Constant(
|
||||
n,1,numeric_limits<typename DerivedZ::Scalar>::max());
|
||||
|
||||
int iter = 0;
|
||||
while(true)
|
||||
{
|
||||
#ifdef ACTIVE_SET_CPP_DEBUG
|
||||
cout<<"Iteration: "<<iter<<":"<<endl;
|
||||
cout<<" pre"<<endl;
|
||||
#endif
|
||||
// FIND BREACHES OF CONSTRAINTS
|
||||
int new_as_lx = 0;
|
||||
int new_as_ux = 0;
|
||||
int new_as_ieq = 0;
|
||||
if(Z.size() > 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<Aieq.rows();a++)
|
||||
{
|
||||
if(AieqZ(a) > Bieq(a))
|
||||
{
|
||||
new_as_ieq += (as_ieq(a)?0:1);
|
||||
as_ieq(a) = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef ACTIVE_SET_CPP_DEBUG
|
||||
cout<<" new_as_lx: "<<new_as_lx<<endl;
|
||||
cout<<" new_as_ux: "<<new_as_ux<<endl;
|
||||
#endif
|
||||
const double diff = (Z-old_Z).squaredNorm();
|
||||
#ifdef ACTIVE_SET_CPP_DEBUG
|
||||
cout<<"diff: "<<diff<<endl;
|
||||
#endif
|
||||
if(diff < params.solution_diff_threshold)
|
||||
{
|
||||
ret = SOLVER_STATUS_CONVERGED;
|
||||
break;
|
||||
}
|
||||
old_Z = Z;
|
||||
}
|
||||
|
||||
const int as_lx_count = std::count(as_lx.data(),as_lx.data()+n,TRUE);
|
||||
const int as_ux_count = std::count(as_ux.data(),as_ux.data()+n,TRUE);
|
||||
const int as_ieq_count =
|
||||
std::count(as_ieq.data(),as_ieq.data()+as_ieq.size(),TRUE);
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
int count = 0;
|
||||
for(int a = 0;a<as_ieq.size();a++)
|
||||
{
|
||||
if(as_ieq(a))
|
||||
{
|
||||
assert(as_ieq(a) == TRUE);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
assert(as_ieq_count == count);
|
||||
}
|
||||
#endif
|
||||
|
||||
// PREPARE FIXED VALUES
|
||||
Derivedknown known_i;
|
||||
known_i.resize(nk + as_lx_count + as_ux_count,1);
|
||||
DerivedY Y_i;
|
||||
Y_i.resize(nk + as_lx_count + as_ux_count,1);
|
||||
{
|
||||
known_i.block(0,0,known.rows(),known.cols()) = known;
|
||||
Y_i.block(0,0,Y.rows(),Y.cols()) = Y;
|
||||
int k = nk;
|
||||
// Then all lx
|
||||
for(int z = 0;z < n;z++)
|
||||
{
|
||||
if(as_lx(z))
|
||||
{
|
||||
known_i(k) = z;
|
||||
Y_i(k) = lx(z);
|
||||
k++;
|
||||
}
|
||||
}
|
||||
// Finally all ux
|
||||
for(int z = 0;z < n;z++)
|
||||
{
|
||||
if(as_ux(z))
|
||||
{
|
||||
known_i(k) = z;
|
||||
Y_i(k) = ux(z);
|
||||
k++;
|
||||
}
|
||||
}
|
||||
assert(k==Y_i.size());
|
||||
assert(k==known_i.size());
|
||||
}
|
||||
//cout<<matlab_format((known_i.array()+1).eval(),"known_i")<<endl;
|
||||
// PREPARE EQUALITY CONSTRAINTS
|
||||
Eigen::Matrix<typename DerivedY::Scalar, Eigen::Dynamic, 1> as_ieq_list(as_ieq_count,1);
|
||||
// Gather active constraints and resp. rhss
|
||||
DerivedBeq Beq_i;
|
||||
Beq_i.resize(Beq.rows()+as_ieq_count,1);
|
||||
Beq_i.head(Beq.rows()) = Beq;
|
||||
{
|
||||
int k =0;
|
||||
for(int a=0;a<as_ieq.size();a++)
|
||||
{
|
||||
if(as_ieq(a))
|
||||
{
|
||||
assert(k<as_ieq_list.size());
|
||||
as_ieq_list(k)=a;
|
||||
Beq_i(Beq.rows()+k,0) = Bieq(k,0);
|
||||
k++;
|
||||
}
|
||||
}
|
||||
assert(k == as_ieq_count);
|
||||
}
|
||||
// extract active constraint rows
|
||||
SparseMatrix<AeqT> 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<AT> data;
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
// NO DUPES!
|
||||
Matrix<BOOL,Dynamic,1> fixed = Matrix<BOOL,Dynamic,1>::Constant(n,1,FALSE);
|
||||
for(int k = 0;k<known_i.size();k++)
|
||||
{
|
||||
assert(!fixed[known_i(k)]);
|
||||
fixed[known_i(k)] = TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
DerivedZ sol;
|
||||
if(known_i.size() == A.rows())
|
||||
{
|
||||
// Everything's fixed?
|
||||
#ifdef ACTIVE_SET_CPP_DEBUG
|
||||
cout<<" everything's fixed."<<endl;
|
||||
#endif
|
||||
Z.resize(A.rows(),Y_i.cols());
|
||||
slice_into(Y_i,known_i,1,Z);
|
||||
sol.resize(0,Y_i.cols());
|
||||
assert(Aeq_i.rows() == 0 && "All fixed but linearly constrained");
|
||||
}else
|
||||
{
|
||||
#ifdef ACTIVE_SET_CPP_DEBUG
|
||||
cout<<" min_quad_with_fixed_precompute"<<endl;
|
||||
#endif
|
||||
if(!min_quad_with_fixed_precompute(A,known_i,Aeq_i,params.Auu_pd,data))
|
||||
{
|
||||
cerr<<"Error: min_quad_with_fixed precomputation failed."<<endl;
|
||||
if(iter > 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"<<endl;
|
||||
#endif
|
||||
if(!min_quad_with_fixed_solve(data,B,Y_i,Beq_i,Z,sol))
|
||||
{
|
||||
cerr<<"Error: min_quad_with_fixed solve failed."<<endl;
|
||||
ret = SOLVER_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
//cout<<matlab_format((Aeq*Z-Beq).eval(),"cr")<<endl;
|
||||
//cout<<matlab_format(Z,"Z")<<endl;
|
||||
#ifdef ACTIVE_SET_CPP_DEBUG
|
||||
cout<<" post"<<endl;
|
||||
#endif
|
||||
// Computing Lagrange multipliers needs to be adjusted slightly if A is not symmetric
|
||||
assert(data.Auu_sym);
|
||||
}
|
||||
|
||||
// Compute Lagrange multiplier values for known_i
|
||||
SparseMatrix<AT> 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;l<Aieq_i.rows();l++)
|
||||
{
|
||||
Lambda_Aieq_i(Aieq_i.rows()-1-l) = sol(sol.rows()-1-l);
|
||||
}
|
||||
|
||||
// Remove from active set
|
||||
for(int l = 0;l<as_lx_count;l++)
|
||||
{
|
||||
if(Lambda_known_i(nk + l) < params.inactive_threshold)
|
||||
{
|
||||
as_lx(known_i(nk + l)) = FALSE;
|
||||
}
|
||||
}
|
||||
for(int u = 0;u<as_ux_count;u++)
|
||||
{
|
||||
if(Lambda_known_i(nk + as_lx_count + u) <
|
||||
params.inactive_threshold)
|
||||
{
|
||||
as_ux(known_i(nk + as_lx_count + u)) = FALSE;
|
||||
}
|
||||
}
|
||||
for(int a = 0;a<as_ieq_count;a++)
|
||||
{
|
||||
if(Lambda_Aieq_i(a) < params.inactive_threshold)
|
||||
{
|
||||
as_ieq(int(as_ieq_list(a))) = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
iter++;
|
||||
//cout<<iter<<endl;
|
||||
if(params.max_iter>0 && 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<double, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, double, Eigen::Matrix<double, -1, 1, 0, -1, 1>, double, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(Eigen::SparseMatrix<double, 0, int> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::SparseMatrix<double, 0, int> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::SparseMatrix<double, 0, int> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, igl::active_set_params const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
|
||||
template igl::SolverStatus igl::active_set<double, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, double, Eigen::Matrix<double, -1, 1, 0, -1, 1>, double, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::SparseMatrix<double, 0, int> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::SparseMatrix<double, 0, int> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::SparseMatrix<double, 0, int> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, igl::active_set_params const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
|
||||
#endif
|
||||
+51
-42
@@ -16,39 +16,45 @@
|
||||
namespace igl
|
||||
{
|
||||
struct active_set_params;
|
||||
// Known Bugs: rows of [Aeq;Aieq] **must** be linearly independent. Should be
|
||||
// using QR decomposition otherwise:
|
||||
// https://v8doc.sas.com/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
|
||||
//
|
||||
///
|
||||
/// Minimize convex quadratic energy subject to linear inequality constraints
|
||||
///
|
||||
/// min ½ Zᵀ A Z + Zᵀ B + constant
|
||||
/// Z
|
||||
/// subject to
|
||||
/// Aeq Z = Beq
|
||||
/// Aieq Z <= Bieq
|
||||
/// lx <= Z <= ux
|
||||
/// Z(known) = Y
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// @param[in] A n by n matrix of quadratic coefficients
|
||||
/// @param[in] B n by 1 column of linear coefficients
|
||||
/// @param[in] known list of indices to known rows in Z
|
||||
/// @param[in] Y list of fixed values corresponding to known rows in Z
|
||||
/// @param[in] Aeq meq by n list of linear equality constraint coefficients
|
||||
/// @param[in] Beq meq by 1 list of linear equality constraint constant values
|
||||
/// @param[in] Aieq mieq by n list of linear inequality constraint coefficients
|
||||
/// @param[in] Bieq mieq by 1 list of linear inequality constraint constant values
|
||||
/// @param[in] lx n by 1 list of lower bounds [] implies -Inf
|
||||
/// @param[in] ux n by 1 list of upper bounds [] implies Inf
|
||||
/// @param[in] params struct of additional parameters (see below)
|
||||
/// @param[in,out] Z if not empty, is taken to be an n by 1 list of initial guess values. Set to solution on output.
|
||||
/// @return true on success, false on error
|
||||
///
|
||||
/// \note Benchmark: For a harmonic solve on a mesh with 325K facets, matlab 2.2
|
||||
/// secs, igl/min_quad_with_fixed.h 7.1 secs
|
||||
///
|
||||
/// \pre rows of [Aeq;Aieq] **must** be linearly independent. Should be
|
||||
/// using QR decomposition otherwise:
|
||||
/// https://v8doc.sas.com/sashtml/ormp/chap5/sect32.htm
|
||||
///
|
||||
/// \warning This solver is fairly experimental. It works reasonably well for
|
||||
/// bbw problems but doesn't generalize well to other problems. NASOQ and
|
||||
/// OSQP are better general purpose solvers.
|
||||
template <
|
||||
typename AT,
|
||||
typename DerivedB,
|
||||
@@ -79,22 +85,25 @@ namespace igl
|
||||
};
|
||||
|
||||
#include "EPS.h"
|
||||
/// Input parameters controling active_set
|
||||
///
|
||||
/// \fileinfo
|
||||
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}
|
||||
/// Auu_pd whether Auu is positive definite {false}
|
||||
bool Auu_pd;
|
||||
/// max_iter Maximum number of iterations (0 = Infinity, {100})
|
||||
int max_iter;
|
||||
/// inactive_threshold Threshold on Lagrange multiplier values to determine
|
||||
/// whether to keep constraints active {EPS}
|
||||
double inactive_threshold;
|
||||
/// constraint_threshold Threshold on whether constraints are violated (0
|
||||
/// is perfect) {EPS}
|
||||
double constraint_threshold;
|
||||
/// solution_diff_threshold Threshold on the squared norm of the difference
|
||||
/// between two consecutive solutions {EPS}
|
||||
double solution_diff_threshold;
|
||||
/// @private
|
||||
active_set_params():
|
||||
Auu_pd(false),
|
||||
max_iter(100),
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <algorithm>
|
||||
|
||||
template <typename Index, typename IndexVector>
|
||||
IGL_INLINE void igl::adjacency_list(
|
||||
const Eigen::MatrixBase<Index> & F,
|
||||
std::vector<std::vector<IndexVector> >& A,
|
||||
bool sorted)
|
||||
{
|
||||
A.clear();
|
||||
A.resize(F.maxCoeff()+1);
|
||||
|
||||
// Loop over faces
|
||||
for(int i = 0;i<F.rows();i++)
|
||||
{
|
||||
// Loop over this face
|
||||
for(int j = 0;j<F.cols();j++)
|
||||
{
|
||||
// Get indices of edge: s --> 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<std::vector<std::vector<int> > > SR;
|
||||
SR.resize(A.size());
|
||||
|
||||
for(int i = 0;i<F.rows();i++)
|
||||
{
|
||||
// Loop over this face
|
||||
for(int j = 0;j<F.cols();j++)
|
||||
{
|
||||
// Get indices of edge: s --> 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<int> e(2);
|
||||
e[0] = d;
|
||||
e[1] = v;
|
||||
SR[s].push_back(e);
|
||||
}
|
||||
}
|
||||
|
||||
for(int v=0; v<(int)SR.size();++v)
|
||||
{
|
||||
std::vector<IndexVector>& vv = A.at(v);
|
||||
std::vector<std::vector<int> >& sr = SR[v];
|
||||
|
||||
std::vector<std::vector<int> > 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 <typename Index>
|
||||
IGL_INLINE void igl::adjacency_list(
|
||||
const std::vector<std::vector<Index> > & F,
|
||||
std::vector<std::vector<Index> >& A)
|
||||
{
|
||||
A.clear();
|
||||
|
||||
// Find maxCoeff
|
||||
Index maxCoeff = 0;
|
||||
for(const auto &vec : F)
|
||||
{
|
||||
for(int coeff : vec)
|
||||
{
|
||||
maxCoeff = std::max(coeff, maxCoeff);
|
||||
}
|
||||
}
|
||||
A.resize(maxCoeff + 1);
|
||||
|
||||
// Loop over faces
|
||||
for(int i = 0;i<F.size();i++)
|
||||
{
|
||||
// Loop over this face
|
||||
for(int j = 0;j<F[i].size();j++)
|
||||
{
|
||||
// Get indices of edge: s --> 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<Eigen::Matrix<int, -1, 2, 0, -1, 2>, int>(Eigen::MatrixBase<Eigen::Matrix<int, -1, 2, 0, -1, 2> > const&, std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >&, bool);
|
||||
// generated by autoexplicit.sh
|
||||
template void igl::adjacency_list<Eigen::Matrix<int, -1, -1, 0, -1, -1>, int>(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >&, bool);
|
||||
template void igl::adjacency_list<Eigen::Matrix<int, -1, 3, 0, -1, 3>, int>(Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >&, bool);
|
||||
template void igl::adjacency_list<class Eigen::Matrix<int, -1, -1, 0, -1, -1>, unsigned int>(class Eigen::MatrixBase<class Eigen::Matrix<int, -1, -1, 0, -1, -1> > const &, class std::vector<class std::vector<unsigned int, class std::allocator<unsigned int> >, class std::allocator<class std::vector<unsigned int, class std::allocator<unsigned int> > > > &, bool);
|
||||
template void igl::adjacency_list<int>(std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > > const&, std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >&);
|
||||
#endif
|
||||
@@ -14,29 +14,35 @@
|
||||
#include <vector>
|
||||
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<vector<T> > containing at row i the adjacent vertices of vertex i
|
||||
//
|
||||
// Example:
|
||||
// // Mesh in (V,F)
|
||||
// vector<vector<double> > A;
|
||||
// adjacency_list(F,A);
|
||||
//
|
||||
// See also: edges, cotmatrix, diag
|
||||
/// Constructs the graph adjacency list of a given mesh (V,F)
|
||||
///
|
||||
/// @tparam T should be a eigen sparse matrix primitive type like int or double
|
||||
/// @param[in] F #F by dim list of mesh faces (must be triangles)
|
||||
/// @param[out] A vector<vector<T> > containing at row i the adjacent vertices of vertex i
|
||||
/// @param[in] sorted flag that indicates if the list should be sorted counter-clockwise
|
||||
///
|
||||
/// Example:
|
||||
/// \code{.cpp}
|
||||
/// // Mesh in (V,F)
|
||||
/// vector<vector<double> > A;
|
||||
/// adjacency_list(F,A);
|
||||
/// \endcode
|
||||
///
|
||||
/// \see
|
||||
/// adjacency_matrix
|
||||
/// edges,
|
||||
/// cotmatrix,
|
||||
/// diag
|
||||
template <typename Index, typename IndexVector>
|
||||
IGL_INLINE void adjacency_list(
|
||||
const Eigen::MatrixBase<Index> & F,
|
||||
std::vector<std::vector<IndexVector> >& A,
|
||||
bool sorted = false);
|
||||
|
||||
// Variant that accepts polygonal faces.
|
||||
// Each element of F is a set of indices of a polygonal face.
|
||||
/// Constructs the graph adjacency list of a given _polygon_ mesh (V,F)
|
||||
///
|
||||
/// @tparam T should be a eigen sparse matrix primitive type like int or double
|
||||
/// @param[in] F #F list of polygon face index lists
|
||||
/// @param[out] A vector<vector<T> > containing at row i the adjacent vertices of vertex i
|
||||
template <typename Index>
|
||||
IGL_INLINE void adjacency_list(
|
||||
const std::vector<std::vector<Index> > & F,
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <vector>
|
||||
|
||||
template <typename DerivedF, typename T>
|
||||
IGL_INLINE void igl::adjacency_matrix(
|
||||
const Eigen::MatrixBase<DerivedF> & F,
|
||||
Eigen::SparseMatrix<T>& A)
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
typedef typename DerivedF::Scalar Index;
|
||||
|
||||
typedef Triplet<T> IJV;
|
||||
vector<IJV > ijv;
|
||||
ijv.reserve(F.size()*2);
|
||||
// Loop over **simplex** (i.e., **not quad**)
|
||||
for(int i = 0;i<F.rows();i++)
|
||||
{
|
||||
// Loop over this **simplex**
|
||||
for(int j = 0;j<F.cols();j++)
|
||||
for(int k = j+1;k<F.cols();k++)
|
||||
{
|
||||
// Get indices of edge: s --> 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<A.outerSize(); ++k)
|
||||
{
|
||||
// Iterate over inside
|
||||
for(typename Eigen::SparseMatrix<T>::InnerIterator it (A,k); it; ++it)
|
||||
{
|
||||
assert(it.value() != 0);
|
||||
A.coeffRef(it.row(),it.col()) = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DerivedI, typename DerivedC, typename T>
|
||||
IGL_INLINE void igl::adjacency_matrix(
|
||||
const Eigen::MatrixBase<DerivedI> & I,
|
||||
const Eigen::MatrixBase<DerivedC> & C,
|
||||
Eigen::SparseMatrix<T>& A)
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
|
||||
typedef Triplet<T> IJV;
|
||||
vector<IJV > ijv;
|
||||
ijv.reserve(C(C.size()-1)*2);
|
||||
typedef typename DerivedI::Scalar Index;
|
||||
const Index n = I.maxCoeff()+1;
|
||||
{
|
||||
// loop over polygons
|
||||
for(Index p = 0;p<C.size()-1;p++)
|
||||
{
|
||||
// number of edges
|
||||
const Index np = C(p+1)-C(p);
|
||||
// loop over edges
|
||||
for(Index c = 0;c<np;c++)
|
||||
{
|
||||
const Index i = I(C(p)+c);
|
||||
const Index j = I(C(p)+((c+1)%np));
|
||||
ijv.emplace_back(i,j,1);
|
||||
ijv.emplace_back(j,i,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
A.resize(n,n);
|
||||
A.reserve(6*n);
|
||||
A.setFromTriplets(ijv.begin(),ijv.end());
|
||||
|
||||
// Force all non-zeros to be one
|
||||
|
||||
// Iterate over outside
|
||||
for(int k=0; k<A.outerSize(); ++k)
|
||||
{
|
||||
// Iterate over inside
|
||||
for(typename Eigen::SparseMatrix<T>::InnerIterator it (A,k); it; ++it)
|
||||
{
|
||||
assert(it.value() != 0);
|
||||
A.coeffRef(it.row(),it.col()) = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef IGL_STATIC_LIBRARY
|
||||
// Explicit template instantiation
|
||||
template void igl::adjacency_matrix<Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, int>(Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::SparseMatrix<int, 0, int>& );
|
||||
// generated by autoexplicit.sh
|
||||
template void igl::adjacency_matrix<Eigen::Matrix<int, -1, -1, 0, -1, -1>, bool>(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::SparseMatrix<bool, 0, int>&);
|
||||
template void igl::adjacency_matrix<Eigen::Matrix<int, -1, -1, 0, -1, -1>, double>(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::SparseMatrix<double, 0, int>&);
|
||||
template void igl::adjacency_matrix<Eigen::Matrix<int, -1, -1, 0, -1, -1>, int>(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::SparseMatrix<int, 0, int>&);
|
||||
template void igl::adjacency_matrix<Eigen::Matrix<int, -1, 3, 0, -1, 3>, int>(Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, Eigen::SparseMatrix<int, 0, int>&);
|
||||
#endif
|
||||
@@ -15,43 +15,44 @@
|
||||
|
||||
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)+1 by max(F)+1 adjacency matrix, each row i corresponding to V(i,:)
|
||||
//
|
||||
// Example:
|
||||
// // Mesh in (V,F)
|
||||
// Eigen::SparseMatrix<double> A;
|
||||
// adjacency_matrix(F,A);
|
||||
// // sum each row
|
||||
// SparseVector<double> Asum;
|
||||
// sum(A,1,Asum);
|
||||
// // Convert row sums into diagonal of sparse matrix
|
||||
// SparseMatrix<double> Adiag;
|
||||
// diag(Asum,Adiag);
|
||||
// // Build uniform laplacian
|
||||
// SparseMatrix<double> U;
|
||||
// U = A-Adiag;
|
||||
//
|
||||
// See also: edges, cotmatrix, diag
|
||||
/// Constructs the graph adjacency matrix of a given mesh (V,F)
|
||||
///
|
||||
/// @tparam T should be a eigen sparse matrix primitive type like `int` or `double`
|
||||
/// @param[in] F #F by dim list of mesh simplices
|
||||
/// @param[out] A max(F)+1 by max(F)+1 adjacency matrix, each row i corresponding to V(i,:)
|
||||
///
|
||||
/// #### Example
|
||||
/// \code{.cpp}
|
||||
/// // Mesh in (V,F)
|
||||
/// Eigen::SparseMatrix<double> A;
|
||||
/// adjacency_matrix(F,A);
|
||||
/// // sum each row
|
||||
/// SparseVector<double> Asum;
|
||||
/// sum(A,1,Asum);
|
||||
/// // Convert row sums into diagonal of sparse matrix
|
||||
/// SparseMatrix<double> Adiag;
|
||||
/// diag(Asum,Adiag);
|
||||
/// // Build uniform laplacian
|
||||
/// SparseMatrix<double> U;
|
||||
/// U = A-Adiag;
|
||||
/// \endcode
|
||||
///
|
||||
/// \see
|
||||
/// edges,
|
||||
/// cotmatrix,
|
||||
/// diag
|
||||
template <typename DerivedF, typename T>
|
||||
IGL_INLINE void adjacency_matrix(
|
||||
const Eigen::MatrixBase<DerivedF> & F,
|
||||
Eigen::SparseMatrix<T>& A);
|
||||
// Constructs an vertex adjacency for a polygon mesh.
|
||||
//
|
||||
// Inputs:
|
||||
// I #I vectorized list of polygon corner indices into rows of some matrix V
|
||||
// C #polygons+1 list of cumulative polygon sizes so that C(i+1)-C(i) =
|
||||
// size of the ith polygon, and so I(C(i)) through I(C(i+1)-1) are the
|
||||
// indices of the ith polygon
|
||||
// Outputs:
|
||||
// A max(I)+1 by max(I)+1 adjacency matrix, each row i corresponding to V(i,:)
|
||||
//
|
||||
/// Constructs an vertex adjacency for a polygon mesh.
|
||||
///
|
||||
/// @param[in] I #I vectorized list of polygon corner indices into rows of some matrix V
|
||||
/// @param[in] C #polygons+1 list of cumulative polygon sizes so that C(i+1)-C(i) =
|
||||
/// size of the ith polygon, and so I(C(i)) through I(C(i+1)-1) are the
|
||||
/// indices of the ith polygon
|
||||
/// @param[out] A max(I)+1 by max(I)+1 adjacency matrix, each row i corresponding to V(i,:)
|
||||
///
|
||||
template <typename DerivedI, typename DerivedC, typename T>
|
||||
IGL_INLINE void adjacency_matrix(
|
||||
const Eigen::MatrixBase<DerivedI> & I,
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2016 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <typename AType, typename DerivedB>
|
||||
IGL_INLINE void igl::all(
|
||||
const Eigen::SparseMatrix<AType> & A,
|
||||
const int dim,
|
||||
Eigen::PlainObjectBase<DerivedB>& 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
|
||||
|
||||
|
||||
+9
-10
@@ -12,16 +12,15 @@
|
||||
#include <Eigen/Sparse>
|
||||
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)
|
||||
//
|
||||
/// Check whether all values are logically true along a dimension.
|
||||
///
|
||||
/// \note For Dense matrices use: A.rowwise().all() or A.colwise().all()
|
||||
///
|
||||
/// @param[in] A m by n sparse matrix
|
||||
/// @param[in] dim dimension along which to check for all (1 or 2)
|
||||
/// @param[out] B n-long vector (if dim == 1)
|
||||
/// or m-long vector (if dim == 2)
|
||||
///
|
||||
template <typename AType, typename DerivedB>
|
||||
IGL_INLINE void all(
|
||||
const Eigen::SparseMatrix<AType> & A,
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <Eigen/Dense>
|
||||
|
||||
template <typename Mat>
|
||||
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<V.rows();i++)
|
||||
{
|
||||
for(int j=0;j<U.rows();j++)
|
||||
{
|
||||
D(i,j) = (V.row(i)-U.row(j)).squaredNorm();
|
||||
if(!squared)
|
||||
{
|
||||
D(i,j) = sqrt(D(i,j));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef IGL_STATIC_LIBRARY
|
||||
// Explicit template instantiation
|
||||
// generated by autoexplicit.sh
|
||||
template void igl::all_pairs_distances<Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, bool, Eigen::Matrix<double, -1, -1, 0, -1, -1>&);
|
||||
#endif
|
||||
@@ -11,21 +11,16 @@
|
||||
|
||||
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,:)
|
||||
//
|
||||
/// Compute distances between each point i in V and point j in U
|
||||
///
|
||||
/// D = all_pairs_distances(V,U)
|
||||
///
|
||||
/// @tparam matrix class like MatrixXd
|
||||
/// @param[in] V #V by dim list of points
|
||||
/// @param[in] U #U by dim list of points
|
||||
/// @param[in] squared whether to return squared distances
|
||||
/// @param[out] D #V by #U matrix of distances, where D(i,j) gives the distance or
|
||||
/// squareed distance between V(i,:) and U(j,:)
|
||||
template <typename Mat>
|
||||
IGL_INLINE void all_pairs_distances(
|
||||
const Mat & V,
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2015 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <functional>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
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::MatrixBase<DerivedP> & P,
|
||||
const Eigen::MatrixBase<DerivedN> & N,
|
||||
const int num_samples,
|
||||
Eigen::PlainObjectBase<DerivedS> & 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<float>();
|
||||
|
||||
const auto & inner = [&P,&N,&num_samples,&D,&S,&shoot_ray](const int p)
|
||||
{
|
||||
const Vector3f origin = P.row(p).template cast<float>();
|
||||
const Vector3f normal = N.row(p).template cast<float>();
|
||||
int num_hits = 0;
|
||||
for(int s = 0;s<num_samples;s++)
|
||||
{
|
||||
Vector3f d = D.row(s);
|
||||
if(d.dot(normal) < 0)
|
||||
{
|
||||
// reverse ray
|
||||
d *= -1;
|
||||
}
|
||||
if(shoot_ray(origin,d))
|
||||
{
|
||||
num_hits++;
|
||||
}
|
||||
}
|
||||
S(p) = (double)num_hits/(double)num_samples;
|
||||
};
|
||||
parallel_for(n,inner,1000);
|
||||
}
|
||||
|
||||
template <
|
||||
typename DerivedV,
|
||||
int DIM,
|
||||
typename DerivedF,
|
||||
typename DerivedP,
|
||||
typename DerivedN,
|
||||
typename DerivedS >
|
||||
IGL_INLINE void igl::ambient_occlusion(
|
||||
const igl::AABB<DerivedV,DIM> & aabb,
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
const Eigen::MatrixBase<DerivedF> & F,
|
||||
const Eigen::MatrixBase<DerivedP> & P,
|
||||
const Eigen::MatrixBase<DerivedN> & N,
|
||||
const int num_samples,
|
||||
Eigen::PlainObjectBase<DerivedS> & 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<typename DerivedV::Scalar>().eval(),
|
||||
dir.cast<typename DerivedV::Scalar>().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::MatrixBase<DerivedV> & V,
|
||||
const Eigen::MatrixBase<DerivedF> & F,
|
||||
const Eigen::MatrixBase<DerivedP> & P,
|
||||
const Eigen::MatrixBase<DerivedN> & N,
|
||||
const int num_samples,
|
||||
Eigen::PlainObjectBase<DerivedS> & 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<DerivedV,3> 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<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, int, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
|
||||
// generated by autoexplicit.sh
|
||||
template void igl::ambient_occlusion<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(std::function<bool (Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&)> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, int, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
|
||||
// generated by autoexplicit.sh
|
||||
template void igl::ambient_occlusion<Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(std::function<bool (Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&)> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, int, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
|
||||
// generated by autoexplicit.sh
|
||||
template void igl::ambient_occlusion<Eigen::Matrix<double, 1, 3, 1, 1, 3>, Eigen::Matrix<double, 1, 3, 1, 1, 3>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(std::function<bool (Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&)> const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, int, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
|
||||
template void igl::ambient_occlusion<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(std::function<bool (Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 3, 1, 0, 3, 1> const&)> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, int, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
|
||||
#endif
|
||||
@@ -13,17 +13,17 @@
|
||||
#include <functional>
|
||||
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)
|
||||
//
|
||||
/// Compute ambient occlusion per given point using ray-mesh intersection
|
||||
/// function handle.
|
||||
///
|
||||
/// @param[in] shoot_ray function handle that outputs hits of a given ray against a
|
||||
/// mesh (embedded in function handles as captured variable/data)
|
||||
/// @param[in] P #P by 3 list of origin points
|
||||
/// @param[in] N #P by 3 list of origin normals
|
||||
/// @param[in] num_samples number of samples to use (e.g., 1000)
|
||||
/// @param[out] S #P list of ambient occlusion values between 1 (fully occluded) and
|
||||
/// 0 (not occluded)
|
||||
///
|
||||
template <
|
||||
typename DerivedP,
|
||||
typename DerivedN,
|
||||
@@ -38,8 +38,18 @@ namespace igl
|
||||
const Eigen::MatrixBase<DerivedN> & N,
|
||||
const int num_samples,
|
||||
Eigen::PlainObjectBase<DerivedS> & S);
|
||||
// Inputs:
|
||||
// AABB axis-aligned bounding box hierarchy around (V,F)
|
||||
/// Compute ambient occlusion per given point for mesh (V,F) with precomputed
|
||||
/// AABB tree.
|
||||
///
|
||||
// @param[in] AABB axis-aligned bounding box hierarchy around (V,F)
|
||||
/// @param[in] V #V by 3 list of mesh vertex positions
|
||||
/// @param[in] F #F by 3 list of mesh face indices into V
|
||||
/// @param[in] P #P by 3 list of origin points
|
||||
/// @param[in] N #P by 3 list of origin normals
|
||||
/// @param[in] num_samples number of samples to use (e.g., 1000)
|
||||
/// @param[out] S #P list of ambient occlusion values between 1 (fully occluded) and
|
||||
/// 0 (not occluded)
|
||||
///
|
||||
template <
|
||||
typename DerivedV,
|
||||
int DIM,
|
||||
@@ -55,9 +65,15 @@ namespace igl
|
||||
const Eigen::MatrixBase<DerivedN> & N,
|
||||
const int num_samples,
|
||||
Eigen::PlainObjectBase<DerivedS> & S);
|
||||
// Inputs:
|
||||
// V #V by 3 list of mesh vertex positions
|
||||
// F #F by 3 list of mesh face indices into V
|
||||
/// Compute ambient occlusion per given point for mesh (V,F)
|
||||
///
|
||||
/// @param[in] V #V by 3 list of mesh vertex positions
|
||||
/// @param[in] F #F by 3 list of mesh face indices into V
|
||||
/// @param[in] P #P by 3 list of origin points
|
||||
/// @param[in] N #P by 3 list of origin normals
|
||||
/// @param[in] num_samples number of samples to use (e.g., 1000)
|
||||
/// @param[out] S #P list of ambient occlusion values between 1 (fully occluded) and
|
||||
/// 0 (not occluded)
|
||||
template <
|
||||
typename DerivedV,
|
||||
typename DerivedF,
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <igl/EPS.h>
|
||||
#include <igl/PI.h>
|
||||
IGL_INLINE double igl::angular_distance(
|
||||
const Eigen::Quaterniond & A,
|
||||
const Eigen::Quaterniond & B)
|
||||
{
|
||||
assert(fabs(A.norm()-1)<FLOAT_EPS && "A should be unit norm");
|
||||
assert(fabs(B.norm()-1)<FLOAT_EPS && "B should be unit norm");
|
||||
//// acos is always in [0,2*pi)
|
||||
//return acos(fabs(A.dot(B)));
|
||||
return fmod(2.*acos(A.dot(B)),2.*PI);
|
||||
}
|
||||
@@ -11,13 +11,12 @@
|
||||
#include <Eigen/Geometry>
|
||||
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
|
||||
/// 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.
|
||||
///
|
||||
/// @param[in] A unit quaternion
|
||||
/// @param[in] B unit quaternion
|
||||
/// @return angular distance
|
||||
IGL_INLINE double angular_distance(
|
||||
const Eigen::Quaterniond & A,
|
||||
const Eigen::Quaterniond & B);
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2016 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <typename AType, typename DerivedB>
|
||||
IGL_INLINE void igl::any(
|
||||
const Eigen::SparseMatrix<AType> & A,
|
||||
const int dim,
|
||||
Eigen::PlainObjectBase<DerivedB>& 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<bool, Eigen::Array<bool, -1, 1, 0, -1, 1> >(Eigen::SparseMatrix<bool, 0, int> const&, int, Eigen::PlainObjectBase<Eigen::Array<bool, -1, 1, 0, -1, 1> >&);
|
||||
#endif
|
||||
+9
-10
@@ -12,16 +12,15 @@
|
||||
#include <Eigen/Sparse>
|
||||
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)
|
||||
//
|
||||
/// Check whether any values are logically true along a dimension.
|
||||
///
|
||||
/// \note Dense matrices use: A.rowwise().any() or A.colwise().any()
|
||||
///
|
||||
/// @param[in] A m by n sparse matrix
|
||||
/// @param[in] dim dimension along which to check for any (1 or 2)
|
||||
/// @param[out] B n-long vector (if dim == 1)
|
||||
/// or m-long vector (if dim == 2)
|
||||
///
|
||||
template <typename AType, typename DerivedB>
|
||||
IGL_INLINE void any(
|
||||
const Eigen::SparseMatrix<AType> & A,
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <Eigen/Core>
|
||||
template <typename Mat>
|
||||
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<int, -1, 1, 0, -1, 1> >(Eigen::Matrix<int, -1, 1, 0, -1, 1> const&);
|
||||
#endif
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. If 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 <typename Mat>
|
||||
IGL_INLINE bool any_of(const Mat & S);
|
||||
}
|
||||
#ifndef IGL_STATIC_LIBRARY
|
||||
# include "any_of.cpp"
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,306 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <cassert>
|
||||
#include <iostream>
|
||||
|
||||
template <typename Scalar>
|
||||
using MatrixXX = Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>;
|
||||
|
||||
template <
|
||||
typename DerivedV,
|
||||
typename DerivedF,
|
||||
typename Derivedb>
|
||||
IGL_INLINE bool igl::arap_precomputation(
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
const Eigen::MatrixBase<DerivedF> & F,
|
||||
const int dim,
|
||||
const Eigen::MatrixBase<Derivedb> & b,
|
||||
ARAPData & data)
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
typedef typename DerivedV::Scalar Scalar;
|
||||
typedef typename DerivedF::Scalar Integer;
|
||||
// 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;
|
||||
|
||||
MatrixXX<Scalar> plane_V;
|
||||
MatrixXX<Integer> plane_F;
|
||||
typedef SparseMatrix<Scalar> 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 MatrixXX<Scalar>& ref_V = (flat?plane_V:V);
|
||||
const MatrixXX<Integer>& 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<double> 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<int,Eigen::Dynamic,1> GG;
|
||||
MatrixXi GF(F.rows(),F.cols());
|
||||
for(int j = 0;j<F.cols();j++)
|
||||
{
|
||||
Matrix<int,Eigen::Dynamic,1> GFj;
|
||||
slice(data.G,F.col(j),GFj);
|
||||
GF.col(j) = GFj;
|
||||
}
|
||||
mode<int>(GF,2,GG);
|
||||
data.G=GG;
|
||||
}
|
||||
//printf("group_sum_matrix()\n");
|
||||
group_sum_matrix(data.G,G_sum);
|
||||
}
|
||||
SparseMatrix<double> 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<double> Q = (-L).eval();
|
||||
|
||||
if(data.with_dynamics)
|
||||
{
|
||||
const double h = data.h;
|
||||
assert(h != 0);
|
||||
SparseMatrix<double> M;
|
||||
massmatrix(V,F,MASSMATRIX_TYPE_DEFAULT,data.M);
|
||||
const double dw = (1./data.ym)*(h*h);
|
||||
SparseMatrix<double> 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<double>(),true,data.solver_data);
|
||||
}
|
||||
|
||||
template <
|
||||
typename Derivedbc,
|
||||
typename DerivedU>
|
||||
IGL_INLINE bool igl::arap_solve(
|
||||
const Eigen::MatrixBase<Derivedbc> & bc,
|
||||
ARAPData & data,
|
||||
Eigen::MatrixBase<DerivedU> & U)
|
||||
{
|
||||
using namespace Eigen;
|
||||
using namespace std;
|
||||
assert(data.b.size() == bc.rows());
|
||||
assert(U.size() != 0 && "U cannot be empty");
|
||||
assert(U.cols() == data.dim && "U.cols() match data.dim");
|
||||
if (bc.size() > 0) {
|
||||
assert(bc.cols() == data.dim && "bc.cols() match data.dim");
|
||||
}
|
||||
const int n = data.n;
|
||||
int iter = 0;
|
||||
// changes each arap iteration
|
||||
MatrixXd U_prev = U;
|
||||
// doesn't change for fixed with_dynamics timestep
|
||||
MatrixXd U0;
|
||||
if(data.with_dynamics)
|
||||
{
|
||||
U0 = U_prev;
|
||||
}
|
||||
while(iter < data.max_iter)
|
||||
{
|
||||
U_prev = U;
|
||||
// enforce boundary conditions exactly
|
||||
for(int bi = 0;bi<bc.rows();bi++)
|
||||
{
|
||||
U.row(data.b(bi)) = bc.row(bi);
|
||||
}
|
||||
|
||||
const auto & Udim = U.replicate(data.dim,1);
|
||||
assert(U.cols() == data.dim);
|
||||
// As if U.col(2) was 0
|
||||
MatrixXd S = data.CSM * Udim;
|
||||
// THIS NORMALIZATION IS IMPORTANT TO GET SINGLE PRECISION SVD CODE TO WORK
|
||||
// CORRECTLY.
|
||||
S /= S.array().abs().maxCoeff();
|
||||
|
||||
const int Rdim = data.dim;
|
||||
MatrixXd R(Rdim,data.CSM.rows());
|
||||
if(R.rows() == 2)
|
||||
{
|
||||
fit_rotations_planar(S,R);
|
||||
}else
|
||||
{
|
||||
fit_rotations(S,true,R);
|
||||
//#ifdef __SSE__ // fit_rotations_SSE will convert to float if necessary
|
||||
// fit_rotations_SSE(S,R);
|
||||
//#else
|
||||
// fit_rotations(S,true,R);
|
||||
//#endif
|
||||
}
|
||||
//for(int k = 0;k<(data.CSM.rows()/dim);k++)
|
||||
//{
|
||||
// R.block(0,dim*k,dim,dim) = MatrixXd::Identity(dim,dim);
|
||||
//}
|
||||
|
||||
|
||||
// Number of rotations: #vertices or #elements
|
||||
int num_rots = data.K.cols()/Rdim/Rdim;
|
||||
// distribute group rotations to vertices in each group
|
||||
MatrixXd eff_R;
|
||||
if(data.G.size() == 0)
|
||||
{
|
||||
// copy...
|
||||
eff_R = R;
|
||||
}else
|
||||
{
|
||||
eff_R.resize(Rdim,num_rots*Rdim);
|
||||
for(int r = 0;r<num_rots;r++)
|
||||
{
|
||||
eff_R.block(0,Rdim*r,Rdim,Rdim) =
|
||||
R.block(0,Rdim*data.G(r),Rdim,Rdim);
|
||||
}
|
||||
}
|
||||
|
||||
MatrixXd Dl;
|
||||
if(data.with_dynamics)
|
||||
{
|
||||
assert(data.M.rows() == n &&
|
||||
"No mass matrix. Call arap_precomputation if changing with_dynamics");
|
||||
const double h = data.h;
|
||||
assert(h != 0);
|
||||
//Dl = 1./(h*h*h)*M*(-2.*V0 + Vm1) - fext;
|
||||
// data.vel = (V0-Vm1)/h
|
||||
// h*data.vel = (V0-Vm1)
|
||||
// -h*data.vel = -V0+Vm1)
|
||||
// -V0-h*data.vel = -2V0+Vm1
|
||||
const double dw = (1./data.ym)*(h*h);
|
||||
Dl = dw * (1./(h*h)*data.M*(-U0 - h*data.vel) - data.f_ext);
|
||||
}
|
||||
|
||||
VectorXd Rcol;
|
||||
columnize(eff_R,num_rots,2,Rcol);
|
||||
VectorXd Bcol = -data.K * Rcol;
|
||||
assert(Bcol.size() == data.n*data.dim);
|
||||
for(int c = 0;c<data.dim;c++)
|
||||
{
|
||||
VectorXd Uc,Bc,bcc,Beq;
|
||||
Bc = Bcol.block(c*n,0,n,1);
|
||||
if(data.with_dynamics)
|
||||
{
|
||||
Bc += Dl.col(c);
|
||||
}
|
||||
if(bc.size()>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<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, igl::ARAPData&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
|
||||
template bool igl::arap_precomputation<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, int, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, igl::ARAPData&);
|
||||
#endif
|
||||
+51
-40
@@ -15,36 +15,41 @@
|
||||
|
||||
namespace igl
|
||||
{
|
||||
/// Parameters and precomputed values for arap solver.
|
||||
///
|
||||
/// \fileinfo
|
||||
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
|
||||
/// #V size of mesh
|
||||
int n;
|
||||
/// #V list of group indices (1 to k) for each vertex, such that vertex i
|
||||
/// is assigned to group G(i)
|
||||
Eigen::VectorXi G;
|
||||
/// type of energy to use
|
||||
ARAPEnergyType energy;
|
||||
/// whether using dynamics (need to call arap_precomputation after changing)
|
||||
bool with_dynamics;
|
||||
Eigen::MatrixXd f_ext,vel;
|
||||
/// #V by dim list of external forces
|
||||
Eigen::MatrixXd f_ext;
|
||||
/// #V by dim list of velocities
|
||||
Eigen::MatrixXd vel;
|
||||
/// dynamics time step
|
||||
double h;
|
||||
/// "Young's modulus" smaller is softer, larger is more rigid/stiff
|
||||
double ym;
|
||||
/// maximum inner iterations
|
||||
int max_iter;
|
||||
Eigen::SparseMatrix<double> K,M;
|
||||
/// @private rhs pre-multiplier
|
||||
Eigen::SparseMatrix<double> K;
|
||||
/// @private mass matrix
|
||||
Eigen::SparseMatrix<double> M;
|
||||
/// @private covariance scatter matrix
|
||||
Eigen::SparseMatrix<double> CSM;
|
||||
/// @private quadratic solver data
|
||||
min_quad_with_fixed_data<double> solver_data;
|
||||
/// @private list of boundary indices into V
|
||||
Eigen::VectorXi b;
|
||||
/// @private dimension being used for solving
|
||||
int dim;
|
||||
ARAPData():
|
||||
n(0),
|
||||
@@ -64,16 +69,19 @@ namespace igl
|
||||
};
|
||||
};
|
||||
|
||||
// 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
|
||||
/// Compute necessary information to start using an ARAP deformation using
|
||||
/// local-global solver as described in "As-rigid-as-possible surface
|
||||
/// modeling" [Sorkine and Alexa 2007].
|
||||
///
|
||||
/// @param[in] V #V by dim list of mesh positions
|
||||
/// @param[in] F #F by simplex-size list of triangle|tet indices into V
|
||||
/// @param[in] dim dimension being used at solve time. For deformation usually dim =
|
||||
/// V.cols(), for surface parameterization V.cols() = 3 and dim = 2
|
||||
/// @param[in] b #b list of "boundary" fixed vertex indices into V
|
||||
/// @param[out] data struct containing necessary precomputation
|
||||
/// @return whether initialization succeeded
|
||||
///
|
||||
/// \fileinfo
|
||||
template <
|
||||
typename DerivedV,
|
||||
typename DerivedF,
|
||||
@@ -84,18 +92,21 @@ namespace igl
|
||||
const int dim,
|
||||
const Eigen::MatrixBase<Derivedb> & 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
|
||||
//
|
||||
// NOTE: While the libigl guidelines require outputs to be of type
|
||||
// PlainObjectBase so that the user does not need to worry about allocating
|
||||
// memory for the output, in this case, the user is required to give an initial
|
||||
// guess and hence fix the size of the problem domain.
|
||||
// Taking a reference to MatrixBase in this case thus allows the user to provide e.g.
|
||||
// a map to the position data, allowing seamless interoperability with user-defined
|
||||
// datastructures without requiring a copy.
|
||||
/// Conduct arap solve.
|
||||
///
|
||||
/// @param[in] bc #b by dim list of boundary conditions
|
||||
/// @param[in] data struct containing necessary precomputation and parameters
|
||||
/// @param[in,out] U #V by dim initial guess
|
||||
///
|
||||
/// \fileinfo
|
||||
///
|
||||
/// \note While the libigl guidelines require outputs to be of type
|
||||
/// PlainObjectBase so that the user does not need to worry about allocating
|
||||
/// memory for the output, in this case, the user is required to give an initial
|
||||
/// guess and hence fix the size of the problem domain.
|
||||
/// Taking a reference to MatrixBase in this case thus allows the user to provide e.g.
|
||||
/// a map to the position data, allowing seamless interoperability with user-defined
|
||||
/// datastructures without requiring a copy.
|
||||
template <
|
||||
typename Derivedbc,
|
||||
typename DerivedU>
|
||||
|
||||
@@ -1,884 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 "kkt_inverse.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 <typename LbsMatrixType, typename SSCALAR>
|
||||
IGL_INLINE bool igl::arap_dof_precomputation(
|
||||
const Eigen::MatrixXd & V,
|
||||
const Eigen::MatrixXi & F,
|
||||
const LbsMatrixType & M,
|
||||
const Eigen::Matrix<int,Eigen::Dynamic,1> & G,
|
||||
ArapDOFData<LbsMatrixType, SSCALAR> & data)
|
||||
{
|
||||
using namespace Eigen;
|
||||
typedef Matrix<SSCALAR, Dynamic, Dynamic> 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<double> Lcot;
|
||||
//printf("cotmatrix()\n");
|
||||
cotmatrix(V,F,Lcot);
|
||||
// Discrete laplacian (should be minus matlab version)
|
||||
SparseMatrix<double> Lapl = -2.0*Lcot;
|
||||
#ifdef EXTREME_VERBOSE
|
||||
cout<<"LaplIJV=["<<endl;print_ijv(Lapl,1);cout<<endl<<"];"<<
|
||||
endl<<"Lapl=sparse(LaplIJV(:,1),LaplIJV(:,2),LaplIJV(:,3),"<<
|
||||
Lapl.rows()<<","<<Lapl.cols()<<");"<<endl;
|
||||
#endif
|
||||
|
||||
// Get group sum scatter matrix, when applied sums all entries of the same
|
||||
// group according to G
|
||||
SparseMatrix<double> G_sum;
|
||||
if(G.size() == 0)
|
||||
{
|
||||
speye(n,G_sum);
|
||||
}else
|
||||
{
|
||||
// groups are defined per vertex, convert to per face using mode
|
||||
Eigen::Matrix<int,Eigen::Dynamic,1> GG;
|
||||
if(data.energy == ARAP_ENERGY_TYPE_ELEMENTS)
|
||||
{
|
||||
MatrixXi GF(F.rows(),F.cols());
|
||||
for(int j = 0;j<F.cols();j++)
|
||||
{
|
||||
Matrix<int,Eigen::Dynamic,1> GFj;
|
||||
slice(G,F.col(j),GFj);
|
||||
GF.col(j) = GFj;
|
||||
}
|
||||
mode<int>(GF,2,GG);
|
||||
}else
|
||||
{
|
||||
GG=G;
|
||||
}
|
||||
//printf("group_sum_matrix()\n");
|
||||
group_sum_matrix(GG,G_sum);
|
||||
}
|
||||
|
||||
#ifdef EXTREME_VERBOSE
|
||||
cout<<"G_sumIJV=["<<endl;print_ijv(G_sum,1);cout<<endl<<"];"<<
|
||||
endl<<"G_sum=sparse(G_sumIJV(:,1),G_sumIJV(:,2),G_sumIJV(:,3),"<<
|
||||
G_sum.rows()<<","<<G_sum.cols()<<");"<<endl;
|
||||
#endif
|
||||
|
||||
// Get covariance scatter matrix, when applied collects the covariance matrices
|
||||
// used to fit rotations to during optimization
|
||||
SparseMatrix<double> CSM;
|
||||
//printf("covariance_scatter_matrix()\n");
|
||||
covariance_scatter_matrix(V,F,data.energy,CSM);
|
||||
#ifdef EXTREME_VERBOSE
|
||||
cout<<"CSMIJV=["<<endl;print_ijv(CSM,1);cout<<endl<<"];"<<
|
||||
endl<<"CSM=sparse(CSMIJV(:,1),CSMIJV(:,2),CSMIJV(:,3),"<<
|
||||
CSM.rows()<<","<<CSM.cols()<<");"<<endl;
|
||||
#endif
|
||||
|
||||
|
||||
// Build the covariance matrix "constructor". This is a set of *scatter*
|
||||
// matrices that when multiplied on the right by column of the transformation
|
||||
// matrix entries (the degrees of freedom) L, we get a stack of dim by 1
|
||||
// covariance matrix column, with a column in the stack for each rotation
|
||||
// *group*. The output is a list of matrices because we construct each column
|
||||
// in the stack of covariance matrices with an independent matrix-vector
|
||||
// multiplication.
|
||||
//
|
||||
// We want to build S which is a stack of dim by dim covariance matrices.
|
||||
// Thus S is dim*g by dim, where dim is the number of dimensions and g is the
|
||||
// number of groups. We can precompute dim matrices CSM_M such that column i
|
||||
// in S is computed as S(:,i) = CSM_M{i} * L, where L is a column of the
|
||||
// skinning transformation matrix values. To be clear, the covariance matrix
|
||||
// for group k is then given as the dim by dim matrix pulled from the stack:
|
||||
// S((k-1)*dim + 1:dim,:)
|
||||
|
||||
// Apply group sum to each dimension's block of covariance scatter matrix
|
||||
SparseMatrix<double> G_sum_dim;
|
||||
repdiag(G_sum,data.dim,G_sum_dim);
|
||||
CSM = (G_sum_dim * CSM).eval();
|
||||
#ifdef EXTREME_VERBOSE
|
||||
cout<<"CSMIJV=["<<endl;print_ijv(CSM,1);cout<<endl<<"];"<<
|
||||
endl<<"CSM=sparse(CSMIJV(:,1),CSMIJV(:,2),CSMIJV(:,3),"<<
|
||||
CSM.rows()<<","<<CSM.cols()<<");"<<endl;
|
||||
#endif
|
||||
|
||||
//printf("CSM_M()\n");
|
||||
// Precompute CSM times M for each dimension
|
||||
data.CSM_M.resize(data.dim);
|
||||
#ifdef EXTREME_VERBOSE
|
||||
cout<<"data.CSM_M = cell("<<data.dim<<",1);"<<endl;
|
||||
#endif
|
||||
// span of integers from 0 to n-1
|
||||
Eigen::Matrix<int,Eigen::Dynamic,1> span_n(n);
|
||||
for(int i = 0;i<n;i++)
|
||||
{
|
||||
span_n(i) = i;
|
||||
}
|
||||
|
||||
// span of integers from 0 to M.cols()-1
|
||||
Eigen::Matrix<int,Eigen::Dynamic,1> span_mlbs_cols(M.cols());
|
||||
for(int i = 0;i<M.cols();i++)
|
||||
{
|
||||
span_mlbs_cols(i) = i;
|
||||
}
|
||||
|
||||
// number of groups
|
||||
int k = CSM.rows()/data.dim;
|
||||
for(int i = 0;i<data.dim;i++)
|
||||
{
|
||||
//printf("CSM_M(): Mi\n");
|
||||
LbsMatrixType M_i;
|
||||
//printf("CSM_M(): slice\n");
|
||||
slice(M,(span_n.array()+i*n).matrix().eval(),span_mlbs_cols,M_i);
|
||||
LbsMatrixType M_i_dim;
|
||||
data.CSM_M[i].resize(k*data.dim,data.m*data.dim*(data.dim+1));
|
||||
assert(data.CSM_M[i].cols() == M.cols());
|
||||
for(int j = 0;j<data.dim;j++)
|
||||
{
|
||||
SparseMatrix<double> CSMj;
|
||||
//printf("CSM_M(): slice\n");
|
||||
slice(
|
||||
CSM,
|
||||
colon<int>(j*k,(j+1)*k-1),
|
||||
colon<int>(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=["<<endl<<data.CSM_M[i]<<endl<<"];"<<endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
// precompute arap_rhs matrix
|
||||
//printf("arap_rhs()\n");
|
||||
SparseMatrix<double> K;
|
||||
arap_rhs(V,F,V.cols(),data.energy,K);
|
||||
//#ifdef EXTREME_VERBOSE
|
||||
// cout<<"KIJV=["<<endl;print_ijv(K,1);cout<<endl<<"];"<<
|
||||
// endl<<"K=sparse(KIJV(:,1),KIJV(:,2),KIJV(:,3),"<<
|
||||
// K.rows()<<","<<K.cols()<<");"<<endl;
|
||||
//#endif
|
||||
// Precompute left muliplication by M and right multiplication by G_sum
|
||||
SparseMatrix<double> G_sumT = G_sum.transpose();
|
||||
SparseMatrix<double> 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=["<<endl;print_ijv(data.M_KG,1);cout<<endl<<"];"<<
|
||||
// endl<<"data.M_KG=sparse(data.M_KGIJV(:,1),data.M_KGIJV(:,2),data.M_KGIJV(:,3),"<<
|
||||
// data.M_KG.rows()<<","<<data.M_KG.cols()<<");"<<endl;
|
||||
//#endif
|
||||
|
||||
// Precompute system matrix
|
||||
//printf("A()\n");
|
||||
SparseMatrix<double> A;
|
||||
repdiag(Lapl,data.dim,A);
|
||||
data.Q = MT * (A * M);
|
||||
//#ifdef EXTREME_VERBOSE
|
||||
// cout<<"QIJV=["<<endl;print_ijv(data.Q,1);cout<<endl<<"];"<<
|
||||
// endl<<"Q=sparse(QIJV(:,1),QIJV(:,2),QIJV(:,3),"<<
|
||||
// data.Q.rows()<<","<<data.Q.cols()<<");"<<endl;
|
||||
//#endif
|
||||
|
||||
// Always do dynamics precomputation so we can hot-switch
|
||||
//if(data.with_dynamics)
|
||||
//{
|
||||
// Build cotangent laplacian
|
||||
SparseMatrix<double> Mass;
|
||||
//printf("massmatrix()\n");
|
||||
massmatrix(V,F,(F.cols()>3?MASSMATRIX_TYPE_BARYCENTRIC:MASSMATRIX_TYPE_VORONOI),Mass);
|
||||
//cout<<"MIJV=["<<endl;print_ijv(Mass,1);cout<<endl<<"];"<<
|
||||
// endl<<"M=sparse(MIJV(:,1),MIJV(:,2),MIJV(:,3),"<<
|
||||
// Mass.rows()<<","<<Mass.cols()<<");"<<endl;
|
||||
//speye(data.n,Mass);
|
||||
SparseMatrix<double> 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<data.n;i++)
|
||||
{
|
||||
for(int d = 0;d<data.dim;d++)
|
||||
{
|
||||
ones(i+d*data.n,d) = 1;
|
||||
}
|
||||
}
|
||||
data.fgrav = MT * (Mass_rep * ones);
|
||||
data.fext = MatrixXS::Zero(MT.rows(),1);
|
||||
//data.fgrav = MT * (ones);
|
||||
//}
|
||||
|
||||
|
||||
// This may/should be superfluous
|
||||
//printf("is_symmetric()\n");
|
||||
if(!is_symmetric(data.Q))
|
||||
{
|
||||
//printf("Fixing symmetry...\n");
|
||||
// "Fix" symmetry
|
||||
LbsMatrixType QT = data.Q.transpose();
|
||||
LbsMatrixType Q_copy = data.Q;
|
||||
data.Q = 0.5*(Q_copy+QT);
|
||||
// Check that ^^^ this really worked. It doesn't always
|
||||
//assert(is_symmetric(*Q));
|
||||
}
|
||||
|
||||
//printf("arap_dof_precomputation() succeeded... so far...\n");
|
||||
verbose("Number of handles: %i\n", data.m);
|
||||
return true;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// STATIC FUNCTIONS (These should be removed or properly defined)
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
namespace igl
|
||||
{
|
||||
// returns maximal difference of 'blok' from scalar times 3x3 identity:
|
||||
template <typename SSCALAR>
|
||||
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 <typename MatrixXS>
|
||||
static typename MatrixXS::Scalar condense_CSM(
|
||||
const std::vector<MatrixXS> &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<numRows; r++)
|
||||
{
|
||||
for (int coord=0; coord<dim+1; coord++)
|
||||
{
|
||||
for (int b=0; b<numBones; b++)
|
||||
{
|
||||
// this is just a test if we really have a multiple of 3x3 identity
|
||||
Eigen::Matrix3f blok;
|
||||
for (int v=0; v<3; v++)
|
||||
{
|
||||
for (int w=0; w<3; w++)
|
||||
{
|
||||
blok(v,w) = CSM_M_SSCALAR[v](r, coord*(numBones*dim) + b + w*numBones);
|
||||
}
|
||||
}
|
||||
|
||||
//SSCALAR value[3];
|
||||
//for (int v=0; v<3; v++)
|
||||
// CSM_M_SSCALAR[v](r, coord*(numBones*dim) + b + v*numBones);
|
||||
|
||||
SSCALAR mD = maxBlokErr<SSCALAR>(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 <typename MatL, typename MatLsep>
|
||||
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<numBones; b++)
|
||||
{
|
||||
for (int coord=0; coord<dimp1; coord++)
|
||||
{
|
||||
for (int c=0; c<dim; c++)
|
||||
{
|
||||
Lsep(coord*numBones + b, c) = L(coord*numBones*dim + c*numBones + b, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// the inverse of splitColumns, i.e., takes numBones*(dimp1) x dim matrix 'Lsep' and merges the dimensions
|
||||
// into columns vector 'L' (which is assumed to be already allocated):
|
||||
//
|
||||
// is this the same as columnize? no.
|
||||
template <typename MatrixXS>
|
||||
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<numBones; b++)
|
||||
{
|
||||
for (int coord=0; coord<dimp1; coord++)
|
||||
{
|
||||
for (int c=0; c<dim; c++)
|
||||
{
|
||||
L(coord*numBones*dim + c*numBones + b, 0) = Lsep(coord*numBones + b, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// converts "Solve1" the "rotations" part of FullSolve matrix (the first part)
|
||||
// into one "condensed" matrix CSolve1 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 <typename MatrixXS>
|
||||
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<dim+1; rowCoord++)
|
||||
{
|
||||
for (int b=0; b<numBones; b++)
|
||||
{
|
||||
for (int colCoord=0; colCoord<dim; colCoord++)
|
||||
{
|
||||
for (int g=0; g<numGroups; g++)
|
||||
{
|
||||
Eigen::Matrix3f blok;
|
||||
for (int r=0; r<3; r++)
|
||||
{
|
||||
for (int c=0; c<3; c++)
|
||||
{
|
||||
blok(r, c) = Solve1(rowCoord*numBones*dim + r*numBones + b, colCoord*numGroups*dim + c*numGroups + g);
|
||||
}
|
||||
}
|
||||
|
||||
SSCALAR mD = maxBlokErr<SSCALAR>(blok);
|
||||
if (mD > maxDiff) maxDiff = mD;
|
||||
|
||||
CSolve1(rowCoord*numBones + b, colCoord*numGroups + g) = blok(0,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return maxDiff;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename LbsMatrixType, typename SSCALAR>
|
||||
IGL_INLINE bool igl::arap_dof_recomputation(
|
||||
const Eigen::Matrix<int,Eigen::Dynamic,1> & fixed_dim,
|
||||
const Eigen::SparseMatrix<double> & A_eq,
|
||||
ArapDOFData<LbsMatrixType, SSCALAR> & data)
|
||||
{
|
||||
using namespace Eigen;
|
||||
typedef Matrix<SSCALAR, Dynamic, Dynamic> 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=["<<endl<<data.fixed_dim<<endl<<"]+1;"<<endl;
|
||||
#endif
|
||||
|
||||
// Compute dense solve matrix (alternative of matrix factorization)
|
||||
//printf("kkt_inverse()\n");
|
||||
MatrixXd Qfull(*Q);
|
||||
MatrixXd A_eqfull(A_eq);
|
||||
MatrixXd M_Solve;
|
||||
|
||||
double timer0_start = get_seconds_hires();
|
||||
bool use_lu = data.effective_dim != 2;
|
||||
//use_lu = false;
|
||||
//printf("use_lu: %s\n",(use_lu?"TRUE":"FALSE"));
|
||||
kkt_inverse(Qfull, A_eqfull, use_lu,M_Solve);
|
||||
double timer0_end = get_seconds_hires();
|
||||
verbose("Bob timing: %.20f\n", (timer0_end - timer0_start)*1000.0);
|
||||
|
||||
// Precompute full solve matrix:
|
||||
const int fsRows = data.m * data.dim * (data.dim + 1); // 12 * number_of_bones
|
||||
const int fsCols1 = data.M_KG.cols(); // 9 * number_of_posConstraints
|
||||
const int fsCols2 = A_eq.rows(); // number_of_posConstraints
|
||||
data.M_FullSolve.resize(fsRows, fsCols1 + fsCols2);
|
||||
// note the magical multiplicative constant "-0.5", I've no idea why it has
|
||||
// to be there :)
|
||||
data.M_FullSolve <<
|
||||
(-0.5 * M_Solve.block(0, 0, fsRows, fsRows) * data.M_KG).template cast<SSCALAR>(),
|
||||
M_Solve.block(0, fsRows, fsRows, fsCols2).template cast<SSCALAR>();
|
||||
|
||||
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<SSCALAR>();
|
||||
}
|
||||
|
||||
// Precompute condensed matrices,
|
||||
// first CSM:
|
||||
std::vector<MatrixXS> CSM_M_SSCALAR;
|
||||
CSM_M_SSCALAR.resize(data.dim);
|
||||
for (int i=0; i<data.dim; i++) CSM_M_SSCALAR[i] = data.CSM_M[i].template cast<SSCALAR>();
|
||||
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 <typename LbsMatrixType, typename SSCALAR>
|
||||
IGL_INLINE bool igl::arap_dof_update(
|
||||
const ArapDOFData<LbsMatrixType, SSCALAR> & data,
|
||||
const Eigen::Matrix<double,Eigen::Dynamic,1> & 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<SSCALAR, Dynamic, Dynamic> 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="<<data.dim<<";"<<endl;
|
||||
cout<<"m="<<data.m<<";"<<endl;
|
||||
#endif
|
||||
|
||||
// number of groups
|
||||
const int k = data.CSM_M[0].rows()/data.dim;
|
||||
for(int i = 0;i<data.dim;i++)
|
||||
{
|
||||
assert(data.CSM_M[i].rows()/data.dim == k);
|
||||
}
|
||||
#ifdef EXTREME_VERBOSE
|
||||
cout<<"k="<<k<<";"<<endl;
|
||||
#endif
|
||||
|
||||
// resize output and initialize with initial guess
|
||||
L = L0;
|
||||
#ifndef IGL_ARAP_DOF_FIXED_ITERATIONS_COUNT
|
||||
// Keep track of last solution
|
||||
MatrixXS L_prev;
|
||||
#endif
|
||||
// We will be iterating on L_SSCALAR, only at the end we convert back to double
|
||||
MatrixXS L_SSCALAR = L.cast<SSCALAR>();
|
||||
|
||||
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<SSCALAR,Eigen::Dynamic,1> Rcol(data.dim * data.dim * k);
|
||||
Matrix<SSCALAR,Dynamic,1> B_eq_SSCALAR = B_eq.cast<SSCALAR>();
|
||||
Matrix<SSCALAR,Dynamic,1> B_eq_fix_SSCALAR;
|
||||
Matrix<SSCALAR,Dynamic,1> L0SSCALAR = L0.cast<SSCALAR>();
|
||||
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=["<<endl<<S<<endl<<"];"<<endl;
|
||||
#endif
|
||||
// Fit rotations to covariance matrices
|
||||
if(data.effective_dim == 2)
|
||||
{
|
||||
fit_rotations_planar(S,R);
|
||||
}else
|
||||
{
|
||||
#ifdef __SSE__ // fit_rotations_SSE will convert to float if necessary
|
||||
fit_rotations_SSE(S,R);
|
||||
#else
|
||||
fit_rotations(S,false,R);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef EXTREME_VERBOSE
|
||||
cout<<"R=["<<endl<<R<<endl<<"];"<<endl;
|
||||
#endif
|
||||
|
||||
if(data.print_timings)
|
||||
{
|
||||
sec_fitRotations = get_seconds_hires();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// "Global" step: fix rotations per mesh vertex, solve for
|
||||
// linear transformations at handles
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// all this shuffling is retarded and not completely negligible time-wise;
|
||||
// TODO: change fit_rotations_XXX so it returns R in the format ready for
|
||||
// CSolveBlock1 multiplication
|
||||
columnize(R, k, 2, Rcol);
|
||||
#ifdef EXTREME_VERBOSE
|
||||
cout<<"Rcol=["<<endl<<Rcol<<endl<<"];"<<endl;
|
||||
#endif
|
||||
splitColumns(Rcol, k, data.dim, data.dim, Rxyz);
|
||||
|
||||
if(data.print_timings)
|
||||
{
|
||||
sec_prepMult = get_seconds_hires();
|
||||
}
|
||||
|
||||
L_part1xyz = data.CSolveBlock1 * Rxyz;
|
||||
//#ifdef IGL_ARAP_DOF_DOUBLE_PRECISION_SOLVE
|
||||
// MKL_matMatMult_double(L_part1xyz, data.CSolveBlock1, Rxyz);
|
||||
//#else
|
||||
// MKL_matMatMult_single(L_part1xyz, data.CSolveBlock1, Rxyz);
|
||||
//#endif
|
||||
mergeColumns(L_part1xyz, data.m, data.dim, data.dim + 1, L_part1);
|
||||
|
||||
if(data.with_dynamics)
|
||||
{
|
||||
// Consider reordering or precomputing matrix multiplications
|
||||
MatrixXS L_part1_dyn(data.dim * (data.dim + 1) * data.m, 1);
|
||||
// Eigen can't parse this:
|
||||
//L_part1_dyn =
|
||||
// -(2.0/(data.h*data.h)) * data.Pi_1 * data.Mass_tilde * data.L0 +
|
||||
// (1.0/(data.h*data.h)) * data.Pi_1 * data.Mass_tilde * data.Lm1;
|
||||
// -1.0 because we've moved these linear terms to the right hand side
|
||||
//MatrixXS temp = -1.0 *
|
||||
// ((-2.0/(data.h*data.h)) * data.L0.array() +
|
||||
// (1.0/(data.h*data.h)) * data.Lm1.array()).matrix();
|
||||
//MatrixXS temp = -1.0 *
|
||||
// ( (-1.0/(data.h*data.h)) * data.L0.array() +
|
||||
// (1.0/(data.h*data.h)) * data.Lm1.array()
|
||||
// (-1.0/(data.h*data.h)) * data.L0.array() +
|
||||
// ).matrix();
|
||||
//Lvel0 = (1.0/(data.h)) * data.Lm1.array() - data.L0.array();
|
||||
MatrixXS temp = -1.0 *
|
||||
( (-1.0/(data.h*data.h)) * data.L0.array() +
|
||||
(1.0/(data.h)) * data.Lvel0.array()
|
||||
).matrix();
|
||||
MatrixXd temp_d = temp.template cast<double>();
|
||||
|
||||
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<double>();
|
||||
MatrixXS temp2_f = temp2.template cast<SSCALAR>();
|
||||
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<L_SSCALAR.rows(); i++)
|
||||
{
|
||||
L_SSCALAR(i, 0) = L_part1(i, 0) + L_part2and3(i, 0);
|
||||
}
|
||||
|
||||
#ifdef EXTREME_VERBOSE
|
||||
cout<<"L=["<<endl<<L<<endl<<"];"<<endl;
|
||||
#endif
|
||||
|
||||
if(data.print_timings)
|
||||
{
|
||||
sec_solve = get_seconds_hires();
|
||||
}
|
||||
|
||||
#ifndef IGL_ARAP_DOF_FIXED_ITERATIONS_COUNT
|
||||
// Compute maximum absolute difference with last iteration's solution
|
||||
max_diff = (L_SSCALAR-L_prev).eval().array().abs().matrix().maxCoeff();
|
||||
#endif
|
||||
iters++;
|
||||
|
||||
if(data.print_timings)
|
||||
{
|
||||
sec_end = get_seconds_hires();
|
||||
#ifndef WIN32
|
||||
// trick to get sec_* variables to compile without warning on mac
|
||||
if(false)
|
||||
#endif
|
||||
printf(
|
||||
"\ntotal iteration time = %f "
|
||||
"[local: covGather = %f, "
|
||||
"fitRotations = %f, "
|
||||
"global: prep = %f, "
|
||||
"solve = %f, "
|
||||
"error = %f [ms]]\n",
|
||||
(sec_end - sec_start)*1000.0,
|
||||
(sec_covGather - sec_start)*1000.0,
|
||||
(sec_fitRotations - sec_covGather)*1000.0,
|
||||
(sec_prepMult - sec_fitRotations)*1000.0,
|
||||
(sec_solve - sec_prepMult)*1000.0,
|
||||
(sec_end - sec_solve)*1000.0 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
L = L_SSCALAR.template cast<double>();
|
||||
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<Eigen::Matrix<double, -1, -1, 0, -1, -1>, double>(ArapDOFData<Eigen::Matrix<double, -1, -1, 0, -1, -1>, double> const&, Eigen::Matrix<double, -1, 1, 0, -1, 1> const&, Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, int, double, Eigen::Matrix<double, -1, -1, 0, -1, -1>&);
|
||||
template bool igl::arap_dof_recomputation<Eigen::Matrix<double, -1, -1, 0, -1, -1>, double>(Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, Eigen::SparseMatrix<double, 0, int> const&, ArapDOFData<Eigen::Matrix<double, -1, -1, 0, -1, -1>, double>&);
|
||||
template bool igl::arap_dof_precomputation<Eigen::Matrix<double, -1, -1, 0, -1, -1>, double>(Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, ArapDOFData<Eigen::Matrix<double, -1, -1, 0, -1, -1>, double>&);
|
||||
template bool igl::arap_dof_update<Eigen::Matrix<double, -1, -1, 0, -1, -1>, float>(igl::ArapDOFData<Eigen::Matrix<double, -1, -1, 0, -1, -1>, float> const&, Eigen::Matrix<double, -1, 1, 0, -1, 1> const&, Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, int, double, Eigen::Matrix<double, -1, -1, 0, -1, -1>&);
|
||||
template bool igl::arap_dof_recomputation<Eigen::Matrix<double, -1, -1, 0, -1, -1>, float>(Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, Eigen::SparseMatrix<double, 0, int> const&, igl::ArapDOFData<Eigen::Matrix<double, -1, -1, 0, -1, -1>, float>&);
|
||||
template bool igl::arap_dof_precomputation<Eigen::Matrix<double, -1, -1, 0, -1, -1>, float>(Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, igl::ArapDOFData<Eigen::Matrix<double, -1, -1, 0, -1, -1>, float>&);
|
||||
#endif
|
||||
+136
-134
@@ -14,75 +14,76 @@
|
||||
#include "ARAPEnergyType.h"
|
||||
#include <vector>
|
||||
|
||||
/// @file arap_dof.h
|
||||
/// @brief "Fast Automatic Skinning Transformations" [Jacobson et al.\ 2012]
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
///
|
||||
/// #### Caller example:
|
||||
///
|
||||
/// Once:
|
||||
/// arap_dof_precomputation(...)
|
||||
///
|
||||
/// Each frame:
|
||||
/// while(not satisfied)
|
||||
/// arap_dof_update(...)
|
||||
/// end
|
||||
/// 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
|
||||
///
|
||||
|
||||
|
||||
namespace igl
|
||||
{
|
||||
// Caller example:
|
||||
//
|
||||
// Once:
|
||||
// arap_dof_precomputation(...)
|
||||
//
|
||||
// Each frame:
|
||||
// while(not satisfied)
|
||||
// arap_dof_update(...)
|
||||
// end
|
||||
|
||||
template <typename LbsMatrixType, typename SSCALAR>
|
||||
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
|
||||
/// Precomputes the system to optimize for "Fast Automatic Skinning
|
||||
/// Transformations" [Jacobson et al.\ 2012] skinning degrees of freedom
|
||||
/// optimization using as-rigid-as-possible energy. 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.
|
||||
///
|
||||
/// @param[in] V #V by dim list of vertex positions
|
||||
/// @param[in] F #F by {3|4} list of face indices
|
||||
/// @param[in] 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)
|
||||
/// @param[in] G #V list of group indices (1 to k) for each vertex, such that vertex i
|
||||
/// is assigned to group G(i)
|
||||
/// @param[out] data structure containing all necessary precomputation for calling
|
||||
/// arap_dof_update
|
||||
/// @return true on success, false on error
|
||||
///
|
||||
/// \see lbs_matrix_column
|
||||
///
|
||||
/// \fileinfo
|
||||
template <typename LbsMatrixType, typename SSCALAR>
|
||||
IGL_INLINE bool arap_dof_precomputation(
|
||||
const Eigen::MatrixXd & V,
|
||||
@@ -91,49 +92,49 @@ namespace igl
|
||||
const Eigen::Matrix<int,Eigen::Dynamic,1> & G,
|
||||
ArapDOFData<LbsMatrixType, SSCALAR> & 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
|
||||
/// 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
|
||||
///
|
||||
/// @param[in] 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
|
||||
/// @param[in] 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
|
||||
/// @param[out] data structure containing all necessary precomputation for calling
|
||||
/// arap_dof_update
|
||||
/// @return true on success, false on error
|
||||
///
|
||||
/// \see lbs_matrix_column
|
||||
///
|
||||
/// \fileinfo
|
||||
template <typename LbsMatrixType, typename SSCALAR>
|
||||
IGL_INLINE bool arap_dof_recomputation(
|
||||
const Eigen::Matrix<int,Eigen::Dynamic,1> & fixed_dim,
|
||||
const Eigen::SparseMatrix<double> & A_eq,
|
||||
ArapDOFData<LbsMatrixType, SSCALAR> & 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
|
||||
/// Optimizes the transformations attached to each weight function based on
|
||||
/// precomputed system.
|
||||
///
|
||||
/// @param[in] data precomputation data struct output from arap_dof_precomputation
|
||||
/// @param[in] Beq dim*#constraint_points constraint values.
|
||||
/// @param[in] L0 #handles * dim * dim+1 list of initial guess transformation entries,
|
||||
/// also holds fixed transformation entries for fixed handles
|
||||
/// @param[in] max_iters maximum number of iterations
|
||||
/// @param[in] 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)
|
||||
/// @param[out] L #handles * dim * dim+1 list of final optimized transformation entries,
|
||||
/// allowed to be the same as L
|
||||
///
|
||||
/// \fileinfo
|
||||
template <typename LbsMatrixType, typename SSCALAR>
|
||||
IGL_INLINE bool arap_dof_update(
|
||||
const ArapDOFData<LbsMatrixType,SSCALAR> & data,
|
||||
@@ -144,88 +145,89 @@ namespace igl
|
||||
Eigen::MatrixXd & L
|
||||
);
|
||||
|
||||
// Structure that contains fields for all precomputed data or data that needs
|
||||
// to be remembered at update
|
||||
/// Structure that contains fields for all precomputed data or data that needs
|
||||
/// to be remembered at update
|
||||
///
|
||||
/// \fileinfo
|
||||
template <typename LbsMatrixType, typename SSCALAR>
|
||||
struct ArapDOFData
|
||||
{
|
||||
/// Matrix with SSCALAR type
|
||||
typedef Eigen::Matrix<SSCALAR, Eigen::Dynamic, Eigen::Dynamic> MatrixXS;
|
||||
// Type of arap energy we're solving
|
||||
/// 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<double> lu_data;
|
||||
// List of indices of fixed transformation entries
|
||||
/// List of indices of fixed transformation entries
|
||||
Eigen::Matrix<int,Eigen::Dynamic,1> fixed_dim;
|
||||
// List of precomputed covariance scatter matrices multiplied by lbs
|
||||
// matrices
|
||||
//std::vector<Eigen::SparseMatrix<double> > CSM_M;
|
||||
/// List of precomputed covariance scatter matrices multiplied by lbs
|
||||
/// matrices
|
||||
std::vector<Eigen::MatrixXd> CSM_M;
|
||||
/// @private
|
||||
LbsMatrixType M_KG;
|
||||
// Number of mesh vertices
|
||||
/// Number of mesh vertices
|
||||
int n;
|
||||
// Number of weight functions
|
||||
/// Number of weight functions
|
||||
int m;
|
||||
// Number of dimensions
|
||||
/// Number of dimensions
|
||||
int dim;
|
||||
// Effective dimensions
|
||||
/// Effective dimensions
|
||||
int effective_dim;
|
||||
// List of indices into C of positional constraints
|
||||
/// List of indices into C of positional constraints
|
||||
Eigen::Matrix<int,Eigen::Dynamic,1> interpolated;
|
||||
/// Mask of free variables
|
||||
std::vector<bool> free_mask;
|
||||
// Full quadratic coefficients matrix before lagrangian (should be dense)
|
||||
/// 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
|
||||
/// 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):
|
||||
/// Precomputed condensed matrices (3x3 commutators folded to 1x1):
|
||||
MatrixXS CSM;
|
||||
/// @private
|
||||
MatrixXS CSolveBlock1;
|
||||
|
||||
// Print timings at each update
|
||||
/// Print timings at each update
|
||||
bool print_timings;
|
||||
|
||||
// Dynamics
|
||||
/// 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
|
||||
/// Time step
|
||||
double h;
|
||||
|
||||
// L0 #handles * dim * dim+1 list of transformation entries from
|
||||
// previous solve
|
||||
/// #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"
|
||||
/// "Velocity"
|
||||
MatrixXS Lvel0;
|
||||
|
||||
// #V by dim matrix of external forces
|
||||
// fext
|
||||
/// #V by dim matrix of external forces
|
||||
MatrixXS fext;
|
||||
|
||||
// Mass_tilde: MT * Mass * M
|
||||
/// Mass_tilde: MT * Mass * M
|
||||
LbsMatrixType Mass_tilde;
|
||||
|
||||
// Force due to gravity (premultiplier)
|
||||
/// Force due to gravity (premultiplier)
|
||||
Eigen::MatrixXd fgrav;
|
||||
// Direction of gravity
|
||||
/// Direction of gravity
|
||||
Eigen::Vector3d grav_dir;
|
||||
// Magnitude of gravity
|
||||
/// Magnitude of gravity
|
||||
double grav_mag;
|
||||
|
||||
// Π1 from the paper
|
||||
/// Π1 from the paper
|
||||
MatrixXS Pi_1;
|
||||
|
||||
// Default values
|
||||
// @private Default values
|
||||
ArapDOFData():
|
||||
energy(igl::ARAP_ENERGY_TYPE_SPOKES),
|
||||
with_dynamics(false),
|
||||
|
||||
@@ -1,259 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <Eigen/Dense>
|
||||
|
||||
template <typename MatV, typename MatF, typename MatK>
|
||||
IGL_INLINE void igl::arap_linear_block(
|
||||
const MatV & V,
|
||||
const MatF & F,
|
||||
const int d,
|
||||
const igl::ARAPEnergyType energy,
|
||||
MatK & 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 <typename MatV, typename MatF, typename MatK>
|
||||
IGL_INLINE void igl::arap_linear_block_spokes(
|
||||
const MatV & V,
|
||||
const MatF & F,
|
||||
const int d,
|
||||
MatK & Kd)
|
||||
{
|
||||
typedef typename MatK::Scalar Scalar;
|
||||
|
||||
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<int,Dynamic,2> edges;
|
||||
Kd.resize(V.rows(), V.rows());
|
||||
vector<Triplet<Scalar> > 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<Scalar,Dynamic,Dynamic> 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<m;i++)
|
||||
{
|
||||
// loop over edges of element
|
||||
for(int e = 0;e<edges.rows();e++)
|
||||
{
|
||||
int source = F(i,edges(e,0));
|
||||
int dest = F(i,edges(e,1));
|
||||
double v = 0.5*C(i,e)*(V(source,d)-V(dest,d));
|
||||
Kd_IJV.push_back(Triplet<Scalar>(source,dest,v));
|
||||
Kd_IJV.push_back(Triplet<Scalar>(dest,source,-v));
|
||||
Kd_IJV.push_back(Triplet<Scalar>(source,source,v));
|
||||
Kd_IJV.push_back(Triplet<Scalar>(dest,dest,-v));
|
||||
}
|
||||
}
|
||||
Kd.setFromTriplets(Kd_IJV.begin(),Kd_IJV.end());
|
||||
Kd.makeCompressed();
|
||||
}
|
||||
|
||||
template <typename MatV, typename MatF, typename MatK>
|
||||
IGL_INLINE void igl::arap_linear_block_spokes_and_rims(
|
||||
const MatV & V,
|
||||
const MatF & F,
|
||||
const int d,
|
||||
MatK & Kd)
|
||||
{
|
||||
typedef typename MatK::Scalar Scalar;
|
||||
|
||||
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<Triplet<Scalar> > Kd_IJV;
|
||||
Matrix<int,Dynamic,2> 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<Scalar,Dynamic,Dynamic> 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<m;i++)
|
||||
{
|
||||
// loop over edges of element
|
||||
for(int e = 0;e<edges.rows();e++)
|
||||
{
|
||||
int source = F(i,edges(e,0));
|
||||
int dest = F(i,edges(e,1));
|
||||
double v = C(i,e)*(V(source,d)-V(dest,d))/3.0;
|
||||
// loop over edges again
|
||||
for(int f = 0;f<edges.rows();f++)
|
||||
{
|
||||
int Rs = F(i,edges(f,0));
|
||||
int Rd = F(i,edges(f,1));
|
||||
if(Rs == source && Rd == dest)
|
||||
{
|
||||
Kd_IJV.push_back(Triplet<Scalar>(Rs,Rd,v));
|
||||
Kd_IJV.push_back(Triplet<Scalar>(Rd,Rs,-v));
|
||||
}else if(Rd == source)
|
||||
{
|
||||
Kd_IJV.push_back(Triplet<Scalar>(Rd,Rs,v));
|
||||
}else if(Rs == dest)
|
||||
{
|
||||
Kd_IJV.push_back(Triplet<Scalar>(Rs,Rd,-v));
|
||||
}
|
||||
}
|
||||
Kd_IJV.push_back(Triplet<Scalar>(source,source,v));
|
||||
Kd_IJV.push_back(Triplet<Scalar>(dest,dest,-v));
|
||||
}
|
||||
}
|
||||
Kd.setFromTriplets(Kd_IJV.begin(),Kd_IJV.end());
|
||||
Kd.makeCompressed();
|
||||
}
|
||||
|
||||
template <typename MatV, typename MatF, typename MatK>
|
||||
IGL_INLINE void igl::arap_linear_block_elements(
|
||||
const MatV & V,
|
||||
const MatF & F,
|
||||
const int d,
|
||||
MatK & Kd)
|
||||
{
|
||||
typedef typename MatK::Scalar Scalar;
|
||||
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<Triplet<Scalar> > Kd_IJV;
|
||||
Matrix<int,Dynamic,2> 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<Scalar,Dynamic,Dynamic> 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<m;i++)
|
||||
{
|
||||
// loop over edges of element
|
||||
for(int e = 0;e<edges.rows();e++)
|
||||
{
|
||||
int source = F(i,edges(e,0));
|
||||
int dest = F(i,edges(e,1));
|
||||
double v = C(i,e)*(V(source,d)-V(dest,d));
|
||||
Kd_IJV.push_back(Triplet<Scalar>(source,i,v));
|
||||
Kd_IJV.push_back(Triplet<Scalar>(dest,i,-v));
|
||||
}
|
||||
}
|
||||
Kd.setFromTriplets(Kd_IJV.begin(),Kd_IJV.end());
|
||||
Kd.makeCompressed();
|
||||
}
|
||||
|
||||
|
||||
#ifdef IGL_STATIC_LIBRARY
|
||||
// Explicit template instantiation
|
||||
template void igl::arap_linear_block<Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >, Eigen::SparseMatrix<double, 0, int> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, int, igl::ARAPEnergyType, Eigen::SparseMatrix<double, 0, int>&);
|
||||
template void igl::arap_linear_block<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::SparseMatrix<double, 0, int> >(Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, int, igl::ARAPEnergyType, Eigen::SparseMatrix<double, 0, int>&);
|
||||
#endif
|
||||
@@ -10,39 +10,39 @@
|
||||
#include "igl_inline.h"
|
||||
|
||||
#include <Eigen/Sparse>
|
||||
#include <igl/ARAPEnergyType.h>
|
||||
#include "ARAPEnergyType.h"
|
||||
|
||||
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
|
||||
//
|
||||
/// 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.
|
||||
///
|
||||
/// @tparam MatV vertex position matrix, e.g. Eigen::MatrixXd
|
||||
/// @tparam MatF face index matrix, e.g. Eigen::MatrixXd
|
||||
/// @tparam Scalar e.g. double
|
||||
/// @param[in] V #V by dim list of initial domain positions
|
||||
/// @param[in] F #F by #simplex size list of triangle indices into V
|
||||
/// @param[in] d coordinate of linear constructor to build
|
||||
/// @param[in] energy ARAPEnergyType enum value defining which energy is being used.
|
||||
/// See ARAPEnergyType.h for valid options and explanations.
|
||||
/// @param[out] Kd #V by #V/#F block of the linear constructor matrix
|
||||
/// corresponding to coordinate d
|
||||
///
|
||||
/// \see ARAPEnergyType
|
||||
template <typename MatV, typename MatF, typename MatK>
|
||||
IGL_INLINE void arap_linear_block(
|
||||
const MatV & V,
|
||||
@@ -50,19 +50,54 @@ namespace igl
|
||||
const int d,
|
||||
const igl::ARAPEnergyType energy,
|
||||
MatK & Kd);
|
||||
// Helper functions for each energy type
|
||||
/// Constructs a block of the matrix which constructs the linear terms for
|
||||
/// spokes energy.
|
||||
///
|
||||
/// @tparam MatV vertex position matrix, e.g. Eigen::MatrixXd
|
||||
/// @tparam MatF face index matrix, e.g. Eigen::MatrixXd
|
||||
/// @tparam Scalar e.g. double
|
||||
/// @param[in] V #V by dim list of initial domain positions
|
||||
/// @param[in] F #F by #simplex size list of triangle indices into V
|
||||
/// @param[in] d coordinate of linear constructor to build (0 index)
|
||||
/// See ARAPEnergyType.h for valid options and explanations.
|
||||
/// @param[out] Kd #V by #V block of the linear constructor matrix
|
||||
/// corresponding to coordinate d
|
||||
template <typename MatV, typename MatF, typename MatK>
|
||||
IGL_INLINE void arap_linear_block_spokes(
|
||||
const MatV & V,
|
||||
const MatF & F,
|
||||
const int d,
|
||||
MatK & Kd);
|
||||
/// Constructs a block of the matrix which constructs the linear terms for
|
||||
/// spokes and rims energy.
|
||||
///
|
||||
/// @tparam MatV vertex position matrix, e.g. Eigen::MatrixXd
|
||||
/// @tparam MatF face index matrix, e.g. Eigen::MatrixXd
|
||||
/// @tparam Scalar e.g. double
|
||||
/// @param[in] V #V by dim list of initial domain positions
|
||||
/// @param[in] F #F by #simplex size list of triangle indices into V
|
||||
/// @param[in] d coordinate of linear constructor to build (0 index)
|
||||
/// See ARAPEnergyType.h for valid options and explanations.
|
||||
/// @param[out] Kd #V by #V block of the linear constructor matrix
|
||||
/// corresponding to coordinate d
|
||||
template <typename MatV, typename MatF, typename MatK>
|
||||
IGL_INLINE void arap_linear_block_spokes_and_rims(
|
||||
const MatV & V,
|
||||
const MatF & F,
|
||||
const int d,
|
||||
MatK & Kd);
|
||||
/// Constructs a block of the matrix which constructs the linear terms for
|
||||
/// per element energy.
|
||||
///
|
||||
/// @tparam MatV vertex position matrix, e.g. Eigen::MatrixXd
|
||||
/// @tparam MatF face index matrix, e.g. Eigen::MatrixXd
|
||||
/// @tparam Scalar e.g. double
|
||||
/// @param[in] V #V by dim list of initial domain positions
|
||||
/// @param[in] F #F by #simplex size list of triangle indices into V
|
||||
/// @param[in] d coordinate of linear constructor to build (0 index)
|
||||
/// See ARAPEnergyType.h for valid options and explanations.
|
||||
/// @param[out] Kd #V by #F block of the linear constructor matrix
|
||||
/// corresponding to coordinate d
|
||||
template <typename MatV, typename MatF, typename MatK>
|
||||
IGL_INLINE void arap_linear_block_elements(
|
||||
const MatV & V,
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <iostream>
|
||||
|
||||
template<typename DerivedV, typename DerivedF, typename DerivedK>
|
||||
IGL_INLINE void igl::arap_rhs(
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
const Eigen::MatrixBase<DerivedF> & F,
|
||||
const int dim,
|
||||
const igl::ARAPEnergyType energy,
|
||||
Eigen::SparseCompressedBase<DerivedK>& 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;
|
||||
}
|
||||
|
||||
DerivedK 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)
|
||||
{
|
||||
DerivedK 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef IGL_STATIC_LIBRARY
|
||||
template void igl::arap_rhs(const Eigen::MatrixBase<Eigen::MatrixXd> & V, const Eigen::MatrixBase<Eigen::MatrixXi> & F,const int dim, const igl::ARAPEnergyType energy,Eigen::SparseCompressedBase<Eigen::SparseMatrix<double>>& K);
|
||||
#endif
|
||||
+14
-15
@@ -8,27 +8,26 @@
|
||||
#ifndef IGL_ARAP_RHS_H
|
||||
#define IGL_ARAP_RHS_H
|
||||
#include "igl_inline.h"
|
||||
#include "ARAPEnergyType.h"
|
||||
|
||||
#include <Eigen/Dense>
|
||||
#include <Eigen/Sparse>
|
||||
#include <igl/ARAPEnergyType.h>
|
||||
|
||||
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
|
||||
/// Right-hand side constructor of global poisson solve for various Arap
|
||||
/// energies
|
||||
///
|
||||
/// @param[in] V #V by Vdim list of initial domain positions
|
||||
/// @param[in] F #F by 3 list of triangle indices into V
|
||||
/// @param[in] dim dimension being used at solve time. For deformation usually dim =
|
||||
/// V.cols(), for surface parameterization V.cols() = 3 and dim = 2
|
||||
/// @param[in] energy igl::ARAPEnergyType enum value defining which energy is being
|
||||
/// used. See igl::ARAPEnergyType.h for valid options and explanations.
|
||||
/// @param[out] 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 arap_linear_block
|
||||
template<typename DerivedV, typename DerivedF, typename DerivedK>
|
||||
IGL_INLINE void arap_rhs(
|
||||
const Eigen::MatrixBase<DerivedV> & V,
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2020 Oded Stein <oded.stein@columbia.edu>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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_from_edges_onto_vertices.h"
|
||||
|
||||
template<typename DerivedF,typename DerivedE,typename DerivedoE,
|
||||
typename DeriveduE,typename DeriveduV>
|
||||
IGL_INLINE void
|
||||
igl::average_from_edges_onto_vertices(
|
||||
const Eigen::MatrixBase<DerivedF> &F,
|
||||
const Eigen::MatrixBase<DerivedE> &E,
|
||||
const Eigen::MatrixBase<DerivedoE> &oE,
|
||||
const Eigen::MatrixBase<DeriveduE> &uE,
|
||||
Eigen::PlainObjectBase<DeriveduV> &uV)
|
||||
{
|
||||
using Scalar = typename DeriveduE::Scalar;
|
||||
using VecX = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;
|
||||
using Int = typename DerivedF::Scalar;
|
||||
|
||||
assert(E.rows()==F.rows() && "E does not match dimensions of F.");
|
||||
assert(oE.rows()==F.rows() && "oE does not match dimensions of F.");
|
||||
assert(E.cols()==3 && F.cols()==3 && oE.cols()==3 &&
|
||||
"This method is for triangle meshes.");
|
||||
|
||||
const Int n = F.maxCoeff()+1;
|
||||
|
||||
VecX edgesPerVertex(n);
|
||||
edgesPerVertex.setZero();
|
||||
uV.resize(n,1);
|
||||
uV.setZero();
|
||||
|
||||
for(Eigen::Index i=0; i<F.rows(); ++i) {
|
||||
for(int j=0; j<3; ++j) {
|
||||
if(oE(i,j)<0) {
|
||||
continue;
|
||||
}
|
||||
const Int e = E(i,j);
|
||||
const Int vi=F(i,(j+1)%3), vj=F(i,(j+2)%3);
|
||||
|
||||
//Count vertex valence
|
||||
++edgesPerVertex(vi);
|
||||
++edgesPerVertex(vj);
|
||||
|
||||
//Average uE value onto vertices
|
||||
uV(vi) += uE(e);
|
||||
uV(vj) += uE(e);
|
||||
}
|
||||
}
|
||||
|
||||
//Divide by valence
|
||||
for(Int i=0; i<n; ++i) {
|
||||
const Scalar valence = edgesPerVertex(i);
|
||||
if(valence>0) {
|
||||
uV(i) /= valence;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef IGL_STATIC_LIBRARY
|
||||
// Explicit template instantiation
|
||||
template void igl::average_from_edges_onto_vertices<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::PartialReduxExpr<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::internal::member_norm<double>, 1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::PartialReduxExpr<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::internal::member_norm<double>, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
|
||||
template void igl::average_from_edges_onto_vertices<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
|
||||
template void igl::average_from_edges_onto_vertices<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
|
||||
#endif
|
||||
@@ -12,16 +12,15 @@
|
||||
#include <Eigen/Dense>
|
||||
namespace igl
|
||||
{
|
||||
// Move a scalar field defined on edges to vertices by averaging
|
||||
//
|
||||
// Input:
|
||||
// F: triangle mesh connectivity
|
||||
// E, oE: mapping from halfedges to edges and orientation as generated by
|
||||
// orient_halfedges
|
||||
// uE: scalar field defined on edges, one per edge
|
||||
//
|
||||
// Output:
|
||||
// uV: scalar field defined on vertices
|
||||
/// Move a scalar field defined on edges to vertices by averaging
|
||||
///
|
||||
/// @param[in] F #F by 3 triangle mesh connectivity
|
||||
/// @param[in] E #E by 3 mapping from each halfedge to each edge
|
||||
/// @param[in] oE #E by 3 orientation as generated by orient_halfedges
|
||||
/// @param[in] uE #E by 1 list of scalars
|
||||
/// @param[out] uV #V by 1 list of scalar defined on vertices
|
||||
///
|
||||
/// \see orient_halfedges
|
||||
template<typename DerivedF,typename DerivedE,typename DerivedoE,
|
||||
typename DeriveduE,typename DeriveduV>
|
||||
IGL_INLINE void average_from_edges_onto_vertices(
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <typename DerivedF, typename DerivedS, typename DerivedSF>
|
||||
IGL_INLINE void igl::average_onto_faces(
|
||||
const Eigen::MatrixBase<DerivedF> & F,
|
||||
const Eigen::MatrixBase<DerivedS> & S,
|
||||
Eigen::PlainObjectBase<DerivedSF> & SF)
|
||||
{
|
||||
SF.setConstant(F.rows(),S.cols(),0);
|
||||
for (int i = 0; i <F.rows(); ++i)
|
||||
for (int j = 0; j<F.cols(); ++j)
|
||||
SF.row(i) += S.row(F(i,j));
|
||||
SF.array() /= F.cols();
|
||||
}
|
||||
|
||||
#ifdef IGL_STATIC_LIBRARY
|
||||
// Explicit template instantiation
|
||||
// generated by autoexplicit.sh
|
||||
template void igl::average_onto_faces<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
|
||||
#endif
|
||||
@@ -12,14 +12,11 @@
|
||||
#include <Eigen/Dense>
|
||||
namespace igl
|
||||
{
|
||||
// average_onto_vertices
|
||||
// Move a scalar field defined on faces to vertices by averaging
|
||||
//
|
||||
// Input:
|
||||
// F #F by ss list of simples/faces
|
||||
// S #V by dim list of per-vertex values
|
||||
// Output:
|
||||
// SF #F by dim list of per-face values
|
||||
/// Move a scalar field defined on vertices to faces by averaging
|
||||
///
|
||||
/// @param[in] F #F by ss list of simples/faces
|
||||
/// @param[in] S #V by dim list of per-vertex values
|
||||
/// @param[out] SF #F by dim list of per-face values
|
||||
template <typename DerivedF, typename DerivedS, typename DerivedSF>
|
||||
IGL_INLINE void average_onto_faces(
|
||||
const Eigen::MatrixBase<DerivedF> & F,
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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<typename DerivedV,typename DerivedF,typename DerivedS,typename DerivedSV >
|
||||
IGL_INLINE void igl::average_onto_vertices(const Eigen::MatrixBase<DerivedV> &V,
|
||||
const Eigen::MatrixBase<DerivedF> &F,
|
||||
const Eigen::MatrixBase<DerivedS> &S,
|
||||
Eigen::PlainObjectBase<DerivedSV> &SV)
|
||||
{
|
||||
SV = DerivedS::Zero(V.rows(),S.cols());
|
||||
Eigen::Matrix<typename DerivedF::Scalar,Eigen::Dynamic,1> COUNT(V.rows());
|
||||
COUNT.setZero();
|
||||
for (int i = 0; i <F.rows(); ++i)
|
||||
{
|
||||
for (int j = 0; j<F.cols(); ++j)
|
||||
{
|
||||
SV.row(F(i,j)) += S.row(i);
|
||||
COUNT[F(i,j)] ++;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i <V.rows(); ++i)
|
||||
SV.row(i) /= COUNT[i];
|
||||
};
|
||||
|
||||
#ifdef IGL_STATIC_LIBRARY
|
||||
// Explicit template instantiation
|
||||
#endif
|
||||
@@ -12,15 +12,12 @@
|
||||
#include <Eigen/Dense>
|
||||
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
|
||||
/// Move a scalar field defined on faces to vertices by averaging
|
||||
///
|
||||
/// @param[in] V #V by 3 list of mesh vertex positions
|
||||
/// @param[in] F #F by 3 list of mesh face indices into rows of V
|
||||
/// @param[in] S #F by 1 scalar field defined on faces
|
||||
/// @param[out] SV #V by 1 scalar field defined on vertices
|
||||
template<typename DerivedV,typename DerivedF,typename DerivedS,typename DerivedSV>
|
||||
IGL_INLINE void average_onto_vertices(const Eigen::MatrixBase<DerivedV> &V,
|
||||
const Eigen::MatrixBase<DerivedF> &F,
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 "edges.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
template <typename DerivedV, typename DerivedF>
|
||||
IGL_INLINE double igl::avg_edge_length(
|
||||
const Eigen::MatrixBase<DerivedV>& V,
|
||||
const Eigen::MatrixBase<DerivedF>& F)
|
||||
{
|
||||
typedef typename DerivedF::Scalar Index;
|
||||
Eigen::Matrix<Index, Eigen::Dynamic, 2> E;
|
||||
|
||||
igl::edges(F, E);
|
||||
|
||||
double avg = 0;
|
||||
|
||||
for (unsigned i=0;i<E.rows();++i)
|
||||
{
|
||||
avg += (V.row(E(i,0)) - V.row(E(i,1))).norm();
|
||||
}
|
||||
|
||||
return avg / (double) E.rows();
|
||||
}
|
||||
|
||||
#ifdef IGL_STATIC_LIBRARY
|
||||
// Explicit template instantiation
|
||||
// generated by autoexplicit.sh
|
||||
template double igl::avg_edge_length<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&);
|
||||
template double igl::avg_edge_length<Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&);
|
||||
// generated by autoexplicit.sh
|
||||
#endif
|
||||
@@ -15,18 +15,16 @@
|
||||
|
||||
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
|
||||
/// Compute the average edge length for the given triangle mesh
|
||||
///
|
||||
/// @tparam DerivedV derived from vertex positions matrix type: i.e. MatrixXd
|
||||
/// @tparam DerivedF derived from face indices matrix type: i.e. MatrixXi
|
||||
/// @tparam DerivedL derived from edge lengths matrix type: i.e. MatrixXd
|
||||
/// @param[in] V #V by dim list of mesh vertex positions
|
||||
/// @param[in] F #F by simplex-size list of mesh faces (must be simplex)
|
||||
/// @return average edge length
|
||||
///
|
||||
/// \see adjacency_matrix
|
||||
template <typename DerivedV, typename DerivedF>
|
||||
IGL_INLINE double avg_edge_length(
|
||||
const Eigen::MatrixBase<DerivedV>& V,
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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 <cmath>
|
||||
|
||||
// http://www.antisphere.com/Wiki/tools:anttweakbar
|
||||
template <typename Q_type>
|
||||
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>())
|
||||
{
|
||||
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>(double const*, double, double*);
|
||||
// generated by autoexplicit.sh
|
||||
template void igl::axis_angle_to_quat<float>(float const*, float, float*);
|
||||
#endif
|
||||
@@ -11,14 +11,15 @@
|
||||
|
||||
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
|
||||
/// 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
|
||||
/// @param[in] axis 3d vector
|
||||
/// @param[in] angle scalar
|
||||
/// @param[out] out pointer to new quaternion
|
||||
///
|
||||
/// \deprecated Use `Eigen::AngleAxisd` instead
|
||||
template <typename Q_type>
|
||||
IGL_INLINE void axis_angle_to_quat(
|
||||
const Q_type *axis,
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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<DerivedV> & V,
|
||||
const Eigen::MatrixBase<DerivedF> & F,
|
||||
Eigen::PlainObjectBase<DerivedBC> & BC)
|
||||
{
|
||||
BC.setZero(F.rows(),V.cols());
|
||||
// Loop over faces
|
||||
for(int i = 0;i<F.rows();i++)
|
||||
{
|
||||
// loop around face
|
||||
for(int j = 0;j<F.cols();j++)
|
||||
{
|
||||
// Accumulate
|
||||
BC.row(i) += V.row(F(i,j));
|
||||
}
|
||||
// average
|
||||
BC.row(i) /= double(F.cols());
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef IGL_STATIC_LIBRARY
|
||||
// Explicit template instantiation
|
||||
// generated by autoexplicit.sh
|
||||
template void igl::barycenter<Eigen::Matrix<double, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 3, 0, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 1, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<double, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, 3, 1, -1, 3>, Eigen::Matrix<double, -1, 3, 0, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 1, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<double, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 1, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<float, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<float, -1, 3, 0, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<float, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 0, -1, 3> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<float, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<float, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, 3, 1, -1, 3>, Eigen::Matrix<float, -1, 3, 0, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 0, -1, 3> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, 3, 0, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 0, -1, 3> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 4, 0, -1, 4> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 4, 0, -1, 4> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<double, -1, 4, 0, -1, 4>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 4, 0, -1, 4> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, 4, 0, -1, 4> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 4, 0, -1, 4> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 3, 0, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<double, -1, 3, 0, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 2, 0, -1, 2> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 2, 0, -1, 2> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, 2, 3, 0, 2, 3>, Eigen::Matrix<double, 2, 3, 0, 2, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 2, 3, 0, 2, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, 2, 3, 0, 2, 3> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, 2, 3, 0, 2, 3>, Eigen::Matrix<double, 2, 3, 0, 2, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, 2, 3, 0, 2, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, 2, 3, 0, 2, 3> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 2, 0, -1, 2> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 2, 0, -1, 2> >&);
|
||||
template void igl::barycenter<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 3, 0, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&);
|
||||
#endif
|
||||
@@ -11,14 +11,12 @@
|
||||
#include <Eigen/Dense>
|
||||
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
|
||||
//
|
||||
/// Computes the barycenter of every simplex.
|
||||
///
|
||||
/// @param[in] V #V x dim matrix of vertex coordinates
|
||||
/// @param[in] F #F x simplex_size matrix of indices of simplex corners into V
|
||||
/// @param[out] BC #F x dim matrix of 3d vertices
|
||||
///
|
||||
template <
|
||||
typename DerivedV,
|
||||
typename DerivedF,
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2014 Alec Jacobson <alecjacobson@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. 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<DerivedP> & P,
|
||||
const Eigen::MatrixBase<DerivedA> & A,
|
||||
const Eigen::MatrixBase<DerivedB> & B,
|
||||
const Eigen::MatrixBase<DerivedC> & C,
|
||||
const Eigen::MatrixBase<DerivedD> & D,
|
||||
Eigen::PlainObjectBase<DerivedL> & 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<typename DerivedL::Scalar,DerivedL::RowsAtCompileTime,1>
|
||||
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<<LA,LB,LC,LD;
|
||||
L.array().colwise() /= vol.array();
|
||||
}
|
||||
|
||||
template <
|
||||
typename DerivedP,
|
||||
typename DerivedA,
|
||||
typename DerivedB,
|
||||
typename DerivedC,
|
||||
typename DerivedL>
|
||||
IGL_INLINE void igl::barycentric_coordinates(
|
||||
const Eigen::MatrixBase<DerivedP> & P,
|
||||
const Eigen::MatrixBase<DerivedA> & A,
|
||||
const Eigen::MatrixBase<DerivedB> & B,
|
||||
const Eigen::MatrixBase<DerivedC> & C,
|
||||
Eigen::PlainObjectBase<DerivedL> & 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<float, 1, -1, 1, 1, -1>, Eigen::Matrix<float, 1, 3, 1, 1, 3>, Eigen::Matrix<float, 1, 3, 1, 1, 3>, Eigen::Matrix<float, 1, 3, 1, 1, 3>, Eigen::Matrix<float, 1, 3, 1, 1, 3> >(Eigen::MatrixBase<Eigen::Matrix<float, 1, -1, 1, 1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 1, 3, 1, 1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, 1, 3, 1, 1, 3> >&);
|
||||
template void igl::barycentric_coordinates<Eigen::Matrix<double, 1, -1, 1, 1, -1>, Eigen::Matrix<double, 1, 3, 1, 1, 3>, Eigen::Matrix<double, 1, 3, 1, 1, 3>, Eigen::Matrix<double, 1, 3, 1, 1, 3>, Eigen::Matrix<double, 1, 3, 1, 1, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, 1, -1, 1, 1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> >&);
|
||||
template void igl::barycentric_coordinates<Eigen::Matrix<float, 1, 3, 1, 1, 3>, Eigen::Matrix<float, 1, 3, 1, 1, 3>, Eigen::Matrix<float, 1, 3, 1, 1, 3>, Eigen::Matrix<float, 1, 3, 1, 1, 3>, Eigen::Matrix<float, 1, 3, 1, 1, 3> >(Eigen::MatrixBase<Eigen::Matrix<float, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 1, 3, 1, 1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, 1, 3, 1, 1, 3> >&);
|
||||
template void igl::barycentric_coordinates<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
|
||||
template void igl::barycentric_coordinates<Eigen::Matrix<double, 1, 3, 1, 1, 3>, Eigen::Matrix<double, 1, 3, 1, 1, 3>, Eigen::Matrix<double, 1, 3, 1, 1, 3>, Eigen::Matrix<double, 1, 3, 1, 1, 3>, Eigen::Matrix<double, 1, 3, 1, 1, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> >&);
|
||||
template void igl::barycentric_coordinates<Eigen::Matrix<double, 1, 2, 1, 1, 2>, Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Matrix<double, 1, 3, 1, 1, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, 1, 2, 1, 1, 2> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> >&);
|
||||
template void igl::barycentric_coordinates<Eigen::Matrix<double, 1, 3, 1, 1, 3>, Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Matrix<double, 1, 3, 1, 1, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> >&);
|
||||
template void igl::barycentric_coordinates<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
|
||||
#endif
|
||||
@@ -11,17 +11,15 @@
|
||||
#include <Eigen/Core>
|
||||
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
|
||||
//
|
||||
/// Compute barycentric coordinates of each point in a corresponding tetrahedron.
|
||||
///
|
||||
/// @param[in] P #P by 3 Query points in 3d
|
||||
/// @param[in] A #P by 3 Tet corners in 3d
|
||||
/// @param[in] B #P by 3 Tet corners in 3d
|
||||
/// @param[in] C #P by 3 Tet corners in 3d
|
||||
/// @param[in] D #P by 3 Tet corners in 3d
|
||||
/// @param[out] L #P by 4 list of barycentric coordinates
|
||||
///
|
||||
template <
|
||||
typename DerivedP,
|
||||
typename DerivedA,
|
||||
@@ -36,16 +34,14 @@ namespace igl
|
||||
const Eigen::MatrixBase<DerivedC> & C,
|
||||
const Eigen::MatrixBase<DerivedD> & D,
|
||||
Eigen::PlainObjectBase<DerivedL> & 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
|
||||
//
|
||||
/// Compute barycentric coordinates in a triangle
|
||||
///
|
||||
/// @param[in] P #P by dim Query points
|
||||
/// @param[in] A #P by dim Triangle corners
|
||||
/// @param[in] B #P by dim Triangle corners
|
||||
/// @param[in] C #P by dim Triangle corners
|
||||
/// @param[out] L #P by 3 list of barycentric coordinates
|
||||
///
|
||||
template <
|
||||
typename DerivedP,
|
||||
typename DerivedA,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user