Compare commits

...
Author SHA1 Message Date
Alec Jacobson 0cbcaf8863 bad types in lex (#2544)
Build / macos-15 tutorial Release (push) Waiting to run
Build / ubuntu-24.04 tutorial Release (push) Waiting to run
Build / macos-15 tests Release (push) Waiting to run
Build / ubuntu-24.04 tests Release (push) Waiting to run
Build / macos-15 tutorial tests Release (push) Waiting to run
Build / ubuntu-24.04 tutorial tests Release (push) Waiting to run
Build / Windows tutorial 1 Release (push) Waiting to run
Build / Windows tutorial 10 Release (push) Waiting to run
Build / Windows tutorial 2 Release (push) Waiting to run
Build / Windows tutorial 3 Release (push) Waiting to run
Build / Windows tutorial 4 Release (push) Waiting to run
Build / Windows tutorial 5 Release (push) Waiting to run
Build / Windows tutorial 6 Release (push) Waiting to run
Build / Windows tutorial 7 Release (push) Waiting to run
Build / Windows tutorial 8 Release (push) Waiting to run
Build / Windows tutorial 9 Release (push) Waiting to run
Build / Windows tests Release (push) Waiting to run
Build / Windows tutorial tests Release (push) Waiting to run
2026-08-21 21:50:18 -04:00
Alec JacobsonandClaude Opus 5 f378129b33 swept volume overload (#2552)
* swept volume overload

* swept volume: take transform list, templatize, expose SignedDistanceType

Replace the transform(t)+steps interface of swept_volume,
swept_volume_signed_distance and swept_volume_bounding_box with a list of
rigid transformations passed directly. The transform(t)+steps overloads are
removed rather than kept.

Templatize on Eigen::MatrixBase inputs / Eigen::PlainObjectBase outputs per
libigl style. The transform list is templated on both scalar and allocator so
std::vector<Eigen::Affine3d> and the aligned_allocator spelling both bind.

Expose SignedDistanceType on swept_volume and swept_volume_signed_distance,
dispatching like signed_distance_3 (pseudonormal / winding number / fast
winding number / unsigned) with the precomputation hoisted out of the
per-time-step loop.

swept_volume's isolevel is now a distance (typename DerivedV::Scalar) rather
than a count of grid cells. The padding cancelled out of the grid spacing
already (h == diag/(grid_res-1) regardless of pad), so pad is now derived from
the requested distance instead. Passing isolevel = k*h for integer k
reproduces the old isolevel_grid = k grid exactly. The doc had claimed
"distance level to be contoured" all along while the size_t type and
isolevel_grid implementation meant cells.

707_SweptVolume preloads the motion as a list of transforms sampled uniformly
over t in [0,1] and passes that to swept_volume. Its isolevel of 0.1 had been
truncating to 0 by the size_t conversion, so the tutorial was dilating by
nothing; it is now 10% of the bunny's largest side.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 20:56:14 -04:00
Alec Jacobson 72217e00ba add e2v output version (#2551) 2026-08-20 15:35:21 -04:00
Alec JacobsonandClaude Opus 5 c678e8658b Squared distance between two simplices (#2548)
Adds igl::simplex_simplex_squared_distance, which computes the squared
distance between the closest pair of points on two simplices along with
the barycentric coordinates of that pair. The simplices may have
different sizes (point, segment, triangle, tet, ...) and may be
degenerate; they only have to share a dimension.

Ported from gptoolbox's simplex_simplex_squared_distance.m. The
algorithm parameterizes both affine hulls, finds their closest pair by
minimum-norm least squares, and recurses over codimension-one facets
when that pair falls outside either simplex. The affine-hull distance is
a lower bound for the whole subproblem, so it doubles as a pruning test.

The implementation is templated on the corner counts so that statically
sized inputs (e.g. Matrix3d in, Vector3d out) unroll into fixed-size
linear algebra with no heap allocation. Faces are represented as
bitmasks and memoized, which is exact here because the running best only
decreases: a face pair that was pruned once stays pruned, and one that
was explored cannot improve on a second visit. A closest-corner seed and
a per-node bounding-box bound give the pruning test something to bite
on, and single-unknown nodes use the closed-form projection, which is
already the minimum-norm solution.

Relative to a straightforward dynamically sized recursion this is ~14x
faster for triangle-triangle queries on Matrix3d, ~74x for tet-tet, and
~5x even for MatrixXd, with zero allocations on the static path.

Tested against analytic point/segment/triangle/tet cases, degenerate
simplices, mixed and fixed-size scalar types, and an exhaustive
unpruned enumeration of every face-pair subproblem over random inputs.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 12:23:39 -04:00
Copilotandcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> 477e15a3d5 Update macOS CI runner and action versions (#2545)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-07-14 23:01:50 -04:00
Alec JacobsonandClaude Sonnet 4.6 cab03717c7 Replace corner-based pruning in lipschitz_octree_prune with center-based (#2541)
Previously lipschitz_octree_prune evaluated udf at every unique corner of
each candidate cell (via unique_sparse_voxel_corners) and pruned cells
where any corner had udf > h*sqrt(3).

This replaces that with a single evaluation at each cell center and prunes
if udf(center) > h*sqrt(3)/2 (the half space-diagonal — the maximum
distance from the center to any point in the cell).

The center-based method is provably more aggressive: if any corner has
udf > h*sqrt(3), then by 1-Lipschitz the center has
udf > h*sqrt(3)/2, so center-based prunes a strict superset of cells.
This means fewer false-positive cells survive each level of refinement.

Practically, this eliminates the unique_sparse_voxel_corners call
(hash-based deduplication), reduces the number of udf evaluations from
~4x cells to exactly 1x cells, and makes the pruning step a fully
parallel loop with no coordination.

All 305 existing tests pass.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 22:58:11 -04:00
Philip Trettner a5e8177ec7 Mesh generalized winding numbers via The Antipodal Method (SIGGRAPH 2026) (#2540)
* initial libigl-friendly implementation of the Antipodal Method for generalized winding numbers.

Only contains the triangle-mesh version and Embree intersector support

* removed sse intrinsics code from the intersector

* split into separate files as per style guide

* moved paper/project references to the entry header
2026-05-06 14:32:32 -04:00
Max MandelandAlec Jacobson 83807ad36f Fix MSH Tag Mapping-Related Vulnerability (#2537)
* Fix MSH tag vulnerability

* add test for non sequential MSH file read

---------

Co-authored-by: Alec Jacobson <alecjacobson@gmail.com>
2026-04-16 09:19:24 -04:00
Michael Wechner 989049ca71 add more missing <cassert> includes (#2533) 2026-04-07 13:41:17 -04:00
Alec Jacobson f95a8edf10 Adjust text shift scale factor based on label size (#2539) 2026-04-06 17:06:58 -04:00
Federico Sichetti aeeea9b416 Port to Eigen 5.0.1 (#2538)
* Bump version to 5.0.1 and fix compilation errors on Linux

* fix compile error on mac, missing header

* fix is_symmetric and add some tests

* fix failing GLFW test

* replaced manual scoop install with action for sccache
2026-04-04 14:43:44 -04:00
Alec JacobsonandClaude Sonnet 4.6 30fb450205 Fix typos in header documentation comments (#2534)
Fixes 23 typos across 7 headers:
- collpased → collapsed (collapse_edge, collapse_least_cost_edge, decimate_callback_types)
- triange → triangle (seam_edges)
- itnersections → intersections, aptch → patch, seperate → separate (trim_with_solid)
- seperate → separate, doubled "the the" (FastWindingNumberForSoups)
- doubled "of of" (circulation)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 14:17:26 -05:00
Alec Jacobson 6000ccb70f fix double for loop bug (#2530) 2026-02-11 13:16:35 -05:00
Alec Jacobson a739d79615 Alecjacobson/fix point in convex hull (#2528) [ci skip]
* failing test

* bug fix and templates
2026-02-03 10:30:09 -05:00
Alec Jacobson db07a47bec Winding number and distances to Bézier splines (#2527)
* roots, cubics, tests and tutorial

* Orientation to igl::, point_in_convex_hull, eyt_winding_number with func handle, fix bug in eyt_sdf, tutorial, tests

* working spline winding number and demo

* doc

* better cm
2026-01-26 22:46:39 -05:00
Nicolas Ulrich 7313592e16 Add missing #include <cassert> (#2521) 2026-01-16 16:49:44 -05:00
Alec Jacobson 2282ec8018 accelerated 2D winding number (and sdf in example) (#2522)
* accelerated 2D winding number (and sdf in example)

* line width [ci skip]

* empty
2026-01-16 16:49:21 -05:00
Alec Jacobson fdddfa2b6e 503_ARAPParam: take input path from arg if present (#2524) 2026-01-16 16:49:01 -05:00
Sven-Kristofer PilzandAlec Jacobson 95ec606b70 Reduce mallocs in principal_curvature. (#2497)
Co-authored-by: Alec Jacobson <alecjacobson@gmail.com>
2026-01-10 17:35:56 -05:00
Alec JacobsonandxTree cd73f3a4f3 add templates for planarize_quad_mesh (#2512)
* add templates for `planarize_quad_mesh`

* templates in subroutine

* xcode version in ci

---------

Co-authored-by: xTree <xliotx@gmail.com>
2025-11-12 15:12:16 -05:00
Rob McDonald ae8f959ea2 Force codesign to sign binaries, even if they are already signed (#2494)
For some reason, Apple binaries need to be signed, but sometimes are already signed.  This triggers an error in codesign unless the force (-f) flag is passed.
2025-08-01 12:16:58 -04:00
Alec Jacobson b0aa5f2a1c Orient2d vectorized (#2492) [skip ci]
* orient2d

* missing include

* missing include

* fix include
2025-07-28 13:49:05 -04:00
Alec Jacobson 09e1598e6d Minimal AABB tree + SDFs + Variable Radius Offsets (#2490)
* working eytzinger aabb

* working and reasonably efficient variable radius offset

* working example

* comments

* better key commands

* bad includes, defines

* missing sign function; test
2025-07-21 22:35:34 -04:00
Alec Jacobson 678e1fff76 fix bad templating breaking python build (#2489)
* fix bad templating breaking python build

* assumed int when python using Integer
2025-07-16 22:47:15 -04:00
Alec Jacobson 47b557df58 Lipschitz octree pruning (#2488) [skip ci]
* lipshitz_octree tutorial example

* better comments

* revert to shallow default

* better names and documentation, dont return I

* long long -> std::int64_t

* asdd tutorail chapter 10

* precious windows

* int64_t -> std::int64_t

* batch version

* fix template chain from overzealous index typing

* bump to c++17 (if constexpr)
2025-07-16 21:20:22 -04:00
Alec JacobsonandBruegelN 182e36df24 No longer using namespace std; and using namespace Eigen; to avoid issues with other libs (#2483)
* remove `using namespace std;` and fix resulting errors with missing namespaces

* remove `using namespace Eigen;` and fix resulting errors with missing namespaces

This should fix issues like https://github.com/libigl/libigl/issues/2480

* Add missing namespaces

fixup for 9ef213ba34
fixup for 3e88aa04fd

* missing Eigen::

* missing std::

* more missing std::

---------

Co-authored-by: BruegelN <bruegeln@crashing.systems>
2025-06-24 09:22:16 -04:00
Alec Jacobson bf9bdb9c70 pressing N,n changes lighting to pseudocolored normals (#2478) 2025-06-23 17:25:10 -04:00
Alec Jacobson 73a2e0de9b split functions into files; add vectorized orient3d (#2479) 2025-06-16 09:47:46 -04:00
Alec Jacobson 8866f214a0 crashing unless Epick is used 2025-05-23 14:16:46 -04:00
Alec Jacobson cf9ed7f492 Super Fibonacci and Oriented Bounding Boxes (#2472) [skip ci]
* add codesign for mac execs

* better ifdef guard

* simple brute force obb

* wrapper on cgals obb

* tutorial for OBB

* minimal test

* minimal test

* note

* use igl::PI

* expose quantity to optimize over
2025-05-23 10:57:22 -04:00
Peizhuo LiandAlec Jacobson 40e7900ccb Fix dqs not checking if quaternions are on the same hemisphere (#2390)
Co-authored-by: Alec Jacobson <alecjacobson@gmail.com>
2025-05-14 23:21:03 -04:00
NevsorandAlec Jacobson b443ac0261 Fix "Assertion failed" when calling igl::heat_geodesics_precompute using a V with fixed column count (#2419) [skip ci]
* Fix bug where `igl::heat_geodesics_precompute` will fail for `V` with a fixed number of columns.

See https://github.com/libigl/libigl/issues/2418

* Add missing call to .transpose()

---------

Co-authored-by: Alec Jacobson <alecjacobson@gmail.com>
2025-05-14 23:05:22 -04:00
Siqi WangandAlec Jacobson b286e13ac6 Update extract_non_manifold_edge_curves.cpp (#2423) [skip ci]
Co-authored-by: Alec Jacobson <alecjacobson@gmail.com>
2025-05-14 22:59:25 -04:00
Flo e91ffcb549 dijkstra: Fix inconsistent typing (#2469) [skip ci] 2025-05-14 22:37:48 -04:00
nicolas hsu d9524ade53 laplace equation tutorial - fixed bug with slice/lazy eval (#2447) [ci-skip] 2025-05-14 21:29:56 -04:00
Jérémie Dumas 89267b4a80 Use box-drawing characters in comments. (#2466) 2025-04-25 13:20:51 -07:00
Jérémie Dumas 7c3c05d637 Update ubuntu image (#2467) 2025-04-25 11:24:24 -07:00
Alec Jacobson 7888711039 bump deps (#2462) 2025-04-15 09:35:58 -04:00
Alec JacobsonandAlec Jacobson 08be0704c2 bump embree 4 and missing template (#2460)
* add missing template

* bump embree

---------

Co-authored-by: Alec Jacobson <ajx@mac.lan>
2025-04-07 17:23:22 -04:00
Alec Jacobson 0e360d5250 Revert templating on collapse_edge, separate overloads (#2455)
* Fix 2452

* fix cachev2 issue?

* and the windows ❄️

* removed __1::

* fix tutorial to use new func

* cmake bullshit
2025-03-31 17:00:36 -04:00
Alec Jacobson 5e561c28c8 Update isolines.h doc [ci skip] 2025-03-28 11:15:45 -04:00
Alec Jacobson 25d63024bb [ci skip] improve doc 2025-03-18 19:40:46 -04:00
Alec Jacobson f85a3c76db [ci skip] improve doc 2025-03-18 19:40:05 -04:00
Alec Jacobson 0c9c8cd643 Merge branch 'main' of github.com:libigl/libigl 2025-03-18 19:39:44 -04:00
Alec Jacobson a72b9386c8 [ci skip] improve doc 2025-03-18 19:39:38 -04:00
DJAntivenom ba69acc509 Fix division type error for highdpi calculation (#2386)
The way the highdpi value was calculated could lead to it being set to 0 or inf for certain types of window managers.
(Tiling window managers). This was caused by it trying to resize the window to a width and height of 0x0, or by having
the logical width/height of the window be smaller than the pyhsical one. This would cause the `highdpi` variable to be
set to 0, which would later cause a glfw call to be made with `inf` as an argument.
2025-02-18 08:36:41 -05:00
evouga a221faf1e4 Update voxel_grid.h (#2441)
Clarify the documentation
2025-01-07 18:50:48 -05:00
Alec Jacobson 69e2b7ee67 Fix Derived in heat_geodesics; boost url (#2440)
* PlainObject -> MatrixBase

* template igl::Hit

* vector input intersect rays with multiple hits

* initialize

* initialize

* only write if hit

* fix templates

* template me baby

* templates

* my god. so many PlainObject -> Matrix; Derived -> PlainMatrix<Derived>

* Options doesn't exist for Maps/Refs

* templates; windows size_t shinanigans

* Derived->PlainMatrix

* windows template

* derived -> plainvector

* options

* fix templating

* std types

* fix windingnumbertree tempalting

* further fix windingnumbertree tempalting

* Xi->XI

* templating

* clean up and template some of the decimation code; eventually gave up on templating outer functions

* rm needless cast

* attempt to fix index templating in boolean code

* remove debugging casts

* more templatin hell

* debug kruft

* vector resize

* assert

* int -> template

* int -> template

* int -> template

* templating away more Xi

* templating away more 3i

* formatting

* vector

* plainmatrix

* plainmatrix

* bug

* templating MSH io

* remove use of Map

* zero default tags

* bug fix

* file debug flags

* __1::

* better typing

* knn tempalte

* note

* templating

* reorder scaf inputs

* fix build

* doc

* templates

* messier than I thought

* doc

* Xi -> XI

* use templated type

* 1x1 is always symmetric

* use index type

* split intrinsic

* templating

* template internal

* restore boost link

* actually add the change to hg
2025-01-07 18:50:16 -05:00
Alec Jacobson 667101084a Add support for maintaining segments during refinement (#2424)
* segment control

* typos
2024-12-20 11:12:35 -05:00
Alec Jacobson 7472691fe6 PlainObject -> MatrixBase (#2425)
* PlainObject -> MatrixBase

* template igl::Hit

* vector input intersect rays with multiple hits

* initialize

* initialize

* only write if hit

* fix templates

* template me baby

* templates

* my god. so many PlainObject -> Matrix; Derived -> PlainMatrix<Derived>

* Options doesn't exist for Maps/Refs

* templates; windows size_t shinanigans

* Derived->PlainMatrix

* windows template

* derived -> plainvector

* options

* fix templating

* std types

* fix windingnumbertree tempalting

* further fix windingnumbertree tempalting

* Xi->XI

* templating

* clean up and template some of the decimation code; eventually gave up on templating outer functions

* rm needless cast

* attempt to fix index templating in boolean code

* remove debugging casts

* more templatin hell

* debug kruft

* vector resize

* assert

* int -> template

* int -> template

* int -> template

* templating away more Xi

* templating away more 3i

* formatting

* vector

* plainmatrix

* plainmatrix

* bug

* templating MSH io

* remove use of Map

* zero default tags

* bug fix

* file debug flags

* __1::

* better typing

* knn tempalte

* note

* templating

* reorder scaf inputs

* fix build

* doc

* templates

* messier than I thought

* doc

* Xi -> XI

* use templated type

* 1x1 is always symmetric

* use index type

* split intrinsic

* templating

* template internal
2024-12-20 11:12:11 -05:00
Alf-André Walla 20c3ee0740 Add missing include <cassert> in AABB.h (#2432) 2024-12-06 14:52:26 -05:00
Alec Jacobson 5067c8b7eb bump cgal, boost; rm gmp, mpfr (#2431)
* bump cgal; boost; mpf4; (mpfr+gmp may no longer be needed)

* actually use boost from cmake

* rm gmp mpfr 🎉

* try to tell cgal to use boost

* explicitly disable gmp

* rm gmp templates

* actually remove them
2024-11-26 22:30:10 -05:00
Alec Jacobson c2f96e8e18 improved docs 2024-11-07 08:28:37 -05:00
Alec Jacobson f962e4a6b6 Support Batched Marching Cubes (#2422)
* Working example; need to change name rather than overwrite 705

* separate tutorial for batch

* comment
2024-10-28 11:25:43 -04:00
Alec Jacobson 5d93f800ba fix warnings for Eigen's 'convenience' type all (#2421) 2024-10-28 11:25:29 -04:00
Martin Heistermann 8aca5bd0c4 Eigen build fix, Eigen::all has been renamed: (#2399)
Replace deprecated/removed Eigen::all with Eigen::placeholders:all.
2024-10-28 09:19:00 -04:00
Jérémie Dumas fac5d4a01d Change arg to const & to avoid MSan issue. (#2415) 2024-09-27 07:52:34 -04:00
Alec Jacobson 0e02103df7 add refine functionality for triangle wrapper (#2402) 2024-07-18 12:38:47 -04:00
Alec Jacobson dd9654a476 allow shared edge to be conflictingly oriented (#2395) 2024-06-13 09:08:10 -04:00
Alec Jacobson 01f2dc0a60 Add and fix test for fast_find_self_intersections (#2382)
* add test case

* add coplanarity test

* another failing test

* use orient3d

* no printing

* reverting... That introduced lots of other failure cases

* subdivided knight case

* wip predicates

* promising predicates version

* tri_tri_overlap compiles, header guards, predicate find_*

* tests for predicates::find_in...

* parallel for

* note

* mv to predicates

* remove old functions

* tutorials; extracting segments is broken 904

* fix extraction bug

* missing header

* capture consts
2024-05-03 12:01:17 -04:00
Alec Jacobson 8afe66e8fd fix static bug (#2380) 2024-05-02 22:34:41 -04:00
Alec Jacobson 6e32964a82 Fix CI Build: avoid test on windows, remove Comiso module, specific xcode on github (#2384)
* just avoid failing test on windows 😔

* mayfil instead

* mac os x xcode bug fix

* rm bonus endif

* rm comiso
2024-04-29 14:42:12 -04:00
Alec JacobsonandAlec Jacobson dafd52343b fix merge of non shared edges (#2374)
Co-authored-by: Alec Jacobson <alecjacobson@adobe.com>
2024-04-15 13:28:17 -04:00
David Coeurjolly 36930e5d19 Fixing shadowed variable declaration (that may lead to a compiler error if -Werror=shadow) (#2366) 2024-03-28 08:34:42 -04:00
Alec JacobsonandAlec Jacobson b4d8556a6b hybrid mass matrix for tets (#2364)
Co-authored-by: Alec Jacobson <alecjacobson@adobe.com>
2024-03-18 23:53:46 -04:00
Alec JacobsonandAlec Jacobson 81180a6e6a Boundary facets orientation (#2362)
* fix boundary_facets orientation + test

* rm print in tests

---------

Co-authored-by: Alec Jacobson <alecjacobson@adobe.com>
2024-03-17 11:10:07 -04:00
Alec JacobsonandAlec Jacobson a8819dcf9b fix bug (#2361)
Co-authored-by: Alec Jacobson <alecjacobson@adobe.com>
2024-03-14 17:45:34 -04:00
Alec JacobsonandAlec Jacobson 1886d18147 fix debug bug (#2360)
Co-authored-by: Alec Jacobson <alecjacobson@adobe.com>
2024-03-07 20:16:48 -05:00
Alec Jacobson 7e6bf3b81c missing break (#2354) 2024-02-20 11:23:23 -05:00
Alec Jacobson c7a84522c3 CGAL tests don't seem to run on Windows CI builds (#2351)
* show all tests

* fix cmake target
2024-02-19 23:18:34 -05:00
Alec Jacobson fe65ecb907 remove or hide cerr<< behind ifdef (#2349) [ci skip] 2024-02-09 11:13:07 -05:00
William8915 33a931d019 Fix compile error on gcc-12.3 (#2336)
This is a follow up fix of #2254. After #2254 gcc-12.3 reports the error "template-id not allowed for destructor".
2024-02-07 09:26:22 -05:00
Alec JacobsonandAlec Jacobson 94c6afde11 fix bug where cost of collapsed edge was attempted (#2347)
Co-authored-by: Alec Jacobson <alecjacobson@adobe.com>
2024-02-07 09:25:54 -05:00
Alec Jacobson 8185a213d0 centroid only worked for fixed size input (#2340)
* template for variable size and failing test

* fix

* static asserts
2024-01-23 20:14:49 -05:00
Alec JacobsonandAlec Jacobson 7d1614af1e Fix split_nonmanifold (#2344)
* failing test

* simply cut along all non-manifold edges

* fix compile

* before rewrite

* after rewrite

---------

Co-authored-by: Alec Jacobson <alecjacobson@adobe.com>
2024-01-23 09:25:44 -05:00
Bryn LloydandBryn Lloyd 293e79ff86 enable mixed polygons using convention that negative indices are ignored (#2338)
* enable mixed polygons using convention that negative indices are ignored

* add unit tests

---------

Co-authored-by: Bryn Lloyd <lloyd@itis.swiss>
2024-01-17 10:36:24 -05:00
Alec Jacobson 37f3b1d821 fix bug in cut_mesh; improve documentation; add test (#2315) 2024-01-10 09:40:11 -05:00
Taylor Holliday a8b3833942 fix compile error (#2328)
* Update eigs.cpp

Reduce terminal spew

* Revert "Update eigs.cpp"

This reverts commit fdbdb42934.

* Fix compile error and propagate errors
2024-01-10 09:39:41 -05:00
Alec JacobsonandAlec Jacobson 7f7f0fe007 use our boost-cmake which uses slow, non-jfrog url (#2330)
Co-authored-by: Alec Jacobson <alecjacobson@adobe.com>
2024-01-08 18:06:08 -05:00
Alec Jacobson 0b9030b1ed wrong header 2023-10-23 08:25:36 -04:00
Alec JacobsonandAlec Jacobson 112c1b8e48 Dynamic updates to AABB tree; intersection-blocking mesh decimation (#2301)
* working insertion and rotation b-b-b-but pointers aren't stuck to m_primitive

* insert now maintains primitive's pointers;+rotate is now getting heights in right ballpark

* sibling rotations working (and helping); dry run test working (and helping)

* working insertion, deletion (detach), and refit with padding.

* working; inexact

* working; inexact

* working; inexact

* note about poor assumptions

* working well after bug fixes. before refactor into functions

* simple self-intersection test function

* moved all functions to files

* docs

* blocking directly in qslim. better docs. aabb templates/tests;

* dont use size_t and fix namespace

* brute-force too fast on linux

* rm overloads in qslim/decimate; cgal template

* fix coplanar bug; factor out raytri.c

* fix and cgal debug

* refactor fast_find; fix bugs in fast_find; fix bugs in shared_vertex

* tutorials running again

* cleaned up aabb tutorials; templates

* format docs

* docs. arg names

* template name

* improve docs

* debugging test

* debugging test

* debugging test

* debugging test

* debugging test

* debugging test

* debugging test

* debugging test

* ebuggin test

* ebuggin test

* add epsilon to ray_triangle ifs

* erroneous includes

* rm leftover includes

* fix cmake bug

* uh actually fix cmake bug

* missing delete

* simple insert test

* don't pad all leaves F.rows() times

* fix pad bug

---------

Co-authored-by: Alec Jacobson <alecjacobson@adobe.com>
2023-10-14 07:32:50 -04:00
Alec Jacobson 7c9387c92b prefer std::u?int[0-9]+_t and include <cstdint> else include <stdint.h> (#2302) 2023-10-13 20:15:17 -04:00
Alec Jacobson 1c8c6d38ba Stay up to date with stable (#2300)
* bump version in cmake

* fix aassertions, add templates to compile in debug (#2299)
2023-10-06 19:47:53 -04:00
Alec JacobsonandAlec Jacobson 7991ee81d8 tata -> data (#2295) [ci skip]
Co-authored-by: Alec Jacobson <alecjacobson@adobe.com>
2023-09-28 13:12:57 -04:00
774 changed files with 21770 additions and 11725 deletions
+20 -25
View File
@@ -25,14 +25,14 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, macos-latest]
os: [ubuntu-24.04, macos-15]
config: [Release]
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:
- name: Checkout repository
uses: actions/checkout@v1
uses: actions/checkout@v4
with:
fetch-depth: 10
@@ -52,9 +52,15 @@ jobs:
run: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install ccache
- name: Setup Xcode version
if: runner.os == 'macOS'
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
- name: Cache Build
id: cache-build
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ~/.ccache
key: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.build-params.static }}-cache
@@ -68,7 +74,9 @@ jobs:
run: |
mkdir -p build
cd build
# https://github.com/eclipse-ecal/ecal/issues/2041
cmake .. \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_BUILD_TYPE=${{ matrix.config }} \
-DLIBIGL_USE_STATIC_LIBRARY=${{ matrix.build-params.static }} \
@@ -81,7 +89,7 @@ jobs:
run: cd build; make -j2; ccache --show-stats
- name: Tests
run: cd build; ctest --verbose
run: cd build; ctest --show-only; ctest --verbose
####################
# Windows
@@ -106,40 +114,27 @@ jobs:
{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},
{static: OFF, tutorials: ON, tests: OFF, selected_tutorial: 10},
]
steps:
- name: Checkout repository
uses: actions/checkout@v1
uses: actions/checkout@v4
with:
fetch-depth: 10
- name: Install Ninja
uses: seanmiddleditch/gha-setup-ninja@master
- name: Set env variable for sccache
run: |
echo "appdata=$env:LOCALAPPDATA" >> ${env:GITHUB_ENV}
- name: Cache build
id: cache-build
uses: actions/cache@v2
with:
path: ${{ env.appdata }}\Mozilla\sccache
key: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.build-params.static }}-cache
- name: Prepare sccache
run: |
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}
- name: Install sccache
uses: mozilla-actions/sccache-action@v0.0.9
- name: Configure and build
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" -arch=x64
# https://github.com/eclipse-ecal/ecal/issues/2041
cmake -G Ninja ^
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 ^
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache ^
-DCMAKE_BUILD_TYPE=${{ matrix.config }} ^
-DLIBIGL_USE_STATIC_LIBRARY=${{ matrix.build-params.static }} ^
@@ -156,10 +151,10 @@ jobs:
-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' }} ^
-DLIBIGL_TUTORIALS_CHAPTER10=${{ (matrix.build-params.selected_tutorial == 'NONE' || matrix.build-params.selected_tutorial == '10') && 'ON' || 'OFF' }} ^
-B build ^
-S .
cmake --build build -j2
- name: Tests
run: cd build; ctest --verbose -j2
run: cd build; ctest --show-only; ctest --verbose -j2
+1
View File
@@ -51,3 +51,4 @@ LibiglOptions.cmake
dox/
latex/
scripts/
CLAUDE.md
+2 -1
View File
@@ -37,7 +37,7 @@ if(HUNTER_ENABLED)
endif()
################################################################################
project(libigl VERSION 2.4.0)
project(libigl VERSION 2.5.0)
# CMake module path
list(PREPEND CMAKE_MODULE_PATH
@@ -99,6 +99,7 @@ option(LIBIGL_USE_STATIC_LIBRARY "Use libigl as static library" ${LIBIGL_TOPLEVE
# Permissive modules. These modules are available under MPL2 license, and their dependencies are available
# under a permissive or public domain license.
option(LIBIGL_CYCODEBASE "Build target igl::cycodebase" ${LIBIGL_TOPLEVEL_PROJECT})
option(LIBIGL_EMBREE "Build target igl::embree" ${LIBIGL_TOPLEVEL_PROJECT})
option(LIBIGL_GLFW "Build target igl::glfw" ${LIBIGL_TOPLEVEL_PROJECT})
option(LIBIGL_IMGUI "Build target igl::imgui" ${LIBIGL_TOPLEVEL_PROJECT})
-95
View File
@@ -1,95 +0,0 @@
# Try to find the GNU Multiple Precision Arithmetic Library (GMP)
# See http://gmplib.org/
if(${CMAKE_VERSION} VERSION_LESS "3.18.0")
set(REQUIRED_FLAG "")
else()
set(REQUIRED_FLAG REQUIRED)
endif()
# On Windows, we must use the pre-compiled versions downloaded with libigl
if(WIN32)
set(NO_DEFAULT_FLAG NO_DEFAULT_PATH)
else()
set(NO_DEFAULT_FLAG "")
endif()
find_path(GMP_INCLUDES
NAMES
gmp.h
PATHS
ENV GMP_DIR
${INCLUDE_INSTALL_DIR}
PATH_SUFFIXES
include
${REQUIRED_FLAG}
${NO_DEFAULT_FLAG}
)
find_library(GMP_LIBRARIES
NAMES
gmp
libgmp-10
PATHS
ENV GMP_DIR
${LIB_INSTALL_DIR}
PATH_SUFFIXES
lib
${REQUIRED_FLAG}
${NO_DEFAULT_FLAG}
)
set(GMP_EXTRA_VARS "")
if(WIN32)
# Find dll file and set IMPORTED_LOCATION to the .dll file
find_file(GMP_RUNTIME_LIB
NAMES
gmp.dll
libgmp-10.dll
PATHS
ENV GMP_DIR
${LIB_INSTALL_DIR}
PATH_SUFFIXES
lib
${REQUIRED_FLAG}
${NO_DEFAULT_FLAG}
)
list(APPEND GMP_EXTRA_VARS GMP_RUNTIME_LIB)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GMP
REQUIRED_VARS
GMP_INCLUDES
GMP_LIBRARIES
${GMP_EXTRA_VARS}
REASON_FAILURE_MESSAGE
"GMP is not installed on your system. Either install GMP using your preferred package manager, or disable libigl modules that depend on GMP, such as CGAL. See LibiglOptions.cmake.sample for configuration options. Do not forget to delete your <build>/CMakeCache.txt for the changes to take effect."
)
mark_as_advanced(GMP_INCLUDES GMP_LIBRARIES)
if(GMP_INCLUDES AND GMP_LIBRARIES AND NOT TARGET gmp::gmp)
if(GMP_RUNTIME_LIB)
add_library(gmp::gmp SHARED IMPORTED)
else()
add_library(gmp::gmp UNKNOWN IMPORTED)
endif()
# Set public header location and link language
set_target_properties(gmp::gmp PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
INTERFACE_INCLUDE_DIRECTORIES "${GMP_INCLUDES}"
)
# Set lib location. On Windows we specify both the .lib and the .dll paths
if(GMP_RUNTIME_LIB)
set_target_properties(gmp::gmp PROPERTIES
IMPORTED_IMPLIB "${GMP_LIBRARIES}"
IMPORTED_LOCATION "${GMP_RUNTIME_LIB}"
)
else()
set_target_properties(gmp::gmp PROPERTIES
IMPORTED_LOCATION "${GMP_LIBRARIES}"
)
endif()
endif()
-95
View File
@@ -1,95 +0,0 @@
# Try to find the MPFR library
# See http://www.mpfr.org/
if(${CMAKE_VERSION} VERSION_LESS "3.18.0")
set(REQUIRED_FLAG "")
else()
set(REQUIRED_FLAG REQUIRED)
endif()
# On Windows, we must use the pre-compiled versions downloaded with libigl
if(WIN32)
set(NO_DEFAULT_FLAG NO_DEFAULT_PATH)
else()
set(NO_DEFAULT_FLAG "")
endif()
find_path(MPFR_INCLUDES
NAMES
mpfr.h
PATHS
ENV MPFR_DIR
${INCLUDE_INSTALL_DIR}
PATH_SUFFIXES
include
${REQUIRED_FLAG}
${NO_DEFAULT_FLAG}
)
find_library(MPFR_LIBRARIES
NAMES
mpfr
libmpfr-4
PATHS
ENV MPFR_DIR
${LIB_INSTALL_DIR}
PATH_SUFFIXES
lib
${REQUIRED_FLAG}
${NO_DEFAULT_FLAG}
)
set(MPFR_EXTRA_VARS "")
if(WIN32)
# Find dll file and set IMPORTED_LOCATION to the .dll file
find_file(MPFR_RUNTIME_LIB
NAMES
mpfr.dll
libmpfr-4.dll
PATHS
ENV MPFR_DIR
${LIB_INSTALL_DIR}
PATH_SUFFIXES
lib
${REQUIRED_FLAG}
${NO_DEFAULT_FLAG}
)
list(APPEND MPFR_EXTRA_VARS MPFR_RUNTIME_LIB)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MPFR
REQUIRED_VARS
MPFR_INCLUDES
MPFR_LIBRARIES
${MPFR_EXTRA_VARS}
REASON_FAILURE_MESSAGE
"MPFR is not installed on your system. Either install MPFR using your preferred package manager, or disable libigl modules that depend on MPFR, such as CGAL. See LibiglOptions.cmake.sample for configuration options. Do not forget to delete your <build>/CMakeCache.txt for the changes to take effect."
)
mark_as_advanced(MPFR_INCLUDES MPFR_LIBRARIES)
if(MPFR_INCLUDES AND MPFR_LIBRARIES AND NOT TARGET mpfr::mpfr)
if(MPFR_RUNTIME_LIB)
add_library(mpfr::mpfr SHARED IMPORTED)
else()
add_library(mpfr::mpfr UNKNOWN IMPORTED)
endif()
# Set public header location and link language
set_target_properties(mpfr::mpfr PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
INTERFACE_INCLUDE_DIRECTORIES "${MPFR_INCLUDES}"
)
# Set lib location. On Windows we specify both the .lib and the .dll paths
if(MPFR_RUNTIME_LIB)
set_target_properties(mpfr::mpfr PROPERTIES
IMPORTED_IMPLIB "${MPFR_LIBRARIES}"
IMPORTED_LOCATION "${MPFR_RUNTIME_LIB}"
)
else()
set_target_properties(mpfr::mpfr PROPERTIES
IMPORTED_LOCATION "${MPFR_LIBRARIES}"
)
endif()
endif()
+2 -2
View File
@@ -33,8 +33,8 @@ function(igl_add_library module_name)
target_compile_definitions(${module_name} ${IGL_SCOPE} -DIGL_STATIC_LIBRARY)
endif()
# C++11 features
target_compile_features(${module_name} ${IGL_SCOPE} cxx_std_11)
# C++17 features
target_compile_features(${module_name} ${IGL_SCOPE} cxx_std_17)
if(LIBIGL_WARNINGS_AS_ERRORS)
target_compile_options(${module_name} PRIVATE -Wall -Wextra -Wpedantic -Wno-sign-compare -Werror -Wno-gnu -Wno-unknown-pragmas)
+9
View File
@@ -17,4 +17,13 @@ function(igl_add_tutorial name)
)
set_target_properties(${name} PROPERTIES FOLDER Libigl_Tutorials)
# Do this codesign only on macOS
# add_custom_command(TARGET your_target POST_BUILD COMMAND codesign -s - $<TARGET_FILE:your_target>
if(APPLE)
add_custom_command(TARGET ${name} POST_BUILD
COMMAND codesign -f -s - $<TARGET_FILE:${name}>
COMMENT "Codesigning ${name}"
)
endif()
endfunction()
+4
View File
@@ -50,6 +50,10 @@ function(igl_copy_dll target)
if(NOT WIN32)
return()
endif()
if(NOT TARGET ${target})
message(STATUS "igl_copy_dll() was called with a non-target: ${target}")
return()
endif()
# Sanity checks
get_target_property(TYPE ${target} TYPE)
-27
View File
@@ -1,27 +0,0 @@
# 1. Define module
igl_add_library(igl_copyleft_comiso)
# 2. Include headers
include(GNUInstallDirs)
target_include_directories(igl_copyleft_comiso ${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/copyleft/comiso/*.h")
file(GLOB SRC_FILES "${libigl_SOURCE_DIR}/include/igl/copyleft/comiso/*.cpp")
igl_target_sources(igl_copyleft_comiso ${INC_FILES} ${SRC_FILES})
# 4. Dependencies
include(comiso)
igl_include(copyleft core)
target_link_libraries(igl_copyleft_comiso ${IGL_SCOPE}
igl::core
igl_copyleft::core
CoMISo::CoMISo
)
# 5. Unit tests
file(GLOB SRC_FILES "${libigl_SOURCE_DIR}/tests/include/igl/copyleft/comiso/*.cpp")
igl_add_test(igl_copyleft_comiso ${SRC_FILES})
+26
View File
@@ -0,0 +1,26 @@
# 1. Define module
igl_add_library(igl_cycodebase)
# 2. Include headers
include(GNUInstallDirs)
target_include_directories(igl_cycodebase ${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/cycodebase/*.h")
file(GLOB SRC_FILES "${libigl_SOURCE_DIR}/include/igl/cycodebase/*.cpp")
igl_target_sources(igl_cycodebase ${INC_FILES} ${SRC_FILES})
# 4. Dependencies
include(cycodebase)
target_link_libraries(igl_cycodebase ${IGL_SCOPE}
igl::core
cyCodeBase::cyCodeBase
)
# 5. Unit tests
file(GLOB SRC_FILES "${libigl_SOURCE_DIR}/tests/include/igl/cycodebase/*.cpp")
igl_add_test(igl_cycodebase ${SRC_FILES})
+1 -1
View File
@@ -13,6 +13,7 @@ include(igl_windows)
# Libigl permissive modules
igl_include(core)
igl_include_optional(cycodebase)
igl_include_optional(embree)
igl_include_optional(opengl)
igl_include_optional(glfw)
@@ -25,7 +26,6 @@ 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
+22 -26
View File
@@ -4,32 +4,27 @@ endif()
message(STATUS "Third-party: creating targets 'Boost::boost'...")
cmake_minimum_required(VERSION 3.24) # Ensure modern FetchContent features
project(BoostFetchExample)
include(FetchContent)
# Define the Boost library to fetch
FetchContent_Declare(
boost-cmake
GIT_REPOSITORY https://github.com/Orphis/boost-cmake.git
GIT_TAG 7f97a08b64bd5d2e53e932ddf80c40544cf45edf
Boost
URL https://archives.boost.io/release/1.86.0/source/boost_1_86_0.tar.gz
URL_HASH MD5=ac857d73bb754b718a039830b07b9624
)
# Fetch Boost
FetchContent_MakeAvailable(Boost)
set(PREVIOUS_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
set(OLD_CMAKE_POSITION_INDEPENDENT_CODE ${CMAKE_POSITION_INDEPENDENT_CODE})
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# This guy will download boost using FetchContent
FetchContent_GetProperties(boost-cmake)
if(NOT boost-cmake_POPULATED)
FetchContent_Populate(boost-cmake)
# File lcid.cpp from Boost_locale.cpp doesn't compile on MSVC, so we exclude them from the default
# targets being built by the project (only targets explicitly used by other targets will be built).
add_subdirectory(${boost-cmake_SOURCE_DIR} ${boost-cmake_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
# Ensure Boost paths are set before CGAL
set(Boost_INCLUDE_DIR ${boost_SOURCE_DIR})
set(Boost_LIBRARY_DIR ${boost_BINARY_DIR})
set(CMAKE_POSITION_INDEPENDENT_CODE ${OLD_CMAKE_POSITION_INDEPENDENT_CODE})
set(CMAKE_CXX_FLAGS "${PREVIOUS_CMAKE_CXX_FLAGS}")
# Set VS target folders
set(boost_modules
# Add Boost libraries needed for your project
set(BOOST_LIBRARIES
container
regex
atomic
@@ -48,6 +43,7 @@ set(boost_modules
log_setup
unit_test_framework
math
multiprecision
program_options
timer
random
@@ -55,10 +51,10 @@ set(boost_modules
system
thread
type_erasure
)
foreach(module IN ITEMS ${boost_modules})
if(TARGET Boost_${module})
set_target_properties(Boost_${module} PROPERTIES FOLDER ThirdParty/Boost)
endif()
endforeach()
)
foreach(lib IN LISTS BOOST_LIBRARIES)
add_library(boost_${lib} INTERFACE)
target_include_directories(boost_${lib} INTERFACE ${Boost_SOURCE_DIR})
target_link_libraries(boost_${lib} INTERFACE Boost::${lib})
endforeach()
+6 -14
View File
@@ -7,8 +7,8 @@ message(STATUS "Third-party: creating target 'CGAL::CGAL'")
include(FetchContent)
FetchContent_Declare(
cgal
URL https://github.com/CGAL/cgal/releases/download/v5.6/CGAL-5.6-library.tar.xz
URL_MD5 793da2d1597f3a5c0e3524f73a0b4039
URL https://github.com/CGAL/cgal/releases/download/v6.0.1/CGAL-6.0.1-library.tar.xz
URL_MD5 ea827f6778063e00554ae41f4c845492
)
FetchContent_GetProperties(cgal)
if(cgal_POPULATED)
@@ -29,29 +29,21 @@ function(cgal_import_target)
set(${NAME}_ROOT ${CMAKE_CURRENT_BINARY_DIR}/${NAME} CACHE PATH "")
endmacro()
include(gmp)
include(mpfr)
include(boost)
ignore_package(GMP 5.0.1)
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 "")
set(MPFR_LIBRARIES mpfr::mpfr)
ignore_package(Boost 1.71.0)
set(Boost_INCLUDE_DIRS "")
set(Boost_LIBRARIES Boost::thread Boost::system)
set(Boost_LIBRARIES Boost::thread Boost::system Boost::multiprecision)
# 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")
set(CGAL_CMAKE_EXACT_NT_BACKEND "BOOST_BACKEND" CACHE STRING "CGAL exact NT backend")
set(CGAL_DISABLE_GMP ON CACHE BOOL "Disable GMP")
find_package(CGAL CONFIG COMPONENTS Core PATHS ${cgal_SOURCE_DIR} NO_DEFAULT_PATH)
endfunction()
-31
View File
@@ -1,31 +0,0 @@
if(TARGET CoMISo::CoMISo)
return()
endif()
message(STATUS "Third-party: creating target 'CoMISo::CoMISo'")
include(FetchContent)
FetchContent_Declare(
comiso
GIT_REPOSITORY https://github.com/libigl/CoMISo.git
GIT_TAG 536440e714f412e7ef6c0b96b90ba37b1531bb39
)
include(eigen)
FetchContent_MakeAvailable(comiso)
add_library(CoMISo::CoMISo ALIAS CoMISo)
# Copy .hh headers into a subfolder `CoMISo/`
file(GLOB_RECURSE INC_FILES "${comiso_SOURCE_DIR}/*.hh" "${comiso_SOURCE_DIR}/*.cc")
set(output_folder "${CMAKE_CURRENT_BINARY_DIR}/CoMISo/include/CoMISo")
message(VERBOSE "Copying CoMISo headers to '${output_folder}'")
foreach(filepath IN ITEMS ${INC_FILES})
file(RELATIVE_PATH filename "${comiso_SOURCE_DIR}" ${filepath})
configure_file(${filepath} "${output_folder}/${filename}" COPYONLY)
endforeach()
target_include_directories(CoMISo PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/CoMISo/include)
set_target_properties(CoMISo PROPERTIES FOLDER ThirdParty)
+18
View File
@@ -0,0 +1,18 @@
if(TARGET cycodebase::cycodebase)
return()
endif()
FetchContent_Declare(
cyCodeBase
GIT_REPOSITORY https://github.com/cemyuksel/cyCodeBase/
GIT_TAG e36f3cffca65eb12a8a071f0443128b7de6ed75d
)
FetchContent_Populate(cyCodeBase)
add_library(cyCodeBase_interface INTERFACE)
target_include_directories(cyCodeBase_interface INTERFACE ${cycodebase_SOURCE_DIR})
if(NOT (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64|i[3-6]86"))
target_compile_definitions(cyCodeBase_interface INTERFACE CY_NO_INTRIN_H)
endif()
add_library(cyCodeBase::cyCodeBase ALIAS cyCodeBase_interface)
+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.4.0
GIT_TAG tags/5.0.1
GIT_SHALLOW TRUE
)
FetchContent_GetProperties(eigen)
+1 -1
View File
@@ -8,7 +8,7 @@ include(FetchContent)
FetchContent_Declare(
embree
GIT_REPOSITORY https://github.com/embree/embree.git
GIT_TAG v3.13.3
GIT_TAG v4.4.0
GIT_SHALLOW TRUE
)
+1 -1
View File
@@ -8,7 +8,7 @@ include(FetchContent)
FetchContent_Declare(
glad
GIT_REPOSITORY https://github.com/libigl/libigl-glad.git
GIT_TAG ead2d21fd1d9f566d8f9a9ce99ddf85829258c7a
GIT_TAG 651a425101365aa6e8504988ef9bb363d066c5ee
)
FetchContent_MakeAvailable(glad)
-96
View File
@@ -1,96 +0,0 @@
if(TARGET gmp::gmp)
return()
endif()
# Download precompiled .dll on Windows
if(WIN32)
include(gmp_mpfr)
# Find_package will look for our downloaded lib on Windows, and system-wide on Linux/macOS
find_package(GMP REQUIRED)
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)
include(ExternalProject)
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}
${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://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://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
INSTALL_DIR ${gmp_INSTALL}
TEST_COMMAND ""
BUILD_BYPRODUCTS ${gmp_LIBRARY}
)
ExternalProject_Get_Property(gmp SOURCE_DIR)
set(gmp_LIBRARIES ${gmp_LIBRARY})
add_library(gmp::gmp INTERFACE IMPORTED GLOBAL)
file(MAKE_DIRECTORY ${gmp_INCLUDE_DIR}) # avoid race condition
target_include_directories(gmp::gmp INTERFACE ${gmp_INCLUDE_DIR})
target_link_libraries(gmp::gmp INTERFACE "${gmp_LIBRARIES}") # need the quotes to expand list
add_dependencies(gmp::gmp gmp)
endif()
if(NOT TARGET gmp::gmp)
message(FATAL_ERROR "Creation of target 'gmp::gmp' failed")
endif()
-34
View File
@@ -1,34 +0,0 @@
if(WIN32)
message(STATUS "Third-party: downloading gmp + mpfr")
include(FetchContent)
# CGAL 5+ ships with a single .zip combining GMP + MPFR's precompiled dlls.
# For now we still download them separately.
FetchContent_Declare(
gmp
URL https://cgal.geometryfactory.com/CGAL/precompiled_libs/auxiliary/x64/GMP/5.0.1/gmp-all-CGAL-3.9.zip
URL_MD5 508c1292319c832609329116a8234c9f
)
FetchContent_MakeAvailable(gmp)
FetchContent_Declare(
mpfr
URL https://cgal.geometryfactory.com/CGAL/precompiled_libs/auxiliary/x64/MPFR/3.0.0/mpfr-all-CGAL-3.9.zip
URL_MD5 48840454eef0ff18730050c05028734b
)
FetchContent_MakeAvailable(mpfr)
# FetchContent_Declare(
# gmp_mpfr
# URL https://github.com/CGAL/cgal/releases/download/v5.2.1/CGAL-5.2.1-win64-auxiliary-libraries-gmp-mpfr.zip
# URL_MD5 247f4dca741c6b9a9be76286414070fa
# )
# For CGAL
set(ENV{GMP_DIR} "${gmp_SOURCE_DIR}")
set(ENV{MPFR_DIR} "${mpfr_SOURCE_DIR}")
else()
# On Linux/macOS, gmp+mpfr will be fetched and compiled
endif()
+3 -3
View File
@@ -6,13 +6,13 @@ message(STATUS "Third-party: creating target 'igl::tests_data'")
include(FetchContent)
FetchContent_Declare(
libigl_tests_tata
libigl_tests_data
GIT_REPOSITORY https://github.com/libigl/libigl-tests-data
GIT_TAG 19cedf96d70702d8b3a83eb27934780c542356fe
)
FetchContent_MakeAvailable(libigl_tests_tata)
FetchContent_MakeAvailable(libigl_tests_data)
add_library(igl_tests_data INTERFACE)
add_library(igl::tests_data ALIAS igl_tests_data)
target_compile_definitions(igl_tests_data INTERFACE LIBIGL_DATA_DIR=\"${libigl_tests_tata_SOURCE_DIR}\")
target_compile_definitions(igl_tests_data INTERFACE LIBIGL_DATA_DIR=\"${libigl_tests_data_SOURCE_DIR}\")
+4 -4
View File
@@ -6,13 +6,13 @@ message(STATUS "Third-party: creating target 'igl::tutorial_data'")
include(FetchContent)
FetchContent_Declare(
libigl_tutorial_tata
libigl_tutorial_data
GIT_REPOSITORY https://github.com/libigl/libigl-tutorial-data
GIT_TAG c1f9ede366d02e3531ecbaec5e3769312f31cccd
GIT_TAG 644dd4104843b6d736745d9dafbd70bf8d175648
)
FetchContent_MakeAvailable(libigl_tutorial_tata)
FetchContent_MakeAvailable(libigl_tutorial_data)
add_library(igl_tutorial_data INTERFACE)
add_library(igl::tutorial_data ALIAS igl_tutorial_data)
target_compile_definitions(igl_tutorial_data INTERFACE "-DTUTORIAL_SHARED_PATH=\"${libigl_tutorial_tata_SOURCE_DIR}\"")
target_compile_definitions(igl_tutorial_data INTERFACE "-DTUTORIAL_SHARED_PATH=\"${libigl_tutorial_data_SOURCE_DIR}\"")
-91
View File
@@ -1,91 +0,0 @@
# Expects
# gmp_INCLUDE_DIR
# gmp_LIB_DIR
# gmp_LIBRARIES
if(TARGET mpfr::mpfr)
return()
endif()
# Download precompiled .dll on Windows
if(WIN32)
include(gmp_mpfr)
# Find_package will look for our downloaded lib on Windows, and system-wide on Linux/macOS
find_package(MPFR REQUIRED)
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)
include(ExternalProject)
set(prefix ${FETCHCONTENT_BASE_DIR}/mpfr)
set(mpfr_INSTALL ${prefix}/install)
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.2.0.tar.xz
URL_MD5 a25091f337f25830c16d2054d74b5af7
UPDATE_DISCONNECTED true # need this to avoid constant rebuild
${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
INSTALL_DIR ${mpfr_INSTALL}
TEST_COMMAND ""
BUILD_BYPRODUCTS ${mpfr_LIBRARY}
)
#PATCH_COMMAND curl "https://raw.githubusercontent.com/Homebrew/formula-patches/03cf8088210822aa2c1ab544ed58ea04c897d9c4/libtool/configure-big_sur.diff" "|" sed -e "s/configure.orig/configure/g" "|" git apply -v
ExternalProject_Get_Property(mpfr SOURCE_DIR)
set(mpfr_LIBRARIES ${mpfr_LIBRARY})
add_library(mpfr::mpfr INTERFACE IMPORTED GLOBAL)
file(MAKE_DIRECTORY ${mpfr_INCLUDE_DIR}) # avoid race condition
target_include_directories(mpfr::mpfr INTERFACE ${mpfr_INCLUDE_DIR})
target_link_libraries(mpfr::mpfr INTERFACE "${mpfr_LIBRARIES}") # need the quotes to expand list
# This is necessary to ensure that mpfr appears before gmp in link order.
# Otherwise undefined reference errors occur at link time on Linux with gcc
target_link_libraries(mpfr::mpfr INTERFACE "${gmp_LIBRARIES}")
add_dependencies(mpfr::mpfr mpfr)
endif()
if(NOT TARGET mpfr::mpfr)
message(FATAL_ERROR "Creation of target 'mpfr::mpfr' failed")
endif()
+1 -1
View File
@@ -8,7 +8,7 @@ include(FetchContent)
FetchContent_Declare(
predicates
GIT_REPOSITORY https://github.com/libigl/libigl-predicates.git
GIT_TAG 488242fa2b1f98a9c5bd1441297fb4a99a6a9ae4
GIT_TAG decb7bc1260e689cbe008109e3cc5d3a5a433aea
)
FetchContent_MakeAvailable(predicates)
+1 -1
View File
@@ -8,7 +8,7 @@ include(FetchContent)
FetchContent_Declare(
tetgen
GIT_REPOSITORY https://github.com/libigl/tetgen.git
GIT_TAG 4f3bfba3997f20aa1f96cfaff604313a8c2c85b6
GIT_TAG e05aca7df74e3f531bc35733ed87d36d437266c5
)
FetchContent_MakeAvailable(tetgen)
+1 -1
View File
@@ -8,7 +8,7 @@ include(FetchContent)
FetchContent_Declare(
triangle
GIT_REPOSITORY https://github.com/libigl/triangle.git
GIT_TAG 3ee6cac2230f0fe1413879574f741c7b6da11221
GIT_TAG 62f02db9ab4ff4b62d5ff82a77c8ea458c84c23a
)
FetchContent_MakeAvailable(triangle)
+845 -208
View File
File diff suppressed because it is too large Load Diff
+483 -121
View File
@@ -1,15 +1,16 @@
// 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
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#ifndef IGL_AABB_H
#define IGL_AABB_H
#include "Hit.h"
#include "igl_inline.h"
#include <cassert>
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <vector>
@@ -26,9 +27,12 @@ namespace igl
/// @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
class AABB
{
public:
///////////////////////////////////////////////////////////////////////////////
// Member variables
///////////////////////////////////////////////////////////////////////////////
/// Scalar type of vertex positions (e.g., `double`)
typedef typename DerivedV::Scalar Scalar;
/// Fixed-size (`DIM`) RowVector type using `Scalar`
@@ -39,29 +43,35 @@ public:
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;
/// Pointer to "parent" node (`nullptr` if root)
AABB * m_parent;
/// Axis-Aligned Bounding Box containing this node
Eigen::AlignedBox<Scalar,DIM> m_box;
/// Index of single primitive in this node if full leaf, otherwise -1 for non-leaf
int m_primitive;
///////////////////////////////////////////////////////////////////////////////
// Non-templated member functions
///////////////////////////////////////////////////////////////////////////////
//Scalar m_low_sqr_d;
//int m_depth;
/// @private
AABB():
m_left(NULL), m_right(NULL),
m_left(nullptr), m_right(nullptr),m_parent(nullptr),
m_box(), m_primitive(-1)
//m_low_sqr_d(std::numeric_limits<double>::infinity()),
//m_depth(0)
{
static_assert(DerivedV::ColsAtCompileTime == DIM || DerivedV::ColsAtCompileTime == Eigen::Dynamic,"DerivedV::ColsAtCompileTime == DIM || DerivedV::ColsAtCompileTime == Eigen::Dynamic");
}
{
static_assert(DerivedV::ColsAtCompileTime == DIM || DerivedV::ColsAtCompileTime == Eigen::Dynamic,"DerivedV::ColsAtCompileTime == DIM || DerivedV::ColsAtCompileTime == Eigen::Dynamic");
}
/// @private
// http://stackoverflow.com/a/3279550/148668
AABB(const AABB& other):
m_left(other.m_left ? new AABB(*other.m_left) : NULL),
m_right(other.m_right ? new AABB(*other.m_right) : NULL),
m_left (other.m_left ? new AABB(*other.m_left) : nullptr),
m_right(other.m_right ? new AABB(*other.m_right) : nullptr),
m_parent(other.m_parent),
m_box(other.m_box),
m_primitive(other.m_primitive)
//m_low_sqr_d(other.m_low_sqr_d),
@@ -69,6 +79,8 @@ public:
// m_left ? m_left->m_depth + 1 : 0,
// m_right ? m_right->m_depth + 1 : 0))
{
if(m_left) { m_left->m_parent = this; }
if(m_right) { m_right->m_parent = this; }
}
/// @private
// copy-swap idiom
@@ -78,6 +90,7 @@ public:
using std::swap;
swap(first.m_left,second.m_left);
swap(first.m_right,second.m_right);
swap(first.m_parent,second.m_parent);
swap(first.m_box,second.m_box);
swap(first.m_primitive,second.m_primitive);
//swap(first.m_low_sqr_d,second.m_low_sqr_d);
@@ -93,44 +106,381 @@ public:
/// @private
AABB(AABB&& other):
// initialize via default constructor
AABB()
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()
IGL_INLINE void clear()
{
m_primitive = -1;
m_box = Eigen::AlignedBox<Scalar,DIM>();
delete m_left;
m_left = NULL;
m_left = nullptr;
delete m_right;
m_right = NULL;
m_right = nullptr;
// Tell my parent I'm dead
if(m_parent)
{
if(m_parent->m_left == this)
{
m_parent->m_left = nullptr;
}else if(m_parent->m_right == this)
{
m_parent->m_right = nullptr;
}else
{
assert(false && "I'm not my parent's child");
}
auto * grandparent = m_parent->m_parent;
if(grandparent)
{
// Before
// grandparent
//
// parent pibling
//
// sibling this
//
// After
// grandparent
//
// sibling® pibling
}else
{
// Before
// parent=root
//
// sibling this
//
// After
// grandparent
//
// sibling® pibling
}
}
// Now my parent is dead to me.
m_parent = nullptr;
}
/// @private
~AABB()
{
deinit();
clear();
}
/// Return whether at leaf node
IGL_INLINE bool is_leaf() const;
/// Return whether at root node
IGL_INLINE bool is_root() const;
/// Return the root node of this node's tree by following its parent
IGL_INLINE AABB<DerivedV,DIM>* root() const;
IGL_INLINE AABB<DerivedV,DIM>* detach();
IGL_INLINE void refit_lineage();
/// Get a vector of leaves indexed by their m_primitive id (these better
/// be non-negative and tightly packed.
/// @param[in] m number of leaves/elements (Ele.rows())
/// @returns leaves m list of pointers to leaves
IGL_INLINE std::vector<AABB<DerivedV,DIM>*> gather_leaves(const int m);
/// \overload where m is the max m_primitive id in the tree.
IGL_INLINE std::vector<AABB<DerivedV,DIM>*> gather_leaves();
/// Pad leaves by `pad` in each dimension
/// @param[in] pad padding amount
/// @param[in] polish_rotate_passes number of passes to polish rotations
/// @returns pointer to (potentially new) root
IGL_INLINE AABB<DerivedV,DIM>* pad(
const std::vector<AABB<DerivedV,DIM>*> & leaves,
const Scalar pad,
const int polish_rotate_passes=0);
/// @returns `this` if no update was needed, otherwise returns pointer to
/// (potentially new) root
///
/// Example:
/// ```cpp
/// auto * up = leaf->update(new_box);
/// if(up != leaf)
/// {
/// tree = up->root();
/// }else
/// {
/// printf("no update occurred\n");
/// }
///
/// // or simply
/// tree = leaf->update(new_box)->root();
/// ```
IGL_INLINE AABB<DerivedV,DIM>* update(
const Eigen::AlignedBox<Scalar,DIM> & new_box,
const Scalar pad=0);
/// Insert a (probably a leaf) AABB `other` into this AABB tree. If
/// `other`'s box is contained in this AABB's box then insert it as a child recursively.
///
/// If `other`'s box is not contained in this AABB's box then insert it as a
/// sibling.
///
/// It's a very good idea to call either `rotate` (faster, less good) or `rotate_lineage` (slower, better)
/// after insertion. Rotating continues to improve the tree's quality so
/// after doing a bunch of insertions you might even consider calling
/// `rotate` on all nodes.
///
/// `insert` attempts to minimize total internal surface area. Where as
/// `init` is top-down and splits boxes based on the median along the
/// longest dimension. When initializing a tree, `init` seems to result in
/// great trees (small height and small total internal surface area).
///
/// @param[in] other pointer to another AABB node
/// @returns pointer to the parent of `other` or `other` itself. This
/// could be == to a `new`ly created internal node or to `other` if
/// `this==other`. Calling ->root() on this returned node will give you
/// the root of the tree.
///
/// ##### Example
///
/// ```cpp
/// // Create a tree (use pointer to track changes to root)
/// auto * tree = new igl::AABB<DerivedV,3>::AABB();
/// // Fill the tree (e.g., using ->init())
/// …
/// // Create a new leafe node
/// auto * leaf = new igl::AABB<DerivedV,3>::AABB();
/// // Fill the leaf node with a primitive and box
/// …
/// // Insert into the tree and find the possibly new root
/// tree = tree->insert(leaf)->root();
/// ```
IGL_INLINE AABB<DerivedV,DIM>* insert(AABB * other);
/// Insert `other` as a sibling to `this` by creating a new internal node
/// to be their shared parent.
///
/// Before
/// parent
///
/// this(C) sibling
///
/// left right
///
/// After
/// parent
///
/// newbie sibling
///
/// this other
///
/// left right
///
///
/// @param[in] other pointer to another AABB node
/// @returns pointer to the new shared parent.
IGL_INLINE AABB<DerivedV,DIM>* insert_as_sibling(AABB * other);
/// Try to swap this node with its close relatives if it will decrease
/// total internal surface area.
///
///
/// grandparent
///
/// parent pibling°
/// ╱ ╲ ╱ ╲
/// sibling this cuz1° cuz2°
///
/// nib1° nib2°
///
/// °Swap Candidates
///
/// @param[in] dry_run if true then don't actually swap
/// @return[in] the change in total internal surface area, 0 if no
/// improvement and rotate won't be carried out.
IGL_INLINE Scalar rotate(const bool dry_run = false);
/// Try to swap this node with its cousins if it will decrease
/// total internal surface area.
///
/// @param[in] dry_run if true then don't actually swap
/// @return[in] the change in total internal surface area, 0 if no
/// improvement and rotate won't be carried out.
///
/// Before
/// grandparent
///
/// parent pibling
/// ╱ ╲ ╱ ╲
/// sibling this cuz1 cuz2
///
///
/// Candidates
/// grandparent
///
/// parent pibling
/// ╱ ╲ ╱ ╲
/// sibling cuz1 this cuz2
///
/// Or
/// grandparent
///
/// parent pibling
/// ╱ ╲ ╱ ╲
/// sibling cuz2 cuz1 this
IGL_INLINE Scalar rotate_across(const bool dry_run = false);
/// Try to swap this node with its pibling if it will decrease
/// total internal surface area.
///
/// @param[in] dry_run if true then don't actually swap
/// @return[in] the change in total internal surface area, 0 if no
/// improvement and rotate won't be carried out.
///
/// Before
/// grandparent
///
/// other parent
///
/// this sibling
///
///
/// Candidate
/// grandparent
///
/// this parent
///
/// other sibling
IGL_INLINE Scalar rotate_up(const bool dry_run = false);
/// Try to swap this node with one of its niblings if it will decrease
/// total internal surface area.
///
/// @param[in] dry_run if true then don't actually swap
/// @return[in] the change in total internal surface area, 0 if no
/// improvement and rotate won't be carried out.
///
/// Before
/// parent
///
/// this sibling
///
/// left right
///
///
/// Candidates
/// parent
///
/// left sibling
///
/// this right
///
/// Or
///
/// parent
///
/// right sibling
///
/// left this
IGL_INLINE Scalar rotate_down(const bool dry_run = false);
/// "Rotate" (swap) `reining` with `challenger`.
///
/// Before
/// grandparent
///
/// reining parent
/// ╱ ╲
/// challenger sibling
///
///
/// Candidate
/// grandparent
///
/// challenger parent
/// ╱ ╲
/// reining sibling
/// @param[in] reining pointer to AABB node to be rotated
/// @param[in] grandparent pointer to challenger's grandparent
/// @param[in] parent pointer to challenger's parent
/// @param[in] challenger pointer to AABB node to be rotated
/// @param[in] sibling pointer to challenger's sibling
/// @returns true only if rotation was possible and successfully carried
/// out.
static IGL_INLINE Scalar rotate_up(
const bool dry_run,
AABB<DerivedV,DIM>* reining,
AABB<DerivedV,DIM>* grandparent,
AABB<DerivedV,DIM>* parent,
AABB<DerivedV,DIM>* challenger,
AABB<DerivedV,DIM>* sibling);
// Should this be a static function with an argument?
IGL_INLINE void rotate_lineage();
/// Number of nodes contained in subtree (is it?)
///
/// \note At best, this function has a dubious name. This is really an
/// internal helper function for the serialization.
///
/// \see size()
///
/// @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;
/// @param[in] box query box
/// @param[in,out] leaves list of leaves to append to
IGL_INLINE bool append_intersecting_leaves(
const Eigen::AlignedBox<Scalar,DIM> & box,
std::vector<const AABB<DerivedV,DIM>*> & leaves) const;
/// Compute sum of surface area of all internal (non-root, non-leaf) boxes
IGL_INLINE typename DerivedV::Scalar internal_surface_area() const;
/// Validate the subtree under this node by running a bunch of assertions.
/// Does nothing when not in debug mode
IGL_INLINE void validate() const;
/// print the memory addresses of the tree in a somewhat legible way
IGL_INLINE void print(const int depth = 0) const;
/// @returns Actual size of tree. Total number of nodes in tree. A singleton root
/// has size 1.
///
/// \see subtree_size
IGL_INLINE int size() const;
/// @returns Height of the tree. A singleton root has height 1.
IGL_INLINE int height() const;
private:
/// If new distance (sqr_d_candidate) is less than current distance
/// (sqr_d), then update this distance and its associated values
/// _in-place_:
///
/// @param[in] p dim-long query point (only used in DEBUG mode)
/// @param[in] sqr_d candidate minimum distance for this query, see
/// output
/// @param[in] i candidate index into Ele of closest point, see output
/// @param[in] c dim-long candidate closest point, see output
/// @param[in] sqr_d current minimum distance for this query, see output
/// @param[in] i current index into Ele of closest point, see output
/// @param[in] c dim-long current closest point, see output
/// @param[out] sqr_d minimum of initial value and squared distance to
/// this primitive
/// @param[out] i possibly updated index into Ele of closest point
/// @param[out] c dim-long possibly updated closest point
IGL_INLINE void set_min(
const RowVectorDIMS & p,
const Scalar sqr_d_candidate,
const int & i_candidate,
const RowVectorDIMS & c_candidate,
Scalar & sqr_d,
int & i,
Eigen::PlainObjectBase<RowVectorDIMS> & c) const;
public:
///////////////////////////////////////////////////////////////////////////////
// Templated member functions
///////////////////////////////////////////////////////////////////////////////
/// 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] 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,
typename DerivedEle,
typename Derivedbb_mins,
typename Derivedbb_maxs,
typename Derivedelements>
IGL_INLINE void init(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<Derivedbb_mins> & bb_mins,
const Eigen::MatrixBase<Derivedbb_maxs> & bb_maxs,
const Eigen::MatrixBase<Derivedelements> & elements,
@@ -138,62 +488,65 @@ public:
/// 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] 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.
///
/// @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)
///
/// @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<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedSI> & SI,
const Eigen::MatrixBase<DerivedI>& I);
/// Return whether at leaf node
IGL_INLINE bool is_leaf() const;
/// @returns `this` if no update was needed, otherwise returns pointer to
/// (potentially new) root
template <typename DerivedEle>
IGL_INLINE AABB<DerivedV,DIM>* update_primitive(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Scalar pad=0);
/// 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] 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,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<Derivedq> & q,
const bool first=false) const;
/// 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)
///
/// @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[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_mins,
typename Derivedbb_maxs,
typename Derivedelements>
IGL_INLINE void serialize(
@@ -205,7 +558,7 @@ public:
///
/// @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] p dim-long query point
/// @param[out] i facet index corresponding to smallest distances
/// @param[out] c closest point
/// @return squared distance
@@ -215,7 +568,7 @@ public:
template <typename DerivedEle>
IGL_INLINE Scalar squared_distance(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedEle> & Ele,
const RowVectorDIMS & p,
int & i,
Eigen::PlainObjectBase<RowVectorDIMS> & c) const;
@@ -224,12 +577,12 @@ public:
///
/// @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[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
@@ -239,7 +592,7 @@ public:
template <typename DerivedEle>
IGL_INLINE Scalar squared_distance(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedEle> & Ele,
const RowVectorDIMS & p,
const Scalar low_sqr_d,
const Scalar up_sqr_d,
@@ -249,10 +602,10 @@ public:
///
/// @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[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
@@ -260,7 +613,7 @@ public:
template <typename DerivedEle>
IGL_INLINE Scalar squared_distance(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedEle> & Ele,
const RowVectorDIMS & p,
const Scalar up_sqr_d,
int & i,
@@ -276,10 +629,10 @@ public:
template <typename DerivedEle>
IGL_INLINE bool intersect_ray(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedEle> & Ele,
const RowVectorDIMS & origin,
const RowVectorDIMS & dir,
std::vector<igl::Hit> & hits) const;
std::vector<igl::Hit<typename DerivedV::Scalar>> & hits) const;
/// Intersect a ray with the mesh return first hit
///
/// @param[in] V #V by dim list of vertex positions
@@ -291,10 +644,11 @@ public:
template <typename DerivedEle>
IGL_INLINE bool intersect_ray(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedEle> & Ele,
const RowVectorDIMS & origin,
const RowVectorDIMS & dir,
igl::Hit & hit) const;
igl::Hit<typename DerivedV::Scalar> & hit) const;
/// Intersect a ray with the mesh return first hit farther than `min_t`
///
/// @param[in] V #V by dim list of vertex positions
@@ -307,13 +661,48 @@ public:
template <typename DerivedEle>
IGL_INLINE bool intersect_ray(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedEle> & Ele,
const RowVectorDIMS & origin,
const RowVectorDIMS & dir,
const Scalar min_t,
igl::Hit & hit) const;
public:
igl::Hit<typename DerivedV::Scalar> & hit) const;
/// Intersect a rays with the mesh return first hit for each
///
/// @param[in] V #V by dim list of vertex positions
/// @param[in] Ele #Ele by dim list of simplex indices
/// @param[in] origin #ray by dim+1 list of ray origins
/// @param[in] dir #ray by dim list of ray directions
/// @param[in] min_t minimum t value to consider
/// @param[out] I #ray list of indices into Ele of closest primitives
/// (-1 indicates no hit)
/// @param[out] T #ray list of t values (nan indicates no hit)
/// @param[out] UV #ray by dim list of barycentric coordinates
template <
typename DerivedEle,
typename DerivedOrigin,
typename DerivedDir,
typename DerivedI,
typename DerivedT,
typename DerivedUV>
IGL_INLINE void intersect_ray(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedOrigin> & origin,
const Eigen::MatrixBase<DerivedDir> & dir,
const Scalar min_t,
Eigen::PlainObjectBase<DerivedI> & I,
Eigen::PlainObjectBase<DerivedT> & T,
Eigen::PlainObjectBase<DerivedUV> & UV);
template <
typename DerivedEle,
typename DerivedOrigin,
typename DerivedDir>
IGL_INLINE void intersect_ray(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedOrigin> & origin,
const Eigen::MatrixBase<DerivedDir> & dir,
std::vector<std::vector<igl::Hit<typename DerivedV::Scalar>>> & hits);
/// 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).
@@ -326,18 +715,17 @@ public:
/// @param[out] C #P by dim list of closest points
template <
typename DerivedEle,
typename DerivedP,
typename DerivedsqrD,
typename DerivedI,
typename DerivedP,
typename DerivedsqrD,
typename DerivedI,
typename DerivedC>
IGL_INLINE void squared_distance(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedP> & P,
Eigen::PlainObjectBase<DerivedsqrD> & sqrD,
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).
@@ -351,36 +739,36 @@ public:
/// @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 <
template <
typename DerivedEle,
typename Derivedother_V,
typename Derivedother_Ele,
typename DerivedsqrD,
typename DerivedI,
typename DerivedsqrD,
typename DerivedI,
typename DerivedC>
IGL_INLINE void squared_distance(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedEle> & Ele,
const AABB<Derivedother_V,DIM> & other,
const Eigen::MatrixBase<Derivedother_V> & other_V,
const Eigen::MatrixBase<Derivedother_Ele> & other_Ele,
const Eigen::MatrixBase<Derivedother_Ele> & other_Ele,
Eigen::PlainObjectBase<DerivedsqrD> & sqrD,
Eigen::PlainObjectBase<DerivedI> & I,
Eigen::PlainObjectBase<DerivedC> & C) const;
private:
template <
template <
typename DerivedEle,
typename Derivedother_V,
typename Derivedother_Ele,
typename DerivedsqrD,
typename DerivedI,
typename DerivedsqrD,
typename DerivedI,
typename DerivedC>
IGL_INLINE Scalar squared_distance_helper(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedEle> & Ele,
const AABB<Derivedother_V,DIM> * other,
const Eigen::MatrixBase<Derivedother_V> & other_V,
const Eigen::MatrixBase<Derivedother_Ele>& other_Ele,
const Eigen::MatrixBase<Derivedother_Ele>& other_Ele,
const Scalar up_sqr_d,
Eigen::PlainObjectBase<DerivedsqrD> & sqrD,
Eigen::PlainObjectBase<DerivedI> & I,
@@ -403,7 +791,7 @@ private:
template <typename DerivedEle>
IGL_INLINE void leaf_squared_distance(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedEle> & Ele,
const RowVectorDIMS & p,
const Scalar low_sqr_d,
Scalar & sqr_d,
@@ -413,37 +801,11 @@ private:
template <typename DerivedEle>
IGL_INLINE void leaf_squared_distance(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedEle> & Ele,
const RowVectorDIMS & p,
Scalar & sqr_d,
int & i,
Eigen::PlainObjectBase<RowVectorDIMS> & c) const;
// If new distance (sqr_d_candidate) is less than current distance
// (sqr_d), then update this distance and its associated values
// _in-place_:
//
// Inputs:
// p dim-long query point (only used in DEBUG mode)
// sqr_d candidate minimum distance for this query, see output
// i candidate index into Ele of closest point, see output
// c dim-long candidate closest point, see output
// sqr_d current minimum distance for this query, see output
// i current index into Ele of closest point, see output
// c dim-long current closest point, see output
// Outputs:
// sqr_d minimum of initial value and squared distance to this
// primitive
// i possibly updated index into Ele of closest point
// c dim-long possibly updated closest point
IGL_INLINE void set_min(
const RowVectorDIMS & p,
const Scalar sqr_d_candidate,
const int i_candidate,
const RowVectorDIMS & c_candidate,
Scalar & sqr_d,
int & i,
Eigen::PlainObjectBase<RowVectorDIMS> & c) const;
/// Intersect a ray with the mesh return all hits
///
/// @param[in] V #V by dim list of vertex positions
@@ -460,7 +822,7 @@ private:
const RowVectorDIMS & dir,
const RowVectorDIMS & inv_dir,
const RowVectorDIMS & inv_dir_pad,
std::vector<igl::Hit> & hits) const;
std::vector<igl::Hit<typename DerivedV::Scalar>> & hits) const;
/// Intersect a ray with the mesh return first hit farther than `min_t`
///
/// @param[in] V #V by dim list of vertex positions
@@ -479,7 +841,7 @@ private:
const RowVectorDIMS & inv_dir,
const RowVectorDIMS & inv_dir_pad,
const Scalar min_t,
igl::Hit & hit) const;
igl::Hit<typename DerivedV::Scalar> & hit) const;
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
+19
View File
@@ -0,0 +1,19 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2025 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_COLLAPSE_EDGE_NULL_H
#define IGL_COLLAPSE_EDGE_NULL_H
namespace igl
{
#ifndef IGL_COLLAPSE_EDGE_NULL
/// Special value for indicating a null vertex index as the result of a
/// collapsed edge.
#define IGL_COLLAPSE_EDGE_NULL 0
#endif
}
#endif
+401 -401
View File
@@ -281,7 +281,7 @@ static constexpr inline fpreal32 SYSmax(fpreal32 a, fpreal32 b) { return h_max(a
static constexpr inline fpreal64 SYSmin(fpreal64 a, fpreal64 b) { return h_min(a,b); }
static constexpr inline fpreal64 SYSmax(fpreal64 a, fpreal64 b) { return h_max(a,b); }
// Some systems have size_t as a seperate type from uint. Some don't.
// Some systems have size_t as a separate type from uint. Some don't.
#if (defined(LINUX) && defined(IA64)) || defined(MBSD)
static constexpr inline size_t SYSmin(size_t a, size_t b) { return h_min(a,b); }
static constexpr inline size_t SYSmax(size_t a, size_t b) { return h_max(a,b); }
@@ -742,408 +742,408 @@ vm_allbits(const v4si &a)
#endif
#endif
#pragma once
#ifndef __SSE__
#ifndef __VM_SIMDFunc__
#define __VM_SIMDFunc__
#pragma once
#ifndef __SSE__
#ifndef __VM_SIMDFunc__
#define __VM_SIMDFunc__
#include <cmath>
#include <cmath>
namespace igl {
/// @private
namespace FastWindingNumber {
struct v4si {
int32 v[4];
};
struct v4sf {
float v[4];
};
static SYS_FORCE_INLINE v4sf V4SF(const v4si &v) {
static_assert(sizeof(v4si) == sizeof(v4sf) && alignof(v4si) == alignof(v4sf), "v4si and v4sf must be compatible");
return *(const v4sf*)&v;
}
static SYS_FORCE_INLINE v4si V4SI(const v4sf &v) {
static_assert(sizeof(v4si) == sizeof(v4sf) && alignof(v4si) == alignof(v4sf), "v4si and v4sf must be compatible");
return *(const v4si*)&v;
}
static SYS_FORCE_INLINE int32 conditionMask(bool c) {
return c ? int32(0xFFFFFFFF) : 0;
}
static SYS_FORCE_INLINE v4sf
VM_SPLATS(float f) {
return v4sf{{f, f, f, f}};
}
static SYS_FORCE_INLINE v4si
VM_SPLATS(uint32 i) {
return v4si{{int32(i), int32(i), int32(i), int32(i)}};
}
static SYS_FORCE_INLINE v4si
VM_SPLATS(int32 i) {
return v4si{{i, i, i, i}};
}
static SYS_FORCE_INLINE v4sf
VM_SPLATS(float a, float b, float c, float d) {
return v4sf{{a, b, c, d}};
}
static SYS_FORCE_INLINE v4si
VM_SPLATS(uint32 a, uint32 b, uint32 c, uint32 d) {
return v4si{{int32(a), int32(b), int32(c), int32(d)}};
}
static SYS_FORCE_INLINE v4si
VM_SPLATS(int32 a, int32 b, int32 c, int32 d) {
return v4si{{a, b, c, d}};
}
static SYS_FORCE_INLINE v4si
VM_LOAD(const int32 v[4]) {
return v4si{{v[0], v[1], v[2], v[3]}};
}
static SYS_FORCE_INLINE v4sf
VM_LOAD(const float v[4]) {
return v4sf{{v[0], v[1], v[2], v[3]}};
}
static inline v4si VM_ICMPEQ(v4si a, v4si b) {
return v4si{{
conditionMask(a.v[0] == b.v[0]),
conditionMask(a.v[1] == b.v[1]),
conditionMask(a.v[2] == b.v[2]),
conditionMask(a.v[3] == b.v[3])
}};
}
static inline v4si VM_ICMPGT(v4si a, v4si b) {
return v4si{{
conditionMask(a.v[0] > b.v[0]),
conditionMask(a.v[1] > b.v[1]),
conditionMask(a.v[2] > b.v[2]),
conditionMask(a.v[3] > b.v[3])
}};
}
static inline v4si VM_ICMPLT(v4si a, v4si b) {
return v4si{{
conditionMask(a.v[0] < b.v[0]),
conditionMask(a.v[1] < b.v[1]),
conditionMask(a.v[2] < b.v[2]),
conditionMask(a.v[3] < b.v[3])
}};
}
static inline v4si VM_IADD(v4si a, v4si b) {
return v4si{{
(a.v[0] + b.v[0]),
(a.v[1] + b.v[1]),
(a.v[2] + b.v[2]),
(a.v[3] + b.v[3])
}};
}
static inline v4si VM_ISUB(v4si a, v4si b) {
return v4si{{
(a.v[0] - b.v[0]),
(a.v[1] - b.v[1]),
(a.v[2] - b.v[2]),
(a.v[3] - b.v[3])
}};
}
static inline v4si VM_OR(v4si a, v4si b) {
return v4si{{
(a.v[0] | b.v[0]),
(a.v[1] | b.v[1]),
(a.v[2] | b.v[2]),
(a.v[3] | b.v[3])
}};
}
static inline v4si VM_AND(v4si a, v4si b) {
return v4si{{
(a.v[0] & b.v[0]),
(a.v[1] & b.v[1]),
(a.v[2] & b.v[2]),
(a.v[3] & b.v[3])
}};
}
static inline v4si VM_ANDNOT(v4si a, v4si b) {
return v4si{{
((~a.v[0]) & b.v[0]),
((~a.v[1]) & b.v[1]),
((~a.v[2]) & b.v[2]),
((~a.v[3]) & b.v[3])
}};
}
static inline v4si VM_XOR(v4si a, v4si b) {
return v4si{{
(a.v[0] ^ b.v[0]),
(a.v[1] ^ b.v[1]),
(a.v[2] ^ b.v[2]),
(a.v[3] ^ b.v[3])
}};
}
static SYS_FORCE_INLINE int
VM_EXTRACT(const v4si v, int index) {
return v.v[index];
}
static SYS_FORCE_INLINE float
VM_EXTRACT(const v4sf v, int index) {
return v.v[index];
}
static SYS_FORCE_INLINE v4si
VM_INSERT(v4si v, int32 value, int index) {
v.v[index] = value;
return v;
}
static SYS_FORCE_INLINE v4sf
VM_INSERT(v4sf v, float value, int index) {
v.v[index] = value;
return v;
}
static inline v4si VM_CMPEQ(v4sf a, v4sf b) {
return v4si{{
conditionMask(a.v[0] == b.v[0]),
conditionMask(a.v[1] == b.v[1]),
conditionMask(a.v[2] == b.v[2]),
conditionMask(a.v[3] == b.v[3])
}};
}
static inline v4si VM_CMPNE(v4sf a, v4sf b) {
return v4si{{
conditionMask(a.v[0] != b.v[0]),
conditionMask(a.v[1] != b.v[1]),
conditionMask(a.v[2] != b.v[2]),
conditionMask(a.v[3] != b.v[3])
}};
}
static inline v4si VM_CMPGT(v4sf a, v4sf b) {
return v4si{{
conditionMask(a.v[0] > b.v[0]),
conditionMask(a.v[1] > b.v[1]),
conditionMask(a.v[2] > b.v[2]),
conditionMask(a.v[3] > b.v[3])
}};
}
static inline v4si VM_CMPLT(v4sf a, v4sf b) {
return v4si{{
conditionMask(a.v[0] < b.v[0]),
conditionMask(a.v[1] < b.v[1]),
conditionMask(a.v[2] < b.v[2]),
conditionMask(a.v[3] < b.v[3])
}};
}
static inline v4si VM_CMPGE(v4sf a, v4sf b) {
return v4si{{
conditionMask(a.v[0] >= b.v[0]),
conditionMask(a.v[1] >= b.v[1]),
conditionMask(a.v[2] >= b.v[2]),
conditionMask(a.v[3] >= b.v[3])
}};
}
static inline v4si VM_CMPLE(v4sf a, v4sf b) {
return v4si{{
conditionMask(a.v[0] <= b.v[0]),
conditionMask(a.v[1] <= b.v[1]),
conditionMask(a.v[2] <= b.v[2]),
conditionMask(a.v[3] <= b.v[3])
}};
}
static inline v4sf VM_ADD(v4sf a, v4sf b) {
return v4sf{{
(a.v[0] + b.v[0]),
(a.v[1] + b.v[1]),
(a.v[2] + b.v[2]),
(a.v[3] + b.v[3])
}};
}
static inline v4sf VM_SUB(v4sf a, v4sf b) {
return v4sf{{
(a.v[0] - b.v[0]),
(a.v[1] - b.v[1]),
(a.v[2] - b.v[2]),
(a.v[3] - b.v[3])
}};
}
static inline v4sf VM_NEG(v4sf a) {
return v4sf{{
(-a.v[0]),
(-a.v[1]),
(-a.v[2]),
(-a.v[3])
}};
}
static inline v4sf VM_MUL(v4sf a, v4sf b) {
return v4sf{{
(a.v[0] * b.v[0]),
(a.v[1] * b.v[1]),
(a.v[2] * b.v[2]),
(a.v[3] * b.v[3])
}};
}
static inline v4sf VM_DIV(v4sf a, v4sf b) {
return v4sf{{
(a.v[0] / b.v[0]),
(a.v[1] / b.v[1]),
(a.v[2] / b.v[2]),
(a.v[3] / b.v[3])
}};
}
static inline v4sf VM_MADD(v4sf a, v4sf b, v4sf c) {
return v4sf{{
(a.v[0] * b.v[0]) + c.v[0],
(a.v[1] * b.v[1]) + c.v[1],
(a.v[2] * b.v[2]) + c.v[2],
(a.v[3] * b.v[3]) + c.v[3]
}};
}
static inline v4sf VM_ABS(v4sf a) {
return v4sf{{
(a.v[0] < 0) ? -a.v[0] : a.v[0],
(a.v[1] < 0) ? -a.v[1] : a.v[1],
(a.v[2] < 0) ? -a.v[2] : a.v[2],
(a.v[3] < 0) ? -a.v[3] : a.v[3]
}};
}
static inline v4sf VM_MAX(v4sf a, v4sf b) {
return v4sf{{
(a.v[0] < b.v[0]) ? b.v[0] : a.v[0],
(a.v[1] < b.v[1]) ? b.v[1] : a.v[1],
(a.v[2] < b.v[2]) ? b.v[2] : a.v[2],
(a.v[3] < b.v[3]) ? b.v[3] : a.v[3]
}};
}
static inline v4sf VM_MIN(v4sf a, v4sf b) {
return v4sf{{
(a.v[0] > b.v[0]) ? b.v[0] : a.v[0],
(a.v[1] > b.v[1]) ? b.v[1] : a.v[1],
(a.v[2] > b.v[2]) ? b.v[2] : a.v[2],
(a.v[3] > b.v[3]) ? b.v[3] : a.v[3]
}};
}
static inline v4sf VM_INVERT(v4sf a) {
return v4sf{{
(1.0f/a.v[0]),
(1.0f/a.v[1]),
(1.0f/a.v[2]),
(1.0f/a.v[3])
}};
}
static inline v4sf VM_SQRT(v4sf a) {
return v4sf{{
std::sqrt(a.v[0]),
std::sqrt(a.v[1]),
std::sqrt(a.v[2]),
std::sqrt(a.v[3])
}};
}
static inline v4si VM_INT(v4sf a) {
return v4si{{
int32(a.v[0]),
int32(a.v[1]),
int32(a.v[2]),
int32(a.v[3])
}};
}
static inline v4sf VM_IFLOAT(v4si a) {
return v4sf{{
float(a.v[0]),
float(a.v[1]),
float(a.v[2]),
float(a.v[3])
}};
}
static SYS_FORCE_INLINE void VM_P_FLOOR() {}
static SYS_FORCE_INLINE int32 singleIntFloor(float f) {
// Casting to int32 usually truncates toward zero, instead of rounding down,
// so subtract one if the result is above f.
int32 i = int32(f);
i -= (float(i) > f);
return i;
}
static inline v4si VM_FLOOR(v4sf a) {
return v4si{{
singleIntFloor(a.v[0]),
singleIntFloor(a.v[1]),
singleIntFloor(a.v[2]),
singleIntFloor(a.v[3])
}};
}
static SYS_FORCE_INLINE void VM_E_FLOOR() {}
static SYS_FORCE_INLINE bool vm_allbits(v4si a) {
return (
(a.v[0] == -1) &&
(a.v[1] == -1) &&
(a.v[2] == -1) &&
(a.v[3] == -1)
);
}
int SYS_FORCE_INLINE _mm_movemask_ps(const v4si& v) {
return (
int(v.v[0] < 0) |
(int(v.v[1] < 0)<<1) |
(int(v.v[2] < 0)<<2) |
(int(v.v[3] < 0)<<3)
);
}
int SYS_FORCE_INLINE _mm_movemask_ps(const v4sf& v) {
// Use std::signbit just in case it needs to distinguish between +0 and -0
// or between positive and negative NaN values (e.g. these could really
// be integers instead of floats).
return (
int(std::signbit(v.v[0])) |
(int(std::signbit(v.v[1]))<<1) |
(int(std::signbit(v.v[2]))<<2) |
(int(std::signbit(v.v[3]))<<3)
);
}
}}
#endif
#endif
namespace FastWindingNumber {
struct v4si {
int32 v[4];
};
struct v4sf {
float v[4];
};
static SYS_FORCE_INLINE v4sf V4SF(const v4si &v) {
static_assert(sizeof(v4si) == sizeof(v4sf) && alignof(v4si) == alignof(v4sf), "v4si and v4sf must be compatible");
return *(const v4sf*)&v;
}
static SYS_FORCE_INLINE v4si V4SI(const v4sf &v) {
static_assert(sizeof(v4si) == sizeof(v4sf) && alignof(v4si) == alignof(v4sf), "v4si and v4sf must be compatible");
return *(const v4si*)&v;
}
static SYS_FORCE_INLINE int32 conditionMask(bool c) {
return c ? int32(0xFFFFFFFF) : 0;
}
static SYS_FORCE_INLINE v4sf
VM_SPLATS(float f) {
return v4sf{{f, f, f, f}};
}
static SYS_FORCE_INLINE v4si
VM_SPLATS(uint32 i) {
return v4si{{int32(i), int32(i), int32(i), int32(i)}};
}
static SYS_FORCE_INLINE v4si
VM_SPLATS(int32 i) {
return v4si{{i, i, i, i}};
}
static SYS_FORCE_INLINE v4sf
VM_SPLATS(float a, float b, float c, float d) {
return v4sf{{a, b, c, d}};
}
static SYS_FORCE_INLINE v4si
VM_SPLATS(uint32 a, uint32 b, uint32 c, uint32 d) {
return v4si{{int32(a), int32(b), int32(c), int32(d)}};
}
static SYS_FORCE_INLINE v4si
VM_SPLATS(int32 a, int32 b, int32 c, int32 d) {
return v4si{{a, b, c, d}};
}
static SYS_FORCE_INLINE v4si
VM_LOAD(const int32 v[4]) {
return v4si{{v[0], v[1], v[2], v[3]}};
}
static SYS_FORCE_INLINE v4sf
VM_LOAD(const float v[4]) {
return v4sf{{v[0], v[1], v[2], v[3]}};
}
static inline v4si VM_ICMPEQ(v4si a, v4si b) {
return v4si{{
conditionMask(a.v[0] == b.v[0]),
conditionMask(a.v[1] == b.v[1]),
conditionMask(a.v[2] == b.v[2]),
conditionMask(a.v[3] == b.v[3])
}};
}
static inline v4si VM_ICMPGT(v4si a, v4si b) {
return v4si{{
conditionMask(a.v[0] > b.v[0]),
conditionMask(a.v[1] > b.v[1]),
conditionMask(a.v[2] > b.v[2]),
conditionMask(a.v[3] > b.v[3])
}};
}
static inline v4si VM_ICMPLT(v4si a, v4si b) {
return v4si{{
conditionMask(a.v[0] < b.v[0]),
conditionMask(a.v[1] < b.v[1]),
conditionMask(a.v[2] < b.v[2]),
conditionMask(a.v[3] < b.v[3])
}};
}
static inline v4si VM_IADD(v4si a, v4si b) {
return v4si{{
(a.v[0] + b.v[0]),
(a.v[1] + b.v[1]),
(a.v[2] + b.v[2]),
(a.v[3] + b.v[3])
}};
}
static inline v4si VM_ISUB(v4si a, v4si b) {
return v4si{{
(a.v[0] - b.v[0]),
(a.v[1] - b.v[1]),
(a.v[2] - b.v[2]),
(a.v[3] - b.v[3])
}};
}
static inline v4si VM_OR(v4si a, v4si b) {
return v4si{{
(a.v[0] | b.v[0]),
(a.v[1] | b.v[1]),
(a.v[2] | b.v[2]),
(a.v[3] | b.v[3])
}};
}
static inline v4si VM_AND(v4si a, v4si b) {
return v4si{{
(a.v[0] & b.v[0]),
(a.v[1] & b.v[1]),
(a.v[2] & b.v[2]),
(a.v[3] & b.v[3])
}};
}
static inline v4si VM_ANDNOT(v4si a, v4si b) {
return v4si{{
((~a.v[0]) & b.v[0]),
((~a.v[1]) & b.v[1]),
((~a.v[2]) & b.v[2]),
((~a.v[3]) & b.v[3])
}};
}
static inline v4si VM_XOR(v4si a, v4si b) {
return v4si{{
(a.v[0] ^ b.v[0]),
(a.v[1] ^ b.v[1]),
(a.v[2] ^ b.v[2]),
(a.v[3] ^ b.v[3])
}};
}
static SYS_FORCE_INLINE int
VM_EXTRACT(const v4si v, int index) {
return v.v[index];
}
static SYS_FORCE_INLINE float
VM_EXTRACT(const v4sf v, int index) {
return v.v[index];
}
static SYS_FORCE_INLINE v4si
VM_INSERT(v4si v, int32 value, int index) {
v.v[index] = value;
return v;
}
static SYS_FORCE_INLINE v4sf
VM_INSERT(v4sf v, float value, int index) {
v.v[index] = value;
return v;
}
static inline v4si VM_CMPEQ(v4sf a, v4sf b) {
return v4si{{
conditionMask(a.v[0] == b.v[0]),
conditionMask(a.v[1] == b.v[1]),
conditionMask(a.v[2] == b.v[2]),
conditionMask(a.v[3] == b.v[3])
}};
}
static inline v4si VM_CMPNE(v4sf a, v4sf b) {
return v4si{{
conditionMask(a.v[0] != b.v[0]),
conditionMask(a.v[1] != b.v[1]),
conditionMask(a.v[2] != b.v[2]),
conditionMask(a.v[3] != b.v[3])
}};
}
static inline v4si VM_CMPGT(v4sf a, v4sf b) {
return v4si{{
conditionMask(a.v[0] > b.v[0]),
conditionMask(a.v[1] > b.v[1]),
conditionMask(a.v[2] > b.v[2]),
conditionMask(a.v[3] > b.v[3])
}};
}
static inline v4si VM_CMPLT(v4sf a, v4sf b) {
return v4si{{
conditionMask(a.v[0] < b.v[0]),
conditionMask(a.v[1] < b.v[1]),
conditionMask(a.v[2] < b.v[2]),
conditionMask(a.v[3] < b.v[3])
}};
}
static inline v4si VM_CMPGE(v4sf a, v4sf b) {
return v4si{{
conditionMask(a.v[0] >= b.v[0]),
conditionMask(a.v[1] >= b.v[1]),
conditionMask(a.v[2] >= b.v[2]),
conditionMask(a.v[3] >= b.v[3])
}};
}
static inline v4si VM_CMPLE(v4sf a, v4sf b) {
return v4si{{
conditionMask(a.v[0] <= b.v[0]),
conditionMask(a.v[1] <= b.v[1]),
conditionMask(a.v[2] <= b.v[2]),
conditionMask(a.v[3] <= b.v[3])
}};
}
static inline v4sf VM_ADD(v4sf a, v4sf b) {
return v4sf{{
(a.v[0] + b.v[0]),
(a.v[1] + b.v[1]),
(a.v[2] + b.v[2]),
(a.v[3] + b.v[3])
}};
}
static inline v4sf VM_SUB(v4sf a, v4sf b) {
return v4sf{{
(a.v[0] - b.v[0]),
(a.v[1] - b.v[1]),
(a.v[2] - b.v[2]),
(a.v[3] - b.v[3])
}};
}
static inline v4sf VM_NEG(v4sf a) {
return v4sf{{
(-a.v[0]),
(-a.v[1]),
(-a.v[2]),
(-a.v[3])
}};
}
static inline v4sf VM_MUL(v4sf a, v4sf b) {
return v4sf{{
(a.v[0] * b.v[0]),
(a.v[1] * b.v[1]),
(a.v[2] * b.v[2]),
(a.v[3] * b.v[3])
}};
}
static inline v4sf VM_DIV(v4sf a, v4sf b) {
return v4sf{{
(a.v[0] / b.v[0]),
(a.v[1] / b.v[1]),
(a.v[2] / b.v[2]),
(a.v[3] / b.v[3])
}};
}
static inline v4sf VM_MADD(v4sf a, v4sf b, v4sf c) {
return v4sf{{
(a.v[0] * b.v[0]) + c.v[0],
(a.v[1] * b.v[1]) + c.v[1],
(a.v[2] * b.v[2]) + c.v[2],
(a.v[3] * b.v[3]) + c.v[3]
}};
}
static inline v4sf VM_ABS(v4sf a) {
return v4sf{{
(a.v[0] < 0) ? -a.v[0] : a.v[0],
(a.v[1] < 0) ? -a.v[1] : a.v[1],
(a.v[2] < 0) ? -a.v[2] : a.v[2],
(a.v[3] < 0) ? -a.v[3] : a.v[3]
}};
}
static inline v4sf VM_MAX(v4sf a, v4sf b) {
return v4sf{{
(a.v[0] < b.v[0]) ? b.v[0] : a.v[0],
(a.v[1] < b.v[1]) ? b.v[1] : a.v[1],
(a.v[2] < b.v[2]) ? b.v[2] : a.v[2],
(a.v[3] < b.v[3]) ? b.v[3] : a.v[3]
}};
}
static inline v4sf VM_MIN(v4sf a, v4sf b) {
return v4sf{{
(a.v[0] > b.v[0]) ? b.v[0] : a.v[0],
(a.v[1] > b.v[1]) ? b.v[1] : a.v[1],
(a.v[2] > b.v[2]) ? b.v[2] : a.v[2],
(a.v[3] > b.v[3]) ? b.v[3] : a.v[3]
}};
}
static inline v4sf VM_INVERT(v4sf a) {
return v4sf{{
(1.0f/a.v[0]),
(1.0f/a.v[1]),
(1.0f/a.v[2]),
(1.0f/a.v[3])
}};
}
static inline v4sf VM_SQRT(v4sf a) {
return v4sf{{
std::sqrt(a.v[0]),
std::sqrt(a.v[1]),
std::sqrt(a.v[2]),
std::sqrt(a.v[3])
}};
}
static inline v4si VM_INT(v4sf a) {
return v4si{{
int32(a.v[0]),
int32(a.v[1]),
int32(a.v[2]),
int32(a.v[3])
}};
}
static inline v4sf VM_IFLOAT(v4si a) {
return v4sf{{
float(a.v[0]),
float(a.v[1]),
float(a.v[2]),
float(a.v[3])
}};
}
static SYS_FORCE_INLINE void VM_P_FLOOR() {}
static SYS_FORCE_INLINE int32 singleIntFloor(float f) {
// Casting to int32 usually truncates toward zero, instead of rounding down,
// so subtract one if the result is above f.
int32 i = int32(f);
i -= (float(i) > f);
return i;
}
static inline v4si VM_FLOOR(v4sf a) {
return v4si{{
singleIntFloor(a.v[0]),
singleIntFloor(a.v[1]),
singleIntFloor(a.v[2]),
singleIntFloor(a.v[3])
}};
}
static SYS_FORCE_INLINE void VM_E_FLOOR() {}
static SYS_FORCE_INLINE bool vm_allbits(v4si a) {
return (
(a.v[0] == -1) &&
(a.v[1] == -1) &&
(a.v[2] == -1) &&
(a.v[3] == -1)
);
}
int SYS_FORCE_INLINE _mm_movemask_ps(const v4si& v) {
return (
int(v.v[0] < 0) |
(int(v.v[1] < 0)<<1) |
(int(v.v[2] < 0)<<2) |
(int(v.v[3] < 0)<<3)
);
}
int SYS_FORCE_INLINE _mm_movemask_ps(const v4sf& v) {
// Use std::signbit just in case it needs to distinguish between +0 and -0
// or between positive and negative NaN values (e.g. these could really
// be integers instead of floats).
return (
int(std::signbit(v.v[0])) |
(int(std::signbit(v.v[1]))<<1) |
(int(std::signbit(v.v[2]))<<2) |
(int(std::signbit(v.v[3]))<<3)
);
}
}}
#endif
#endif
/*
* Copyright (c) 2018 Side Effects Software Inc.
*
@@ -3758,7 +3758,7 @@ namespace UT_Thread { inline int getNumProcessors() {
/////
///// Requirements for the Range functor are:
///// - the requirements of the tbb Range Concept
///// - UT_estimatorNumItems<Range> must return the the estimated number of work items
///// - UT_estimatorNumItems<Range> must return the estimated number of work items
///// for the range. When Range::size() is not the correct estimate, then a
///// (partial) specialization of UT_estimatorNumItemsimatorRange must be provided
///// for the type Range.
+6 -4
View File
@@ -8,6 +8,8 @@
#include "HalfEdgeIterator.h"
#include <cassert>
template <typename DerivedF, typename DerivedFF, typename DerivedFFi>
IGL_INLINE igl::HalfEdgeIterator<DerivedF,DerivedFF,DerivedFFi>::HalfEdgeIterator(
const Eigen::MatrixBase<DerivedF>& _F,
@@ -63,10 +65,10 @@ IGL_INLINE bool igl::HalfEdgeIterator<DerivedF,DerivedFF,DerivedFFi>::isBorder()
/*!
* Returns the next edge skipping the border
* _________
* /\ c | b /\
* / \ | / \
* / d \ | / a \
* /______\|/______\
* ╱╲ 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, ...
+4 -4
View File
@@ -69,10 +69,10 @@ namespace igl
/// Change to next edge skipping the border
/// _________
/// /\ c | b /\
/// / \ | / \
/// / d \ | / a \
/// /______\|/______\
/// ╱╲ 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
+3 -3
View File
@@ -13,7 +13,7 @@ namespace igl
{
/// Reimplementation of the embree::Hit struct from embree1.0
///
// TODO: template on floating point type
template <typename Scalar>
struct Hit
{
/// primitive id
@@ -22,10 +22,10 @@ namespace igl
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;
Scalar u,v;
/// parametric distance so that
/// pos = origin + t * dir
float t;
Scalar t;
};
}
#endif
+94 -44
View File
@@ -114,10 +114,31 @@ IGL_INLINE igl::MshLoader::MshLoader(const std::string &filename) {
fin.close();
}
IGL_INLINE int igl::MshLoader::node_dense_index(int node_tag) const {
const auto it = m_node_tag_to_dense.find(node_tag);
if (it == m_node_tag_to_dense.end()) {
std::stringstream err_msg;
err_msg << "Unknown node tag: " << node_tag;
throw std::runtime_error(err_msg.str());
}
return it->second;
}
IGL_INLINE int igl::MshLoader::element_dense_index(int elem_tag) const {
const auto it = m_element_tag_to_dense.find(elem_tag);
if (it == m_element_tag_to_dense.end()) {
std::stringstream err_msg;
err_msg << "Unknown element tag: " << elem_tag;
throw std::runtime_error(err_msg.str());
}
return it->second;
}
IGL_INLINE void igl::MshLoader::parse_nodes(std::ifstream& fin) {
size_t num_nodes;
fin >> num_nodes;
m_nodes.resize(num_nodes*3);
m_node_tag_to_dense.clear();
if (m_binary) {
size_t stride = (4+3*m_data_size);
@@ -127,23 +148,37 @@ IGL_INLINE void igl::MshLoader::parse_nodes(std::ifstream& 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);
int node_tag;
memcpy(&node_tag, data+i*stride, sizeof(int));
if (node_tag <= 0) {
throw std::runtime_error("Invalid node tag");
}
if (m_node_tag_to_dense.find(node_tag) != m_node_tag_to_dense.end()) {
throw std::runtime_error("Duplicate node tag");
}
m_node_tag_to_dense[node_tag] = static_cast<int>(i);
// directly move into vector storage
// this works only when m_data_size==sizeof(Float)==sizeof(double)
memcpy(&m_nodes[i*3], data+i*stride + 4, m_data_size*3);
}
delete [] data;
} else {
int node_idx;
int node_tag;
for (size_t i=0; i<num_nodes; i++) {
fin >> node_idx;
node_idx -= 1;
fin >> node_tag;
if (node_tag <= 0) {
throw std::runtime_error("Invalid node tag");
}
if (m_node_tag_to_dense.find(node_tag) != m_node_tag_to_dense.end()) {
throw std::runtime_error("Duplicate node tag");
}
m_node_tag_to_dense[node_tag] = static_cast<int>(i);
// here it's 3D node explicitly
fin >> m_nodes[node_idx*3]
>> m_nodes[node_idx*3+1]
>> m_nodes[node_idx*3+2];
fin >> m_nodes[i*3]
>> m_nodes[i*3+1]
>> m_nodes[i*3+2];
}
}
}
@@ -152,6 +187,7 @@ 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;
m_element_tag_to_dense.clear();
size_t nodes_per_element;
@@ -168,15 +204,24 @@ IGL_INLINE void igl::MshLoader::parse_elements(std::ifstream& fin) {
// store node info
for (size_t i=0; i<num_elems; i++) {
int elem_idx;
int elem_tag;
// 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);
fin.read((char*)&elem_tag, sizeof(int));
if (elem_tag <= 0) {
throw std::runtime_error("Invalid element tag");
}
if (m_element_tag_to_dense.find(elem_tag) != m_element_tag_to_dense.end()) {
throw std::runtime_error("Duplicate element tag");
}
m_element_tag_to_dense[elem_tag] = static_cast<int>(m_elements_ids.size());
elem_tag -= 1;
m_elements_ids.push_back(elem_tag);
// read first two tags
for (size_t j=0; j<num_tags; j++) {
@@ -191,10 +236,10 @@ IGL_INLINE void igl::MshLoader::parse_elements(std::ifstream& fin) {
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));
int node_tag;
fin.read((char*)&node_tag, sizeof(int));
m_elements.push_back(idx-1);
m_elements.push_back(node_dense_index(node_tag));
}
}
elem_read += num_elems;
@@ -202,8 +247,16 @@ IGL_INLINE void igl::MshLoader::parse_elements(std::ifstream& fin) {
} 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;
int elem_tag, elem_type, num_tags;
fin >> elem_tag >> elem_type >> num_tags;
if (elem_tag <= 0) {
throw std::runtime_error("Invalid element tag");
}
if (m_element_tag_to_dense.find(elem_tag) != m_element_tag_to_dense.end()) {
throw std::runtime_error("Duplicate element tag");
}
m_element_tag_to_dense[elem_tag] = static_cast<int>(m_elements_ids.size());
// read tags.
for (size_t j=0; j<num_tags; j++) {
@@ -218,14 +271,14 @@ IGL_INLINE void igl::MshLoader::parse_elements(std::ifstream& fin) {
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);
elem_tag -= 1;
m_elements_ids.push_back(elem_tag);
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.
int node_tag;
fin >> node_tag;
m_elements.push_back(node_dense_index(node_tag)); // msh index starts from 1.
}
}
}
@@ -274,7 +327,7 @@ IGL_INLINE void igl::MshLoader::parse_node_field( std::ifstream& fin ) {
int num_components = int_tags[1];
int num_entries = int_tags[2];
std::vector<Float> field( num_entries*num_components );
std::vector<Float> field((m_nodes.size()/3)*num_components);
if (m_binary) {
size_t num_bytes = (num_components * m_data_size + 4) * num_entries;
@@ -282,23 +335,20 @@ IGL_INLINE void igl::MshLoader::parse_node_field( std::ifstream& fin ) {
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");
int node_tag;
memcpy(&node_tag,&data[i*(4+num_components*m_data_size)],4);
const int node_idx = node_dense_index(node_tag);
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;
int node_tag;
for (size_t i=0; i<num_entries; i++) {
fin >> node_idx;
node_idx -= 1;
fin >> node_tag;
const int node_idx = node_dense_index(node_tag);
for (size_t j=0; j<num_components; j++) {
fin >> field[node_idx*num_components+j];
}
@@ -346,7 +396,7 @@ IGL_INLINE void igl::MshLoader::parse_element_field(std::ifstream& fin) {
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);
std::vector<Float> field(m_elements_ids.size()*num_components);
if (m_binary) {
size_t num_bytes = (num_components * m_data_size + 4) * num_entries;
@@ -354,20 +404,20 @@ IGL_INLINE void igl::MshLoader::parse_element_field(std::ifstream& fin) {
igl::_msh_eat_white_space(fin);
fin.read(data, num_bytes);
for (int i=0; i<num_entries; i++) {
int elem_idx;
int elem_tag;
// works with sizeof(int)==4
memcpy(&elem_idx, &data[i*(4+num_components*m_data_size)],4);
elem_idx -= 1;
memcpy(&elem_tag, &data[i*(4+num_components*m_data_size)],4);
const int elem_idx = element_dense_index(elem_tag);
// 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;
int elem_tag;
for (size_t i=0; i<num_entries; i++) {
fin >> elem_idx;
elem_idx -= 1;
fin >> elem_tag;
const int elem_idx = element_dense_index(elem_tag);
for (size_t j=0; j<num_components; j++) {
fin >> field[elem_idx*num_components+j];
}
+6 -1
View File
@@ -15,6 +15,7 @@
#include <string>
#include <vector>
#include <algorithm>
#include <unordered_map>
namespace igl {
@@ -155,11 +156,15 @@ class MshLoader {
void parse_element_field(std::ifstream& fin);
void parse_unknown_field(std::ifstream& fin,
const std::string& fieldname);
int node_dense_index(int node_tag) const;
int element_dense_index(int elem_tag) const;
private:
bool m_binary;
size_t m_data_size;
std::unordered_map<int, int> m_node_tag_to_dense;
std::unordered_map<int, int> m_element_tag_to_dense;
FloatVector m_nodes; // len x 3 vector
IndexVector m_elements; // linear array for nodes corresponding to each element
+28
View File
@@ -0,0 +1,28 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2019 Qingnan Zhou <qnzhou@gmail.com>
// Copyright (C) 2025 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/.
#pragma once
#ifndef IGL_ORIENTATION_H
#define IGL_ORIENTATION_H
#include "igl_inline.h"
#include <Eigen/Core>
namespace igl {
/// Types of orientations and other predicate results.
///
/// \fileinfo
enum class Orientation {
POSITIVE=1, INSIDE=1,
NEGATIVE=-1, OUTSIDE=-1,
COLLINEAR=0, COPLANAR=0, COCIRCULAR=0, COSPHERICAL=0, DEGENERATE=0
};
}
#endif
+95
View File
@@ -0,0 +1,95 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2024 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_PLAINMATRIX_H
#define IGL_PLAINMATRIX_H
#include <Eigen/Core>
#include <type_traits>
#include <Eigen/Dense>
// Define void_t for compatibility if it's not in the standard library (C++11 and later)
#if __cplusplus < 201703L
namespace std {
template <typename... Ts>
using void_t = void;
}
#endif
#ifndef IGL_DEFAULT_MAJORING
#define IGL_DEFAULT_MAJORING Eigen::ColMajor
#endif
namespace igl
{
template <typename Derived, int Rows, int Cols, int Options>
struct PlainMatrixHelper {
using Type = Eigen::Matrix<typename Derived::Scalar,Rows,Cols,((Rows == 1 && Cols != 1) ? Eigen::RowMajor : ((Cols == 1 && Rows != 1) ? Eigen::ColMajor : Options))>;
};
template <typename Derived, typename = void>
struct get_options {
static constexpr int value = IGL_DEFAULT_MAJORING;
};
template <typename Derived>
struct get_options<Derived, std::void_t<decltype(Derived::Options)>> {
static constexpr int value = Derived::Options;
};
/// Some libigl implementations would (still do?) use a pattern like:
///
/// template <typename DerivedA>
/// void foo(const Eigen::MatrixBase<DerivedA>& A)
/// {
/// DerivedA B;
/// igl::unique_rows(A,true,B);
/// }
///
/// If `DerivedA` is `Eigen::Matrix`, then this may compile, but `DerivedA` might be
/// from a Eigen::Map or Eigen::Ref and fail to compile due to missing
/// construtor.
///
/// Even worse, the code above will work if `DerivedA` has dynamic rows, but will
/// throw a runtime error if `DerivedA` has fixed number of rows.
///
/// Instead it's better to declare `B` as a `Eigen::Matrix`
///
/// Eigen::Matrix<typename DerivedA::Scalar,Eigen::Dynamic,DerivedA::ColsAtCompileTime,DerivedA::Options> B;
///
/// Using `Eigen::Dynamic` for dimensions that may not be known at compile
/// time (or may be different from A).
///
/// `igl::PlainMatrix` is just a helper to make this easier. So in this case
/// we could write:
///
/// igl::PlainMatrix<DerivedA,Eigen::Dynamic> B;
///
/// IIUC, if the code in question looks like:
///
/// template <typename DerivedC>
/// void foo(Eigen::PlainObjectBase<DerivedC>& C)
/// {
/// DerivedC B;
/// …
/// C.resize(not_known_at_compile_time,also_not_known_at_compile_time);
/// }
///
/// Then it's probably fine. If C can be resized to different sizes, then
/// `DerivedC` should be `Eigen::Matrix`-like .
// Helper to check if `Options` exists in Derived
// Modify PlainMatrix to use get_options
template <typename Derived,
int Rows = Derived::RowsAtCompileTime,
int Cols = Derived::ColsAtCompileTime,
int Options = get_options<Derived>::value>
using PlainMatrix = typename PlainMatrixHelper<Derived, Rows, Cols, Options>::Type;
}
#endif
+36
View File
@@ -0,0 +1,36 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2024 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_PLAINVECTOR_H
#define IGL_PLAINVECTOR_H
#include <Eigen/Core>
#include "PlainMatrix.h"
namespace igl
{
// PlainVectorHelper to determine correct matrix type based on Derived and Size
template <typename Derived, int Size, int Options>
struct PlainVectorHelper {
// Conditional Type: Column vector if is_column_vector is true, otherwise row vector
using Type = Eigen::Matrix<
typename Derived::Scalar,
(Derived::ColsAtCompileTime == 1 && Derived::RowsAtCompileTime != 1) ? Size : 1,
(Derived::ColsAtCompileTime == 1 && Derived::RowsAtCompileTime != 1) ? 1 : Size,
Options>;
};
/// \see PlainMatrix
template <
typename Derived,
int Size = (Derived::ColsAtCompileTime == 1 && Derived::RowsAtCompileTime != 1) ? Derived::RowsAtCompileTime : Derived::ColsAtCompileTime,
int Options = get_options<Derived>::value>
using PlainVector = typename PlainVectorHelper<Derived, Size, Options>::Type;
}
#endif
+232
View File
@@ -0,0 +1,232 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2025 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 "SphereMeshWedge.h"
#include "round_cone_signed_distance.h"
#include "sign.h"
#include <cassert>
#include <Eigen/QR>
#include <Eigen/Geometry>
template <typename Scalar>
IGL_INLINE igl::SphereMeshWedge<Scalar>::SphereMeshWedge(
const RowVector3S & V0,
const RowVector3S & V1,
const RowVector3S & V2,
const Scalar r0,
const Scalar r1,
const Scalar r2)
{
// Internal copy
V.row(0) = V0;
V.row(1) = V1;
V.row(2) = V2;
r(0) = r0;
r(1) = r1;
r(2) = r2;
flavor = FULL;
// By default use full
EV.row(0) = V.row(2) - V.row(1);
EV.row(1) = V.row(0) - V.row(2);
EV.row(2) = V.row(1) - V.row(0);
l = EV.rowwise().norm();
l2 = l.array().square();
rr << r(1) - r(2), r(2) - r(0), r(0) - r(1);
a2 = l2.array() - rr.array().square();
il2 = 1.0/l2.array();
/////////////////////////////////////////////
/// BIG_VERTEX ?
/////////////////////////////////////////////
{
r.maxCoeff(&max_i);
int j = (max_i+1)%3;
int k = (max_i+2)%3;
if((l(k) + r(j) < r(max_i)) && (l(j) + r(k) < r(max_i)))
{
flavor = BIG_VERTEX;
}
}
/////////////////////////////////////////////
/// BIG_EDGE ?
/////////////////////////////////////////////
if(flavor == FULL)
{
// Case where one edge's roundCone containes the others
for(int e = 0;e<3;e++)
{
const int i = (e+1)%3;
const int j = (e+2)%3;
const int k = (e+3)%3;
const Scalar s =
igl::round_cone_signed_distance(V.row(k),V.row(i),V.row(j),r(i),r(j));
if(-s > r(k))
{
flavor = BIG_EDGE;
max_i = i;
break;
}
}
}
if(flavor == FULL && !compute_planes())
{
flavor = NO_TRIANGLE;
}
}
template <typename Scalar>
IGL_INLINE Scalar igl::SphereMeshWedge<Scalar>::operator()(const RowVector3S & p) const
{
if(flavor == BIG_VERTEX)
{
// Case 0: Vertex i
return (p - V.row(max_i)).norm() - r(max_i);
}
if(flavor == BIG_EDGE)
{
const int i = max_i;
const int j = (i+1)%3;
// Case 1: Edge e
return this->round_cone_signed_distance(p,i,j);
}
Scalar s = std::numeric_limits<Scalar>::infinity();
if(flavor == FULL)
{
// This is possibly the bottleneck and could be turned into precomputed
// plane equations.
// signed distance to triangle plane (this is immediately recomputed later in
// sdSkewedExtrudedTriangle...)
const auto plane_sdf = [](
const RowVector3S & p,
const Eigen::RowVector4d & plane)
{
return plane.head<3>().dot(p) + plane(3);
};
Scalar d0 = plane_sdf(p, planes.row(0));
Scalar planes_s = -std::abs(d0);
// Reflect if necessary so that q is always on negative side of plane
RowVector3S q = p - (d0 - planes_s) * planes.row(0).template head<3>();
// Other planes (for negative side slab, by symmetry)
for(int i = 1;i<planes.rows();i++)
{
planes_s = std::max(planes_s,plane_sdf(q, planes.row(i)));
}
// This produces correct interior distance
if(planes_s <= 0)
{
s = std::min(s,planes_s);
}else
{
const auto & nor = planes.row(1).template head<3>();
const RowVector3S q0 = q - T.row(0);
const RowVector3S q1 = q - T.row(1);
const RowVector3S q2 = q - T.row(2);
if(!(sign(C.row(0).dot(q0)) +
sign(C.row(1).dot(q1)) +
sign(C.row(2).dot(q2))<2.0))
{
s = std::min(s,planes_s);
}
}
//s = std::min(s,sdSkewedExtrudedTriangle(q,V,T));
//s = std::min(s,sdSkewedExtrudedTriangle(p,B,V));
}
assert(flavor == FULL || flavor == NO_TRIANGLE);
for(int e = 0;e<3;e++)
{
const int i = (e+1)%3;
const int j = (e+2)%3;
s = std::min(s,this->round_cone_signed_distance(p,i,j));
}
return s;
}
template <typename Scalar>
IGL_INLINE bool igl::SphereMeshWedge<Scalar>::compute_planes()
{
// Non-degenerate case
const RowVector3S & a = V.row(0);
const RowVector3S & b = V.row(1);
const RowVector3S & c = V.row(2);
const Scalar & ra = r(0);
const Scalar & rb = r(1);
const Scalar & rc = r(2);
Eigen::Matrix<Scalar,2,3,Eigen::RowMajor> A;
A<<
b-a,
c-a;
const Eigen::Vector2d d(rb-ra,rc-ra);
const RowVector3S N = (A.row(0).cross(A.row(1))).normalized();
//const Eigen::CompleteOrthogonalDecomposition<decltype(A)> cod(A);
const RowVector3S n0 = A.completeOrthogonalDecomposition().solve(d);
const Scalar qA = N.squaredNorm();
// qB is zeros by construction. We could delete all terms involving qB
// It's not even clear if keeping them would lead to more accurate results.
const Scalar qB = 2 * N.dot(n0);
const Scalar qC = n0.squaredNorm() - 1;
const Scalar qD = qB*qB - 4*qA*qC;
if(qD<0) { return false; }
Scalar t_sol_1 = (-qB + std::sqrt(qD)) / (2*qA);
RowVector3S n1 = -(t_sol_1 * N + n0);
T = V + r * n1;
const auto plane_equation = [](
const RowVector3S & a,
const RowVector3S & b,
const RowVector3S & c)->Eigen::RowVector4d
{
RowVector3S n = (b-a).cross(c-a).normalized();
n.normalize();
Scalar d = -n.dot(a);
return Eigen::RowVector4d(n(0),n(1),n(2),d);
};
planes.row(0) = plane_equation(V.row(0),V.row(1),V.row(2));
planes.row(1) = plane_equation(T.row(2),T.row(1),T.row(0));
planes.row(2) = plane_equation(V.row(1),V.row(0),T.row(0));
planes.row(3) = plane_equation(V.row(2),V.row(1),T.row(1));
planes.row(4) = plane_equation(V.row(0),V.row(2),T.row(2));
// Determine if the closest point is on the face.
const RowVector3S v10 = T.row(1) - T.row(0);
const RowVector3S v21 = T.row(2) - T.row(1);
const RowVector3S v02 = T.row(0) - T.row(2);
const auto & nor = planes.row(1).template head<3>();
const RowVector3S c10 = v10.cross(nor);
const RowVector3S c21 = v21.cross(nor);
const RowVector3S c02 = v02.cross(nor);
C<<c10,c21,c02;
return true;
}
template <typename Scalar>
IGL_INLINE Scalar igl::SphereMeshWedge<Scalar>::round_cone_signed_distance(const RowVector3S & p, const int i, const int j) const
{
const int e = (j+1)%3;
return igl::round_cone_signed_distance(
p, V.row(i), r(i), r(j), EV.row(e), l2(e), rr(e), a2(e), il2(e));
}
#ifdef IGL_STATIC_LIBRARY
/// Explicit template instantiation
template class igl::SphereMeshWedge<double>;
#endif
+87
View File
@@ -0,0 +1,87 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2025 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_SPHERE_MESH_WEDGE_H
#define IGL_SPHERE_MESH_WEDGE_H
#include "igl_inline.h"
#include <Eigen/Core>
namespace igl
{
/// A class to compute the signed distance to a "Sphere-Mesh Wedge" as seen in
/// variable radius offset surfaces or Sphere-Meshes. Each wedge is defined
/// by three vertices and three radii, one at each vertex. The wedge is
/// the union of all spheres at points on the triangle with radius linearly
/// interpolated. See, e.g., "Sphere-Meshes for Real-Time Hand Modeling and
/// Tracking" or "A Multilinear Model for Bidirectional Craniofacial
/// Reconstruction" or "Sphere-Meshes: Shape Approximation using Spherical
/// Quadric Error Metrics" or "Variable-Radius Offset Surface Approximation on
/// the GPU".
///
template <typename Scalar>
class SphereMeshWedge
{
public:
using RowVector3S = Eigen::Matrix<Scalar, 1, 3>;
// Fields
enum
{
BIG_VERTEX = 0,
BIG_EDGE = 1,
NO_TRIANGLE = 2,
FULL = 3
} flavor;
Eigen::Matrix<Scalar,3,3,Eigen::RowMajor> V;
Eigen::Matrix<Scalar,3,1> r;
Eigen::Matrix<Scalar,3,3,Eigen::RowMajor> EV;
Eigen::Matrix<Scalar,3,1> l,l2,rr,a2,il2;
int max_i;
Eigen::Matrix<Scalar,5,4,Eigen::RowMajor> planes;
Eigen::Matrix<Scalar,3,3,Eigen::RowMajor> T;
Eigen::Matrix<Scalar,3,3,Eigen::RowMajor> C;
SphereMeshWedge(){}
/// Constructor that takes three vertices and three radii
///
/// @param V0 first vertex position
/// @param V1 second vertex position
/// @param V2 third vertex position
/// @param r0 radius at first vertex
/// @param r1 radius at second vertex
/// @param r2 radius at third vertex
IGL_INLINE SphereMeshWedge(
const RowVector3S & V0,
const RowVector3S & V1,
const RowVector3S & V2,
const Scalar r0,
const Scalar r1,
const Scalar r2);
/// @param[in] p 3-vector query point
/// @return signed distance to the wedge at point p
IGL_INLINE Scalar operator()(const RowVector3S & p) const;
private:
/// Precompute planes used for determining bounded signed to the skewed
/// triangular slab portion.
///
/// @return true if planes are well defined (false implies this slab has
/// no contribution).
IGL_INLINE bool compute_planes();
/// Compute the signed distance to the wedge at a point p for the edge
/// (i,j)
///
/// @param[in] p 3-vector query point
/// @param[in] i index of first vertex (0,1,2)
/// @param[in] j index of second vertex (0,1,2)
IGL_INLINE Scalar round_cone_signed_distance(const RowVector3S & p, const int i, const int j) const;
};
}
#ifndef IGL_STATIC_LIBRARY
#include "SphereMeshWedge.cpp"
#endif
#endif
+5 -4
View File
@@ -22,6 +22,7 @@
#include <sys/time.h>
#endif
#include <cstddef>
#include <cstdint>
namespace igl
{
@@ -69,9 +70,9 @@ namespace igl
/// @param[in] endTime end time
/// @param[in] startTime start time
/// @return time
double subtractTimes( uint64_t endTime, uint64_t startTime )
double subtractTimes( std::uint64_t endTime, std::uint64_t startTime )
{
uint64_t difference = endTime - startTime;
std::uint64_t difference = endTime - startTime;
static double conversion = 0.0;
if( conversion == 0.0 )
@@ -175,8 +176,8 @@ namespace igl
LARGE_INTEGER startCount;
LARGE_INTEGER endCount;
#elif __APPLE__
uint64_t startCount;
uint64_t endCount;
std::uint64_t startCount;
std::uint64_t endCount;
#else
timeval startCount;
timeval endCount;
+86 -94
View File
@@ -13,21 +13,24 @@
#ifndef IGL_WINDINGNUMBERAABB_H
#define IGL_WINDINGNUMBERAABB_H
#include "WindingNumberTree.h"
#include "PlainMatrix.h"
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,
typename DerivedF >
class WindingNumberAABB : public WindingNumberTree<Point,DerivedV,DerivedF>
typename Scalar,
typename Index>
class WindingNumberAABB : public WindingNumberTree<Scalar,Index>
{
protected:
// WindingNumberTree defines Point
using Point = typename WindingNumberTree<Scalar,Index>::Point;
using MatrixXF = typename WindingNumberTree<Scalar,Index>::MatrixXF;
Point min_corner;
Point max_corner;
typename DerivedV::Scalar total_positive_area;
Scalar total_positive_area;
public:
enum SplitMethod
{
@@ -37,23 +40,25 @@ namespace igl
} split_method;
public:
inline WindingNumberAABB():
total_positive_area(std::numeric_limits<typename DerivedV::Scalar>::infinity()),
total_positive_area(std::numeric_limits<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
template <typename DerivedV, typename DerivedF>
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);
const WindingNumberTree<Scalar,Index> & parent,
const typename WindingNumberTree<Scalar,Index>::MatrixXF & F);
/// 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
template <typename DerivedV, typename DerivedF>
inline void set_mesh(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F);
@@ -62,8 +67,8 @@ namespace igl
inline virtual void grow();
// Compute min and max corners
inline void compute_min_max_corners();
inline typename DerivedV::Scalar max_abs_winding_number(const Point & p) const;
inline typename DerivedV::Scalar max_simple_abs_winding_number(const Point & p) const;
inline Scalar max_abs_winding_number(const Point & p) const;
inline Scalar max_simple_abs_winding_number(const Point & p) const;
};
}
@@ -86,70 +91,73 @@ namespace igl
# define WindingNumberAABB_MIN_F 100
#endif
template <typename Point, typename DerivedV, typename DerivedF>
inline void igl::WindingNumberAABB<Point,DerivedV,DerivedF>::set_mesh(
template <typename Scalar, typename Index>
template <typename DerivedV, typename DerivedF>
inline void igl::WindingNumberAABB<Scalar,Index>::set_mesh(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F)
{
igl::WindingNumberTree<Point,DerivedV,DerivedF>::set_mesh(V,F);
// static assert that DerivedF::ColsAtCompileTime == 3 or Eigen::Dynamic
static_assert(
DerivedF::ColsAtCompileTime == 3 || DerivedF::ColsAtCompileTime == Eigen::Dynamic,
"F should have 3 or Dynamic columns");
igl::WindingNumberTree<Scalar,Index>::set_mesh(V,F);
init();
}
template <typename Point, typename DerivedV, typename DerivedF>
inline void igl::WindingNumberAABB<Point,DerivedV,DerivedF>::init()
template <typename Scalar, typename Index>
inline void igl::WindingNumberAABB<Scalar,Index>::init()
{
using namespace Eigen;
assert(max_corner.size() == 3);
assert(min_corner.size() == 3);
compute_min_max_corners();
Eigen::Matrix<typename DerivedV::Scalar,Eigen::Dynamic,1> dblA;
doublearea(this->getV(),this->getF(),dblA);
Eigen::Matrix<Scalar,Eigen::Dynamic,1> dblA;
doublearea((*this->Vptr),(this->F),dblA);
total_positive_area = dblA.sum()/2.0;
}
template <typename Point, typename DerivedV, typename DerivedF>
inline igl::WindingNumberAABB<Point,DerivedV,DerivedF>::WindingNumberAABB(
template <typename Scalar, typename Index>
template <typename DerivedV, typename DerivedF>
inline igl::WindingNumberAABB<Scalar,Index>::WindingNumberAABB(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F):
WindingNumberTree<Point,DerivedV,DerivedF>(V,F),
WindingNumberTree<Scalar,Index>(V,F),
min_corner(),
max_corner(),
total_positive_area(
std::numeric_limits<typename DerivedV::Scalar>::infinity()),
std::numeric_limits<Scalar>::infinity()),
split_method(MEDIAN_ON_LONGEST_AXIS)
{
init();
}
template <typename Point, typename DerivedV, typename DerivedF>
inline igl::WindingNumberAABB<Point,DerivedV,DerivedF>::WindingNumberAABB(
const WindingNumberTree<Point,DerivedV,DerivedF> & parent,
const Eigen::MatrixBase<DerivedF> & F):
WindingNumberTree<Point,DerivedV,DerivedF>(parent,F),
template <typename Scalar, typename Index>
inline igl::WindingNumberAABB<Scalar,Index>::WindingNumberAABB(
const WindingNumberTree<Scalar,Index> & parent,
const typename WindingNumberTree<Scalar,Index>::MatrixXF & F):
WindingNumberTree<Scalar,Index>(parent,F),
min_corner(),
max_corner(),
total_positive_area(
std::numeric_limits<typename DerivedV::Scalar>::infinity()),
std::numeric_limits<Scalar>::infinity()),
split_method(MEDIAN_ON_LONGEST_AXIS)
{
init();
}
template <typename Point, typename DerivedV, typename DerivedF>
inline void igl::WindingNumberAABB<Point,DerivedV,DerivedF>::grow()
template <typename Scalar, typename Index>
inline void igl::WindingNumberAABB<Scalar,Index>::grow()
{
using namespace std;
using namespace Eigen;
// Clear anything that already exists
this->delete_children();
//cout<<"cap.rows(): "<<this->getcap().rows()<<endl;
//cout<<"F.rows(): "<<this->getF().rows()<<endl;
//cout<<"cap.rows(): "<<(this->cap).rows()<<endl;
//cout<<"F.rows(): "<<(this->F).rows()<<endl;
// Base cases
if(
this->getF().rows() <= (WindingNumberAABB_MIN_F>0?WindingNumberAABB_MIN_F:0) ||
(this->getcap().rows() - 2) >= this->getF().rows())
(this->F).rows() <= (WindingNumberAABB_MIN_F>0?WindingNumberAABB_MIN_F:0) ||
((this->cap).rows() - 2) >= (this->F).rows())
{
// Don't grow
return;
@@ -157,8 +165,8 @@ inline void igl::WindingNumberAABB<Point,DerivedV,DerivedF>::grow()
// Compute longest direction
int max_d = -1;
typename DerivedV::Scalar max_len =
-numeric_limits<typename DerivedV::Scalar>::infinity();
Scalar max_len =
-std::numeric_limits<Scalar>::infinity();
for(int d = 0;d<min_corner.size();d++)
{
if( (max_corner[d] - min_corner[d]) > max_len )
@@ -168,13 +176,13 @@ inline void igl::WindingNumberAABB<Point,DerivedV,DerivedF>::grow()
}
}
// Compute facet barycenters
Eigen::Matrix<typename DerivedV::Scalar,Eigen::Dynamic,Eigen::Dynamic> BC;
barycenter(this->getV(),this->getF(),BC);
Eigen::Matrix<Scalar,Eigen::Dynamic,Eigen::Dynamic> BC;
barycenter((*this->Vptr),(this->F),BC);
// Blerg, why is selecting rows so difficult
typename DerivedV::Scalar split_value;
Scalar split_value;
// Split in longest direction
switch(split_method)
{
@@ -191,8 +199,8 @@ inline void igl::WindingNumberAABB<Point,DerivedV,DerivedF>::grow()
//cout<<"c: "<<0.5*(max_corner[max_d] + min_corner[max_d])<<" "<<
// "m: "<<split_value<<endl;;
vector<int> id( this->getF().rows());
for(int i = 0;i<this->getF().rows();i++)
std::vector<int> id( (this->F).rows());
for(int i = 0;i<(this->F).rows();i++)
{
if(BC(i,max_d) <= split_value)
{
@@ -210,19 +218,19 @@ inline void igl::WindingNumberAABB<Point,DerivedV,DerivedF>::grow()
// badly balanced base case (could try to recut)
return;
}
assert(lefts+rights == this->getF().rows());
DerivedF leftF(lefts, this->getF().cols());
DerivedF rightF(rights,this->getF().cols());
assert(lefts+rights == (this->F).rows());
MatrixXF leftF(lefts, (this->F).cols());
MatrixXF rightF(rights,(this->F).cols());
int left_i = 0;
int right_i = 0;
for(int i = 0;i<this->getF().rows();i++)
for(int i = 0;i<(this->F).rows();i++)
{
if(id[i] == 0)
{
leftF.row(left_i++) = this->getF().row(i);
leftF.row(left_i++) = (this->F).row(i);
}else if(id[i] == 1)
{
rightF.row(right_i++) = this->getF().row(i);
rightF.row(right_i++) = (this->F).row(i);
}else
{
assert(false);
@@ -231,18 +239,18 @@ inline void igl::WindingNumberAABB<Point,DerivedV,DerivedF>::grow()
assert(right_i == rightF.rows());
assert(left_i == leftF.rows());
// Finally actually grow children and Recursively grow
WindingNumberAABB<Point,DerivedV,DerivedF> * leftWindingNumberAABB =
new WindingNumberAABB<Point,DerivedV,DerivedF>(*this,leftF);
WindingNumberAABB<Scalar,Index> * leftWindingNumberAABB =
new WindingNumberAABB<Scalar,Index>(*this,leftF);
leftWindingNumberAABB->grow();
this->children.push_back(leftWindingNumberAABB);
WindingNumberAABB<Point,DerivedV,DerivedF> * rightWindingNumberAABB =
new WindingNumberAABB<Point,DerivedV,DerivedF>(*this,rightF);
WindingNumberAABB<Scalar,Index> * rightWindingNumberAABB =
new WindingNumberAABB<Scalar,Index>(*this,rightF);
rightWindingNumberAABB->grow();
this->children.push_back(rightWindingNumberAABB);
}
template <typename Point, typename DerivedV, typename DerivedF>
inline bool igl::WindingNumberAABB<Point,DerivedV,DerivedF>::inside(const Point & p) const
template <typename Scalar, typename Index>
inline bool igl::WindingNumberAABB<Scalar,Index>::inside(const Point & p) const
{
assert(p.size() == max_corner.size());
assert(p.size() == min_corner.size());
@@ -259,39 +267,38 @@ inline bool igl::WindingNumberAABB<Point,DerivedV,DerivedF>::inside(const Point
return true;
}
template <typename Point, typename DerivedV, typename DerivedF>
inline void igl::WindingNumberAABB<Point,DerivedV,DerivedF>::compute_min_max_corners()
template <typename Scalar, typename Index>
inline void igl::WindingNumberAABB<Scalar,Index>::compute_min_max_corners()
{
using namespace std;
// initialize corners
for(int d = 0;d<min_corner.size();d++)
{
min_corner[d] = numeric_limits<typename Point::Scalar>::infinity();
max_corner[d] = -numeric_limits<typename Point::Scalar>::infinity();
min_corner[d] = std::numeric_limits<typename Point::Scalar>::infinity();
max_corner[d] = -std::numeric_limits<typename Point::Scalar>::infinity();
}
this->center = Point(0,0,0);
// Loop over facets
for(int i = 0;i<this->getF().rows();i++)
for(int i = 0;i<(this->F).rows();i++)
{
for(int j = 0;j<this->getF().cols();j++)
for(int j = 0;j<(this->F).cols();j++)
{
for(int d = 0;d<min_corner.size();d++)
{
min_corner[d] =
this->getV()(this->getF()(i,j),d) < min_corner[d] ?
this->getV()(this->getF()(i,j),d) : min_corner[d];
(*this->Vptr)((this->F)(i,j),d) < min_corner[d] ?
(*this->Vptr)((this->F)(i,j),d) : min_corner[d];
max_corner[d] =
this->getV()(this->getF()(i,j),d) > max_corner[d] ?
this->getV()(this->getF()(i,j),d) : max_corner[d];
(*this->Vptr)((this->F)(i,j),d) > max_corner[d] ?
(*this->Vptr)((this->F)(i,j),d) : max_corner[d];
}
// This is biased toward vertices incident on more than one face, but
// perhaps that's good
this->center += this->getV().row(this->getF()(i,j));
this->center += (*this->Vptr).row((this->F)(i,j));
}
}
// Average
this->center.array() /= this->getF().size();
this->center.array() /= (this->F).size();
//cout<<"min_corner: "<<this->min_corner.transpose()<<endl;
//cout<<"Center: "<<this->center.transpose()<<endl;
@@ -302,32 +309,29 @@ inline void igl::WindingNumberAABB<Point,DerivedV,DerivedF>::compute_min_max_cor
this->radius = (max_corner-min_corner).norm()/2.0;
}
template <typename Point, typename DerivedV, typename DerivedF>
inline typename DerivedV::Scalar
igl::WindingNumberAABB<Point,DerivedV,DerivedF>::max_abs_winding_number(const Point & p) const
template <typename Scalar, typename Index>
inline Scalar
igl::WindingNumberAABB<Scalar,Index>::max_abs_winding_number(const Point & p) const
{
using namespace std;
// Only valid if not inside
if(inside(p))
{
return numeric_limits<typename DerivedV::Scalar>::infinity();
return std::numeric_limits<Scalar>::infinity();
}
// Q: we know the total positive area so what's the most this could project
// to? Remember it could be layered in the same direction.
return numeric_limits<typename DerivedV::Scalar>::infinity();
return std::numeric_limits<Scalar>::infinity();
}
template <typename Point, typename DerivedV, typename DerivedF>
inline typename DerivedV::Scalar
igl::WindingNumberAABB<Point,DerivedV,DerivedF>::max_simple_abs_winding_number(
template <typename Scalar, typename Index>
inline Scalar
igl::WindingNumberAABB<Scalar,Index>::max_simple_abs_winding_number(
const Point & p) const
{
using namespace std;
using namespace Eigen;
// Only valid if not inside
if(inside(p))
{
return numeric_limits<typename DerivedV::Scalar>::infinity();
return std::numeric_limits<Scalar>::infinity();
}
// Max simple is the same as sum of positive winding number contributions of
// bounding box
@@ -335,10 +339,10 @@ inline typename DerivedV::Scalar
// begin precomputation
//MatrixXd BV((int)pow(2,3),3);
typedef
Eigen::Matrix<typename DerivedV::Scalar,Eigen::Dynamic,Eigen::Dynamic>
Eigen::Matrix<Scalar,Eigen::Dynamic,Eigen::Dynamic>
MatrixXS;
typedef
Eigen::Matrix<typename DerivedF::Scalar,Eigen::Dynamic,Eigen::Dynamic>
Eigen::Matrix<Index,Eigen::Dynamic,Eigen::Dynamic>
MatrixXF;
MatrixXS BV((int)(1<<3),3);
BV <<
@@ -383,16 +387,4 @@ inline typename DerivedV::Scalar
return igl::winding_number(BV,PBF,p);
}
// This is a bullshit template because AABB annoyingly needs templates for bad
// combinations of 3D V with DIM=2 AABB
//
// _Define_ as a no-op rather than monkeying around with the proper code above
namespace igl
{
template <> inline igl::WindingNumberAABB<Eigen::Matrix<double, 1, 3, 1, 1, 3>,Eigen::Matrix<double, -1, 2, 0, -1, 2>,Eigen::Matrix<int, -1, 2, 0, -1, 2>>::WindingNumberAABB(const Eigen::MatrixBase<Eigen::Matrix<double, -1, 2, 0, -1, 2>> & , const Eigen::MatrixBase<Eigen::Matrix<int, -1, 2, 0, -1, 2>> & ){};
template <> inline void igl::WindingNumberAABB<Eigen::Matrix<double, 1, 3, 1, 1, 3>,Eigen::Matrix<double, -1, 2, 0, -1, 2>,Eigen::Matrix<int, -1, 2, 0, -1, 2>>::grow(){};
template <> inline void igl::WindingNumberAABB<Eigen::Matrix<double, 1, 3, 1, 1, 3>,Eigen::Matrix<double, -1, 2, 0, -1, 2>,Eigen::Matrix<int, -1, 2, 0, -1, 2>>::init(){};
}
#endif
+259
View File
@@ -0,0 +1,259 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2026 Philip Trettner <trettner@shapedcode.com>, Cedric Martens <cedric.martens@umontreal.ca>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#ifndef IGL_WINDINGNUMBERANTIPODALSCENE_H
#define IGL_WINDINGNUMBERANTIPODALSCENE_H
#include "PI.h"
#include "parallel_for.h"
#include <Eigen/Core>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <unordered_map>
#include <vector>
namespace igl
{
/// Precomputed scene for the Antipodal Method generalized winding number
///
/// The scene stores only the weighted open-boundary edges of the input
/// triangle mesh and a fixed antipodal reference direction `x0`. Closed
/// (manifold) meshes have an empty boundary and the fractional term is
/// exactly zero.
///
/// Querying the winding number additionally requires an `Intersector` that
/// returns the signed ray-mesh crossing count along `(p, x0)` over the
/// original mesh; the scene itself is intersector-agnostic. See
/// `igl::embree::EmbreeIntersector::signedIntersectionsRay` for an
/// optimized concrete implementation.
///
/// ### Intersector concept
/// A type `I` satisfies the concept when it exposes:
/// - `using OriginType = ...;` (3D row vector type)
/// - `using DirectionType = ...;` (3D row vector type)
/// - `int signedIntersectionsRay(
/// OriginType origin, DirectionType direction,
/// /* defaulted tnear, tfar, mask */) const;`
///
/// `winding_number` casts query point/direction to the intersector's types
/// at the call site, so a `double`-precision scene against a float-only
/// intersector works without an adaptor.
template <typename Scalar>
class WindingNumberAntipodalScene
{
public:
using Point = Eigen::Matrix<Scalar, 1, 3>;
using Direction = Eigen::Matrix<Scalar, 1, 3>;
private:
struct WeightedSeg
{
Point a;
Point b;
Scalar w;
};
public:
/// Build a scene from a 3D triangle mesh.
///
/// @param[in] V #V by 3 list of vertex positions
/// @param[in] F #F by 3 list of triangle indices
/// @param[in] x0 unit reference direction (defaults to a fixed non-axis-aligned vector)
template <typename DerivedV, typename DerivedF>
WindingNumberAntipodalScene(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F,
const Direction & x0 = default_x0())
: m_x0(x0), m_face_count(static_cast<size_t>(F.rows()))
{
assert(V.cols() == 3 && "WindingNumberAntipodalScene: only 3D vertex positions are supported");
assert(F.cols() == 3 && "WindingNumberAntipodalScene: only triangle meshes are supported");
build_boundary_segments(V, F, m_boundary);
}
/// Single-point query: full generalized winding number at `p`
/// (fractional + signed integer crossings).
template <typename Intersector, typename Derivedp>
Scalar winding_number(
const Intersector & intersector,
const Eigen::MatrixBase<Derivedp> & p) const
{
const Point pp(static_cast<Scalar>(p(0)),
static_cast<Scalar>(p(1)),
static_cast<Scalar>(p(2)));
const Direction x1 = -m_x0;
Scalar area = Scalar(0);
for (const auto & ws : m_boundary)
{
const Point v0 = ws.a - pp;
const Point v1 = ws.b - pp;
area += ws.w * half_solid_angle_unorm(x1, v0, v1);
}
const Scalar frac = area / (Scalar(2) * Scalar(igl::PI));
using IO = typename Intersector::OriginType;
using ID = typename Intersector::DirectionType;
const IO io(static_cast<typename IO::Scalar>(pp(0)),
static_cast<typename IO::Scalar>(pp(1)),
static_cast<typename IO::Scalar>(pp(2)));
const ID id(static_cast<typename ID::Scalar>(m_x0(0)),
static_cast<typename ID::Scalar>(m_x0(1)),
static_cast<typename ID::Scalar>(m_x0(2)));
const int c = intersector.signedIntersectionsRay(io, id);
return frac + Scalar(c);
}
/// Batch query, parallelized via `igl::parallel_for`.
///
/// @param[in] intersector Concept-compatible intersector built over the
/// same mesh used to construct the scene. Must
/// be safe to query concurrently.
/// @param[in] O #O by 3 list of query points
/// @param[out] W #O by 1 list of winding numbers
template <typename Intersector, typename DerivedO, typename DerivedW>
void winding_number(
const Intersector & intersector,
const Eigen::MatrixBase<DerivedO> & O,
Eigen::PlainObjectBase<DerivedW> & W) const
{
W.resize(O.rows(), 1);
// Adaptive parallel-for threshold.
//
// The libigl thread pool has a roughly fixed ~1 ms TOTAL overhead per
// parallel_for invocation (not per iteration). So we only spawn the
// pool when the WHOLE batch is expected to take ≥ 1 ms.
//
// Per-query work heuristic:
// t_q ≈ 50 * B + 100 * sqrt(F) ns
// with B = boundary segment count, F = triangle count. Pool wins once
// t_q * O > 10^6 ns ⇒ O > 10^6 / t_q
// which is exactly parallel_for's `min_parallel` semantics.
//
// (This is a rough heuristic and should be revisited once parallel_for becomes lower-overhead)
const double t_q_ns =
50.0 * static_cast<double>(m_boundary.size()) +
100.0 * std::sqrt(static_cast<double>(m_face_count));
const size_t min_parallel = static_cast<size_t>(
std::ceil(1.0e6 / std::max(t_q_ns, 1.0)));
igl::parallel_for(O.rows(), [&](const int o)
{
W(o) = winding_number(intersector, O.row(o));
}, min_parallel);
}
/// Reference direction `x0` used to evaluate this scene.
const Direction & x0() const { return m_x0; }
/// Number of weighted boundary edge segments.
size_t num_boundary_segments() const { return m_boundary.size(); }
/// Number of triangles in the original mesh.
size_t num_faces() const { return m_face_count; }
/// Default reference direction: normalize(1, sqrt(2), sqrt(3)). A fixed
/// non-axis-aligned unit vector; any unit vector works per the paper;
/// this choice avoids accidental alignment with axis-aligned geometry.
static Direction default_x0()
{
Direction d(Scalar(1),
Scalar(std::sqrt(2.0)),
Scalar(std::sqrt(3.0)));
return d / d.norm();
}
/// Half the signed solid angle subtended by the spherical triangle
/// (x1, v0, v1) at the origin, via the unnormalized
/// Van Oosterom-Strackee formula. `x1` is expected to be a unit vector
/// (the antipodal "south pole" `-x0`); `v0`, `v1` need not be normalized.
/// Callers accumulate per-edge contributions and divide by 2π.
static Scalar half_solid_angle_unorm(
const Direction & x1, const Point & v0, const Point & v1)
{
const Scalar l0 = v0.norm();
const Scalar l1 = v1.norm();
const Scalar num = x1.dot(v0.cross(v1));
const Scalar denom = l0 * l1
+ l1 * x1.dot(v0)
+ l0 * x1.dot(v1)
+ v0.dot(v1);
return std::atan2(num, denom);
}
/// Extract the open boundary as oriented, weighted edge segments.
///
/// Each undirected edge {min, max} accumulates +1 for every triangle
/// that traverses it as min→max and -1 for every traversal max→min.
/// Interior edges of an oriented manifold cancel to zero and are
/// dropped. Surviving edges are emitted with positive weight; the
/// segment direction is flipped when the net count is negative so the
/// weight is always > 0. Non-manifold (≥3 incident triangles per edge)
/// is handled the same way. The surviving net count becomes the weight.
template <typename DerivedV, typename DerivedF>
static void build_boundary_segments(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F,
std::vector<WeightedSeg> & out)
{
out.clear();
std::unordered_map<std::uint64_t, int> counts;
counts.reserve(static_cast<size_t>(F.rows()) * 3);
const auto pack = [](int lo, int hi) -> std::uint64_t
{
return (static_cast<std::uint64_t>(static_cast<std::uint32_t>(lo)) << 32)
| static_cast<std::uint64_t>(static_cast<std::uint32_t>(hi));
};
for (Eigen::Index t = 0; t < F.rows(); ++t)
{
const int tri[3] = {
static_cast<int>(F(t, 0)),
static_cast<int>(F(t, 1)),
static_cast<int>(F(t, 2))
};
for (int e = 0; e < 3; ++e)
{
const int u = tri[e];
const int v = tri[(e + 1) % 3];
const int lo = u < v ? u : v;
const int hi = u < v ? v : u;
const std::uint64_t k = pack(lo, hi);
counts[k] += (u < v) ? +1 : -1;
}
}
out.reserve(counts.size());
for (const auto & kv : counts)
{
const int sum = kv.second;
if (sum == 0) continue;
const std::uint32_t lo = static_cast<std::uint32_t>(kv.first >> 32);
const std::uint32_t hi = static_cast<std::uint32_t>(kv.first & 0xFFFFFFFFu);
const Point a(static_cast<Scalar>(V(lo, 0)),
static_cast<Scalar>(V(lo, 1)),
static_cast<Scalar>(V(lo, 2)));
const Point b(static_cast<Scalar>(V(hi, 0)),
static_cast<Scalar>(V(hi, 1)),
static_cast<Scalar>(V(hi, 2)));
WeightedSeg ws;
if (sum > 0) { ws.a = a; ws.b = b; ws.w = static_cast<Scalar>(sum); }
else { ws.a = b; ws.b = a; ws.w = static_cast<Scalar>(-sum); }
out.push_back(ws);
}
}
std::vector<WeightedSeg> m_boundary;
Direction m_x0;
size_t m_face_count = 0;
};
}
#endif
+105 -137
View File
@@ -11,73 +11,68 @@
#include <map>
#include <Eigen/Dense>
#include "WindingNumberMethod.h"
#include <cassert>
#include <memory>
namespace igl
{
/// Space partitioning tree for computing winding number hierarchically.
///
/// @tparam Point type for points in space, e.g. Eigen::Vector3d
template <
typename Point,
typename DerivedV,
typename DerivedF >
typename Scalar,
typename Index>
class WindingNumberTree
{
public:
using Point = Eigen::Matrix<Scalar,1,3>;
// Method to use (see enum above)
//static double min_max_w;
static std::map<
std::pair<const WindingNumberTree*,const WindingNumberTree*>,
typename DerivedV::Scalar>
Scalar>
cached;
// This is only need to fill in references, it should never actually be touched
// and shouldn't cause race conditions. (This is a hack, but I think it's "safe")
static DerivedV dummyV;
protected:
WindingNumberMethod method;
const WindingNumberTree * parent;
std::list<WindingNumberTree * > children;
typedef
Eigen::Matrix<typename DerivedV::Scalar,Eigen::Dynamic,Eigen::Dynamic>
Eigen::Matrix<Scalar,Eigen::Dynamic,Eigen::Dynamic>
MatrixXS;
typedef
Eigen::Matrix<typename DerivedF::Scalar,Eigen::Dynamic,Eigen::Dynamic>
Eigen::Matrix<Index,Eigen::Dynamic,Eigen::Dynamic>
MatrixXF;
//// List of boundary edges (recall edges are vertices in 2d)
//const Eigen::MatrixXi boundary;
// Base mesh vertices
DerivedV & V;
// Base mesh vertices with duplicates removed
// Base mesh vertices with duplicates removed (root will fill this in and
// then everyone's Vptr will point to it.
MatrixXS SV;
// Shared pointer to base mesh vertices
std::shared_ptr<MatrixXS> Vptr;
// Facets in this bounding volume
MatrixXF F;
// Tessellated boundary curve
MatrixXF cap;
// Upper Bound on radius of enclosing ball
typename DerivedV::Scalar radius;
Scalar radius;
// (Approximate) center (of mass)
Point center;
public:
inline WindingNumberTree();
// For root
template <typename DerivedV, typename DerivedF>
inline WindingNumberTree(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F);
// For chilluns
inline WindingNumberTree(
const WindingNumberTree<Point,DerivedV,DerivedF> & parent,
const Eigen::MatrixBase<DerivedF> & F);
const WindingNumberTree<Scalar,Index> & parent,
const typename igl::WindingNumberTree<Scalar,Index>::MatrixXF & F);
inline virtual ~WindingNumberTree();
inline void delete_children();
inline virtual void set_mesh(
template <typename DerivedV, typename DerivedF>
inline void set_mesh(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F);
// Set method
inline void set_method( const WindingNumberMethod & m);
public:
inline const DerivedV & getV() const;
inline const MatrixXF & getF() const;
inline const MatrixXF & getcap() const;
// Grow the Tree recursively
inline virtual void grow();
// Determine whether a given point is inside the bounding
@@ -92,12 +87,12 @@ namespace igl
// Inputs:
// p query point
// Returns winding number
inline typename DerivedV::Scalar winding_number(const Point & p) const;
inline Scalar winding_number(const Point & p) const;
// Same as above, but always computes winding number using exact method
// (sum over every facet)
inline typename DerivedV::Scalar winding_number_all(const Point & p) const;
inline Scalar winding_number_all(const Point & p) const;
// Same as above, but always computes using sum over tessllated boundary
inline typename DerivedV::Scalar winding_number_boundary(const Point & p) const;
inline Scalar winding_number_boundary(const Point & p) const;
//// Same as winding_number above, but if max_simple_abs_winding_number is
//// less than some threshold min_max_w just return 0 (colloquially the "fast
//// multipole method)
@@ -120,10 +115,10 @@ namespace igl
// Inputs:
// p query point
// Returns max winding number of
inline virtual typename DerivedV::Scalar max_abs_winding_number(const Point & p) const;
inline virtual Scalar max_abs_winding_number(const Point & p) const;
// Same as above, but stronger assumptions on (V,F). Assumes (V,F) is a
// simple polyhedron
inline virtual typename DerivedV::Scalar max_simple_abs_winding_number(const Point & p) const;
inline virtual Scalar max_simple_abs_winding_number(const Point & p) const;
// Compute or read cached winding number for point p with respect to mesh
// in bounding box, recursing according to approximation criteria
//
@@ -131,7 +126,7 @@ namespace igl
// p query point
// that WindingNumberTree containing mesh w.r.t. which we're computing w.n.
// Returns cached winding number
inline virtual typename DerivedV::Scalar cached_winding_number(const WindingNumberTree & that, const Point & p) const;
inline virtual Scalar cached_winding_number(const WindingNumberTree & that, const Point & p) const;
};
}
@@ -148,83 +143,89 @@ namespace igl
#include <iostream>
#include <limits>
//template <typename Point, typename DerivedV, typename DerivedF>
//WindingNumberMethod WindingNumberTree<Point,DerivedV,DerivedF>::method = EXACT_WINDING_NUMBER_METHOD;
//template <typename Point, typename DerivedV, typename DerivedF>
//double WindingNumberTree<Point,DerivedV,DerivedF>::min_max_w = 0;
template <typename Point, typename DerivedV, typename DerivedF>
std::map< std::pair<const igl::WindingNumberTree<Point,DerivedV,DerivedF>*,const igl::WindingNumberTree<Point,DerivedV,DerivedF>*>, typename DerivedV::Scalar>
igl::WindingNumberTree<Point,DerivedV,DerivedF>::cached;
//template <typename Scalar, typename Index>
//WindingNumberMethod WindingNumberTree<Scalar,Index>::method = EXACT_WINDING_NUMBER_METHOD;
//template <typename Scalar, typename Index>
//double WindingNumberTree<Scalar,Index>::min_max_w = 0;
template <typename Scalar, typename Index>
std::map< std::pair<const igl::WindingNumberTree<Scalar,Index>*,const igl::WindingNumberTree<Scalar,Index>*>, Scalar>
igl::WindingNumberTree<Scalar,Index>::cached;
template <typename Point, typename DerivedV, typename DerivedF>
inline igl::WindingNumberTree<Point,DerivedV,DerivedF>::WindingNumberTree():
template <typename Scalar, typename Index>
inline igl::WindingNumberTree<Scalar,Index>::WindingNumberTree():
method(EXACT_WINDING_NUMBER_METHOD),
parent(NULL),
V(dummyV),
SV(),
F(),
cap(),
radius(std::numeric_limits<typename DerivedV::Scalar>::infinity()),
radius(std::numeric_limits<Scalar>::infinity()),
center(0,0,0)
{
}
template <typename Point, typename DerivedV, typename DerivedF>
inline igl::WindingNumberTree<Point,DerivedV,DerivedF>::WindingNumberTree(
template <typename Scalar, typename Index>
template <typename DerivedV, typename DerivedF>
inline igl::WindingNumberTree<Scalar,Index>::WindingNumberTree(
const Eigen::MatrixBase<DerivedV> & _V,
const Eigen::MatrixBase<DerivedF> & _F):
method(EXACT_WINDING_NUMBER_METHOD),
parent(NULL),
V(dummyV),
SV(),
F(),
cap(),
radius(std::numeric_limits<typename DerivedV::Scalar>::infinity()),
radius(std::numeric_limits<Scalar>::infinity()),
center(0,0,0)
{
set_mesh(_V,_F);
}
template <typename Point, typename DerivedV, typename DerivedF>
inline void igl::WindingNumberTree<Point,DerivedV,DerivedF>::set_mesh(
template <typename Scalar, typename Index>
template <typename DerivedV, typename DerivedF>
inline void igl::WindingNumberTree<Scalar,Index>::set_mesh(
const Eigen::MatrixBase<DerivedV> & _V,
const Eigen::MatrixBase<DerivedF> & _F)
{
using namespace std;
// Remove any exactly duplicate vertices
// Q: Can this ever increase the complexity of the boundary?
// Q: Would we gain even more by remove almost exactly duplicate vertices?
Eigen::Matrix<typename MatrixXF::Scalar,Eigen::Dynamic,1> SVI,SVJ;
igl::remove_duplicate_vertices(_V,_F,0.0,SV,SVI,SVJ,F);
triangle_fan(igl::exterior_edges(F),cap);
V = SV;
{
Eigen::Matrix<typename MatrixXF::Scalar,Eigen::Dynamic,2> EE;
igl::exterior_edges(F,EE);
triangle_fan(EE,cap);
}
// point Vptr to SV
Vptr = std::make_shared<MatrixXS>(SV);
}
template <typename Point, typename DerivedV, typename DerivedF>
inline igl::WindingNumberTree<Point,DerivedV,DerivedF>::WindingNumberTree(
const igl::WindingNumberTree<Point,DerivedV,DerivedF> & parent,
const Eigen::MatrixBase<DerivedF> & _F):
template <typename Scalar, typename Index>
inline igl::WindingNumberTree<Scalar,Index>::WindingNumberTree(
const igl::WindingNumberTree<Scalar,Index> & parent,
const typename igl::WindingNumberTree<Scalar,Index>::MatrixXF & _F):
method(parent.method),
parent(&parent),
V(parent.V),
Vptr(parent.Vptr),
SV(),
F(_F),
cap(triangle_fan(igl::exterior_edges(_F)))
cap()
{
Eigen::Matrix<typename MatrixXF::Scalar,Eigen::Dynamic,2> EE;
igl::exterior_edges(F,EE);
triangle_fan(EE,cap);
}
template <typename Point, typename DerivedV, typename DerivedF>
inline igl::WindingNumberTree<Point,DerivedV,DerivedF>::~WindingNumberTree<Point,DerivedV,DerivedF>()
template <typename Scalar, typename Index>
inline igl::WindingNumberTree<Scalar,Index>::~WindingNumberTree()
{
delete_children();
}
template <typename Point, typename DerivedV, typename DerivedF>
inline void igl::WindingNumberTree<Point,DerivedV,DerivedF>::delete_children()
template <typename Scalar, typename Index>
inline void igl::WindingNumberTree<Scalar,Index>::delete_children()
{
using namespace std;
// Delete children
typename list<WindingNumberTree<Point,DerivedV,DerivedF>* >::iterator cit = children.begin();
typename std::list<WindingNumberTree<Scalar,Index>* >::iterator cit = children.begin();
while(cit != children.end())
{
// clear the memory of this item
@@ -234,8 +235,8 @@ inline void igl::WindingNumberTree<Point,DerivedV,DerivedF>::delete_children()
}
}
template <typename Point, typename DerivedV, typename DerivedF>
inline void igl::WindingNumberTree<Point,DerivedV,DerivedF>::set_method(const WindingNumberMethod & m)
template <typename Scalar, typename Index>
inline void igl::WindingNumberTree<Scalar,Index>::set_method(const WindingNumberMethod & m)
{
this->method = m;
for(auto child : children)
@@ -244,44 +245,23 @@ inline void igl::WindingNumberTree<Point,DerivedV,DerivedF>::set_method(const Wi
}
}
template <typename Point, typename DerivedV, typename DerivedF>
inline const DerivedV & igl::WindingNumberTree<Point,DerivedV,DerivedF>::getV() const
{
return V;
}
template <typename Point, typename DerivedV, typename DerivedF>
inline const typename igl::WindingNumberTree<Point,DerivedV,DerivedF>::MatrixXF&
igl::WindingNumberTree<Point,DerivedV,DerivedF>::getF() const
{
return F;
}
template <typename Point, typename DerivedV, typename DerivedF>
inline const typename igl::WindingNumberTree<Point,DerivedV,DerivedF>::MatrixXF&
igl::WindingNumberTree<Point,DerivedV,DerivedF>::getcap() const
{
return cap;
}
template <typename Point, typename DerivedV, typename DerivedF>
inline void igl::WindingNumberTree<Point,DerivedV,DerivedF>::grow()
template <typename Scalar, typename Index>
inline void igl::WindingNumberTree<Scalar,Index>::grow()
{
// Don't grow
return;
}
template <typename Point, typename DerivedV, typename DerivedF>
inline bool igl::WindingNumberTree<Point,DerivedV,DerivedF>::inside(const Point & /*p*/) const
template <typename Scalar, typename Index>
inline bool igl::WindingNumberTree<Scalar,Index>::inside(const Point & /*p*/) const
{
return true;
}
template <typename Point, typename DerivedV, typename DerivedF>
inline typename DerivedV::Scalar
igl::WindingNumberTree<Point,DerivedV,DerivedF>::winding_number(const Point & p) const
template <typename Scalar, typename Index>
inline Scalar
igl::WindingNumberTree<Scalar,Index>::winding_number(const Point & p) const
{
using namespace std;
//cout<<"+"<<boundary.rows();
// If inside then we need to be careful
if(inside(p))
@@ -290,9 +270,9 @@ igl::WindingNumberTree<Point,DerivedV,DerivedF>::winding_number(const Point & p)
if(children.size()>0)
{
// Recurse on each child and accumulate
typename DerivedV::Scalar sum = 0;
Scalar sum = 0;
for(
typename list<WindingNumberTree<Point,DerivedV,DerivedF>* >::const_iterator cit = children.begin();
typename std::list<WindingNumberTree<Scalar,Index>* >::const_iterator cit = children.begin();
cit != children.end();
cit++)
{
@@ -330,7 +310,7 @@ igl::WindingNumberTree<Point,DerivedV,DerivedF>::winding_number(const Point & p)
return winding_number_boundary(p);
case APPROX_SIMPLE_WINDING_NUMBER_METHOD:
{
typename DerivedV::Scalar dist = (p-center).norm();
Scalar dist = (p-center).norm();
// Radius is already an overestimate of inside
if(dist>1.0*radius)
{
@@ -355,24 +335,22 @@ igl::WindingNumberTree<Point,DerivedV,DerivedF>::winding_number(const Point & p)
return 0;
}
template <typename Point, typename DerivedV, typename DerivedF>
inline typename DerivedV::Scalar
igl::WindingNumberTree<Point,DerivedV,DerivedF>::winding_number_all(const Point & p) const
template <typename Scalar, typename Index>
inline Scalar
igl::WindingNumberTree<Scalar,Index>::winding_number_all(const Point & p) const
{
return igl::winding_number(V,F,p);
return igl::winding_number(*Vptr,F,p);
}
template <typename Point, typename DerivedV, typename DerivedF>
inline typename DerivedV::Scalar
igl::WindingNumberTree<Point,DerivedV,DerivedF>::winding_number_boundary(const Point & p) const
template <typename Scalar, typename Index>
inline Scalar
igl::WindingNumberTree<Scalar,Index>::winding_number_boundary(const Point & p) const
{
using namespace Eigen;
using namespace std;
return igl::winding_number(V,cap,p);
return igl::winding_number(*Vptr,cap,p);
}
//template <typename Point, typename DerivedV, typename DerivedF>
//inline double igl::WindingNumberTree<Point,DerivedV,DerivedF>::winding_number_approx_simple(
//template <typename Scalar, typename Index>
//inline double igl::WindingNumberTree<Scalar,Index>::winding_number_approx_simple(
// const Point & p,
// const double min_max_w)
//{
@@ -387,46 +365,43 @@ igl::WindingNumberTree<Point,DerivedV,DerivedF>::winding_number_boundary(const P
// }
//}
template <typename Point, typename DerivedV, typename DerivedF>
inline void igl::WindingNumberTree<Point,DerivedV,DerivedF>::print(const char * tab)
template <typename Scalar, typename Index>
inline void igl::WindingNumberTree<Scalar,Index>::print(const char * tab)
{
using namespace std;
// Print all facets
cout<<tab<<"["<<endl<<F<<endl<<"]";
std::cout<<tab<<"["<<std::endl<<F<<std::endl<<"]";
// Print children
for(
typename list<WindingNumberTree<Point,DerivedV,DerivedF>* >::iterator cit = children.begin();
typename std::list<WindingNumberTree<Scalar,Index>* >::iterator cit = children.begin();
cit != children.end();
cit++)
{
cout<<","<<endl;
(*cit)->print((string(tab)+"").c_str());
std::cout<<","<<std::endl;
(*cit)->print((std::string(tab)+"").c_str());
}
}
template <typename Point, typename DerivedV, typename DerivedF>
inline typename DerivedV::Scalar
igl::WindingNumberTree<Point,DerivedV,DerivedF>::max_abs_winding_number(const Point & /*p*/) const
template <typename Scalar, typename Index>
inline Scalar
igl::WindingNumberTree<Scalar,Index>::max_abs_winding_number(const Point & /*p*/) const
{
return std::numeric_limits<typename DerivedV::Scalar>::infinity();
return std::numeric_limits<Scalar>::infinity();
}
template <typename Point, typename DerivedV, typename DerivedF>
inline typename DerivedV::Scalar
igl::WindingNumberTree<Point,DerivedV,DerivedF>::max_simple_abs_winding_number(
template <typename Scalar, typename Index>
inline Scalar
igl::WindingNumberTree<Scalar,Index>::max_simple_abs_winding_number(
const Point & /*p*/) const
{
using namespace std;
return numeric_limits<typename DerivedV::Scalar>::infinity();
return std::numeric_limits<Scalar>::infinity();
}
template <typename Point, typename DerivedV, typename DerivedF>
inline typename DerivedV::Scalar
igl::WindingNumberTree<Point,DerivedV,DerivedF>::cached_winding_number(
const igl::WindingNumberTree<Point,DerivedV,DerivedF> & that,
template <typename Scalar, typename Index>
inline Scalar
igl::WindingNumberTree<Scalar,Index>::cached_winding_number(
const igl::WindingNumberTree<Scalar,Index> & that,
const Point & p) const
{
using namespace std;
// Simple metric for `is_far`
//
// this that
@@ -447,7 +422,7 @@ igl::WindingNumberTree<Point,DerivedV,DerivedF>::cached_winding_number(
bool is_far = this->radius<that.radius;
if(is_far)
{
typename DerivedV::Scalar a = atan2(
Scalar a = atan2(
that.radius - this->radius,
(that.center - this->center).norm());
assert(a>0);
@@ -457,7 +432,7 @@ igl::WindingNumberTree<Point,DerivedV,DerivedF>::cached_winding_number(
if(is_far)
{
// Not implemented yet
pair<const WindingNumberTree*,const WindingNumberTree*> this_that(this,&that);
std::pair<const WindingNumberTree*,const WindingNumberTree*> this_that(this,&that);
// Need to compute it for first time?
if(cached.count(this_that)==0)
{
@@ -472,7 +447,7 @@ igl::WindingNumberTree<Point,DerivedV,DerivedF>::cached_winding_number(
}else
{
for(
typename list<WindingNumberTree<Point,DerivedV,DerivedF>* >::const_iterator cit = children.begin();
typename std::list<WindingNumberTree<Scalar,Index>* >::const_iterator cit = children.begin();
cit != children.end();
cit++)
{
@@ -490,11 +465,4 @@ igl::WindingNumberTree<Point,DerivedV,DerivedF>::cached_winding_number(
return 0;
}
// Explicit instantiation of static variable
template <
typename Point,
typename DerivedV,
typename DerivedF >
DerivedV igl::WindingNumberTree<Point,DerivedV,DerivedF>::dummyV;
#endif
+35 -28
View File
@@ -11,7 +11,10 @@
#include "slice_into.h"
#include "cat.h"
//#include "matlab_format.h"
#include "placeholders.h"
#include "PlainMatrix.h"
#include <cassert>
#include <iostream>
#include <limits>
#include <algorithm>
@@ -31,15 +34,15 @@ template <
>
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::MatrixBase<DerivedB> & B,
const Eigen::MatrixBase<Derivedknown> & known,
const Eigen::MatrixBase<DerivedY> & Y,
const Eigen::SparseMatrix<AeqT>& Aeq,
const Eigen::PlainObjectBase<DerivedBeq> & Beq,
const Eigen::MatrixBase<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 Eigen::MatrixBase<DerivedBieq> & Bieq,
const Eigen::MatrixBase<Derivedlx> & p_lx,
const Eigen::MatrixBase<Derivedux> & p_ux,
const igl::active_set_params & params,
Eigen::PlainObjectBase<DerivedZ> & Z
)
@@ -49,8 +52,6 @@ IGL_INLINE igl::SolverStatus igl::active_set(
#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");
@@ -73,7 +74,7 @@ IGL_INLINE igl::SolverStatus igl::active_set(
if(p_lx.size() == 0)
{
lx = Derivedlx::Constant(
n,1,-numeric_limits<typename Derivedlx::Scalar>::max());
n,1,-std::numeric_limits<typename Derivedlx::Scalar>::max());
}else
{
lx = p_lx;
@@ -81,7 +82,7 @@ IGL_INLINE igl::SolverStatus igl::active_set(
if(p_ux.size() == 0)
{
ux = Derivedux::Constant(
n,1,numeric_limits<typename Derivedux::Scalar>::max());
n,1,std::numeric_limits<typename Derivedux::Scalar>::max());
}else
{
ux = p_ux;
@@ -105,12 +106,12 @@ IGL_INLINE igl::SolverStatus igl::active_set(
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);
Eigen::Matrix<BOOL,Eigen::Dynamic,1> as_lx = Eigen::Matrix<BOOL,Eigen::Dynamic,1>::Constant(n,1,FALSE);
Eigen::Matrix<BOOL,Eigen::Dynamic,1> as_ux = Eigen::Matrix<BOOL,Eigen::Dynamic,1>::Constant(n,1,FALSE);
Eigen::Matrix<BOOL,Eigen::Dynamic,1> as_ieq = Eigen::Matrix<BOOL,Eigen::Dynamic,1>::Constant(Aieq.rows(),1,FALSE);
// Keep track of previous Z for comparison
DerivedZ old_Z;
PlainMatrix<DerivedZ> old_Z;
int iter = 0;
while(true)
@@ -148,7 +149,7 @@ IGL_INLINE igl::SolverStatus igl::active_set(
}
if(Aieq.rows() > 0)
{
DerivedZ AieqZ;
PlainMatrix<DerivedZ,Eigen::Dynamic> AieqZ;
AieqZ = Aieq*Z;
for(int a = 0;a<Aieq.rows();a++)
{
@@ -202,7 +203,7 @@ IGL_INLINE igl::SolverStatus igl::active_set(
// PREPARE FIXED VALUES
Eigen::Matrix<typename Derivedknown::Scalar,Eigen::Dynamic,1> known_i;
known_i.resize(nk + as_lx_count + as_ux_count,1);
DerivedY Y_i;
PlainMatrix<DerivedY,Eigen::Dynamic,1> Y_i;
Y_i.resize(nk + as_lx_count + as_ux_count,1);
{
known_i.block(0,0,known.rows(),known.cols()) = known;
@@ -235,7 +236,7 @@ IGL_INLINE igl::SolverStatus igl::active_set(
// 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;
PlainMatrix<DerivedBeq,Eigen::Dynamic,1> Beq_i;
Beq_i.resize(Beq.rows()+as_ieq_count,1);
Beq_i.head(Beq.rows()) = Beq;
{
@@ -253,7 +254,7 @@ IGL_INLINE igl::SolverStatus igl::active_set(
assert(k == as_ieq_count);
}
// extract active constraint rows
SparseMatrix<AeqT> Aeq_i,Aieq_i;
Eigen::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);
@@ -263,7 +264,7 @@ IGL_INLINE igl::SolverStatus igl::active_set(
#ifndef NDEBUG
{
// NO DUPES!
Matrix<BOOL,Dynamic,1> fixed = Matrix<BOOL,Dynamic,1>::Constant(n,1,FALSE);
Eigen::Matrix<BOOL ,Eigen::Dynamic,1> fixed = Eigen::Matrix<BOOL ,Eigen::Dynamic,1>::Constant(n,1,FALSE);
for(int k = 0;k<known_i.size();k++)
{
assert(!fixed[known_i(k)]);
@@ -272,7 +273,7 @@ IGL_INLINE igl::SolverStatus igl::active_set(
}
#endif
DerivedZ sol;
PlainMatrix<DerivedZ,Eigen::Dynamic,Eigen::Dynamic> sol;
if(known_i.size() == A.rows())
{
// Everything's fixed?
@@ -280,7 +281,7 @@ IGL_INLINE igl::SolverStatus igl::active_set(
cout<<" everything's fixed."<<endl;
#endif
Z.resize(A.rows(),Y_i.cols());
Z(known_i,Eigen::all) = Y_i;
Z(known_i,igl::placeholders::all) = Y_i;
sol.resize(0,Y_i.cols());
assert(Aeq_i.rows() == 0 && "All fixed but linearly constrained");
}else
@@ -290,11 +291,15 @@ IGL_INLINE igl::SolverStatus igl::active_set(
#endif
if(!min_quad_with_fixed_precompute(A,known_i,Aeq_i,params.Auu_pd,data))
{
#ifdef ACTIVE_SET_CPP_DEBUG
cerr<<"Error: min_quad_with_fixed precomputation failed."<<endl;
#endif
if(iter > 0 && Aeq_i.rows() > Aeq.rows())
{
#ifdef ACTIVE_SET_CPP_DEBUG
cerr<<" *Are you sure rows of [Aeq;Aieq] are linearly independent?*"<<
endl;
#endif
}
ret = SOLVER_STATUS_ERROR;
break;
@@ -304,7 +309,9 @@ IGL_INLINE igl::SolverStatus igl::active_set(
#endif
if(!min_quad_with_fixed_solve(data,B,Y_i,Beq_i,Z,sol))
{
#ifdef ACTIVE_SET_CPP_DEBUG
cerr<<"Error: min_quad_with_fixed solve failed."<<endl;
#endif
ret = SOLVER_STATUS_ERROR;
break;
}
@@ -318,18 +325,18 @@ IGL_INLINE igl::SolverStatus igl::active_set(
}
// Compute Lagrange multiplier values for known_i
SparseMatrix<AT> Ak;
Eigen::SparseMatrix<AT> Ak;
// Slow
slice(A,known_i,1,Ak);
//slice(B,known_i,Bk);
DerivedB Bk = B(known_i,Eigen::all);
MatrixXd Lambda_known_i = -(0.5*Ak*Z + 0.5*Bk);
PlainMatrix<DerivedB,Eigen::Dynamic> Bk = B(known_i,igl::placeholders::all);
Eigen::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);
Eigen::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);
@@ -375,6 +382,6 @@ IGL_INLINE igl::SolverStatus igl::active_set(
#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> >&);
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::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::SparseMatrix<double, 0, int> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::SparseMatrix<double, 0, int> 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&, 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::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::SparseMatrix<double, 0, int> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::SparseMatrix<double, 0, int> 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&, igl::active_set_params const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
#endif
+7 -7
View File
@@ -70,15 +70,15 @@ namespace igl
>
IGL_INLINE igl::SolverStatus active_set(
const Eigen::SparseMatrix<AT>& A,
const Eigen::PlainObjectBase<DerivedB> & B,
const Eigen::PlainObjectBase<Derivedknown> & known,
const Eigen::PlainObjectBase<DerivedY> & Y,
const Eigen::MatrixBase<DerivedB> & B,
const Eigen::MatrixBase<Derivedknown> & known,
const Eigen::MatrixBase<DerivedY> & Y,
const Eigen::SparseMatrix<AeqT>& Aeq,
const Eigen::PlainObjectBase<DerivedBeq> & Beq,
const Eigen::MatrixBase<DerivedBeq> & Beq,
const Eigen::SparseMatrix<AieqT>& Aieq,
const Eigen::PlainObjectBase<DerivedBieq> & Bieq,
const Eigen::PlainObjectBase<Derivedlx> & lx,
const Eigen::PlainObjectBase<Derivedux> & ux,
const Eigen::MatrixBase<DerivedBieq> & Bieq,
const Eigen::MatrixBase<Derivedlx> & lx,
const Eigen::MatrixBase<Derivedux> & ux,
const igl::active_set_params & params,
Eigen::PlainObjectBase<DerivedZ> & Z
);
+7 -9
View File
@@ -9,6 +9,7 @@
#include "verbose.h"
#include <cassert>
#include <vector>
template <typename DerivedF, typename T>
@@ -16,12 +17,10 @@ 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;
typedef Eigen::Triplet<T> IJV;
std::vector<IJV > ijv;
ijv.reserve(F.size()*2);
// Loop over **simplex** (i.e., **not quad**)
for(int i = 0;i<F.rows();i++)
@@ -71,11 +70,8 @@ IGL_INLINE void igl::adjacency_matrix(
const Eigen::MatrixBase<DerivedC> & C,
Eigen::SparseMatrix<T>& A)
{
using namespace std;
using namespace Eigen;
typedef Triplet<T> IJV;
vector<IJV > ijv;
typedef Eigen::Triplet<T> IJV;
std::vector<IJV > ijv;
ijv.reserve(C(C.size()-1)*2);
typedef typename DerivedI::Scalar Index;
const Index n = I.maxCoeff()+1;
@@ -116,6 +112,8 @@ IGL_INLINE void igl::adjacency_matrix(
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
// generated by autoexplicit.sh
template void igl::adjacency_matrix<Eigen::Matrix<int, -1, 3, 1, -1, 3>, int>(Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 1, -1, 3>> const&, Eigen::SparseMatrix<int, 0, int>&);
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>&);
+2 -2
View File
@@ -30,10 +30,10 @@ namespace igl
/// SparseVector<double> Asum;
/// sum(A,1,Asum);
/// // Convert row sums into diagonal of sparse matrix
/// SparseMatrix<double> Adiag;
/// Eigen::SparseMatrix<double> Adiag;
/// diag(Asum,Adiag);
/// // Build uniform laplacian
/// SparseMatrix<double> U;
/// Eigen::SparseMatrix<double> U;
/// U = A-Adiag;
/// \endcode
///
+1
View File
@@ -7,6 +7,7 @@
// obtain one at http://mozilla.org/MPL/2.0/.
#include "all_pairs_distances.h"
#include <Eigen/Dense>
#include <cassert>
template <typename Mat>
IGL_INLINE void igl::all_pairs_distances(
+1 -1
View File
@@ -15,7 +15,7 @@ namespace igl
///
/// D = all_pairs_distances(V,U)
///
/// @tparam matrix class like MatrixXd
/// @tparam matrix class like Eigen::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
+30 -25
View File
@@ -22,29 +22,31 @@ template <
IGL_INLINE void igl::ambient_occlusion(
const std::function<
bool(
const Eigen::Vector3f&,
const Eigen::Vector3f&)
const Eigen::Matrix<typename DerivedP::Scalar,3,1> &,
const Eigen::Matrix<typename DerivedP::Scalar,3,1> &)
> & 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>();
typedef typename DerivedP::Scalar Scalar;
typedef Eigen::Matrix<Scalar,3,1> Vector3N;
const Eigen::Matrix<Scalar,Eigen::Dynamic,3> D = random_dir_stratified(num_samples).cast<Scalar>();
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>();
const Vector3N origin = P.row(p);
const Vector3N normal = N.row(p);
int num_hits = 0;
for(int s = 0;s<num_samples;s++)
{
Vector3f d = D.row(s);
Vector3N d = D.row(s);
if(d.dot(normal) < 0)
{
// reverse ray
@@ -76,17 +78,19 @@ IGL_INLINE void igl::ambient_occlusion(
const int num_samples,
Eigen::PlainObjectBase<DerivedS> & S)
{
typedef typename DerivedV::Scalar Scalar;
using Vector3S = Eigen::Matrix<Scalar,3,1>;
const auto & shoot_ray = [&aabb,&V,&F](
const Eigen::Vector3f& _s,
const Eigen::Vector3f& dir)->bool
const Eigen::Matrix<Scalar,3,1> & _s,
const Eigen::Matrix<Scalar,3,1> & dir)->bool
{
Eigen::Vector3f s = _s+1e-4*dir;
igl::Hit hit;
Vector3S s = _s+1e-4*dir;
igl::Hit<Scalar> hit;
return aabb.intersect_ray(
V,
F,
s .cast<typename DerivedV::Scalar>().eval(),
dir.cast<typename DerivedV::Scalar>().eval(),
s,
dir,
hit);
};
return ambient_occlusion(shoot_ray,P,N,num_samples,S);
@@ -107,15 +111,17 @@ IGL_INLINE void igl::ambient_occlusion(
const int num_samples,
Eigen::PlainObjectBase<DerivedS> & S)
{
typedef typename DerivedV::Scalar Scalar;
using Vector3S = Eigen::Matrix<Scalar,3,1>;
if(F.rows() < 100)
{
// Super naive
const auto & shoot_ray = [&V,&F](
const Eigen::Vector3f& _s,
const Eigen::Vector3f& dir)->bool
const Eigen::Matrix<Scalar,3,1> & _s,
const Eigen::Matrix<Scalar,3,1> & dir)->bool
{
Eigen::Vector3f s = _s+1e-4*dir;
igl::Hit hit;
Vector3S s = _s+1e-4*dir;
igl::Hit<Scalar> hit;
return ray_mesh_intersect(s,dir,V,F,hit);
};
return ambient_occlusion(shoot_ray,P,N,num_samples,S);
@@ -127,13 +133,12 @@ IGL_INLINE void igl::ambient_occlusion(
#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> >&);
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<double, 3, 1, 0, 3, 1> const&, Eigen::Matrix<double, 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> >&);
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<double, 3, 1, 0, 3, 1> const&, Eigen::Matrix<double, 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> >&);
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<double, 3, 1, 0, 3, 1> const&, Eigen::Matrix<double, 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<double, 3, 1, 0, 3, 1> const&, Eigen::Matrix<double, 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> >&);
template void igl::ambient_occlusion<Eigen::Matrix<float, 1, 3, 1, 1, 3>, Eigen::Matrix<float, 1, 3, 1, 1, 3>, Eigen::Matrix<float, -1, 1, 0, -1, 1>>(std::function<bool (Eigen::Matrix<Eigen::Matrix<float, 1, 3, 1, 1, 3>::Scalar, 3, 1, 0, 3, 1> const&, Eigen::Matrix<Eigen::Matrix<float, 1, 3, 1, 1, 3>::Scalar, 3, 1, 0, 3, 1> const&)> const&, Eigen::MatrixBase<Eigen::Matrix<float, 1, 3, 1, 1, 3>> const&, Eigen::MatrixBase<Eigen::Matrix<float, 1, 3, 1, 1, 3>> const&, int, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 1, 0, -1, 1>>&);
template void igl::ambient_occlusion<Eigen::Matrix<float, -1, 3, 0, -1, 3>, Eigen::Matrix<float, -1, 3, 0, -1, 3>, Eigen::Matrix<float, -1, 1, 0, -1, 1>>(std::function<bool (Eigen::Matrix<Eigen::Matrix<float, -1, 3, 0, -1, 3>::Scalar, 3, 1, 0, 3, 1> const&, Eigen::Matrix<Eigen::Matrix<float, -1, 3, 0, -1, 3>::Scalar, 3, 1, 0, 3, 1> const&)> const&, Eigen::MatrixBase<Eigen::Matrix<float, -1, 3, 0, -1, 3>> const&, Eigen::MatrixBase<Eigen::Matrix<float, -1, 3, 0, -1, 3>> const&, int, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 1, 0, -1, 1>>&);
template void igl::ambient_occlusion<Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, 1, 0, -1, 1>>(std::function<bool (Eigen::Matrix<Eigen::Matrix<float, -1, -1, 0, -1, -1>::Scalar, 3, 1, 0, 3, 1> const&, Eigen::Matrix<Eigen::Matrix<float, -1, -1, 0, -1, -1>::Scalar, 3, 1, 0, 3, 1> const&)> const&, Eigen::MatrixBase<Eigen::Matrix<float, -1, -1, 0, -1, -1>> const&, Eigen::MatrixBase<Eigen::Matrix<float, -1, -1, 0, -1, -1>> const&, int, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 1, 0, -1, 1>>&);
#endif
+2 -2
View File
@@ -31,8 +31,8 @@ namespace igl
IGL_INLINE void ambient_occlusion(
const std::function<
bool(
const Eigen::Vector3f&,
const Eigen::Vector3f&)
const Eigen::Matrix<typename DerivedP::Scalar,3,1>&,
const Eigen::Matrix<typename DerivedP::Scalar,3,1>&)
> & shoot_ray,
const Eigen::MatrixBase<DerivedP> & P,
const Eigen::MatrixBase<DerivedN> & N,
+3
View File
@@ -8,6 +8,9 @@
#include "angular_distance.h"
#include "EPS.h"
#include "PI.h"
#include <cassert>
IGL_INLINE double igl::angular_distance(
const Eigen::Quaterniond & A,
const Eigen::Quaterniond & B)
+21 -25
View File
@@ -35,8 +35,6 @@ IGL_INLINE bool igl::arap_precomputation(
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
@@ -53,14 +51,14 @@ IGL_INLINE bool igl::arap_precomputation(
data.dim = dim;
//assert(dim == 3 && "Only 3d supported");
// Defaults
data.f_ext = MatrixXd::Zero(n,data.dim);
data.f_ext = Eigen::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;
typedef Eigen::SparseMatrix<Scalar> SparseMatrixS;
SparseMatrixS ref_map,ref_map_dim;
if(flat)
{
@@ -106,7 +104,7 @@ IGL_INLINE bool igl::arap_precomputation(
// Get group sum scatter matrix, when applied sums all entries of the same
// group according to G
SparseMatrix<double> G_sum;
Eigen::SparseMatrix<double> G_sum;
if(data.G.size() == 0)
{
if(eff_energy == ARAP_ENERGY_TYPE_ELEMENTS)
@@ -122,7 +120,7 @@ IGL_INLINE bool igl::arap_precomputation(
if(eff_energy == ARAP_ENERGY_TYPE_ELEMENTS)
{
Eigen::Matrix<int,Eigen::Dynamic,1> GG;
MatrixXi GF(F.rows(),F.cols());
Eigen::MatrixXi GF(F.rows(),F.cols());
for(int j = 0;j<F.cols();j++)
{
GF.col(j) = data.G(F.col(j));
@@ -133,7 +131,7 @@ IGL_INLINE bool igl::arap_precomputation(
//printf("group_sum_matrix()\n");
group_sum_matrix(data.G,G_sum);
}
SparseMatrix<double> G_sum_dim;
Eigen::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();
@@ -146,24 +144,24 @@ IGL_INLINE bool igl::arap_precomputation(
}
assert(data.K.rows() == data.n*data.dim);
SparseMatrix<double> Q = (-L).eval();
Eigen::SparseMatrix<double> Q = (-L).eval();
if(data.with_dynamics)
{
const double h = data.h;
assert(h != 0);
SparseMatrix<double> M;
Eigen::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;
Eigen::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);
data.f_ext = Eigen::MatrixXd::Zero(n,data.dim);
data.vel = Eigen::MatrixXd::Zero(n,data.dim);
}
return min_quad_with_fixed_precompute(
Q,b,SparseMatrix<double>(),true,data.solver_data);
Q,b,Eigen::SparseMatrix<double>(),true,data.solver_data);
}
template <
@@ -174,8 +172,6 @@ IGL_INLINE bool igl::arap_solve(
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");
@@ -185,9 +181,9 @@ IGL_INLINE bool igl::arap_solve(
const int n = data.n;
int iter = 0;
// changes each arap iteration
MatrixXd U_prev = U;
Eigen::MatrixXd U_prev = U;
// doesn't change for fixed with_dynamics timestep
MatrixXd U0;
Eigen::MatrixXd U0;
if(data.with_dynamics)
{
U0 = U_prev;
@@ -204,13 +200,13 @@ IGL_INLINE bool igl::arap_solve(
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;
Eigen::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());
Eigen::MatrixXd R(Rdim,data.CSM.rows());
if(R.rows() == 2)
{
fit_rotations_planar(S,R);
@@ -225,14 +221,14 @@ IGL_INLINE bool igl::arap_solve(
}
//for(int k = 0;k<(data.CSM.rows()/dim);k++)
//{
// R.block(0,dim*k,dim,dim) = MatrixXd::Identity(dim,dim);
// R.block(0,dim*k,dim,dim) = Eigen::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;
Eigen::MatrixXd eff_R;
if(data.G.size() == 0)
{
// copy...
@@ -247,7 +243,7 @@ IGL_INLINE bool igl::arap_solve(
}
}
MatrixXd Dl;
Eigen::MatrixXd Dl;
if(data.with_dynamics)
{
assert(data.M.rows() == n &&
@@ -263,13 +259,13 @@ IGL_INLINE bool igl::arap_solve(
Dl = dw * (1./(h*h)*data.M*(-U0 - h*data.vel) - data.f_ext);
}
VectorXd Rcol;
Eigen::VectorXd Rcol;
columnize(eff_R,num_rots,2,Rcol);
VectorXd Bcol = -data.K * Rcol;
Eigen::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;
Eigen::VectorXd Uc,Bc,bcc,Beq;
Bc = Bcol.block(c*n,0,n,1);
if(data.with_dynamics)
{
+29 -32
View File
@@ -66,8 +66,7 @@ IGL_INLINE bool igl::arap_dof_precomputation(
const Eigen::Matrix<int,Eigen::Dynamic,1> & G,
ArapDOFData<LbsMatrixType, SSCALAR> & data)
{
using namespace Eigen;
typedef Matrix<SSCALAR, Dynamic, Dynamic> MatrixXS;
typedef Eigen::Matrix<SSCALAR, Eigen::Dynamic, Eigen::Dynamic> MatrixXS;
// number of mesh (domain) vertices
int n = V.rows();
// cache problem size
@@ -95,11 +94,11 @@ IGL_INLINE bool igl::arap_dof_precomputation(
//printf("n=%d; dim=%d; m=%d;\n",n,data.dim,data.m);
// Build cotangent laplacian
SparseMatrix<double> Lcot;
Eigen::SparseMatrix<double> Lcot;
//printf("cotmatrix()\n");
cotmatrix(V,F,Lcot);
// Discrete laplacian (should be minus matlab version)
SparseMatrix<double> Lapl = -2.0*Lcot;
Eigen::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),"<<
@@ -108,7 +107,7 @@ IGL_INLINE bool igl::arap_dof_precomputation(
// Get group sum scatter matrix, when applied sums all entries of the same
// group according to G
SparseMatrix<double> G_sum;
Eigen::SparseMatrix<double> G_sum;
if(G.size() == 0)
{
speye(n,G_sum);
@@ -118,7 +117,7 @@ IGL_INLINE bool igl::arap_dof_precomputation(
Eigen::Matrix<int,Eigen::Dynamic,1> GG;
if(data.energy == ARAP_ENERGY_TYPE_ELEMENTS)
{
MatrixXi GF(F.rows(),F.cols());
Eigen::MatrixXi GF(F.rows(),F.cols());
for(int j = 0;j<F.cols();j++)
{
GF.col(j) = G(F.col(j));
@@ -140,7 +139,7 @@ IGL_INLINE bool igl::arap_dof_precomputation(
// Get covariance scatter matrix, when applied collects the covariance matrices
// used to fit rotations to during optimization
SparseMatrix<double> CSM;
Eigen::SparseMatrix<double> CSM;
//printf("covariance_scatter_matrix()\n");
covariance_scatter_matrix(V,F,data.energy,CSM);
#ifdef EXTREME_VERBOSE
@@ -167,7 +166,7 @@ IGL_INLINE bool igl::arap_dof_precomputation(
// S((k-1)*dim + 1:dim,:)
// Apply group sum to each dimension's block of covariance scatter matrix
SparseMatrix<double> G_sum_dim;
Eigen::SparseMatrix<double> G_sum_dim;
repdiag(G_sum,data.dim,G_sum_dim);
CSM = (G_sum_dim * CSM).eval();
#ifdef EXTREME_VERBOSE
@@ -205,7 +204,7 @@ IGL_INLINE bool igl::arap_dof_precomputation(
//printf("CSM_M(): slice\n");
#if __cplusplus >= 201703L
// Check if LbsMatrixType is a sparse matrix
if constexpr (std::is_base_of<SparseMatrixBase<LbsMatrixType>, LbsMatrixType>::value)
if constexpr (std::is_base_of<Eigen::SparseMatrixBase<LbsMatrixType>, LbsMatrixType>::value)
{
slice(M,(span_n.array()+i*n).matrix().eval(),span_mlbs_cols,M_i);
}
@@ -214,7 +213,7 @@ IGL_INLINE bool igl::arap_dof_precomputation(
M_i = M((span_n.array()+i*n).eval(),span_mlbs_cols);
}
#else
constexpr bool LbsMatrixTypeIsSparse = std::is_base_of<SparseMatrixBase<LbsMatrixType>, LbsMatrixType>::value;
constexpr bool LbsMatrixTypeIsSparse = std::is_base_of<Eigen::SparseMatrixBase<LbsMatrixType>, LbsMatrixType>::value;
arap_dof_slice_helper<LbsMatrixType,LbsMatrixTypeIsSparse>::slice(M,(span_n.array()+i*n).matrix().eval(),span_mlbs_cols,M_i);
#endif
LbsMatrixType M_i_dim;
@@ -222,7 +221,7 @@ IGL_INLINE bool igl::arap_dof_precomputation(
assert(data.CSM_M[i].cols() == M.cols());
for(int j = 0;j<data.dim;j++)
{
SparseMatrix<double> CSMj;
Eigen::SparseMatrix<double> CSMj;
//printf("CSM_M(): slice\n");
slice(
CSM,
@@ -236,7 +235,7 @@ IGL_INLINE bool igl::arap_dof_precomputation(
{
// Convert to full
//printf("CSM_M(): full\n");
MatrixXd CSMjM_ifull(CSMjM_i);
Eigen::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(),
@@ -256,7 +255,7 @@ IGL_INLINE bool igl::arap_dof_precomputation(
// precompute arap_rhs matrix
//printf("arap_rhs()\n");
SparseMatrix<double> K;
Eigen::SparseMatrix<double> K;
arap_rhs(V,F,V.cols(),data.energy,K);
//#ifdef EXTREME_VERBOSE
// cout<<"KIJV=["<<endl;print_ijv(K,1);cout<<endl<<"];"<<
@@ -264,8 +263,8 @@ IGL_INLINE bool igl::arap_dof_precomputation(
// 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;
Eigen::SparseMatrix<double> G_sumT = G_sum.transpose();
Eigen::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
@@ -278,7 +277,7 @@ IGL_INLINE bool igl::arap_dof_precomputation(
// Precompute system matrix
//printf("A()\n");
SparseMatrix<double> A;
Eigen::SparseMatrix<double> A;
repdiag(Lapl,data.dim,A);
data.Q = MT * (A * M);
//#ifdef EXTREME_VERBOSE
@@ -291,19 +290,19 @@ IGL_INLINE bool igl::arap_dof_precomputation(
//if(data.with_dynamics)
//{
// Build cotangent laplacian
SparseMatrix<double> Mass;
Eigen::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;
Eigen::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);
Eigen::MatrixXd ones(data.dim*data.n,data.dim);
for(int i = 0;i<data.n;i++)
{
for(int d = 0;d<data.dim;d++)
@@ -531,8 +530,7 @@ IGL_INLINE bool igl::arap_dof_recomputation(
const Eigen::SparseMatrix<double> & A_eq,
ArapDOFData<LbsMatrixType, SSCALAR> & data)
{
using namespace Eigen;
typedef Matrix<SSCALAR, Dynamic, Dynamic> MatrixXS;
typedef Eigen::Matrix<SSCALAR, Eigen::Dynamic, Eigen::Dynamic> MatrixXS;
LbsMatrixType * Q;
LbsMatrixType Qdyn;
@@ -577,9 +575,9 @@ IGL_INLINE bool igl::arap_dof_recomputation(
// Compute dense solve matrix (alternative of matrix factorization)
//printf("kkt_inverse()\n");
MatrixXd Qfull(*Q);
MatrixXd A_eqfull(A_eq);
MatrixXd M_Solve;
Eigen::MatrixXd Qfull(*Q);
Eigen::MatrixXd A_eqfull(A_eq);
Eigen::MatrixXd M_Solve;
double timer0_start = get_seconds();
bool use_lu = data.effective_dim != 2;
@@ -646,8 +644,7 @@ IGL_INLINE bool igl::arap_dof_update(
Eigen::MatrixXd & L
)
{
using namespace Eigen;
typedef Matrix<SSCALAR, Dynamic, Dynamic> MatrixXS;
typedef Eigen::Matrix<SSCALAR, Eigen::Dynamic, Eigen::Dynamic> MatrixXS;
#ifdef ARAP_GLOBAL_TIMING
double timer_start = get_seconds();
#endif
@@ -700,9 +697,9 @@ IGL_INLINE bool igl::arap_dof_update(
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> L0SSCALAR = L0.cast<SSCALAR>();
Matrix<SSCALAR,Dynamic,1> B_eq_fix_SSCALAR = L0SSCALAR(data.fixed_dim);
Eigen::Matrix<SSCALAR ,Eigen::Dynamic,1> B_eq_SSCALAR = B_eq.cast<SSCALAR>();
Eigen::Matrix<SSCALAR ,Eigen::Dynamic,1> L0SSCALAR = L0.cast<SSCALAR>();
Eigen::Matrix<SSCALAR ,Eigen::Dynamic,1> B_eq_fix_SSCALAR = L0SSCALAR(data.fixed_dim);
//MatrixXS rhsFull(Rcol.rows() + B_eq.rows() + B_eq_fix_SSCALAR.rows(), 1);
MatrixXS Lsep(data.m*(data.dim + 1), 3);
@@ -831,13 +828,13 @@ IGL_INLINE bool igl::arap_dof_update(
( (-1.0/(data.h*data.h)) * data.L0.array() +
(1.0/(data.h)) * data.Lvel0.array()
).matrix();
MatrixXd temp_d = temp.template cast<double>();
Eigen::MatrixXd temp_d = temp.template cast<double>();
MatrixXd temp_g = data.fgrav*(data.grav_mag*data.grav_dir);
Eigen::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>();
Eigen::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();
+22 -27
View File
@@ -9,6 +9,7 @@
#include "verbose.h"
#include "cotmatrix_entries.h"
#include <Eigen/Dense>
#include <cassert>
template <typename MatV, typename MatF, typename MatK>
IGL_INLINE void igl::arap_linear_block(
@@ -45,16 +46,14 @@ IGL_INLINE void igl::arap_linear_block_spokes(
{
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;
Eigen::Matrix<int ,Eigen::Dynamic,2> edges;
Kd.resize(V.rows(), V.rows());
vector<Triplet<Scalar> > Kd_IJV;
std::vector<Eigen::Triplet<Scalar> > Kd_IJV;
if(simplex_size == 3)
{
// triangles
@@ -80,7 +79,7 @@ IGL_INLINE void igl::arap_linear_block_spokes(
3,2;
}
// gather cotangent weights
Matrix<Scalar,Dynamic,Dynamic> C;
Eigen::Matrix<Scalar ,Eigen::Dynamic ,Eigen::Dynamic> C;
cotmatrix_entries(V,F,C);
// should have weights for each edge
assert(C.cols() == edges.rows());
@@ -93,10 +92,10 @@ IGL_INLINE void igl::arap_linear_block_spokes(
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_IJV.push_back(Eigen::Triplet<Scalar>(source,dest,v));
Kd_IJV.push_back(Eigen::Triplet<Scalar>(dest,source,-v));
Kd_IJV.push_back(Eigen::Triplet<Scalar>(source,source,v));
Kd_IJV.push_back(Eigen::Triplet<Scalar>(dest,dest,-v));
}
}
Kd.setFromTriplets(Kd_IJV.begin(),Kd_IJV.end());
@@ -112,16 +111,14 @@ IGL_INLINE void igl::arap_linear_block_spokes_and_rims(
{
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;
std::vector<Eigen::Triplet<Scalar> > Kd_IJV;
Eigen::Matrix<int ,Eigen::Dynamic,2> edges;
if(simplex_size == 3)
{
// triangles
@@ -149,7 +146,7 @@ IGL_INLINE void igl::arap_linear_block_spokes_and_rims(
assert(false);
}
// gather cotangent weights
Matrix<Scalar,Dynamic,Dynamic> C;
Eigen::Matrix<Scalar ,Eigen::Dynamic ,Eigen::Dynamic> C;
cotmatrix_entries(V,F,C);
// should have weights for each edge
assert(C.cols() == edges.rows());
@@ -169,18 +166,18 @@ IGL_INLINE void igl::arap_linear_block_spokes_and_rims(
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));
Kd_IJV.push_back(Eigen::Triplet<Scalar>(Rs,Rd,v));
Kd_IJV.push_back(Eigen::Triplet<Scalar>(Rd,Rs,-v));
}else if(Rd == source)
{
Kd_IJV.push_back(Triplet<Scalar>(Rd,Rs,v));
Kd_IJV.push_back(Eigen::Triplet<Scalar>(Rd,Rs,v));
}else if(Rs == dest)
{
Kd_IJV.push_back(Triplet<Scalar>(Rs,Rd,-v));
Kd_IJV.push_back(Eigen::Triplet<Scalar>(Rs,Rd,-v));
}
}
Kd_IJV.push_back(Triplet<Scalar>(source,source,v));
Kd_IJV.push_back(Triplet<Scalar>(dest,dest,-v));
Kd_IJV.push_back(Eigen::Triplet<Scalar>(source,source,v));
Kd_IJV.push_back(Eigen::Triplet<Scalar>(dest,dest,-v));
}
}
Kd.setFromTriplets(Kd_IJV.begin(),Kd_IJV.end());
@@ -195,16 +192,14 @@ IGL_INLINE void igl::arap_linear_block_elements(
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;
std::vector<Eigen::Triplet<Scalar> > Kd_IJV;
Eigen::Matrix<int ,Eigen::Dynamic,2> edges;
if(simplex_size == 3)
{
// triangles
@@ -230,7 +225,7 @@ IGL_INLINE void igl::arap_linear_block_elements(
3,2;
}
// gather cotangent weights
Matrix<Scalar,Dynamic,Dynamic> C;
Eigen::Matrix<Scalar ,Eigen::Dynamic ,Eigen::Dynamic> C;
cotmatrix_entries(V,F,C);
// should have weights for each edge
assert(C.cols() == edges.rows());
@@ -243,8 +238,8 @@ IGL_INLINE void igl::arap_linear_block_elements(
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_IJV.push_back(Eigen::Triplet<Scalar>(source,i,v));
Kd_IJV.push_back(Eigen::Triplet<Scalar>(dest,i,-v));
}
}
Kd.setFromTriplets(Kd_IJV.begin(),Kd_IJV.end());
+4 -5
View File
@@ -10,6 +10,7 @@
#include "verbose.h"
#include "repdiag.h"
#include "cat.h"
#include <cassert>
#include <iostream>
template<typename DerivedV, typename DerivedF, typename DerivedK>
@@ -20,8 +21,6 @@ IGL_INLINE void igl::arap_rhs(
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
@@ -49,7 +48,7 @@ IGL_INLINE void igl::arap_rhs(
return;
}
DerivedK KX,KY,KZ;
Eigen::SparseMatrix<typename DerivedK::Scalar> KX,KY,KZ;
arap_linear_block(V,F,0,energy,KX);
arap_linear_block(V,F,1,energy,KY);
if(Vdim == 2)
@@ -63,7 +62,7 @@ IGL_INLINE void igl::arap_rhs(
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());
Eigen::SparseMatrix<typename DerivedK::Scalar> ZZ(KX.rows()*2,KX.cols());
K = cat(2,cat(2,
cat(2,repdiag(KX,dim),ZZ),
cat(2,repdiag(KY,dim),ZZ)),
@@ -92,4 +91,4 @@ IGL_INLINE void igl::arap_rhs(
#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
#endif
@@ -7,6 +7,8 @@
// obtain one at http://mozilla.org/MPL/2.0/.
#include "average_from_edges_onto_vertices.h"
#include <cassert>
template<typename DerivedF,typename DerivedE,typename DerivedoE,
typename DeriveduE,typename DeriveduV>
IGL_INLINE void
@@ -15,9 +15,9 @@ namespace igl
/// 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[in] E #F by 3 mapping from each halfedge to each edge
/// @param[in] oE #F by 3 orientation as generated by orient_halfedges
/// @param[in] uE #uE by 1 list of scalars
/// @param[out] uV #V by 1 list of scalar defined on vertices
///
/// \see orient_halfedges
+3 -3
View File
@@ -17,9 +17,9 @@ namespace igl
{
/// 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
/// @tparam DerivedV derived from vertex positions matrix type: i.e. Eigen::MatrixXd
/// @tparam DerivedF derived from face indices matrix type: i.e. Eigen::MatrixXi
/// @tparam DerivedL derived from edge lengths matrix type: i.e. Eigen::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
+1
View File
@@ -60,4 +60,5 @@ template void igl::barycenter<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::M
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> >&);
template void igl::barycenter<Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, -1, 0, -1, -1>>(Eigen::MatrixBase<Eigen::Matrix<float, -1, -1, 0, -1, -1>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>> const&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, -1, 0, -1, -1>>&);
#endif
+3 -3
View File
@@ -8,6 +8,8 @@
#include "barycentric_coordinates.h"
#include "volume.h"
#include <cassert>
template <
typename DerivedP,
typename DerivedA,
@@ -23,7 +25,6 @@ IGL_INLINE void igl::barycentric_coordinates(
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");
@@ -33,7 +34,7 @@ IGL_INLINE void igl::barycentric_coordinates(
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>
typedef Eigen::Matrix<typename DerivedL::Scalar,DerivedL::RowsAtCompileTime,1>
VectorXS;
// Total volume
VectorXS vol,LA,LB,LC,LD;
@@ -60,7 +61,6 @@ IGL_INLINE void igl::barycentric_coordinates(
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");
+26 -25
View File
@@ -26,9 +26,8 @@ igl::BBWData::BBWData():
void igl::BBWData::print()
{
using namespace std;
cout<<"partition_unity: "<<partition_unity<<endl;
cout<<"W0=["<<endl<<W0<<endl<<"];"<<endl;
std::cout<<"partition_unity: "<<partition_unity<<std::endl;
std::cout<<"W0=["<<std::endl<<W0<<std::endl<<"];"<<std::endl;
}
@@ -39,16 +38,14 @@ template <
typename Derivedbc,
typename DerivedW>
IGL_INLINE bool igl::bbw(
const Eigen::PlainObjectBase<DerivedV> & V,
const Eigen::PlainObjectBase<DerivedEle> & Ele,
const Eigen::PlainObjectBase<Derivedb> & b,
const Eigen::PlainObjectBase<Derivedbc> & bc,
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<Derivedb> & b,
const Eigen::MatrixBase<Derivedbc> & bc,
igl::BBWData & data,
Eigen::PlainObjectBase<DerivedW> & W
)
{
using namespace std;
using namespace Eigen;
assert(!data.partition_unity && "partition_unity not implemented yet");
// number of domain vertices
int n = V.rows();
@@ -59,23 +56,23 @@ IGL_INLINE bool igl::bbw(
harmonic(V,Ele,2,Q);
W.derived().resize(n,m);
// No linear terms
VectorXd c = VectorXd::Zero(n);
Eigen::VectorXd c = Eigen::VectorXd::Zero(n);
// No linear constraints
SparseMatrix<typename DerivedW::Scalar> A(0,n),Aeq(0,n),Aieq(0,n);
VectorXd Beq(0,1),Bieq(0,1);
Eigen::SparseMatrix<typename DerivedW::Scalar> A(0,n),Aeq(0,n),Aieq(0,n);
Eigen::VectorXd Beq(0,1),Bieq(0,1);
// Upper and lower box constraints (Constant bounds)
VectorXd ux = VectorXd::Ones(n);
VectorXd lx = VectorXd::Zero(n);
Eigen::VectorXd ux = Eigen::VectorXd::Ones(n);
Eigen::VectorXd lx = Eigen::VectorXd::Zero(n);
active_set_params eff_params = data.active_set_params;
if(data.verbosity >= 1)
{
cout<<"BBW: max_iter: "<<data.active_set_params.max_iter<<endl;
cout<<"BBW: eff_max_iter: "<<eff_params.max_iter<<endl;
std::cout<<"BBW: max_iter: "<<data.active_set_params.max_iter<<std::endl;
std::cout<<"BBW: eff_max_iter: "<<eff_params.max_iter<<std::endl;
}
if(data.verbosity >= 1)
{
cout<<"BBW: Computing initial weights for "<<m<<" handle"<<
(m!=1?"s":"")<<"."<<endl;
std::cout<<"BBW: Computing initial weights for "<<m<<" handle"<<
(m!=1?"s":"")<<"."<<std::endl;
}
min_quad_with_fixed_data<typename DerivedW::Scalar > mqwf;
min_quad_with_fixed_precompute(Q,b,Aeq,true,mqwf);
@@ -95,11 +92,11 @@ IGL_INLINE bool igl::bbw(
if(data.verbosity >= 1)
{
std::lock_guard<std::mutex> lock(critical);
cout<<"BBW: Computing weight for handle "<<i+1<<" out of "<<m<<
"."<<endl;
std::cout<<"BBW: Computing weight for handle "<<i+1<<" out of "<<m<<
"."<<std::endl;
}
VectorXd bci = bc.col(i);
VectorXd Wi;
Eigen::VectorXd bci = bc.col(i);
Eigen::VectorXd Wi;
// use initial guess
Wi = W.col(i);
SolverStatus ret = active_set(
@@ -109,11 +106,15 @@ IGL_INLINE bool igl::bbw(
case SOLVER_STATUS_CONVERGED:
break;
case SOLVER_STATUS_MAX_ITER:
#ifdef IGL_BBW_DEBUG
cerr<<"active_set: max iter without convergence."<<endl;
#endif
break;
case SOLVER_STATUS_ERROR:
default:
#ifdef IGL_BBW_DEBUG
cerr<<"active_set error."<<endl;
#endif
error = true;
}
W.col(i) = Wi;
@@ -129,8 +130,8 @@ IGL_INLINE bool igl::bbw(
const double min_rowsum = W.rowwise().sum().array().abs().minCoeff();
if(min_rowsum < 0.1)
{
cerr<<"bbw.cpp: Warning, minimum row sum is very low. Consider more "
"active set iterations or enforcing partition of unity."<<endl;
std::cerr<<"bbw.cpp: Warning, minimum row sum is very low. Consider more "
"active set iterations or enforcing partition of unity."<<std::endl;
}
#endif
@@ -139,6 +140,6 @@ IGL_INLINE bool igl::bbw(
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
template bool igl::bbw<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::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(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<int, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, igl::BBWData&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
template bool igl::bbw<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::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<int, -1, 1, 0, -1, 1>> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>> const&, igl::BBWData&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>>&);
#endif
+9 -9
View File
@@ -40,11 +40,11 @@ namespace igl
/// Compute Bounded Biharmonic Weights on a given domain (V,Ele) with a given
/// set of boundary conditions
///
/// @tparam DerivedV derived type of eigen matrix for V (e.g. MatrixXd)
/// @tparam DerivedF derived type of eigen matrix for F (e.g. MatrixXi)
/// @tparam Derivedb derived type of eigen matrix for b (e.g. VectorXi)
/// @tparam Derivedbc derived type of eigen matrix for bc (e.g. MatrixXd)
/// @tparam DerivedW derived type of eigen matrix for W (e.g. MatrixXd)
/// @tparam DerivedV derived type of eigen matrix for V (e.g. Eigen::MatrixXd)
/// @tparam DerivedF derived type of eigen matrix for F (e.g. Eigen::MatrixXi)
/// @tparam Derivedb derived type of eigen matrix for b (e.g. Eigen::VectorXi)
/// @tparam Derivedbc derived type of eigen matrix for bc (e.g. Eigen::MatrixXd)
/// @tparam DerivedW derived type of eigen matrix for W (e.g. Eigen::MatrixXd)
/// @param[in] V #V by dim vertex positions
/// @param[in] Ele #Elements by simplex-size list of element indices
/// @param[in] b #b boundary indices into V
@@ -60,10 +60,10 @@ namespace igl
typename Derivedbc,
typename DerivedW>
IGL_INLINE bool bbw(
const Eigen::PlainObjectBase<DerivedV> & V,
const Eigen::PlainObjectBase<DerivedEle> & Ele,
const Eigen::PlainObjectBase<Derivedb> & b,
const Eigen::PlainObjectBase<Derivedbc> & bc,
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<Derivedb> & b,
const Eigen::MatrixBase<Derivedbc> & bc,
BBWData & data,
Eigen::PlainObjectBase<DerivedW> & W);
}
+2 -1
View File
@@ -6,6 +6,7 @@
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "bezier.h"
#include "PlainMatrix.h"
#include <cassert>
// Adapted from main.c accompanying
@@ -19,7 +20,7 @@ IGL_INLINE void igl::bezier(
Eigen::PlainObjectBase<DerivedP> & P)
{
// working local copy
DerivedV Vtemp = V;
PlainMatrix<DerivedV> Vtemp = V;
int degree = Vtemp.rows()-1;
/* Triangle computation */
for (int i = 1; i <= degree; i++)
+6 -8
View File
@@ -17,16 +17,14 @@ IGL_INLINE void igl::bfs_orient(
Eigen::PlainObjectBase<DerivedFF> & FF,
Eigen::PlainObjectBase<DerivedC> & C)
{
using namespace Eigen;
using namespace std;
SparseMatrix<typename DerivedF::Scalar> A;
Eigen::SparseMatrix<typename DerivedF::Scalar> A;
orientable_patches(F,C,A);
// number of faces
const int m = F.rows();
// number of patches
const int num_cc = C.maxCoeff()+1;
VectorXi seen = VectorXi::Zero(m);
Eigen::VectorXi seen = Eigen::VectorXi::Zero(m);
// Edge sets
const int ES[3][2] = {{1,2},{2,0},{0,1}};
@@ -38,7 +36,7 @@ IGL_INLINE void igl::bfs_orient(
// loop over patches
parallel_for(num_cc,[&](const int c)
{
queue<typename DerivedF::Scalar> Q;
std::queue<typename DerivedF::Scalar> Q;
// find first member of patch c
for(int f = 0;f<FF.rows();f++)
{
@@ -59,7 +57,7 @@ IGL_INLINE void igl::bfs_orient(
}
seen(f)++;
// loop over neighbors of f
for(typename SparseMatrix<typename DerivedF::Scalar>::InnerIterator it (A,f); it; ++it)
for(typename Eigen::SparseMatrix<typename DerivedF::Scalar>::InnerIterator it (A,f); it; ++it)
{
// might be some lingering zeros, and skip self-adjacency
if(it.value() != 0 && it.row() != f)
@@ -70,12 +68,12 @@ IGL_INLINE void igl::bfs_orient(
for(int efi = 0;efi<3;efi++)
{
// efi'th edge of face f
Vector2i ef(FF(f,ES[efi][0]),FF(f,ES[efi][1]));
Eigen::Vector2i ef(FF(f,ES[efi][0]),FF(f,ES[efi][1]));
// loop over edges of n
for(int eni = 0;eni<3;eni++)
{
// eni'th edge of face n
Vector2i en(FF(n,ES[eni][0]),FF(n,ES[eni][1]));
Eigen::Vector2i en(FF(n,ES[eni][0]),FF(n,ES[eni][1]));
// Match (half-edges go same direction)
if(ef(0) == en(0) && ef(1) == en(1))
{
+20 -22
View File
@@ -15,6 +15,7 @@
#include "normal_derivative.h"
#include "on_boundary.h"
#include <Eigen/Sparse>
#include <cassert>
template <
typename DerivedV,
@@ -42,30 +43,27 @@ IGL_INLINE bool igl::biharmonic_coordinates(
const int k,
Eigen::PlainObjectBase<DerivedW> & W)
{
using namespace Eigen;
using namespace std;
typedef typename DerivedV::Scalar Scalar;
typedef typename DerivedT::Scalar Integer;
// This is not the most efficient way to build A, but follows "Linear
// Subspace Design for Real-Time Shape Deformation" [Wang et al. 2015].
SparseMatrix<Scalar> A;
Eigen::SparseMatrix<Scalar> A;
{
DiagonalMatrix<Scalar, Dynamic> Minv;
SparseMatrix<Scalar> L, K;
Array<bool,Dynamic,Dynamic> C;
Eigen::DiagonalMatrix<Scalar, Eigen::Dynamic> Minv;
Eigen::SparseMatrix<Scalar> L, K;
Eigen::Array<bool ,Eigen::Dynamic ,Eigen::Dynamic> C;
{
Array<bool,Dynamic,1> I;
Eigen::Array<bool ,Eigen::Dynamic,1> I;
on_boundary(T,I,C);
}
#ifdef false
// Version described in paper is "wrong"
// http://www.cs.toronto.edu/~jacobson/images/error-in-linear-subspace-design-for-real-time-shape-deformation-2017-wang-et-al.pdf
SparseMatrix<Scalar> N, Z, M;
Eigen::SparseMatrix<Scalar> N, Z, M;
normal_derivative(V,T,N);
{
std::vector<Triplet<Scalar>> ZIJV;
std::vector<Eigen::Triplet<Scalar>> ZIJV;
for(int t =0;t<T.rows();t++)
{
for(int f =0;f<T.cols();f++)
@@ -88,13 +86,13 @@ IGL_INLINE bool igl::biharmonic_coordinates(
K = N+L;
massmatrix(V,T,MASSMATRIX_TYPE_DEFAULT,M);
// normalize
M /= ((Matrix<Scalar, Dynamic, 1>)M.diagonal()).array().abs().maxCoeff();
M /= ((Matrix<Scalar, Eigen::Dynamic, 1>)M.diagonal()).array().abs().maxCoeff();
Minv =
((Matrix<Scalar, Dynamic, 1>)M.diagonal().array().inverse()).asDiagonal();
((Matrix<Scalar, Eigen::Dynamic, 1>)M.diagonal().array().inverse()).asDiagonal();
#else
Eigen::SparseMatrix<Scalar> M;
Eigen::Matrix<Integer, Dynamic, Dynamic> E;
Eigen::Matrix<Integer, Dynamic, 1> EMAP;
Eigen::Matrix<Integer, Eigen::Dynamic, Eigen::Dynamic> E;
Eigen::Matrix<Integer, Eigen::Dynamic, 1> EMAP;
crouzeix_raviart_massmatrix(V,T,M,E,EMAP);
crouzeix_raviart_cotmatrix(V,T,E,EMAP,L);
// Ad #E by #V facet-vertex incidence matrix
@@ -111,14 +109,14 @@ IGL_INLINE bool igl::biharmonic_coordinates(
Ad.setFromTriplets(AIJV.begin(),AIJV.end());
}
// Degrees
Eigen::Matrix<Scalar, Dynamic, 1> De;
Eigen::Matrix<Scalar, Eigen::Dynamic, 1> De;
sum(Ad,2,De);
Eigen::DiagonalMatrix<Scalar,Eigen::Dynamic> De_diag =
De.array().inverse().matrix().asDiagonal();
K = L*(De_diag*Ad);
// normalize
M /= ((Matrix<Scalar, Dynamic, 1>)M.diagonal()).array().abs().maxCoeff();
Minv = ((Matrix<Scalar, Dynamic, 1>)M.diagonal().array().inverse()).asDiagonal();
M /= ((Eigen::Matrix<Scalar, Eigen::Dynamic, 1>)M.diagonal()).array().abs().maxCoeff();
Minv = ((Eigen::Matrix<Scalar, Eigen::Dynamic, 1>)M.diagonal().array().inverse()).asDiagonal();
// kill boundary edges
for(int f = 0;f<T.rows();f++)
{
@@ -149,7 +147,7 @@ IGL_INLINE bool igl::biharmonic_coordinates(
}
// Vertices in point handles
const size_t mp =
count_if(S.begin(),S.end(),[](const vector<int> & h){return h.size()==1;});
count_if(S.begin(),S.end(),[](const std::vector<SType> & h){return h.size()==1;});
// number of region handles
const size_t r = S.size()-mp;
// Vertices in region handles
@@ -163,9 +161,9 @@ IGL_INLINE bool igl::biharmonic_coordinates(
}
const size_t dim = T.cols()-1;
// Might as well be dense... I think...
Matrix<Scalar, Dynamic, Dynamic> J = Matrix<Scalar, Dynamic, Dynamic>::Zero(mp+mr,mp+r*(dim+1));
Matrix<Integer, Dynamic, 1> b(mp+mr);
Matrix<Scalar, Dynamic, Dynamic> H(mp+r*(dim+1),dim);
Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> J = Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>::Zero(mp+mr,mp+r*(dim+1));
Eigen::Matrix<Integer, Eigen::Dynamic, 1> b(mp+mr);
Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> H(mp+r*(dim+1),dim);
{
int v = 0;
int c = 0;
@@ -198,7 +196,7 @@ IGL_INLINE bool igl::biharmonic_coordinates(
// minimize ½ W' A W'
// subject to W(b,:) = J
return min_quad_with_fixed(
A,Matrix<Scalar, Dynamic, 1>::Zero(A.rows()).eval(),b,J,SparseMatrix<Scalar>(),Matrix<Scalar, Dynamic, 1>(),true,W);
A,Eigen::Matrix<Scalar, Eigen::Dynamic, 1>::Zero(A.rows()).eval(),b,J,Eigen::SparseMatrix<Scalar>(),Eigen::Matrix<Scalar, Eigen::Dynamic, 1>(),true,W);
}
#ifdef IGL_STATIC_LIBRARY
+2 -2
View File
@@ -39,10 +39,10 @@ namespace igl
/// #### Example:
///
/// \code{cpp}
/// MatrixXd W;
/// Eigen::MatrixXd W;
/// igl::biharmonic_coordinates(V,F,S,W);
/// const size_t dim = T.cols()-1;
/// MatrixXd H(W.cols(),dim);
/// Eigen::MatrixXd H(W.cols(),dim);
/// {
/// int c = 0;
/// for(int h = 0;h<S.size();h++)
@@ -9,7 +9,9 @@
#include "doublearea.h"
#include "harmonic.h"
#include "placeholders.h"
//#include "matlab/MatlabWorkspace.h"
#include <cassert>
#include <iostream>
template <
@@ -50,7 +52,7 @@ IGL_INLINE bool igl::bijective_composite_harmonic_mapping(
assert(F.cols() == 3 && "F should contain triangles");
int nsteps = min_steps;
Eigen::Matrix<typename Derivedbc::Scalar, Eigen::Dynamic, Eigen::Dynamic> bc0 =
V(b.col(0),Eigen::all);
V(b.col(0),igl::placeholders::all);
// It's difficult to check for flips "robustly" in the sense that the input
// mesh might not have positive/consistent sign to begin with.
@@ -81,7 +83,7 @@ IGL_INLINE bool igl::bijective_composite_harmonic_mapping(
//mw.save(bct,"bct");
//mw.write("numerical.mat");
harmonic(Eigen::Matrix<typename DerivedU::Scalar, Eigen::Dynamic, Eigen::Dynamic>(U), F, b, bct, 1, U);
bct = U(b.col(0),Eigen::all);
bct = U(b.col(0),igl::placeholders::all);
nans = (U.array() != U.array()).count();
if(test_for_flips)
{
+7 -4
View File
@@ -9,18 +9,21 @@
#include "doublearea.h"
#include "random_points_on_mesh.h"
#include "sortrows.h"
#include "placeholders.h"
#include "PI.h"
#include "get_seconds.h"
#include <unordered_map>
#include <algorithm>
#include <vector>
#include <random>
#include <cstdint>
#include <cassert>
namespace igl
{
// It is very important that we use 64bit keys to avoid out of bounds (easy to
// get to happen with dense samplings (e.g., r = 0.0005*bbd)
typedef int64_t BlueNoiseKeyType;
typedef std::int64_t BlueNoiseKeyType;
}
// Helper functions
@@ -289,10 +292,10 @@ IGL_INLINE void igl::blue_noise(
{
Eigen::VectorXi I;
igl::sortrows(decltype(Xs)(Xs),true,Xs,I);
X = X(I,Eigen::all).eval();
X = X(I,igl::placeholders::all).eval();
// These two could be spun off in their own thread.
XB = XB(I,Eigen::all).eval();
XFI = XFI(I,Eigen::all).eval();
XB = XB(I,igl::placeholders::all).eval();
XFI = XFI(I,igl::placeholders::all).eval();
}
// Initialization
std::unordered_map<BlueNoiseKeyType,std::vector<int> > M;
+48 -35
View File
@@ -11,38 +11,46 @@
#include "EPS.h"
#include "project_to_line.h"
#include <cassert>
#include <vector>
#include <map>
#include <iostream>
template <
typename DerivedV,
typename DerivedEle,
typename DerivedC,
typename DerivedP,
typename DerivedBE,
typename DerivedCE,
typename DerivedCF,
typename Derivedb,
typename Derivedbc>
IGL_INLINE bool igl::boundary_conditions(
const Eigen::MatrixXd & V ,
const Eigen::MatrixXi & /*Ele*/,
const Eigen::MatrixXd & C ,
const Eigen::VectorXi & P ,
const Eigen::MatrixXi & BE ,
const Eigen::MatrixXi & CE ,
const Eigen::MatrixXi & CF ,
Eigen::VectorXi & b ,
Eigen::MatrixXd & bc )
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedC> & C,
const Eigen::MatrixBase<DerivedP> & P,
const Eigen::MatrixBase<DerivedBE> & BE,
const Eigen::MatrixBase<DerivedCE> & CE,
const Eigen::MatrixBase<DerivedCF> & CF,
Eigen::PlainObjectBase<Derivedb> & b,
Eigen::PlainObjectBase<Derivedbc> & bc)
{
using namespace Eigen;
using namespace std;
if(P.size()+BE.rows() == 0)
{
verbose("^%s: Error: no handles found\n",__FUNCTION__);
return false;
}
vector<int> bci;
vector<int> bcj;
vector<double> bcv;
std::vector<int> bci;
std::vector<int> bcj;
std::vector<double> bcv;
// loop over points
for(int p = 0;p<P.size();p++)
{
VectorXd pos = C.row(P(p));
Eigen::VectorXd pos = C.row(P(p));
// loop over domain vertices
for(int i = 0;i<V.rows();i++)
{
@@ -51,7 +59,7 @@ IGL_INLINE bool igl::boundary_conditions(
// EIGEN GOTCHA:
// double sqrd = (V.row(i)-pos).array().pow(2).sum();
// Must first store in temporary
VectorXd vi = V.row(i);
Eigen::VectorXd vi = V.row(i);
double sqrd = (vi-pos).squaredNorm();
if(sqrd <= FLOAT_EPS)
{
@@ -76,8 +84,8 @@ IGL_INLINE bool igl::boundary_conditions(
for(int i = 0;i<V.rows();i++)
{
// Find samples from tip up to tail
VectorXd tip = C.row(BE(e,0));
VectorXd tail = C.row(BE(e,1));
Eigen::VectorXd tip = C.row(BE(e,0));
Eigen::VectorXd tail = C.row(BE(e,1));
// Compute parameter along bone and squared distance
double t,sqrd;
project_to_line(
@@ -101,8 +109,8 @@ IGL_INLINE bool igl::boundary_conditions(
for(int i = 0;i<V.rows();i++)
{
// Find samples from tip up to tail
VectorXd tip = C.row(P(CE(e,0)));
VectorXd tail = C.row(P(CE(e,1)));
Eigen::VectorXd tip = C.row(P(CE(e,0)));
Eigen::VectorXd tail = C.row(P(CE(e,1)));
// Compute parameter along bone and squared distance
double t,sqrd;
project_to_line(
@@ -122,14 +130,14 @@ IGL_INLINE bool igl::boundary_conditions(
}
}
std::vector<uint8_t> vertices_marked(V.rows(), 0);
std::vector<bool> vertices_marked(V.rows(), false);
// loop over cage faces
for(int f = 0;f<CF.rows();f++)
{
Vector3d v_0 = C.row(P(CF(f, 0)));
Vector3d v_1 = C.row(P(CF(f, 1)));
Vector3d v_2 = C.row(P(CF(f, 2)));
Vector3d n = (v_1 - v_0).cross(v_2 - v_1);
Eigen::Vector3d v_0 = C.row(P(CF(f, 0)));
Eigen::Vector3d v_1 = C.row(P(CF(f, 1)));
Eigen::Vector3d v_2 = C.row(P(CF(f, 2)));
Eigen::Vector3d n = (v_1 - v_0).cross(v_2 - v_1);
n.normalize();
// loop over domain vertices
for (int i = 0;i<V.rows();i++)
@@ -139,13 +147,13 @@ IGL_INLINE bool igl::boundary_conditions(
{
continue;
}
Vector3d point = V.row(i);
Vector3d v = point - v_0;
Eigen::Vector3d point = V.row(i);
Eigen::Vector3d v = point - v_0;
double dist = abs(v.dot(n));
if (dist <= 1.e-1f)
{
//barycentric coordinates
Vector3d vec_0 = v_1 - v_0, vec_1 = v_2 - v_0, vec_2 = point - v_0;
Eigen::Vector3d vec_0 = v_1 - v_0, vec_1 = v_2 - v_0, vec_2 = point - v_0;
double d00 = vec_0.dot(vec_0);
double d01 = vec_0.dot(vec_1);
double d11 = vec_1.dot(vec_1);
@@ -158,7 +166,7 @@ IGL_INLINE bool igl::boundary_conditions(
if (u>=0. && u<=1.0 && v>=0. && v<=1.0 && w >=0. && w<=1.0)
{
vertices_marked[i] = 1;
vertices_marked[i] = true;
bci.push_back(i);
bcj.push_back(CF(f, 0));
bcv.push_back(u);
@@ -174,17 +182,17 @@ IGL_INLINE bool igl::boundary_conditions(
}
// find unique boundary indices
vector<int> vb = bci;
sort(vb.begin(),vb.end());
std::vector<int> vb = bci;
std::sort(vb.begin(),vb.end());
vb.erase(unique(vb.begin(), vb.end()), vb.end());
b.resize(vb.size());
bc = MatrixXd::Zero(vb.size(),P.size()+BE.rows());
bc = Eigen::MatrixXd::Zero(vb.size(),P.size()+BE.rows());
// Map from boundary index to index in boundary
map<int,int> bim;
std::map<int,int> bim;
int i = 0;
// Also fill in b
for(vector<int>::iterator bit = vb.begin();bit != vb.end();bit++)
for(std::vector<int>::iterator bit = vb.begin();bit != vb.end();bit++)
{
b(i) = *bit;
bim[*bit] = i;
@@ -242,3 +250,8 @@ IGL_INLINE bool igl::boundary_conditions(
return true;
}
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
template bool igl::boundary_conditions<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<int, -1, 1, 0, -1, 1>, 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<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::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<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::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>>&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>>&);
#endif
+19 -9
View File
@@ -36,16 +36,26 @@ namespace igl
/// some column of bc doesn't have a 0 (assuming bc has >1 columns)
/// some column of bc doesn't have a 1 (assuming bc has >1 columns)
///
template <
typename DerivedV,
typename DerivedEle,
typename DerivedC,
typename DerivedP,
typename DerivedBE,
typename DerivedCE,
typename DerivedCF,
typename Derivedb,
typename Derivedbc>
IGL_INLINE bool boundary_conditions(
const Eigen::MatrixXd & V,
const Eigen::MatrixXi & Ele,
const Eigen::MatrixXd & C,
const Eigen::VectorXi & P,
const Eigen::MatrixXi & BE,
const Eigen::MatrixXi & CE,
const Eigen::MatrixXi & CF,
Eigen::VectorXi & b,
Eigen::MatrixXd & bc);
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedEle> & Ele,
const Eigen::MatrixBase<DerivedC> & C,
const Eigen::MatrixBase<DerivedP> & P,
const Eigen::MatrixBase<DerivedBE> & BE,
const Eigen::MatrixBase<DerivedCE> & CE,
const Eigen::MatrixBase<DerivedCF> & CF,
Eigen::PlainObjectBase<Derivedb> & b,
Eigen::PlainObjectBase<Derivedbc> & bc);
}
#ifndef IGL_STATIC_LIBRARY
+9 -97
View File
@@ -15,6 +15,7 @@
#include <Eigen/Core>
#include <cassert>
#include <map>
#include <iostream>
@@ -47,21 +48,21 @@ IGL_INLINE void igl::boundary_facets(
for(int i = 0; i< (int)T.rows();i++)
{
// get face in correct order
allF(i*simplex_size+0,0) = T(i,1);
allF(i*simplex_size+0,2) = T(i,1);
allF(i*simplex_size+0,1) = T(i,3);
allF(i*simplex_size+0,2) = T(i,2);
allF(i*simplex_size+0,0) = T(i,2);
// get face in correct order
allF(i*simplex_size+1,0) = T(i,0);
allF(i*simplex_size+1,2) = T(i,0);
allF(i*simplex_size+1,1) = T(i,2);
allF(i*simplex_size+1,2) = T(i,3);
allF(i*simplex_size+1,0) = T(i,3);
// get face in correct order
allF(i*simplex_size+2,0) = T(i,0);
allF(i*simplex_size+2,2) = T(i,0);
allF(i*simplex_size+2,1) = T(i,3);
allF(i*simplex_size+2,2) = T(i,1);
allF(i*simplex_size+2,0) = T(i,1);
// get face in correct order
allF(i*simplex_size+3,0) = T(i,0);
allF(i*simplex_size+3,2) = T(i,0);
allF(i*simplex_size+3,1) = T(i,1);
allF(i*simplex_size+3,2) = T(i,2);
allF(i*simplex_size+3,0) = T(i,2);
}
break;
case 3:
@@ -124,101 +125,12 @@ Ret igl::boundary_facets(
return F;
}
template <typename IntegerT, typename IntegerF>
IGL_INLINE void igl::boundary_facets(
const std::vector<std::vector<IntegerT> > & T,
std::vector<std::vector<IntegerF> > & F)
{
// Kept for legacy reasons. Could probably just delete.
using namespace std;
if(T.size() == 0)
{
F.clear();
return;
}
int simplex_size = T[0].size();
// Get a list of all faces
vector<vector<IntegerF> > allF(
T.size()*simplex_size,
vector<IntegerF>(simplex_size-1));
// Gather faces, loop over tets
for(int i = 0; i< (int)T.size();i++)
{
assert((int)T[i].size() == simplex_size);
switch(simplex_size)
{
case 4:
// get face in correct order
allF[i*simplex_size+0][0] = T[i][1];
allF[i*simplex_size+0][1] = T[i][3];
allF[i*simplex_size+0][2] = T[i][2];
// get face in correct order
allF[i*simplex_size+1][0] = T[i][0];
allF[i*simplex_size+1][1] = T[i][2];
allF[i*simplex_size+1][2] = T[i][3];
// get face in correct order
allF[i*simplex_size+2][0] = T[i][0];
allF[i*simplex_size+2][1] = T[i][3];
allF[i*simplex_size+2][2] = T[i][1];
// get face in correct order
allF[i*simplex_size+3][0] = T[i][0];
allF[i*simplex_size+3][1] = T[i][1];
allF[i*simplex_size+3][2] = T[i][2];
break;
case 3:
allF[i*simplex_size+0][0] = T[i][1];
allF[i*simplex_size+0][1] = T[i][2];
allF[i*simplex_size+1][0] = T[i][2];
allF[i*simplex_size+1][1] = T[i][0];
allF[i*simplex_size+2][0] = T[i][0];
allF[i*simplex_size+2][1] = T[i][1];
break;
}
}
// Counts
vector<int> C;
face_occurrences(allF,C);
// Q: Why not just count the number of ones?
// A: because we are including non-manifold edges as boundary edges
int twos = (int) count(C.begin(),C.end(),2);
//int ones = (int) count(C.begin(),C.end(),1);
// Resize output to fit number of ones
F.resize(allF.size() - twos);
//F.resize(ones);
int k = 0;
for(int i = 0;i< (int)allF.size();i++)
{
if(C[i] != 2)
{
assert(k<(int)F.size());
F[k] = allF[i];
k++;
}
}
assert(k==(int)F.size());
//if(k != F.size())
//{
// printf("%d =? %d\n",k,F.size());
//}
}
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
// generated by autoexplicit.sh
template void igl::boundary_facets<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::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
// generated by autoexplicit.sh
template void igl::boundary_facets<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<unsigned int, -1, 3, 1, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, 3, 1, -1, 3> >&);
// generated by autoexplicit.sh
template void igl::boundary_facets<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 3, 0, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> >&);
template void igl::boundary_facets<int, 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> > > >&);
//template Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > igl::boundary_facets(Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&);
template Eigen::Matrix<int, -1, -1, 0, -1, -1> igl::boundary_facets<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&);
template void igl::boundary_facets<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 3, 1, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> >&);
#endif
+3 -10
View File
@@ -19,7 +19,9 @@ namespace igl
/// (analogous to qptoolbox's `outline` and `boundary_faces`).
///
/// @param[in] T tetrahedron (triangle) index list, m by 4 (3), where m is the number of tetrahedra
/// @param[out] F list of boundary faces, n by 3 (2), where n is the number of boundary faces
/// @param[out] F list of boundary faces, n by 3 (2), where n is the number
/// of boundary faces. Faces are oriented so that igl::centroid(V,F,…)
/// computes the same sign volume as igl::volume(V,T)
/// @param[out] J list of indices into T, n by 1
/// @param[out] K list of indices revealing across from which vertex is this facet
///
@@ -48,15 +50,6 @@ namespace igl
template <typename DerivedT, typename Ret>
Ret boundary_facets(
const Eigen::MatrixBase<DerivedT>& T);
/// Determine boundary faces (edges) of tetrahedra (triangles) stored in T;
/// inputs and outputs lists.
///
/// @param[in] T tetrahedron (triangle) index list, m by 4 (3), where m is the number of tetrahedra
/// @param[out] F list of boundary faces, n by 3 (2), where n is the number of boundary faces
template <typename IntegerT, typename IntegerF>
IGL_INLINE void boundary_facets(
const std::vector<std::vector<IntegerT> > & T,
std::vector<std::vector<IntegerF> > & F);
}
#ifndef IGL_STATIC_LIBRARY
+7 -16
View File
@@ -16,20 +16,17 @@ IGL_INLINE void igl::boundary_loop(
const Eigen::MatrixBase<DerivedF> & F,
std::vector<std::vector<Index> >& L)
{
using namespace std;
using namespace Eigen;
if(F.rows() == 0)
return;
VectorXd Vdummy(F.maxCoeff()+1,1);
Eigen::VectorXd Vdummy(F.maxCoeff()+1,1);
Eigen::Matrix<typename DerivedF::Scalar, Eigen::Dynamic, Eigen::Dynamic> TT,TTi;
vector<std::vector<int> > VF, VFi;
std::vector<std::vector<int> > VF, VFi;
triangle_triangle_adjacency(F,TT,TTi);
vertex_triangle_adjacency(Vdummy,F,VF,VFi);
vector<bool> unvisited = is_border_vertex(F);
set<int> unseen;
std::vector<bool> unvisited = is_border_vertex(F);
std::set<int> unseen;
for (size_t i = 0; i < unvisited.size(); ++i)
{
if (unvisited[i])
@@ -38,7 +35,7 @@ IGL_INLINE void igl::boundary_loop(
while (!unseen.empty())
{
vector<Index> l;
std::vector<Index> l;
// Get first vertex of loop
int start = *unseen.begin();
@@ -93,13 +90,10 @@ IGL_INLINE void igl::boundary_loop(
const Eigen::MatrixBase<DerivedF>& F,
std::vector<Index>& L)
{
using namespace Eigen;
using namespace std;
if(F.rows() == 0)
return;
vector<vector<int> > Lall;
std::vector<std::vector<int> > Lall;
boundary_loop(F,Lall);
int idxMax = -1;
@@ -132,13 +126,10 @@ IGL_INLINE void igl::boundary_loop(
const Eigen::MatrixBase<DerivedF>& F,
Eigen::PlainObjectBase<DerivedL>& L)
{
using namespace Eigen;
using namespace std;
if(F.rows() == 0)
return;
vector<int> Lvec;
std::vector<int> Lvec;
boundary_loop(F,Lvec);
L.resize(Lvec.size(), 1);
+1 -1
View File
@@ -6,6 +6,7 @@
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "bounding_box.h"
#include <cassert>
#include <iostream>
template <typename DerivedV, typename DerivedBV, typename DerivedBF>
@@ -24,7 +25,6 @@ IGL_INLINE void igl::bounding_box(
Eigen::PlainObjectBase<DerivedBV>& BV,
Eigen::PlainObjectBase<DerivedBF>& BF)
{
using namespace std;
const int dim = V.cols();
const auto & minV = V.colwise().minCoeff().array()-pad;
+2 -3
View File
@@ -13,9 +13,8 @@
IGL_INLINE double igl::bounding_box_diagonal(
const Eigen::MatrixXd & V)
{
using namespace Eigen;
VectorXd maxV,minV;
VectorXi maxVI,minVI;
Eigen::VectorXd maxV,minV;
Eigen::VectorXi maxVI,minVI;
igl::max(V,1,maxV,maxVI);
igl::min(V,1,minV,minVI);
return sqrt((maxV-minV).array().square().sum());
+94
View File
@@ -0,0 +1,94 @@
#include "box_faces.h"
#include "AABB.h"
#include <vector>
#include <utility>
template <typename DerivedV, typename DerivedQ>
IGL_INLINE void igl::box_faces(
const Eigen::AlignedBox<typename DerivedV::Scalar,3> & box,
const typename DerivedV::Scalar shrink,
Eigen::PlainObjectBase<DerivedV> & P,
Eigen::PlainObjectBase<DerivedQ> & Q)
{
auto min_corner = box.min();
auto max_corner = box.max();
// shrink by 3%
min_corner = min_corner + shrink*(max_corner-min_corner);
max_corner = max_corner - shrink*(max_corner-min_corner);
P.resize(8,3);
Q.resize(6,4);
int p = 0;
int q = 0;
Q.row(q++) << p+0,p+1,p+2,p+3;
Q.row(q++) << p+0,p+1,p+5,p+4;
Q.row(q++) << p+1,p+2,p+6,p+5;
Q.row(q++) << p+2,p+3,p+7,p+6;
Q.row(q++) << p+3,p+0,p+4,p+7;
Q.row(q++) << p+4,p+5,p+6,p+7;
P.row(p++) = min_corner;
P.row(p++) = Eigen::RowVector3d(max_corner[0],min_corner[1],min_corner[2]);
P.row(p++) = Eigen::RowVector3d(max_corner[0],max_corner[1],min_corner[2]);
P.row(p++) = Eigen::RowVector3d(min_corner[0],max_corner[1],min_corner[2]);
P.row(p++) = Eigen::RowVector3d(min_corner[0],min_corner[1],max_corner[2]);
P.row(p++) = Eigen::RowVector3d(max_corner[0],min_corner[1],max_corner[2]);
P.row(p++) = max_corner;
P.row(p++) = Eigen::RowVector3d(min_corner[0],max_corner[1],max_corner[2]);
}
template <
typename DerivedV,
typename DerivedP,
typename DerivedQ,
typename DerivedD >
IGL_INLINE void igl::box_faces(
const igl::AABB<DerivedV,3> & tree,
Eigen::PlainObjectBase<DerivedP> & P,
Eigen::PlainObjectBase<DerivedQ> & Q,
Eigen::PlainObjectBase<DerivedD> & D)
{
const int num_nodes = tree.size();
P.resize(8*num_nodes,3);
Q.resize(6*num_nodes,4);
D.resize(6*num_nodes);
int d = 0;
int p = 0;
int q = 0;
std::vector<std::pair<const igl::AABB<DerivedV,3> *,int> > stack;
stack.push_back({&tree,0});
while(!stack.empty())
{
const auto pair = stack.back();
const auto * node = pair.first;
const int depth = pair.second;
D(d++) = depth;
D(d++) = depth;
D(d++) = depth;
D(d++) = depth;
D(d++) = depth;
D(d++) = depth;
stack.pop_back();
const auto & box = node->m_box;
DerivedP Pi;
DerivedQ Qi;
box_faces(box,0.03,Pi,Qi);
P.block(p,0,8,3) = Pi;
Q.block(q,0,6,4) = Qi.array()+p;
p += 8;
q += 6;
if(node->m_left)
{
stack.push_back({node->m_left,depth+1});
}
if(node->m_right)
{
stack.push_back({node->m_right,depth+1});
}
}
}
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
// generated by autoexplicit.sh
template void igl::box_faces<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(igl::AABB<Eigen::Matrix<double, -1, -1, 0, -1, -1>, 3> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
#endif
+53
View File
@@ -0,0 +1,53 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2025 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_BOX_FACES_H
#define IGL_BOX_FACES_H
#include "igl_inline.h"
#include <Eigen/Core>
#include <Eigen/Geometry>
namespace igl
{
/// Compute the quad faces of an axis-aligned bounding box) shrunk by a given
/// factor.
///
/// @param[in] box Axis-aligned bounding box
/// @param[in] shrink Factor by which to shrink the box
/// @param[out] P #P by 3 list of vertex positions
/// @param[out] Q #Q by 4 list of triangle indices into rows of P
template <typename DerivedV, typename DerivedQ>
IGL_INLINE void box_faces(
const Eigen::AlignedBox<typename DerivedV::Scalar,3> & box,
const typename DerivedV::Scalar shrink,
Eigen::PlainObjectBase<DerivedV> & P,
Eigen::PlainObjectBase<DerivedQ> & Q);
// Forward declaration
template <typename DerivedV, int DIM> class AABB;
/// Compute the quad faces of a tree of axis-aligned bounding boxes.
///
/// @param[in] tree Tree of axis-aligned bounding boxes
/// @param[out] P #P by 3 list of vertex positions
/// @param[out] Q #Q by 4 list of triangle indices into rows of P
/// @param[out] D #Q list of tree depths (0==root)
template <
typename DerivedV,
typename DerivedP,
typename DerivedQ,
typename DerivedD >
IGL_INLINE void box_faces(
const igl::AABB<DerivedV,3> & tree,
Eigen::PlainObjectBase<DerivedP> & P,
Eigen::PlainObjectBase<DerivedQ> & Q,
Eigen::PlainObjectBase<DerivedD> & D);
}
#ifndef IGL_STATIC_LIBRARY
# include "box_faces.cpp"
#endif
#endif
+47
View File
@@ -0,0 +1,47 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2025 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 "box_simplices.h"
#include "parallel_for.h"
template <
typename DerivedV,
typename DerivedF,
typename DerivedB
>
IGL_INLINE void igl::box_simplices(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F,
Eigen::PlainObjectBase<DerivedB> & B1,
Eigen::PlainObjectBase<DerivedB> & B2)
{
B1.setConstant(F.rows(),V.cols(),std::numeric_limits<double>::infinity());
B2.setConstant(F.rows(),V.cols(),-std::numeric_limits<double>::infinity());
//for(int f = 0; f < F.rows(); f++)
igl::parallel_for(F.rows(),[&](const int f)
{
for(int c = 0; c < F.cols(); c++)
{
for(int d = 0; d < V.cols(); d++)
{
B1(f,d) = std::min(B1(f,d),V(F(f,c),d));
B2(f,d) = std::max(B2(f,d),V(F(f,c),d));
}
}
},1000);
}
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
// generated by autoexplicit.sh
template void igl::box_simplices<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 2, 1, -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, 1, -1, 2>>&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 2, 1, -1, 2>>&);
// generated by autoexplicit.sh
template void igl::box_simplices<Eigen::Matrix<double, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, 2, 1, -1, 2>, Eigen::Matrix<double, -1, 3, 1, -1, 3>>(Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 1, -1, 3>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 2, 1, -1, 2>> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 1, -1, 3>>&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 1, -1, 3>>&);
// generated by autoexplicit.sh
template void igl::box_simplices<Eigen::Matrix<double, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -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<int, -1, 3, 1, -1, 3>> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 1, -1, 3>>&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 1, -1, 3>>&);
template void igl::box_simplices<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>>&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3>>&);
#endif
+37
View File
@@ -0,0 +1,37 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2025 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_BOX_SIMPLICES_H
#define IGL_BOX_SIMPLICES_H
#include "igl_inline.h"
#include <Eigen/Core>
namespace igl
{
///
///
/// @param[in] V #V by dim list of vertex positions
/// @param[in] F #F by ss list of simplex indices into V
/// @param[out] B1 #B by dim list of minimum corners of the Eytzinger AABBs
/// @param[out] B2 #B by dim list of maximum corners of the Eytzinger AABBs
///
template <
typename DerivedV,
typename DerivedF,
typename DerivedB
>
IGL_INLINE void box_simplices(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F,
Eigen::PlainObjectBase<DerivedB> & B1,
Eigen::PlainObjectBase<DerivedB> & B2);
}
#ifndef IGL_STATIC_LIBRARY
# include "box_simplices.cpp"
#endif
#endif
+35
View File
@@ -0,0 +1,35 @@
#include "box_surface_area.h"
template <typename DerivedCorner>
IGL_INLINE typename DerivedCorner::Scalar igl::box_surface_area(
const Eigen::MatrixBase<DerivedCorner> & min_corner,
const Eigen::MatrixBase<DerivedCorner> & max_corner)
{
using Scalar = typename DerivedCorner::Scalar;
const auto dimensions = (max_corner - min_corner).eval();
const auto num_dimensions = dimensions.size();
Scalar surface_area = 0;
for (int i = 0; i < num_dimensions; ++i) {
for (int j = i + 1; j < num_dimensions; ++j) {
surface_area += 2 * dimensions[i] * dimensions[j];
}
}
return surface_area;
}
template <typename Scalar, int AmbientDim>
IGL_INLINE Scalar igl::box_surface_area(
const Eigen::AlignedBox<Scalar,AmbientDim> & box)
{
return igl::box_surface_area(box.min(),box.max());
}
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
// generated by autoexplicit.sh
template double igl::box_surface_area<double, 2>(Eigen::AlignedBox<double, 2> const&);
template double igl::box_surface_area<double, 3>(Eigen::AlignedBox<double, 3> const&);
#endif
+30
View File
@@ -0,0 +1,30 @@
#ifndef IGL_BOX_SURFACE_AREA_H
#define IGL_BOX_SURFACE_AREA_H
#include "igl_inline.h"
#include <Eigen/Core>
#include <Eigen/Geometry>
namespace igl
{
/// Compute the surface area of a box given its min and max corners
///
/// @param[in] min_corner #d vector of min corner position
/// @param[in] max_corner #d vector of max corner position
/// @return surface area of box
template <typename DerivedCorner>
IGL_INLINE typename DerivedCorner::Scalar box_surface_area(
const Eigen::MatrixBase<DerivedCorner> & min_corner,
const Eigen::MatrixBase<DerivedCorner> & max_corner);
/// \overload
/// @param[in] box axis-aligned bounding box
template <typename Scalar, int AmbientDim>
IGL_INLINE Scalar box_surface_area(
const Eigen::AlignedBox<Scalar,AmbientDim> & box);
}
#ifndef IGL_STATIC_LIBRARY
# include "box_surface_area.cpp"
#endif
#endif
+10 -13
View File
@@ -7,6 +7,7 @@
// obtain one at http://mozilla.org/MPL/2.0/.
#include "cat.h"
#include <cassert>
#include <cstdio>
// Bug in unsupported/Eigen/SparseExtra needs iostream first
@@ -26,7 +27,6 @@ IGL_INLINE void igl::cat(
{
assert(dim == 1 || dim == 2);
using namespace Eigen;
// Special case if B or A is empty
if(A.size() == 0)
{
@@ -40,7 +40,7 @@ IGL_INLINE void igl::cat(
}
// This is faster than using DynamicSparseMatrix or setFromTriplets
C = SparseMatrix<Scalar>(
C = Eigen::SparseMatrix<Scalar>(
dim == 1 ? A.rows()+B.rows() : A.rows(),
dim == 1 ? A.cols() : A.cols()+B.cols());
Eigen::VectorXi per_col = Eigen::VectorXi::Zero(C.cols());
@@ -49,11 +49,11 @@ IGL_INLINE void igl::cat(
assert(A.outerSize() == B.outerSize());
for(int k = 0;k<A.outerSize();++k)
{
for(typename SparseMatrix<Scalar>::InnerIterator it (A,k); it; ++it)
for(typename Eigen::SparseMatrix<Scalar>::InnerIterator it (A,k); it; ++it)
{
per_col(k)++;
}
for(typename SparseMatrix<Scalar>::InnerIterator it (B,k); it; ++it)
for(typename Eigen::SparseMatrix<Scalar>::InnerIterator it (B,k); it; ++it)
{
per_col(k)++;
}
@@ -62,14 +62,14 @@ IGL_INLINE void igl::cat(
{
for(int k = 0;k<A.outerSize();++k)
{
for(typename SparseMatrix<Scalar>::InnerIterator it (A,k); it; ++it)
for(typename Eigen::SparseMatrix<Scalar>::InnerIterator it (A,k); it; ++it)
{
per_col(k)++;
}
}
for(int k = 0;k<B.outerSize();++k)
{
for(typename SparseMatrix<Scalar>::InnerIterator it (B,k); it; ++it)
for(typename Eigen::SparseMatrix<Scalar>::InnerIterator it (B,k); it; ++it)
{
per_col(A.cols() + k)++;
}
@@ -80,11 +80,11 @@ IGL_INLINE void igl::cat(
{
for(int k = 0;k<A.outerSize();++k)
{
for(typename SparseMatrix<Scalar>::InnerIterator it (A,k); it; ++it)
for(typename Eigen::SparseMatrix<Scalar>::InnerIterator it (A,k); it; ++it)
{
C.insert(it.row(),k) = it.value();
}
for(typename SparseMatrix<Scalar>::InnerIterator it (B,k); it; ++it)
for(typename Eigen::SparseMatrix<Scalar>::InnerIterator it (B,k); it; ++it)
{
C.insert(A.rows()+it.row(),k) = it.value();
}
@@ -93,14 +93,14 @@ IGL_INLINE void igl::cat(
{
for(int k = 0;k<A.outerSize();++k)
{
for(typename SparseMatrix<Scalar>::InnerIterator it (A,k); it; ++it)
for(typename Eigen::SparseMatrix<Scalar>::InnerIterator it (A,k); it; ++it)
{
C.insert(it.row(),k) = it.value();
}
}
for(int k = 0;k<B.outerSize();++k)
{
for(typename SparseMatrix<Scalar>::InnerIterator it (B,k); it; ++it)
for(typename Eigen::SparseMatrix<Scalar>::InnerIterator it (B,k); it; ++it)
{
C.insert(it.row(),A.cols()+k) = it.value();
}
@@ -157,7 +157,6 @@ IGL_INLINE Mat igl::cat(const int dim, const Mat & A, const Mat & B)
template <class Mat>
IGL_INLINE void igl::cat(const std::vector<std::vector< Mat > > & A, Mat & C)
{
using namespace std;
// Start with empty matrix
C.resize(0,0);
for(const auto & row_vec : A)
@@ -178,8 +177,6 @@ template <typename T, typename DerivedC>
IGL_INLINE void igl::cat(const int dim, const std::vector<T> & A, Eigen::PlainObjectBase<DerivedC> & C)
{
assert(dim == 1 || dim == 2);
using namespace Eigen;
const int num_mat = A.size();
if(num_mat == 0)
{
+2 -2
View File
@@ -21,8 +21,8 @@ namespace igl
/// This is an attempt to act like matlab's cat function.
///
/// @tparam Scalar scalar data type for sparse matrices like double or int
/// @tparam Mat matrix type for all matrices (e.g. MatrixXd, SparseMatrix)
/// @tparam MatC matrix type for output matrix (e.g. MatrixXd) needs to support
/// @tparam Mat matrix type for all matrices (e.g. Eigen::MatrixXd, SparseMatrix)
/// @tparam MatC matrix type for output matrix (e.g. Eigen::MatrixXd) needs to support
/// resize
/// @param[in] dim dimension along which to concatenate, 1 or 2
/// @param[in] A first input matrix
+2 -3
View File
@@ -10,15 +10,14 @@
template < typename DerivedX, typename DerivedY>
IGL_INLINE void igl::ceil(
const Eigen::PlainObjectBase<DerivedX>& X,
const Eigen::MatrixBase<DerivedX>& X,
Eigen::PlainObjectBase<DerivedY>& Y)
{
using namespace std;
typedef typename DerivedX::Scalar Scalar;
Y = X.unaryExpr([](const Scalar &x)->Scalar{return std::ceil(x);}).template cast<typename DerivedY::Scalar >();
}
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
template void igl::ceil<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
template void igl::ceil<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::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
#endif
+1 -1
View File
@@ -17,7 +17,7 @@ namespace igl
/// @param[out] Y m by n matrix of ceiled integers
template < typename DerivedX, typename DerivedY>
IGL_INLINE void ceil(
const Eigen::PlainObjectBase<DerivedX>& X,
const Eigen::MatrixBase<DerivedX>& X,
Eigen::PlainObjectBase<DerivedY>& Y);
}
+15 -2
View File
@@ -8,6 +8,8 @@
#include "centroid.h"
#include <Eigen/Geometry>
#include <cassert>
template <
typename DerivedV,
typename DerivedF,
@@ -19,11 +21,19 @@ IGL_INLINE void igl::centroid(
Eigen::PlainObjectBase<Derivedc>& cen,
Derivedvol & vol)
{
using namespace Eigen;
assert(F.cols() == 3 && "F should contain triangles.");
assert(V.cols() == 3 && "V should contain 3d points.");
const int m = F.rows();
cen.setZero();
// static assert that cen is either a 3d rowvector a 3d vector or a variable
// size vector
static_assert(Derivedc::IsVectorAtCompileTime,"cen should be a vector");
static_assert(
Derivedc::RowsAtCompileTime == 3 ||
Derivedc::RowsAtCompileTime == -1 ||
Derivedc::ColsAtCompileTime == 3 ||
Derivedc::ColsAtCompileTime == -1,
"cen should be sizeable to 3 vector");
cen.setZero(3);
vol = 0;
// loop over faces
for(int f = 0;f<m;f++)
@@ -61,6 +71,8 @@ IGL_INLINE void igl::centroid(
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
// generated by autoexplicit.sh
template void igl::centroid<Eigen::Matrix<double, 8, 3, 0, 8, 3>, Eigen::Matrix<int, 12, 3, 0, 12, 3>, Eigen::Matrix<double, 1, 3, 1, 1, 3>, double>(Eigen::MatrixBase<Eigen::Matrix<double, 8, 3, 0, 8, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, 12, 3, 0, 12, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> >&, double&);
template void igl::centroid<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, 1, 3, 1, 1, 3>, double>(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, 1, 1, 3> >&, double&);
// generated by autoexplicit.sh
template void igl::centroid<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<unsigned int, -1, 3, 1, -1, 3>, Eigen::Matrix<float, 3, 1, 0, 3, 1>, float>(Eigen::MatrixBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<unsigned int, -1, 3, 1, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, 3, 1, 0, 3, 1> >&, float&);
@@ -70,4 +82,5 @@ template void igl::centroid<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix
template void igl::centroid<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -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<int, -1, 3, 1, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, 1, 3, 1, 1, 3> >&);
template void igl::centroid<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, 3, 1, 0, 3, 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, 3, 1, 0, 3, 1> >&);
template void igl::centroid<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, 1, 3, 1, 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, 1, 1, 3> >&);
template void igl::centroid<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, 1, -1, 1, 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, 1, 1, -1>>&);
#endif
+35 -25
View File
@@ -1,20 +1,21 @@
// 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
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "circulation.h"
#include "list_to_matrix.h"
#include <cassert>
template <typename DerivedEMAP, typename DerivedEF, typename DerivedEI>
IGL_INLINE std::vector<int> igl::circulation(
const int e,
const bool ccw,
const Eigen::VectorXi & EMAP,
const Eigen::MatrixXi & EF,
const Eigen::MatrixXi & EI)
const Eigen::MatrixBase<DerivedEMAP> & EMAP,
const Eigen::MatrixBase<DerivedEF> & EF,
const Eigen::MatrixBase<DerivedEI> & EI)
{
// prepare output
std::vector<int> N;
@@ -22,9 +23,9 @@ IGL_INLINE std::vector<int> igl::circulation(
const int m = EMAP.size()/3;
assert(m*3 == EMAP.size());
const auto & step = [&](
const int e,
const int e,
const int ff,
int & ne,
int & ne,
int & nf)
{
assert((EF(e,1) == ff || EF(e,0) == ff) && "e should touch ff");
@@ -33,7 +34,7 @@ IGL_INLINE std::vector<int> igl::circulation(
const int nv = EI(e,nside);
// get next face
nf = EF(e,nside);
// get next edge
// get next edge
const int dir = ccw?-1:1;
ne = EMAP(nf+m*((nv+dir+3)%3));
};
@@ -56,36 +57,38 @@ IGL_INLINE std::vector<int> igl::circulation(
return N;
}
template <typename DerivedEMAP, typename DerivedEF, typename DerivedEI, typename DerivedvN>
IGL_INLINE void igl::circulation(
const int e,
const bool ccw,
const Eigen::VectorXi & EMAP,
const Eigen::MatrixXi & EF,
const Eigen::MatrixXi & EI,
Eigen::VectorXi & vN)
const Eigen::MatrixBase<DerivedEMAP> & EMAP,
const Eigen::MatrixBase<DerivedEF> & EF,
const Eigen::MatrixBase<DerivedEI> & EI,
Eigen::PlainObjectBase<DerivedvN> & vN)
{
std::vector<int> N = circulation(e,ccw,EMAP,EF,EI);
igl::list_to_matrix(N,vN);
}
template <typename DerivedF, typename DerivedEMAP, typename DerivedEF, typename DerivedEI, typename Nv_type>
IGL_INLINE void igl::circulation(
const int e,
const bool ccw,
const Eigen::MatrixXi & F,
const Eigen::VectorXi & EMAP,
const Eigen::MatrixXi & EF,
const Eigen::MatrixXi & EI,
const Eigen::MatrixBase<DerivedF> & F,
const Eigen::MatrixBase<DerivedEMAP> & EMAP,
const Eigen::MatrixBase<DerivedEF> & EF,
const Eigen::MatrixBase<DerivedEI> & EI,
/*std::vector<int> & Ne,*/
std::vector<int> & Nv,
std::vector<int> & Nf)
std::vector<Nv_type> & Nv,
std::vector<Nv_type> & Nf)
{
//
// for e --> (bf) and ccw=true
//
// c---d
// / \ / \
//
// a---b-e-f
// \ / \ /
//
// g---h
//
// // (might start with {bhf} depending on edge)
@@ -101,9 +104,9 @@ IGL_INLINE void igl::circulation(
const int m = EMAP.size()/3;
assert(m*3 == EMAP.size());
const auto & step = [&](
const int e,
const int e,
const int ff,
int & ne,
int & ne,
//int & re,
int & rv,
int & nf)
@@ -114,7 +117,7 @@ IGL_INLINE void igl::circulation(
const int nv = EI(e,nside);
// get next face
nf = EF(e,nside);
// get next edge
// get next edge
const int dir = ccw?-1:1;
rv = F(nf,nv);
ne = EMAP(nf+m*((nv+dir+3)%3));
@@ -141,3 +144,10 @@ IGL_INLINE void igl::circulation(
}
}
}
#ifdef IGL_STATIC_LIBRARY
// Explicit template specialization
template std::vector<int, std::allocator<int>> igl::circulation<Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>>(int, bool, 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&);
template void igl::circulation<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<int, -1, 1, 0, -1, 1>>(int, bool, 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::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>>&);
template void igl::circulation<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<int, -1, -1, 0, -1, -1>, int>(int, bool, 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<int, -1, -1, 0, -1, -1>> const&, std::vector<int, std::allocator<int>>&, std::vector<int, std::allocator<int>>&);
#endif
+17 -14
View File
@@ -28,12 +28,13 @@ namespace igl
/// @return list of faces touched by circulation (in cyclically order).
///
/// \see edge_flaps
template <typename DerivedEMAP, typename DerivedEF, typename DerivedEI>
IGL_INLINE std::vector<int> circulation(
const int e,
const bool ccw,
const Eigen::VectorXi & EMAP,
const Eigen::MatrixXi & EF,
const Eigen::MatrixXi & EI);
const Eigen::MatrixBase<DerivedEMAP> & EMAP,
const Eigen::MatrixBase<DerivedEF> & EF,
const Eigen::MatrixBase<DerivedEI> & EI);
/// Return list of faces around the end point of an edge. Assumes
/// data-structures are built from an edge-manifold **closed** mesh.
///
@@ -46,16 +47,17 @@ namespace igl
/// F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1) "
/// e=(j->i)
/// @param[in] EI #E by 2 list of edge flap corners (see above).
/// @param[out] #vN list of of faces touched by circulation (in cyclically order).
/// @param[out] #vN list of faces touched by circulation (in cyclically order).
///
/// \see edge_flaps
template <typename DerivedEMAP, typename DerivedEF, typename DerivedEI, typename DerivedvN>
IGL_INLINE void circulation(
const int e,
const bool ccw,
const Eigen::VectorXi & EMAP,
const Eigen::MatrixXi & EF,
const Eigen::MatrixXi & EI,
Eigen::VectorXi & vN);
const Eigen::MatrixBase<DerivedEMAP> & EMAP,
const Eigen::MatrixBase<DerivedEF> & EF,
const Eigen::MatrixBase<DerivedEI> & EI,
Eigen::PlainObjectBase<DerivedvN> & vN);
/// Return list of faces around the end point of an edge. Assumes
/// data-structures are built from an edge-manifold **closed** mesh.
///
@@ -72,16 +74,17 @@ namespace igl
/// @param[out] Nf #Nf list of face indices
///
/// \see edge_flaps
template <typename DerivedF, typename DerivedEMAP, typename DerivedEF, typename DerivedEI, typename Nv_type>
IGL_INLINE void circulation(
const int e,
const bool ccw,
const Eigen::MatrixXi & F,
const Eigen::VectorXi & EMAP,
const Eigen::MatrixXi & EF,
const Eigen::MatrixXi & EI,
const Eigen::MatrixBase<DerivedF> & F,
const Eigen::MatrixBase<DerivedEMAP> & EMAP,
const Eigen::MatrixBase<DerivedEF> & EF,
const Eigen::MatrixBase<DerivedEI> & EI,
/*std::vector<int> & Ne,*/
std::vector<int> & Nv,
std::vector<int> & Nf);
std::vector<Nv_type> & Nv,
std::vector<Nv_type> & Nf);
}
#ifndef IGL_STATIC_LIBRARY
+60
View File
@@ -8,6 +8,10 @@
#include "circumradius.h"
#include "edge_lengths.h"
#include "doublearea.h"
#include "placeholders.h"
#include <Eigen/QR>
#include <cassert>
template <
typename DerivedV,
typename DerivedF,
@@ -17,6 +21,9 @@ IGL_INLINE void igl::circumradius(
const Eigen::MatrixBase<DerivedF> & F,
Eigen::PlainObjectBase<DerivedR> & R)
{
// cols at compiletime should be Dynamic or 3
static_assert(DerivedF::ColsAtCompileTime == Eigen::Dynamic || DerivedF::ColsAtCompileTime == 3,"F should contain triangles");
assert(F.cols() == 3 && "F should contain triangles");
Eigen::Matrix<typename DerivedV::Scalar,Eigen::Dynamic,3> l;
igl::edge_lengths(V,F,l);
DerivedR A;
@@ -25,6 +32,59 @@ IGL_INLINE void igl::circumradius(
R = l.col(0).array() * l.col(1).array() * l.col(2).array() / (2.0*A.array());
}
template <
typename DerivedV,
typename DerivedT,
typename DerivedR,
typename DerivedC,
typename DerivedB>
IGL_INLINE void igl::circumradius(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedT> & T,
Eigen::PlainObjectBase<DerivedR> & R,
Eigen::PlainObjectBase<DerivedC> & C,
Eigen::PlainObjectBase<DerivedB> & B)
{
using Scalar = typename DerivedV::Scalar;
const int TCOLS = DerivedT::ColsAtCompileTime;
const int VCOLS = DerivedV::ColsAtCompileTime;
const int ACOLS = TCOLS==Eigen::Dynamic?Eigen::Dynamic:TCOLS+1;
const int ss = T.cols();
R.resize(T.rows(),1);
C.resize(T.rows(),V.cols());
B.resize(T.rows(),T.cols());
for(int i = 0;i<T.rows();i++)
{
Eigen::Matrix<Scalar,ACOLS,ACOLS> A(T.cols()+1,T.cols()+1);
// Not sure if this .eval() is a good idea
const auto Vi = V(T.row(i),igl::placeholders::all).eval();
A.topLeftCorner(T.cols(),T.cols()) = 2*(Vi*Vi.transpose());
A.block(0,T.cols(),T.cols(),1).setConstant(1);
A.block(T.cols(),0,1,T.cols()).setConstant(1);
A(T.cols(),T.cols()) = 0;
Eigen::Matrix<Scalar,ACOLS,1> b(T.cols()+1,1);
b.head(T.cols()) = (Vi.array().square().rowwise().sum()).eval();
b(T.cols()) = 1;
// [B;λ] = A\b
const auto x = (A.colPivHouseholderQr().solve(b)).eval();
B.row(i) = x.head(T.cols());
C.row(i) = B.row(i) * Vi;
const Scalar lambda = x(T.cols());
R(i) = std::sqrt(lambda + C.row(i).squaredNorm());
}
}
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
// generated by autoexplicit.sh
template void igl::circumradius<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::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
template void igl::circumradius<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::circumradius<Eigen::Matrix<double, -1, -1, 1, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<double, -1, 4, 0, -1, 4> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 4, 0, -1, 4> >&);
template void igl::circumradius<Eigen::Matrix<double, -1, -1, 1, -1, -1>, Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<double, -1, 3, 0, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&);
template void igl::circumradius<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, 3, 0, -1, 3>, 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, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 4, 0, -1, 4> >&);
template void igl::circumradius<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 3, 0, -1, 3>, 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, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&);
template void igl::circumradius<Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<double, -1, 4, 0, -1, 4> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 4, 0, -1, 4> >&);
template void igl::circumradius<Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 4, 0, -1, 4>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<double, -1, 4, 0, -1, 4> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 4, 0, -1, 4> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 4, 0, -1, 4> >&);
template void igl::circumradius<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::Matrix<double, -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, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&);
template void igl::circumradius<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> >&);
#endif
+20
View File
@@ -25,6 +25,26 @@ namespace igl
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F,
Eigen::PlainObjectBase<DerivedR> & R);
/// Generic version
///
/// @param[in] V #V by dim list of mesh vertex positions
/// @param[in] T #T by simplex-size list of simplex indices into V
/// @param[out] R #T list of circumradius
/// @param[out] C #T by dim list of circumcenter
/// @param[out] B #T by simplex-size list of barycentric coordinates of circumcenter
template <
typename DerivedV,
typename DerivedT,
typename DerivedR,
typename DerivedC,
typename DerivedB>
IGL_INLINE void circumradius(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedT> & T,
Eigen::PlainObjectBase<DerivedR> & R,
Eigen::PlainObjectBase<DerivedC> & C,
Eigen::PlainObjectBase<DerivedB> & B);
}
#ifndef IGL_STATIC_LIBRARY
# include "circumradius.cpp"
+37 -209
View File
@@ -8,18 +8,26 @@
#include "collapse_edge.h"
#include "circulation.h"
#include "edge_collapse_is_valid.h"
#include "decimate_trivial_callbacks.h"
#include <cassert>
#include <vector>
template <
typename Derivedp,
typename DerivedV,
typename DerivedF,
typename DerivedE,
typename DerivedEMAP,
typename DerivedEF,
typename DerivedEI>
IGL_INLINE bool igl::collapse_edge(
const int e,
const Eigen::RowVectorXd & p,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
Eigen::VectorXi & EMAP,
Eigen::MatrixXi & EF,
Eigen::MatrixXi & EI,
const Eigen::MatrixBase<Derivedp> & p,
Eigen::MatrixBase<DerivedV> & V,
Eigen::MatrixBase<DerivedF> & F,
Eigen::MatrixBase<DerivedE> & E,
Eigen::MatrixBase<DerivedEMAP> & EMAP,
Eigen::MatrixBase<DerivedEF> & EF,
Eigen::MatrixBase<DerivedEI> & EI,
int & e1,
int & e2,
int & f1,
@@ -33,19 +41,28 @@ IGL_INLINE bool igl::collapse_edge(
e,p,Nsv,Nsf,Ndv,Ndf,V,F,E,EMAP,EF,EI,e1,e2,f1,f2);
}
template
<
typename Derivedp,
typename DerivedV,
typename DerivedF,
typename DerivedE,
typename DerivedEMAP,
typename DerivedEF,
typename DerivedEI>
IGL_INLINE bool igl::collapse_edge(
const int e,
const Eigen::RowVectorXd & p,
const Eigen::MatrixBase<Derivedp> & p,
/*const*/ std::vector<int> & Nsv,
const std::vector<int> & Nsf,
/*const*/ std::vector<int> & Ndv,
const std::vector<int> & Ndf,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
Eigen::VectorXi & EMAP,
Eigen::MatrixXi & EF,
Eigen::MatrixXi & EI,
Eigen::MatrixBase<DerivedV> & V,
Eigen::MatrixBase<DerivedF> & F,
Eigen::MatrixBase<DerivedE> & E,
Eigen::MatrixBase<DerivedEMAP> & EMAP,
Eigen::MatrixBase<DerivedEF> & EF,
Eigen::MatrixBase<DerivedEI> & EI,
int & a_e1,
int & a_e2,
int & a_f1,
@@ -54,8 +71,6 @@ IGL_INLINE bool igl::collapse_edge(
// Assign this to 0 rather than, say, -1 so that deleted elements will get
// draw as degenerate elements at vertex 0 (which should always exist and
// never get collapsed to anything else since it is the smallest index)
using namespace Eigen;
using namespace std;
const int eflip = E(e,0)>E(e,1);
// source and destination
const int s = eflip?E(e,1):E(e,0);
@@ -178,196 +193,9 @@ IGL_INLINE bool igl::collapse_edge(
return true;
}
IGL_INLINE bool igl::collapse_edge(
const int e,
const Eigen::RowVectorXd & p,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
Eigen::VectorXi & EMAP,
Eigen::MatrixXi & EF,
Eigen::MatrixXi & EI)
{
int e1,e2,f1,f2;
return collapse_edge(e,p,V,F,E,EMAP,EF,EI,e1,e2,f1,f2);
}
IGL_INLINE bool igl::collapse_edge(
const decimate_cost_and_placement_callback & cost_and_placement,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
Eigen::VectorXi & EMAP,
Eigen::MatrixXi & EF,
Eigen::MatrixXi & EI,
igl::min_heap< std::tuple<double,int,int> > & Q,
Eigen::VectorXi & EQ,
Eigen::MatrixXd & C)
{
int e,e1,e2,f1,f2;
decimate_pre_collapse_callback always_try;
decimate_post_collapse_callback never_care;
decimate_trivial_callbacks(always_try,never_care);
return
collapse_edge(
cost_and_placement,always_try,never_care,
V,F,E,EMAP,EF,EI,Q,EQ,C,e,e1,e2,f1,f2);
}
IGL_INLINE bool igl::collapse_edge(
const decimate_cost_and_placement_callback & cost_and_placement,
const decimate_pre_collapse_callback & pre_collapse,
const decimate_post_collapse_callback & post_collapse,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
Eigen::VectorXi & EMAP,
Eigen::MatrixXi & EF,
Eigen::MatrixXi & EI,
igl::min_heap< std::tuple<double,int,int> > & Q,
Eigen::VectorXi & EQ,
Eigen::MatrixXd & C)
{
int e,e1,e2,f1,f2;
return
collapse_edge(
cost_and_placement,pre_collapse,post_collapse,
V,F,E,EMAP,EF,EI,Q,EQ,C,e,e1,e2,f1,f2);
}
IGL_INLINE bool igl::collapse_edge(
const decimate_cost_and_placement_callback & cost_and_placement,
const decimate_pre_collapse_callback & pre_collapse,
const decimate_post_collapse_callback & post_collapse,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
Eigen::VectorXi & EMAP,
Eigen::MatrixXi & EF,
Eigen::MatrixXi & EI,
igl::min_heap< std::tuple<double,int,int> > & Q,
Eigen::VectorXi & EQ,
Eigen::MatrixXd & C,
int & e,
int & e1,
int & e2,
int & f1,
int & f2)
{
using namespace Eigen;
using namespace igl;
std::tuple<double,int,int> p;
while(true)
{
// Check if Q is empty
if(Q.empty())
{
// no edges to collapse
e = -1;
return false;
}
// pop from Q
p = Q.top();
if(std::get<0>(p) == std::numeric_limits<double>::infinity())
{
e = -1;
// min cost edge is infinite cost
return false;
}
Q.pop();
e = std::get<1>(p);
// Check if matches timestamp
if(std::get<2>(p) == EQ(e))
{
break;
}
// must be stale or dead.
assert(std::get<2>(p) < EQ(e) || EQ(e) == -1);
// try again.
}
// Why is this computed up here?
// If we just need original face neighbors of edge, could we gather that more
// directly than gathering face neighbors of each vertex?
std::vector<int> /*Nse,*/Nsf,Nsv;
circulation(e, true,F,EMAP,EF,EI,/*Nse,*/Nsv,Nsf);
std::vector<int> /*Nde,*/Ndf,Ndv;
circulation(e, false,F,EMAP,EF,EI,/*Nde,*/Ndv,Ndf);
bool collapsed = true;
if(pre_collapse(V,F,E,EMAP,EF,EI,Q,EQ,C,e))
{
collapsed = collapse_edge(
e,C.row(e),
Nsv,Nsf,Ndv,Ndf,
V,F,E,EMAP,EF,EI,e1,e2,f1,f2);
}else
{
// Aborted by pre collapse callback
collapsed = false;
}
post_collapse(V,F,E,EMAP,EF,EI,Q,EQ,C,e,e1,e2,f1,f2,collapsed);
if(collapsed)
{
// Erase the two, other collapsed edges by marking their timestamps as -1
EQ(e1) = -1;
EQ(e2) = -1;
// TODO: visits edges multiple times, ~150% more updates than should
//
// update local neighbors
// loop over original face neighbors
//
// Can't use previous computed Nse and Nde because those refer to EMAP
// before it was changed...
std::vector<int> Nf;
Nf.reserve( Nsf.size() + Ndf.size() ); // preallocate memory
Nf.insert( Nf.end(), Nsf.begin(), Nsf.end() );
Nf.insert( Nf.end(), Ndf.begin(), Ndf.end() );
// https://stackoverflow.com/a/1041939/148668
std::sort( Nf.begin(), Nf.end() );
Nf.erase( std::unique( Nf.begin(), Nf.end() ), Nf.end() );
// Collect all edges that must be updated
std::vector<int> Ne;
Ne.reserve(3*Nf.size());
for(auto & n : Nf)
{
if(F(n,0) != IGL_COLLAPSE_EDGE_NULL ||
F(n,1) != IGL_COLLAPSE_EDGE_NULL ||
F(n,2) != IGL_COLLAPSE_EDGE_NULL)
{
for(int v = 0;v<3;v++)
{
// get edge id
const int ei = EMAP(v*F.rows()+n);
Ne.push_back(ei);
}
}
}
// Only process edge once
std::sort( Ne.begin(), Ne.end() );
Ne.erase( std::unique( Ne.begin(), Ne.end() ), Ne.end() );
for(auto & ei : Ne)
{
// compute cost and potential placement
double cost;
RowVectorXd place;
cost_and_placement(ei,V,F,E,EMAP,EF,EI,cost,place);
// Increment timestamp
EQ(ei)++;
// Replace in queue
Q.emplace(cost,ei,EQ(ei));
C.row(ei) = place;
}
}else
{
// reinsert with infinite weight (the provided cost function must **not**
// have given this un-collapsable edge inf cost already)
// Increment timestamp
EQ(e)++;
// Replace in queue
Q.emplace(std::numeric_limits<double>::infinity(),e,EQ(e));
}
return collapsed;
}
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
template bool igl::collapse_edge<Eigen::Matrix<double, 1, -1, 1, 1, -1>, 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::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>>(int, Eigen::MatrixBase<Eigen::Matrix<double, 1, -1, 1, 1, -1>> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, int&, int&, int&, int&);
template bool igl::collapse_edge<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1>, 1, -1, false>, 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::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>>(int, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1>, 1, -1, false>> const&, std::vector<int, std::allocator<int>>&, std::vector<int, std::allocator<int>> const&, std::vector<int, std::allocator<int>>&, std::vector<int, std::allocator<int>> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, int&, int&, int&, int&);
#endif
+100 -120
View File
@@ -8,18 +8,11 @@
#ifndef IGL_COLLAPSE_EDGE_H
#define IGL_COLLAPSE_EDGE_H
#include "igl_inline.h"
#include "min_heap.h"
#include "decimate_callback_types.h"
#include "COLLAPSE_EDGE_NULL.h"
#include <Eigen/Core>
#include <vector>
#include <set>
namespace igl
{
#ifndef IGL_COLLAPSE_EDGE_NULL
/// Special value for indicating a null vertex index as the result of a
/// collapsed edge.
#define IGL_COLLAPSE_EDGE_NULL 0
#endif
/// Attempt to collapse a given edge of a mesh. Assumes (V,F) is a closed
/// manifold mesh (except for previously collapsed faces which should be set
/// to: [IGL_COLLAPSE_EDGE_NULL IGL_COLLAPSE_EDGE_NULL
@@ -43,20 +36,88 @@ namespace igl
/// e=(j->i)
/// @param[in,out] EI #E by 2 list of edge flap corners (see above).
/// [mesh inputs]
/// @param[out] e1 index into E of edge collpased on left
/// @param[out] e2 index into E of edge collpased on right
/// @param[out] f1 index into F of face collpased on left
/// @param[out] f2 index into F of face collpased on right
/// @param[out] e1 index into E of edge collapsed on left
/// @param[out] e2 index into E of edge collapsed on right
/// @param[out] f1 index into F of face collapsed on left
/// @param[out] f2 index into F of face collapsed on right
/// @return true if edge was collapsed
///
///
/// Define [s,d] = sort(E(e,:)) so that s<d, then d is "detached" from
/// connectivity meaning all faces/edges incident on d will now be incident on
/// s. (This reduces fragmentation by preferring to collapse toward the start
/// of V)¹. If E(e,1)==s then we say the edge is "flipped" (`eflip` true in
/// the implementation).
///
/// f1 is set to EF(e,0) and f2 is set to EF(e,1). Let v1 be EI(e,0) the
/// corner of F(f1,:) opposite e. _If_ (s<d) then e1 will be the edge after e
/// within f1:
///
/// s<d
/// ✅s----e-----d☠️
/// \ ← /
/// \ ↘f₁↗ /
/// e₁ /
/// \ /
/// \/
///
/// _If_ (s>d) then e1 will be the edge after e within f1:
///
/// s>d
/// ✅s----e-----d☠️
/// \ ← /
/// \ ↘f₁↗ /
/// \ e₁
/// \ /
/// \/
///
///
/// ¹Or at least it would if we templated these functions to allow using
/// RowMajor V.
///
/// It really seems that this callback should provide a meaningful edge on the
/// _new_ mesh. Meanwhile Oof You can use this gross mechanism to find the faces incident on the
/// collapsed vertex:
///
/// ```cpp
/// const auto survivors =
/// [&F,&e,&EMAP](const int f1, const int e1, int & d1)
/// {
/// for(int c=0;c<3;c++)
/// {
/// d1 = EMAP(f1+c*F.rows());
/// if((d1 != e) && (d1 != e1)) { break; }
/// }
/// };
/// int d1,d2;
/// survivors(f1,e1,d1);
/// survivors(f2,e2,d2);
/// // Will circulating by continuing in the CCW direction of E(d1,:)
/// // encircle the common edge? That is, is E(d1,1) the common vertex?
/// const bool ccw = E(d1,1) == E(d2,0) || E(d1,1) == E(d2,1);
/// std::vector<int> Nf;
/// {
/// std::vector<int> Nv;
/// igl::circulation(d1,ccw,F,EMAP,EF,EI,Nv,Nf);
/// }
/// ```
template <
typename Derivedp,
typename DerivedV,
typename DerivedF,
typename DerivedE,
typename DerivedEMAP,
typename DerivedEF,
typename DerivedEI>
IGL_INLINE bool collapse_edge(
const int e,
const Eigen::RowVectorXd & p,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
Eigen::VectorXi & EMAP,
Eigen::MatrixXi & EF,
Eigen::MatrixXi & EI,
const Eigen::MatrixBase<Derivedp> & p,
Eigen::MatrixBase<DerivedV> & V,
Eigen::MatrixBase<DerivedF> & F,
Eigen::MatrixBase<DerivedE> & E,
Eigen::MatrixBase<DerivedEMAP> & EMAP,
Eigen::MatrixBase<DerivedEF> & EF,
Eigen::MatrixBase<DerivedEI> & EI,
int & e1,
int & e2,
int & f1,
@@ -67,113 +128,32 @@ namespace igl
/// @param[in] Nsf #Nsf face circulation around s
/// @param[in] Ndv #Ndv vertex circulation around d
/// @param[in] Ndf #Ndf face circulation around d
template
<
typename Derivedp,
typename DerivedV,
typename DerivedF,
typename DerivedE,
typename DerivedEMAP,
typename DerivedEF,
typename DerivedEI>
IGL_INLINE bool collapse_edge(
const int e,
const Eigen::RowVectorXd & p,
const Eigen::MatrixBase<Derivedp> & p,
/*const*/ std::vector<int> & Nsv,
const std::vector<int> & Nsf,
/*const*/ std::vector<int> & Ndv,
const std::vector<int> & Ndf,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
Eigen::VectorXi & EMAP,
Eigen::MatrixXi & EF,
Eigen::MatrixXi & EI,
int & e1,
int & e2,
int & f1,
int & f2);
/// \overload
IGL_INLINE bool collapse_edge(
const int e,
const Eigen::RowVectorXd & p,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
Eigen::VectorXi & EMAP,
Eigen::MatrixXi & EF,
Eigen::MatrixXi & EI);
/// Collapse least-cost edge from a priority queue and update queue
///
/// See decimate.h for more details.
///
/// @param[in] cost_and_placement function computing cost of collapsing an edge and 3d
/// position where it should be placed:
/// cost_and_placement(V,F,E,EMAP,EF,EI,cost,placement);
/// **If the edges is collapsed** then this function will be called on all
/// edges of all faces previously incident on the endpoints of the
/// collapsed edge.
/// @param[in] pre_collapse callback called with index of edge whose collapse is about
/// to be attempted. This function should return whether to **proceed**
/// with the collapse: returning true means "yes, try to collapse",
/// returning false means "No, consider this edge 'uncollapsable', behave
/// as if collapse_edge(e) returned false.
/// @param[in] post_collapse callback called with index of edge whose collapse was
/// just attempted and a flag revealing whether this was successful.
/// @param[in,out] V #V by dim list of vertex positions, lesser index of E(e,:) will be set
/// to midpoint of edge.
/// @param[in,out] F #F by 3 list of face indices into V.
/// @param[in,out] E #E by 2 list of edge indices into V.
/// @param[in,out] EMAP #F*3 list of indices into E, mapping each directed edge to unique
/// unique edge in E
/// @param[in,out] EF #E by 2 list of edge flaps, EF(e,0)=f means e=(i-->j) is the edge of
/// F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1)
/// e=(j->i)
/// @param[in,out] EI #E by 2 list of edge flap corners (see above).
/// @param[in] Q queue containing pairs of costs and edge indices and insertion "time"
/// @param[in] EQ #E list of "time" of last time pushed into Q
/// @param[in] C #E by dim list of stored placements
/// @param[out] e index into E of attempted collapsed edge. Set to -1 if Q is empty or
/// contains only infinite cost edges.
/// @param[out] e1 index into E of edge collpased on left.
/// @param[out] e2 index into E of edge collpased on right.
/// @param[out] f1 index into F of face collpased on left.
/// @param[out] f2 index into F of face collpased on right.
IGL_INLINE bool collapse_edge(
const decimate_cost_and_placement_callback & cost_and_placement,
const decimate_pre_collapse_callback & pre_collapse,
const decimate_post_collapse_callback & post_collapse,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
Eigen::VectorXi & EMAP,
Eigen::MatrixXi & EF,
Eigen::MatrixXi & EI,
igl::min_heap< std::tuple<double,int,int> > & Q,
Eigen::VectorXi & EQ,
Eigen::MatrixXd & C,
int & e,
int & e1,
int & e2,
int & f1,
int & f2);
/// \overload
IGL_INLINE bool collapse_edge(
const decimate_cost_and_placement_callback & cost_and_placement,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
Eigen::VectorXi & EMAP,
Eigen::MatrixXi & EF,
Eigen::MatrixXi & EI,
igl::min_heap< std::tuple<double,int,int> > & Q,
Eigen::VectorXi & EQ,
Eigen::MatrixXd & C);
/// \overload
IGL_INLINE bool collapse_edge(
const decimate_cost_and_placement_callback & cost_and_placement,
const decimate_pre_collapse_callback & pre_collapse,
const decimate_post_collapse_callback & post_collapse,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
Eigen::VectorXi & EMAP,
Eigen::MatrixXi & EF,
Eigen::MatrixXi & EI,
igl::min_heap< std::tuple<double,int,int> > & Q,
Eigen::VectorXi & EQ,
Eigen::MatrixXd & C);
Eigen::MatrixBase<DerivedV> & V,
Eigen::MatrixBase<DerivedF> & F,
Eigen::MatrixBase<DerivedE> & E,
Eigen::MatrixBase<DerivedEMAP> & EMAP,
Eigen::MatrixBase<DerivedEF> & EF,
Eigen::MatrixBase<DerivedEI> & EI,
int & a_e1,
int & a_e2,
int & a_f1,
int & a_f2);
}
#ifndef IGL_STATIC_LIBRARY
@@ -0,0 +1,176 @@
#include "collapse_edge_would_create_intersections.h"
#include "AABB.h"
#include "circulation.h"
#include "writePLY.h"
#include "triangle_triangle_intersect.h"
#include <Eigen/Geometry>
#include <vector>
#include <iostream>
#include <algorithm>
#include <cassert>
template <
typename Derivedp,
typename DerivedV,
typename DerivedF,
typename DerivedE,
typename DerivedEMAP,
typename DerivedEF,
typename DerivedEI>
IGL_INLINE bool igl::collapse_edge_would_create_intersections(
const int e,
const Eigen::MatrixBase<Derivedp> & p,
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F,
const Eigen::MatrixBase<DerivedE> & E,
const Eigen::MatrixBase<DerivedEMAP> & EMAP,
const Eigen::MatrixBase<DerivedEF> & EF,
const Eigen::MatrixBase<DerivedEI> & EI,
const igl::AABB<DerivedV,3> & tree,
const int inf_face_id)
{
// Merge two lists of integers
const auto merge = [&](
const std::vector<int> & A, const std::vector<int> & B)->
std::vector<int>
{
std::vector<int> C;
C.reserve( A.size() + B.size() ); // preallocate memory
C.insert( C.end(), A.begin(), A.end() );
C.insert( C.end(), B.begin(), B.end() );
// https://stackoverflow.com/a/1041939/148668
std::sort( C.begin(), C.end() );
C.erase( std::unique( C.begin(), C.end() ), C.end() );
return C;
};
std::vector<int> old_one_ring;
{
std::vector<int> Nsv,Nsf,Ndv,Ndf;
igl::circulation(e, true,F,EMAP,EF,EI,Nsv,Nsf);
igl::circulation(e,false,F,EMAP,EF,EI,Ndv,Ndf);
old_one_ring = merge(Nsf,Ndf);
}
int f1 = EF(e,0);
int f2 = EF(e,1);
std::vector<int> new_one_ring = old_one_ring;
// erase if ==f1 or ==f2
new_one_ring.erase(
std::remove(new_one_ring.begin(), new_one_ring.end(), f1),
new_one_ring.end());
new_one_ring.erase(
std::remove(new_one_ring.begin(), new_one_ring.end(), f2),
new_one_ring.end());
// big box containing new_one_ring
Eigen::AlignedBox<double,3> big_box;
// Extend box by placement point
big_box.extend(p.transpose());
// Extend box by all other corners (skipping old edge vertices)
for(const auto f : new_one_ring)
{
Eigen::RowVector3d corners[3];
for(int c = 0;c<3;c++)
{
if(F(f,c) == E(e,0) || F(f,c) == E(e,1))
{
corners[c] = p;
}else
{
corners[c] = V.row(F(f,c));
big_box.extend(V.row(F(f,c)).transpose());
}
}
// Degenerate triangles are considered intersections
if((corners[0]-corners[1]).cross(corners[0]-corners[2]).squaredNorm() < 1e-16)
{
return true;
}
}
std::vector<const igl::AABB<Eigen::MatrixXd,3>*> candidates;
tree.append_intersecting_leaves(big_box,candidates);
// Exclude any candidates that are in old_one_ring.
// consider using unordered_set above so that this is O(n+m) rather than O(nm)
candidates.erase(
std::remove_if(candidates.begin(), candidates.end(),
[&](const igl::AABB<Eigen::MatrixXd,3>* candidate) {
return std::find(old_one_ring.begin(), old_one_ring.end(), candidate->m_primitive) != old_one_ring.end();
}),
candidates.end());
// print candidates
//const bool stinker = e==2581;
constexpr bool stinker = false;
if(stinker)
{
igl::writePLY("before.ply",V,F);
std::cout<<"Ee = ["<<E(e,0)<<" "<<E(e,1)<<"]+1;"<<std::endl;
std::cout<<"p = ["<<p<<"];"<<std::endl;
// print new_one_ring as matlab vector of indices
std::cout<<"new_one_ring = [";
for(const auto f : new_one_ring)
{
std::cout<<f<<" ";
}
std::cout<<"]+1;"<<std::endl;
// print candidates as matlab vector of indices
std::cout<<"candidates = [";
for(const auto * candidate : candidates)
{
std::cout<<candidate->m_primitive<<" ";
}
std::cout<<"]+1;"<<std::endl;
}
// For each pair of candidate and new_one_ring, check if they intersect
bool found_intersection = false;
for(const int & f : new_one_ring)
{
if(inf_face_id >= 0 && f >= inf_face_id) { continue; }
Eigen::AlignedBox<double,3> small_box;
small_box.extend(p.transpose());
for(int c = 0;c<3;c++)
{
if(F(f,c) != E(e,0) && F(f,c) != E(e,1))
{
small_box.extend(V.row(F(f,c)).transpose());
}
}
for(const auto * candidate : candidates)
{
const int g = candidate->m_primitive;
//constexpr bool inner_stinker = false;
const bool inner_stinker = stinker && (f==1492 && g==1554);
if(inner_stinker){ printf(" f: %d g: %d\n",f,g); }
if(!small_box.intersects(candidate->m_box))
{
if(inner_stinker){ printf(" ✅ boxes don't overlap\n"); }
continue;
}
// Corner replaced by p
int c;
for(c = 0;c<3;c++)
{
if(F(f,c) == E(e,0) || F(f,c) == E(e,1))
{
break;
}
}
assert(c<3);
found_intersection = triangle_triangle_intersect(V,F,E,EMAP,EF,f,c,p,g);
if(found_intersection) { break; }
}
if(found_intersection) { break; }
}
return found_intersection;
}
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
template bool igl::collapse_edge_would_create_intersections<Eigen::Matrix<double, 1, -1, 1, 1, -1>, 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::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>>(int, Eigen::MatrixBase<Eigen::Matrix<double, 1, -1, 1, 1, -1>> const&, 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<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<int, -1, -1, 0, -1, -1>> const&, igl::AABB<Eigen::Matrix<double, -1, -1, 0, -1, -1>, 3> const&, int);
#endif
@@ -0,0 +1,53 @@
#ifndef IGL_COLLAPSE_EDGE_WOULD_CREATE_INTERSECTIONS_H
#define IGL_COLLAPSE_EDGE_WOULD_CREATE_INTERSECTIONS_H
#include "igl_inline.h"
#include <Eigen/Core>
namespace igl
{
/// Determine if collapse the edge `e` would create new intersections.
///
/// @param[in] e index into E of edge to try to collapse. E(e,:) = [s d] or [d s] so
/// that s<d, then d is collapsed to s.
/// @param[in] p dim list of vertex position where to place merged vertex
/// [mesh inputs]
/// @param[in,out] V #V by dim list of vertex positions, lesser index of E(e,:) will be set
/// to midpoint of edge.
/// @param[in,out] F #F by 3 list of face indices into V.
/// @param[in,out] E #E by 2 list of edge indices into V.
/// @param[in,out] EMAP #F*3 list of indices into E, mapping each directed edge to unique
/// unique edge in E
/// @param[in,out] EF #E by 2 list of edge flaps, EF(e,0)=f means e=(i-->j) is the edge of
/// F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1) "
/// e=(j->i)
/// @param[in,out] EI #E by 2 list of edge flap corners (see above).
/// [mesh inputs]
/// @param[in] tree AABB tree whose leaves correspond to the current
/// (non-null) faces in (V,F)
///
/// \see collapse_edge
template <typename DerivedV, int DIM> class AABB;
template <
typename Derivedp,
typename DerivedV,
typename DerivedF,
typename DerivedE,
typename DerivedEMAP,
typename DerivedEF,
typename DerivedEI>
IGL_INLINE bool collapse_edge_would_create_intersections(
const int e,
const Eigen::MatrixBase<Derivedp> & p,
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F,
const Eigen::MatrixBase<DerivedE> & E,
const Eigen::MatrixBase<DerivedEMAP> & EMAP,
const Eigen::MatrixBase<DerivedEF> & EF,
const Eigen::MatrixBase<DerivedEI> & EI,
const igl::AABB<DerivedV,3> & tree,
const int inf_face_id = -1);
}
#ifndef IGL_STATIC_LIBRARY
# include "collapse_edge_would_create_intersections.cpp"
#endif
#endif
+149
View File
@@ -0,0 +1,149 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2025 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 "collapse_least_cost_edge.h"
#include "collapse_edge.h"
#include "circulation.h"
#include <cassert>
IGL_INLINE bool igl::collapse_least_cost_edge(
const decimate_cost_and_placement_callback & cost_and_placement,
const decimate_pre_collapse_callback & pre_collapse,
const decimate_post_collapse_callback & post_collapse,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
Eigen::VectorXi & EMAP,
Eigen::MatrixXi & EF,
Eigen::MatrixXi & EI,
igl::min_heap< std::tuple<double,int,int> > & Q,
Eigen::VectorXi & EQ,
Eigen::MatrixXd & C,
int & e,
int & e1,
int & e2,
int & f1,
int & f2)
{
using namespace igl;
std::tuple<double,int,int> p;
while(true)
{
// Check if Q is empty
if(Q.empty())
{
// no edges to collapse
e = -1;
return false;
}
// pop from Q
p = Q.top();
if(std::get<0>(p) == std::numeric_limits<double>::infinity())
{
e = -1;
// min cost edge is infinite cost
return false;
}
Q.pop();
e = std::get<1>(p);
// Check if matches timestamp
if(std::get<2>(p) == EQ(e))
{
break;
}
// must be stale or dead.
assert(std::get<2>(p) < EQ(e) || EQ(e) == -1);
// try again.
}
// Why is this computed up here?
// If we just need original face neighbors of edge, could we gather that more
// directly than gathering face neighbors of each vertex?
std::vector<int> /*Nse,*/Nsf,Nsv;
circulation(e, true,F,EMAP,EF,EI,/*Nse,*/Nsv,Nsf);
std::vector<int> /*Nde,*/Ndf,Ndv;
circulation(e, false,F,EMAP,EF,EI,/*Nde,*/Ndv,Ndf);
bool collapsed = true;
if(pre_collapse(V,F,E,EMAP,EF,EI,Q,EQ,C,e))
{
collapsed = collapse_edge(
e,C.row(e),
Nsv,Nsf,Ndv,Ndf,
V,F,E,EMAP,EF,EI,e1,e2,f1,f2);
}else
{
// Aborted by pre collapse callback
collapsed = false;
}
post_collapse(V,F,E,EMAP,EF,EI,Q,EQ,C,e,e1,e2,f1,f2,collapsed);
if(collapsed)
{
// Erase the center edge, marking its timestamp as -1
EQ(e) = -1;
// Erase the two, other collapsed edges by marking their timestamps as -1
EQ(e1) = -1;
EQ(e2) = -1;
// TODO: visits edges multiple times, ~150% more updates than should
//
// update local neighbors
// loop over original face neighbors
//
// Can't use previous computed Nse and Nde because those refer to EMAP
// before it was changed...
std::vector<int> Nf;
Nf.reserve( Nsf.size() + Ndf.size() ); // preallocate memory
Nf.insert( Nf.end(), Nsf.begin(), Nsf.end() );
Nf.insert( Nf.end(), Ndf.begin(), Ndf.end() );
// https://stackoverflow.com/a/1041939/148668
std::sort( Nf.begin(), Nf.end() );
Nf.erase( std::unique( Nf.begin(), Nf.end() ), Nf.end() );
// Collect all edges that must be updated
std::vector<int> Ne;
Ne.reserve(3*Nf.size());
for(auto & n : Nf)
{
if(F(n,0) != IGL_COLLAPSE_EDGE_NULL ||
F(n,1) != IGL_COLLAPSE_EDGE_NULL ||
F(n,2) != IGL_COLLAPSE_EDGE_NULL)
{
for(int v = 0;v<3;v++)
{
// get edge id
const int ei = EMAP(v*F.rows()+n);
Ne.push_back(ei);
}
}
}
// Only process edge once
std::sort( Ne.begin(), Ne.end() );
Ne.erase( std::unique( Ne.begin(), Ne.end() ), Ne.end() );
for(auto & ei : Ne)
{
// compute cost and potential placement
double cost;
Eigen::RowVectorXd place;
cost_and_placement(ei,V,F,E,EMAP,EF,EI,cost,place);
// Increment timestamp
EQ(ei)++;
// Replace in queue
Q.emplace(cost,ei,EQ(ei));
C.row(ei) = place;
}
}else
{
// reinsert with infinite weight (the provided cost function must **not**
// have given this un-collapsable edge inf cost already)
// Increment timestamp
EQ(e)++;
// Replace in queue
Q.emplace(std::numeric_limits<double>::infinity(),e,EQ(e));
}
return collapsed;
}
+82
View File
@@ -0,0 +1,82 @@
// 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/.
#ifndef IGL_COLLAPSE_LEAST_COST_EDGE_H
#define IGL_COLLAPSE_LEAST_COST_EDGE_H
#include "igl_inline.h"
#include "min_heap.h"
#include "decimate_callback_types.h"
#include "COLLAPSE_EDGE_NULL.h"
#include <Eigen/Core>
#include <vector>
namespace igl
{
/// Collapse least-cost edge from a priority queue and update queue
///
/// See decimate.h for more details.
///
/// @param[in] cost_and_placement function computing cost of collapsing an edge and 3d
/// position where it should be placed:
/// cost_and_placement(V,F,E,EMAP,EF,EI,cost,placement);
/// **If the edges is collapsed** then this function will be called on all
/// edges of all faces previously incident on the endpoints of the
/// collapsed edge.
/// @param[in] pre_collapse callback called with index of edge whose collapse is about
/// to be attempted. This function should return whether to **proceed**
/// with the collapse: returning true means "yes, try to collapse",
/// returning false means "No, consider this edge 'uncollapsable', behave
/// as if collapse_edge(e) returned false.
/// @param[in] post_collapse callback called with index of edge whose collapse was
/// just attempted and a flag revealing whether this was successful.
/// @param[in,out] V #V by dim list of vertex positions, lesser index of E(e,:) will be set
/// to midpoint of edge.
/// @param[in,out] F #F by 3 list of face indices into V.
/// @param[in,out] E #E by 2 list of edge indices into V.
/// @param[in,out] EMAP #F*3 list of indices into E, mapping each directed edge to unique
/// unique edge in E
/// @param[in,out] EF #E by 2 list of edge flaps, EF(e,0)=f means e=(i-->j) is the edge of
/// F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1)
/// e=(j->i)
/// @param[in,out] EI #E by 2 list of edge flap corners (see above).
/// @param[in] Q queue containing pairs of costs and edge indices and insertion "time"
/// @param[in] EQ #E list of "time" of last time pushed into Q
/// @param[in] C #E by dim list of stored placements
/// @param[out] e index into E of attempted collapsed edge. Set to -1 if Q is empty or
/// contains only infinite cost edges.
/// @param[out] e1 index into E of edge collapsed on left.
/// @param[out] e2 index into E of edge collapsed on right.
/// @param[out] f1 index into F of face collapsed on left.
/// @param[out] f2 index into F of face collapsed on right.
///
/// \bug This function is not templated nicely and refactoring it and its
/// dependencies to do so is non-trivial, see
/// https://github.com/libigl/libigl/issues/2452
IGL_INLINE bool collapse_least_cost_edge(
const decimate_cost_and_placement_callback & cost_and_placement,
const decimate_pre_collapse_callback & pre_collapse,
const decimate_post_collapse_callback & post_collapse,
Eigen::MatrixXd & V,
Eigen::MatrixXi & F,
Eigen::MatrixXi & E,
Eigen::VectorXi & EMAP,
Eigen::MatrixXi & EF,
Eigen::MatrixXi & EI,
igl::min_heap< std::tuple<double,int,int> > & Q,
Eigen::VectorXi & EQ,
Eigen::MatrixXd & C,
int & e,
int & e1,
int & e2,
int & f1,
int & f2);
}
#ifndef IGL_STATIC_LIBRARY
# include "collapse_least_cost_edge.cpp"
#endif
#endif
+18 -10
View File
@@ -13,24 +13,27 @@
#include "colon.h"
#include "faces_first.h"
#include <cassert>
#include <limits>
#include <iostream>
template <
typename DerivedV,
typename DerivedF,
typename DerivedFF>
void igl::collapse_small_triangles(
const Eigen::MatrixXd & V,
const Eigen::MatrixXi & F,
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F,
const double eps,
Eigen::MatrixXi & FF)
Eigen::PlainObjectBase<DerivedFF> & FF)
{
using namespace Eigen;
using namespace std;
// Compute bounding box diagonal length
double bbd = bounding_box_diagonal(V);
MatrixXd l;
Eigen::MatrixXd l;
edge_lengths(V,F,l);
VectorXd dblA;
Eigen::VectorXd dblA;
doublearea(l,0.,dblA);
// Minimum area tolerance
@@ -81,7 +84,7 @@ void igl::collapse_small_triangles(
}
// Reindex faces
MatrixXi rF = F;
Eigen::MatrixXi rF = F;
// Loop over triangles
for(int f = 0;f<rF.rows();f++)
{
@@ -138,6 +141,11 @@ void igl::collapse_small_triangles(
//// force base case
//return;
MatrixXi recFF = FF;
Eigen::MatrixXi recFF = FF;
return collapse_small_triangles(V,recFF,eps,FF);
}
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
template void igl::collapse_small_triangles<Eigen::MatrixXd, Eigen::MatrixXi, Eigen::MatrixXi> ( const Eigen::MatrixBase<Eigen::MatrixXd> &, const Eigen::MatrixBase<Eigen::MatrixXi> &, const double , Eigen::PlainObjectBase<Eigen::MatrixXi> & );
#endif
+7 -3
View File
@@ -24,11 +24,15 @@ namespace igl
/// @param[out] FF #FF by 3 list of triangle indices into V
///
///
template <
typename DerivedV,
typename DerivedF,
typename DerivedFF>
void collapse_small_triangles(
const Eigen::MatrixXd & V,
const Eigen::MatrixXi & F,
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F,
const double eps,
Eigen::MatrixXi & FF);
Eigen::PlainObjectBase<DerivedFF> & FF);
}
#ifndef IGL_STATIC_LIBRARY

Some files were not shown because too many files have changed in this diff Show More