Compare commits

...
Author SHA1 Message Date
Alec Jacobson e3283e29b7 delete everything but 101 2023-08-24 17:23:27 -04:00
Alec Jacobson 261fec1d05 blah 2023-08-24 17:16:39 -04:00
Alec Jacobson ab6229c77a Euler Characteristic tests (#2250)
* test for euler characteristic

* doc
2023-08-23 16:19:42 -04:00
Alec Jacobson b1fe6ba49c read blank lines as comments (#2247) [ci skip] 2023-08-22 20:20:53 -04:00
Alec Jacobson 8d940367bf support rowmajor (#2246) [ci skip] 2023-08-22 20:20:34 -04:00
Alec Jacobson fb13b11a19 border vertex for quads (#2245) [ci skip] 2023-08-22 15:32:43 -04:00
Alec Jacobson 7c58cb041d better doc; [ci skip] 2023-08-22 14:32:54 -04:00
Alec Jacobson 10e95bb93b robust ray box intersect (#2244) [ci skip]
* nan-proof min/max

* templates
2023-08-22 14:28:34 -04:00
Alec Jacobson 00100b60db bad assert [ci skip] 2023-08-22 14:06:19 -04:00
Alec Jacobson 3f8c5426b8 further split up mqwf templates 2023-08-22 12:20:19 -04:00
Alec Jacobson fd16e24391 assertion bug and too large allocation 2023-08-21 21:33:04 -04:00
Alec Jacobson 44785345e5 float templates + fix (#2243) [ci skip] 2023-08-21 21:09:24 -04:00
Alec Jacobson 71676a111e Remove omp pragmas (#2242) [ci skip]
* remove omp pragmas

* continue -> return
2023-08-21 17:54:51 -04:00
Alec Jacobson e5e7d8a76a fix 2025 with documentation [ci skip] 2023-08-21 17:47:15 -04:00
Alec Jacobson 10002b6cf1 block windows from gl version hell 2023-08-21 17:03:11 -04:00
Alec Jacobson 418dd43cfe small changes for python to compile (#2241) 2023-08-21 11:19:20 -04:00
Alec Jacobson 9162fb7d79 Fix 2228 with blas check on linux (#2240)
* split up 406; cmake

* split up 716

* rm old file

* 716 changes to main

* 709 split up

* missing include

* special syntax for windows ❄️

* stupid windows struct/class

* split up 805

* split up 610)'

* Fix 2228 with blas check on linux
2023-08-20 16:49:05 -04:00
hanxiaoandAlec Jacobson dd38f82afd Ear clipping function fix (#1565) [ci skip]
* fix the is_ear check

* fix order of outputs; overload that does any orientation

---------

Co-authored-by: Alec Jacobson <alecjacobson@gmail.com>
2023-08-19 23:05:58 -04:00
215368a07b Speed up of SelfIntersectMesh: (#1413) [ci skip
* Speed up of SelfIntersectMesh:
The test for intersection of two triangles sharing a common edge
has been optimized to reject non-overlaping triangles with
the least amount of

* Update SelfIntersectMesh.h

---------

Co-authored-by: Jérémie Dumas <jdumas@users.noreply.github.com>
Co-authored-by: Alec Jacobson <alecjacobson@gmail.com>
2023-08-19 20:43:44 -04:00
Sven-Kristofer PilzandAlec Jacobson 17787c86d6 Reduce dynamic allocations for AABB queries (#2001) [ci skip]
* Use static num of cols if available.

* Don't allocate a new vector for only one triangle.

* bug in test

---------

Co-authored-by: Alec Jacobson <alecjacobson@gmail.com>
2023-08-19 20:42:46 -04:00
Alec Jacobson f3f7879364 Split up larger tutorials (#2237) [ci skip]
* split up 406; cmake

* split up 716

* rm old file

* 716 changes to main

* 709 split up

* missing include

* special syntax for windows ❄️

* stupid windows struct/class

* split up 805

* split up 610)'
2023-08-19 19:19:44 -04:00
Alec Jacobson e2a345a43d Alecjacobson/fix mqwf ldlt (#2239)
* enable ldlt

* mqwf test
2023-08-19 17:34:59 -04:00
Alec Jacobson e45a7e0868 missing PI on windows ❄️ 2023-08-19 12:00:29 -04:00
Alec Jacobson c410e80608 turning number in 2D 2023-08-19 11:51:41 -04:00
Alec Jacobson 7765697eb6 split windows header-only tutorial actions (#2238)
* split windows header-only tutorial actions

* double quote issue?

* split 7 into 8 and 9
2023-08-19 11:06:48 -04:00
Alec Jacobson 83922780e1 Missing colon in CMakeLists.txt
Not sure how this was ever working.
2023-08-18 20:42:31 -04:00
Vladimir S. FONOVandAlec Jacobson 02e0a1ba83 Separated png module from opengl, added unit test for png (#1693) [ci skip]
* ENH: separate png from opengl, by splitting it into stb and opengl_image modules
     Added a unit test for PNG,BMP,TGA,JPG reading and writing
     Renamed module png to stb
     Renamed functions readPNG to read_image and writePNG to write_image

* name folder by dependency; consistent arg order

---------

Co-authored-by: Alec Jacobson <alecjacobson@gmail.com>
2023-08-18 14:32:25 -04:00
Alexander Sulfrian 97fb89c955 Source code should not be marked as executable (#2231) [ci skip]
The executable file flag should only be used for binaries and scripts
with a shebang specifying the interpreter of the script.
2023-08-18 11:04:36 -04:00
62fe771ae1 Fix blue_noise and random_points_on_mesh reproducibility (#2235)
* Fix `blue_noise` and `random_points_on_mesh` reproducibility

- added new overload for `blue_noise` and `random_points_on_mesh` for that accept an UnformRandomBitGenerator as input. The default signature (ie without URBG) still works, and uses by default std::minstd_rand initialized with random seed generated by std::rand(). This is following the existing behaviour of `randperm`.
- added reproducibility test case for `blue_noise` and `random_points_on_mesh`

* `blue_noise` and `random_points_on_mesh` overloads without an URBG parameter now use a fixed default seed

ie `std::minstd_rand()`

default seed

* Fix static compilation of tutorials

* revert to jdumas std::rand seed for default

* missing templates on linux

* more missing linux templates

* use std names rather than expansions in explicit templates

---------

Co-authored-by: stourneux <stourneux@buf.com>
Co-authored-by: seb-tourneux <sebastientourneux1@gmail.com>
2023-08-17 23:43:54 -04:00
a183e28109 Fix scale and reflections in Procrustes solver (#2226) [ci skip]
* Allow returning reflections from polar_dec and polar_svd

* Fix scale in Procrustes solver

* Fix reflection case in Procrustes solver

* Add unit test for Procrustes

* fix overloads for backwards comp

* missing templates on linux

---------

Co-authored-by: Alec Jacobson <alecjacobson@adobe.com>
Co-authored-by: Alec Jacobson <alecjacobson@gmail.com>
2023-08-17 22:10:23 -04:00
a69c8c96f3 Adjust igl::boundary_conditions(...) to support 3D cages (#2229)
* Calculate boundary conditions for cage faces

* Remove std:: prefix to conform to original libigl

* Adjust tests to new boundary_conditions(...)

* Adjusting tutorial 403 to new boundary_conditions(...)

* Removing unnecessary include

* Remove tolerances from boundary conditions for cages to prevent dups

---------

Co-authored-by: Daniel <daniel.stoeter@gris.tu-darmstadt.de>
Co-authored-by: Alec Jacobson <alecjacobson@gmail.com>
2023-08-17 19:18:42 -04:00
Alec Jacobson 724ff6b05a Remove deprecated functions (#2234)
* basic config file

* documentation for two funcs

* better theme; subnamespace

* A-c

* documentation for all core headers

* more documentation

* documentation for all headers (except a few classes)

* rm accidental comment on igl

* just h

* typo

* accidental delete

* fix compile issues

* add main page [ci skip]

* relative include paths

* relative include paths

* inexplicably need two more templates

* rm get_seconds_hires

* make fwn namespace private for dox

* hide internal fwn from documentation

* fix doc

* rm deprecated euler

* rm __1
2023-08-17 00:47:43 -04:00
Nico 3cf08b7f68 Cleanup #2216 (#2225) 2023-08-16 21:13:27 -04:00
Alec Jacobson 5ded7da086 Update README.md [ci skip] 2023-08-16 13:15:48 -04:00
Alec Jacobson 2cc372f70d Doxygen based documentation (#2233)
* basic config file

* documentation for two funcs

* better theme; subnamespace

* A-c

* documentation for all core headers

* more documentation

* documentation for all headers (except a few classes)

* rm accidental comment on igl

* just h

* typo

* accidental delete

* fix compile issues

* add main page [ci skip]
2023-08-16 13:14:06 -04:00
Alec Jacobson b1bd5b1216 spectra module (#2216)
* lscm hessian and spectral

* spectra module + test

* try to use Eigen3_FOUND (not working locally)

* use fork

* Don't use size_t for small int (windows hell)
2023-06-28 11:37:34 -04:00
Alec Jacobson 598b0b194a lscm hessian and spectral (#2214) [ci skip] 2023-06-28 11:35:55 -04:00
Alec Jacobson 4de0a0569a use vector () (#2218) [ci skip] 2023-06-28 11:35:36 -04:00
Fabien Péan afafc7cf5a Fix libigl-config.cmake.in (#2188) 2023-06-26 11:45:34 -04:00
Chao Li 514271af51 Implement full type mass matrix (#2193) 2023-06-26 10:47:59 -04:00
Alec Jacobson 282388c68a switch to gmp mirror (#2215)
* switch to gmp mirror

* use gist for patch
2023-06-25 10:14:36 -04:00
Felix Wang 7b6cc27284 Add missing <cstdint> header for gcc 13 (#2192) 2023-04-20 15:42:48 -04:00
Q-MinhandAlec Jacobson a05865e265 Fix ambiguous assignment operator compile error (#2157) [ci skip]
* Fix ambiguous assignment operator compile error

* Revert "Fix ambiguous assignment operator compile error"

This reverts commit 661c482140.

* Fix ambiguous assignment operator compile error

* Add typename

* add template (hopefully trigger error on windows CI

* simpler fix for windows

* use DerivedFI

---------

Co-authored-by: Alec Jacobson <alecjacobson@adobe.com>
2023-03-15 09:26:34 -04:00
Vladimir S. FONOV 1d007f4252 Fast mesh-to-mesh intersection and mesh self intersection without CGAL (#2109)
* Added code for fast triangle-triangle intersection checking and function for fast detection of mesh self-intersections and mesh-to-mesh intersections withoug CGAL

* Fixed auto parameters in lambda helper function

* Replaced cbegin/cend with begin/end

* Fixed auto parameters in lambda helper function

* Added tests for igl::tri_tri_intersection_test_3d

* Added more tests, converted macros in Guigue2003_tri_tri_intersect.cpp to proper c++

* Renamed files and function names to follow IGL guidelines,
added reference to the original license for tri_tri_intersect
2023-03-14 11:37:22 -04:00
Alec Jacobson 3374c1ad71 Update ViewerData.cpp (#2173) [ci skip] 2023-02-25 12:27:17 -05:00
Alec Jacobson 90464ffbc3 Fixes 2174 [ci skip]
Fixes 2174
2023-02-21 08:19:09 -05:00
Alec Jacobson 7e5512ce71 try to use j2 on windows (#2170) 2023-02-11 23:58:58 -05:00
Alec Jacobson 46f0860c18 fix unique_rows and sortrows templating (#2169) 2023-02-11 18:48:39 -05:00
Alec Jacobson 67b406d60d Merge branch 'main' of github.com:libigl/libigl [ci skip] 2023-02-11 14:03:24 -05:00
Alec Jacobson c01718d0b4 fix marker documentation [ci skip] 2023-02-11 14:02:50 -05:00
Alec Jacobson 78015d4da1 bump mpfr version; add logic for mac os cross compiling (#2165)
* bump mpfr version; add logic for mac os cross compiling

* hmm github actions cmake complains where my didn't

* split up tests and tutorial in header only CI

* hmmm why isn't matrix working

* try to fix matrix a different way

* better names

* oops wrong tests in name

* typpppoooo

* mooooree typpppoooos
2023-02-11 00:42:25 -05:00
Alec Jacobson 4a91b88f81 Update continuous.yml (#2161) 2023-02-04 15:46:22 -05:00
Martin Heistermann 3e3c96d0fd CoMISo-MRosy solver: compatibility with newer CoMISo: (#2072)
Omit last (show_timings) argument for ConstraintedSolver::solve call.
This was optional before, and has been removed in CoMISo
174ef38344e09a9547f8a684bf6520cdc8fbe3ba.
2023-02-04 11:55:46 -05:00
Alec Jacobson ee7a7a0aa1 hot fix for missing template 2023-02-04 11:33:14 -05:00
MotivaCG 70dad85a27 Update715_MeshImplicitFunction (#2090)
* Update715_MeshImplicitFunction #2088

Replace the copyleft marching_cubes function with native the libigl one.

* Fix static lib compilation for sample 715
2023-02-04 11:14:25 -05:00
Franck HOUSSEN 610a495be4 glfw viewer - high dpi: handling both width and height. (#2117) 2023-02-04 11:06:28 -05:00
Bryn LloydandBryn Lloyd f1981ff873 BUG: readMESH stuck in endless loop (#2142)
Co-authored-by: Bryn Lloyd <lloyd@itis.swiss>
2023-02-04 11:04:33 -05:00
Alec Jacobson 83dbca4ffd Update compilation.md 2023-02-03 13:44:58 -05:00
Alec Jacobson a3b0fe4a20 Update ViewerCore.cpp 2023-02-02 21:10:09 -05:00
Alec Jacobson 50ac379c53 Shadow Mapping in igl::opengl::glfw::Viewer (#2155)
* floor and working tutorial example

* working shadows with tutorial

* shadows + matcaps

* fiddling
2023-02-02 19:52:09 -05:00
Alec Jacobson dcd1d45d4f remove pinv header 2023-02-01 18:45:29 -05:00
Alec Jacobson e4b8bfb28d throttle tight loop over glfwWaitEvents with sleep (#2151)
* throttle tight loop over glfwWaitEvents with sleep

* restore changes in Main
2023-01-20 12:55:21 -05:00
Alec Jacobson e9c9c5d228 missing template and use template type (#2150) 2023-01-20 01:02:17 -05:00
Alec Jacobson bd85c8998f Improve robustness of ICP solve when closest points and normals are degenerate (#2107)
* fix hard coded double + template

* oops wrong template type

* more robust solve
2023-01-19 22:09:33 -05:00
jmespadero af06a10939 boundary_facets_optimization (#2104) 2023-01-19 22:08:50 -05:00
huihao 2f625e66fd chore(cmake): use GIT_SHALLOW for imgui (#2148) 2023-01-19 22:05:42 -05:00
Alec Jacobson d91f4edeb0 don't crash on brew update (#2138)
* don't crash on brew update

* don't update (again) on brew install ccache

* rm 2to3 before. force true on install, too

* rm 2to3 and others; don't update

* dont rm anything ; just install ccache with no update
2023-01-04 13:02:30 -05:00
Nico 1284a39f13 Fix typo in test that caused test failure in Debug mode (#2137)
Signed-off-by: BruegelN <BruegelN@crashing.systems>

Signed-off-by: BruegelN <BruegelN@crashing.systems>
2023-01-03 13:56:32 -05:00
Dimitrii NikolaevandDimitrii Nikolaev 0c77359c89 Fixes hunter issue https://github.com/cpp-pm/hunter/pull/484 with newest MSVC, which leads to compilation failure (#2123)
Co-authored-by: Dimitrii Nikolaev <nikolaev@ift.at>
2022-12-19 09:28:25 +01:00
Alec Jacobson 87a550af22 fix hard coded double + template (#2106)
* fix hard coded double + template

* oops wrong template type
2022-10-23 16:05:00 -04:00
Alec Jacobson 574ab1a3a8 test needs to be less strict 2022-10-03 11:42:37 -04:00
Richard Liu d6db1cf8c2 lscm: fix order of UV after area term sign fix (#1863)
The fix in #1853 causes the solution UVs to be flipped, so this is a simple fix to that.
2022-10-02 13:47:16 -04:00
Alec Jacobson 3ea7f94809 fix bug when faces don't have markers (#2069) 2022-08-15 09:05:36 -04:00
ubc-nvining 3370a3e9ca Fixes a race condition in the dual contouring code. (#2045)
The previous version of the code would call new_vertex() outside of the mutex, which would trigger a call to resize some std::vector<>s.
This addresses the issue by moving the vertex such that the initialization of ev is contained within the mutex.
2022-06-04 16:03:47 -04:00
Alec Jacobson 33ed4e010b Split mesh non-manifold (and non-orientable) edges and non-manifold vertices (#2047)
* split_nonmanifold following gptoolbox; tests; templates

* connected components should be done on strongly connected adjacency matrix

* clean up templates
2022-05-27 09:53:59 -04:00
Alec Jacobson b4406f2397 fix comment 2022-05-26 23:17:43 -04:00
Alec Jacobson fda700937b fix comment 2022-05-26 21:15:37 -04:00
Alec Jacobson 30019acb17 exact_geodesic: fix bugs, clean up asserts, allow 2D (#2046) 2022-05-26 19:13:19 -04:00
Alec Jacobson 2869f98629 Templates in CGAL module (#2031)
* templates

* attempt to fix windows templates

* attempt to fix windows templates take 2
2022-05-01 19:08:15 -04:00
Alec Jacobson 04f06d7837 point GMPXX_INCLUDE_DIR to locally built gmp (#2030)
* use locally built gmpxx (part of gmp)

* use ignore_package

* reverting to explicitly setting GMP*_INCLUDE_DIR for Windows ❄️

* rm ignore_package(gmpxx); breaks windows
2022-05-01 18:07:44 -04:00
Jérémie Dumas 91f6c503f6 Update glad backend. (#2012) 2022-05-01 11:48:29 -07:00
Alec Jacobson 639378c1f4 Fix 2014 (#2028) 2022-05-01 14:29:47 -04:00
Alec Jacobson dbecea2bc4 cleanup triangulate templates 2022-04-29 19:23:27 -04:00
Alec Jacobson 238a607032 moments of mass (#2027) 2022-04-19 17:48:14 -04:00
Alec Jacobson 4fff4670d3 Update ambient_occlusion.h 2022-04-01 19:28:14 -04:00
Alec Jacobson 1c3d487d8e rm printing from some tests 2022-04-01 13:19:31 -04:00
Alec Jacobson c35f0fee39 eigen → v3.4.0 (#2011)
* eigen -> v3.4.0

* rm problematic template (not used in tutorial or tests)
2022-04-01 12:39:37 -04:00
Alec Jacobson 4498aa8dfc Bump CGAL → Boolean + remesh_*intersections performance boost (#1895)
* bump cgal; parallel remesh; templates; outer*

* more parallel; fewer copies; uE2E→uEC,uEE

* rm spurious warnings leftover from debuggin'

* fix __1; add back many templates

* missing templates

* use CGAL::Epeck::FT in templates directly

* inline to fix header only

* cgal bump requires bumping cmake and boost

* template hell on windows

* fix windows templates to compile on mac os

* fix templates to work on linux

* more template hell; simplify some, use pttr_t, split legacy

* rename legacy fix includes

* fix orientation bug

* missing templates

* abstract templates

* assign overload + templates

* missing templates + missing overload

* merge with main

* special template for windows ❄️
2022-03-31 22:16:05 -04:00
Alec JacobsonandAlec Jacobson e83a560347 Avoid converting exact input to inexact when detecting intersections (#2019)
* Update remesh_self_intersections.cpp

* use epeck when input epeck

* add test case

* missing templates

* special templates for windows ❄️

Co-authored-by: Alec Jacobson <alecjacobson@adobe.com>
2022-03-30 19:28:42 -04:00
Jérémie Dumas 322db89acd Update Windows action. (#2018) 2022-03-28 09:42:06 -04:00
d444bb173f Rework sparse repmat (#1819)
* Add test repmat function

* Rework repmat function

* Add repmat test for colMajor sparse matrices

* derived majorType from params

* missing templates

Co-authored-by: Paul Rötzer <paul@Pauls-MacBook-Pro-2.local>
Co-authored-by: Alec Jacobson <alecjacobson@adobe.com>
Co-authored-by: Alec Jacobson <alecjacobson@gmail.com>
2022-03-27 14:09:03 -04:00
Alec Jacobson 7174a7ac94 rm DynamicSparseMatrix; rm commented code; fix bug in slice_into; +tests (#2016) 2022-03-27 14:08:39 -04:00
Alec Jacobson 6f1571bacd further split min_quad_with_fixed templates (#2017) 2022-03-27 14:06:14 -04:00
zhuguiqian 85db4b17e3 Update principal_curvature.cpp (#2013)
fix crash
2022-03-27 11:59:06 -04:00
Alec Jacobson 142fd6026a Improved draw_buffer (#2010)
* better doc of ViewerCore::draw_buffer and auto sizing

* draw_buffer at per core level, including depth, templated output
2022-03-27 09:48:35 -04:00
ZhaoMAandAlec Jacobson c98c375270 mod: correct comments in header files. (#2007)
* mod: correct comments in header files.

* Update average_onto_faces.h

Co-authored-by: Alec Jacobson <alecjacobson@gmail.com>
2022-03-24 22:00:42 -04:00
Kenshi Takayama fc42e420cd test/lscm: fix wrong assertion (#1889) 2022-03-24 21:59:11 -04:00
Jérémie Dumas a95612e2c9 Fix Windows GitHub Actions 2022-03-24 18:15:09 -07:00
Alec Jacobson 5c2aa0bed8 Revert "Revert "templates""
This reverts commit 6a8e81f800.
2022-03-24 20:14:27 -04:00
Alec Jacobson 068a28f431 Revert "eigen -> v3.4.0"
This reverts commit 92f790b48a.
2022-03-24 20:13:47 -04:00
Alec Jacobson 6a8e81f800 Revert "templates"
This reverts commit 3479ca7ab1.
2022-03-24 20:11:58 -04:00
Alec Jacobson 92f790b48a eigen -> v3.4.0 2022-03-24 20:07:35 -04:00
Alec Jacobson 3479ca7ab1 templates 2022-03-24 19:39:56 -04:00
1324 changed files with 17293 additions and 104818 deletions
+2 -1
View File
@@ -21,5 +21,6 @@ assignees: ''
<!-- Check all that apply (change to `[x]`) -->
- [ ] Windows
- [ ] macOS
- [ ] macOS Intel
- [ ] macOS Arm (e.g., M1, M2)
- [ ] Linux
+44 -30
View File
@@ -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
+1
View File
@@ -47,3 +47,4 @@ LibiglOptions.cmake
# macos debris
.DS_Store
*~
+15 -3
View File
@@ -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()
+1 -1
View File
@@ -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)
+2 -2
View File
@@ -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) |
-49
View File
@@ -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()
+3 -1
View File
@@ -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
+4
View File
@@ -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)
-23
View File
@@ -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
)
+25
View File
@@ -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})
+35
View File
@@ -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()
-18
View File
@@ -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)
+7 -7
View File
@@ -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()
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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)
+47 -5
View File
@@ -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
+1
View File
@@ -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)
+33 -3
View File
@@ -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
+16
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+21
View File
@@ -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/
-1070
View File
File diff suppressed because it is too large Load Diff
+160 -119
View File
@@ -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,
+12 -15
View File
@@ -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
};
}
-130
View File
@@ -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
View File
@@ -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
View File
@@ -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
-359
View File
@@ -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
-30
View File
@@ -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
View File
@@ -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>();
+47 -17
View File
@@ -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 {
+1 -1
View File
@@ -10,7 +10,7 @@
namespace igl
{
/// File encoding types for writing files.
enum class FileEncoding {
Binary,
Ascii
+1
View File
@@ -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)
-162
View File
@@ -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
+49 -45
View File
@@ -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
View File
@@ -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;
};
}
+7 -9
View File
@@ -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
View File
@@ -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(
+2 -3
View File
@@ -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,
+7
View File
@@ -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
};
};
-497
View File
@@ -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));
}
}
+5 -3
View File
@@ -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
-347
View File
@@ -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();
}
+6 -3
View File
@@ -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();
+5 -4
View File
@@ -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
View File
@@ -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;
+2
View File
@@ -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
}
+9 -1
View File
@@ -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
View File
@@ -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
+5 -3
View File
@@ -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,
};
};
+15 -2
View File
@@ -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
View File
@@ -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;
+1
View File
@@ -10,6 +10,7 @@
namespace igl
{
/// @private
// Simple Viewport class for an opengl context. Handles reshaping and mouse.
struct Viewport
{
+10 -1
View File
@@ -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);
+4 -3
View File
@@ -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
};
}
+5 -6
View File
@@ -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>
-52
View File
@@ -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
View File
@@ -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
-370
View File
@@ -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
View File
@@ -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),
-180
View File
@@ -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
+24 -18
View File
@@ -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,
-125
View File
@@ -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
+34 -33
View File
@@ -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,
-26
View File
@@ -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
View File
@@ -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,
-39
View File
@@ -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
+10 -15
View File
@@ -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,
-139
View File
@@ -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
+32 -16
View File
@@ -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,
-20
View File
@@ -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);
}
+6 -7
View File
@@ -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);
-26
View File
@@ -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
View File
@@ -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,
-20
View File
@@ -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
-26
View File
@@ -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
-306
View File
@@ -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
View File
@@ -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>
-884
View File
@@ -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
View File
@@ -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),
-259
View File
@@ -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
+66 -31
View File
@@ -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,
-95
View File
@@ -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
View File
@@ -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
+9 -10
View File
@@ -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(
-27
View File
@@ -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
+5 -8
View File
@@ -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,
-33
View File
@@ -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
+6 -9
View File
@@ -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,
-39
View File
@@ -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
+10 -12
View File
@@ -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,
-42
View File
@@ -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
+9 -8
View File
@@ -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,
-56
View File
@@ -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
+6 -8
View File
@@ -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,
-113
View File
@@ -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
+17 -21
View File
@@ -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