duanbotu123 commited on
Commit
f6dd1c2
·
0 Parent(s):

Initial commit: add index.html

Browse files
Files changed (50) hide show
  1. .gitattributes +39 -0
  2. .gitignore +28 -0
  3. 3rdparty/nanoflann.hpp +1305 -0
  4. CMakeLists.txt +156 -0
  5. README.md +14 -0
  6. app.py +638 -0
  7. apt.txt +16 -0
  8. build.sh +22 -0
  9. cmake/FindEigen3.cmake +19 -0
  10. cmake/FindNanoFlann.cmake +24 -0
  11. cmake/FindOpenMesh.cmake +27 -0
  12. cpp/core/nonrigid_spare_registration.cpp +1013 -0
  13. cpp/core/nonrigid_spare_registration.h +142 -0
  14. cpp/core/registration.cpp +279 -0
  15. cpp/core/registration.h +84 -0
  16. cpp/core/rigid_fricp_registration.cpp +448 -0
  17. cpp/core/rigid_fricp_registration.h +48 -0
  18. cpp/io/io.h +94 -0
  19. cpp/pch.h +21 -0
  20. cpp/tools/AndersonAcceleration.h +132 -0
  21. cpp/tools/OmpHelper.h +79 -0
  22. cpp/tools/Types.h +248 -0
  23. cpp/tools/geodesic/geodesic_algorithm_base.h +417 -0
  24. cpp/tools/geodesic/geodesic_algorithm_exact.h +918 -0
  25. cpp/tools/geodesic/geodesic_algorithm_exact_elements.h +152 -0
  26. cpp/tools/geodesic/geodesic_constants_and_simple_functions.h +153 -0
  27. cpp/tools/geodesic/geodesic_memory.h +192 -0
  28. cpp/tools/median.h +61 -0
  29. cpp/tools/nanoflann.h +108 -0
  30. cpp/tools/nodeSampler.cpp +638 -0
  31. cpp/tools/nodeSampler.h +94 -0
  32. cpp/tools/parameters.h +92 -0
  33. cpp/tools/robust_norm.h +37 -0
  34. cpp/tools/tools.cpp +85 -0
  35. cpp/tools/tools.h +12 -0
  36. docs/.gitkeep +1 -0
  37. examples/.gitkeep +1 -0
  38. examples/c++/CMakeLists.txt +16 -0
  39. examples/c++/main.cpp +128 -0
  40. examples/data/fricp/fricpm3reg_pc.ply +0 -0
  41. examples/data/fricp/fricpm3trans.txt +4 -0
  42. examples/data/fricp/source.ply +0 -0
  43. examples/data/fricp/target.ply +0 -0
  44. examples/data/spare/source.obj +0 -0
  45. examples/data/spare/target.obj +0 -0
  46. examples/python/example_python.py +172 -0
  47. output/.gitkeep +1 -0
  48. python/pyregister.cpp +88 -0
  49. requirements.txt +5 -0
  50. scripts/compile.sh +5 -0
.gitattributes ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.so filter=lfs diff=lfs merge=lfs -text
37
+ *.stl filter=lfs diff=lfs merge=lfs -text
38
+ *.pm filter=lfs diff=lfs merge=lfs -text
39
+ *.om filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 构建目录
2
+ build/
3
+
4
+ # CMake 生成文件
5
+ CMakeFiles/
6
+ CMakeCache.txt
7
+ cmake_install.cmake
8
+ Makefile
9
+
10
+ # Visual Studio 编译产物
11
+ *.o
12
+ *.exe
13
+ *.pdb
14
+ *.lib
15
+ *.dll
16
+ .vs/
17
+ *.vcxproj*
18
+ *.sln
19
+
20
+ # Build outputs
21
+ build/
22
+ *.pdb
23
+ *.lib
24
+ *.ipch
25
+ .vs/
26
+
27
+ # VSCode 配置目录
28
+ .vscode/
3rdparty/nanoflann.hpp ADDED
@@ -0,0 +1,1305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***********************************************************************
2
+ * Software License Agreement (BSD License)
3
+ *
4
+ * Copyright 2008-2009 Marius Muja ([email protected]). All rights reserved.
5
+ * Copyright 2008-2009 David G. Lowe ([email protected]). All rights reserved.
6
+ * Copyright 2011 Jose Luis Blanco ([email protected]).
7
+ * All rights reserved.
8
+ *
9
+ * THE BSD LICENSE
10
+ *
11
+ * Redistribution and use in source and binary forms, with or without
12
+ * modification, are permitted provided that the following conditions
13
+ * are met:
14
+ *
15
+ * 1. Redistributions of source code must retain the above copyright
16
+ * notice, this list of conditions and the following disclaimer.
17
+ * 2. Redistributions in binary form must reproduce the above copyright
18
+ * notice, this list of conditions and the following disclaimer in the
19
+ * documentation and/or other materials provided with the distribution.
20
+ *
21
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ *************************************************************************/
32
+
33
+ #ifndef NANOFLANN_HPP_
34
+ #define NANOFLANN_HPP_
35
+
36
+ #include <vector>
37
+ #include <cassert>
38
+ #include <algorithm>
39
+ #include <stdexcept>
40
+ #include <cstdio> // for fwrite()
41
+ #include <cmath> // for fabs(),...
42
+ #include <limits>
43
+
44
+ // Avoid conflicting declaration of min/max macros in windows headers
45
+ #if !defined(NOMINMAX) && (defined(_WIN32) || defined(_WIN32_) || defined(WIN32) || defined(_WIN64))
46
+ # define NOMINMAX
47
+ # ifdef max
48
+ # undef max
49
+ # undef min
50
+ # endif
51
+ #endif
52
+
53
+ namespace nanoflann
54
+ {
55
+ /** @addtogroup nanoflann_grp nanoflann C++ library for ANN
56
+ * @{ */
57
+
58
+ /** Library version: 0xMmP (M=Major,m=minor,P=path) */
59
+ #define NANOFLANN_VERSION 0x113
60
+
61
+ /** @addtogroup result_sets_grp Result set classes
62
+ * @{ */
63
+ template <typename DistanceType, typename IndexType = size_t, typename CountType = size_t>
64
+ class KNNResultSet
65
+ {
66
+ IndexType * indices;
67
+ DistanceType* dists;
68
+ CountType capacity;
69
+ CountType count;
70
+
71
+ public:
72
+ inline KNNResultSet(CountType capacity_) : capacity(capacity_), count(0)
73
+ {
74
+ }
75
+
76
+ inline void init(IndexType* indices_, DistanceType* dists_)
77
+ {
78
+ indices = indices_;
79
+ dists = dists_;
80
+ count = 0;
81
+ dists[capacity-1] = (std::numeric_limits<DistanceType>::max)();
82
+ }
83
+
84
+ inline CountType size() const
85
+ {
86
+ return count;
87
+ }
88
+
89
+ inline bool full() const
90
+ {
91
+ return count == capacity;
92
+ }
93
+
94
+
95
+ inline void addPoint(DistanceType dist, IndexType index)
96
+ {
97
+ CountType i;
98
+ for (i=count; i>0; --i) {
99
+ #ifdef NANOFLANN_FIRST_MATCH // If defined and two poins have the same distance, the one with the lowest-index will be returned first.
100
+ if ( (dists[i-1]>dist) || ((dist==dists[i-1])&&(indices[i-1]>index)) ) {
101
+ #else
102
+ if (dists[i-1]>dist) {
103
+ #endif
104
+ if (i<capacity) {
105
+ dists[i] = dists[i-1];
106
+ indices[i] = indices[i-1];
107
+ }
108
+ }
109
+ else break;
110
+ }
111
+ if (i<capacity) {
112
+ dists[i] = dist;
113
+ indices[i] = index;
114
+ }
115
+ if (count<capacity) count++;
116
+ }
117
+
118
+ inline DistanceType worstDist() const
119
+ {
120
+ return dists[capacity-1];
121
+ }
122
+ };
123
+
124
+
125
+ /**
126
+ * A result-set class used when performing a radius based search.
127
+ */
128
+ template <typename DistanceType, typename IndexType = size_t>
129
+ class RadiusResultSet
130
+ {
131
+ public:
132
+ const DistanceType radius;
133
+
134
+ std::vector<std::pair<IndexType,DistanceType> >& m_indices_dists;
135
+
136
+ inline RadiusResultSet(DistanceType radius_, std::vector<std::pair<IndexType,DistanceType> >& indices_dists) : radius(radius_), m_indices_dists(indices_dists)
137
+ {
138
+ init();
139
+ }
140
+
141
+ inline ~RadiusResultSet() { }
142
+
143
+ inline void init() { clear(); }
144
+ inline void clear() { m_indices_dists.clear(); }
145
+
146
+ inline size_t size() const { return m_indices_dists.size(); }
147
+
148
+ inline bool full() const { return true; }
149
+
150
+ inline void addPoint(DistanceType dist, IndexType index)
151
+ {
152
+ if (dist<radius)
153
+ m_indices_dists.push_back(std::make_pair<IndexType,DistanceType>(index,dist));
154
+ }
155
+
156
+ inline DistanceType worstDist() const { return radius; }
157
+
158
+ /** Clears the result set and adjusts the search radius. */
159
+ inline void set_radius_and_clear( const DistanceType r )
160
+ {
161
+ radius = r;
162
+ clear();
163
+ }
164
+
165
+ /**
166
+ * Find the worst result (furtherest neighbor) without copying or sorting
167
+ * Pre-conditions: size() > 0
168
+ */
169
+ std::pair<IndexType,DistanceType> worst_item() const
170
+ {
171
+ if (m_indices_dists.empty()) throw std::runtime_error("Cannot invoke RadiusResultSet::worst_item() on an empty list of results.");
172
+ typedef typename std::vector<std::pair<IndexType,DistanceType> >::const_iterator DistIt;
173
+ DistIt it = std::max_element(m_indices_dists.begin(), m_indices_dists.end());
174
+ return *it;
175
+ }
176
+ };
177
+
178
+ /** operator "<" for std::sort() */
179
+ struct IndexDist_Sorter
180
+ {
181
+ /** PairType will be typically: std::pair<IndexType,DistanceType> */
182
+ template <typename PairType>
183
+ inline bool operator()(const PairType &p1, const PairType &p2) const {
184
+ return p1.second < p2.second;
185
+ }
186
+ };
187
+
188
+ /** @} */
189
+
190
+
191
+ /** @addtogroup loadsave_grp Load/save auxiliary functions
192
+ * @{ */
193
+ template<typename T>
194
+ void save_value(FILE* stream, const T& value, size_t count = 1)
195
+ {
196
+ fwrite(&value, sizeof(value),count, stream);
197
+ }
198
+
199
+ template<typename T>
200
+ void save_value(FILE* stream, const std::vector<T>& value)
201
+ {
202
+ size_t size = value.size();
203
+ fwrite(&size, sizeof(size_t), 1, stream);
204
+ fwrite(&value[0], sizeof(T), size, stream);
205
+ }
206
+
207
+ template<typename T>
208
+ void load_value(FILE* stream, T& value, size_t count = 1)
209
+ {
210
+ size_t read_cnt = fread(&value, sizeof(value), count, stream);
211
+ if (read_cnt != count) {
212
+ throw std::runtime_error("Cannot read from file");
213
+ }
214
+ }
215
+
216
+
217
+ template<typename T>
218
+ void load_value(FILE* stream, std::vector<T>& value)
219
+ {
220
+ size_t size;
221
+ size_t read_cnt = fread(&size, sizeof(size_t), 1, stream);
222
+ if (read_cnt!=1) {
223
+ throw std::runtime_error("Cannot read from file");
224
+ }
225
+ value.resize(size);
226
+ read_cnt = fread(&value[0], sizeof(T), size, stream);
227
+ if (read_cnt!=size) {
228
+ throw std::runtime_error("Cannot read from file");
229
+ }
230
+ }
231
+ /** @} */
232
+
233
+
234
+ /** @addtogroup metric_grp Metric (distance) classes
235
+ * @{ */
236
+
237
+ template<typename T> inline T abs(T x) { return (x<0) ? -x : x; }
238
+ template<> inline int abs<int>(int x) { return ::abs(x); }
239
+ template<> inline float abs<float>(float x) { return fabsf(x); }
240
+ template<> inline double abs<double>(double x) { return fabs(x); }
241
+ template<> inline long double abs<long double>(long double x) { return fabsl(x); }
242
+
243
+ /** Manhattan distance functor (generic version, optimized for high-dimensionality data sets).
244
+ * Corresponding distance traits: nanoflann::metric_L1
245
+ * \tparam T Type of the elements (e.g. double, float, uint8_t)
246
+ * \tparam DistanceType Type of distance variables (must be signed) (e.g. float, double, int64_t)
247
+ */
248
+ template<class T, class DataSource, typename _DistanceType = T>
249
+ struct L1_Adaptor
250
+ {
251
+ typedef T ElementType;
252
+ typedef _DistanceType DistanceType;
253
+
254
+ const DataSource &data_source;
255
+
256
+ L1_Adaptor(const DataSource &_data_source) : data_source(_data_source) { }
257
+
258
+ inline DistanceType operator()(const T* a, const size_t b_idx, size_t size, DistanceType worst_dist = -1) const
259
+ {
260
+ DistanceType result = DistanceType();
261
+ const T* last = a + size;
262
+ const T* lastgroup = last - 3;
263
+ size_t d = 0;
264
+
265
+ /* Process 4 items with each loop for efficiency. */
266
+ while (a < lastgroup) {
267
+ const DistanceType diff0 = nanoflann::abs(a[0] - data_source.kdtree_get_pt(b_idx,d++));
268
+ const DistanceType diff1 = nanoflann::abs(a[1] - data_source.kdtree_get_pt(b_idx,d++));
269
+ const DistanceType diff2 = nanoflann::abs(a[2] - data_source.kdtree_get_pt(b_idx,d++));
270
+ const DistanceType diff3 = nanoflann::abs(a[3] - data_source.kdtree_get_pt(b_idx,d++));
271
+ result += diff0 + diff1 + diff2 + diff3;
272
+ a += 4;
273
+ if ((worst_dist>0)&&(result>worst_dist)) {
274
+ return result;
275
+ }
276
+ }
277
+ /* Process last 0-3 components. Not needed for standard vector lengths. */
278
+ while (a < last) {
279
+ result += nanoflann::abs( *a++ - data_source.kdtree_get_pt(b_idx,d++) );
280
+ }
281
+ return result;
282
+ }
283
+
284
+ template <typename U, typename V>
285
+ inline DistanceType accum_dist(const U a, const V b, int dim) const
286
+ {
287
+ return (a-b)*(a-b);
288
+ }
289
+ };
290
+
291
+ /** Squared Euclidean distance functor (generic version, optimized for high-dimensionality data sets).
292
+ * Corresponding distance traits: nanoflann::metric_L2
293
+ * \tparam T Type of the elements (e.g. double, float, uint8_t)
294
+ * \tparam DistanceType Type of distance variables (must be signed) (e.g. float, double, int64_t)
295
+ */
296
+ template<class T, class DataSource, typename _DistanceType = T>
297
+ struct L2_Adaptor
298
+ {
299
+ typedef T ElementType;
300
+ typedef _DistanceType DistanceType;
301
+
302
+ const DataSource &data_source;
303
+
304
+ L2_Adaptor(const DataSource &_data_source) : data_source(_data_source) { }
305
+
306
+ inline DistanceType operator()(const T* a, const size_t b_idx, size_t size, DistanceType worst_dist = -1) const
307
+ {
308
+ DistanceType result = DistanceType();
309
+ const T* last = a + size;
310
+ const T* lastgroup = last - 3;
311
+ size_t d = 0;
312
+
313
+ /* Process 4 items with each loop for efficiency. */
314
+ while (a < lastgroup) {
315
+ const DistanceType diff0 = a[0] - data_source.kdtree_get_pt(b_idx,d++);
316
+ const DistanceType diff1 = a[1] - data_source.kdtree_get_pt(b_idx,d++);
317
+ const DistanceType diff2 = a[2] - data_source.kdtree_get_pt(b_idx,d++);
318
+ const DistanceType diff3 = a[3] - data_source.kdtree_get_pt(b_idx,d++);
319
+ result += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3;
320
+ a += 4;
321
+ if ((worst_dist>0)&&(result>worst_dist)) {
322
+ return result;
323
+ }
324
+ }
325
+ /* Process last 0-3 components. Not needed for standard vector lengths. */
326
+ while (a < last) {
327
+ const DistanceType diff0 = *a++ - data_source.kdtree_get_pt(b_idx,d++);
328
+ result += diff0 * diff0;
329
+ }
330
+ return result;
331
+ }
332
+
333
+ template <typename U, typename V>
334
+ inline DistanceType accum_dist(const U a, const V b, int dim) const
335
+ {
336
+ return (a-b)*(a-b);
337
+ }
338
+ };
339
+
340
+ /** Squared Euclidean distance functor (suitable for low-dimensionality datasets, like 2D or 3D point clouds)
341
+ * Corresponding distance traits: nanoflann::metric_L2_Simple
342
+ * \tparam T Type of the elements (e.g. double, float, uint8_t)
343
+ * \tparam DistanceType Type of distance variables (must be signed) (e.g. float, double, int64_t)
344
+ */
345
+ template<class T, class DataSource, typename _DistanceType = T>
346
+ struct L2_Simple_Adaptor
347
+ {
348
+ typedef T ElementType;
349
+ typedef _DistanceType DistanceType;
350
+
351
+ const DataSource &data_source;
352
+
353
+ L2_Simple_Adaptor(const DataSource &_data_source) : data_source(_data_source) { }
354
+
355
+ inline DistanceType operator()(const T* a, const size_t b_idx, size_t size) const {
356
+ return data_source.kdtree_distance(a,b_idx,size);
357
+ }
358
+
359
+ template <typename U, typename V>
360
+ inline DistanceType accum_dist(const U a, const V b, int dim) const
361
+ {
362
+ dim = dim;
363
+ return (a-b)*(a-b);
364
+ }
365
+ };
366
+
367
+ /** Metaprogramming helper traits class for the L1 (Manhattan) metric */
368
+ struct metric_L1 {
369
+ template<class T, class DataSource>
370
+ struct traits {
371
+ typedef L1_Adaptor<T,DataSource> distance_t;
372
+ };
373
+ };
374
+ /** Metaprogramming helper traits class for the L2 (Euclidean) metric */
375
+ struct metric_L2 {
376
+ template<class T, class DataSource>
377
+ struct traits {
378
+ typedef L2_Adaptor<T,DataSource> distance_t;
379
+ };
380
+ };
381
+ /** Metaprogramming helper traits class for the L2_simple (Euclidean) metric */
382
+ struct metric_L2_Simple {
383
+ template<class T, class DataSource>
384
+ struct traits {
385
+ typedef L2_Simple_Adaptor<T,DataSource> distance_t;
386
+ };
387
+ };
388
+
389
+ /** @} */
390
+
391
+
392
+
393
+ /** @addtogroup param_grp Parameter structs
394
+ * @{ */
395
+
396
+ /** Parameters (see http://code.google.com/p/nanoflann/ for help choosing the parameters)
397
+ */
398
+ struct KDTreeSingleIndexAdaptorParams
399
+ {
400
+ KDTreeSingleIndexAdaptorParams(size_t _leaf_max_size = 10, int dim_ = -1) :
401
+ leaf_max_size(_leaf_max_size), dim(dim_)
402
+ {}
403
+
404
+ size_t leaf_max_size;
405
+ int dim;
406
+ };
407
+
408
+ /** Search options for KDTreeSingleIndexAdaptor::findNeighbors() */
409
+ struct SearchParams
410
+ {
411
+ /** Note: The first argument (checks_IGNORED_) is ignored, but kept for compatibility with the FLANN interface */
412
+ SearchParams(int checks_IGNORED_ = 32, float eps_ = 0, bool sorted_ = true ) :
413
+ eps(eps_), sorted(sorted_) {
414
+ checks_IGNORED_ = checks_IGNORED_;
415
+ }
416
+
417
+ int checks; //!< Ignored parameter (Kept for compatibility with the FLANN interface).
418
+ float eps; //!< search for eps-approximate neighbours (default: 0)
419
+ bool sorted; //!< only for radius search, require neighbours sorted by distance (default: true)
420
+ };
421
+ /** @} */
422
+
423
+
424
+ /** @addtogroup memalloc_grp Memory allocation
425
+ * @{ */
426
+
427
+ /**
428
+ * Allocates (using C's malloc) a generic type T.
429
+ *
430
+ * Params:
431
+ * count = number of instances to allocate.
432
+ * Returns: pointer (of type T*) to memory buffer
433
+ */
434
+ template <typename T>
435
+ inline T* allocate(size_t count = 1)
436
+ {
437
+ T* mem = (T*) ::malloc(sizeof(T)*count);
438
+ return mem;
439
+ }
440
+
441
+
442
+ /**
443
+ * Pooled storage allocator
444
+ *
445
+ * The following routines allow for the efficient allocation of storage in
446
+ * small chunks from a specified pool. Rather than allowing each structure
447
+ * to be freed individually, an entire pool of storage is freed at once.
448
+ * This method has two advantages over just using malloc() and free(). First,
449
+ * it is far more efficient for allocating small objects, as there is
450
+ * no overhead for remembering all the information needed to free each
451
+ * object or consolidating fragmented memory. Second, the decision about
452
+ * how long to keep an object is made at the time of allocation, and there
453
+ * is no need to track down all the objects to free them.
454
+ *
455
+ */
456
+
457
+ const size_t WORDSIZE=16;
458
+ const size_t BLOCKSIZE=8192;
459
+
460
+ class PooledAllocator
461
+ {
462
+ /* We maintain memory alignment to word boundaries by requiring that all
463
+ allocations be in multiples of the machine wordsize. */
464
+ /* Size of machine word in bytes. Must be power of 2. */
465
+ /* Minimum number of bytes requested at a time from the system. Must be multiple of WORDSIZE. */
466
+
467
+
468
+ size_t remaining; /* Number of bytes left in current block of storage. */
469
+ void* base; /* Pointer to base of current block of storage. */
470
+ void* loc; /* Current location in block to next allocate memory. */
471
+ size_t blocksize;
472
+
473
+
474
+ public:
475
+ size_t usedMemory;
476
+ size_t wastedMemory;
477
+
478
+ /**
479
+ Default constructor. Initializes a new pool.
480
+ */
481
+ PooledAllocator(const size_t blocksize = BLOCKSIZE)
482
+ {
483
+ this->blocksize = blocksize;
484
+ remaining = 0;
485
+ base = NULL;
486
+
487
+ usedMemory = 0;
488
+ wastedMemory = 0;
489
+ }
490
+
491
+ /**
492
+ * Destructor. Frees all the memory allocated in this pool.
493
+ */
494
+ ~PooledAllocator()
495
+ {
496
+ while (base != NULL) {
497
+ void *prev = *((void**) base); /* Get pointer to prev block. */
498
+ ::free(base);
499
+ base = prev;
500
+ }
501
+ }
502
+
503
+ /**
504
+ * Returns a pointer to a piece of new memory of the given size in bytes
505
+ * allocated from the pool.
506
+ */
507
+ void* malloc(const size_t req_size)
508
+ {
509
+ /* Round size up to a multiple of wordsize. The following expression
510
+ only works for WORDSIZE that is a power of 2, by masking last bits of
511
+ incremented size to zero.
512
+ */
513
+ const size_t size = (req_size + (WORDSIZE - 1)) & ~(WORDSIZE - 1);
514
+
515
+ /* Check whether a new block must be allocated. Note that the first word
516
+ of a block is reserved for a pointer to the previous block.
517
+ */
518
+ if (size > remaining) {
519
+
520
+ wastedMemory += remaining;
521
+
522
+ /* Allocate new storage. */
523
+ const size_t blocksize = (size + sizeof(void*) + (WORDSIZE-1) > BLOCKSIZE) ?
524
+ size + sizeof(void*) + (WORDSIZE-1) : BLOCKSIZE;
525
+
526
+ // use the standard C malloc to allocate memory
527
+ void* m = ::malloc(blocksize);
528
+ if (!m) {
529
+ fprintf(stderr,"Failed to allocate memory.\n");
530
+ return NULL;
531
+ }
532
+
533
+ /* Fill first word of new block with pointer to previous block. */
534
+ ((void**) m)[0] = base;
535
+ base = m;
536
+
537
+ size_t shift = 0;
538
+ //int size_t = (WORDSIZE - ( (((size_t)m) + sizeof(void*)) & (WORDSIZE-1))) & (WORDSIZE-1);
539
+
540
+ remaining = blocksize - sizeof(void*) - shift;
541
+ loc = ((char*)m + sizeof(void*) + shift);
542
+ }
543
+ void* rloc = loc;
544
+ loc = (char*)loc + size;
545
+ remaining -= size;
546
+
547
+ usedMemory += size;
548
+
549
+ return rloc;
550
+ }
551
+
552
+ /**
553
+ * Allocates (using this pool) a generic type T.
554
+ *
555
+ * Params:
556
+ * count = number of instances to allocate.
557
+ * Returns: pointer (of type T*) to memory buffer
558
+ */
559
+ template <typename T>
560
+ T* allocate(const size_t count = 1)
561
+ {
562
+ T* mem = (T*) this->malloc(sizeof(T)*count);
563
+ return mem;
564
+ }
565
+
566
+ };
567
+ /** @} */
568
+
569
+
570
+ /** @addtogroup kdtrees_grp KD-tree classes and adaptors
571
+ * @{ */
572
+
573
+ /** kd-tree index
574
+ *
575
+ * Contains the k-d trees and other information for indexing a set of points
576
+ * for nearest-neighbor matching.
577
+ *
578
+ * The class "DatasetAdaptor" must provide the following interface (can be non-virtual, inlined methods):
579
+ *
580
+ * \code
581
+ * // Must return the number of data points
582
+ * inline size_t kdtree_get_point_count() const { ... }
583
+ *
584
+ * // Must return the Euclidean (L2) distance between the vector "p1[0:size-1]" and the data point with index "idx_p2" stored in the class:
585
+ * inline DistanceType kdtree_distance(const T *p1, const size_t idx_p2,size_t size) const { ... }
586
+ *
587
+ * // Must return the dim'th component of the idx'th point in the class:
588
+ * inline T kdtree_get_pt(const size_t idx, int dim) const { ... }
589
+ *
590
+ * // Optional bounding-box computation: return false to default to a standard bbox computation loop.
591
+ * // Return true if the BBOX was already computed by the class and returned in "bb" so it can be avoided to redo it again.
592
+ * // Look at bb.size() to find out the expected dimensionality (e.g. 2 or 3 for point clouds)
593
+ * template <class BBOX>
594
+ * bool kdtree_get_bbox(BBOX &bb) const
595
+ * {
596
+ * bb[0].low = ...; bb[0].high = ...; // 0th dimension limits
597
+ * bb[1].low = ...; bb[1].high = ...; // 1st dimension limits
598
+ * ...
599
+ * return true;
600
+ * }
601
+ *
602
+ * \endcode
603
+ *
604
+ * \tparam IndexType Will be typically size_t or int
605
+ */
606
+ template <typename Distance, class DatasetAdaptor,int DIM = -1, typename IndexType = size_t>
607
+ class KDTreeSingleIndexAdaptor
608
+ {
609
+ public:
610
+ typedef typename Distance::ElementType ElementType;
611
+ typedef typename Distance::DistanceType DistanceType;
612
+ protected:
613
+
614
+ /**
615
+ * Array of indices to vectors in the dataset.
616
+ */
617
+ std::vector<IndexType> vind;
618
+
619
+ size_t m_leaf_max_size;
620
+
621
+
622
+ /**
623
+ * The dataset used by this index
624
+ */
625
+ const DatasetAdaptor &dataset; //!< The source of our data
626
+
627
+ const KDTreeSingleIndexAdaptorParams index_params;
628
+
629
+ size_t m_size;
630
+ int dim; //!< Dimensionality of each data point
631
+
632
+
633
+ /*--------------------- Internal Data Structures --------------------------*/
634
+ struct Node
635
+ {
636
+ union {
637
+ struct
638
+ {
639
+ /**
640
+ * Indices of points in leaf node
641
+ */
642
+ IndexType left, right;
643
+ } lr;
644
+ struct
645
+ {
646
+ /**
647
+ * Dimension used for subdivision.
648
+ */
649
+ int divfeat;
650
+ /**
651
+ * The values used for subdivision.
652
+ */
653
+ DistanceType divlow, divhigh;
654
+ } sub;
655
+ };
656
+ /**
657
+ * The child nodes.
658
+ */
659
+ Node* child1, * child2;
660
+ };
661
+ typedef Node* NodePtr;
662
+
663
+
664
+ struct Interval
665
+ {
666
+ ElementType low, high;
667
+ };
668
+
669
+ typedef std::vector<Interval> BoundingBox;
670
+
671
+
672
+ /** This record represents a branch point when finding neighbors in
673
+ the tree. It contains a record of the minimum distance to the query
674
+ point, as well as the node at which the search resumes.
675
+ */
676
+ template <typename T, typename DistanceType>
677
+ struct BranchStruct
678
+ {
679
+ T node; /* Tree node at which search resumes */
680
+ DistanceType mindist; /* Minimum distance to query for all nodes below. */
681
+
682
+ BranchStruct() {}
683
+ BranchStruct(const T& aNode, DistanceType dist) : node(aNode), mindist(dist) {}
684
+
685
+ inline bool operator<(const BranchStruct<T, DistanceType>& rhs) const
686
+ {
687
+ return mindist<rhs.mindist;
688
+ }
689
+ };
690
+
691
+ /**
692
+ * Array of k-d trees used to find neighbours.
693
+ */
694
+ NodePtr root_node;
695
+ typedef BranchStruct<NodePtr, DistanceType> BranchSt;
696
+ typedef BranchSt* Branch;
697
+
698
+ BoundingBox root_bbox;
699
+
700
+ /**
701
+ * Pooled memory allocator.
702
+ *
703
+ * Using a pooled memory allocator is more efficient
704
+ * than allocating memory directly when there is a large
705
+ * number small of memory allocations.
706
+ */
707
+ PooledAllocator pool;
708
+
709
+ public:
710
+
711
+ Distance distance;
712
+
713
+ /**
714
+ * KDTree constructor
715
+ *
716
+ * Params:
717
+ * inputData = dataset with the input features
718
+ * params = parameters passed to the kdtree algorithm (see http://code.google.com/p/nanoflann/ for help choosing the parameters)
719
+ */
720
+ KDTreeSingleIndexAdaptor(const int dimensionality, const DatasetAdaptor& inputData, const KDTreeSingleIndexAdaptorParams& params = KDTreeSingleIndexAdaptorParams() ) :
721
+ dataset(inputData), index_params(params), distance(inputData)
722
+ {
723
+ m_size = dataset.kdtree_get_point_count();
724
+ dim = dimensionality;
725
+ if (DIM>0) dim=DIM;
726
+ else {
727
+ if (params.dim>0) dim = params.dim;
728
+ }
729
+ m_leaf_max_size = params.leaf_max_size;
730
+
731
+ // Create a permutable array of indices to the input vectors.
732
+ init_vind();
733
+ }
734
+
735
+ /**
736
+ * Standard destructor
737
+ */
738
+ ~KDTreeSingleIndexAdaptor()
739
+ {
740
+ }
741
+
742
+ /**
743
+ * Builds the index
744
+ */
745
+ void buildIndex()
746
+ {
747
+ init_vind();
748
+ computeBoundingBox(root_bbox);
749
+ root_node = divideTree(0, m_size, root_bbox ); // construct the tree
750
+ }
751
+
752
+ /**
753
+ * Returns size of index.
754
+ */
755
+ size_t size() const
756
+ {
757
+ return m_size;
758
+ }
759
+
760
+ /**
761
+ * Returns the length of an index feature.
762
+ */
763
+ size_t veclen() const
764
+ {
765
+ return static_cast<size_t>(DIM>0 ? DIM : dim);
766
+ }
767
+
768
+ /**
769
+ * Computes the inde memory usage
770
+ * Returns: memory used by the index
771
+ */
772
+ size_t usedMemory() const
773
+ {
774
+ return pool.usedMemory+pool.wastedMemory+dataset.kdtree_get_point_count()*sizeof(IndexType); // pool memory and vind array memory
775
+ }
776
+
777
+ /** \name Query methods
778
+ * @{ */
779
+
780
+ /**
781
+ * Find set of nearest neighbors to vec[0:dim-1]. Their indices are stored inside
782
+ * the result object.
783
+ *
784
+ * Params:
785
+ * result = the result object in which the indices of the nearest-neighbors are stored
786
+ * vec = the vector for which to search the nearest neighbors
787
+ *
788
+ * \tparam RESULTSET Should be any ResultSet<DistanceType>
789
+ * \sa knnSearch, radiusSearch
790
+ */
791
+ template <typename RESULTSET>
792
+ void findNeighbors(RESULTSET& result, const ElementType* vec, const SearchParams& searchParams) const
793
+ {
794
+ assert(vec);
795
+ float epsError = 1+searchParams.eps;
796
+
797
+ std::vector<DistanceType> dists( (DIM>0 ? DIM : dim) ,0);
798
+ DistanceType distsq = computeInitialDistances(vec, dists);
799
+ searchLevel(result, vec, root_node, distsq, dists, epsError); // "count_leaf" parameter removed since was neither used nor returned to the user.
800
+ }
801
+
802
+ /**
803
+ * Find the "num_closest" nearest neighbors to the \a query_point[0:dim-1]. Their indices are stored inside
804
+ * the result object.
805
+ * \sa radiusSearch, findNeighbors
806
+ * \note nChecks_IGNORED is ignored but kept for compatibility with the original FLANN interface.
807
+ */
808
+ inline void knnSearch(const ElementType *query_point, const size_t num_closest, IndexType *out_indices, DistanceType *out_distances_sq) const
809
+ {
810
+ nanoflann::KNNResultSet<DistanceType,IndexType> resultSet(num_closest);
811
+ resultSet.init(out_indices, out_distances_sq);
812
+ this->findNeighbors(resultSet, query_point, nanoflann::SearchParams());
813
+ }
814
+
815
+ /**
816
+ * Find all the neighbors to \a query_point[0:dim-1] within a maximum radius.
817
+ * The output is given as a vector of pairs, of which the first element is a point index and the second the corresponding distance.
818
+ * Previous contents of \a IndicesDists are cleared.
819
+ *
820
+ * If searchParams.sorted==true, the output list is sorted by ascending distances.
821
+ *
822
+ * For a better performance, it is advisable to do a .reserve() on the vector if you have any wild guess about the number of expected matches.
823
+ *
824
+ * \sa knnSearch, findNeighbors
825
+ * \return The number of points within the given radius (i.e. indices.size() or dists.size() )
826
+ */
827
+ size_t radiusSearch(const ElementType *query_point,const DistanceType radius, std::vector<std::pair<IndexType,DistanceType> >& IndicesDists, const SearchParams& searchParams) const
828
+ {
829
+ RadiusResultSet<DistanceType,IndexType> resultSet(radius,IndicesDists);
830
+ this->findNeighbors(resultSet, query_point, searchParams);
831
+
832
+ if (searchParams.sorted)
833
+ std::sort(IndicesDists.begin(),IndicesDists.end(), IndexDist_Sorter() );
834
+
835
+ return resultSet.size();
836
+ }
837
+
838
+ /** @} */
839
+
840
+ private:
841
+ /** Make sure the auxiliary list \a vind has the same size than the current dataset, and re-generate if size has changed. */
842
+ void init_vind()
843
+ {
844
+ // Create a permutable array of indices to the input vectors.
845
+ m_size = dataset.kdtree_get_point_count();
846
+ if (vind.size()!=m_size)
847
+ {
848
+ vind.resize(m_size);
849
+ for (size_t i = 0; i < m_size; i++) vind[i] = i;
850
+ }
851
+ }
852
+
853
+ /// Helper accessor to the dataset points:
854
+ inline ElementType dataset_get(size_t idx, int component) const {
855
+ return dataset.kdtree_get_pt(idx,component);
856
+ }
857
+
858
+
859
+ void save_tree(FILE* stream, NodePtr tree)
860
+ {
861
+ save_value(stream, *tree);
862
+ if (tree->child1!=NULL) {
863
+ save_tree(stream, tree->child1);
864
+ }
865
+ if (tree->child2!=NULL) {
866
+ save_tree(stream, tree->child2);
867
+ }
868
+ }
869
+
870
+
871
+ void load_tree(FILE* stream, NodePtr& tree)
872
+ {
873
+ tree = pool.allocate<Node>();
874
+ load_value(stream, *tree);
875
+ if (tree->child1!=NULL) {
876
+ load_tree(stream, tree->child1);
877
+ }
878
+ if (tree->child2!=NULL) {
879
+ load_tree(stream, tree->child2);
880
+ }
881
+ }
882
+
883
+
884
+ void computeBoundingBox(BoundingBox& bbox)
885
+ {
886
+ bbox.resize((DIM>0 ? DIM : dim));
887
+ if (dataset.kdtree_get_bbox(bbox))
888
+ {
889
+ // Done! It was implemented in derived class
890
+ }
891
+ else
892
+ {
893
+ for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
894
+ bbox[i].low =
895
+ bbox[i].high = dataset_get(0,i);
896
+ }
897
+ const size_t N = dataset.kdtree_get_point_count();
898
+ for (size_t k=1; k<N; ++k) {
899
+ for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
900
+ if (dataset_get(k,i)<bbox[i].low) bbox[i].low = dataset_get(k,i);
901
+ if (dataset_get(k,i)>bbox[i].high) bbox[i].high = dataset_get(k,i);
902
+ }
903
+ }
904
+ }
905
+ }
906
+
907
+
908
+ /**
909
+ * Create a tree node that subdivides the list of vecs from vind[first]
910
+ * to vind[last]. The routine is called recursively on each sublist.
911
+ * Place a pointer to this new tree node in the location pTree.
912
+ *
913
+ * Params: pTree = the new node to create
914
+ * first = index of the first vector
915
+ * last = index of the last vector
916
+ */
917
+ NodePtr divideTree(const IndexType left, const IndexType right, BoundingBox& bbox)
918
+ {
919
+ NodePtr node = pool.allocate<Node>(); // allocate memory
920
+
921
+ /* If too few exemplars remain, then make this a leaf node. */
922
+ if ( (right-left) <= m_leaf_max_size) {
923
+ node->child1 = node->child2 = NULL; /* Mark as leaf node. */
924
+ node->lr.left = left;
925
+ node->lr.right = right;
926
+
927
+ // compute bounding-box of leaf points
928
+ for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
929
+ bbox[i].low = dataset_get(vind[left],i);
930
+ bbox[i].high = dataset_get(vind[left],i);
931
+ }
932
+ for (IndexType k=left+1; k<right; ++k) {
933
+ for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
934
+ if (bbox[i].low>dataset_get(vind[k],i)) bbox[i].low=dataset_get(vind[k],i);
935
+ if (bbox[i].high<dataset_get(vind[k],i)) bbox[i].high=dataset_get(vind[k],i);
936
+ }
937
+ }
938
+ }
939
+ else {
940
+ IndexType idx;
941
+ int cutfeat;
942
+ DistanceType cutval;
943
+ middleSplit_(&vind[0]+left, right-left, idx, cutfeat, cutval, bbox);
944
+
945
+ node->sub.divfeat = cutfeat;
946
+
947
+ BoundingBox left_bbox(bbox);
948
+ left_bbox[cutfeat].high = cutval;
949
+ node->child1 = divideTree(left, left+idx, left_bbox);
950
+
951
+ BoundingBox right_bbox(bbox);
952
+ right_bbox[cutfeat].low = cutval;
953
+ node->child2 = divideTree(left+idx, right, right_bbox);
954
+
955
+ node->sub.divlow = left_bbox[cutfeat].high;
956
+ node->sub.divhigh = right_bbox[cutfeat].low;
957
+
958
+ for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
959
+ bbox[i].low = std::min(left_bbox[i].low, right_bbox[i].low);
960
+ bbox[i].high = std::max(left_bbox[i].high, right_bbox[i].high);
961
+ }
962
+ }
963
+
964
+ return node;
965
+ }
966
+
967
+ void computeMinMax(IndexType* ind, IndexType count, int element, ElementType& min_elem, ElementType& max_elem)
968
+ {
969
+ min_elem = dataset_get(ind[0],element);
970
+ max_elem = dataset_get(ind[0],element);
971
+ for (IndexType i=1; i<count; ++i) {
972
+ ElementType val = dataset_get(ind[i],element);
973
+ if (val<min_elem) min_elem = val;
974
+ if (val>max_elem) max_elem = val;
975
+ }
976
+ }
977
+
978
+ void middleSplit(IndexType* ind, IndexType count, IndexType& index, int& cutfeat, DistanceType& cutval, const BoundingBox& bbox)
979
+ {
980
+ // find the largest span from the approximate bounding box
981
+ ElementType max_span = bbox[0].high-bbox[0].low;
982
+ cutfeat = 0;
983
+ cutval = (bbox[0].high+bbox[0].low)/2;
984
+ for (int i=1; i<(DIM>0 ? DIM : dim); ++i) {
985
+ ElementType span = bbox[i].low-bbox[i].low;
986
+ if (span>max_span) {
987
+ max_span = span;
988
+ cutfeat = i;
989
+ cutval = (bbox[i].high+bbox[i].low)/2;
990
+ }
991
+ }
992
+
993
+ // compute exact span on the found dimension
994
+ ElementType min_elem, max_elem;
995
+ computeMinMax(ind, count, cutfeat, min_elem, max_elem);
996
+ cutval = (min_elem+max_elem)/2;
997
+ max_span = max_elem - min_elem;
998
+
999
+ // check if a dimension of a largest span exists
1000
+ size_t k = cutfeat;
1001
+ for (size_t i=0; i<(DIM>0 ? DIM : dim); ++i) {
1002
+ if (i==k) continue;
1003
+ ElementType span = bbox[i].high-bbox[i].low;
1004
+ if (span>max_span) {
1005
+ computeMinMax(ind, count, i, min_elem, max_elem);
1006
+ span = max_elem - min_elem;
1007
+ if (span>max_span) {
1008
+ max_span = span;
1009
+ cutfeat = i;
1010
+ cutval = (min_elem+max_elem)/2;
1011
+ }
1012
+ }
1013
+ }
1014
+ IndexType lim1, lim2;
1015
+ planeSplit(ind, count, cutfeat, cutval, lim1, lim2);
1016
+
1017
+ if (lim1>count/2) index = lim1;
1018
+ else if (lim2<count/2) index = lim2;
1019
+ else index = count/2;
1020
+ }
1021
+
1022
+
1023
+ void middleSplit_(IndexType* ind, IndexType count, IndexType& index, int& cutfeat, DistanceType& cutval, const BoundingBox& bbox)
1024
+ {
1025
+ const DistanceType EPS=static_cast<DistanceType>(0.00001);
1026
+ ElementType max_span = bbox[0].high-bbox[0].low;
1027
+ for (int i=1; i<(DIM>0 ? DIM : dim); ++i) {
1028
+ ElementType span = bbox[i].high-bbox[i].low;
1029
+ if (span>max_span) {
1030
+ max_span = span;
1031
+ }
1032
+ }
1033
+ ElementType max_spread = -1;
1034
+ cutfeat = 0;
1035
+ for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
1036
+ ElementType span = bbox[i].high-bbox[i].low;
1037
+ if (span>(1-EPS)*max_span) {
1038
+ ElementType min_elem, max_elem;
1039
+ computeMinMax(ind, count, cutfeat, min_elem, max_elem);
1040
+ ElementType spread = max_elem-min_elem;;
1041
+ if (spread>max_spread) {
1042
+ cutfeat = i;
1043
+ max_spread = spread;
1044
+ }
1045
+ }
1046
+ }
1047
+ // split in the middle
1048
+ DistanceType split_val = (bbox[cutfeat].low+bbox[cutfeat].high)/2;
1049
+ ElementType min_elem, max_elem;
1050
+ computeMinMax(ind, count, cutfeat, min_elem, max_elem);
1051
+
1052
+ if (split_val<min_elem) cutval = min_elem;
1053
+ else if (split_val>max_elem) cutval = max_elem;
1054
+ else cutval = split_val;
1055
+
1056
+ IndexType lim1, lim2;
1057
+ planeSplit(ind, count, cutfeat, cutval, lim1, lim2);
1058
+
1059
+ if (lim1>count/2) index = lim1;
1060
+ else if (lim2<count/2) index = lim2;
1061
+ else index = count/2;
1062
+ }
1063
+
1064
+
1065
+ /**
1066
+ * Subdivide the list of points by a plane perpendicular on axe corresponding
1067
+ * to the 'cutfeat' dimension at 'cutval' position.
1068
+ *
1069
+ * On return:
1070
+ * dataset[ind[0..lim1-1]][cutfeat]<cutval
1071
+ * dataset[ind[lim1..lim2-1]][cutfeat]==cutval
1072
+ * dataset[ind[lim2..count]][cutfeat]>cutval
1073
+ */
1074
+ void planeSplit(IndexType* ind, const IndexType count, int cutfeat, DistanceType cutval, IndexType& lim1, IndexType& lim2)
1075
+ {
1076
+ /* Move vector indices for left subtree to front of list. */
1077
+ IndexType left = 0;
1078
+ IndexType right = count-1;
1079
+ for (;; ) {
1080
+ while (left<=right && dataset_get(ind[left],cutfeat)<cutval) ++left;
1081
+ while (right && left<=right && dataset_get(ind[right],cutfeat)>=cutval) --right;
1082
+ if (left>right || !right) break; // "!right" was added to support unsigned Index types
1083
+ std::swap(ind[left], ind[right]);
1084
+ ++left;
1085
+ --right;
1086
+ }
1087
+ /* If either list is empty, it means that all remaining features
1088
+ * are identical. Split in the middle to maintain a balanced tree.
1089
+ */
1090
+ lim1 = left;
1091
+ right = count-1;
1092
+ for (;; ) {
1093
+ while (left<=right && dataset_get(ind[left],cutfeat)<=cutval) ++left;
1094
+ while (right && left<=right && dataset_get(ind[right],cutfeat)>cutval) --right;
1095
+ if (left>right || !right) break; // "!right" was added to support unsigned Index types
1096
+ std::swap(ind[left], ind[right]);
1097
+ ++left;
1098
+ --right;
1099
+ }
1100
+ lim2 = left;
1101
+ }
1102
+
1103
+ DistanceType computeInitialDistances(const ElementType* vec, std::vector<DistanceType>& dists) const
1104
+ {
1105
+ assert(vec);
1106
+ DistanceType distsq = 0.0;
1107
+
1108
+ for (int i = 0; i < (DIM>0 ? DIM : dim); ++i) {
1109
+ if (vec[i] < root_bbox[i].low) {
1110
+ dists[i] = distance.accum_dist(vec[i], root_bbox[i].low, i);
1111
+ distsq += dists[i];
1112
+ }
1113
+ if (vec[i] > root_bbox[i].high) {
1114
+ dists[i] = distance.accum_dist(vec[i], root_bbox[i].high, i);
1115
+ distsq += dists[i];
1116
+ }
1117
+ }
1118
+
1119
+ return distsq;
1120
+ }
1121
+
1122
+ /**
1123
+ * Performs an exact search in the tree starting from a node.
1124
+ * \tparam RESULTSET Should be any ResultSet<DistanceType>
1125
+ */
1126
+ template <class RESULTSET>
1127
+ void searchLevel(RESULTSET& result_set, const ElementType* vec, const NodePtr node, DistanceType mindistsq,
1128
+ std::vector<DistanceType>& dists, const float epsError) const
1129
+ {
1130
+ /* If this is a leaf node, then do check and return. */
1131
+ if ((node->child1 == NULL)&&(node->child2 == NULL)) {
1132
+ //count_leaf += (node->lr.right-node->lr.left); // Removed since was neither used nor returned to the user.
1133
+ DistanceType worst_dist = result_set.worstDist();
1134
+ for (IndexType i=node->lr.left; i<node->lr.right; ++i) {
1135
+ const IndexType index = vind[i];// reorder... : i;
1136
+ DistanceType dist = distance(vec, index, (DIM>0 ? DIM : dim));
1137
+ if (dist<worst_dist) {
1138
+ result_set.addPoint(dist,vind[i]);
1139
+ }
1140
+ }
1141
+ return;
1142
+ }
1143
+
1144
+ /* Which child branch should be taken first? */
1145
+ int idx = node->sub.divfeat;
1146
+ ElementType val = vec[idx];
1147
+ DistanceType diff1 = val - node->sub.divlow;
1148
+ DistanceType diff2 = val - node->sub.divhigh;
1149
+
1150
+ NodePtr bestChild;
1151
+ NodePtr otherChild;
1152
+ DistanceType cut_dist;
1153
+ if ((diff1+diff2)<0) {
1154
+ bestChild = node->child1;
1155
+ otherChild = node->child2;
1156
+ cut_dist = distance.accum_dist(val, node->sub.divhigh, idx);
1157
+ }
1158
+ else {
1159
+ bestChild = node->child2;
1160
+ otherChild = node->child1;
1161
+ cut_dist = distance.accum_dist( val, node->sub.divlow, idx);
1162
+ }
1163
+
1164
+ /* Call recursively to search next level down. */
1165
+ searchLevel(result_set, vec, bestChild, mindistsq, dists, epsError);
1166
+
1167
+ DistanceType dst = dists[idx];
1168
+ mindistsq = mindistsq + cut_dist - dst;
1169
+ dists[idx] = cut_dist;
1170
+ if (mindistsq*epsError<=result_set.worstDist()) {
1171
+ searchLevel(result_set, vec, otherChild, mindistsq, dists, epsError);
1172
+ }
1173
+ dists[idx] = dst;
1174
+ }
1175
+
1176
+
1177
+ void saveIndex(FILE* stream)
1178
+ {
1179
+ save_value(stream, m_size);
1180
+ save_value(stream, dim);
1181
+ save_value(stream, root_bbox);
1182
+ save_value(stream, m_leaf_max_size);
1183
+ save_value(stream, vind);
1184
+ save_tree(stream, root_node);
1185
+ }
1186
+
1187
+ void loadIndex(FILE* stream)
1188
+ {
1189
+ load_value(stream, m_size);
1190
+ load_value(stream, dim);
1191
+ load_value(stream, root_bbox);
1192
+ load_value(stream, m_leaf_max_size);
1193
+ load_value(stream, vind);
1194
+ load_tree(stream, root_node);
1195
+ }
1196
+
1197
+ }; // class KDTree
1198
+
1199
+
1200
+ /** A simple KD-tree adaptor for working with data directly stored in an Eigen Matrix, without duplicating the data storage.
1201
+ * Each row in the matrix represents a point in the state space.
1202
+ *
1203
+ * Example of usage:
1204
+ * \code
1205
+ * Eigen::Matrix<num_t,Dynamic,Dynamic> mat;
1206
+ * // Fill out "mat"...
1207
+ *
1208
+ * typedef KDTreeEigenMatrixAdaptor< Eigen::Matrix<num_t,Dynamic,Dynamic> > my_kd_tree_t;
1209
+ * const int max_leaf = 10;
1210
+ * my_kd_tree_t mat_index(dimdim, mat, max_leaf );
1211
+ * mat_index.index->buildIndex();
1212
+ * mat_index.index->...
1213
+ * \endcode
1214
+ *
1215
+ * \tparam DIM If set to >0, it specifies a compile-time fixed dimensionality for the points in the data set, allowing more compiler optimizations.
1216
+ * \tparam Distance The distance metric to use: nanoflann::metric_L1, nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc.
1217
+ * \tparam IndexType The type for indices in the KD-tree index (typically, size_t of int)
1218
+ */
1219
+ template <class MatrixType, int DIM = -1, class Distance = nanoflann::metric_L2, typename IndexType = size_t>
1220
+ struct KDTreeEigenMatrixAdaptor
1221
+ {
1222
+ typedef KDTreeEigenMatrixAdaptor<MatrixType,DIM,Distance> self_t;
1223
+ typedef typename MatrixType::Scalar num_t;
1224
+ typedef typename Distance::template traits<num_t,self_t>::distance_t metric_t;
1225
+ typedef KDTreeSingleIndexAdaptor< metric_t,self_t,DIM,IndexType> index_t;
1226
+
1227
+ index_t* index; //! The kd-tree index for the user to call its methods as usual with any other FLANN index.
1228
+
1229
+ /// Constructor: takes a const ref to the matrix object with the data points
1230
+ KDTreeEigenMatrixAdaptor(const int dimensionality, const MatrixType &mat, const int leaf_max_size = 10) : m_data_matrix(mat)
1231
+ {
1232
+ const size_t dims = mat.cols();
1233
+ if (DIM>0 && static_cast<int>(dims)!=DIM)
1234
+ throw std::runtime_error("Data set dimensionality does not match the 'DIM' template argument");
1235
+ index = new index_t( dims, *this /* adaptor */, nanoflann::KDTreeSingleIndexAdaptorParams(leaf_max_size, dims ) );
1236
+ index->buildIndex();
1237
+ }
1238
+
1239
+ ~KDTreeEigenMatrixAdaptor() {
1240
+ delete index;
1241
+ }
1242
+
1243
+ const MatrixType &m_data_matrix;
1244
+
1245
+ /** Query for the \a num_closest closest points to a given point (entered as query_point[0:dim-1]).
1246
+ * Note that this is a short-cut method for index->findNeighbors().
1247
+ * The user can also call index->... methods as desired.
1248
+ * \note nChecks_IGNORED is ignored but kept for compatibility with the original FLANN interface.
1249
+ */
1250
+ inline void query(const num_t *query_point, const size_t num_closest, IndexType *out_indices, num_t *out_distances_sq, const int nChecks_IGNORED = 10) const
1251
+ {
1252
+ nanoflann::KNNResultSet<typename MatrixType::Scalar,IndexType> resultSet(num_closest);
1253
+ resultSet.init(out_indices, out_distances_sq);
1254
+ index->findNeighbors(resultSet, query_point, nanoflann::SearchParams());
1255
+ }
1256
+
1257
+ /** @name Interface expected by KDTreeSingleIndexAdaptor
1258
+ * @{ */
1259
+
1260
+ const self_t & derived() const {
1261
+ return *this;
1262
+ }
1263
+ self_t & derived() {
1264
+ return *this;
1265
+ }
1266
+
1267
+ // Must return the number of data points
1268
+ inline size_t kdtree_get_point_count() const {
1269
+ return m_data_matrix.rows();
1270
+ }
1271
+
1272
+ // Returns the distance between the vector "p1[0:size-1]" and the data point with index "idx_p2" stored in the class:
1273
+ inline num_t kdtree_distance(const num_t *p1, const size_t idx_p2,size_t size) const
1274
+ {
1275
+ num_t s=0;
1276
+ for (size_t i=0; i<size; i++) {
1277
+ const num_t d= p1[i]-m_data_matrix.coeff(idx_p2,i);
1278
+ s+=d*d;
1279
+ }
1280
+ return s;
1281
+ }
1282
+
1283
+ // Returns the dim'th component of the idx'th point in the class:
1284
+ inline num_t kdtree_get_pt(const size_t idx, int dim) const {
1285
+ return m_data_matrix.coeff(idx,dim);
1286
+ }
1287
+
1288
+ // Optional bounding-box computation: return false to default to a standard bbox computation loop.
1289
+ // Return true if the BBOX was already computed by the class and returned in "bb" so it can be avoided to redo it again.
1290
+ // Look at bb.size() to find out the expected dimensionality (e.g. 2 or 3 for point clouds)
1291
+ template <class BBOX>
1292
+ bool kdtree_get_bbox(BBOX &bb) const {
1293
+ return false;
1294
+ }
1295
+
1296
+ /** @} */
1297
+
1298
+ }; // end of KDTreeEigenMatrixAdaptor
1299
+ /** @} */
1300
+
1301
+ /** @} */ // end of grouping
1302
+ } // end of NS
1303
+
1304
+
1305
+ #endif /* NANOFLANN_HPP_ */
CMakeLists.txt ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ cmake_minimum_required(VERSION 3.16)
2
+ project(Lightweight3DRegLib)
3
+
4
+ set(CMAKE_CXX_STANDARD 17)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+
7
+ if(NOT CMAKE_BUILD_TYPE)
8
+ set(CMAKE_BUILD_TYPE Release)
9
+ endif()
10
+
11
+ # 第三方库路径
12
+ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
13
+
14
+ # --- 第三方库
15
+ find_package(Eigen3 3.4.0 REQUIRED)
16
+ message(STATUS "Found Eigen3 version: ${EIGEN3_VERSION_STRING}")
17
+ find_package(OpenMesh REQUIRED)
18
+ find_package(OpenMP REQUIRED)
19
+
20
+ # --- 添加 pybind11 支持 ---
21
+ find_package(pybind11 CONFIG REQUIRED)
22
+
23
+ option(USEPARDISO "Use Pardiso solver" ON)
24
+
25
+ # --- 源文件
26
+ file(GLOB_RECURSE SOURCES
27
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/core/*.cpp"
28
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/io/*.cpp"
29
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/*.cpp"
30
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/geodesic/*.cpp"
31
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/preprocess/*.cpp"
32
+ )
33
+
34
+ file(GLOB_RECURSE HEADERS
35
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/*.h"
36
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/core/*.h"
37
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/io/*.h"
38
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/*.h"
39
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/geodesic/*.h"
40
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/preprocess/*.h"
41
+ "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/*.h"
42
+ )
43
+
44
+ # --- 创建静态库
45
+ add_library(Lightweight3DRegLib STATIC ${SOURCES} ${HEADERS})
46
+
47
+ target_precompile_headers(Lightweight3DRegLib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/cpp/pch.h")
48
+
49
+ set_target_properties(Lightweight3DRegLib PROPERTIES POSITION_INDEPENDENT_CODE ON)
50
+
51
+ # --- include 路径
52
+ target_include_directories(Lightweight3DRegLib
53
+ PUBLIC
54
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp"
55
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/core"
56
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/io"
57
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools"
58
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/geodesic"
59
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/preprocess"
60
+ "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty"
61
+ ${EIGEN3_INCLUDE_DIRS}
62
+ )
63
+
64
+ # --- 链接库
65
+ target_link_libraries(Lightweight3DRegLib
66
+ PUBLIC OpenMeshCore OpenMeshTools
67
+ PUBLIC OpenMP::OpenMP_CXX
68
+ )
69
+
70
+ # --- Pardiso / MKL
71
+ if(USEPARDISO AND ${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux")
72
+ if(EXISTS /opt/intel/oneapi/mkl/latest/include/mkl_pardiso.h)
73
+ target_compile_definitions(Lightweight3DRegLib PUBLIC USE_PARDISO)
74
+ target_include_directories(Lightweight3DRegLib SYSTEM PUBLIC "/opt/intel/oneapi/mkl/latest/include")
75
+ target_compile_options(Lightweight3DRegLib PUBLIC "-m64")
76
+ target_link_libraries(Lightweight3DRegLib PRIVATE
77
+ "-Wl,--start-group /opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_intel_lp64.a"
78
+ "/opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_gnu_thread.a"
79
+ "/opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_core.a"
80
+ "-Wl,--end-group -lgomp -lpthread -lm -ldl"
81
+ )
82
+ else()
83
+ message("Cannot find Intel MKL at /opt/intel/oneapi/mkl. Using Eigen LDLT.")
84
+ endif()
85
+ endif()
86
+
87
+ # --- 添加 Python 模块 ---
88
+ pybind11_add_module(pyregister ${CMAKE_CURRENT_SOURCE_DIR}/python/pyregister.cpp)
89
+
90
+ target_link_libraries(pyregister
91
+ PRIVATE
92
+ Lightweight3DRegLib
93
+ OpenMeshCore
94
+ OpenMeshTools
95
+ OpenMP::OpenMP_CXX
96
+ )
97
+
98
+ target_include_directories(pyregister
99
+ PRIVATE
100
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp"
101
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/core"
102
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/io"
103
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools"
104
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/geodesic"
105
+ "${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/preprocess"
106
+ "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty"
107
+ ${EIGEN3_INCLUDE_DIRS}
108
+ )
109
+
110
+ target_precompile_headers(pyregister REUSE_FROM Lightweight3DRegLib)
111
+
112
+ if(USEPARDISO AND ${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux")
113
+ if(EXISTS /opt/intel/oneapi/mkl/latest/include/mkl_pardiso.h)
114
+ target_compile_definitions(pyregister PRIVATE USE_PARDISO)
115
+ target_include_directories(pyregister SYSTEM PRIVATE "/opt/intel/oneapi/mkl/latest/include")
116
+ target_compile_options(pyregister PRIVATE "-m64")
117
+ target_link_libraries(pyregister PRIVATE
118
+ "-Wl,--start-group /opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_intel_lp64.a"
119
+ "/opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_gnu_thread.a"
120
+ "/opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_core.a"
121
+ "-Wl,--end-group -lgomp -lpthread -lm -ldl"
122
+ )
123
+ endif()
124
+ endif()
125
+
126
+ set_target_properties(pyregister PROPERTIES
127
+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/python
128
+ )
129
+
130
+
131
+ # =====================================================================
132
+ # --- 添加示例可执行程序 (examples/c++/main.cpp) ---
133
+ # =====================================================================
134
+ file(GLOB EXAMPLE_SOURCES
135
+ "${CMAKE_CURRENT_SOURCE_DIR}/examples/c++/*.cpp"
136
+ )
137
+
138
+
139
+ add_executable(Lightweight3DRegDemo ${EXAMPLE_SOURCES})
140
+
141
+ target_precompile_headers(Lightweight3DRegDemo PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/cpp/pch.h")
142
+
143
+
144
+ target_link_libraries(Lightweight3DRegDemo
145
+ PRIVATE
146
+ Lightweight3DRegLib
147
+ OpenMeshCore
148
+ OpenMeshTools
149
+ OpenMP::OpenMP_CXX
150
+ )
151
+
152
+
153
+
154
+ set_target_properties(Lightweight3DRegDemo PROPERTIES
155
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/examples
156
+ )
README.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Lowweight Register
3
+ emoji: 🚀
4
+ colorFrom: purple
5
+ colorTo: red
6
+ sdk: gradio
7
+ sdk_version: 5.47.2
8
+ app_file: app.py
9
+ pinned: false
10
+ license: apache-2.0
11
+ short_description: '00000'
12
+ ---
13
+
14
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,638 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ import os
3
+ import sys
4
+ import glob
5
+ import numpy as np
6
+ import trimesh
7
+ import plotly.graph_objs as go
8
+ import gradio as gr
9
+ from plyfile import PlyData
10
+
11
+ # ------------------ Load pyregister module ------------------
12
+ so_candidates = glob.glob("build/python/pyregister*.so")
13
+ if not so_candidates:
14
+ os.system("bash build.sh")
15
+ so_candidates = glob.glob("build/python/pyregister*.so")
16
+ if not so_candidates:
17
+ raise FileNotFoundError("pyregister build failed, no .so found in build/python")
18
+ sys.path.append("build/python")
19
+
20
+ import pyregister
21
+
22
+ # ------------------ Example data ------------------
23
+ EXAMPLES = {
24
+ "Rigid": {"target": "./examples/data/fricp/target.ply", "source": "./examples/data/fricp/source.ply"},
25
+ "NonRigid": {"target": "./examples/data/spare/target.obj", "source": "./examples/data/spare/source.obj"},
26
+ }
27
+
28
+ FRICP_PARAS = {
29
+ "useinit": False,
30
+ "fileinit": "",
31
+ "maxiter": 100,
32
+ "stop": 1e-5
33
+ }
34
+
35
+ SPARE_PARAS = {
36
+ "iters": 30,
37
+ "stopcoarse": 1e-3,
38
+ "stopfine": 1e-4,
39
+ "use_landmark": False,
40
+ "src": [],
41
+ "tar": []
42
+ }
43
+
44
+ def load_example(example_type):
45
+ target_path = EXAMPLES[example_type]["target"]
46
+ source_path = EXAMPLES[example_type]["source"]
47
+ method = example_type
48
+ return source_path, target_path, method
49
+
50
+ def read_mesh(file_path):
51
+ ext = os.path.splitext(file_path)[1].lower()
52
+ if ext == ".ply":
53
+ plydata = PlyData.read(file_path)
54
+ vertex = plydata["vertex"]
55
+ vertices = np.stack([vertex["x"], vertex["y"], vertex["z"]], axis=-1)
56
+ faces = None
57
+ if "face" in plydata and len(plydata["face"].data) > 0:
58
+ face_list = [f[0] for f in plydata["face"].data]
59
+ if len(face_list) > 0:
60
+ faces = np.array(face_list)
61
+ if faces.ndim == 1:
62
+ faces = faces.reshape(-1, 3)
63
+ return vertices, faces
64
+ elif ext == ".obj":
65
+ mesh = trimesh.load(file_path, process=False)
66
+ return np.asarray(mesh.vertices), (np.asarray(mesh.faces) if mesh.faces.size > 0 else None)
67
+ else:
68
+ raise ValueError(f"Unsupported file type: {ext}")
69
+
70
+ def plot_source_target_mesh(target_file, source_file, alpha_target=1.0, alpha_source=1.0, scatter_mode=False):
71
+ target_v, target_f = read_mesh(target_file)
72
+ source_v, source_f = read_mesh(source_file)
73
+
74
+ fig = go.Figure()
75
+ lighting_opts = dict(ambient=0.5, diffuse=0.8, specular=0.6, roughness=0.25)
76
+ light_pos = dict(x=100, y=200, z=50)
77
+
78
+ target_color = "crimson"
79
+
80
+ if scatter_mode:
81
+
82
+ fig.add_trace(go.Scatter3d(
83
+ x=target_v[:, 0], y=target_v[:, 1], z=target_v[:, 2],
84
+ mode="markers",
85
+
86
+ marker=dict(size=0.6, color=target_color, opacity=1.0),
87
+ name="Target Points",
88
+ hoverinfo="text",
89
+ text=[
90
+ f"<b><span style='color:black;'>Target ID:</span></b> {i}"
91
+ f"<br><span style='font-size:12px;'>x={x:.3f}, y={y:.3f}, z={z:.3f}</span>"
92
+ for i, (x, y, z) in enumerate(target_v)
93
+ ],
94
+ hoverlabel=dict(
95
+ bgcolor="white",
96
+ bordercolor="black",
97
+ font=dict(color="black", size=13, family="Arial", weight="bold")
98
+ )
99
+ ))
100
+
101
+ fig.add_trace(go.Scatter3d(
102
+ x=source_v[:, 0], y=source_v[:, 1], z=source_v[:, 2],
103
+ mode="markers",
104
+
105
+ marker=dict(size=0.6, color="limegreen", opacity=1.0),
106
+ name="Source Points",
107
+ hoverinfo="text",
108
+ text=[
109
+ f"<b><span style='color:red;'>Source ID:</span></b> {i}"
110
+ f"<br><span style='font-size:12px;'>x={x:.3f}, y={y:.3f}, z={z:.3f}</span>"
111
+ for i, (x, y, z) in enumerate(source_v)
112
+ ],
113
+ hoverlabel=dict(
114
+ bgcolor="white",
115
+ bordercolor="limegreen",
116
+ font=dict(color="red", size=13, family="Arial", weight="bold")
117
+ )
118
+ ))
119
+
120
+ else:
121
+
122
+ if target_f is not None:
123
+ fig.add_trace(go.Mesh3d(
124
+ x=target_v[:, 0], y=target_v[:, 1], z=target_v[:, 2],
125
+ i=target_f[:, 0], j=target_f[:, 1], k=target_f[:, 2],
126
+ color="khaki", opacity=alpha_target, name="Target Mesh",
127
+ lighting=lighting_opts, lightposition=light_pos,
128
+ hovertemplate="<b>Target Surface</b><br>x=%{x:.3f}<br>y=%{y:.3f}<br>z=%{z:.3f}<extra></extra>",
129
+ ))
130
+ else:
131
+ fig.add_trace(go.Scatter3d(
132
+ x=target_v[:, 0], y=target_v[:, 1], z=target_v[:, 2],
133
+ mode="markers",
134
+ marker=dict(size=0.6, color=target_color, opacity=alpha_target),
135
+ name="Target Points"
136
+ ))
137
+
138
+ if source_f is not None:
139
+ fig.add_trace(go.Mesh3d(
140
+ x=source_v[:, 0], y=source_v[:, 1], z=source_v[:, 2],
141
+ i=source_f[:, 0], j=source_f[:, 1], k=source_f[:, 2],
142
+ color="darkseagreen", opacity=alpha_source, name="Source Mesh",
143
+ lighting=lighting_opts, lightposition=light_pos,
144
+ hovertemplate="<b>Source Surface</b><br>x=%{x:.3f}<br>y=%{y:.3f}<br>z=%{z:.3f}<extra></extra>",
145
+ ))
146
+ else:
147
+ fig.add_trace(go.Scatter3d(
148
+ x=source_v[:, 0], y=source_v[:, 1], z=source_v[:, 2],
149
+ mode="markers",
150
+ marker=dict(size=0.6, color="limegreen", opacity=alpha_source),
151
+ name="Source Points"
152
+ ))
153
+
154
+ fig.update_layout(
155
+ height=600, width=600, margin=dict(l=0, r=0, t=40, b=0),
156
+ scene=dict(aspectmode="data"), showlegend=True
157
+ )
158
+ return fig
159
+
160
+
161
+
162
+ def plot_result_target_mesh(target_file, result_file):
163
+ target_v, target_f = read_mesh(target_file)
164
+ result_v, result_f = read_mesh(result_file)
165
+
166
+ fig = go.Figure()
167
+ lighting_opts = dict(ambient=0.5, diffuse=0.8, specular=0.6, roughness=0.25)
168
+ light_pos = dict(x=100, y=200, z=50)
169
+
170
+ target_mesh_color = "khaki"
171
+ result_mesh_color = "lightblue"
172
+
173
+ if target_f is not None:
174
+ fig.add_trace(go.Mesh3d(
175
+ x=target_v[:, 0], y=target_v[:, 1], z=target_v[:, 2],
176
+ i=target_f[:, 0], j=target_f[:, 1], k=target_f[:, 2],
177
+ color=target_mesh_color, opacity=1.0, name="Target Mesh",
178
+ lighting=lighting_opts, lightposition=light_pos
179
+ ))
180
+ else:
181
+ fig.add_trace(go.Scatter3d(
182
+ x=target_v[:, 0], y=target_v[:, 1], z=target_v[:, 2],
183
+ mode="markers",
184
+ marker=dict(size=0.6, color="crimson", opacity=1.0),
185
+ name="Target Points (Red)"
186
+ ))
187
+
188
+ if result_f is not None:
189
+ fig.add_trace(go.Mesh3d(
190
+ x=result_v[:, 0], y=result_v[:, 1], z=result_v[:, 2],
191
+ i=result_f[:, 0], j=result_f[:, 1], k=result_f[:, 2],
192
+ color=result_mesh_color, opacity=1.0, name="Result Mesh",
193
+ lighting=lighting_opts, lightposition=light_pos
194
+ ))
195
+ else:
196
+ fig.add_trace(go.Scatter3d(
197
+ x=result_v[:, 0], y=result_v[:, 1], z=result_v[:, 2],
198
+ mode="markers",
199
+ marker=dict(size=0.6, color="royalblue", opacity=1.0),
200
+ name="Result Points (Blue)"
201
+ ))
202
+
203
+ fig.update_layout(height=600, width=600, margin=dict(l=0, r=0, t=40, b=0),
204
+ scene=dict(aspectmode="data"), showlegend=True)
205
+ return fig
206
+
207
+ # ------------------ Registration ------------------
208
+ def register_and_visualize_with_zip(target_file, source_file, output_dir, method,
209
+ useinit=False, fileinit=None, maxiter=100, stop=1e-5,
210
+ iters=30, stopcoarse=1e-3, stopfine=1e-4):
211
+ if target_file is None or source_file is None:
212
+ raise gr.Error("Please upload both target and source point cloud files first!")
213
+
214
+ target_path = target_file.name
215
+ source_path = source_file.name
216
+ os.makedirs(output_dir, exist_ok=True)
217
+
218
+ # === Rigid ===
219
+ if method == "Rigid":
220
+ reg = pyregister.RigidFricpRegistration()
221
+ FRICP_PARAS["useinit"] = bool(useinit)
222
+ FRICP_PARAS["fileinit"] = fileinit.name if useinit and fileinit else ""
223
+ FRICP_PARAS["maxiter"] = int(maxiter)
224
+ FRICP_PARAS["stop"] = float(stop)
225
+
226
+ reg.Paras_init(FRICP_PARAS["useinit"],
227
+ FRICP_PARAS["fileinit"],
228
+ FRICP_PARAS["maxiter"],
229
+ FRICP_PARAS["stop"])
230
+ output_file = "FRICP_res.ply"
231
+
232
+ # === NonRigid ===
233
+ else:
234
+ reg = pyregister.NonrigidSpareRegistration()
235
+ SPARE_PARAS["iters"] = int(iters)
236
+ SPARE_PARAS["stopcoarse"] = float(stopcoarse)
237
+ SPARE_PARAS["stopfine"] = float(stopfine)
238
+
239
+ reg.Paras_init(
240
+ SPARE_PARAS["iters"],
241
+ SPARE_PARAS["stopcoarse"],
242
+ SPARE_PARAS["stopfine"],
243
+ SPARE_PARAS["use_landmark"],
244
+ SPARE_PARAS["src"],
245
+ SPARE_PARAS["tar"]
246
+ )
247
+ output_file = "spare_res.ply"
248
+
249
+
250
+ reg.Reg(target_path, source_path, output_dir)
251
+
252
+ result_path = os.path.join(output_dir, output_file)
253
+ fig_result = plot_result_target_mesh(target_path, result_path)
254
+
255
+ import zipfile
256
+ zip_path = os.path.join(output_dir, "results.zip")
257
+ with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zipf:
258
+ for f in os.listdir(output_dir):
259
+ if f.endswith(".ply"):
260
+ zipf.write(os.path.join(output_dir, f), arcname=f)
261
+
262
+ return fig_result, zip_path
263
+
264
+
265
+ # ------------------ Reregister ------------------
266
+ def reuse_last_result_as_source(target_file, output_dir):
267
+ if target_file is None:
268
+ raise gr.Error("Please upload the target point cloud file first!")
269
+ if not os.path.exists(output_dir):
270
+ raise gr.Error("Output directory does not exist!")
271
+
272
+ result_candidates = []
273
+ for fname in ["FRICP_res.ply", "spare_res.ply"]:
274
+ fpath = os.path.join(output_dir, fname)
275
+ if os.path.exists(fpath):
276
+ result_candidates.append(fpath)
277
+ if not result_candidates:
278
+ raise gr.Error("No previous registration result found. Please run registration first!")
279
+
280
+ result_candidates.sort(key=os.path.getmtime, reverse=True)
281
+ latest_result = result_candidates[0]
282
+
283
+ fig = plot_source_target_mesh(target_file.name, latest_result)
284
+ return latest_result, fig
285
+
286
+ # ------------------ Utility ------------------
287
+ def reset_parameters():
288
+ """
289
+ Reset all registration parameters (Rigid + NonRigid + Landmarks)
290
+ and clear any manual inputs in the UI.
291
+ This also resets the 'Use Landmarks' checkbox and hides the landmark UI group.
292
+ """
293
+
294
+ # --- Reset Rigid (FRICP) parameters ---
295
+ FRICP_PARAS.update({
296
+ "useinit": False,
297
+ "fileinit": "",
298
+ "maxiter": 100,
299
+ "stop": 1e-5
300
+ })
301
+
302
+ # --- Reset NonRigid (Spare) parameters ---
303
+ SPARE_PARAS.update({
304
+ "iters": 30,
305
+ "stopcoarse": 1e-3,
306
+ "stopfine": 1e-4,
307
+ "use_landmark": False,
308
+ "src": [],
309
+ "tar": []
310
+ })
311
+
312
+ print("[reset] All parameters reset.")
313
+ print("[reset] FRICP_PARAS:", FRICP_PARAS)
314
+ print("[reset] SPARE_PARAS:", SPARE_PARAS)
315
+
316
+
317
+ return (
318
+ FRICP_PARAS["useinit"], # Checkbox
319
+ None, # File input reset
320
+ FRICP_PARAS["maxiter"],
321
+ FRICP_PARAS["stop"],
322
+ SPARE_PARAS["iters"],
323
+ SPARE_PARAS["stopcoarse"],
324
+ SPARE_PARAS["stopfine"],
325
+ gr.update(value=""),
326
+ gr.update(value=""),
327
+ gr.update(value=False),
328
+ gr.update(visible=False)
329
+ )
330
+
331
+
332
+ def clear_all():
333
+ """
334
+ Full reset for all UI elements:
335
+ - Clears uploaded files
336
+ - Resets plots
337
+ - Resets all parameters (Rigid + NonRigid + Landmark)
338
+ - Resets landmark checkbox & hides group
339
+ """
340
+
341
+ FRICP_PARAS.update({
342
+ "useinit": False,
343
+ "fileinit": "",
344
+ "maxiter": 100,
345
+ "stop": 1e-5
346
+ })
347
+ SPARE_PARAS.update({
348
+ "iters": 30,
349
+ "stopcoarse": 1e-3,
350
+ "stopfine": 1e-4,
351
+ "use_landmark": False,
352
+ "src": [],
353
+ "tar": []
354
+ })
355
+
356
+ print("[clear] All files, parameters, and landmarks cleared.")
357
+ return (
358
+ None, # target_input
359
+ None, # source_input
360
+ None, # upload_plot
361
+ None, # result_plot
362
+ FRICP_PARAS["useinit"], # fricp_useinit
363
+ None, # fricp_fileinit
364
+ FRICP_PARAS["maxiter"],
365
+ FRICP_PARAS["stop"],
366
+ SPARE_PARAS["iters"],
367
+ SPARE_PARAS["stopcoarse"],
368
+ SPARE_PARAS["stopfine"],
369
+ gr.update(value=""),
370
+ gr.update(value=""),
371
+ gr.update(value=False),
372
+ gr.update(visible=False)
373
+ )
374
+
375
+
376
+ def visualize_and_store(target_file, source_file, scatter_mode=False):
377
+ if target_file is None or source_file is None:
378
+ return None, None, None
379
+ target_v, _ = read_mesh(target_file.name)
380
+ source_v, _ = read_mesh(source_file.name)
381
+ fig = plot_source_target_mesh(target_file.name, source_file.name, scatter_mode=scatter_mode)
382
+
383
+ return fig, source_v, target_v
384
+
385
+ def clear_landmarks():
386
+ return [], [], "", ""
387
+ def highlight_landmarks_on_mesh(target_file, source_file, src_text, tar_text):
388
+ src_v, _ = read_mesh(source_file.name)
389
+ tar_v, _ = read_mesh(target_file.name)
390
+ src_ids = [int(i) for i in src_text.split(",") if i.strip().isdigit()]
391
+ tar_ids = [int(i) for i in tar_text.split(",") if i.strip().isdigit()]
392
+
393
+
394
+ fig = plot_source_target_mesh(target_file.name, source_file.name)
395
+
396
+ # === Source landmarks ===
397
+ if len(src_ids) > 0:
398
+ pts = src_v[src_ids]
399
+ fig.add_trace(go.Scatter3d(
400
+ x=pts[:, 0], y=pts[:, 1], z=pts[:, 2],
401
+ mode="markers+text",
402
+ text=[str(i) for i in src_ids],
403
+ textposition="top center",
404
+ textfont=dict(color="black", size=10, family="Arial"),
405
+ marker=dict(
406
+ size=6,
407
+ color="limegreen",
408
+ opacity=0.9,
409
+ line=dict(width=2, color="white"),
410
+ symbol="circle"
411
+ ),
412
+ name="Source Landmarks",
413
+ hoverinfo="text",
414
+ hovertext=[
415
+ f"<b>Source ID:</b> {i}<br>x={x:.3f}, y={y:.3f}, z={z:.3f}"
416
+ for i, (x, y, z) in zip(src_ids, pts)
417
+ ]
418
+ ))
419
+
420
+ # === Target landmarks ===
421
+ if len(tar_ids) > 0:
422
+ pts = tar_v[tar_ids]
423
+ fig.add_trace(go.Scatter3d(
424
+ x=pts[:, 0], y=pts[:, 1], z=pts[:, 2],
425
+ mode="markers+text",
426
+ text=[str(i) for i in tar_ids],
427
+ textposition="top center",
428
+ textfont=dict(color="black", size=10, family="Arial"),
429
+ marker=dict(
430
+ size=6,
431
+ color="crimson",
432
+ opacity=0.9,
433
+ line=dict(width=2, color="white"),
434
+ symbol="circle"
435
+ ),
436
+ name="Target Landmarks",
437
+ hoverinfo="text",
438
+ hovertext=[
439
+ f"<b>Target ID:</b> {i}<br>x={x:.3f}, y={y:.3f}, z={z:.3f}"
440
+ for i, (x, y, z) in zip(tar_ids, pts)
441
+ ]
442
+ ))
443
+
444
+
445
+ fig.update_layout(
446
+ scene=dict(
447
+ xaxis=dict(visible=False),
448
+ yaxis=dict(visible=False),
449
+ zaxis=dict(visible=False)
450
+ ),
451
+ legend=dict(bgcolor="rgba(255,255,255,0.6)"),
452
+ margin=dict(l=0, r=0, t=40, b=0),
453
+ title="Landmark Highlight View"
454
+ )
455
+
456
+ return fig
457
+
458
+ def start_landmark_selection(target_file, source_file):
459
+ if target_file is None or source_file is None:
460
+ raise gr.Error("Please upload both Source and Target first!")
461
+
462
+ fig = plot_source_target_mesh(target_file.name, source_file.name, scatter_mode=True)
463
+ return fig
464
+ def update_landmark_ids(src_text, tar_text):
465
+ src_ids = [int(i) for i in src_text.split(",") if i.strip().isdigit()]
466
+ tar_ids = [int(i) for i in tar_text.split(",") if i.strip().isdigit()]
467
+ return src_ids, tar_ids, src_text, tar_text
468
+
469
+
470
+ def set_landmarks_to_registration(src_ids, tar_ids):
471
+ if not src_ids or not tar_ids:
472
+ raise gr.Error("Please select both Source and Target points first!")
473
+ if len(src_ids) != len(tar_ids):
474
+ raise gr.Error(f"Landmark count mismatch: Source={len(src_ids)} vs Target={len(tar_ids)}")
475
+
476
+ SPARE_PARAS["use_landmark"] = True
477
+ SPARE_PARAS["src"] = [int(i) for i in src_ids]
478
+ SPARE_PARAS["tar"] = [int(i) for i in tar_ids]
479
+
480
+ return f"✅ Landmarks staged: {len(src_ids)} Source ↔ {len(tar_ids)} Target. Will be used in NonRigid registration."
481
+
482
+ # ------------------ UI ------------------
483
+ CSS_FIX = """
484
+ html, body, .gradio-container {
485
+ min-height: 100vh;
486
+ overflow-y: auto;
487
+ }
488
+ """
489
+
490
+
491
+
492
+ with gr.Blocks(css=CSS_FIX) as demo:
493
+ gr.Markdown("## Point Cloud / Mesh Registration Visualization Tool")
494
+
495
+ src_ids_state = gr.State([])
496
+ tar_ids_state = gr.State([])
497
+ src_vertices_state = gr.State(None)
498
+ tar_vertices_state = gr.State(None)
499
+
500
+ with gr.Row():
501
+ with gr.Column(scale=1):
502
+
503
+ source_input = gr.File(label="Source File", file_types=[".ply", ".obj"])
504
+ target_input = gr.File(label="Target File", file_types=[".ply", ".obj"])
505
+ method_dropdown = gr.Dropdown(label="Registration Method",
506
+ choices=["Rigid", "NonRigid"], value="Rigid")
507
+
508
+ with gr.Accordion("Rigid Parameters", open=False):
509
+ fricp_useinit = gr.Checkbox(label="Use initial transform?", value=False)
510
+ fricp_fileinit = gr.File(label="Initial transform file", file_types=[".txt"])
511
+ fricp_maxiter = gr.Number(label="Max iterations", value=100)
512
+ fricp_stop = gr.Number(label="Stop threshold", value=1e-5)
513
+
514
+
515
+ with gr.Accordion("NonRigid Parameters", open=False):
516
+ spare_iters = gr.Number(label="Iterations", value=30)
517
+ spare_stopcoarse = gr.Number(label="Stop Coarse", value=1e-3)
518
+ spare_stopfine = gr.Number(label="Stop Fine", value=1e-4)
519
+
520
+
521
+ use_landmark_cb = gr.Checkbox(label="Use Landmarks", value=False)
522
+
523
+
524
+ with gr.Group(visible=False) as landmark_group:
525
+ gr.Markdown("### Landmark Point Selection")
526
+ selected_src = gr.Textbox(
527
+ label="Selected Source IDs (comma-separated)",
528
+ placeholder="e.g. 12,45,89", interactive=True)
529
+ selected_tar = gr.Textbox(
530
+ label="Selected Target IDs (comma-separated)",
531
+ placeholder="e.g. 7,42,105", interactive=True)
532
+
533
+ with gr.Row():
534
+ start_landmark_btn = gr.Button("Start Landmark Selection", elem_classes="primary")
535
+ highlight_btn = gr.Button("Highlight Landmarks")
536
+ clear_landmark_btn = gr.Button("🧹 Clear Selections")
537
+
538
+ select_button = gr.Button("Confirm Landmarks to Registration", elem_classes="primary")
539
+
540
+
541
+ output_dir = gr.Textbox(label="Output Directory", value="./output/", placeholder="./output/")
542
+ with gr.Row():
543
+ run_button = gr.Button("Run Registration", elem_classes="primary")
544
+ rerun_button = gr.Button("Reregister (Use Last Result as Source)", elem_classes="primary")
545
+ clear_button = gr.Button("Clear", elem_classes="secondary")
546
+ reset_button = gr.Button("Reset Parameters", elem_classes="primary")
547
+
548
+
549
+ with gr.Row():
550
+ example_dropdown = gr.Dropdown(label="Load Example Data",
551
+ choices=["Rigid", "NonRigid"], value="Rigid")
552
+ example_button = gr.Button("Load Example")
553
+
554
+ example_button.click(fn=load_example,
555
+ inputs=[example_dropdown],
556
+ outputs=[source_input, target_input, method_dropdown])
557
+
558
+
559
+ with gr.Column(scale=2):
560
+ upload_plot = gr.Plot(label="Source vs Target Mesh")
561
+ result_plot = gr.Plot(label="Result vs Target Mesh")
562
+ download_button = gr.File(label="Download Result Directory",
563
+ file_types=[".zip"], interactive=False)
564
+
565
+
566
+ reset_button.click(
567
+ fn=reset_parameters,
568
+ inputs=None,
569
+ outputs=[
570
+ fricp_useinit, fricp_fileinit, fricp_maxiter, fricp_stop,
571
+ spare_iters, spare_stopcoarse, spare_stopfine,
572
+ selected_src, selected_tar,
573
+ use_landmark_cb, landmark_group
574
+ ]
575
+ )
576
+
577
+ def toggle_landmark_visibility(use_landmark):
578
+ SPARE_PARAS["use_landmark"] = bool(use_landmark)
579
+ print(f"[UI] use_landmark set to {SPARE_PARAS['use_landmark']}")
580
+ return gr.update(visible=use_landmark)
581
+
582
+ use_landmark_cb.change(fn=toggle_landmark_visibility,
583
+ inputs=[use_landmark_cb],
584
+ outputs=[landmark_group])
585
+
586
+ clear_button.click(
587
+ fn=clear_all,
588
+ inputs=None,
589
+ outputs=[
590
+ target_input, source_input, upload_plot, result_plot,
591
+ fricp_useinit, fricp_fileinit, fricp_maxiter, fricp_stop,
592
+ spare_iters, spare_stopcoarse, spare_stopfine,
593
+ selected_src, selected_tar,
594
+ use_landmark_cb, landmark_group
595
+ ]
596
+ )
597
+
598
+
599
+ target_input.change(fn=visualize_and_store,
600
+ inputs=[target_input, source_input],
601
+ outputs=[upload_plot, src_vertices_state, tar_vertices_state])
602
+ source_input.change(fn=visualize_and_store,
603
+ inputs=[target_input, source_input],
604
+ outputs=[upload_plot, src_vertices_state, tar_vertices_state])
605
+
606
+ start_landmark_btn.click(fn=start_landmark_selection,
607
+ inputs=[target_input, source_input],
608
+ outputs=[upload_plot])
609
+
610
+ highlight_btn.click(fn=highlight_landmarks_on_mesh,
611
+ inputs=[target_input, source_input, selected_src, selected_tar],
612
+ outputs=[upload_plot])
613
+
614
+ clear_landmark_btn.click(fn=clear_landmarks,
615
+ inputs=None,
616
+ outputs=[src_ids_state, tar_ids_state, selected_src, selected_tar])
617
+
618
+ select_button.click(fn=update_landmark_ids,
619
+ inputs=[selected_src, selected_tar],
620
+ outputs=[src_ids_state, tar_ids_state, selected_src, selected_tar]
621
+ ).then(fn=set_landmarks_to_registration,
622
+ inputs=[src_ids_state, tar_ids_state],
623
+ outputs=[gr.Textbox(label="Landmark Setup Info")])
624
+
625
+ run_button.click(fn=register_and_visualize_with_zip,
626
+ inputs=[target_input, source_input, output_dir, method_dropdown,
627
+ fricp_useinit, fricp_fileinit, fricp_maxiter, fricp_stop,
628
+ spare_iters, spare_stopcoarse, spare_stopfine],
629
+ outputs=[result_plot, download_button])
630
+
631
+ rerun_button.click(fn=reuse_last_result_as_source,
632
+ inputs=[target_input, output_dir],
633
+ outputs=[source_input, upload_plot])
634
+
635
+ if __name__ == "__main__":
636
+ demo.launch(server_name="0.0.0.0", server_port=7860)
637
+
638
+
apt.txt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ build-essential
3
+ cmake
4
+ g++
5
+ git
6
+ wget
7
+ curl
8
+ make
9
+ pkg-config
10
+ libeigen3-dev
11
+ libopenmesh-dev
12
+ pybind11-dev
13
+ python3-dev
14
+ python3-pip
15
+
16
+
build.sh ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -e
3
+
4
+ # 安装系统依赖
5
+ apt-get update
6
+ xargs -a apt.txt apt-get install -y
7
+
8
+ # 清理并创建 build 目录
9
+ rm -rf build
10
+ mkdir build
11
+ cd build
12
+
13
+ # 生成 Makefile,指定 Python
14
+ cmake .. -DPYTHON_EXECUTABLE=$(which python3)
15
+
16
+ # 编译
17
+ cmake --build . --config Release -j$(nproc)
18
+
19
+ # 回到主目录
20
+ cd ..
21
+
22
+ echo "✅ Build complete. .so is in build/python/"
cmake/FindEigen3.cmake ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FIND_PATH( EIGEN3_INCLUDE_DIRS Eigen/Geometry
2
+ $ENV{EIGEN3DIR}/include
3
+ /usr/local/include/eigen3
4
+ /usr/local/include
5
+ /usr/local/X11R6/include
6
+ /usr/X11R6/include
7
+ /usr/X11/include
8
+ /usr/include/X11
9
+ /usr/include/eigen3/
10
+ /usr/include
11
+ /opt/X11/include
12
+ /opt/include
13
+ ${CMAKE_SOURCE_DIR}/external/eigen/include
14
+ ${CMAKE_SOURCE_DIR}/include/eigen3)
15
+
16
+ SET(EIGEN3_FOUND "NO")
17
+ IF(EIGEN3_INCLUDE_DIRS)
18
+ SET(EIGEN3_FOUND "YES")
19
+ ENDIF()
cmake/FindNanoFlann.cmake ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FIND_PATH(
2
+ # filled variable
3
+ NANOFLANN_INCLUDE_DIR
4
+ # what I am looking for?
5
+ nanoflann.hpp
6
+ # where should I look?
7
+ $ENV{NANOFLANN_DIR}
8
+ ${CMAKE_SOURCE_DIR}/include)
9
+
10
+ IF(NANOFLANN_INCLUDE_DIR)
11
+ SET(NANOFLANN_FOUND TRUE)
12
+ else()
13
+ SET(NANOFLANN_FOUND FALSE)
14
+ ENDIF()
15
+
16
+ IF(NANOFLANN_FOUND)
17
+ IF(NOT CMAKE_FIND_QUIETLY)
18
+ MESSAGE(STATUS "Found NanoFlann: ${NANOFLANN_INCLUDE_DIR}")
19
+ ENDIF()
20
+ ELSE()
21
+ IF(NANOFLANN_FOUND_REQUIRED)
22
+ MESSAGE(FATAL_ERROR "Could not find NanoFlann")
23
+ ENDIF()
24
+ ENDIF()
cmake/FindOpenMesh.cmake ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # - Try to find OpenMesh
2
+ # This is a minimal FindOpenMesh.cmake for systems with libopenmesh-dev installed
3
+
4
+ # 查找头文件
5
+ find_path(OPENMESH_INCLUDE_DIR OpenMesh/Core/IO/MeshIO.hh
6
+ PATHS /usr/include /usr/include/OpenMesh
7
+ PATH_SUFFIXES OpenMesh)
8
+
9
+ # 查找库文件
10
+ find_library(OPENMESH_CORE_LIBRARY OpenMeshCore
11
+ PATHS /usr/lib /usr/lib/x86_64-linux-gnu)
12
+
13
+ find_library(OPENMESH_TOOLS_LIBRARY OpenMeshTools
14
+ PATHS /usr/lib /usr/lib/x86_64-linux-gnu)
15
+
16
+ # 设置变量给 CMake 使用
17
+ set(OPENMESH_LIBRARIES ${OPENMESH_CORE_LIBRARY} ${OPENMESH_TOOLS_LIBRARY})
18
+ set(OPENMESH_FOUND TRUE)
19
+
20
+ # 输出信息
21
+ if(OPENMESH_FOUND)
22
+ message(STATUS "Found OpenMesh:")
23
+ message(STATUS " Include dir: ${OPENMESH_INCLUDE_DIR}")
24
+ message(STATUS " Libraries: ${OPENMESH_LIBRARIES}")
25
+ else()
26
+ message(FATAL_ERROR "OpenMesh not found")
27
+ endif()
cpp/core/nonrigid_spare_registration.cpp ADDED
@@ -0,0 +1,1013 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ #include "nonrigid_spare_registration.h"
3
+ #include "tools.h"
4
+ #include "robust_norm.h"
5
+ #include <median.h>
6
+
7
+ #if (__cplusplus >= 201402L) || (defined(_MSC_VER) && _MSC_VER >= 1800)
8
+ #define MAKE_UNIQUE std::make_unique
9
+ #else
10
+ #define MAKE_UNIQUE company::make_unique
11
+ #endif
12
+
13
+ /// @param Source (one 3D point per column)
14
+ /// @param Target (one 3D point per column)
15
+ /// @param Confidence weights
16
+
17
+
18
+ NonrigidSpareRegistration::NonrigidSpareRegistration() {
19
+ };
20
+
21
+ NonrigidSpareRegistration::~NonrigidSpareRegistration()
22
+ {
23
+ }
24
+
25
+ void NonrigidSpareRegistration::Initialize()
26
+ {
27
+
28
+ //welsch weight paramete
29
+ InitWelschParam();
30
+
31
+
32
+ int knn_num_neighbor = 6;
33
+
34
+ if(!pars_.use_geodesic_dist)
35
+ {
36
+ src_knn_indices_.resize(knn_num_neighbor, n_src_vertex_);
37
+ KDtree* src_tree = new KDtree(src_points_);
38
+ #pragma omp parallel for
39
+ for(int i = 0; i < n_src_vertex_; i++)
40
+ {
41
+ int* out_indices = new int[knn_num_neighbor+1];
42
+ Scalar *out_dists = new Scalar[knn_num_neighbor+1];
43
+ src_tree->query(src_points_.col(i).data(), knn_num_neighbor+1, out_indices, out_dists);
44
+ for(int j = 0; j < knn_num_neighbor; j++)
45
+ {
46
+ src_knn_indices_(j, i) = out_indices[j+1];
47
+ }
48
+ delete[] out_indices;
49
+ delete[] out_dists;
50
+ }
51
+ delete src_tree;
52
+ }
53
+
54
+
55
+ Scalar sample_radius;
56
+
57
+ if(pars_.use_geodesic_dist)
58
+ sample_radius = src_sample_nodes.SampleAndConstuct(*src_mesh_, pars_.uni_sample_radio, src_points_);
59
+ else
60
+ sample_radius = src_sample_nodes.SampleAndConstuctFPS(*src_mesh_, pars_.uni_sample_radio, src_points_, src_knn_indices_, 4, 8);
61
+
62
+
63
+ num_sample_nodes = src_sample_nodes.nodeSize();
64
+ pars_.num_sample_nodes = num_sample_nodes;
65
+
66
+ X_.resize(12 * num_sample_nodes); X_.setZero();
67
+ align_coeff_PV0_.resize(3 * n_src_vertex_, 12 * num_sample_nodes);
68
+ nodes_P_.resize(n_src_vertex_ * 3);
69
+
70
+ nodes_R_.resize(9 * num_sample_nodes); nodes_R_.setZero();
71
+ rigid_coeff_L_.resize(12 * num_sample_nodes, 12 * num_sample_nodes);
72
+ rigid_coeff_J_.resize(12 * num_sample_nodes, 9 * num_sample_nodes);
73
+
74
+ std::vector<Triplet> coeffv(4 * num_sample_nodes);
75
+ std::vector<Triplet> coeffL(9 * num_sample_nodes);
76
+ std::vector<Triplet> coeffJ(9 * num_sample_nodes);
77
+ for (int i = 0; i < num_sample_nodes; i++)
78
+ {
79
+ // X_
80
+ X_[12 * i] = 1.0;
81
+ X_[12 * i + 4] = 1.0;
82
+ X_[12 * i + 8] = 1.0;
83
+
84
+ // nodes_R_
85
+ nodes_R_[9 * i] = 1.0;
86
+ nodes_R_[9 * i + 4] = 1.0;
87
+ nodes_R_[9 * i + 8] = 1.0;
88
+
89
+ for (int j = 0; j < 9; j++)
90
+ {
91
+ // rigid_coeff_L_
92
+ coeffL[9 * i + j] = Triplet(12 * i + j, 12 * i + j, 1.0);
93
+ // rigid_coeff_J_
94
+ coeffJ[9 * i + j] = Triplet(12 * i + j, 9 * i + j, 1.0);
95
+ }
96
+ }
97
+ rigid_coeff_L_.setFromTriplets(coeffL.begin(), coeffL.end());
98
+ rigid_coeff_J_.setFromTriplets(coeffJ.begin(), coeffJ.end());
99
+
100
+
101
+ // update coefficient matrices
102
+ src_sample_nodes.initWeight(align_coeff_PV0_, nodes_P_,
103
+ reg_coeff_B_, reg_right_D_, reg_cwise_weights_);
104
+
105
+ num_graph_edges = reg_cwise_weights_.rows();
106
+
107
+
108
+
109
+ // update ARAP coeffs
110
+ FullInARAPCoeff();
111
+
112
+ local_rotations_.resize(3, n_src_vertex_ * 3);
113
+
114
+ if(pars_.use_geodesic_dist)
115
+ {
116
+ num_edges = src_mesh_->n_halfedges();
117
+ arap_right_.resize(3*src_mesh_->n_halfedges());
118
+ arap_right_fine_.resize(3 * src_mesh_->n_halfedges());
119
+ }
120
+ else{
121
+ num_edges = knn_num_neighbor*n_src_vertex_;
122
+ arap_right_.resize(3*knn_num_neighbor*n_src_vertex_);
123
+ arap_right_fine_.resize(3 * knn_num_neighbor*n_src_vertex_);
124
+ }
125
+
126
+
127
+
128
+ InitRotations();
129
+
130
+
131
+
132
+ sampling_indices_.clear();
133
+
134
+ // start points
135
+ size_t startIndex = 0;
136
+ sampling_indices_.push_back(startIndex);
137
+
138
+
139
+ // FPS to get sampling points in align term
140
+
141
+ VectorX minDistances(n_src_vertex_);
142
+ minDistances.setConstant(std::numeric_limits<Scalar>::max());
143
+ minDistances[startIndex] = 0;
144
+
145
+ vertex_sample_indices_.resize(n_src_vertex_, -1);
146
+ vertex_sample_indices_[startIndex] = 0;
147
+
148
+ // repeat select farthest points
149
+ while (sampling_indices_.size() < align_sampling_num_) {
150
+ // calculate the distance between each point with the sampling points set.
151
+ #pragma omp parallel for
152
+ for (size_t i = 0; i < n_src_vertex_; ++i) {
153
+ if(i==startIndex)
154
+ continue;
155
+
156
+ Scalar dist = (src_points_.col(startIndex) - src_points_.col(i)).norm();
157
+ if(dist < minDistances[i])
158
+ minDistances[i] = dist;
159
+ }
160
+
161
+ // choose farthest point
162
+ int maxDistanceIndex;
163
+ minDistances.maxCoeff(&maxDistanceIndex);
164
+ minDistances[maxDistanceIndex] = 0;
165
+
166
+ // add the farthest point into the sampling points set.
167
+ sampling_indices_.push_back(maxDistanceIndex);
168
+ startIndex= maxDistanceIndex;
169
+ vertex_sample_indices_[startIndex] = sampling_indices_.size()-1;
170
+ }
171
+ if(pars_.use_landmark && pars_.landmark_src.size() > 0)
172
+ {
173
+ CalcLandmarkCoeff();
174
+ vertex_landmark_indices_.resize(n_src_vertex_, -1);
175
+ for(int i = 0; i < pars_.landmark_src.size(); i++)
176
+ {
177
+ vertex_landmark_indices_[pars_.landmark_src[i]] = i;
178
+ }
179
+ }
180
+ }
181
+ void NonrigidSpareRegistration::CalcLandmarkCoeff()
182
+ {
183
+ size_t n_landmarks = pars_.landmark_src.size();
184
+ tar_landmarks_.resize(n_landmarks*3);
185
+ RowMajorSparseMatrix landmark_coeff(n_landmarks*3, 12*num_sample_nodes);
186
+ std::vector<Triplet> coeffs;
187
+ for(int idx = 0; idx < n_landmarks; idx++)
188
+ {
189
+ int src_idx = pars_.landmark_src[idx];
190
+ for (int k = 0; k < 3; k++)
191
+ {
192
+ for (RowMajorSparseMatrix::InnerIterator it(align_coeff_PV0_, src_idx*3+k); it; ++it)
193
+ {
194
+ coeffs.push_back(Triplet(idx*3+k, it.col(), it.value()));
195
+ }
196
+ tar_landmarks_[idx*3+k] = tar_points_(k, pars_.landmark_tar[idx]) - nodes_P_[src_idx*3+k];
197
+ }
198
+ }
199
+ landmark_coeff.setFromTriplets(coeffs.begin(), coeffs.end());
200
+ landmark_mul_ = landmark_coeff.transpose() * landmark_coeff;
201
+ landmark_right_ = landmark_coeff.transpose() * tar_landmarks_;
202
+
203
+ std::vector<Triplet> coeffs_fine;
204
+ landmark_right_fine_.resize(n_src_vertex_*3);
205
+ landmark_mul_fine_.resize(n_src_vertex_*3, n_src_vertex_*3);
206
+ landmark_right_fine_.setZero();
207
+ for(int idx = 0; idx < n_landmarks; idx++)
208
+ {
209
+ int src_idx = pars_.landmark_src[idx];
210
+ for (int k = 0; k < 3; k++)
211
+ {
212
+ coeffs_fine.push_back(Triplet(src_idx*3+k, src_idx*3+k, 1.0));
213
+ landmark_right_fine_[src_idx*3+k] = tar_points_(k, pars_.landmark_tar[idx]);
214
+ }
215
+ }
216
+ landmark_mul_fine_.setFromTriplets(coeffs_fine.begin(), coeffs_fine.end());
217
+ }
218
+
219
+ void NonrigidSpareRegistration::InitWelschParam()
220
+ {
221
+ // welsch parameters
222
+ weight_d_.resize(n_src_vertex_*3);
223
+ weight_d_.setOnes();
224
+
225
+ // Initialize correspondences
226
+ InitCorrespondence(correspondence_pairs_);
227
+
228
+ VectorX init_nus(correspondence_pairs_.size());
229
+ #pragma omp parallel for
230
+ for(size_t i = 0; i < correspondence_pairs_.size(); i++)
231
+ {
232
+ Vector3 closet = correspondence_pairs_[i].position;
233
+ init_nus[i] = (src_mesh_->point(src_mesh_->vertex_handle(correspondence_pairs_[i].src_idx))
234
+ - Vec3(closet[0], closet[1], closet[2])).norm();
235
+ }
236
+ igl::median(init_nus, pars_.Data_nu);
237
+
238
+ if(pars_.calc_gt_err&&n_src_vertex_ == n_tar_vertex_)
239
+ {
240
+ VectorX gt_err(n_src_vertex_);
241
+ for(int i = 0; i < n_src_vertex_; i++)
242
+ {
243
+ gt_err[i] = (src_mesh_->point(src_mesh_->vertex_handle(i)) - tar_mesh_->point(tar_mesh_->vertex_handle(i))).norm();
244
+ }
245
+ pars_.init_gt_mean_errs = std::sqrt(gt_err.squaredNorm()/n_src_vertex_);
246
+ pars_.init_gt_max_errs = gt_err.maxCoeff();
247
+ }
248
+ }
249
+ void NonrigidSpareRegistration::InitwithLandmark()
250
+ {
251
+ // Scalar energy=0., landmark_err=0., reg_err=0., rot_err=0., arap_err=0.;
252
+ VectorX prevV = VectorX::Zero(n_src_vertex_ * 3);
253
+
254
+ // welsch_sweight
255
+ bool run_once = true;
256
+
257
+ Timer time;
258
+ Timer::EventID begin_time, run_time;
259
+
260
+
261
+ w_smo = optimize_w_smo;
262
+
263
+ // pars_.each_energys.push_back(0.0);
264
+ // pars_.each_gt_max_errs.push_back(pars_.init_gt_max_errs);
265
+ // pars_.each_gt_mean_errs.push_back(pars_.init_gt_mean_errs);
266
+ // pars_.each_iters.push_back(0);
267
+ // pars_.each_times.push_back(pars_.non_rigid_init_time);
268
+ // pars_.each_term_energy.push_back(Vector4(0, 0, 0, 0));
269
+
270
+ Scalar gt_err;
271
+
272
+ VectorX prev_X = X_;
273
+
274
+ begin_time = time.get_time();
275
+
276
+ #ifdef DEBUG
277
+ double find_cp_time = 0.0;
278
+ double construct_mat_time = 0.0;
279
+ double solve_eq_time = 0.0;
280
+ double calc_energy_time = 0.0;
281
+ double robust_weight_time = 0.0;
282
+ double update_r_time = 0.0;
283
+ #endif
284
+
285
+ std::cout << "init A" << std::endl;
286
+ RowMajorSparseMatrix A_fixed_coeff = optimize_w_smo * reg_coeff_B_.transpose() * reg_cwise_weights_.asDiagonal() * reg_coeff_B_ + optimize_w_rot * rigid_coeff_L_ + optimize_w_landmark * landmark_mul_; // + optimize_w_arap * arap_coeff_mul_;
287
+
288
+ int out_iter = 0;
289
+ while (out_iter < pars_.max_outer_iters)
290
+ {
291
+ #ifdef DEBUG
292
+ Timer::EventID inner_start_time = time.get_time();
293
+ #endif
294
+
295
+ // int welsch_iter;
296
+ int total_inner_iters = 0;
297
+
298
+ // std::cout << "out_iter" << out_iter << std::endl;
299
+
300
+ mat_A0_ = A_fixed_coeff;
301
+ vec_b_ = optimize_w_smo * reg_coeff_B_.transpose() * reg_cwise_weights_.asDiagonal() * reg_right_D_ + optimize_w_rot * rigid_coeff_J_ * nodes_R_ + optimize_w_landmark * landmark_right_; // + optimize_w_arap * arap_coeff_.transpose() * arap_right_;
302
+
303
+ #ifdef DEBUG
304
+ Timer::EventID end_construct_eq = time.get_time();
305
+ eps_time1 = time.elapsed_time(end_robust_weight, end_construct_eq);
306
+ construct_mat_time += eps_time1;
307
+ #endif
308
+
309
+ if (run_once)
310
+ {
311
+ solver_.analyzePattern(mat_A0_);
312
+ run_once = false;
313
+ }
314
+ solver_.factorize(mat_A0_);
315
+ X_ = solver_.solve(vec_b_);
316
+
317
+ run_time = time.get_time();
318
+ double eps_time = time.elapsed_time(begin_time, run_time);
319
+
320
+ #ifdef DEBUG
321
+ eps_time1 = time.elapsed_time(end_construct_eq, run_time);
322
+ solve_eq_time += eps_time1;
323
+ #endif
324
+
325
+
326
+ #ifdef DEBUG
327
+ energy = CalcEnergy(align_err, reg_err, rot_err, arap_err, reg_cwise_weights_);
328
+ // std::cout << "energy = " << energy << std::endl;
329
+ #endif
330
+ // std::cout << "X = " << X_ << X_.sum() << std::endl;
331
+ deformed_points_ = align_coeff_PV0_ * X_ + nodes_P_;
332
+ // std::cout << "diff = " << (deformed_points_ - prevV).norm() << std::endl;
333
+
334
+ #ifdef DEBUG
335
+ Timer::EventID end_calc_energy = time.get_time();
336
+ eps_time1 = time.elapsed_time(run_time, end_calc_energy);
337
+ calc_energy_time += eps_time1;
338
+ #endif
339
+
340
+
341
+
342
+ #ifdef DEBUG
343
+ Timer::EventID end_update_r = time.get_time();
344
+ eps_time1 = time.elapsed_time(end_calc_energy, end_update_r);
345
+ update_r_time += eps_time1;
346
+ #endif
347
+
348
+ CalcLocalRotations(0);
349
+ CalcNodeRotations();
350
+ CalcDeformedNormals();
351
+
352
+ if (n_src_vertex_ == n_tar_vertex_)
353
+ gt_err = (deformed_points_ - Eigen::Map<VectorX>(tar_points_.data(), 3 * n_src_vertex_)).squaredNorm();
354
+
355
+ // save results
356
+ // pars_.each_gt_mean_errs.push_back(gt_err);
357
+ // pars_.each_gt_max_errs.push_back(0);
358
+ // pars_.each_energys.push_back(energy);
359
+ // pars_.each_iters.push_back(total_inner_iters);
360
+ // pars_.each_term_energy.push_back(Vector4(0.0, reg_err, rot_err, arap_err));
361
+
362
+ if((deformed_points_ - prevV).norm()/sqrtf(n_src_vertex_) < pars_.stop_coarse)
363
+ {
364
+ break;
365
+ }
366
+ prevV = deformed_points_;
367
+ out_iter++;
368
+
369
+ #ifdef DEBUG
370
+ Timer::EventID end_find_cp2 = time.get_time();
371
+ eps_time1 = time.elapsed_time(end_calc_energy, end_find_cp2);
372
+ find_cp_time += eps_time1;
373
+ #endif
374
+ }
375
+
376
+ #ifdef DEBUG
377
+ std::cout << "find cp time = " << find_cp_time
378
+ << "\nconstruct_mat_timem = " << construct_mat_time
379
+ << "\nsolve_eq_time = " << solve_eq_time
380
+ << "\ncalc_energy_time = " << calc_energy_time
381
+ << "\nrobust_weight_time = " << robust_weight_time
382
+ << "\nupdate_r_time = " << update_r_time
383
+ << "\nacculate_iter = " << out_iter << std::endl;
384
+ #endif
385
+ }
386
+
387
+
388
+ Scalar NonrigidSpareRegistration::DoNonRigid()
389
+ {
390
+ // Data term parameters
391
+ Scalar nu1 = pars_.Data_initk * pars_.Data_nu;
392
+ if(pars_.landmark_src.size()>0)
393
+ {
394
+ optimize_w_landmark = 1.0/pars_.landmark_src.size();
395
+ optimize_w_smo = pars_.w_smo/reg_coeff_B_.rows();
396
+ optimize_w_rot = pars_.w_rot/num_sample_nodes;
397
+ optimize_w_arap = pars_.w_arap_coarse/arap_coeff_.rows();
398
+ std::cout << "Init Stage: optimize w_landmark = " << optimize_w_landmark << " | w_smo = " << optimize_w_smo << " | w_rot = " << optimize_w_rot << " | w_arap = " << optimize_w_arap << std::endl;
399
+ InitwithLandmark();
400
+ }
401
+ optimize_w_align = 1.0;
402
+ //A parameter used to balance the influence of each energy component on the total energy.
403
+ optimize_w_smo = pars_.w_smo/reg_coeff_B_.rows() * sampling_indices_.size();
404
+ optimize_w_rot = pars_.w_rot/num_sample_nodes * sampling_indices_.size();
405
+ optimize_w_arap = pars_.w_arap_coarse/arap_coeff_.rows() * sampling_indices_.size();
406
+ std::cout << "Coarse Stage: optimize w_align = " << optimize_w_align << " | w_smo = " << optimize_w_smo << " | w_rot = " << optimize_w_rot << " | w_arap = " << optimize_w_arap << std::endl;
407
+ GraphCoarseReg(nu1);//nu1 is the parameter of welsch kernel
408
+
409
+
410
+ optimize_w_align = 1.0;
411
+ optimize_w_arap = pars_.w_arap_fine /arap_coeff_fine_.rows() * n_src_vertex_;
412
+ std::cout << "Fine Stage: optimize w_align = " << optimize_w_align << " | w_arap = " << optimize_w_arap << std::endl;
413
+ PointwiseFineReg(nu1);//nu1 is still the parameter of welsch kernel
414
+
415
+
416
+ // set the final deformed points to the source mesh
417
+ //and return the final gt error if needed,if not return -1
418
+ deformed_points_3X_ = Eigen::Map<Eigen::Matrix<double, 3, Eigen::Dynamic>>(deformed_points_.data(), 3, n_src_vertex_);
419
+ return 0;
420
+ }
421
+
422
+
423
+ void NonrigidSpareRegistration::CalcNodeRotations()
424
+ {
425
+ #pragma omp parallel for
426
+ for (int i = 0; i < num_sample_nodes; i++)
427
+ {
428
+ Matrix33 rot;
429
+ Eigen::JacobiSVD<Matrix33> svd(Eigen::Map<Matrix33>(X_.data()+12*i, 3,3), Eigen::ComputeFullU | Eigen::ComputeFullV);
430
+ if (svd.matrixU().determinant()*svd.matrixV().determinant() < 0.0) {
431
+ Vector3 S = Vector3::Ones(); S(2) = -1.0;
432
+ rot = svd.matrixU()*S.asDiagonal()*svd.matrixV().transpose();
433
+ }
434
+ else {
435
+ rot = svd.matrixU()*svd.matrixV().transpose();
436
+ }
437
+ nodes_R_.segment(9 * i, 9) = Eigen::Map<VectorX>(rot.data(), 9);
438
+ }
439
+ }
440
+
441
+
442
+
443
+
444
+ void NonrigidSpareRegistration::FullInARAPCoeff()
445
+ {
446
+ arap_laplace_weights_.resize(n_src_vertex_);
447
+ Timer timer;
448
+
449
+ if(pars_.use_geodesic_dist)
450
+ {
451
+ for (int i = 0; i < n_src_vertex_; i++)
452
+ {
453
+ int nn = 0;
454
+ OpenMesh::VertexHandle vh = src_mesh_->vertex_handle(i);
455
+ for (auto vv = src_mesh_->vv_begin(vh); vv != src_mesh_->vv_end(vh); vv++)
456
+ {
457
+ nn++;
458
+ }
459
+ arap_laplace_weights_[i] = 1.0 / nn;
460
+ }
461
+
462
+ std::vector<Triplet> coeffs;
463
+ std::vector<Triplet> coeffs_fine;
464
+ for (int i = 0; i < src_mesh_->n_halfedges(); i++)
465
+ {
466
+ int src_idx = src_mesh_->from_vertex_handle(src_mesh_->halfedge_handle(i)).idx();
467
+ int tar_idx = src_mesh_->to_vertex_handle(src_mesh_->halfedge_handle(i)).idx();
468
+ Scalar w = sqrtf(arap_laplace_weights_[src_idx]);
469
+
470
+
471
+ for (int k = 0; k < 3; k++)
472
+ {
473
+ for (RowMajorSparseMatrix::InnerIterator it(align_coeff_PV0_, src_idx*3+k); it; ++it)
474
+ {
475
+ coeffs.push_back(Triplet(i*3+k, it.col(), w*it.value()));
476
+ }
477
+ for (RowMajorSparseMatrix::InnerIterator it(align_coeff_PV0_, tar_idx*3+k); it; ++it)
478
+ {
479
+ coeffs.push_back(Triplet(i*3+k, it.col(), -w*it.value()));
480
+ }
481
+ }
482
+
483
+
484
+ coeffs_fine.push_back(Triplet(i * 3, src_idx * 3, w));
485
+ coeffs_fine.push_back(Triplet(i * 3 + 1, src_idx * 3 + 1, w));
486
+ coeffs_fine.push_back(Triplet(i * 3 + 2, src_idx * 3 + 2, w));
487
+ coeffs_fine.push_back(Triplet(i * 3, tar_idx * 3, -w));
488
+ coeffs_fine.push_back(Triplet(i * 3 + 1, tar_idx * 3 + 1, -w));
489
+ coeffs_fine.push_back(Triplet(i * 3 + 2, tar_idx * 3 + 2, -w));
490
+ }
491
+
492
+
493
+ arap_coeff_.resize(src_mesh_->n_halfedges()*3, num_sample_nodes * 12);
494
+ arap_coeff_.setFromTriplets(coeffs.begin(), coeffs.end());
495
+ arap_coeff_mul_ = arap_coeff_.transpose() * arap_coeff_;
496
+
497
+
498
+ arap_coeff_fine_.resize(src_mesh_->n_halfedges() * 3, n_src_vertex_ * 3);
499
+ arap_coeff_fine_.setFromTriplets(coeffs_fine.begin(), coeffs_fine.end());
500
+ arap_coeff_mul_fine_ = arap_coeff_fine_.transpose() * arap_coeff_fine_;
501
+ }
502
+ else
503
+ {
504
+ int nn = src_knn_indices_.rows();
505
+ for (int i = 0; i < n_src_vertex_; i++)
506
+ {
507
+ arap_laplace_weights_[i] = 1.0 / nn;
508
+ }
509
+
510
+ std::vector<Triplet> coeffs;
511
+ std::vector<Triplet> coeffs_fine;
512
+ for(int src_idx = 0; src_idx < n_src_vertex_; src_idx++)
513
+ {
514
+ for(int j = 0; j < nn; j++)
515
+ {
516
+ int i = src_idx*nn+j;
517
+ int tar_idx = src_knn_indices_(j, src_idx);
518
+ Scalar w = sqrtf(arap_laplace_weights_[src_idx]);
519
+
520
+
521
+ for (int k = 0; k < 3; k++)
522
+ {
523
+ for (RowMajorSparseMatrix::InnerIterator it(align_coeff_PV0_, src_idx*3+k); it; ++it)
524
+ {
525
+ coeffs.push_back(Triplet(i*3+k, it.col(), w*it.value()));
526
+ }
527
+ for (RowMajorSparseMatrix::InnerIterator it(align_coeff_PV0_, tar_idx*3+k); it; ++it)
528
+ {
529
+ coeffs.push_back(Triplet(i*3+k, it.col(), -w*it.value()));
530
+ }
531
+ }
532
+
533
+
534
+ coeffs_fine.push_back(Triplet(i * 3, src_idx * 3, w));
535
+ coeffs_fine.push_back(Triplet(i * 3 + 1, src_idx * 3 + 1, w));
536
+ coeffs_fine.push_back(Triplet(i * 3 + 2, src_idx * 3 + 2, w));
537
+ coeffs_fine.push_back(Triplet(i * 3, tar_idx * 3, -w));
538
+ coeffs_fine.push_back(Triplet(i * 3 + 1, tar_idx * 3 + 1, -w));
539
+ coeffs_fine.push_back(Triplet(i * 3 + 2, tar_idx * 3 + 2, -w));
540
+ }
541
+ }
542
+
543
+ arap_coeff_.resize(n_src_vertex_*nn*3, num_sample_nodes * 12);
544
+ arap_coeff_.setFromTriplets(coeffs.begin(), coeffs.end());
545
+ arap_coeff_mul_ = arap_coeff_.transpose() * arap_coeff_;
546
+
547
+
548
+ arap_coeff_fine_.resize(n_src_vertex_*nn * 3, n_src_vertex_ * 3);
549
+ arap_coeff_fine_.setFromTriplets(coeffs_fine.begin(), coeffs_fine.end());
550
+ arap_coeff_mul_fine_ = arap_coeff_fine_.transpose() * arap_coeff_fine_;
551
+ }
552
+ }
553
+
554
+
555
+ void NonrigidSpareRegistration::CalcARAPRight()
556
+ {
557
+ if(pars_.use_geodesic_dist)
558
+ {
559
+ #pragma omp parallel for
560
+ for (int i = 0; i < src_mesh_->n_halfedges(); i++)
561
+ {
562
+ int src_idx = src_mesh_->from_vertex_handle(src_mesh_->halfedge_handle(i)).idx();
563
+ int tar_idx = src_mesh_->to_vertex_handle(src_mesh_->halfedge_handle(i)).idx();
564
+
565
+ Vector3 vij = local_rotations_.block(0, 3 * src_idx,3, 3) * (src_points_.col(src_idx) - src_points_.col(tar_idx));
566
+
567
+ Scalar w = sqrtf(arap_laplace_weights_[src_idx]);
568
+
569
+
570
+ arap_right_[i * 3] = w*(vij[0] - nodes_P_[src_idx * 3] + nodes_P_[tar_idx * 3]);
571
+ arap_right_[i * 3 + 1] = w*(vij[1] - nodes_P_[src_idx * 3 + 1] + nodes_P_[tar_idx * 3 + 1]);
572
+ arap_right_[i * 3 + 2] = w*(vij[2] - nodes_P_[src_idx * 3 + 2] + nodes_P_[tar_idx * 3 + 2]);
573
+ }
574
+ }
575
+ else
576
+ {
577
+ int nn = src_knn_indices_.rows();
578
+ #pragma omp parallel for
579
+ for (int src_idx = 0; src_idx < n_src_vertex_; src_idx++)
580
+ {
581
+ for (int j = 0; j < nn; j++)
582
+ {
583
+ int i = src_idx*nn + j;
584
+ int tar_idx = src_knn_indices_(j, src_idx);
585
+
586
+ Vector3 vij = local_rotations_.block(0, 3 * src_idx,3, 3) * (src_points_.col(src_idx) - src_points_.col(tar_idx));
587
+
588
+ Scalar w = sqrtf(arap_laplace_weights_[src_idx]);
589
+
590
+
591
+ arap_right_[i * 3] = w*(vij[0] - nodes_P_[src_idx * 3] + nodes_P_[tar_idx * 3]);
592
+ arap_right_[i * 3 + 1] = w*(vij[1] - nodes_P_[src_idx * 3 + 1] + nodes_P_[tar_idx * 3 + 1]);
593
+ arap_right_[i * 3 + 2] = w*(vij[2] - nodes_P_[src_idx * 3 + 2] + nodes_P_[tar_idx * 3 + 2]);
594
+ }
595
+ }
596
+ }
597
+ }
598
+
599
+ void NonrigidSpareRegistration::CalcARAPRightFine()
600
+ {
601
+ if(pars_.use_geodesic_dist)
602
+ {
603
+ #pragma omp parallel for
604
+ for (int i = 0; i < src_mesh_->n_halfedges(); i++)
605
+ {
606
+ int src_idx = src_mesh_->from_vertex_handle(src_mesh_->halfedge_handle(i)).idx();
607
+ int tar_idx = src_mesh_->to_vertex_handle(src_mesh_->halfedge_handle(i)).idx();
608
+
609
+ Vector3 vij = local_rotations_.block(0, 3 * src_idx, 3, 3) * (src_points_.col(src_idx) - src_points_.col(tar_idx));
610
+
611
+ Scalar w = sqrtf(arap_laplace_weights_[src_idx]);
612
+
613
+ arap_right_fine_[i * 3] = w*(vij[0]);
614
+ arap_right_fine_[i * 3 + 1] = w*(vij[1]);
615
+ arap_right_fine_[i * 3 + 2] = w*(vij[2]);
616
+ }
617
+ }
618
+ else
619
+ {
620
+ int nn = src_knn_indices_.rows();
621
+ #pragma omp parallel for
622
+ for(int src_idx = 0; src_idx < n_src_vertex_; src_idx++)
623
+ {
624
+ for(int j = 0; j < nn; j++)
625
+ {
626
+ int tar_idx = src_knn_indices_(j, src_idx);
627
+ int i = src_idx*nn+j;
628
+
629
+ Vector3 vij = local_rotations_.block(0, 3 * src_idx, 3, 3) * (src_points_.col(src_idx) - src_points_.col(tar_idx));
630
+
631
+ Scalar w = sqrtf(arap_laplace_weights_[src_idx]);
632
+
633
+ arap_right_fine_[i * 3] = w*(vij[0]);
634
+ arap_right_fine_[i * 3 + 1] = w*(vij[1]);
635
+ arap_right_fine_[i * 3 + 2] = w*(vij[2]);
636
+ }
637
+ }
638
+ }
639
+ }
640
+
641
+
642
+ void NonrigidSpareRegistration::InitRotations()
643
+ {
644
+ local_rotations_.resize(3, n_src_vertex_ * 3);
645
+ local_rotations_.setZero();
646
+ #pragma omp parallel for
647
+ for (int i = 0; i < n_src_vertex_; i++)
648
+ {
649
+ local_rotations_(0, i * 3) = 1;
650
+ local_rotations_(1, i * 3 + 1) = 1;
651
+ local_rotations_(2, i * 3 + 2) = 1;
652
+ }
653
+ }
654
+
655
+
656
+ void NonrigidSpareRegistration::CalcLocalRotations(bool isCoarseAlign)
657
+ {
658
+
659
+ #pragma omp parallel for
660
+ for (int i = 0; i < n_src_vertex_; i++)
661
+ {
662
+ Matrix33 sum;
663
+ sum.setZero();
664
+
665
+ int nn = 0;
666
+ if(pars_.use_geodesic_dist)
667
+ {
668
+ OpenMesh::VertexHandle vh = src_mesh_->vertex_handle(i);
669
+ for (auto vv = src_mesh_->vv_begin(vh); vv != src_mesh_->vv_end(vh); vv++)
670
+ {
671
+ int neighbor_idx = vv->idx();
672
+ Vector3 dv = src_points_.col(i) - src_points_.col(neighbor_idx);
673
+ Vector3 new_dv = deformed_points_.segment(3 * i, 3) - deformed_points_.segment(3 * neighbor_idx, 3);
674
+ sum += dv * new_dv.transpose();
675
+ nn++;
676
+ }
677
+ }
678
+ else
679
+ {
680
+ nn = src_knn_indices_.rows();
681
+ for(int j = 0; j < nn; j++)
682
+ {
683
+ int neighbor_idx = src_knn_indices_(j, i);
684
+ Vector3 dv = src_points_.col(i) - src_points_.col(neighbor_idx);
685
+ Vector3 new_dv = deformed_points_.segment(3 * i, 3) - deformed_points_.segment(3 * neighbor_idx, 3);
686
+ sum += dv * new_dv.transpose();
687
+ }
688
+ }
689
+
690
+
691
+ sum*= 1.0*optimize_w_arap/nn;
692
+
693
+ if(!isCoarseAlign)
694
+ {
695
+ int tar_idx = correspondence_pairs_[i].tar_idx;
696
+ Vector3 d = deformed_points_.segment(3 * i, 3) - tar_points_.col(tar_idx);
697
+ Scalar c = (target_normals_.col(tar_idx) + deformed_normals_.col(i)).dot(d);
698
+ Scalar d_norm2 = d[0]*d[0] + d[1]*d[1] + d[2]*d[2];
699
+ Vector3 h = deformed_normals_.col(i) - c*d/d_norm2;
700
+
701
+ Scalar w = optimize_w_align*d_norm2*weight_d_[i];
702
+ sum += w * src_normals_.col(i) * h.transpose();
703
+ }
704
+ else if(vertex_sample_indices_[i] >= 0)
705
+ {
706
+ int tar_idx = correspondence_pairs_[vertex_sample_indices_[i]].tar_idx;
707
+ Vector3 d = deformed_points_.segment(3 * i, 3) - tar_points_.col(tar_idx);
708
+ Scalar c = (target_normals_.col(tar_idx) + deformed_normals_.col(i)).dot(d);
709
+ Scalar d_norm2 = d[0]*d[0] + d[1]*d[1] + d[2]*d[2];
710
+ Vector3 h = deformed_normals_.col(i) - c*d/d_norm2;
711
+
712
+ Scalar w = optimize_w_align*d_norm2*weight_d_[vertex_sample_indices_[i]];
713
+ sum += w * src_normals_.col(i) * h.transpose();
714
+ }
715
+
716
+
717
+ Eigen::JacobiSVD<Matrix33> svd(sum, Eigen::ComputeFullU | Eigen::ComputeFullV);
718
+
719
+ if (svd.matrixU().determinant()*svd.matrixV().determinant() < 0.0) {
720
+ Vector3 S = Vector3::Ones(); S(2) = -1.0;
721
+ sum = svd.matrixV()*S.asDiagonal()*svd.matrixU().transpose();
722
+ }
723
+ else {
724
+ sum = svd.matrixV()*svd.matrixU().transpose();
725
+ }
726
+ for (int s = 0; s < 3; s++)
727
+ {
728
+ for (int t = 0; t < 3; t++)
729
+ {
730
+ local_rotations_(s, 3 * i + t) = sum(s, t);
731
+ }
732
+ }
733
+ }
734
+ }
735
+
736
+ void NonrigidSpareRegistration::CalcDeformedNormals()
737
+ {
738
+ #pragma omp parallel for
739
+ for (int i = 0; i < n_src_vertex_; i++)
740
+ {
741
+ deformed_normals_.col(i) = local_rotations_.block(0, i * 3, 3, 3) * src_normals_.col(i);
742
+ }
743
+ }
744
+
745
+ void NonrigidSpareRegistration::CalcNormalsSum()
746
+ {
747
+ #pragma omp parallel for
748
+ for(int i = 0; i < correspondence_pairs_.size(); i++)
749
+ {
750
+ int sidx = correspondence_pairs_[i].src_idx;
751
+ int tidx = correspondence_pairs_[i].tar_idx;
752
+ int j = 0;
753
+ for (RowMajorSparseMatrix::InnerIterator it(normals_sum_, i); it; ++it)
754
+ {
755
+ it.valueRef() = deformed_normals_(j, sidx) + target_normals_(j, tidx);
756
+ j++;
757
+ }
758
+ }
759
+ }
760
+
761
+
762
+ void NonrigidSpareRegistration::InitNormalsSum()
763
+ {
764
+ std::vector<Triplet> coeffs(3 * correspondence_pairs_.size());
765
+ normals_sum_.resize(correspondence_pairs_.size(), 3*n_src_vertex_);
766
+ normals_sum_.setZero();
767
+
768
+ #pragma omp parallel for
769
+ for(int i = 0; i < correspondence_pairs_.size(); i++)
770
+ {
771
+ int sidx = correspondence_pairs_[i].src_idx;
772
+ int tidx = correspondence_pairs_[i].tar_idx;
773
+ coeffs[i * 3] = Triplet(i, 3 * sidx, deformed_normals_(0, sidx) + target_normals_(0, tidx));
774
+ coeffs[i * 3 + 1] = Triplet(i, 3 * sidx + 1, deformed_normals_(1, sidx) + target_normals_(1, tidx));
775
+ coeffs[i * 3 + 2] = Triplet(i, 3 * sidx + 2, deformed_normals_(2, sidx) + target_normals_(2, tidx));
776
+ }
777
+ normals_sum_.setFromTriplets(coeffs.begin(), coeffs.end());
778
+ }
779
+
780
+ void NonrigidSpareRegistration::PointwiseFineReg(Scalar nu1)
781
+ {
782
+
783
+ Scalar energy=-1., align_err=-1., arap_err=-1.;
784
+
785
+ VectorX prevV = VectorX::Zero(n_src_vertex_ * 3);
786
+
787
+ bool run_once = true;
788
+
789
+
790
+ // Smooth term parameters
791
+ w_align = optimize_w_align;
792
+ w_smo = optimize_w_smo;
793
+ w_align = optimize_w_align *(2.0*nu1*nu1);
794
+
795
+
796
+ Scalar gt_err = -1;
797
+
798
+ double construct_mat_time = 0.0;
799
+ double solve_eq_time = 0.0;
800
+
801
+
802
+ int out_iter = 0;
803
+ while (out_iter < pars_.max_outer_iters)
804
+ {
805
+ // Find clost points
806
+ FindClosestPoints(target_tree_,deformed_points_,correspondence_pairs_);
807
+ // according correspondence_pairs to update corres_U0_;
808
+ corres_U0_.setZero();
809
+ weight_d_.resize(n_src_vertex_);
810
+
811
+
812
+ for (size_t i = 0; i < correspondence_pairs_.size(); i++)
813
+ {
814
+ corres_U0_.segment(i * 3, 3) = correspondence_pairs_[i].position;
815
+ weight_d_[i] = correspondence_pairs_[i].min_dist2;
816
+ int tar_idx = correspondence_pairs_[i].tar_idx;
817
+ if(deformed_normals_.col(i).dot(target_normals_.col(tar_idx))<0)
818
+ weight_d_[i] = -1;
819
+ }
820
+
821
+ // update weight
822
+
823
+ welsch_weight(weight_d_, nu1);
824
+
825
+ if (run_once == true && pars_.use_landmark == true)
826
+ {
827
+ weight_d_.setOnes();
828
+ }
829
+
830
+ // construct matrix A0 and pre-decompose
831
+ if(out_iter==0)
832
+ InitNormalsSum();
833
+ else
834
+ CalcNormalsSum();
835
+
836
+ RowMajorSparseMatrix normals_sum_mul = normals_sum_.transpose() * weight_d_.asDiagonal()* normals_sum_;
837
+ mat_A0_ = optimize_w_align * normals_sum_mul
838
+ + optimize_w_arap * arap_coeff_mul_fine_;
839
+
840
+ CalcARAPRightFine();
841
+
842
+ vec_b_ = optimize_w_align * normals_sum_mul * corres_U0_
843
+ + optimize_w_arap * arap_coeff_fine_.transpose() * arap_right_fine_;
844
+
845
+
846
+ if (run_once)
847
+ {
848
+ solver_.analyzePattern(mat_A0_);
849
+ run_once = false;
850
+ }
851
+ solver_.factorize(mat_A0_);
852
+ deformed_points_ = solver_.solve(vec_b_);
853
+ CalcLocalRotations(false);
854
+ CalcDeformedNormals();
855
+ if((deformed_points_ - prevV).norm()/sqrtf(n_src_vertex_) < pars_.stop_fine)
856
+ {
857
+ break;
858
+ }
859
+ prevV = deformed_points_;
860
+ out_iter++;
861
+ }
862
+ }
863
+
864
+ void NonrigidSpareRegistration::GraphCoarseReg(Scalar nu1)
865
+ {
866
+ Scalar energy=0., align_err=0., reg_err=0., rot_err=0., arap_err=0.;
867
+ VectorX prevV = VectorX::Zero(n_src_vertex_ * 3);
868
+
869
+ // welsch_sweight
870
+ bool run_once = true;
871
+
872
+
873
+ w_align = optimize_w_align;
874
+ w_smo = optimize_w_smo;
875
+ w_align = optimize_w_align *(2.0*nu1*nu1);
876
+
877
+ Scalar gt_err;
878
+
879
+ VectorX prev_X = X_;
880
+
881
+ RowMajorSparseMatrix A_fixed_coeff = optimize_w_smo * reg_coeff_B_.transpose() * reg_cwise_weights_.asDiagonal() * reg_coeff_B_ + optimize_w_rot * rigid_coeff_L_ + optimize_w_arap * arap_coeff_mul_;
882
+ int out_iter = 0;
883
+ while (out_iter < pars_.max_outer_iters)
884
+ {
885
+
886
+
887
+ correspondence_pairs_.clear();
888
+ FindClosestPoints(target_tree_,correspondence_pairs_, deformed_points_, sampling_indices_);
889
+ corres_U0_.setZero();
890
+ weight_d_.resize(correspondence_pairs_.size());
891
+ weight_d_.setConstant(-1);
892
+
893
+ #pragma omp parallel for
894
+ for (size_t i = 0; i < correspondence_pairs_.size(); i++)
895
+ {
896
+ corres_U0_.segment(correspondence_pairs_[i].src_idx * 3, 3) = correspondence_pairs_[i].position;
897
+ weight_d_[i] = correspondence_pairs_[i].min_dist2;
898
+ if(deformed_normals_.col(correspondence_pairs_[i].src_idx).dot(target_normals_.col(correspondence_pairs_[i].tar_idx))<0)
899
+ weight_d_[i] = -1;
900
+ }
901
+
902
+ // update weight
903
+
904
+ welsch_weight(weight_d_, nu1);
905
+
906
+ if (run_once == true && pars_.use_landmark == true)
907
+ {
908
+ weight_d_.setOnes();
909
+ }
910
+
911
+ if(out_iter==0)
912
+ InitNormalsSum();
913
+ else
914
+ CalcNormalsSum();
915
+
916
+ diff_UP_ = (corres_U0_ - nodes_P_);
917
+
918
+ RowMajorSparseMatrix weight_NPV = normals_sum_ * align_coeff_PV0_;
919
+
920
+ mat_A0_ = optimize_w_align * weight_NPV.transpose() * weight_d_.asDiagonal() * weight_NPV + A_fixed_coeff;
921
+
922
+ CalcARAPRight();
923
+ vec_b_ = optimize_w_align * weight_NPV.transpose() * weight_d_.asDiagonal() * normals_sum_ * diff_UP_ + optimize_w_smo * reg_coeff_B_.transpose() * reg_cwise_weights_.asDiagonal() * reg_right_D_ + optimize_w_rot * rigid_coeff_J_ * nodes_R_ + optimize_w_arap * arap_coeff_.transpose() * arap_right_;
924
+
925
+
926
+ if (run_once)
927
+ {
928
+ solver_.analyzePattern(mat_A0_);
929
+ run_once = false;
930
+ }
931
+ solver_.factorize(mat_A0_);
932
+ X_ = solver_.solve(vec_b_);
933
+ deformed_points_ = align_coeff_PV0_ * X_ + nodes_P_;
934
+ CalcLocalRotations(true);
935
+ CalcNodeRotations();
936
+ CalcDeformedNormals();
937
+
938
+ if (n_src_vertex_ == n_tar_vertex_)
939
+ gt_err = (deformed_points_ - Eigen::Map<VectorX>(tar_points_.data(), 3 * n_src_vertex_)).squaredNorm();
940
+
941
+
942
+ if((deformed_points_ - prevV).norm()/sqrtf(n_src_vertex_) < pars_.stop_coarse)
943
+ {
944
+ break;
945
+ }
946
+ prevV = deformed_points_;
947
+ out_iter++;
948
+ }
949
+
950
+ }
951
+
952
+ void NonrigidSpareRegistration::Paras_init(
953
+ int iters,
954
+ double stopcoarse,
955
+ double stopfine,
956
+ bool uselandmark,
957
+ std::vector<int> src,
958
+ std::vector<int> tar)
959
+ {
960
+ paras.max_outer_iters = iters;
961
+ paras.stop_coarse = stopcoarse;
962
+ paras.stop_fine = stopfine;
963
+
964
+ if (uselandmark && !src.empty() && src.size() == tar.size()) {
965
+ paras.use_landmark = true;
966
+ paras.landmark_src = src;
967
+ paras.landmark_tar = tar;
968
+ } else {
969
+ paras.use_landmark = false;
970
+ paras.landmark_src.clear();
971
+ paras.landmark_tar.clear();
972
+ }
973
+ }
974
+
975
+
976
+ void NonrigidSpareRegistration::Register()
977
+ {
978
+ if(src_mesh.n_vertices()==0 || tar_mesh.n_vertices()==0)
979
+ exit(0);
980
+
981
+ if(src_mesh.n_vertices() != tar_mesh.n_vertices())
982
+ paras.calc_gt_err = false;
983
+
984
+ if(src_mesh.n_faces()==0)
985
+ paras.use_geodesic_dist = false;
986
+
987
+ if(paras.use_landmark)
988
+ read_landmark(landmark_file.c_str(), paras.landmark_src, paras.landmark_tar);
989
+ if(normalize)// The default value of `normalize` is `true`.
990
+ scale = mesh_scaling(src_mesh, tar_mesh);
991
+ pars_ = paras;
992
+ Timer time;
993
+ std::cout << "registration to initial... (mesh scale: " << scale << ")" << std::endl;
994
+ Timer::EventID time1 = time.get_time();
995
+ Init_data();
996
+ Initialize();
997
+ Timer::EventID time2 = time.get_time();
998
+ std::cout << "non-rigid registration... (graph node number: " << pars_.num_sample_nodes << ")" << std::endl;
999
+ DoNonRigid();
1000
+ Timer::EventID time3 = time.get_time();
1001
+ std::cout << "Registration done!\ninitialize time : "
1002
+ << time.elapsed_time(time1, time2) << " s \tnon-rigid reg running time = " << time.elapsed_time(time2, time3) << " s" << std::endl;
1003
+ return;
1004
+ }
1005
+
1006
+ void NonrigidSpareRegistration::Reg(const std::string& file_target,
1007
+ const std::string& file_source,
1008
+ const std::string& out_path )
1009
+ {
1010
+ Read_data(file_target, file_source);
1011
+ Register();
1012
+ Output_data(out_path, "spare");
1013
+ }
cpp/core/nonrigid_spare_registration.h ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef NONRIGIDREG_SPARE_H
2
+ #define NONRIGIDREG_SPARE_H
3
+
4
+ #include "nodeSampler.h"
5
+ #include "registration.h"
6
+
7
+
8
+
9
+
10
+ #ifdef USE_PARDISO
11
+ #include <Eigen/PardisoSupport>
12
+ #endif
13
+
14
+
15
+ class NonrigidSpareRegistration : public Registration {
16
+ public:
17
+ NonrigidSpareRegistration();
18
+ ~NonrigidSpareRegistration();
19
+
20
+ // Adjusted parameters
21
+ spare::Parameters pars_;
22
+
23
+ // Non-rigid energy function parameters
24
+ VectorX weight_d_; // robust weight for alignment α_i
25
+ RowMajorSparseMatrix mat_A0_; // symmetric coefficient matrix for linear equations
26
+ VectorX vec_b_; // right-hand side for linear equations
27
+
28
+ // Welsch parameters
29
+ Scalar nu;
30
+
31
+ #ifdef USE_PARDISO
32
+ Eigen::PardisoLDLT<RowMajorSparseMatrix, Eigen::Lower> solver_;
33
+ #else
34
+ Eigen::SimplicialLDLT<RowMajorSparseMatrix, Eigen::Lower> solver_;
35
+ #endif
36
+
37
+ // Sampling parameters
38
+ int num_sample_nodes; // (r,) number of sample nodes
39
+ int num_graph_edges;
40
+ int num_edges;
41
+
42
+ // Node storage structure
43
+ svr::nodeSampler src_sample_nodes;
44
+
45
+ // Variables
46
+ VectorX X_; // (12r,) transformations of sample nodes
47
+
48
+ // Alignment term
49
+ VectorX nodes_P_; // (3n,) all sample nodes' coordinates
50
+ RowMajorSparseMatrix align_coeff_PV0_; // (3n, 12r) coefficient matrix F
51
+
52
+ // Smooth term
53
+ RowMajorSparseMatrix reg_coeff_B_; // (6|E_G|, 12r) smoothness between nodes
54
+ VectorX reg_right_D_; // (6|E_G|,) coordinate differences between xi and xj
55
+ VectorX reg_cwise_weights_; // (6|E_G|,) smooth weights
56
+
57
+ // landmark term
58
+ RowMajorSparseMatrix landmark_mul_; // (12r, 12r)
59
+ VectorX tar_landmarks_; // (3, l)
60
+ VectorX landmark_right_; // (12r,)
61
+ RowMajorSparseMatrix landmark_mul_fine_; // (3n, 3n)
62
+ VectorX landmark_right_fine_; // (3n,)
63
+ // Rotation matrix term
64
+ VectorX nodes_R_; // (9r,) proj(A)
65
+ RowMajorSparseMatrix rigid_coeff_L_; // (12r, 12r) H
66
+ RowMajorSparseMatrix rigid_coeff_J_; // (9r, 12r) Y
67
+ VectorX diff_UP_; // auxiliary matrix
68
+
69
+ // ARAP term (coarse)
70
+ VectorX arap_laplace_weights_; // (6E,)
71
+ Matrix3X local_rotations_; // (3n, 3) R
72
+ RowMajorSparseMatrix arap_coeff_; // (6E, 12r) B
73
+ RowMajorSparseMatrix arap_coeff_mul_; // (12r, 12r) BᵀB
74
+ VectorX arap_right_; // (6E,) L
75
+
76
+ // ARAP term (fine)
77
+ RowMajorSparseMatrix arap_coeff_fine_; // (6E, 3n) B
78
+ RowMajorSparseMatrix arap_coeff_mul_fine_;// (3n, 3n) BᵀB
79
+ VectorX arap_right_fine_; // (6E,) Y
80
+
81
+ // Point clouds
82
+ RowMajorSparseMatrix normals_sum_; // (n, 3n) N for alignment term
83
+
84
+ // Sampling points & vertices relation
85
+ std::vector<size_t> sampling_indices_;
86
+ std::vector<int> vertex_sample_indices_;
87
+ std::vector<int> vertex_landmark_indices_;
88
+ // KNN-neighbor indices for source points (if no faces)
89
+ Eigen::MatrixXi src_knn_indices_;
90
+
91
+ int align_sampling_num_ = 3000;
92
+
93
+ // Weights of terms during optimization
94
+ Scalar w_align;
95
+ Scalar w_smo;
96
+ Scalar optimize_w_align;
97
+ Scalar optimize_w_smo;
98
+ Scalar optimize_w_rot;
99
+ Scalar optimize_w_arap;
100
+ Scalar optimize_w_landmark;
101
+ // Initialization & main steps
102
+ void Initialize();
103
+ void InitFromInput(Mesh& src_mesh, Mesh& tar_mesh, spare::Parameters& paras);
104
+ virtual Scalar DoNonRigid();
105
+
106
+ // Welsch weighting
107
+ //void welsch_weight(VectorX& r, Scalar p);
108
+ void InitWelschParam();
109
+ void InitwithLandmark();
110
+
111
+ // Rotation calculations
112
+ void CalcNodeRotations();
113
+ void InitRotations();
114
+ void CalcLocalRotations(bool isCoarseAlign);
115
+
116
+ // ARAP calculations
117
+ void FullInARAPCoeff();
118
+ void CalcARAPRight();
119
+ void CalcARAPRightFine();
120
+ // Normal calculations
121
+ void CalcDeformedNormals();
122
+ void InitNormalsSum();
123
+ void CalcNormalsSum();
124
+ void CalcLandmarkCoeff();
125
+ // Optimization steps
126
+ void PointwiseFineReg(Scalar nu1);
127
+ void GraphCoarseReg(Scalar nu1);
128
+ void Paras_init(int iters = 30 ,double stopcoarse=1e-3,double stopfine=1e-4,bool uselandmark = false,std::vector<int> src=std::vector<int>(),std::vector<int> tar=std::vector<int>());
129
+
130
+ void Register()override;
131
+ void Reg(const std::string& file_target,
132
+ const std::string& file_source,
133
+ const std::string& out_path)override;
134
+ std::string landmark_file;
135
+ spare::Parameters paras;
136
+ bool normalize = true;
137
+
138
+ };
139
+
140
+
141
+
142
+ #endif
cpp/core/registration.cpp ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include "registration.h"
2
+ #include "io.h"
3
+ #include "tools.h"
4
+ #include <median.h>
5
+
6
+ void Registration::FindClosestPoints(VPairs & corres)
7
+ {
8
+ corres.resize(n_src_vertex_);
9
+
10
+ #pragma omp parallel for
11
+ for (int i = 0; i < n_src_vertex_; i++)
12
+ {
13
+ Scalar mini_dist2;
14
+ int idx = target_tree_->closest(src_mesh_->point(src_mesh_->vertex_handle(i)).data(), mini_dist2);
15
+ Closest c;
16
+ c.src_idx = i;
17
+ c.position = tar_points_.col(idx);
18
+ c.normal = Vec2Eigen(tar_mesh_->normal(tar_mesh_->vertex_handle(idx)));
19
+ c.min_dist2 = mini_dist2;
20
+ c.tar_idx = idx;
21
+ corres[i] = c;
22
+ }
23
+ }
24
+ void Registration::FindClosestPoints(KDtree* target_tree_tem,VectorX & deformed_v,VPairs & corres)
25
+ {
26
+ corres.resize(n_src_vertex_);
27
+ #pragma omp parallel for
28
+ for (int i = 0; i < n_src_vertex_; i++)
29
+ {
30
+ Scalar mini_dist2;
31
+ int idx = target_tree_tem->closest(deformed_v.data() + 3*i, mini_dist2);
32
+ Closest c;
33
+ c.src_idx = i;
34
+ c.position = tar_points_.col(idx);
35
+ c.min_dist2 = mini_dist2;
36
+ c.tar_idx = idx;
37
+ corres[i] = c;
38
+ }
39
+
40
+ }
41
+ void Registration::FindClosestPoints(KDtree* target_tree_tem,VPairs & corres, VectorX & deformed_v, std::vector<size_t>& sample_indices)
42
+ {
43
+ corres.resize(sample_indices.size());
44
+ #pragma omp parallel for
45
+ for(int i = 0; i < sample_indices.size(); i++)
46
+ {
47
+ int sidx = sample_indices[i];
48
+ Scalar mini_dist2;
49
+ int tidx = target_tree_tem->closest(deformed_v.data() + 3*sidx, mini_dist2);
50
+ Closest c;
51
+ c.src_idx = sidx;
52
+ c.position = tar_points_.col(tidx);
53
+ c.min_dist2 = mini_dist2;
54
+ c.tar_idx = tidx;
55
+ corres[i] = c;
56
+ }
57
+ }
58
+ double Registration::FindKnearestMed(const KDtree& kdtree,
59
+ const Matrix3X& X, int nk)
60
+ {
61
+ Eigen::VectorXd X_nearest(X.cols());
62
+ #pragma omp parallel for
63
+ for(int i = 0; i<X.cols(); i++)
64
+ {
65
+ int* id = new int[nk];
66
+ double *dist = new double[nk];
67
+ kdtree.query(X.col(i).data(), nk, id, dist);
68
+ Eigen::VectorXd k_dist = Eigen::Map<Eigen::VectorXd>(dist, nk);
69
+ igl::median(k_dist.tail(nk-1), X_nearest[i]);
70
+ delete[]id;
71
+ delete[]dist;
72
+ }
73
+ double med;
74
+ igl::median(X_nearest, med);
75
+ return sqrt(med);
76
+ }
77
+ void Registration::LandMarkCorres(VPairs & corres)
78
+ {
79
+ corres.clear();
80
+ if (pars_.landmark_src.size() != pars_.landmark_tar.size())
81
+ {
82
+ std::cout << "Error: landmark data wrong!!" << std::endl;
83
+ }
84
+ n_landmark_nodes_ = pars_.landmark_tar.size();
85
+ for (int i = 0; i < n_landmark_nodes_; i++)
86
+ {
87
+ Closest c;
88
+ c.src_idx = pars_.landmark_src[i];
89
+ OpenMesh::VertexHandle vh = tar_mesh_->vertex_handle(pars_.landmark_tar[i]);
90
+
91
+ if (c.src_idx > n_src_vertex_ || c.src_idx < 0)
92
+ std::cout << "Error: source index in Landmark is out of range!" << std::endl;
93
+ if (vh.idx() < 0)
94
+ std::cout << "Error: target index in Landmark is out of range!" << std::endl;
95
+
96
+ c.position = Vec2Eigen(tar_mesh_->point(vh));
97
+ c.normal = Vec2Eigen(tar_mesh_->normal(vh));
98
+ corres.push_back(c);
99
+ }
100
+ std::cout << " use landmark and landmark is ... " << pars_.landmark_src.size() << std::endl;
101
+ }
102
+
103
+
104
+ bool Registration::read_landmark(const char* filename, std::vector<int>& landmark_src, std::vector<int>& landmark_tar)
105
+ {
106
+ std::ifstream in(filename);
107
+ std::cout << "filename = " << filename << std::endl;
108
+ if (!in)
109
+ {
110
+ std::cout << "Can't open the landmark file!!" << std::endl;
111
+ return false;
112
+ }
113
+ int x, y;
114
+ landmark_src.clear();
115
+ landmark_tar.clear();
116
+ while (!in.eof())
117
+ {
118
+ if (in >> x >> y) {
119
+ landmark_src.push_back(x);
120
+ landmark_tar.push_back(y);
121
+ }
122
+ }
123
+ in.close();
124
+ std::cout << "landmark_src = " << landmark_src.size() << " tar = " << landmark_tar.size() << std::endl;
125
+ return true;
126
+ }
127
+
128
+
129
+ void Registration::InitCorrespondence(VPairs & corres)
130
+ {
131
+ if(pars_.use_landmark)
132
+ {
133
+ corres.clear();
134
+ for(size_t i = 0; i < pars_.landmark_src.size(); i++)
135
+ {
136
+ Closest c;
137
+ c.src_idx = pars_.landmark_src[i];
138
+ c.tar_idx = pars_.landmark_tar[i];
139
+ c.position = tar_points_.col(c.tar_idx);
140
+ c.normal = Vec2Eigen(tar_mesh_->normal(tar_mesh_->vertex_handle(c.tar_idx)));
141
+ corres.push_back(c);
142
+ }
143
+ }
144
+ else
145
+ {
146
+ FindClosestPoints(corres);
147
+ }
148
+ }
149
+ void Registration::Init_data()
150
+ {
151
+ src_mesh_ = new Mesh;
152
+ tar_mesh_ = new Mesh;
153
+ src_mesh_ = &src_mesh;
154
+ tar_mesh_ = &tar_mesh;
155
+ deformed_mesh =src_mesh;
156
+ deformed_mesh_=&deformed_mesh;
157
+ n_src_vertex_ = src_mesh_->n_vertices();
158
+ n_tar_vertex_ = tar_mesh_->n_vertices();
159
+ tar_points_.resize(3, n_tar_vertex_);
160
+ target_normals_.resize(3, n_tar_vertex_);
161
+ for (int i = 0; i < n_tar_vertex_; i++)
162
+ {
163
+ auto vh = tar_mesh_->vertex_handle(i);
164
+
165
+ // 顶点坐标
166
+ tar_points_(0, i) = tar_mesh_->point(vh)[0];
167
+ tar_points_(1, i) = tar_mesh_->point(vh)[1];
168
+ tar_points_(2, i) = tar_mesh_->point(vh)[2];
169
+
170
+ // 顶点法向量
171
+ target_normals_(0, i) = tar_mesh_->normal(vh)[0];
172
+ target_normals_(1, i) = tar_mesh_->normal(vh)[1];
173
+ target_normals_(2, i) = tar_mesh_->normal(vh)[2];
174
+ }
175
+
176
+ // construct kd Tree
177
+ target_tree_ = new KDtree(tar_points_);
178
+ src_points_.resize(3, n_src_vertex_);
179
+ src_normals_.resize(3, n_src_vertex_);
180
+ corres_U0_.resize(3* n_src_vertex_);
181
+ #pragma omp parallel for
182
+ for (int i = 0; i < n_src_vertex_; i++)
183
+ {
184
+ Vec3 p = src_mesh_->point(src_mesh_->vertex_handle(i));
185
+ src_points_(0, i) = p[0];
186
+ src_points_(1, i) = p[1];
187
+ src_points_(2, i) = p[2];
188
+ Vec3 n = src_mesh_->normal(src_mesh_->vertex_handle(i));
189
+ src_normals_(0, i) = n[0];
190
+ src_normals_(1, i) = n[1];
191
+ src_normals_(2, i) = n[2];
192
+ }
193
+ deformed_normals_ = src_normals_;
194
+ deformed_points_ = Eigen::Map<VectorX>(src_points_.data(), 3 * n_src_vertex_);
195
+ }
196
+
197
+ void Registration::Read_data(const std::string& file_target,
198
+ const std::string& file_source)
199
+ {
200
+ read_by_openmesh(file_source, src_mesh);
201
+ read_by_openmesh(file_target, tar_mesh);
202
+ return;
203
+ }
204
+ void Registration::Read_data(const Matrix3X &target_p,const Matrix3X &source_p,const Matrix3X &target_n,const Matrix3X &source_n)
205
+ {
206
+ SetMeshPoints(&src_mesh, source_p, source_n);
207
+ SetMeshPoints(&tar_mesh, target_p, target_n);
208
+ return;
209
+ }
210
+
211
+
212
+ void Registration::Read_data(const Mesh& tar,const Mesh& src)
213
+ {
214
+ src_mesh=src;
215
+ tar_mesh=tar;
216
+ return;
217
+ }
218
+ Scalar Registration::SetMeshPoints(Mesh* mesh, const Matrix3X &point, const Matrix3X &point_n)
219
+ {
220
+ int n_vertices = point.cols();
221
+ mesh->request_vertex_normals(); // 确保法向量可以设置
222
+
223
+ bool use_input_normals = (point_n.cols() == n_vertices);
224
+ if (!use_input_normals) {
225
+ std::cout << "[Info] 法向量数据不正确或为空,正在自动计算法向量..." << std::endl;
226
+ }
227
+
228
+ // 判断 mesh 是否为空
229
+ bool mesh_is_empty = (mesh->n_vertices() == 0);
230
+ std::vector<Mesh::VertexHandle> vhandles(n_vertices);
231
+
232
+ if (mesh_is_empty) {
233
+ // 空 mesh:添加顶点
234
+ for (int i = 0; i < n_vertices; i++) {
235
+ vhandles[i] = mesh->add_vertex(Vec3(point(0, i), point(1, i), point(2, i)));
236
+ }
237
+ } else {
238
+ // 已有 mesh:直接覆盖顶点位置
239
+ if (mesh->n_vertices() != n_vertices) {
240
+ std::cerr << "[Warning] mesh 顶点数量和要替换的mesh不匹配" << std::endl;
241
+ }
242
+ int idx = 0;
243
+ for (auto v : mesh->vertices()) {
244
+ if (idx >= n_vertices) break;
245
+ mesh->set_point(v, Vec3(point(0, idx), point(1, idx), point(2, idx)));
246
+ vhandles[idx] = v;
247
+ idx++;
248
+ }
249
+ }
250
+
251
+ // 设置法向量
252
+ #pragma omp parallel for
253
+ for (int i = 0; i < n_vertices; i++) {
254
+ if (use_input_normals) {
255
+ Vec3 n(point_n(0, i), point_n(1, i), point_n(2, i));
256
+ mesh->set_normal(vhandles[i], n);
257
+ } else {
258
+ Vec3 n(0.0, 0.0, 0.0); // 临时零向量,后续自动计算
259
+ mesh->set_normal(vhandles[i], n);
260
+ }
261
+ }
262
+
263
+ // 如果没有输入法向量,则自动计算
264
+ if (!use_input_normals) {
265
+ mesh->update_normals();
266
+ }
267
+
268
+ return 0;
269
+ }
270
+
271
+ void Registration::Output_data(const std::string& out_path,const std::string& method_name)
272
+ {
273
+ Scalar gt_err = SetMeshPoints(deformed_mesh_, deformed_points_3X_, deformed_normals_);
274
+ std::string out_file;
275
+ out_file = out_path + method_name+"_res.ply";
276
+ write_by_openmesh(out_file.c_str(), deformed_mesh, scale);
277
+ std::cout<< "write the result to " << out_file << "\n" << std::endl;
278
+ return;
279
+ }
cpp/core/registration.h ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef REGISTRATION_H
2
+ #define REGISTRATION_H
3
+ #include "Types.h"
4
+ #include "parameters.h"
5
+
6
+
7
+
8
+
9
+ class Registration
10
+ {
11
+ public:
12
+ Registration() = default;
13
+ virtual ~Registration() = default;
14
+
15
+
16
+ Matrix3X src_points_; // (3,n)
17
+ Matrix3X src_normals_; // (3,n)
18
+ Matrix3X src_vert_colors_; // (3,n)
19
+ Matrix3X deformed_normals_; // (3,n)
20
+ Matrix3X deformed_points_3X_;
21
+ VectorX deformed_points_; // (3n,)
22
+ Matrix3X tar_points_; // (3,n);
23
+ Matrix3X target_normals_; // (3,n)
24
+ Matrix3X tar_vert_colors; // (3,n)
25
+
26
+ double scale=1;
27
+ Mesh src_mesh;
28
+ Mesh tar_mesh;
29
+ Mesh deformed_mesh;
30
+ Mesh* src_mesh_;
31
+ Mesh* tar_mesh_;
32
+ Mesh* deformed_mesh_;
33
+ int n_src_vertex_;
34
+ int n_tar_vertex_;
35
+ int n_landmark_nodes_;
36
+ KDtree* target_tree_; // correspondence paras
37
+ spare::Parameters pars_;
38
+
39
+ std::string res_trans_path_;
40
+ MatrixXX res_trans;
41
+
42
+ struct Closest{
43
+ int src_idx; // vertex index from source model
44
+ int tar_idx; // face index from target model
45
+ Vector3 position;
46
+ Vector3 normal;
47
+ Scalar min_dist2;
48
+ };
49
+ typedef std::vector<Closest> VPairs;
50
+ VPairs correspondence_pairs_;
51
+
52
+ VectorX corres_U0_; // all correspondence points (3, n)
53
+
54
+ void InitCorrespondence(VPairs & corres);
55
+ void FindClosestPoints(VPairs & corres);
56
+ void FindClosestPoints(KDtree* target_tree_tem,VectorX & deformed_v,VPairs & corres);
57
+ void FindClosestPoints(KDtree* target_tree_tem,VPairs & corres, VectorX & deformed_v, std::vector<size_t>& sample_indices);
58
+ double FindKnearestMed(const KDtree& kdtree,
59
+ const Matrix3X& X, int nk);
60
+ void LandMarkCorres(VPairs & correspondence_pairs);
61
+ bool read_landmark(const char* filename, std::vector<int>& landmark_src, std::vector<int>& landmark_tar);
62
+ virtual void Read_data(const std::string& file_target,
63
+ const std::string& file_source) ;
64
+ virtual void Read_data(const Matrix3X &target_p,const Matrix3X &source_p,const Matrix3X &target_n,const Matrix3X &source_n) ;
65
+ virtual void Read_data(const Mesh& tar,const Mesh& src);
66
+
67
+ virtual void Register(){};
68
+
69
+
70
+
71
+ virtual void Init_data();
72
+ Scalar SetMeshPoints(Mesh* mesh, const Matrix3X &point,const Matrix3X &point_n);
73
+ virtual void Output_data(const std::string& out_path,const std::string& method_name) ;
74
+ virtual void Reg(const std::string& file_target,
75
+ const std::string& file_source,
76
+ const std::string& out_path){};
77
+ };
78
+
79
+
80
+
81
+
82
+ #endif // REGISTRATION_H
83
+
84
+
cpp/core/rigid_fricp_registration.cpp ADDED
@@ -0,0 +1,448 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include "rigid_fricp_registration.h"
2
+ #include "tools.h"
3
+ #include "robust_norm.h"
4
+ #include <median.h>
5
+ #include "io.h"
6
+
7
+ AffineMatrix3 RigidFricpRegistration::LogMatrix(const AffineMatrix3& T)
8
+ {
9
+ Eigen::RealSchur<AffineMatrix3> schur(T);
10
+ AffineMatrix3 U = schur.matrixU();
11
+ AffineMatrix3 R = schur.matrixT();
12
+ std::vector<bool> selected(3, true);
13
+ Matrix33 mat_B = Matrix33::Zero(3, 3);
14
+ Matrix33 mat_V = Matrix33::Identity(3, 3);
15
+
16
+ for (int i = 0; i < 3; i++)
17
+ {
18
+ if (selected[i] && fabs(R(i, i) - 1)> SAME_THRESHOLD)
19
+ {
20
+ int pair_second = -1;
21
+ for (int j = i + 1; j <3; j++)
22
+ {
23
+ if (fabs(R(j, j) - R(i, i)) < SAME_THRESHOLD)
24
+ {
25
+ pair_second = j;
26
+ selected[j] = false;
27
+ break;
28
+ }
29
+ }
30
+ if (pair_second > 0)
31
+ {
32
+ selected[i] = false;
33
+ R(i, i) = R(i, i) < -1 ? -1 : R(i, i);
34
+ double theta = acos(R(i, i));
35
+ if (R(i, pair_second) < 0)
36
+ {
37
+ theta = -theta;
38
+ }
39
+ mat_B(i, pair_second) += theta;
40
+ mat_B(pair_second, i) += -theta;
41
+ mat_V(i, pair_second) += -theta / 2;
42
+ mat_V(pair_second, i) += theta / 2;
43
+ double coeff = 1 - (theta * R(i, pair_second)) / (2 * (1 - R(i, i)));
44
+ mat_V(i, i) += -coeff;
45
+ mat_V(pair_second, pair_second) += -coeff;
46
+ }
47
+ }
48
+ }
49
+
50
+ AffineMatrix3 LogTrim = AffineMatrix3::Zero();
51
+ LogTrim.block(0, 0, 3, 3) = mat_B;
52
+ LogTrim.block(0, 3, 3, 1) = mat_V * R.block(0, 3, 3, 1);
53
+ AffineMatrix3 res = U * LogTrim * U.transpose();
54
+ return res;
55
+ }
56
+
57
+ Affine3d RigidFricpRegistration::point_to_point(Matrix3X& X,
58
+ Matrix3X& Y,
59
+ const VectorX& w) {
60
+ int dim = X.rows();
61
+ /// Normalize weight vector
62
+ Eigen::VectorXd w_normalized = w / w.sum();
63
+ /// De-mean
64
+ Eigen::VectorXd X_mean(dim), Y_mean(dim);
65
+ for (int i = 0; i<dim; ++i) {
66
+ X_mean(i) = (X.row(i).array()*w_normalized.transpose().array()).sum();
67
+ Y_mean(i) = (Y.row(i).array()*w_normalized.transpose().array()).sum();
68
+ }
69
+ X.colwise() -= X_mean;
70
+ Y.colwise() -= Y_mean;
71
+ /// Compute transformation
72
+ Affine3d transformation;
73
+ MatrixXX sigma = X * w_normalized.asDiagonal() * Y.transpose();
74
+ Eigen::JacobiSVD<MatrixXX> svd(sigma, Eigen::ComputeFullU | Eigen::ComputeFullV);
75
+ if (svd.matrixU().determinant()*svd.matrixV().determinant() < 0.0) {
76
+ Vector3 S = Vector3::Ones(dim); S(dim-1) = -1.0;
77
+ transformation.linear() = svd.matrixV()*S.asDiagonal()*svd.matrixU().transpose();
78
+ }
79
+ else {
80
+ transformation.linear() = svd.matrixV()*svd.matrixU().transpose();
81
+ }
82
+ transformation.translation() = Y_mean - transformation.linear()*X_mean;
83
+ /// Re-apply mean
84
+ X.colwise() += X_mean;
85
+ Y.colwise() += Y_mean;
86
+ /// Return transformation
87
+ return transformation;
88
+ }
89
+
90
+ Eigen::Affine3d RigidFricpRegistration::point_to_plane(Eigen::Matrix3Xd& X,
91
+ Eigen::Matrix3Xd& Y,
92
+ const Eigen::Matrix3Xd& Norm,
93
+ const Eigen::VectorXd& w,
94
+ const Eigen::VectorXd& u) {
95
+ /// Normalize weight vector
96
+ Eigen::VectorXd w_normalized = w / w.sum();
97
+ /// De-mean
98
+ Eigen::Vector3d X_mean;
99
+ for (int i = 0; i<3; ++i)
100
+ X_mean(i) = (X.row(i).array()*w_normalized.transpose().array()).sum();
101
+ X.colwise() -= X_mean;
102
+ Y.colwise() -= X_mean;
103
+ /// Prepare LHS and RHS
104
+ Matrix66 LHS = Matrix66::Zero();
105
+ Vector6 RHS = Vector6::Zero();
106
+ Block33 TL = LHS.topLeftCorner<3, 3>();
107
+ Block33 TR = LHS.topRightCorner<3, 3>();
108
+ Block33 BR = LHS.bottomRightCorner<3, 3>();
109
+ Eigen::MatrixXd C = Eigen::MatrixXd::Zero(3, X.cols());
110
+
111
+ #pragma omp parallel
112
+ {
113
+ #pragma omp for
114
+ for (int i = 0; i<X.cols(); i++) {
115
+ C.col(i) = X.col(i).cross(Norm.col(i));
116
+ }
117
+ #pragma omp sections nowait
118
+ {
119
+ #pragma omp section
120
+ for (int i = 0; i<X.cols(); i++) TL.selfadjointView<Eigen::Upper>().rankUpdate(C.col(i), w(i));
121
+ #pragma omp section
122
+ for (int i = 0; i<X.cols(); i++) TR += (C.col(i)*Norm.col(i).transpose())*w(i);
123
+ #pragma omp section
124
+ for (int i = 0; i<X.cols(); i++) BR.selfadjointView<Eigen::Upper>().rankUpdate(Norm.col(i), w(i));
125
+ #pragma omp section
126
+ for (int i = 0; i<C.cols(); i++) {
127
+ double dist_to_plane = -((X.col(i) - Y.col(i)).dot(Norm.col(i)) - u(i))*w(i);
128
+ RHS.head<3>() += C.col(i)*dist_to_plane;
129
+ RHS.tail<3>() += Norm.col(i)*dist_to_plane;
130
+ }
131
+ }
132
+ }
133
+ LHS = LHS.selfadjointView<Eigen::Upper>();
134
+ /// Compute transformation
135
+ Eigen::Affine3d transformation;
136
+ Eigen::LDLT<Matrix66> ldlt(LHS);
137
+ RHS = ldlt.solve(RHS);
138
+ transformation = Eigen::AngleAxisd(RHS(0), Eigen::Vector3d::UnitX()) *
139
+ Eigen::AngleAxisd(RHS(1), Eigen::Vector3d::UnitY()) *
140
+ Eigen::AngleAxisd(RHS(2), Eigen::Vector3d::UnitZ());
141
+ transformation.translation() = RHS.tail<3>();
142
+
143
+ /// Apply transformation
144
+ /// Re-apply mean
145
+ X.colwise() += X_mean;
146
+ Y.colwise() += X_mean;
147
+ transformation.translation() += X_mean - transformation.linear()*X_mean;
148
+ /// Return transformation
149
+ return transformation;
150
+ }
151
+
152
+ void RigidFricpRegistration::point_to_point(Matrix3X& X, Matrix3X& Y,Matrix3X& Z, Vector3& source_mean_,
153
+ Vector3& target_mean_, ICP::Parameters& par){
154
+ /// Build kd-tree
155
+ KDtree kdtree(Y);
156
+ /// Buffers
157
+ n_src_vertex_ = X.cols();
158
+ Matrix3X Q = Matrix3X::Zero(3, X.cols());
159
+ VectorX W = VectorX::Zero(X.cols());
160
+ deformed_points_ = VectorX::Zero(3 * n_src_vertex_);
161
+ Affine3d T;
162
+ if (par.use_init)
163
+ {
164
+ T.matrix() = par.init_trans;
165
+ }
166
+ else
167
+ {
168
+ T = Affine3d::Identity();
169
+ }
170
+ MatrixXX To1 = T.matrix();
171
+ MatrixXX To2 = T.matrix();
172
+ //Anderson Acc para
173
+ AndersonAcceleration accelerator_;
174
+ Affine3d SVD_T = T;
175
+ double energy = .0, last_energy = std::numeric_limits<double>::max();
176
+ //ground truth point clouds
177
+ Matrix3X X_gt = X;
178
+ if(par.has_groundtruth)
179
+ {
180
+ Vector3 temp_trans = par.gt_trans.col(3).head(3);
181
+ X_gt.colwise() += source_mean_;
182
+ X_gt = par.gt_trans.block(0, 0, 3, 3) * X_gt;
183
+ X_gt.colwise() += temp_trans - target_mean_;
184
+ }
185
+ //output para
186
+ std::string file_out = par.out_path;
187
+ double gt_mse = 0.0;
188
+ // dynamic welsch paras
189
+ double nu1 = 1, nu2 = 1;
190
+ Matrix3X X_deformed = T * X;
191
+ deformed_points_ = Eigen::Map<const VectorX>(X_deformed.data(), 3 * n_src_vertex_);
192
+ FindClosestPoints(target_tree_,deformed_points_,correspondence_pairs_);
193
+ #pragma omp parallel for
194
+ for (int i = 0; i<n_src_vertex_; ++i)
195
+ {
196
+ W[i] = correspondence_pairs_[i].min_dist2;
197
+ Q.col(i) = correspondence_pairs_[i].position;
198
+ }
199
+ //dynamic welsch, calc k-nearest points with itself;
200
+ nu2 = par.nu_end_k * FindKnearestMed(kdtree, Y, 7);
201
+ double med1;
202
+ Eigen::VectorXd W_sqrt = W.array().sqrt();
203
+ igl::median(W_sqrt, med1);
204
+
205
+ nu1 = par.nu_begin_k * med1;
206
+ nu1 = nu1>nu2? nu1:nu2;
207
+
208
+ //AA init
209
+ accelerator_.init(par.anderson_m, (3 + 1) * (3 + 1), LogMatrix(T.matrix()).data());
210
+
211
+ bool stop1 = false;
212
+ while(!stop1)
213
+ {
214
+ /// run ICP
215
+ int icp = 0;
216
+ for (; icp<par.max_icp; ++icp)
217
+ {
218
+ bool accept_aa = false;
219
+ energy = get_energy(W, nu1);
220
+
221
+ if (energy < last_energy)
222
+ {
223
+ last_energy = energy;
224
+ accept_aa = true;
225
+ }
226
+ else
227
+ {
228
+ accelerator_.replace(LogMatrix(SVD_T.matrix()).data());
229
+ X_deformed = SVD_T * X;
230
+ deformed_points_ = Eigen::Map<const VectorX>(X_deformed.data(), 3 * n_src_vertex_);
231
+ FindClosestPoints(target_tree_,deformed_points_,correspondence_pairs_);
232
+ #pragma omp parallel for
233
+ for (int i = 0; i<n_src_vertex_; ++i)
234
+ {
235
+ W[i] = correspondence_pairs_[i].min_dist2;
236
+ Q.col(i) = correspondence_pairs_[i].position;
237
+ }
238
+ last_energy = get_energy(W, nu1);
239
+ }
240
+
241
+
242
+
243
+ if(par.has_groundtruth)
244
+ {
245
+ gt_mse = (T*X - X_gt).squaredNorm()/n_src_vertex_;
246
+ }
247
+
248
+ robust_weight(W, nu1);
249
+ // Rotation and translation update
250
+ Eigen::VectorXd W_sqrt = W.array().sqrt();
251
+ T = point_to_point(X, Q, W_sqrt);
252
+
253
+ //Anderson Acc
254
+ SVD_T = T;
255
+
256
+ AffineMatrix3 Trans = (Eigen::Map<const AffineMatrix3>(accelerator_.compute(LogMatrix(T.matrix()).data()).data(), 3+1, 3+1)).exp();
257
+ T.linear() = Trans.block(0,0,3,3);
258
+ T.translation() = Trans.block(0,3,3,1);
259
+ X_deformed = T * X;
260
+ deformed_points_ = Eigen::Map<const VectorX>(X_deformed.data(), 3 * n_src_vertex_);
261
+ FindClosestPoints(target_tree_,deformed_points_,correspondence_pairs_);
262
+ #pragma omp parallel for
263
+ for (int i = 0; i<n_src_vertex_; ++i)
264
+ {
265
+ W[i] = correspondence_pairs_[i].min_dist2;
266
+ Q.col(i) = correspondence_pairs_[i].position;
267
+ }
268
+ /// Stopping criteria
269
+ double stop2 = (T.matrix() - To2).norm();
270
+ To2 = T.matrix();
271
+ if(stop2 < par.stop)
272
+ {
273
+ break;
274
+ }
275
+ }
276
+
277
+ stop1 = fabs(nu1 - nu2)<SAME_THRESHOLD? true: false;
278
+ nu1 = nu1*par.nu_alpha > nu2? nu1*par.nu_alpha : nu2;
279
+
280
+ accelerator_.reset(LogMatrix(T.matrix()).data());
281
+ last_energy = std::numeric_limits<double>::max();
282
+
283
+ }
284
+
285
+ ///calc convergence energy
286
+
287
+ last_energy = get_energy(W, nu1);
288
+ Z = T * X;
289
+ gt_mse = (Z-X_gt).squaredNorm()/n_src_vertex_;
290
+ T.translation() += - T.rotation() * source_mean_ + target_mean_;
291
+ Z.colwise() += target_mean_;
292
+
293
+ ///save convergence result
294
+ par.convergence_energy = last_energy;
295
+ par.convergence_gt_mse = gt_mse;
296
+ par.res_trans = T.matrix();
297
+ }
298
+
299
+ void RigidFricpRegistration::point_to_plane(Eigen::Matrix3Xd& X,
300
+ Eigen::Matrix3Xd& Y, Eigen::Matrix3Xd& norm_x, Eigen::Matrix3Xd& norm_y,
301
+ Eigen::Vector3d& source_mean_, Eigen::Vector3d& target_mean_,
302
+ ICP::Parameters &par) {
303
+ /// Build kd-tree
304
+ KDtree kdtree(Y);
305
+ /// Buffers
306
+ Eigen::Matrix3Xd Qp = Eigen::Matrix3Xd::Zero(3, X.cols());
307
+ Eigen::Matrix3Xd Qn = Eigen::Matrix3Xd::Zero(3, X.cols());
308
+ Eigen::VectorXd W = Eigen::VectorXd::Zero(X.cols());
309
+ Eigen::Matrix3Xd ori_X = X;
310
+ Affine3d T;
311
+ if (par.use_init) T.matrix() = par.init_trans;
312
+ else T = Affine3d::Identity();
313
+ AffineMatrix3 To1 = T.matrix();
314
+ X = T*X;
315
+
316
+ Eigen::Matrix3Xd X_gt = X;
317
+ if(par.has_groundtruth)
318
+ {
319
+ Eigen::Vector3d temp_trans = par.gt_trans.block(0, 3, 3, 1);
320
+ X_gt = ori_X;
321
+ X_gt.colwise() += source_mean_;
322
+ X_gt = par.gt_trans.block(0, 0, 3, 3) * X_gt;
323
+ X_gt.colwise() += temp_trans - target_mean_;
324
+ }
325
+
326
+ double gt_mse = 0.0;
327
+
328
+ //Anderson Acc para
329
+ AndersonAcceleration accelerator_;
330
+ Affine3d LG_T = T;
331
+ double energy = 0.0, prev_res = std::numeric_limits<double>::max(), res = 0.0;
332
+
333
+ // Find closest point
334
+ #pragma omp parallel for
335
+ for (int i = 0; i<X.cols(); ++i) {
336
+ int id = kdtree.closest(X.col(i).data());
337
+ Qp.col(i) = Y.col(id);
338
+ Qn.col(i) = norm_y.col(id);
339
+ W[i] = std::abs(Qn.col(i).transpose() * (X.col(i) - Qp.col(i)));
340
+ }
341
+
342
+ bool stop1 = false;
343
+ while(!stop1)
344
+ {
345
+ /// ICP
346
+ for(int icp=0; icp<par.max_icp; ++icp) {
347
+
348
+
349
+ bool accept_aa = false;
350
+ W = W.array().square();//如果后续合并逻辑,就要去掉
351
+ energy = get_energy( W, par.p);
352
+
353
+ Eigen::VectorXd test_w = (X-Qp).colwise().norm();
354
+ if(par.has_groundtruth)
355
+ {
356
+ gt_mse = (X - X_gt).squaredNorm()/X.cols();
357
+ }
358
+
359
+ /// Compute weights
360
+ W = W.array().square();
361
+ robust_weight( W, par.p);
362
+ /// Rotation and translation update
363
+ T = point_to_plane(X, Qp, Qn, W, Eigen::VectorXd::Zero(X.cols()))*T;
364
+ /// Find closest point
365
+ #pragma omp parallel for
366
+ for(int i=0; i<X.cols(); i++) {
367
+ X.col(i) = T * ori_X.col(i);
368
+ int id = kdtree.closest(X.col(i).data());
369
+ Qp.col(i) = Y.col(id);
370
+ Qn.col(i) = norm_y.col(id);
371
+ W[i] = std::abs(Qn.col(i).transpose() * (X.col(i) - Qp.col(i)));
372
+ }
373
+
374
+ /// Stopping criteria
375
+ double stop2 = (T.matrix() - To1).norm();
376
+ To1 = T.matrix();
377
+ if(stop2 < par.stop) break;
378
+ }
379
+ stop1 = true;
380
+ }
381
+
382
+ par.res_trans = T.matrix();
383
+
384
+ ///calc convergence energy
385
+ W = (Qn.array()*(X - Qp).array()).colwise().sum().abs().transpose();
386
+ W = W.array().square();
387
+ energy = get_energy(W, par.p);
388
+ gt_mse = (X - X_gt).squaredNorm() / X.cols();
389
+ T.translation().noalias() += -T.rotation()*source_mean_ + target_mean_;
390
+ X.colwise() += target_mean_;
391
+ norm_x = T.rotation()*norm_x;
392
+
393
+ ///save convergence result
394
+ par.convergence_energy = energy;
395
+ par.convergence_gt_mse = gt_mse;
396
+ par.res_trans = T.matrix();
397
+
398
+ }
399
+ void RigidFricpRegistration::Paras_init(bool useinit,std::string fileinit,int maxiter,double stop)
400
+ {
401
+ pars.use_init = useinit;//not use the initial transformation
402
+ file_init_ = fileinit;
403
+ pars.max_icp=maxiter;
404
+ pars.stop=stop;
405
+
406
+ }
407
+ void RigidFricpRegistration::use_init_transform(bool a)
408
+ {
409
+ if(a)
410
+ {
411
+ MatrixXX init_trans;
412
+ read_transMat(init_trans, file_init_);
413
+ init_trans.block(0, 3, 3, 1) /= scale;
414
+ init_trans.block(0,3,3,1) += init_trans.block(0,0,3,3)*source_mean_ - target_mean_;
415
+ pars.use_init = true;
416
+ pars.init_trans = init_trans;
417
+ //spars.init_trans = init_trans;
418
+ }
419
+
420
+
421
+ }
422
+
423
+ void RigidFricpRegistration::Register()
424
+ {
425
+ // normalization
426
+ scale = mesh_scaling(src_mesh, tar_mesh);
427
+ Init_data();
428
+ //scale=pointwise_normalize(tar_points_,src_points_,source_mean_,target_mean_);
429
+ use_init_transform(pars.use_init);
430
+ //--- Execute registration
431
+ std::cout << "begin registration..." << std::endl;
432
+ point_to_point(src_points_, tar_points_, deformed_points_3X_,source_mean_, target_mean_, pars);
433
+ res_trans = pars.res_trans;
434
+ std::cout << "Registration done!" <<std::endl;
435
+ return ;
436
+ }
437
+ void RigidFricpRegistration::Reg(const std::string& file_target,
438
+ const std::string& file_source,
439
+ const std::string& out_path)
440
+ {
441
+ Read_data(file_target, file_source);
442
+ Register();
443
+ Output_data(out_path,"FRICP");
444
+ return;
445
+ }
446
+
447
+
448
+
cpp/core/rigid_fricp_registration.h ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ #ifndef FRICP_H
3
+ #define FRICP_H
4
+
5
+ #include <AndersonAcceleration.h>
6
+ #define TUPLE_SCALE 0.95
7
+ #define TUPLE_MAX_CNT 1000
8
+ #include "registration.h"
9
+
10
+
11
+
12
+
13
+
14
+
15
+ class RigidFricpRegistration:public Registration
16
+ {
17
+ public:
18
+ RigidFricpRegistration(){};
19
+ ~RigidFricpRegistration(){};
20
+ std::string file_init_ ;
21
+ VectorN source_mean_, target_mean_;
22
+ double time;
23
+ ICP::Parameters pars;
24
+ void use_init_transform(bool a);
25
+ AffineMatrix3 LogMatrix(const AffineMatrix3& T);
26
+ Affine3d point_to_point(Matrix3X& X,
27
+ Matrix3X& Y,
28
+ const VectorX& w) ;
29
+ Eigen::Affine3d point_to_plane(Eigen::Matrix3Xd& X,
30
+ Eigen::Matrix3Xd& Y,
31
+ const Eigen::Matrix3Xd& Norm,
32
+ const Eigen::VectorXd& w,
33
+ const Eigen::VectorXd& u) ;
34
+ void point_to_point(Matrix3X& X, Matrix3X& Y,Matrix3X& Z, Vector3& source_mean_,
35
+ Vector3& target_mean_, ICP::Parameters& par);
36
+ void point_to_plane(Eigen::Matrix3Xd& X,
37
+ Eigen::Matrix3Xd& Y, Eigen::Matrix3Xd& norm_x, Eigen::Matrix3Xd& norm_y,
38
+ Eigen::Vector3d& source_mean_, Eigen::Vector3d& target_mean_,
39
+ ICP::Parameters &par) ;
40
+ void Paras_init(bool useinit =false,std::string fileinit=" ",int maxiter=100,double stop=1e-5);
41
+ void Register()override;
42
+ void Reg(const std::string& file_target,
43
+ const std::string& file_source,
44
+ const std::string& out_path)override;
45
+ };
46
+
47
+
48
+ #endif
cpp/io/io.h ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef IO_H
2
+ #define IO_H
3
+ #include <cstdio>
4
+ #include "pch.h"
5
+ #include "Types.h"
6
+
7
+
8
+ inline bool read_by_openmesh(const std::string filename, Mesh& mesh)
9
+ {
10
+ OpenMesh::IO::Options opt_read = OpenMesh::IO::Options::VertexNormal;
11
+ mesh.request_vertex_normals();
12
+ bool read_OK = OpenMesh::IO::read_mesh(mesh, filename,opt_read);
13
+
14
+ std::cout << "filename = " << filename << std::endl;
15
+ if (read_OK)
16
+ {
17
+ mesh.request_vertex_status();
18
+ mesh.request_edge_status();
19
+ mesh.request_face_status();
20
+
21
+ mesh.request_face_normals();
22
+
23
+ mesh.update_face_normals();
24
+ if(mesh.n_faces()>0)
25
+ mesh.update_vertex_normals();
26
+
27
+ Vec3 MeshScales;
28
+ MeshScales[0] = 0.0; MeshScales[1] = 0.0; MeshScales[2] = 0.0;
29
+ for (Mesh::VertexIter v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); ++v_it)
30
+ {
31
+ MeshScales += mesh.point(*v_it);
32
+ }
33
+ MeshScales /= mesh.n_vertices();
34
+ return true;
35
+ }
36
+ std::cout << "#vertices = " << mesh.n_vertices() << std::endl;
37
+ return false;
38
+ }
39
+
40
+ //---openmesh for output
41
+ inline bool write_by_openmesh(const char* filename, Mesh& mesh, Scalar scale)
42
+ {
43
+ for (Mesh::VertexIter v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); ++v_it)
44
+ {
45
+
46
+ mesh.point(*v_it) = mesh.point(*v_it)*scale;
47
+ }
48
+ OpenMesh::IO::Options opt_write = OpenMesh::IO::Options::VertexNormal;
49
+ bool ok = OpenMesh::IO::write_mesh(mesh, filename,opt_write);
50
+ return ok;
51
+ }
52
+
53
+ // If it exists, read the initial rigid transformation matrix.
54
+ template <class MatrixType>
55
+ bool read_transMat(MatrixType& trans, const std::string& filename)
56
+ {
57
+ std::ifstream input(filename);
58
+ std::string line;
59
+ int rows, cols;
60
+ std::vector<std::vector<double>> total_data;
61
+ while (getline(input, line)) {
62
+ if(line[0] == 'V' || line[0] == 'M')
63
+ continue;
64
+ std::istringstream iss(line);
65
+ std::vector<double> lineVec;
66
+ while (iss) {
67
+ double item;
68
+ if (iss >> item)
69
+ lineVec.push_back(item);
70
+ }
71
+ cols = lineVec.size();
72
+ total_data.push_back(lineVec);
73
+ }
74
+ if (total_data.size() == 0)
75
+ {
76
+ std::cout << filename << " is empty !! " << std::endl;
77
+ return false;
78
+ }
79
+ rows = total_data.size();
80
+ trans.resize(rows, cols);
81
+ std::cout << "rows = " << rows << " cols = " << cols << std::endl;
82
+ for (int i = 0; i < rows; i++)
83
+ {
84
+ for (int j = 0; j < cols; j++)
85
+ {
86
+ trans(i, j) = total_data[i][j];
87
+ }
88
+ }
89
+ input.close();
90
+ std::cout << "read trans = \n" << trans << std::endl;
91
+ return true;
92
+ }
93
+
94
+ #endif // IO_H
cpp/pch.h ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+ #include <Eigen/Dense>
3
+ #include <vector>
4
+ #include <utility>
5
+ #include <iostream>
6
+ #include <string>
7
+ #include <nanoflann.hpp>
8
+ #include "nanoflann.h"
9
+ #include <unsupported/Eigen/MatrixFunctions>
10
+ #include <limits>
11
+ #include <type_traits>
12
+ #include <time.h>
13
+ #include <fstream>
14
+ #include <algorithm>
15
+ #include <Eigen/Sparse>
16
+ #include <OpenMesh/Core/Geometry/VectorT.hh>
17
+ #include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
18
+ #include <OpenMesh/Core/IO/MeshIO.hh>
19
+ #include <cmath>
20
+ #include "Types.h"
21
+ #include "OmpHelper.h"
cpp/tools/AndersonAcceleration.h ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef ANDERSONACCELERATION_H_
2
+ #define ANDERSONACCELERATION_H_
3
+
4
+ #include "Types.h"
5
+ #include <cassert>
6
+ #include <algorithm>
7
+ #include <vector>
8
+ #include <omp.h>
9
+ #include <fstream>
10
+
11
+ class AndersonAcceleration
12
+ {
13
+ public:
14
+ AndersonAcceleration()
15
+ :m_(-1), dim_(-1), iter_(-1), col_idx_(-1) {}
16
+
17
+ void replace(const Scalar *u)
18
+ {
19
+ current_u_ = Eigen::Map<const VectorX>(u, dim_);
20
+ }
21
+
22
+ const VectorX& compute(const Scalar* g)
23
+ {
24
+ assert(iter_ >= 0);
25
+
26
+ Eigen::Map<const VectorX> G(g, dim_);
27
+ current_F_ = G - current_u_;
28
+
29
+ if (iter_ == 0)
30
+ {
31
+ prev_dF_.col(0) = -current_F_;
32
+ prev_dG_.col(0) = -G;
33
+ current_u_ = G;
34
+ }
35
+ else
36
+ {
37
+ prev_dF_.col(col_idx_) += current_F_;
38
+ prev_dG_.col(col_idx_) += G;
39
+
40
+ Scalar eps = 1e-14;
41
+ Scalar scale = std::max(eps, prev_dF_.col(col_idx_).norm());
42
+ dF_scale_(col_idx_) = scale;
43
+ prev_dF_.col(col_idx_) /= scale;
44
+
45
+ int m_k = std::min(m_, iter_);
46
+
47
+
48
+ if (m_k == 1)
49
+ {
50
+ theta_(0) = 0;
51
+ Scalar dF_sqrnorm = prev_dF_.col(col_idx_).squaredNorm();
52
+ M_(0, 0) = dF_sqrnorm;
53
+ Scalar dF_norm = std::sqrt(dF_sqrnorm);
54
+
55
+ if (dF_norm > eps) {
56
+ theta_(0) = (prev_dF_.col(col_idx_) / dF_norm).dot(current_F_ / dF_norm);
57
+ }
58
+ }
59
+ else
60
+ {
61
+ // Update the normal equation matrix, for the column and row corresponding to the new dF column
62
+ VectorX new_inner_prod = (prev_dF_.col(col_idx_).transpose() * prev_dF_.block(0, 0, dim_, m_k)).transpose();
63
+ M_.block(col_idx_, 0, 1, m_k) = new_inner_prod.transpose();
64
+ M_.block(0, col_idx_, m_k, 1) = new_inner_prod;
65
+
66
+ // Solve normal equation
67
+ cod_.compute(M_.block(0, 0, m_k, m_k));
68
+ theta_.head(m_k) = cod_.solve(prev_dF_.block(0, 0, dim_, m_k).transpose() * current_F_);
69
+ }
70
+
71
+ // Use rescaled theata to compute new u
72
+ current_u_ = G - prev_dG_.block(0, 0, dim_, m_k) * ((theta_.head(m_k).array() / dF_scale_.head(m_k).array()).matrix());
73
+ col_idx_ = (col_idx_ + 1) % m_;
74
+ prev_dF_.col(col_idx_) = -current_F_;
75
+ prev_dG_.col(col_idx_) = -G;
76
+ }
77
+
78
+ iter_++;
79
+ return current_u_;
80
+ }
81
+ void reset(const Scalar *u)
82
+ {
83
+ iter_ = 0;
84
+ col_idx_ = 0;
85
+ current_u_ = Eigen::Map<const VectorX>(u, dim_);
86
+ }
87
+
88
+ // m: number of previous iterations used
89
+ // d: dimension of variables
90
+ // u0: initial variable values
91
+ void init(int m, int d, const Scalar* u0)
92
+ {
93
+ assert(m > 0);
94
+ m_ = m;
95
+ dim_ = d;
96
+ current_u_.resize(d);
97
+ current_F_.resize(d);
98
+ prev_dG_.resize(d, m);
99
+ prev_dF_.resize(d, m);
100
+ M_.resize(m, m);
101
+ theta_.resize(m);
102
+ dF_scale_.resize(m);
103
+ current_u_ = Eigen::Map<const VectorX>(u0, d);
104
+ iter_ = 0;
105
+ col_idx_ = 0;
106
+ }
107
+
108
+ private:
109
+ VectorX current_u_;
110
+ VectorX current_F_;
111
+ MatrixXX prev_dG_;
112
+ MatrixXX prev_dF_;
113
+ MatrixXX M_; // Normal equations matrix for the computing theta
114
+ VectorX theta_; // theta value computed from normal equations
115
+ VectorX dF_scale_; // The scaling factor for each column of prev_dF
116
+ Eigen::CompleteOrthogonalDecomposition<MatrixXX> cod_;
117
+
118
+ int m_; // Number of previous iterates used for Andreson Acceleration
119
+ int dim_; // Dimension of variables
120
+ int iter_; // Iteration count since initialization
121
+ int col_idx_; // Index for history matrix column to store the next value
122
+ int m_k_;
123
+
124
+ Eigen::Matrix4d current_T_;
125
+ Eigen::Matrix4d current_F_T_;
126
+
127
+ MatrixXX T_prev_dF_;
128
+ MatrixXX T_prev_dG_;
129
+ };
130
+
131
+
132
+ #endif /* ANDERSONACCELERATION_H_ */
cpp/tools/OmpHelper.h ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef OMPHELPER_H_
2
+ #define OMPHELPER_H_
3
+
4
+ #define USE_OPENMP
5
+
6
+ #ifdef USE_OPENMP
7
+ #include <omp.h>
8
+ #ifdef USE_MSVC
9
+ #define OMP_PARALLEL __pragma(omp parallel)
10
+ #define OMP_FOR __pragma(omp for)
11
+ #define OMP_SINGLE __pragma(omp single)
12
+ #define OMP_SECTIONS __pragma(omp sections)
13
+ #define OMP_SECTION __pragma(omp section)
14
+ #else
15
+ #define OMP_PARALLEL _Pragma("omp parallel")
16
+ #define OMP_FOR _Pragma("omp for")
17
+ #define OMP_SINGLE _Pragma("omp single")
18
+ #define OMP_SECTIONS _Pragma("omp sections")
19
+ #define OMP_SECTION _Pragma("omp section")
20
+ #endif
21
+ #else
22
+ #include <ctime>
23
+ #define OMP_PARALLEL
24
+ #define OMP_FOR
25
+ #define OMP_SINGLE
26
+ #define OMP_SECTIONS
27
+ #define OMP_SECTION
28
+ #endif
29
+
30
+ #include <cassert>
31
+ #include <vector>
32
+
33
+
34
+
35
+ class Timer
36
+ {
37
+ public:
38
+
39
+ typedef int EventID;
40
+
41
+ EventID get_time()
42
+ {
43
+ EventID id = time_values_.size();
44
+
45
+ #ifdef USE_OPENMP
46
+ time_values_.push_back(omp_get_wtime());
47
+ #else
48
+ time_values_.push_back(clock());
49
+ #endif
50
+
51
+ return id;
52
+ }
53
+
54
+ double elapsed_time(EventID event1, EventID event2)
55
+ {
56
+ assert(event1 >= 0 && event1 < static_cast<EventID>(time_values_.size()));
57
+ assert(event2 >= 0 && event2 < static_cast<EventID>(time_values_.size()));
58
+
59
+ #ifdef USE_OPENMP
60
+ return time_values_[event2] - time_values_[event1];
61
+ #else
62
+ return double(time_values_[event2] - time_values_[event1]) / CLOCKS_PER_SEC;
63
+ #endif
64
+ }
65
+
66
+ void reset()
67
+ {
68
+ time_values_.clear();
69
+ }
70
+
71
+ private:
72
+ #ifdef USE_OPENMP
73
+ std::vector<double> time_values_;
74
+ #else
75
+ std::vector<clock_t> time_values_;
76
+ #endif
77
+ };
78
+
79
+ #endif /* OMPHELPER_H_ */
cpp/tools/Types.h ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef TYPES_H
2
+ #define TYPES_H
3
+
4
+ #include <Eigen/Dense>
5
+ #include <Eigen/Sparse>
6
+ #include <OpenMesh/Core/Geometry/VectorT.hh>
7
+ #include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
8
+ #include <OpenMesh/Core/IO/MeshIO.hh>
9
+
10
+ #include <iostream>
11
+ #include <fstream>
12
+ #include <iomanip>
13
+
14
+ #include <stdlib.h>
15
+ #include <stdio.h>
16
+ #include <cstdio>
17
+
18
+ #include <algorithm>
19
+ #include <math.h>
20
+ #include <cmath>
21
+
22
+ #include <vector>
23
+ #include <list>
24
+ #include <set>
25
+ #include <queue>
26
+ #include <map>
27
+ #include <string>
28
+
29
+ #include <time.h>
30
+ #include <assert.h>
31
+ #include <cstddef>
32
+ #include <limits>
33
+
34
+ //#define USE_FLOAT_SCALAR
35
+
36
+ #define SAME_THRESHOLD 1e-6
37
+
38
+
39
+ #ifdef USE_FLOAT_SCALAR
40
+ typedef float Scalar;
41
+ #else
42
+ typedef double Scalar;
43
+ #endif
44
+
45
+ typedef Eigen::SparseMatrix<Scalar, Eigen::ColMajor> ColMajorSparseMatrix;
46
+ typedef Eigen::SparseMatrix<Scalar, Eigen::RowMajor> RowMajorSparseMatrix;
47
+
48
+
49
+ #ifdef EIGEN_DONT_ALIGN
50
+ #define EIGEN_ALIGNMENT Eigen::DontAlign
51
+ #else
52
+ #define EIGEN_ALIGNMENT Eigen::AutoAlign
53
+ #endif
54
+
55
+
56
+ typedef Eigen::Triplet<Scalar> Triplet;
57
+
58
+ // ======================================================
59
+ // 基础模板别名
60
+ // ======================================================
61
+ template <int Rows, int Cols, int Options = (Eigen::ColMajor | Eigen::AutoAlign)>
62
+ using MatrixT = Eigen::Matrix<Scalar, Rows, Cols, Options>;
63
+
64
+ // ======================
65
+ // 向量类型
66
+ // ======================
67
+ using Vector2 = MatrixT<2,1>;
68
+ using Vector3 = MatrixT<3,1>;
69
+ using Vector4 = MatrixT<4,1>;
70
+ using Vector6 = MatrixT<6,1>;
71
+ using VectorX = MatrixT<Eigen::Dynamic,1>;
72
+ using VectorN = Vector3; // 别名(等价于 Vector3)
73
+
74
+ // ======================
75
+ // 固定尺寸矩阵
76
+ // ======================
77
+ using Matrix22 = MatrixT<2,2>;
78
+ using Matrix23 = MatrixT<2,3>;
79
+ using Matrix32 = MatrixT<3,2>;
80
+ using Matrix33 = MatrixT<3,3>;
81
+ using Matrix34 = MatrixT<3,4>;
82
+ using Matrix44 = MatrixT<4,4>;
83
+ using Matrix66 = MatrixT<6,6>;
84
+ using EigenMatrix12 = MatrixT<12,12>;
85
+
86
+ // ======================
87
+ // 动态尺寸矩阵
88
+ // ======================
89
+ using Matrix2X = MatrixT<2,Eigen::Dynamic>;
90
+ using Matrix3X = MatrixT<3,Eigen::Dynamic>;
91
+ using Matrix4X = MatrixT<4,Eigen::Dynamic>;
92
+ using MatrixX2 = MatrixT<Eigen::Dynamic,2>;
93
+ using MatrixX3 = MatrixT<Eigen::Dynamic,3>;
94
+ using MatrixXX = MatrixT<Eigen::Dynamic,Eigen::Dynamic>;
95
+ using Vertices = Matrix3X; // 顶点矩阵,3×N
96
+
97
+ // ======================
98
+ // 特殊变换与几何类型
99
+ // ======================
100
+ using Affine3 = Eigen::Transform<Scalar,3,Eigen::Affine>;
101
+ using Affine3d = Affine3; // 同义,方便兼容
102
+ using AffineMatrix3 = Matrix44; // 4×4 变换矩阵
103
+
104
+ using EigenAngleAxis = Eigen::AngleAxis<Scalar>;
105
+ using EigenQuaternion = Eigen::Quaternion<Scalar, Eigen::DontAlign>;
106
+
107
+ // ======================
108
+ // 块类型
109
+ // ======================
110
+ using Block33 = Eigen::Block<Matrix66,3,3>;
111
+
112
+ // Conversion between a 3d vector type to Eigen::Vector3d
113
+ template<typename Vec_T>
114
+ inline Vector3 to_eigen_vec3(const Vec_T &vec)
115
+ {
116
+ return Vector3(vec[0], vec[1], vec[2]);
117
+ }
118
+
119
+
120
+ template<typename Vec_T>
121
+ inline Vec_T from_eigen_vec3(const Vector3 &vec)
122
+ {
123
+ Vec_T v;
124
+ v[0] = vec(0);
125
+ v[1] = vec(1);
126
+ v[2] = vec(2);
127
+
128
+ return v;
129
+ }
130
+
131
+ enum VertexState
132
+ {
133
+ OUTSIDE,
134
+ FRONT,
135
+ INSIDE
136
+ };
137
+
138
+ struct TriTraits : public OpenMesh::DefaultTraits {
139
+ #ifdef USE_FLOAT_SCALAR
140
+ typedef OpenMesh::Vec3f Point;
141
+ typedef OpenMesh::Vec3f Normal;
142
+ #else
143
+ typedef OpenMesh::Vec3d Point;
144
+ typedef OpenMesh::Vec3d Normal;
145
+ #endif
146
+ typedef OpenMesh::Vec4f Color;
147
+
148
+
149
+
150
+ VertexAttributes(OpenMesh::Attributes::Status);
151
+ FaceAttributes(OpenMesh::Attributes::Status);
152
+ EdgeAttributes(OpenMesh::Attributes::Status);
153
+ HalfedgeAttributes(OpenMesh::Attributes::Status);
154
+
155
+ VertexTraits
156
+ {
157
+ VertexT() : geodesic_distance(1e100), state(OUTSIDE),incident_point(0.0),
158
+ saddle_or_boundary(false)
159
+ {};
160
+ public:
161
+ Scalar geodesic_distance;
162
+ VertexState state;
163
+ OpenMesh::FaceHandle incident_face;
164
+ Scalar incident_point;
165
+ bool saddle_or_boundary;
166
+ };
167
+ FaceTraits
168
+ {
169
+ FaceT() : corner_angles(0,0,0)
170
+ {};
171
+ public:
172
+ Vector3 corner_angles;
173
+ };
174
+ EdgeTraits
175
+ {
176
+ EdgeT() : length(0.0)
177
+ {};
178
+ public:
179
+ Scalar length;
180
+ };
181
+ };
182
+ typedef OpenMesh::TriMesh_ArrayKernelT<TriTraits> Mesh;
183
+ #ifdef USE_FLOAT_SCALAR
184
+ typedef OpenMesh::Vec3f Vec3;
185
+ #else
186
+ typedef OpenMesh::Vec3d Vec3;
187
+ #endif
188
+
189
+
190
+ class Matrix3333 // 3x3 matrix: each element is a 3x3 matrix
191
+ {
192
+ public:
193
+ Matrix3333();
194
+ Matrix3333(const Matrix3333& other);
195
+ ~Matrix3333() {}
196
+
197
+ void SetZero(); // [0 0 0; 0 0 0; 0 0 0]; 0 = 3x3 zeros
198
+ void SetIdentity(); //[I 0 0; 0 I 0; 0 0 I]; 0 = 3x3 zeros, I = 3x3 identity
199
+
200
+ // operators
201
+ Matrix33& operator() (int row, int col);
202
+ Matrix3333 operator+ (const Matrix3333& plus);
203
+ Matrix3333 operator- (const Matrix3333& minus);
204
+ Matrix3333 operator* (const Matrix33& multi);
205
+ friend Matrix3333 operator* (const Matrix33& multi1, Matrix3333& multi2);
206
+ Matrix3333 operator* (Scalar multi);
207
+ friend Matrix3333 operator* (Scalar multi1, Matrix3333& multi2);
208
+ Matrix3333 transpose();
209
+ Matrix33 Contract(const Matrix33& multi); // this operator is commutative
210
+ Matrix3333 Contract(Matrix3333& multi);
211
+
212
+ //protected:
213
+
214
+ Matrix33 mat[3][3];
215
+ };
216
+
217
+ class Matrix2222 // 2x2 matrix: each element is a 2x2 matrix
218
+ {
219
+ public:
220
+ Matrix2222();
221
+ Matrix2222(const Matrix2222& other);
222
+ ~Matrix2222() {}
223
+
224
+ void SetZero(); // [0 0; 0 0]; 0 = 2x2 zeros
225
+ void SetIdentity(); //[I 0; 0 I;]; 0 = 2x2 zeros, I = 2x2 identity
226
+
227
+ // operators and basic functions
228
+ Matrix22& operator() (int row, int col);
229
+ Matrix2222 operator+ (const Matrix2222& plus);
230
+ Matrix2222 operator- (const Matrix2222& minus);
231
+ Matrix2222 operator* (const Matrix22& multi);
232
+ friend Matrix2222 operator* (const Matrix22& multi1, Matrix2222& multi2);
233
+ Matrix2222 operator* (Scalar multi);
234
+ friend Matrix2222 operator* (Scalar multi1, Matrix2222& multi2);
235
+ Matrix2222 transpose();
236
+ Matrix22 Contract(const Matrix22& multi); // this operator is commutative
237
+ Matrix2222 Contract(Matrix2222& multi);
238
+
239
+ protected:
240
+
241
+ Matrix22 mat[2][2];
242
+ };
243
+
244
+ // dst = src1 \kron src2
245
+ void directProduct(Matrix3333& dst, const Matrix33& src1, const Matrix33& src2);
246
+ void directProduct(Matrix2222& dst, const Matrix22& src1, const Matrix22& src2);
247
+ #endif // TYPES_H
248
+ ///////////////////////////////////////////////////////////////////////////////
cpp/tools/geodesic/geodesic_algorithm_base.h ADDED
@@ -0,0 +1,417 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef GEODESIC_ALGORITHM_BASE
2
+ #define GEODESIC_ALGORITHM_BASE
3
+
4
+ #include "geodesic_algorithm_exact_elements.h"
5
+ #include "geodesic_constants_and_simple_functions.h"
6
+ #define hfid0 0
7
+ #define hfid1 1
8
+
9
+ namespace geodesic{
10
+ class GeodesicAlgorithmBase
11
+ {
12
+ public:
13
+ vertex_pointer opposite_vertex(edge_pointer e, vertex_pointer v)
14
+ {
15
+ halfedge_handle hf0 = this->mesh()->halfedge_handle(e, hfid0);
16
+ if(this->mesh()->from_vertex_handle(hf0).idx()==v.idx())
17
+ return this->mesh()->to_vertex_handle(hf0);
18
+ else
19
+ return this->mesh()->from_vertex_handle(hf0);
20
+ };
21
+
22
+ vertex_pointer opposite_vertex(face_pointer f, edge_pointer e)
23
+ {
24
+ halfedge_handle hf = this->mesh()->halfedge_handle(e, hfid0);
25
+ hf = this->mesh()->face_handle(hf)==f? hf : this->mesh()->opposite_halfedge_handle(hf);
26
+ return this->mesh()->to_vertex_handle(this->mesh()->next_halfedge_handle(hf));
27
+ };
28
+
29
+ bool belongs_v(edge_pointer e, vertex_pointer v)
30
+ {
31
+ halfedge_handle hf = this->mesh()->halfedge_handle(e, hfid0);
32
+ return this->mesh()->from_vertex_handle(hf) == v ||
33
+ this->mesh()->to_vertex_handle(hf) == v;
34
+ }
35
+
36
+ edge_pointer next_edge(face_pointer f, edge_pointer e, vertex_pointer v)
37
+ {
38
+
39
+ halfedge_handle hf = this->mesh()->halfedge_handle(e, hfid0);
40
+ hf = this->mesh()->face_handle(hf)==f? hf : this->mesh()->opposite_halfedge_handle(hf);
41
+ halfedge_handle next_hf;
42
+ if(this->mesh()->from_vertex_handle(hf)==v)
43
+ {
44
+ next_hf = this->mesh()->next_halfedge_handle(this->mesh()->next_halfedge_handle(hf));
45
+ }
46
+ else if(this->mesh()->to_vertex_handle(hf)==v)
47
+ next_hf = this->mesh()->next_halfedge_handle(hf);
48
+ else
49
+ {
50
+ std::cout << "next_edge e and v has no connection" << std::endl;
51
+ exit(1);
52
+ }
53
+ return this->mesh()->edge_handle(next_hf);
54
+ };
55
+
56
+ Scalar vertex_angle(face_pointer f, vertex_pointer v)
57
+ {
58
+ halfedge_handle hf0 = this->mesh()->halfedge_handle(f);
59
+ vertex_pointer v0 = this->mesh()->from_vertex_handle(hf0);
60
+ vertex_pointer v1 = this->mesh()->to_vertex_handle(hf0);
61
+ vertex_pointer v2 = this->mesh()->to_vertex_handle(this->mesh()->next_halfedge_handle(hf0));
62
+ Scalar l1 = (this->mesh()->point(v0) - this->mesh()->point(v1)).norm();
63
+ Scalar l2 = (this->mesh()->point(v1) - this->mesh()->point(v2)).norm();
64
+ Scalar l3 = (this->mesh()->point(v2) - this->mesh()->point(v0)).norm();
65
+ if(v0.idx()==v.idx())
66
+ {
67
+ return angle_from_edges(l2, l3, l1);
68
+ }
69
+ if(v1.idx()==v.idx())
70
+ {
71
+ return angle_from_edges(l3, l1, l2);
72
+ }
73
+ if(v2.idx()==v.idx())
74
+ {
75
+ return angle_from_edges(l1, l2, l3);
76
+ }
77
+ };
78
+
79
+ void update_edgelen()
80
+ {
81
+ for(auto eit = this->mesh()->edges_begin(); eit != this->mesh()->edges_end(); eit++)
82
+ {
83
+ halfedge_handle hf = this->mesh()->halfedge_handle(*eit, hfid0);
84
+ Vec3 v1 = this->mesh()->point(this->mesh()->from_vertex_handle(hf));
85
+ Vec3 v2 = this->mesh()->point(this->mesh()->to_vertex_handle(hf));
86
+ this->mesh()->data(*eit).length = (v1-v2).norm();
87
+ }
88
+ };
89
+
90
+ void build_adjacencies();
91
+
92
+ edge_pointer opposite_edge(face_pointer f, vertex_pointer v)
93
+ {
94
+ for(auto e_it = this->mesh()->fe_begin(f); e_it != this->mesh()->fe_end(f); ++e_it)
95
+ {
96
+ edge_pointer e = *e_it;
97
+ if(!belongs_v(e, v))
98
+ {
99
+ return e;
100
+ }
101
+ }
102
+ };
103
+
104
+ face_pointer opposite_face(edge_pointer e, face_pointer f)
105
+ {
106
+ halfedge_handle hf = this->mesh()->halfedge_handle(e, hfid0);
107
+ return this->mesh()->face_handle(hf) == f?
108
+ this->mesh()->face_handle(this->mesh()->opposite_halfedge_handle(hf))
109
+ :this->mesh()->face_handle(hf);
110
+ };
111
+
112
+ void initialize(Mesh* mesh);
113
+
114
+ GeodesicAlgorithmBase(Mesh* mesh)
115
+ {
116
+ initialize(mesh);
117
+ };
118
+
119
+ GeodesicAlgorithmBase(){m_mesh = NULL;};
120
+
121
+ virtual ~GeodesicAlgorithmBase(){};
122
+
123
+ virtual void print_statistics() //print info about timing and memory usage in the propagation step of the algorithm
124
+ {
125
+ std::cout << "propagation step took " << m_time_consumed << " seconds " << std::endl;
126
+ };
127
+
128
+ Mesh* mesh(){return m_mesh;};
129
+
130
+ // propagate a window
131
+ bool compute_propagated_parameters(Scalar pseudo_x,
132
+ Scalar pseudo_y,
133
+ Scalar start,
134
+ Scalar end, //start/end of the interval
135
+ Scalar alpha, //corner angle
136
+ Scalar L, //length of the new edge
137
+ interval_pointer candidates,
138
+ Scalar d); //if it is the last interval on the edge
139
+
140
+ // intersection point on an edge
141
+ Scalar compute_positive_intersection(Scalar start,
142
+ Scalar pseudo_x,
143
+ Scalar pseudo_y,
144
+ Scalar sin_alpha,
145
+ Scalar cos_alpha);
146
+
147
+ inline bool calculate_triangle_parameters(list_pointer &list, Triangle &Tri); // calculate the parameters of the triangle to be propagated
148
+
149
+ list_pointer interval_list_0(edge_pointer e)
150
+ {
151
+ return &m_edge_interval_lists_0[e.idx()];
152
+ };
153
+
154
+ list_pointer interval_list_1(edge_pointer e)
155
+ {
156
+ return &m_edge_interval_lists_1[e.idx()];
157
+ };
158
+
159
+
160
+ protected:
161
+
162
+ Mesh* m_mesh;
163
+
164
+ Triangle Tri; // the triangle to be propagated
165
+
166
+ IntervalList wl_left, wl_right;
167
+
168
+ std::vector<IntervalList> m_edge_interval_lists_0; // windows propagated from adjacent_face[0] of the edge
169
+ std::vector<IntervalList> m_edge_interval_lists_1; // windows propagated from adjacent_face[1] of the edge
170
+
171
+ };
172
+
173
+ inline void GeodesicAlgorithmBase::initialize(Mesh* mesh)
174
+ {
175
+ m_mesh = mesh;
176
+ m_edge_interval_lists_0.resize(mesh->n_edges());
177
+ m_edge_interval_lists_1.resize(mesh->n_edges());
178
+
179
+ // initialize statistics
180
+ m_queue_max_size = 0;
181
+ m_windows_propagation = 0;
182
+ m_windows_wavefront = 0;
183
+ m_windows_peak = 0;
184
+
185
+ // initialize window lists, similar to half-edge structure
186
+ for (unsigned i = 0; i < m_edge_interval_lists_0.size(); ++i)
187
+ {
188
+ edge_pointer edge = mesh->edge_handle(i);
189
+ m_edge_interval_lists_0[i].initialize(edge);
190
+ m_edge_interval_lists_1[i].initialize(edge);
191
+ interval_list_0(edge)->start_vertex() = mesh->from_vertex_handle(mesh->halfedge_handle(edge, hfid0));
192
+ interval_list_1(edge)->start_vertex() = mesh->from_vertex_handle(mesh->halfedge_handle(edge, hfid1));
193
+ }
194
+ // verify list links
195
+ for (unsigned i = 0; i < mesh->n_faces(); ++i)
196
+ {
197
+ face_pointer f = (mesh->face_handle(i));
198
+ vertex_pointer v[3];
199
+ size_t j = 0;
200
+ for (auto e_it = mesh->fe_begin(f); e_it != mesh->fe_end(f); ++e_it)
201
+ {
202
+ edge_pointer e = *e_it;
203
+ if (mesh->face_handle(mesh->halfedge_handle(e, hfid0)) == f)
204
+ v[j] = interval_list_0(e)->start_vertex();
205
+ else
206
+ v[j] = interval_list_1(e)->start_vertex();
207
+
208
+ if ((interval_list_0(e)->start_vertex().idx() < 0) || (interval_list_1(e)->start_vertex().idx() < 0))
209
+ {
210
+ std::cout << "list link error" << std::endl;
211
+ exit(1);
212
+ }
213
+
214
+ if (interval_list_0(e)->start_vertex() == interval_list_1(e)->start_vertex())
215
+ {
216
+ std::cout << "list link error" << std::endl;
217
+ exit(1);
218
+ }
219
+
220
+ if (!((belongs_v(e, interval_list_0(e)->start_vertex())) &&
221
+ (belongs_v(e, interval_list_1(e)->start_vertex()))))
222
+ {
223
+ std::cout << "list link error" << std::endl;
224
+ exit(1);
225
+ }
226
+ j++;
227
+ }
228
+ if ((v[0].idx() >-1 && v[0] == v[1]) || (v[0].idx() >-1 && v[0] == v[2]) || (v[1].idx() >-1 && v[1] == v[2]))
229
+ {
230
+ std::cout << "list link error" << std::endl;
231
+ exit(1);
232
+ }
233
+ }
234
+ update_edgelen();
235
+ build_adjacencies();
236
+ };
237
+
238
+ inline Scalar GeodesicAlgorithmBase::compute_positive_intersection(Scalar start,
239
+ Scalar pseudo_x,
240
+ Scalar pseudo_y,
241
+ Scalar sin_alpha,
242
+ Scalar cos_alpha)
243
+ {
244
+ //assert(pseudo_y < 0);
245
+ assert(pseudo_y <= 0);
246
+
247
+ Scalar denominator = sin_alpha*(pseudo_x - start) - cos_alpha*pseudo_y;
248
+ if (denominator < 0.0)
249
+ {
250
+ return -1.0;
251
+ }
252
+
253
+ Scalar numerator = -pseudo_y*start;
254
+
255
+ if (numerator < 1e-30)
256
+ {
257
+ return 0.0;
258
+ }
259
+
260
+ if (denominator < 1e-30)
261
+ {
262
+ return -1.0;
263
+ }
264
+
265
+ return numerator / denominator;
266
+ }
267
+
268
+ inline bool GeodesicAlgorithmBase::compute_propagated_parameters(Scalar pseudo_x,
269
+ Scalar pseudo_y,
270
+ Scalar begin,
271
+ Scalar end, //start/end of the interval
272
+ Scalar alpha, //corner angle
273
+ Scalar L, //length of the new edge
274
+ interval_pointer candidates,
275
+ Scalar d)
276
+ {
277
+ assert(pseudo_y <= 0.0);
278
+ assert(begin <= end);
279
+ assert(begin >= 0);
280
+
281
+ ++m_windows_propagation; // Statistics
282
+
283
+ interval_pointer p = candidates;
284
+
285
+ Scalar sin_alpha = sin(alpha);
286
+ Scalar cos_alpha = cos(alpha);
287
+
288
+ //important: for the first_interval, this function returns zero only if the new edge is "visible" from the source
289
+ //if the new edge can be covered only after turn_over, the value is negative (-1.0)
290
+ Scalar L1 = compute_positive_intersection(begin,
291
+ pseudo_x,
292
+ pseudo_y,
293
+ sin_alpha,
294
+ cos_alpha);
295
+
296
+ if (L1 < 0 || L1 >= L) // Does not produce a window on the edge
297
+ return false;
298
+
299
+ Scalar L2 = compute_positive_intersection(end,
300
+ pseudo_x,
301
+ pseudo_y,
302
+ sin_alpha,
303
+ cos_alpha);
304
+
305
+ if (L2 < 0 || L2 >= L) // Covers vertex
306
+ {
307
+ p->start() = L1;
308
+ p->stop() = L;
309
+ p->pseudo_x() = cos_alpha*pseudo_x + sin_alpha*pseudo_y;
310
+ p->pseudo_y() = -sin_alpha*pseudo_x + cos_alpha*pseudo_y;
311
+ assert(p->pseudo_y() <= 0.0);
312
+
313
+ return true;
314
+ }
315
+ else
316
+ {
317
+ // Does not cover vertex
318
+ p->start() = L1;
319
+ p->stop() = L2;
320
+ p->pseudo_x() = cos_alpha*pseudo_x + sin_alpha*pseudo_y;
321
+ p->pseudo_y() = -sin_alpha*pseudo_x + cos_alpha*pseudo_y;
322
+ assert(p->pseudo_y() <= 0.0);
323
+
324
+ return true;
325
+ }
326
+ }
327
+
328
+ inline bool GeodesicAlgorithmBase::calculate_triangle_parameters(list_pointer &list, Triangle &Tri) // Calculate the parameters of the triangle to be propagated
329
+ {
330
+ OpenMesh::HalfedgeHandle hf0 = this->mesh()->halfedge_handle(list->edge(), hfid0);
331
+ OpenMesh::HalfedgeHandle hf1 = this->mesh()->halfedge_handle(list->edge(), hfid1);
332
+ size_t adjface_size=0;
333
+ if(this->mesh()->face_handle(hf0).idx()>-1)
334
+ adjface_size++;
335
+ if(this->mesh()->face_handle(hf1).idx()>-1)
336
+ adjface_size++;
337
+
338
+ if (adjface_size > 1)
339
+ {
340
+ Tri.bottom_edge = list->edge();
341
+
342
+ if (list == interval_list_0(Tri.bottom_edge))
343
+ Tri.face = this->mesh()->face_handle(hf1);
344
+ else
345
+ Tri.face = this->mesh()->face_handle(hf0);
346
+
347
+ Tri.top_vertex = opposite_vertex(Tri.face, Tri.bottom_edge);
348
+ Tri.left_vertex = list->start_vertex();
349
+ Tri.right_vertex = opposite_vertex(Tri.bottom_edge, Tri.left_vertex);
350
+
351
+ Tri.left_edge = next_edge(Tri.face, Tri.bottom_edge, Tri.left_vertex);
352
+ Tri.right_edge = next_edge(Tri.face, Tri.bottom_edge, Tri.right_vertex);
353
+
354
+ Tri.top_alpha = vertex_angle(Tri.face, Tri.top_vertex);
355
+ Tri.left_alpha = vertex_angle(Tri.face, Tri.left_vertex);
356
+ Tri.right_alpha = vertex_angle(Tri.face, Tri.right_vertex);
357
+
358
+ if (this->mesh()->face_handle(this->mesh()->halfedge_handle(Tri.left_edge, hfid0)) == Tri.face)
359
+ Tri.left_list = interval_list_0(Tri.left_edge);
360
+ else
361
+ Tri.left_list = interval_list_1(Tri.left_edge);
362
+
363
+ if (this->mesh()->face_handle(this->mesh()->halfedge_handle(Tri.right_edge, hfid0)) == Tri.face)
364
+ Tri.right_list = interval_list_0(Tri.right_edge);
365
+ else
366
+ Tri.right_list = interval_list_1(Tri.right_edge);
367
+
368
+ return false;
369
+ }
370
+ else
371
+ {
372
+ return true;
373
+ }
374
+ }
375
+
376
+
377
+ inline void GeodesicAlgorithmBase::build_adjacencies()
378
+ {
379
+ // define m_turn_around_flag for vertices
380
+ std::vector<Scalar> total_vertex_angle(this->mesh()->n_vertices(), 0);
381
+ for(auto f_it = this->mesh()->faces_begin(); f_it != this->mesh()->faces_end(); ++f_it)
382
+ {
383
+ halfedge_handle hf0 = this->mesh()->halfedge_handle(*f_it);
384
+ vertex_pointer v0 = this->mesh()->from_vertex_handle(hf0);
385
+ vertex_pointer v1 = this->mesh()->to_vertex_handle(hf0);
386
+ vertex_pointer v2 = this->mesh()->to_vertex_handle(this->mesh()->next_halfedge_handle(hf0));
387
+ Scalar l1 = (this->mesh()->point(v0) - this->mesh()->point(v1)).norm();
388
+ Scalar l2 = (this->mesh()->point(v1) - this->mesh()->point(v2)).norm();
389
+ Scalar l3 = (this->mesh()->point(v2) - this->mesh()->point(v0)).norm();
390
+
391
+ total_vertex_angle[v0.idx()] += angle_from_edges(l2, l3, l1);
392
+ total_vertex_angle[v1.idx()] += angle_from_edges(l3, l1, l2);
393
+ total_vertex_angle[v2.idx()] += angle_from_edges(l1, l2, l3);
394
+ }
395
+
396
+ for(auto v_it = this->mesh()->vertices_begin(); v_it != this->mesh()->vertices_end(); ++v_it)
397
+ {
398
+ vertex_pointer v = *v_it;
399
+ this->mesh()->data(v).saddle_or_boundary = (total_vertex_angle[v.idx()] > 2.0*M_PI - 1e-5);
400
+ }
401
+
402
+ for(auto e_it = this->mesh()->edges_begin(); e_it != this->mesh()->edges_end(); ++e_it)
403
+ {
404
+ edge_pointer e = *e_it;
405
+ if(this->mesh()->is_boundary(e))
406
+ {
407
+ halfedge_handle hf = this->mesh()->halfedge_handle(e, hfid0);
408
+ this->mesh()->data(this->mesh()->from_vertex_handle(hf)).saddle_or_boundary = true;
409
+ this->mesh()->data(this->mesh()->to_vertex_handle(hf)).saddle_or_boundary = true;
410
+ }
411
+ }
412
+ }
413
+
414
+
415
+ }//geodesic
416
+
417
+ #endif
cpp/tools/geodesic/geodesic_algorithm_exact.h ADDED
@@ -0,0 +1,918 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef GEODESIC_ALGORITHM_EXACT
2
+ #define GEODESIC_ALGORITHM_EXACT
3
+
4
+ #include "geodesic_memory.h"
5
+ #include "geodesic_algorithm_base.h"
6
+ #include "geodesic_algorithm_exact_elements.h"
7
+ #include <string.h>
8
+
9
+ namespace geodesic {
10
+
11
+ class GeodesicAlgorithmExact : public GeodesicAlgorithmBase
12
+ {
13
+ public:
14
+
15
+ // basic functions related to class
16
+ GeodesicAlgorithmExact(Mesh* mesh) :
17
+ GeodesicAlgorithmBase(mesh),
18
+ m_memory_allocator(mesh->n_edges(), mesh->n_edges())
19
+ {ori_mesh = mesh;};
20
+
21
+ // construct neighboring mesh around src within m_radius, if no mesh is constructed,
22
+ // increase m_radius with increase_radio
23
+ GeodesicAlgorithmExact(Mesh* mesh, size_t src, Scalar m_radius)
24
+ {
25
+ ori_mesh = mesh;
26
+ Mesh* sub_mesh = new Mesh;
27
+ bool ok = construct_submesh(sub_mesh, src, m_radius);
28
+ if(ok)
29
+ {
30
+ GeodesicAlgorithmBase::initialize(sub_mesh);
31
+ m_memory_allocator.reset(sub_mesh->n_edges(), sub_mesh->n_edges());
32
+ }
33
+ else
34
+ {
35
+ std::cerr << "Error:Some points cannot be covered under the specified radius, please increase the radius" << std::endl;
36
+ exit(1);
37
+ }
38
+ };
39
+ ~GeodesicAlgorithmExact() {};
40
+ void clear() {
41
+ m_memory_allocator.clear();
42
+ for(auto v_it = this->mesh()->vertices_begin();v_it != this->mesh()->vertices_end(); ++v_it)
43
+ {
44
+ this->mesh()->data(*v_it).geodesic_distance = GEODESIC_INF;
45
+ }
46
+ };
47
+
48
+ // main entry
49
+ void propagate(unsigned source, std::vector<size_t>& idxs);
50
+
51
+ // print the resulting statistics
52
+ void print_statistics();
53
+ private:
54
+
55
+ // simple functions
56
+ void initialize_propagation_data();
57
+ void create_pseudo_source_windows(vertex_pointer &v, bool UpdateFIFOQueue);
58
+ void erase_from_queue(vertex_pointer& v);
59
+
60
+ // propagate a windows list (Rule 1)
61
+ void find_separating_point(list_pointer &list); // find the separating point of the windows and the list
62
+ void propagate_windows_to_two_edges(list_pointer &list); // propagates windows to two edges accross a triangle face
63
+
64
+ // pairwise windows checking (Rule 2)
65
+ void check_with_vertices(list_pointer &list);
66
+ windows_state check_between_two_windows(interval_pointer &w1, interval_pointer &w2); // Check two neighbouring crossing windows on same edge
67
+ void pairwise_windows_checking(list_pointer &list); // Check crossing windows on same edge
68
+
69
+ // main operation
70
+ void propagate_one_windows_list(list_pointer &list);
71
+
72
+ // construct neighboring mesh
73
+ bool construct_submesh(Mesh* sub_mesh, size_t source_idx, Scalar radius);
74
+
75
+ // member variables
76
+ std::set<vertex_pointer> m_vertex_queue;
77
+ std::queue<list_pointer> m_list_queue; // FIFO queue for lists
78
+ MemoryAllocator<Interval> m_memory_allocator; // quickly allocate and deallocate intervals
79
+ Scalar neighbor_radius;
80
+
81
+ Eigen::VectorXi SubVidxfromMesh;
82
+ std::vector<int> MeshVidxfromSub;
83
+
84
+ Mesh* ori_mesh;
85
+ unsigned m_source;
86
+ };
87
+
88
+
89
+ //----------------- simple functions ---------------------
90
+ inline void GeodesicAlgorithmExact::initialize_propagation_data()
91
+ {
92
+ clear();
93
+
94
+ // initialize source's parameters
95
+ vertex_pointer source = (this->mesh()->vertex_handle(m_source));
96
+ this->mesh()->data(source).geodesic_distance = 0;
97
+ this->mesh()->data(source).state = VertexState::INSIDE;
98
+
99
+ // initialize windows around source
100
+ create_pseudo_source_windows(source, false);
101
+ }
102
+
103
+ inline void GeodesicAlgorithmExact::erase_from_queue(vertex_pointer& v)
104
+ {
105
+ assert(m_vertex_queue.count(v) <= 1);
106
+
107
+ std::multiset<vertex_pointer>::iterator it = m_vertex_queue.find(v);
108
+ if (it != m_vertex_queue.end())
109
+ m_vertex_queue.erase(it);
110
+ }
111
+
112
+ inline void GeodesicAlgorithmExact::create_pseudo_source_windows(vertex_pointer &pseudo_source, bool inside_traversed_area)
113
+ {
114
+ // update vertices around pseudo_source
115
+ for (auto e_it = this->mesh()->ve_begin(pseudo_source); e_it != this->mesh()->ve_end(pseudo_source); ++e_it)
116
+ {
117
+ edge_pointer edge_it = *e_it;
118
+ vertex_pointer vert_it = opposite_vertex(edge_it, pseudo_source);
119
+
120
+ Scalar distance = this->mesh()->data(pseudo_source).geodesic_distance
121
+ + this->mesh()->data(edge_it).length;
122
+
123
+ if (distance < this->mesh()->data(vert_it).geodesic_distance)
124
+ {
125
+ m_vertex_queue.erase(vert_it);
126
+
127
+ this->mesh()->data(vert_it).geodesic_distance = distance;
128
+ if (this->mesh()->data(vert_it).state == VertexState::OUTSIDE)
129
+ this->mesh()->data(vert_it).state = VertexState::FRONT;
130
+
131
+ this->mesh()->data(vert_it).incident_face = this->mesh()->face_handle(this->mesh()->halfedge_handle(edge_it, hfid0));
132
+ edge_pointer next_edge = geodesic::GeodesicAlgorithmBase::next_edge(
133
+ this->mesh()->data(vert_it).incident_face,edge_it, pseudo_source);
134
+ this->mesh()->data(vert_it).incident_point =
135
+ (this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(next_edge, hfid0)) == pseudo_source) ?
136
+ 0 : this->mesh()->data(next_edge).length;
137
+
138
+ m_vertex_queue.insert(vert_it);
139
+ }
140
+ }
141
+
142
+ // update pseudo_source windows around pseudo_source
143
+ for(auto f_it = this->mesh()->vf_begin(pseudo_source); f_it != this->mesh()->vf_end(pseudo_source); ++f_it)
144
+ {
145
+ face_pointer face_it = *f_it;
146
+ edge_pointer edge_it = geodesic::GeodesicAlgorithmBase::opposite_edge(face_it, pseudo_source);
147
+ list_pointer list = (this->mesh()->face_handle(this->mesh()->halfedge_handle(edge_it, hfid0))==face_it)?
148
+ interval_list_0(edge_it) : interval_list_1(edge_it);
149
+
150
+ // create a window
151
+ interval_pointer candidate = new Interval;
152
+
153
+ candidate->start() = 0;
154
+ candidate->stop() = this->mesh()->data(edge_it).length;
155
+ candidate->d() = this->mesh()->data(pseudo_source).geodesic_distance;
156
+ Scalar angle = geodesic::GeodesicAlgorithmBase::vertex_angle(face_it, list->start_vertex());
157
+ Scalar length = this->mesh()->data(geodesic::GeodesicAlgorithmBase::next_edge
158
+ (face_it, edge_it,list->start_vertex())).length;
159
+ candidate->pseudo_x() = cos(angle) * length;
160
+ candidate->pseudo_y() = -sin(angle) * length;
161
+
162
+ // insert into list
163
+ list->push_back(candidate);
164
+
165
+ // push into M_LIST_QUEUE if inside traversed area
166
+ vertex_pointer v0 = this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(edge_it, hfid0));
167
+ vertex_pointer v1 = this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(edge_it, hfid1));
168
+ if ((inside_traversed_area) &&
169
+ ((this->mesh()->data(v0).state != VertexState::FRONT)
170
+ || (this->mesh()->data(v1).state != VertexState::FRONT)))
171
+ m_list_queue.push(list);
172
+
173
+ // Statistics
174
+ ++m_windows_wavefront;
175
+ if (m_windows_peak < m_windows_wavefront)
176
+ m_windows_peak = m_windows_wavefront;
177
+
178
+ }
179
+ }
180
+
181
+ //----------------- propagate a windows list (Rule 1) ---------------------
182
+ inline void GeodesicAlgorithmExact::find_separating_point(list_pointer &list)
183
+ {
184
+ const Scalar LOCAL_EPSILON = 1e-20 * this->mesh()->data(list->edge()).length; // numerical issue
185
+
186
+ Scalar L = this->mesh()->data(Tri.left_edge).length;
187
+ Scalar top_x = L * cos(Tri.left_alpha);
188
+ Scalar top_y = L * sin(Tri.left_alpha);
189
+
190
+ Scalar temp_geodesic = GEODESIC_INF;
191
+ face_pointer temp_face_handle = this->mesh()->data(Tri.top_vertex).incident_face;
192
+ Scalar temp_incident_point = this->mesh()->data(Tri.top_vertex).incident_point;
193
+
194
+ interval_pointer iter = list->begin();
195
+
196
+ Scalar wlist_sp = 0;
197
+ Scalar wlist_pseudo_x = 0;
198
+ Scalar wlist_pseudo_y = 0;
199
+
200
+ while (iter != NULL)
201
+ {
202
+ interval_pointer &w = iter;
203
+
204
+ Scalar w_sp = w->pseudo_x() - w->pseudo_y() * ((top_x - w->pseudo_x()) / (top_y - w->pseudo_y()));
205
+ Scalar distance = GEODESIC_INF;
206
+
207
+ // shortest path from the window
208
+ if ((w_sp - w->start() > LOCAL_EPSILON) && (w_sp - w->stop() < -LOCAL_EPSILON))
209
+ {
210
+ distance = w->d() + sqrt((top_x - w->pseudo_x()) * (top_x - w->pseudo_x()) + (top_y - w->pseudo_y()) * (top_y - w->pseudo_y()));
211
+ w->shortest_distance() = distance;
212
+ }
213
+ else if (w_sp - w->start() <= LOCAL_EPSILON)
214
+ {
215
+ distance = w->d() + sqrt((top_x - w->start()) * (top_x - w->start()) + top_y * top_y) + sqrt((w->start() - w->pseudo_x()) * (w->start() - w->pseudo_x()) + w->pseudo_y() * w->pseudo_y());
216
+ w->shortest_distance() = distance;
217
+ w_sp = w->start();
218
+ }
219
+ else if (w_sp - w->stop() >= -LOCAL_EPSILON)
220
+ {
221
+ distance = w->d() + sqrt((top_x - w->stop()) * (top_x - w->stop()) + top_y * top_y) + sqrt((w->stop() - w->pseudo_x()) * (w->stop() - w->pseudo_x()) + w->pseudo_y() * w->pseudo_y());
222
+ w->shortest_distance() = distance;
223
+ w_sp = w->stop();
224
+ }
225
+
226
+ // update information at top_t
227
+ if (distance < temp_geodesic)
228
+ {
229
+ temp_geodesic = distance;
230
+ temp_face_handle = Tri.face;
231
+ vertex_pointer v0 = this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(list->edge(), hfid0));
232
+ temp_incident_point = (list->start_vertex() == v0) ?
233
+ w_sp : this->mesh()->data(list->edge()).length - w_sp;
234
+ wlist_sp = w_sp;
235
+ wlist_pseudo_x = w->pseudo_x();
236
+ wlist_pseudo_y = w->pseudo_y();
237
+ }
238
+ w->sp() = w_sp;
239
+
240
+ iter = iter->next();
241
+ }
242
+
243
+ // update top_vertex and M_VERTEX_QUEUE
244
+ if (temp_geodesic < this->mesh()->data(Tri.top_vertex).geodesic_distance)
245
+ {
246
+ if (this->mesh()->data(Tri.top_vertex).state == VertexState::FRONT) erase_from_queue(Tri.top_vertex);
247
+ this->mesh()->data(Tri.top_vertex).geodesic_distance = temp_geodesic;
248
+ this->mesh()->data(Tri.top_vertex).incident_face = temp_face_handle;
249
+ this->mesh()->data(Tri.top_vertex).incident_point = temp_incident_point;
250
+ if (this->mesh()->data(Tri.top_vertex).state == VertexState::FRONT)
251
+ {
252
+ m_vertex_queue.insert(Tri.top_vertex);
253
+ }
254
+
255
+ if ((this->mesh()->data(Tri.top_vertex).state == VertexState::INSIDE)
256
+ && (this->mesh()->data(Tri.top_vertex).saddle_or_boundary))
257
+ create_pseudo_source_windows(Tri.top_vertex, true); // handle saddle vertex
258
+ }
259
+
260
+ list->sp() = wlist_sp;
261
+ list->pseudo_x() = wlist_pseudo_x;
262
+ list->pseudo_y() = wlist_pseudo_y;
263
+ }
264
+
265
+ inline void GeodesicAlgorithmExact::propagate_windows_to_two_edges(list_pointer &list)
266
+ {
267
+ const Scalar LOCAL_EPSILON = 1e-8 * this->mesh()->data(list->edge()).length; // numerical issue
268
+
269
+ interval_pointer iter = list->begin();
270
+ interval_pointer iter_t;
271
+
272
+ enum PropagationDirection
273
+ {
274
+ LEFT,
275
+ RIGHT,
276
+ BOTH
277
+ };
278
+
279
+ PropagationDirection direction;
280
+
281
+ while (!list->empty() && (iter != NULL))
282
+ {
283
+ interval_pointer &w = iter;
284
+ assert(w->start() <= w->stop());
285
+
286
+ if (w->sp() < list->sp() - LOCAL_EPSILON)
287
+ {
288
+ // only propagate to left edge
289
+ Scalar Intersect_X, Intersect_Y;
290
+
291
+ // judge the positions of the two windows
292
+ CalculateIntersectionPoint(list->pseudo_x(), list->pseudo_y(), list->sp(), 0, w->pseudo_x(), w->pseudo_y(), w->stop(), 0, Intersect_X, Intersect_Y);
293
+ if ((w->stop() < list->sp()) || ((Intersect_Y <= 0) && (Intersect_Y >= list->pseudo_y()) && (Intersect_Y >= w->pseudo_y())))
294
+ {
295
+ direction = PropagationDirection::LEFT;
296
+ }
297
+ else
298
+ {
299
+ direction = PropagationDirection::BOTH;
300
+ }
301
+ }
302
+ else if (w->sp() > list->sp() + LOCAL_EPSILON)
303
+ {
304
+ // only propagate to right edge
305
+ Scalar Intersect_X, Intersect_Y;
306
+
307
+ // judge the positions of the two windows
308
+ CalculateIntersectionPoint(list->pseudo_x(), list->pseudo_y(), list->sp(), 0, w->pseudo_x(), w->pseudo_y(), w->start(), 0, Intersect_X, Intersect_Y);
309
+ if ((w->start() > list->sp())||((Intersect_Y <= 0) && (Intersect_Y >= list->pseudo_y()) && (Intersect_Y >= w->pseudo_y())))
310
+ {
311
+ direction = PropagationDirection::RIGHT;
312
+ }
313
+ else
314
+ {
315
+ direction = PropagationDirection::BOTH;
316
+ }
317
+ }
318
+ else
319
+ {
320
+ // propagate to both edges
321
+ direction = PropagationDirection::BOTH;
322
+ }
323
+
324
+ bool ValidPropagation;
325
+ interval_pointer right_w;
326
+
327
+ switch (direction) {
328
+ case PropagationDirection::LEFT:
329
+ ValidPropagation = compute_propagated_parameters(w->pseudo_x(),
330
+ w->pseudo_y(),
331
+ w->start(),
332
+ w->stop(),
333
+ Tri.left_alpha,
334
+ this->mesh()->data(Tri.left_edge).length,
335
+ w,
336
+ w->d());
337
+
338
+ iter_t = iter->next();
339
+ if (ValidPropagation)
340
+ {
341
+ list->erase(w);
342
+ wl_left.push_back(w);
343
+ }
344
+ else
345
+ {
346
+ list->erase(w);
347
+ delete w;
348
+ --m_windows_wavefront;
349
+ }
350
+ iter = iter_t;
351
+ break;
352
+
353
+ case PropagationDirection::RIGHT:
354
+ ValidPropagation = compute_propagated_parameters(this->mesh()->data(Tri.bottom_edge).length - w->pseudo_x(),
355
+ w->pseudo_y(),
356
+ this->mesh()->data(Tri.bottom_edge).length - w->stop(),
357
+ this->mesh()->data(Tri.bottom_edge).length - w->start(),
358
+ Tri.right_alpha,
359
+ this->mesh()->data(Tri.right_edge).length,
360
+ w,
361
+ w->d());
362
+
363
+ iter_t = iter->next();
364
+ if (ValidPropagation)
365
+ {
366
+ Scalar length = this->mesh()->data(Tri.right_edge).length; // invert window
367
+ Scalar start = length - w->stop();
368
+ w->stop() = length - w->start();
369
+ w->start() = start;
370
+ w->pseudo_x() = length - w->pseudo_x();
371
+
372
+ list->erase(w);
373
+ wl_right.push_back(w);
374
+ }
375
+ else
376
+ {
377
+ list->erase(w);
378
+ delete w;
379
+ --m_windows_wavefront;
380
+ }
381
+ iter = iter_t;
382
+ break;
383
+
384
+ case PropagationDirection:: BOTH:
385
+ right_w = new Interval;
386
+ memcpy(right_w, w, sizeof(Interval));
387
+
388
+ ValidPropagation = compute_propagated_parameters(w->pseudo_x(),
389
+ w->pseudo_y(),
390
+ w->start(),
391
+ w->stop(),
392
+ geodesic::GeodesicAlgorithmBase::vertex_angle(Tri.face, Tri.left_vertex),
393
+ this->mesh()->data(Tri.left_edge).length,
394
+ w,
395
+ w->d());
396
+
397
+ iter_t = iter->next();
398
+ if (ValidPropagation)
399
+ {
400
+ list->erase(w);
401
+ wl_left.push_back(w);
402
+ }
403
+ else
404
+ {
405
+ list->erase(w);
406
+ delete w;
407
+ --m_windows_wavefront;
408
+ }
409
+ iter = iter_t;
410
+
411
+ ValidPropagation = compute_propagated_parameters(this->mesh()->data(Tri.bottom_edge).length - right_w->pseudo_x(),
412
+ right_w->pseudo_y(),
413
+ this->mesh()->data(Tri.bottom_edge).length - right_w->stop(),
414
+ this->mesh()->data(Tri.bottom_edge).length - right_w->start(),
415
+ geodesic::GeodesicAlgorithmBase::vertex_angle(Tri.face, Tri.right_vertex),
416
+ this->mesh()->data(Tri.right_edge).length,
417
+ right_w,
418
+ right_w->d());
419
+
420
+ if (ValidPropagation)
421
+ {
422
+ // invert window
423
+ Scalar length = this->mesh()->data(Tri.right_edge).length;
424
+ Scalar start = length - right_w->stop();
425
+ right_w->stop() = length - right_w->start();
426
+ right_w->start() = start;
427
+ right_w->pseudo_x() = length - right_w->pseudo_x();
428
+
429
+ wl_right.push_back(right_w);
430
+
431
+ ++m_windows_wavefront;
432
+ if (m_windows_peak < m_windows_wavefront)
433
+ m_windows_peak = m_windows_wavefront;
434
+ }
435
+ else
436
+ {
437
+ delete right_w;
438
+ }
439
+ break;
440
+
441
+ default:
442
+ break;
443
+ }
444
+ }
445
+ }
446
+
447
+ //----------------- pairwise windows checking (Rule 2) ----------------------
448
+ inline void GeodesicAlgorithmExact::check_with_vertices(list_pointer &list)
449
+ {
450
+ if (list->empty()) return;
451
+
452
+ interval_pointer iter = list->begin();
453
+ interval_pointer iter_t;
454
+
455
+ while ((!list->empty()) && (iter != NULL))
456
+ {
457
+ interval_pointer &w = iter;
458
+ bool w_survive = true;
459
+
460
+ edge_pointer e = list->edge();
461
+ vertex_pointer v1 = list->start_vertex();
462
+ vertex_pointer v2 = opposite_vertex(e, v1);
463
+ Scalar d1 = GEODESIC_INF;
464
+
465
+ d1 = w->d() + sqrt((w->stop() - w->pseudo_x()) * (w->stop() - w->pseudo_x()) + w->pseudo_y() * w->pseudo_y());
466
+ if (this->mesh()->data(v1).geodesic_distance + w->stop() < d1)
467
+ w_survive = false;
468
+
469
+ d1 = w->d() + sqrt((w->start() - w->pseudo_x()) * (w->start() - w->pseudo_x()) + w->pseudo_y() * w->pseudo_y());
470
+ if (this->mesh()->data(v2).geodesic_distance + this->mesh()->data(e).length - w->start() < d1)
471
+ w_survive = false;
472
+
473
+
474
+ iter_t = iter;
475
+ iter = iter->next();
476
+
477
+ if (!w_survive)
478
+ {
479
+ list->erase(iter_t);
480
+ delete iter_t;
481
+ --m_windows_wavefront;
482
+ }
483
+ }
484
+ }
485
+
486
+ inline windows_state GeodesicAlgorithmExact::check_between_two_windows(interval_pointer &w1, interval_pointer &w2)
487
+ {
488
+ Scalar NUMERCIAL_EPSILON = 1 - 1e-12;
489
+ // we implement the discussed 6 cases as follows for simplicity
490
+
491
+ if ((w1->start() >= w2->start()) && (w1->start() <= w2->stop())) // w1->start
492
+ {
493
+ Scalar Intersect_X, Intersect_Y;
494
+
495
+ // judge the order of the two windows
496
+ CalculateIntersectionPoint(w2->pseudo_x(), w2->pseudo_y(), w1->start(), 0, w1->pseudo_x(), w1->pseudo_y(), w1->stop(), 0, Intersect_X, Intersect_Y);
497
+
498
+ if ((Intersect_Y <= 0) && (Intersect_Y >= w1->pseudo_y()) && (Intersect_Y >= w2->pseudo_y()))
499
+ {
500
+ Scalar d1, d2;
501
+ d1 = w1->d() + sqrt((w1->start() - w1->pseudo_x()) * (w1->start() - w1->pseudo_x()) + (w1->pseudo_y()) * (w1->pseudo_y()));
502
+ d2 = w2->d() + sqrt((w1->start() - w2->pseudo_x()) * (w1->start() - w2->pseudo_x()) + (w2->pseudo_y()) * (w2->pseudo_y()));
503
+
504
+ if (d2 < d1 * NUMERCIAL_EPSILON)
505
+ return w1_invalid;
506
+ if (d1 < d2 * NUMERCIAL_EPSILON)
507
+ w2->start() = w1->start();
508
+ }
509
+ }
510
+
511
+ if ((w1->stop() >= w2->start()) && (w1->stop() <= w2->stop())) // w1->stop
512
+ {
513
+ Scalar Intersect_X, Intersect_Y;
514
+
515
+ // judge the order of the two windows
516
+ CalculateIntersectionPoint(w2->pseudo_x(), w2->pseudo_y(), w1->stop(), 0, w1->pseudo_x(), w1->pseudo_y(), w1->start(), 0, Intersect_X, Intersect_Y);
517
+
518
+ if ((Intersect_Y <= 0) && (Intersect_Y >= w1->pseudo_y()) && (Intersect_Y >= w2->pseudo_y()))
519
+ {
520
+ Scalar d1, d2;
521
+ d1 = w1->d() + sqrt((w1->stop() - w1->pseudo_x()) * (w1->stop() - w1->pseudo_x()) + (w1->pseudo_y()) * (w1->pseudo_y()));
522
+ d2 = w2->d() + sqrt((w1->stop() - w2->pseudo_x()) * (w1->stop() - w2->pseudo_x()) + (w2->pseudo_y()) * (w2->pseudo_y()));
523
+
524
+ if (d2 < d1 * NUMERCIAL_EPSILON)
525
+ return w1_invalid;
526
+ if (d1 < d2 * NUMERCIAL_EPSILON)
527
+ w2->stop() = w1->stop();
528
+ }
529
+ }
530
+
531
+ if ((w2->start() >= w1->start()) && (w2->start() <= w1->stop())) // w2->start
532
+ {
533
+ Scalar Intersect_X, Intersect_Y;
534
+
535
+ // judge the previous order of the two windows
536
+ CalculateIntersectionPoint(w1->pseudo_x(), w1->pseudo_y(), w2->start(), 0, w2->pseudo_x(), w2->pseudo_y(), w2->stop(), 0, Intersect_X, Intersect_Y);
537
+
538
+ if ((Intersect_Y <= 0) && (Intersect_Y >= w1->pseudo_y()) && (Intersect_Y >= w2->pseudo_y()))
539
+ {
540
+ Scalar d1, d2;
541
+ d1 = w1->d() + sqrt((w2->start() - w1->pseudo_x()) * (w2->start() - w1->pseudo_x()) + (w1->pseudo_y()) * (w1->pseudo_y()));
542
+ d2 = w2->d() + sqrt((w2->start() - w2->pseudo_x()) * (w2->start() - w2->pseudo_x()) + (w2->pseudo_y()) * (w2->pseudo_y()));
543
+
544
+ if (d1 < d2 * NUMERCIAL_EPSILON)
545
+ return w2_invalid;
546
+ if (d2 < d1 * NUMERCIAL_EPSILON)
547
+ w1->start() = w2->start();
548
+ }
549
+ }
550
+
551
+ if ((w2->stop() >= w1->start()) && (w2->stop() <= w1->stop())) // w2->stop
552
+ {
553
+ Scalar Intersect_X, Intersect_Y;
554
+
555
+ // judge the previous order of the two windows
556
+ CalculateIntersectionPoint(w1->pseudo_x(), w1->pseudo_y(), w2->stop(), 0, w2->pseudo_x(), w2->pseudo_y(), w2->start(), 0, Intersect_X, Intersect_Y);
557
+
558
+ if ((Intersect_Y <= 0) && (Intersect_Y >= w1->pseudo_y()) && (Intersect_Y >= w2->pseudo_y()))
559
+ {
560
+ Scalar d1, d2;
561
+ d1 = w1->d() + sqrt((w2->stop() - w1->pseudo_x()) * (w2->stop() - w1->pseudo_x()) + (w1->pseudo_y()) * (w1->pseudo_y()));
562
+ d2 = w2->d() + sqrt((w2->stop() - w2->pseudo_x()) * (w2->stop() - w2->pseudo_x()) + (w2->pseudo_y()) * (w2->pseudo_y()));
563
+
564
+ if (d1 < d2 * NUMERCIAL_EPSILON)
565
+ return w2_invalid;
566
+ if (d2 < d1 * NUMERCIAL_EPSILON)
567
+ w1->stop() = w2->stop();
568
+ }
569
+ }
570
+
571
+ if (w1->start() >= w2->stop())
572
+ {
573
+ Scalar Intersect_X, Intersect_Y;
574
+
575
+ // judge the previous order of the two windows
576
+ CalculateIntersectionPoint(w1->pseudo_x(), w1->pseudo_y(), w1->start(), 0, w2->pseudo_x(), w2->pseudo_y(), w2->stop(), 0, Intersect_X, Intersect_Y);
577
+
578
+ face_pointer f = opposite_face(Tri.bottom_edge, Tri.face);
579
+ edge_pointer e = next_edge(f, Tri.bottom_edge, Tri.left_vertex);
580
+ Scalar angle = vertex_angle(f, Tri.left_vertex);
581
+ Scalar Cx = this->mesh()->data(e).length * cos(angle);
582
+ Scalar Cy = this->mesh()->data(e).length * -sin(angle);
583
+
584
+ if ((PointInTriangle(Intersect_X, Intersect_Y, this->mesh()->data(Tri.bottom_edge).length, Cx, Cy))
585
+ && (Intersect_Y <= 0) && (Intersect_Y >= w1->pseudo_y()) && (Intersect_Y >= w2->pseudo_y()))
586
+ {
587
+ Scalar d1, d2;
588
+ d1 = w1->d() + sqrt((Intersect_X - w1->pseudo_x()) * (Intersect_X - w1->pseudo_x()) + (Intersect_Y - w1->pseudo_y()) * (Intersect_Y - w1->pseudo_y()));
589
+ d2 = w2->d() + sqrt((Intersect_X - w2->pseudo_x()) * (Intersect_X - w2->pseudo_x()) + (Intersect_Y - w2->pseudo_y()) * (Intersect_Y - w2->pseudo_y()));
590
+
591
+ if (d1 < d2 * NUMERCIAL_EPSILON)
592
+ return w2_invalid;
593
+ if (d2 < d1 * NUMERCIAL_EPSILON)
594
+ return w1_invalid;
595
+ }
596
+ }
597
+
598
+ if (w2->start() >= w1->stop())
599
+ {
600
+ Scalar Intersect_X, Intersect_Y;
601
+
602
+ // judge the previous order of the two windows
603
+ CalculateIntersectionPoint(w2->pseudo_x(), w2->pseudo_y(), w2->start(), 0, w1->pseudo_x(), w1->pseudo_y(), w1->stop(), 0, Intersect_X, Intersect_Y);
604
+
605
+ face_pointer f = opposite_face(Tri.bottom_edge, Tri.face);
606
+ edge_pointer e = next_edge(f, Tri.bottom_edge, Tri.left_vertex);
607
+ Scalar angle = vertex_angle(f, Tri.left_vertex);
608
+ Scalar Cx = this->mesh()->data(e).length * cos(angle);
609
+ Scalar Cy = this->mesh()->data(e).length * -sin(angle);
610
+
611
+ if ((PointInTriangle(Intersect_X, Intersect_Y, this->mesh()->data(Tri.bottom_edge).length, Cx, Cy))
612
+ && (Intersect_Y <= 0) && (Intersect_Y >= w1->pseudo_y()) && (Intersect_Y >= w2->pseudo_y()))
613
+ {
614
+ Scalar d1, d2;
615
+ d1 = w1->d() + sqrt((Intersect_X - w1->pseudo_x()) * (Intersect_X - w1->pseudo_x()) + (Intersect_Y - w1->pseudo_y()) * (Intersect_Y - w1->pseudo_y()));
616
+ d2 = w2->d() + sqrt((Intersect_X - w2->pseudo_x()) * (Intersect_X - w2->pseudo_x()) + (Intersect_Y - w2->pseudo_y()) * (Intersect_Y - w2->pseudo_y()));
617
+
618
+ if (d1 < d2 - NUMERCIAL_EPSILON)
619
+ return w2_invalid;
620
+ if (d2 < d1 - NUMERCIAL_EPSILON)
621
+ return w1_invalid;
622
+ }
623
+ }
624
+
625
+ return both_valid;
626
+ }
627
+
628
+ inline void GeodesicAlgorithmExact::pairwise_windows_checking(list_pointer &list)
629
+ {
630
+ if (list->empty()) return;
631
+
632
+ interval_pointer iter = list->begin();
633
+ interval_pointer next, iter_t;
634
+
635
+ next = iter->next();
636
+
637
+ // traverse successive pairs of windows
638
+ while ((!list->empty()) && (next != NULL))
639
+ {
640
+ windows_state ws = check_between_two_windows(iter, next);
641
+
642
+ switch (ws)
643
+ {
644
+ case geodesic::w1_invalid:
645
+ iter_t = iter;
646
+ if (iter == list->begin())
647
+ {
648
+ iter = iter->next();
649
+ }
650
+ else
651
+ {
652
+ iter = iter->previous();
653
+ }
654
+
655
+ list->erase(iter_t);
656
+ delete iter_t;
657
+ --m_windows_wavefront;
658
+ break;
659
+
660
+ case geodesic::w2_invalid:
661
+ list->erase(next);
662
+ delete next;
663
+ --m_windows_wavefront;
664
+ break;
665
+
666
+ case geodesic::both_valid:
667
+ iter = iter->next();
668
+ break;
669
+
670
+ default:
671
+ break;
672
+ }
673
+
674
+ next = iter->next();
675
+ }
676
+ }
677
+
678
+ //------------------------- main operation ----------------------------
679
+ inline void GeodesicAlgorithmExact::propagate_one_windows_list(list_pointer &list)
680
+ {
681
+ if (list->empty()) return;
682
+ OpenMesh::HalfedgeHandle hf0 = this->mesh()->halfedge_handle(list->edge(), hfid0);
683
+ OpenMesh::HalfedgeHandle hf1 = this->mesh()->halfedge_handle(list->edge(), hfid1);
684
+ if (this->mesh()->face_handle(hf0).idx()>-1 && this->mesh()->face_handle(hf1).idx()>-1)
685
+ {
686
+ // Rule 2: pairwise windows checking
687
+ check_with_vertices(list);
688
+ pairwise_windows_checking(list);
689
+
690
+ // Rule 1: "One Angle Two Sides"
691
+ find_separating_point(list);
692
+ propagate_windows_to_two_edges(list);
693
+ }
694
+ }
695
+
696
+ //-------------------------- main entry --------------------------
697
+ inline void GeodesicAlgorithmExact::propagate(unsigned source, std::vector<size_t>& idxs)
698
+ {
699
+ // initialization
700
+ m_source = SubVidxfromMesh[source];
701
+ initialize_propagation_data();
702
+ while (!m_vertex_queue.empty())
703
+ {
704
+ // (1) pop a vertex from M_VERTEX_QUEUE
705
+ vertex_pointer vert = *m_vertex_queue.begin();
706
+ m_vertex_queue.erase(m_vertex_queue.begin());
707
+
708
+ // (2) update wavefront
709
+ this->mesh()->data(vert).state = VertexState::INSIDE;
710
+ for(auto e_it = this->mesh()->ve_begin(vert); e_it != this->mesh()->ve_end(vert); ++e_it)
711
+ {
712
+ vertex_pointer vert_it = opposite_vertex(*e_it, vert);
713
+ if (this->mesh()->data(vert_it).state == VertexState::OUTSIDE)
714
+ this->mesh()->data(vert_it).state = VertexState::FRONT;
715
+ }
716
+
717
+ // (3) handle saddle vertex
718
+ if (this->mesh()->data(vert).saddle_or_boundary) create_pseudo_source_windows(vert, false);
719
+
720
+ // (4) push window lists on the wavefront incident to v into M_LIST_QUEUE
721
+ for(auto e_it = this->mesh()->ve_begin(vert); e_it != this->mesh()->ve_end(vert); ++e_it)
722
+ {
723
+ edge_pointer edge_it = *e_it;
724
+ if (!interval_list_0(edge_it)->empty())
725
+ {
726
+ m_list_queue.push(interval_list_0(edge_it));
727
+ }
728
+ if (!interval_list_1(edge_it)->empty())
729
+ {
730
+ m_list_queue.push(interval_list_1(edge_it));
731
+ }
732
+ }
733
+
734
+
735
+ for(auto f_it = this->mesh()->vf_begin(vert); f_it != this->mesh()->vf_end(vert); ++f_it)
736
+ {
737
+ edge_pointer edge_it = opposite_edge(*f_it, vert);
738
+ bool two_adjface = (this->mesh()->face_handle(this->mesh()->halfedge_handle(edge_it, hfid0)).idx()>-1)
739
+ && (this->mesh()->face_handle(this->mesh()->halfedge_handle(edge_it, hfid1)).idx()>-1);
740
+ vertex_pointer vert_it;
741
+ if(two_adjface)
742
+ {
743
+ face_pointer faceid = opposite_face(edge_it, *f_it);
744
+ vert_it = opposite_vertex(faceid, edge_it);
745
+ }
746
+ if (!two_adjface || (this->mesh()->data(vert_it).state != VertexState::OUTSIDE))
747
+ {
748
+ if (!interval_list_0(edge_it)->empty())
749
+ {
750
+ m_list_queue.push(interval_list_0(edge_it));
751
+ }
752
+ if (!interval_list_1(edge_it)->empty())
753
+ {
754
+ m_list_queue.push(interval_list_1(edge_it));
755
+ }
756
+ }
757
+ }
758
+
759
+
760
+ // (5) propagate window lists in a FIFO order
761
+ while (!m_list_queue.empty())
762
+ {
763
+ // pop an list from M_LIST_QUEUE
764
+ list_pointer list = m_list_queue.front();
765
+
766
+ m_list_queue.pop();
767
+
768
+ bool is_boundary = calculate_triangle_parameters(list, Tri);
769
+ if (!is_boundary)
770
+ {
771
+ // propagate the window list using Rule 1 and 2
772
+ wl_left.clear(); wl_right.clear();
773
+ propagate_one_windows_list(list);
774
+
775
+ // merge windows lists
776
+ if (!wl_left.empty())
777
+ {
778
+ // in VTP, both "PrimeMerge" and "SecondMerge" connect window lists in an order-free way
779
+ if (!Tri.left_list->empty())
780
+ {
781
+ Tri.left_list->begin()->previous() = wl_left.end();
782
+ wl_left.end()->next() = Tri.left_list->begin();
783
+ Tri.left_list->begin() = wl_left.begin();
784
+ }
785
+ else
786
+ {
787
+ Tri.left_list->begin() = wl_left.begin();
788
+ Tri.left_list->end() = wl_left.end();
789
+ }
790
+
791
+ // push updated list into M_LIST_QUEUE
792
+ vertex_pointer v0 = this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(Tri.left_edge, hfid0));
793
+ vertex_pointer v1 = this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(Tri.left_edge, hfid1));
794
+ if (((this->mesh()->data(v0).state == VertexState::INSIDE)
795
+ || (this->mesh()->data(v1).state == VertexState::INSIDE))
796
+ && (!Tri.left_list->empty()))
797
+ {
798
+ m_list_queue.push(Tri.left_list);
799
+ }
800
+ }
801
+
802
+ if (!wl_right.empty())
803
+ {
804
+ // in VTP, both "PrimeMerge" and "SecondMerge" connect window lists in an order-free way
805
+ if (!Tri.right_list->empty())
806
+ {
807
+ Tri.right_list->end()->next() = wl_right.begin();
808
+ wl_right.begin()->previous() = Tri.right_list->end();
809
+ Tri.right_list->end() = wl_right.end();
810
+ }
811
+ else
812
+ {
813
+ Tri.right_list->begin() = wl_right.begin();
814
+ Tri.right_list->end() = wl_right.end();
815
+ }
816
+
817
+ // push updated list into M_LIST_QUEUE
818
+ vertex_pointer v0 = this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(Tri.right_edge, hfid0));
819
+ vertex_pointer v1 = this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(Tri.right_edge, hfid1));
820
+ if (((this->mesh()->data(v0).state == VertexState::INSIDE)
821
+ || (this->mesh()->data(v1).state == VertexState::INSIDE)) && (!Tri.right_list->empty()))
822
+ m_list_queue.push(Tri.right_list);
823
+ }
824
+ }
825
+
826
+ list->clear();
827
+ }
828
+ // statistics
829
+ if (m_vertex_queue.size() > m_queue_max_size)
830
+ m_queue_max_size = m_vertex_queue.size();
831
+ }
832
+ idxs.clear();
833
+ for(auto v_it = this->mesh()->vertices_begin(); v_it != this->mesh()->vertices_end(); ++v_it)
834
+ {
835
+ idxs.push_back(MeshVidxfromSub[v_it->idx()]);
836
+ this->ori_mesh->data(this->ori_mesh->vertex_handle(MeshVidxfromSub[v_it->idx()])).geodesic_distance
837
+ = this->mesh()->data(*v_it).geodesic_distance;
838
+ }
839
+ }
840
+
841
+ // construct sub mesh
842
+ inline bool GeodesicAlgorithmExact::construct_submesh(Mesh* sub_mesh, size_t source_idx, Scalar radius)
843
+ {
844
+ std::queue<size_t> vertexlist;
845
+ vertexlist.push(source_idx);
846
+ Vec3 srcp = ori_mesh->point(ori_mesh->vertex_handle(source_idx));
847
+ std::vector<bool> visited(ori_mesh->n_vertices(), false);
848
+ std::vector<bool> added_face(ori_mesh->n_faces(), false);
849
+ SubVidxfromMesh.resize(ori_mesh->n_vertices());
850
+ SubVidxfromMesh.setConstant(-1);
851
+ MeshVidxfromSub.clear();
852
+ visited[source_idx] = true;
853
+ while(!vertexlist.empty())
854
+ {
855
+ size_t vidx = vertexlist.front();
856
+ vertexlist.pop();
857
+ OpenMesh::VertexHandle vh = ori_mesh->vertex_handle(vidx);
858
+ Vec3 vp = ori_mesh->point(vh);
859
+ if((srcp - vp).norm() < radius)
860
+ {
861
+ vertex_pointer new_v = sub_mesh->add_vertex(vp);
862
+ SubVidxfromMesh[vh.idx()] = new_v.idx();
863
+ MeshVidxfromSub.push_back(vh.idx());
864
+ for(auto vv_it = ori_mesh->vv_begin(vh); vv_it != ori_mesh->vv_end(vh); vv_it++)
865
+ {
866
+ if(!visited[vv_it->idx()])
867
+ {
868
+ vertexlist.push(vv_it->idx());
869
+ visited[vv_it->idx()] = true;
870
+ }
871
+ }
872
+ for(auto vf_it = ori_mesh->vf_begin(vh); vf_it != ori_mesh->vf_end(vh); vf_it++)
873
+ {
874
+ halfedge_handle hf = ori_mesh->halfedge_handle(*vf_it);
875
+ if(!added_face[vf_it->idx()])
876
+ {
877
+ vertex_pointer vh = ori_mesh->from_vertex_handle(hf);
878
+ vertex_pointer nextv = ori_mesh->to_vertex_handle(hf);
879
+ vertex_pointer thirdv = ori_mesh->to_vertex_handle(ori_mesh->next_halfedge_handle(hf));
880
+ if(SubVidxfromMesh[vh.idx()] >= 0
881
+ && SubVidxfromMesh[nextv.idx()] >= 0
882
+ && SubVidxfromMesh[thirdv.idx()] >= 0)
883
+ {
884
+ std::vector<vertex_pointer> vertices;
885
+ vertices.push_back(sub_mesh->vertex_handle(SubVidxfromMesh[vh.idx()]));
886
+ vertices.push_back(sub_mesh->vertex_handle(SubVidxfromMesh[nextv.idx()]));
887
+ vertices.push_back(sub_mesh->vertex_handle(SubVidxfromMesh[thirdv.idx()]));
888
+ sub_mesh->add_face(vertices);
889
+ added_face[vf_it->idx()] = true;
890
+ }
891
+ }
892
+ }
893
+ }
894
+ }
895
+
896
+ sub_mesh->delete_isolated_vertices();
897
+ sub_mesh->garbage_collection();
898
+ if(sub_mesh->n_vertices() > 0)
899
+ return true;
900
+ else
901
+ return false;
902
+ }
903
+
904
+ //---------------------- print statistics --------------------------
905
+ inline void GeodesicAlgorithmExact::print_statistics()
906
+ {
907
+ GeodesicAlgorithmBase::print_statistics();
908
+
909
+ Scalar memory = sizeof(Interval);
910
+
911
+ //std::cout << std::endl;
912
+ std::cout << "Peak number of intervals on wave-front " << m_windows_peak << std::endl;
913
+ std::cout << "uses about " << memory * m_windows_peak / 1e6 << "MB of memory" << std::endl;
914
+ std::cout << "total interval propagation number " << m_windows_propagation << std::endl;
915
+ std::cout << "maximum interval queue size is " << m_queue_max_size << std::endl;
916
+ }
917
+ } //geodesic
918
+ #endif
cpp/tools/geodesic/geodesic_algorithm_exact_elements.h ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef GEODESIC_ALGORITHM_EXACT_ELEMENTS
2
+ #define GEODESIC_ALGORITHM_EXACT_ELEMENTS
3
+
4
+ #include "../tools/Types.h"
5
+
6
+ namespace geodesic {
7
+
8
+ class Interval;
9
+ class IntervalList;
10
+ typedef Interval* interval_pointer;
11
+ typedef IntervalList* list_pointer;
12
+ typedef OpenMesh::FaceHandle face_pointer;
13
+ typedef OpenMesh::EdgeHandle edge_pointer;
14
+ typedef OpenMesh::VertexHandle vertex_pointer;
15
+ typedef OpenMesh::HalfedgeHandle halfedge_handle;
16
+
17
+ struct Triangle // Components of a face to be propagated
18
+ {
19
+ face_pointer face; // Current Face
20
+
21
+ edge_pointer bottom_edge, // Edges
22
+ left_edge,
23
+ right_edge;
24
+
25
+ vertex_pointer top_vertex, // Vertices
26
+ left_vertex,
27
+ right_vertex;
28
+
29
+ Scalar top_alpha,
30
+ left_alpha,
31
+ right_alpha; // Angles
32
+
33
+ list_pointer left_list,
34
+ right_list; // Lists
35
+ };
36
+
37
+ class Interval //interval of the edge
38
+ {
39
+ public:
40
+
41
+ Interval() {};
42
+ ~Interval() {};
43
+
44
+ Scalar& start() { return m_start; };
45
+ Scalar& stop() { return m_stop; };
46
+ Scalar& d() { return m_d; };
47
+ Scalar& pseudo_x() { return m_pseudo_x; };
48
+ Scalar& pseudo_y() { return m_pseudo_y; };
49
+
50
+ Scalar& sp() { return m_sp; };
51
+
52
+ Scalar& shortest_distance() { return m_shortest_distance; }
53
+
54
+ interval_pointer& next() { return m_next; };
55
+ interval_pointer& previous() { return m_previous; };
56
+
57
+ private:
58
+ Scalar m_start; //initial point of the interval on the edge
59
+ Scalar m_stop;
60
+ Scalar m_d; //distance from the source to the pseudo-source
61
+ Scalar m_pseudo_x; //coordinates of the pseudo-source in the local coordinate system
62
+ Scalar m_pseudo_y; //y-coordinate should be always negative
63
+
64
+ Scalar m_sp; //separating point
65
+
66
+ Scalar m_shortest_distance; //shortest distance from the interval to top_vertex, for numerical precision issue
67
+
68
+ interval_pointer m_next; //pointer to the next interval in the list
69
+ interval_pointer m_previous; //pointer to the previous interval in the list
70
+ };
71
+
72
+ class IntervalList //list of the of intervals of the given edge
73
+ {
74
+ public:
75
+ IntervalList() { /*m_start = NULL; m_edge = NULL;*/ m_sp = -1; m_begin = m_end = NULL; }
76
+ ~IntervalList() {};
77
+
78
+ void clear() { m_begin = m_end = NULL; }
79
+ void initialize(edge_pointer e) { m_edge = e; }
80
+
81
+ vertex_pointer& start_vertex() { return m_start; }
82
+ edge_pointer& edge() { return m_edge; }
83
+
84
+ Scalar& sp() { return m_sp; };
85
+
86
+ Scalar& pseudo_x() { return m_pseudo_x; };
87
+ Scalar& pseudo_y() { return m_pseudo_y; };
88
+
89
+ // List operation
90
+ interval_pointer& begin() { return m_begin; }
91
+
92
+ interval_pointer& end() { return m_end; }
93
+
94
+ bool empty() { return m_begin == NULL; }
95
+
96
+ void push_back(interval_pointer & w)
97
+ {
98
+ if (!m_end)
99
+ {
100
+ w->previous() = NULL;
101
+ w->next() = NULL;
102
+ m_begin = m_end = w;
103
+ }
104
+ else
105
+ {
106
+ w->next() = NULL;
107
+ w->previous() = m_end;
108
+ m_end->next() = w;
109
+ m_end = w;
110
+ }
111
+ }
112
+
113
+ void erase(interval_pointer & w)
114
+ {
115
+ if ((w == m_begin) && (w == m_end))
116
+ {
117
+ m_begin = m_end = NULL;
118
+ }
119
+ else if (w == m_begin)
120
+ {
121
+ m_begin = m_begin->next();
122
+ m_begin->previous() = NULL;
123
+ }
124
+ else if (w == m_end)
125
+ {
126
+ m_end = m_end->previous();
127
+ m_end->next() = NULL;
128
+ }
129
+ else
130
+ {
131
+ w->previous()->next() = w->next();
132
+ w->next()->previous() = w->previous();
133
+ }
134
+ }
135
+
136
+ private:
137
+
138
+ edge_pointer m_edge; //edge that owns this list
139
+ vertex_pointer m_start; //vertex from which the interval list starts
140
+
141
+ interval_pointer m_begin;
142
+ interval_pointer m_end;
143
+
144
+ Scalar m_pseudo_x;
145
+ Scalar m_pseudo_y;
146
+
147
+ Scalar m_sp; //separating point
148
+ };
149
+
150
+ } //geodesic
151
+
152
+ #endif
cpp/tools/geodesic/geodesic_constants_and_simple_functions.h ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef GEODESIC_CONSTANTS
2
+ #define GEODESIC_CONSTANTS
3
+
4
+ // some constants and simple math functions
5
+ #include "../tools/Types.h"
6
+ namespace geodesic{
7
+
8
+ #ifndef M_PI
9
+ #define M_PI 3.14159265358979323846
10
+ #endif
11
+
12
+ Scalar const GEODESIC_INF = 1e100;
13
+
14
+ // statistics
15
+ Scalar m_time_consumed; //how much time does the propagation step takes
16
+ unsigned m_queue_max_size; //used for statistics
17
+ unsigned m_windows_propagation; // how many time a window is propagated
18
+ unsigned m_windows_wavefront; // the number of windows on the wavefront
19
+ unsigned m_windows_peak; // the maximum number of windows, used to calculate the memory
20
+
21
+ // two windows' states after checking
22
+ enum windows_state
23
+ {
24
+ w1_invalid,
25
+ w2_invalid,
26
+ both_valid
27
+ };
28
+
29
+ inline Scalar cos_from_edges(Scalar const a, //compute the cosine of the angle given the lengths of the edges
30
+ Scalar const b,
31
+ Scalar const c)
32
+ {
33
+ assert(a > 1e-50);
34
+ assert(b > 1e-50);
35
+ assert(c > 1e-50);
36
+
37
+ Scalar result = (b * b + c * c - a * a)/(2.0 * b * c);
38
+ result = result>-1.0?result:-1.0;
39
+ return result <1.0 ? result:1.0;
40
+ }
41
+
42
+ inline Scalar angle_from_edges(Scalar const a, //compute the cosine of the angle given the lengths of the edges
43
+ Scalar const b,
44
+ Scalar const c)
45
+ {
46
+ return acos(cos_from_edges(a, b, c));
47
+ }
48
+
49
+ template<class Points, class Faces>
50
+ inline bool read_mesh_from_file(char* filename,
51
+ Points& points,
52
+ Faces& faces,
53
+ std::vector<int> &realIndex,
54
+ int& originalVertNum)
55
+ {
56
+ std::ifstream file(filename);
57
+ assert(file.is_open());
58
+ if(!file.is_open()) return false;
59
+
60
+ char type;
61
+ std::string curLine;
62
+ Scalar coord[3];
63
+ unsigned int vtxIdx[3];
64
+ std::map<std::string, int> mapForDuplicate;
65
+ originalVertNum = 0;
66
+
67
+ while(getline(file, curLine))
68
+ {
69
+ if (curLine.size() < 2) continue;
70
+ if (curLine[0] == 'v' && curLine[1] != 't')
71
+ {
72
+ std::map<std::string, int>::iterator pos = mapForDuplicate.find(curLine);
73
+ if (pos == mapForDuplicate.end())
74
+ {
75
+ int oldSize = mapForDuplicate.size();
76
+ realIndex.push_back(oldSize);
77
+ mapForDuplicate[curLine] = oldSize;
78
+ sscanf(curLine.c_str(), "v %lf %lf %lf", &coord[0], &coord[1], &coord[2]);
79
+ for (int i = 0;i < 3;++i) points.push_back(coord[i]);
80
+ }
81
+ else
82
+ {
83
+ realIndex.push_back(pos->second);
84
+ }
85
+ ++originalVertNum;
86
+ }
87
+ else if (curLine[0] == 'f')
88
+ {
89
+ unsigned tex;
90
+ if (curLine.find('/') != std::string::npos)
91
+ sscanf(curLine.c_str(), "f %d/%d %d/%d %d/%d", &vtxIdx[0], &tex, &vtxIdx[1], &tex, &vtxIdx[2], &tex);
92
+ else
93
+ sscanf(curLine.c_str(), "f %d %d %d", &vtxIdx[0], &vtxIdx[1], &vtxIdx[2]);
94
+
95
+ vtxIdx[0] = realIndex[vtxIdx[0]-1];
96
+ vtxIdx[1] = realIndex[vtxIdx[1]-1];
97
+ vtxIdx[2] = realIndex[vtxIdx[2]-1];
98
+ if (vtxIdx[0] == vtxIdx[1] || vtxIdx[0] == vtxIdx[2] || vtxIdx[1] == vtxIdx[2]) continue;
99
+
100
+ for (int i = 0;i < 3;++i) faces.push_back(vtxIdx[i]);
101
+ }
102
+ }
103
+ file.close();
104
+
105
+ printf("There are %d non-coincidence vertices.\n", points.size() / 3);
106
+
107
+ return true;
108
+ }
109
+
110
+
111
+
112
+ inline void CalculateIntersectionPoint(Scalar X1, Scalar Y1, // compute intersection point of two windows
113
+ Scalar X2, Scalar Y2,
114
+ Scalar X3, Scalar Y3,
115
+ Scalar X4, Scalar Y4,
116
+ Scalar &Intersect_X, Scalar &Intersect_Y)
117
+ {
118
+ Scalar A1, B1, C1, A2, B2, C2;
119
+ A1 = Y2 - Y1;
120
+ B1 = X1 - X2;
121
+ C1 = X2 * Y1 - X1 * Y2;
122
+ A2 = Y4 - Y3;
123
+ B2 = X3 - X4;
124
+ C2 = X4 * Y3 - X3 * Y4;
125
+
126
+ Intersect_X = (B2 * C1 - B1 * C2) / (A2 * B1 - A1 * B2);
127
+ Intersect_Y = (A1 * C2 - A2 * C1) / (A2 * B1 - A1 * B2);
128
+ }
129
+
130
+
131
+ inline bool PointInTriangle(Scalar &X, Scalar &Y, // judge if a point is inside a triangle
132
+ //Scalar Ax, Scalar Ay, // 0, 0
133
+ Scalar &Bx, //Scalar By, // By = 0
134
+ Scalar &Cx, Scalar &Cy)
135
+ {
136
+ Scalar dot00 = Cx * Cx + Cy * Cy;// dot00 = dot(v0, v0)
137
+ Scalar dot01 = Cx * Bx;// dot01 = dot(v0, v1)
138
+ Scalar dot02 = Cx * X + Cy * Y;// dot02 = dot(v0, v2)
139
+ Scalar dot11 = Bx * Bx; // dot11 = dot(v1, v1)
140
+ Scalar dot12 = Bx * X; // dot12 = dot(v1, v2)
141
+
142
+ Scalar invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
143
+ Scalar u = (dot11 * dot02 - dot01 * dot12) * invDenom;
144
+ Scalar v = (dot00 * dot12 - dot01 * dot02) * invDenom;
145
+
146
+ //return (u >= 0) && (v >= 0) && (u + v < 1);
147
+ return (u >= 1e-10) && (v >= 1e-10) && (u + v < 1 - 1e-10);
148
+ }
149
+
150
+
151
+ } //geodesic
152
+
153
+ #endif //GEODESIC_CONSTANTS_20071231
cpp/tools/geodesic/geodesic_memory.h ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef _GEODESIC_MEMORY
2
+ #define _GEODESIC_MEMORY
3
+
4
+ //two fast and simple memory allocators
5
+
6
+ #include "../tools/Types.h"
7
+ #include <memory>
8
+
9
+ namespace geodesic{
10
+
11
+ template<class T> //quickly allocates multiple elements of a given type; no deallocation
12
+ class SimlpeMemoryAllocator
13
+ {
14
+ public:
15
+ typedef T* pointer;
16
+
17
+ SimlpeMemoryAllocator(unsigned block_size = 0,
18
+ unsigned max_number_of_blocks = 0)
19
+ {
20
+ reset(block_size,
21
+ max_number_of_blocks);
22
+ };
23
+
24
+ ~SimlpeMemoryAllocator(){};
25
+
26
+ void reset(unsigned block_size,
27
+ unsigned max_number_of_blocks)
28
+ {
29
+ m_block_size = block_size;
30
+ m_max_number_of_blocks = max_number_of_blocks;
31
+
32
+
33
+ m_current_position = 0;
34
+
35
+ m_storage.reserve(max_number_of_blocks);
36
+ m_storage.resize(1);
37
+ m_storage[0].resize(block_size);
38
+ };
39
+
40
+ pointer allocate(unsigned const n) //allocate n units
41
+ {
42
+ assert(n < m_block_size);
43
+
44
+ if(m_current_position + n >= m_block_size)
45
+ {
46
+ m_storage.push_back( std::vector<T>() );
47
+ m_storage.back().resize(m_block_size);
48
+ m_current_position = 0;
49
+ }
50
+ pointer result = & m_storage.back()[m_current_position];
51
+ m_current_position += n;
52
+
53
+ return result;
54
+ };
55
+ private:
56
+ std::vector<std::vector<T> > m_storage;
57
+ unsigned m_block_size; //size of a single block
58
+ unsigned m_max_number_of_blocks; //maximum allowed number of blocks
59
+ unsigned m_current_position; //first unused element inside the current block
60
+ };
61
+
62
+
63
+ template<class T> //quickly allocates and deallocates single elements of a given type
64
+ class MemoryAllocator
65
+ {
66
+ public:
67
+ typedef T* pointer;
68
+
69
+ MemoryAllocator(unsigned block_size = 1024,
70
+ unsigned max_number_of_blocks = 1024)
71
+ {
72
+ reset(block_size,
73
+ max_number_of_blocks);
74
+ };
75
+
76
+ ~MemoryAllocator(){};
77
+
78
+ void clear()
79
+ {
80
+ reset(m_block_size,
81
+ m_max_number_of_blocks);
82
+ }
83
+
84
+ void reset(unsigned block_size,
85
+ unsigned max_number_of_blocks)
86
+ {
87
+ m_block_size = block_size;
88
+ m_max_number_of_blocks = max_number_of_blocks;
89
+
90
+ assert(m_block_size > 0);
91
+ assert(m_max_number_of_blocks > 0);
92
+
93
+ m_current_position = 0;
94
+
95
+ m_storage.reserve(max_number_of_blocks);
96
+ m_storage.resize(1);
97
+ m_storage[0].resize(block_size);
98
+
99
+ m_deleted.clear();
100
+ m_deleted.reserve(2*block_size);
101
+ };
102
+
103
+ pointer allocate() //allocates single unit of memory
104
+ {
105
+ pointer result;
106
+ if(m_deleted.empty())
107
+ {
108
+ if(m_current_position + 1 >= m_block_size)
109
+ {
110
+ m_storage.push_back( std::vector<T>() );
111
+ m_storage.back().resize(m_block_size);
112
+ m_current_position = 0;
113
+ }
114
+ result = & m_storage.back()[m_current_position];
115
+ ++m_current_position;
116
+ }
117
+ else
118
+ {
119
+ result = m_deleted.back();
120
+ m_deleted.pop_back();
121
+ }
122
+
123
+ return result;
124
+ };
125
+
126
+ void deallocate(pointer p) //allocate n units
127
+ {
128
+ if(m_deleted.size() < m_deleted.capacity())
129
+ {
130
+ m_deleted.push_back(p);
131
+ }
132
+ };
133
+
134
+ private:
135
+ std::vector<std::vector<T> > m_storage;
136
+ unsigned m_block_size; //size of a single block
137
+ unsigned m_max_number_of_blocks; //maximum allowed number of blocks
138
+ unsigned m_current_position; //first unused element inside the current block
139
+
140
+ std::vector<pointer> m_deleted; //pointers to deleted elemets
141
+ };
142
+
143
+
144
+ class OutputBuffer
145
+ {
146
+ public:
147
+ OutputBuffer():
148
+ m_num_bytes(0)
149
+ {}
150
+
151
+ void clear()
152
+ {
153
+ m_num_bytes = 0;
154
+ m_buffer = std::auto_ptr<Scalar>();
155
+ }
156
+
157
+ template<class T>
158
+ T* allocate(unsigned n)
159
+ {
160
+ Scalar wanted = n*sizeof(T);
161
+ if(wanted > m_num_bytes)
162
+ {
163
+ unsigned new_size = (unsigned) ceil(wanted / (Scalar)sizeof(Scalar));
164
+ m_buffer = std::auto_ptr<Scalar>(new Scalar[new_size]);
165
+ m_num_bytes = new_size*sizeof(Scalar);
166
+ }
167
+
168
+ return (T*)m_buffer.get();
169
+ }
170
+
171
+ template <class T>
172
+ T* get()
173
+ {
174
+ return (T*)m_buffer.get();
175
+ }
176
+
177
+ template<class T>
178
+ unsigned capacity()
179
+ {
180
+ return (unsigned)floor((Scalar)m_num_bytes/(Scalar)sizeof(T));
181
+ };
182
+
183
+ private:
184
+
185
+ std::auto_ptr<Scalar> m_buffer;
186
+ unsigned m_num_bytes;
187
+ };
188
+
189
+
190
+ } //geodesic
191
+
192
+ #endif
cpp/tools/median.h ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef MEDIAN_H
2
+ #define MEDIAN_H
3
+ // This file is part of libigl, a simple c++ geometry processing library.
4
+ //
5
+ // Copyright (C) 2013 Alec Jacobson <[email protected]>
6
+ //
7
+ // This Source Code Form is subject to the terms of the Mozilla Public License
8
+ // v. 2.0. If a copy of the MPL was not distributed with this file, You can
9
+ // obtain one at http://mozilla.org/MPL/2.0/.
10
+ #include <Eigen/Dense>
11
+ #include <vector>
12
+ namespace igl
13
+ {
14
+ template <typename DerivedM>
15
+ void matrix_to_list(const Eigen::DenseBase<DerivedM>& M,
16
+ std::vector<typename DerivedM::Scalar> &V)
17
+ {
18
+ using namespace std;
19
+ V.resize(M.size());
20
+ // loop over cols then rows
21
+ for(int j =0; j<M.cols();j++)
22
+ {
23
+ for(int i = 0; i < M.rows();i++)
24
+ {
25
+ V[i+j*M.rows()] = M(i,j);
26
+ }
27
+ }
28
+ }
29
+ // Compute the median of an eigen vector
30
+ //
31
+ // Inputs:
32
+ // V #V list of unsorted values
33
+ // Outputs:
34
+ // m median of those values
35
+ // Returns true on success, false on failure
36
+ template <typename DerivedV, typename mType>
37
+ bool median(
38
+ const Eigen::MatrixBase<DerivedV> & V, mType & m)
39
+ {
40
+ using namespace std;
41
+ if(V.size() == 0)
42
+ {
43
+ return false;
44
+ }
45
+ vector<typename DerivedV::Scalar> vV;
46
+ matrix_to_list(V,vV);
47
+ // http://stackoverflow.com/a/1719155/148668
48
+ size_t n = vV.size()/2;
49
+ nth_element(vV.begin(),vV.begin()+n,vV.end());
50
+ if(vV.size()%2==0)
51
+ {
52
+ nth_element(vV.begin(),vV.begin()+n-1,vV.end());
53
+ m = 0.5*(vV[n]+vV[n-1]);
54
+ }else
55
+ {
56
+ m = vV[n];
57
+ }
58
+ return true;
59
+ }
60
+ }
61
+ #endif
cpp/tools/nanoflann.h ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+ #include <nanoflann.hpp>
3
+ #include <vector>
4
+ #include <Eigen/Dense>
5
+ #include <iostream>
6
+ #include "Types.h"
7
+
8
+ //Collection of the nanoflann-related parts in the two methods
9
+
10
+ namespace nanoflann {
11
+
12
+ /// KD-tree adaptor for working with data directly stored in an Eigen Matrix, without duplicating the data storage.
13
+ /// This code is adapted from the KDTreeEigenMatrixAdaptor class of nanoflann.hpp
14
+ template <class MatrixType, int DIM = -1, class Distance = nanoflann::metric_L2, typename IndexType = int>
15
+ struct KDTreeAdaptor {
16
+ typedef KDTreeAdaptor<MatrixType, DIM, Distance> self_t;
17
+ typedef typename MatrixType::Scalar num_t;
18
+ typedef typename Distance::template traits<num_t, self_t>::distance_t metric_t;
19
+ typedef KDTreeSingleIndexAdaptor< metric_t, self_t, DIM, IndexType> index_t;
20
+
21
+ index_t* index;
22
+ const MatrixType &m_data_matrix;
23
+
24
+ KDTreeAdaptor(const MatrixType &mat, const int leaf_max_size = 10) : m_data_matrix(mat) {
25
+ const size_t dims = mat.rows();
26
+ index = new index_t(dims, *this, nanoflann::KDTreeSingleIndexAdaptorParams(leaf_max_size, dims));
27
+ index->buildIndex();
28
+ }
29
+
30
+ ~KDTreeAdaptor() { delete index; }
31
+
32
+ /// Query for the num_closest closest points to a given point (entered as query_point[0:dim-1]).
33
+ inline void query(const num_t *query_point, const size_t num_closest, IndexType *out_indices, num_t *out_distances_sq) const {
34
+ nanoflann::KNNResultSet<num_t, IndexType> resultSet(num_closest);
35
+ resultSet.init(out_indices, out_distances_sq);
36
+ index->findNeighbors(resultSet, query_point, nanoflann::SearchParams());
37
+ }
38
+
39
+ /// Query for the closest point to a given query.
40
+ inline IndexType closest(const num_t *query_point, num_t& out_distance_sq) const {
41
+ IndexType out_index;
42
+ query(query_point, 1, &out_index, &out_distance_sq);
43
+ return out_index;
44
+ }
45
+
46
+
47
+ inline IndexType closest(const num_t *query_point) const {
48
+ num_t dummy;
49
+ return closest(query_point, dummy); // 调用上面的 2 参数版本
50
+ }
51
+
52
+ /// Radius search
53
+ inline size_t findInRadius(const num_t *query_point, const num_t radius, std::vector<std::pair<IndexType, num_t>>& out_idxdist) const {
54
+ out_idxdist.clear();
55
+ index->radiusSearch(query_point, radius, out_idxdist, nanoflann::SearchParams());
56
+ return out_idxdist.size();
57
+ }
58
+
59
+ const self_t & derived() const { return *this; }
60
+ self_t & derived() { return *this; }
61
+
62
+ inline size_t kdtree_get_point_count() const { return m_data_matrix.cols(); }
63
+
64
+ /// Returns the distance between the vector "p1[0:size-1]" and the data point with index "idx_p2" stored in the class:
65
+ inline num_t kdtree_distance(const num_t *p1, const size_t idx_p2, size_t size) const {
66
+ num_t s = 0;
67
+ for (size_t i = 0; i < size; i++) {
68
+ num_t d = p1[i] - m_data_matrix.coeff(i, idx_p2);
69
+ s += d * d;
70
+ }
71
+ return s;
72
+ }
73
+
74
+ /// Returns the dim'th component of the idx'th point in the class:
75
+ inline num_t kdtree_get_pt(const size_t idx, int dim) const {
76
+ return m_data_matrix.coeff(dim, idx);
77
+ }
78
+
79
+ /// Optional bounding-box computation
80
+ template <class BBOX> bool kdtree_get_bbox(BBOX&) const { return false; }
81
+ };
82
+
83
+
84
+ /// Adapter for std::vector<Eigen::Vector3d>
85
+ struct StdVecOfEigenVector3dRefAdapter {
86
+ public:
87
+ typedef double Scalar; // or float, depending on your Vector3 type
88
+ typedef Eigen::Vector3d Vector3;
89
+
90
+ StdVecOfEigenVector3dRefAdapter(const std::vector<Vector3>& pps) : pts(pps) {};
91
+ const std::vector<Vector3>& pts;
92
+
93
+ inline size_t kdtree_get_point_count() const { return pts.size(); }
94
+
95
+ inline Scalar kdtree_get_pt(const size_t idx, int dim) const {
96
+ if (dim == 0) return pts[idx].x();
97
+ else if (dim == 1) return pts[idx].y();
98
+ else return pts[idx].z();
99
+ }
100
+
101
+ template <class BBOX>
102
+ bool kdtree_get_bbox(BBOX&) const { return false; }
103
+ };
104
+
105
+ } // namespace nanoflann
106
+
107
+
108
+ typedef nanoflann::KDTreeAdaptor<Matrix3X, -1, nanoflann::metric_L2_Simple> KDtree;
cpp/tools/nodeSampler.cpp ADDED
@@ -0,0 +1,638 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //#pragma once
2
+ #include "nodeSampler.h"
3
+ #include "tools.h"
4
+ #include "OmpHelper.h"
5
+ #include "geodesic_algorithm_exact.h"
6
+ #include "nanoflann.h"
7
+
8
+ namespace svr
9
+ {
10
+ // Define helper functions
11
+ static auto square = [](const Scalar argu) { return argu * argu; };
12
+ static auto cube = [](const Scalar argu) { return argu * argu * argu; };
13
+ static auto max = [](const Scalar lhs, const Scalar rhs) { return lhs > rhs ? lhs : rhs; };
14
+
15
+ //------------------------------------------------------------------------
16
+ // Node Sampling based on geodesic distance metric
17
+ //
18
+ // Note that this member function samples nodes along some axis.
19
+ // Each node is not covered by any other node. And distance between each
20
+ // pair of nodes is at least sampling radius.
21
+ //------------------------------------------------------------------------
22
+ // Local geodesic calculation
23
+ Scalar nodeSampler::SampleAndConstuctAxis(Mesh &mesh, Scalar sampleRadiusRatio, sampleAxis axis)
24
+ {
25
+ // Save numbers of vertex and edge
26
+ m_meshVertexNum = mesh.n_vertices();
27
+ m_meshEdgeNum = mesh.n_edges();
28
+ m_mesh = & mesh;
29
+
30
+ // Calculate average edge length of bound mesh
31
+ for (size_t i = 0; i < m_meshEdgeNum; ++i)
32
+ {
33
+ OpenMesh::EdgeHandle eh = mesh.edge_handle(i);
34
+ Scalar edgeLen = mesh.calc_edge_length(eh);
35
+ m_averageEdgeLen += edgeLen;
36
+ }
37
+ m_averageEdgeLen /= m_meshEdgeNum;
38
+
39
+ // Sampling radius is calculated as averageEdgeLen multiplied by sampleRadiusRatio
40
+ m_sampleRadius = sampleRadiusRatio * m_averageEdgeLen;
41
+
42
+ // Reorder mesh vertex along axis
43
+ std::vector<size_t> vertexReorderedAlongAxis(m_meshVertexNum);
44
+ size_t vertexIdx = 0;
45
+ std::generate(vertexReorderedAlongAxis.begin(), vertexReorderedAlongAxis.end(), [&vertexIdx]() -> size_t { return vertexIdx++; });
46
+ std::sort(vertexReorderedAlongAxis.begin(), vertexReorderedAlongAxis.end(), [&mesh, axis](const size_t &lhs, const size_t &rhs) -> bool {
47
+ size_t lhsIdx = lhs;
48
+ size_t rhsIdx = rhs;
49
+ OpenMesh::VertexHandle vhl = mesh.vertex_handle(lhsIdx);
50
+ OpenMesh::VertexHandle vhr = mesh.vertex_handle(rhsIdx);
51
+ Mesh::Point vl = mesh.point(vhl);
52
+ Mesh::Point vr = mesh.point(vhr);
53
+ return vl[axis] > vr[axis];
54
+ });
55
+
56
+ // Sample nodes using radius of m_sampleRadius
57
+ size_t firstVertexIdx = vertexReorderedAlongAxis[0];
58
+ VertexNodeIdx.resize(m_meshVertexNum);
59
+ VertexNodeIdx.setConstant(-1);
60
+ VertexNodeIdx[firstVertexIdx] = 0;
61
+ size_t cur_node_idx = 0;
62
+
63
+ m_vertexGraph.resize(m_meshVertexNum);
64
+ VectorX weight_sum = VectorX::Zero(m_meshVertexNum);
65
+
66
+ for (auto &vertexIdx : vertexReorderedAlongAxis)
67
+ {
68
+ if(VertexNodeIdx[vertexIdx] < 0 && m_vertexGraph.at(vertexIdx).empty())
69
+ {
70
+ m_nodeContainer.emplace_back(cur_node_idx, vertexIdx);
71
+ VertexNodeIdx[vertexIdx] = cur_node_idx;
72
+
73
+ std::vector<size_t> neighbor_verts;
74
+ geodesic::GeodesicAlgorithmExact geoalg(&mesh, vertexIdx, m_sampleRadius);
75
+ geoalg.propagate(vertexIdx, neighbor_verts);
76
+ for(size_t i = 0; i < neighbor_verts.size(); i++)
77
+ {
78
+ int neighIdx = neighbor_verts[i];
79
+ Scalar geodist = mesh.data(mesh.vertex_handle(neighIdx)).geodesic_distance;
80
+ if(geodist < m_sampleRadius)
81
+ {
82
+ Scalar weight = std::pow(1-std::pow(geodist/m_sampleRadius, 2), 3);
83
+ m_vertexGraph.at(neighIdx).emplace(std::pair<int, Scalar>(cur_node_idx, weight));
84
+ weight_sum[neighIdx] += weight;
85
+ }
86
+ }
87
+ cur_node_idx++;
88
+ }
89
+ }
90
+
91
+ m_nodeGraph.resize(cur_node_idx);
92
+ for (auto &vertexIdx : vertexReorderedAlongAxis)
93
+ {
94
+ for(auto &node: m_vertexGraph[vertexIdx])
95
+ {
96
+ size_t nodeIdx = node.first;
97
+ for(auto &neighNode: m_vertexGraph[vertexIdx])
98
+ {
99
+ size_t neighNodeIdx = neighNode.first;
100
+ if(nodeIdx != neighNodeIdx)
101
+ {
102
+ m_nodeGraph.at(nodeIdx).emplace(std::pair<int, Scalar>(neighNodeIdx, 1.0));
103
+ }
104
+ }
105
+ m_vertexGraph.at(vertexIdx).at(nodeIdx) /= weight_sum[vertexIdx];
106
+ }
107
+ }
108
+ return m_sampleRadius;
109
+ }
110
+
111
+ //------------------------------------------------------------------------
112
+ // Node Sampling based on geodesic distance metric
113
+ //
114
+ // Note that this member function samples nodes along some axis.
115
+ // Each node is not covered by any other node. And distance between each
116
+ // pair of nodes is at least sampling radius.
117
+ //------------------------------------------------------------------------
118
+ // Local geodesic calculation
119
+ Scalar nodeSampler::SampleAndConstuct(Mesh &mesh, Scalar sampleRadiusRatio, const Matrix3X & src_points)
120
+ {
121
+ // Save numbers of vertex and edge
122
+ m_meshVertexNum = mesh.n_vertices();
123
+ m_meshEdgeNum = mesh.n_edges();
124
+ m_mesh = & mesh;
125
+
126
+
127
+ // Calculate average edge length of bound mesh
128
+ for (size_t i = 0; i < m_meshEdgeNum; ++i)
129
+ {
130
+ OpenMesh::EdgeHandle eh = mesh.edge_handle(i);
131
+ Scalar edgeLen = mesh.calc_edge_length(eh);
132
+ m_averageEdgeLen += edgeLen;
133
+ }
134
+ m_averageEdgeLen /= m_meshEdgeNum;
135
+
136
+ // Sampling radius is calculated as averageEdgeLen multiplied by sampleRadiusRatio
137
+ m_sampleRadius = sampleRadiusRatio * m_averageEdgeLen;
138
+
139
+ // de-mean
140
+ Vector3 means = src_points.rowwise().mean();
141
+ Matrix3X demean_src_points = src_points.colwise() - means;
142
+ Matrix33 covariance = demean_src_points * demean_src_points.transpose();
143
+
144
+ Eigen::SelfAdjointEigenSolver<Matrix33> eigensolver(covariance);
145
+ Vector3 eigen_values = eigensolver.eigenvalues();
146
+ int max_idx = 2;
147
+ if(eigen_values[0] > eigen_values[max_idx]) max_idx = 0;
148
+ if(eigen_values[1] > eigen_values[max_idx]) max_idx = 1;
149
+
150
+ Vector3 main_axis = eigensolver.eigenvectors().col(max_idx);
151
+
152
+ VectorX projection = demean_src_points.transpose() * main_axis;
153
+
154
+ projection_indices.resize(projection.size());
155
+ projection_indices.setLinSpaced(projection.size(), 0, projection.size() - 1);
156
+
157
+ auto compare = [&projection](int i, int j) {
158
+ return projection(i) < projection(j);
159
+ };
160
+
161
+ std::sort(projection_indices.data(), projection_indices.data() + projection_indices.size(), compare);
162
+
163
+
164
+ // Sample nodes using radius of m_sampleRadius
165
+ size_t firstVertexIdx = projection_indices[0];
166
+ VertexNodeIdx.resize(m_meshVertexNum);
167
+ VertexNodeIdx.setConstant(-1);
168
+ VertexNodeIdx[firstVertexIdx] = 0;
169
+ size_t cur_node_idx = 0;
170
+
171
+ m_vertexGraph.resize(m_meshVertexNum);
172
+ VectorX weight_sum = VectorX::Zero(m_meshVertexNum);
173
+
174
+ for(int idx = 0; idx < projection_indices.size(); idx++)
175
+ {
176
+ int vertexIdx = projection_indices[idx];
177
+ if(VertexNodeIdx[vertexIdx] < 0 && m_vertexGraph.at(vertexIdx).empty())
178
+ {
179
+ m_nodeContainer.emplace_back(cur_node_idx, vertexIdx);
180
+ VertexNodeIdx[vertexIdx] = cur_node_idx;
181
+
182
+ std::vector<size_t> neighbor_verts;
183
+ geodesic::GeodesicAlgorithmExact geoalg(&mesh, vertexIdx, m_sampleRadius);
184
+ geoalg.propagate(vertexIdx, neighbor_verts);
185
+ for(size_t i = 0; i < neighbor_verts.size(); i++)
186
+ {
187
+ int neighIdx = neighbor_verts[i];
188
+ Scalar geodist = mesh.data(mesh.vertex_handle(neighIdx)).geodesic_distance;
189
+ if(geodist < m_sampleRadius)
190
+ {
191
+ Scalar weight = std::pow(1-std::pow(geodist/m_sampleRadius, 2), 3);
192
+ m_vertexGraph.at(neighIdx).emplace(std::pair<int, Scalar>(cur_node_idx, weight));
193
+ weight_sum[neighIdx] += weight;
194
+ }
195
+ }
196
+ cur_node_idx++;
197
+ }
198
+ }
199
+
200
+ m_nodeGraph.resize(cur_node_idx);
201
+ for(int idx = 0; idx < projection_indices.size(); idx++)
202
+ {
203
+ int vertexIdx = projection_indices[idx];
204
+ for(auto &node: m_vertexGraph[vertexIdx])
205
+ {
206
+ size_t nodeIdx = node.first;
207
+ for(auto &neighNode: m_vertexGraph[vertexIdx])
208
+ {
209
+ size_t neighNodeIdx = neighNode.first;
210
+ if(nodeIdx != neighNodeIdx)
211
+ {
212
+ m_nodeGraph.at(nodeIdx).emplace(std::pair<int, Scalar>(neighNodeIdx, 1.0));
213
+ }
214
+ }
215
+ m_vertexGraph.at(vertexIdx).at(nodeIdx) /= weight_sum[vertexIdx];
216
+ }
217
+ }
218
+
219
+
220
+
221
+ // // check neighbors
222
+ // for(int nodeIdx = 0; nodeIdx < cur_node_idx; nodeIdx++)
223
+ // {
224
+ // int num_neighbors = m_nodeGraph[nodeIdx].size();
225
+ // // for (auto &eachNeighbor : m_nodeGraph[nodeIdx])
226
+ // // {
227
+ // // num_neighbors++;
228
+ // // }
229
+ // if(num_neighbors<2)
230
+ // {
231
+
232
+ // VectorX dists(cur_node_idx);
233
+ // #pragma omp parallel for
234
+ // for(int ni = 0; ni < cur_node_idx; ni++)
235
+ // {
236
+ // int vidx0 = getNodeVertexIdx(nodeIdx);
237
+ // int vidx1 = getNodeVertexIdx(ni);
238
+ // Scalar dist = (src_points.col(vidx0) - src_points.col(vidx1)).squaredNorm();
239
+ // dists[ni] = dist;
240
+ // }
241
+ // dists[nodeIdx] = 1e10;
242
+ // for(int k = 0; k < 6; k++)
243
+ // {
244
+ // int min_idx = -1;
245
+ // dists.minCoeff(&min_idx);
246
+ // m_nodeGraph.at(nodeIdx).emplace(std::pair<int, Scalar>(min_idx, 1.0));
247
+ // dists[min_idx] = 1e10;
248
+ // }
249
+ // }
250
+ // }
251
+ // Eigen::VectorXi vnn(cur_node_idx);
252
+ // // check vertex neighbors
253
+ // for(int vidx = 0; vidx < cur_node_idx; vidx++)
254
+ // {
255
+ // // std::cout << "vidx.neighbor_node = " << m_vertexGraph.at(vidx).size() << std::endl;
256
+ // vnn[vidx] = m_nodeGraph.at(vidx).size();
257
+ // }
258
+ // std::cout << "v neighbor max = " << vnn.maxCoeff() << " min = " << vnn.minCoeff() << std::endl;
259
+ return m_sampleRadius;
260
+ }
261
+
262
+
263
+ Scalar nodeSampler::SampleAndConstuctForSrcPoints(Mesh &mesh, Scalar sampleRadiusRatio, const Matrix3X & src_points, const Eigen::MatrixXi & src_knn_indices)
264
+ {
265
+ // Save numbers of vertex and edge
266
+ m_meshVertexNum = src_points.cols();
267
+ int knn_num_neighbor = src_knn_indices.rows();
268
+ m_meshEdgeNum = m_meshVertexNum*knn_num_neighbor;
269
+ m_mesh = & mesh;
270
+
271
+ // Calculate average edge length of bound mesh
272
+ for (size_t i = 0; i < m_meshVertexNum; ++i)
273
+ {
274
+ for(size_t j = 0; j < knn_num_neighbor; j++)
275
+ {
276
+ Scalar edgeLen = (src_points.col(i) - src_points.col(src_knn_indices(j, i))).norm();
277
+ m_averageEdgeLen += edgeLen;
278
+ }
279
+ }
280
+ m_averageEdgeLen /= m_meshEdgeNum;
281
+
282
+
283
+ // Sampling radius is calculated as averageEdgeLen multiplied by sampleRadiusRatio
284
+ m_sampleRadius = sampleRadiusRatio * m_averageEdgeLen;
285
+
286
+ // de-mean
287
+ Vector3 means = src_points.rowwise().mean();
288
+ Matrix3X demean_src_points = src_points.colwise() - means;
289
+ Matrix33 covariance = demean_src_points * demean_src_points.transpose();
290
+
291
+ Eigen::SelfAdjointEigenSolver<Matrix33> eigensolver(covariance);
292
+ Vector3 eigen_values = eigensolver.eigenvalues();
293
+ int max_idx = 2;
294
+ if(eigen_values[0] > eigen_values[max_idx]) max_idx = 0;
295
+ if(eigen_values[1] > eigen_values[max_idx]) max_idx = 1;
296
+
297
+ Vector3 main_axis = eigensolver.eigenvectors().col(max_idx);
298
+
299
+ VectorX projection = demean_src_points.transpose() * main_axis;
300
+
301
+ projection_indices.resize(projection.size());
302
+ projection_indices.setLinSpaced(projection.size(), 0, projection.size() - 1);
303
+
304
+ auto compare = [&projection](int i, int j) {
305
+ return projection(i) < projection(j);
306
+ };
307
+
308
+ std::sort(projection_indices.data(), projection_indices.data() + projection_indices.size(), compare);
309
+
310
+
311
+ // Sample nodes using radius of m_sampleRadius
312
+ size_t firstVertexIdx = projection_indices[0];
313
+ VertexNodeIdx.resize(m_meshVertexNum);
314
+ VertexNodeIdx.setConstant(-1);
315
+ VertexNodeIdx[firstVertexIdx] = 0;
316
+ size_t cur_node_idx = 0;
317
+
318
+ m_vertexGraph.resize(m_meshVertexNum);
319
+ VectorX weight_sum = VectorX::Zero(m_meshVertexNum);
320
+
321
+ for(int idx = 0; idx < projection_indices.size(); idx++)
322
+ {
323
+ int vertexIdx = projection_indices[idx];
324
+ if(VertexNodeIdx[vertexIdx] < 0 && m_vertexGraph.at(vertexIdx).empty())
325
+ {
326
+ m_nodeContainer.emplace_back(cur_node_idx, vertexIdx);
327
+ VertexNodeIdx[vertexIdx] = cur_node_idx;
328
+
329
+ std::queue<size_t> neighbor_verts;
330
+ neighbor_verts.push(vertexIdx);
331
+ Eigen::VectorXi visited(m_meshVertexNum);
332
+ visited.setZero();
333
+ visited[vertexIdx] = 1;
334
+ while(!neighbor_verts.empty())
335
+ {
336
+ size_t vidx = neighbor_verts.front();
337
+ neighbor_verts.pop();
338
+ Scalar dist = (src_points.col(vertexIdx) - src_points.col(vidx)).norm();
339
+
340
+ if(dist < m_sampleRadius)
341
+ {
342
+ Scalar weight = std::pow(1-std::pow(dist/m_sampleRadius, 2), 3);
343
+ m_vertexGraph.at(vidx).emplace(std::pair<int, Scalar>(cur_node_idx, weight));
344
+ weight_sum[vidx] += weight;
345
+ for(int j = 0; j < knn_num_neighbor; j++)
346
+ {
347
+ int neighbor_vidx = src_knn_indices(j, vidx);
348
+ if(visited[neighbor_vidx]==0)
349
+ {
350
+ neighbor_verts.push(neighbor_vidx);
351
+ visited[neighbor_vidx]= 1;
352
+ }
353
+ }
354
+ }
355
+ }
356
+
357
+ cur_node_idx++;
358
+ }
359
+ }
360
+
361
+ m_nodeGraph.resize(cur_node_idx);
362
+ for(int idx = 0; idx < projection_indices.size(); idx++)
363
+ {
364
+ int vertexIdx = projection_indices[idx];
365
+ for(auto &node: m_vertexGraph[vertexIdx])
366
+ {
367
+ size_t nodeIdx = node.first;
368
+ for(auto &neighNode: m_vertexGraph[vertexIdx])
369
+ {
370
+ size_t neighNodeIdx = neighNode.first;
371
+ if(nodeIdx != neighNodeIdx)
372
+ {
373
+ m_nodeGraph.at(nodeIdx).emplace(std::pair<int, Scalar>(neighNodeIdx, 1.0));
374
+ }
375
+ }
376
+ m_vertexGraph.at(vertexIdx).at(nodeIdx) /= weight_sum[vertexIdx];
377
+ }
378
+ }
379
+
380
+ // // check neighbors
381
+ // for(int nodeIdx = 0; nodeIdx < cur_node_idx; nodeIdx++)
382
+ // {
383
+ // int num_neighbors = 0;
384
+ // for (auto &eachNeighbor : m_nodeGraph[nodeIdx])
385
+ // {
386
+ // num_neighbors++;
387
+ // }
388
+ // if(num_neighbors==0)
389
+ // {
390
+
391
+ // VectorX dists(cur_node_idx);
392
+ // #pragma omp parallel for
393
+ // for(int ni = 0; ni < cur_node_idx; ni++)
394
+ // {
395
+ // int vidx0 = getNodeVertexIdx(nodeIdx);
396
+ // int vidx1 = getNodeNeighborSize(ni);
397
+ // Scalar dist = (src_points.col(vidx0) - src_points.col(vidx1)).squaredNorm();
398
+ // dists[ni] = dist;
399
+ // }
400
+ // dists[nodeIdx] = 1e10;
401
+ // int min_idx = -1;
402
+ // dists.minCoeff(&min_idx);
403
+ // m_nodeGraph.at(nodeIdx).emplace(std::pair<int, Scalar>(min_idx, 1.0));
404
+ // }
405
+ // }
406
+ return m_sampleRadius;
407
+ }
408
+
409
+
410
+ Scalar nodeSampler::SampleAndConstuctFPS(Mesh &mesh, Scalar sampleRadiusRatio, const Matrix3X & src_points, const Eigen::MatrixXi & src_knn_indices, int num_vn, int num_nn)
411
+ {
412
+ m_meshVertexNum = src_points.cols();
413
+ int knn_num_neighbor = src_knn_indices.rows();
414
+ m_meshEdgeNum = m_meshVertexNum*knn_num_neighbor;
415
+ m_mesh = & mesh;
416
+
417
+ // Calculate average edge length of bound mesh
418
+ for (size_t i = 0; i < m_meshVertexNum; ++i)
419
+ {
420
+ for(size_t j = 0; j < knn_num_neighbor; j++)
421
+ {
422
+ Scalar edgeLen = (src_points.col(i) - src_points.col(src_knn_indices(j, i))).norm();
423
+ m_averageEdgeLen += edgeLen;
424
+ }
425
+ }
426
+ m_averageEdgeLen /= m_meshEdgeNum;
427
+
428
+ // Sampling radius is calculated as averageEdgeLen multiplied by sampleRadiusRatio
429
+ m_sampleRadius = sampleRadiusRatio * m_averageEdgeLen;
430
+
431
+ // start points
432
+ size_t startIndex = 0;
433
+ // FPS to get sampling points in align term
434
+ VectorX minDistances(m_meshVertexNum);
435
+ minDistances.setConstant(std::numeric_limits<Scalar>::max());
436
+ minDistances[startIndex] = 0;
437
+ m_nodeContainer.emplace_back(startIndex, 0);
438
+
439
+ Scalar minimal_dist = 1e10;
440
+ int cur_node_idx = 1;
441
+ // repeat select farthest points
442
+ while (minimal_dist > m_sampleRadius) {
443
+ // calculate the distance between each point with the sampling points set.
444
+ #pragma omp parallel for
445
+ for (size_t i = 0; i < m_meshVertexNum; ++i) {
446
+ if(i==startIndex)
447
+ continue;
448
+
449
+ Scalar dist = (src_points.col(startIndex) - src_points.col(i)).norm();
450
+ if(dist < minDistances[i])
451
+ minDistances[i] = dist;
452
+ }
453
+
454
+ // choose farthest point
455
+ int maxDistanceIndex;
456
+ minimal_dist = minDistances.maxCoeff(&maxDistanceIndex);
457
+ minDistances[maxDistanceIndex] = 0;
458
+
459
+ // add the farthest point into the sampling points set.
460
+ startIndex = maxDistanceIndex;
461
+ m_nodeContainer.emplace_back(cur_node_idx, startIndex);
462
+ cur_node_idx++;
463
+ }
464
+
465
+ Matrix3X node_positions(3, cur_node_idx);
466
+ for(int i = 0; i < cur_node_idx; i++)
467
+ {
468
+ int vidx = getNodeVertexIdx(i);
469
+ node_positions.col(i) = src_points.col(vidx);
470
+ }
471
+ KDtree node_tree(node_positions);
472
+
473
+ // For each vertex, find num_vn-closest nodes
474
+ m_vertexGraph.resize(m_meshVertexNum);
475
+ VectorX weight_sum(m_meshVertexNum);
476
+ weight_sum.setZero();
477
+ #pragma omp parallel for
478
+ for(int vidx = 0; vidx < m_meshVertexNum; vidx++)
479
+ {
480
+ std::vector<int> out_indices(num_vn);
481
+ std::vector<Scalar> out_dists(num_vn);
482
+ node_tree.query(src_points.col(vidx).data(), num_vn, out_indices.data(), out_dists.data());
483
+ for(int k = 0; k < num_vn; k++)
484
+ {
485
+ Scalar weight = std::pow(1-std::pow(out_dists[k]/m_sampleRadius, 2), 3);
486
+ m_vertexGraph.at(vidx).emplace(std::pair<int, Scalar>(out_indices[k], weight));
487
+ weight_sum[vidx] += weight;
488
+ }
489
+ }
490
+
491
+ #pragma omp parallel for
492
+ for(int vidx = 0; vidx < m_meshVertexNum; vidx++)
493
+ {
494
+ for(auto &neighNode: m_vertexGraph[vidx])
495
+ {
496
+ size_t neighNodeIdx = neighNode.first;
497
+ m_vertexGraph.at(vidx).at(neighNodeIdx) /= weight_sum[vidx];
498
+ }
499
+ }
500
+
501
+
502
+ // For each node, find num_nn-closest nodes
503
+ m_nodeGraph.resize(cur_node_idx);
504
+ #pragma omp parallel for
505
+ for(int nidx = 0; nidx < cur_node_idx; nidx++)
506
+ {
507
+ std::vector<int> out_indices(num_nn+1);
508
+ std::vector<Scalar> out_dists(num_nn+1);
509
+ int vidx = getNodeVertexIdx(nidx);
510
+ node_tree.query(src_points.col(vidx).data(), num_nn+1, out_indices.data(), out_dists.data());
511
+ for(int k = 0; k < num_nn; k++)
512
+ {
513
+ m_nodeGraph.at(nidx).emplace(std::pair<int, Scalar>(out_indices[k+1], 1.0));
514
+ }
515
+ }
516
+ return m_sampleRadius;
517
+ }
518
+
519
+
520
+
521
+
522
+
523
+ void nodeSampler::initWeight(RowMajorSparseMatrix& matPV, VectorX & matP, RowMajorSparseMatrix& matB, VectorX& matD, VectorX& smoothw)
524
+ {
525
+ Timer time;
526
+ std::vector<Eigen::Triplet<Scalar>> coeff;
527
+ matP.setZero();
528
+ Eigen::VectorXi nonzero_num = Eigen::VectorXi::Zero(m_mesh->n_vertices());
529
+ // data coeff
530
+ for (size_t vertexIdx = 0; vertexIdx < m_meshVertexNum; ++vertexIdx)
531
+ {
532
+ Mesh::Point vi = m_mesh->point(m_mesh->vertex_handle(vertexIdx));
533
+ for (auto &eachNeighbor : m_vertexGraph[vertexIdx])
534
+ {
535
+ size_t nodeIdx = eachNeighbor.first;
536
+ Scalar weight = m_vertexGraph.at(vertexIdx).at(nodeIdx);
537
+ Mesh::Point pj = m_mesh->point(m_mesh->vertex_handle(getNodeVertexIdx(nodeIdx)));
538
+
539
+ for (int k = 0; k < 3; k++)
540
+ {
541
+ coeff.push_back(Eigen::Triplet<Scalar>(3 * vertexIdx + k, nodeIdx * 12 + k, weight * (vi[0] - pj[0])));
542
+ coeff.push_back(Eigen::Triplet<Scalar>(3 * vertexIdx + k, nodeIdx * 12 + k + 3, weight * (vi[1] - pj[1])));
543
+ coeff.push_back(Eigen::Triplet<Scalar>(3 * vertexIdx + k, nodeIdx * 12 + k + 6, weight * (vi[2] - pj[2])));
544
+ coeff.push_back(Eigen::Triplet<Scalar>(3 * vertexIdx + k, nodeIdx * 12 + k + 9, weight * 1.0));
545
+ }
546
+
547
+ matP[vertexIdx * 3] += weight * pj[0];
548
+ matP[vertexIdx * 3 + 1] += weight * pj[1];
549
+ matP[vertexIdx * 3 + 2] += weight * pj[2];
550
+ }
551
+ nonzero_num[vertexIdx] = m_vertexGraph[vertexIdx].size();
552
+ }
553
+ matPV.setFromTriplets(coeff.begin(), coeff.end());
554
+
555
+
556
+ // smooth coeff
557
+ coeff.clear();
558
+ int max_edge_num = nodeSize() * (nodeSize()-1);
559
+ matB.resize(max_edge_num * 3, 12 * nodeSize());
560
+ matD.resize(max_edge_num * 3);
561
+ smoothw.resize(max_edge_num*3);
562
+ smoothw.setZero();
563
+ matD.setZero();
564
+ int edge_id = 0;
565
+
566
+ for (size_t nodeIdx = 0; nodeIdx < m_nodeContainer.size(); ++nodeIdx)
567
+ {
568
+ size_t vIdx0 = getNodeVertexIdx(nodeIdx);
569
+ Mesh::VertexHandle vh0 = m_mesh->vertex_handle(vIdx0);
570
+ Mesh::Point v0 = m_mesh->point(vh0);
571
+
572
+ for (auto &eachNeighbor : m_nodeGraph[nodeIdx])
573
+ {
574
+ size_t neighborIdx = eachNeighbor.first;
575
+ size_t vIdx1 = getNodeVertexIdx(neighborIdx);
576
+ Mesh::Point v1 = m_mesh->point(m_mesh->vertex_handle(vIdx1));
577
+ Mesh::Point dv = v0 - v1;
578
+ int k = edge_id;
579
+
580
+ for (int t = 0; t < 3; t++)
581
+ {
582
+ coeff.push_back(Eigen::Triplet<Scalar>(k * 3 + t, neighborIdx * 12 + t, dv[0]));
583
+ coeff.push_back(Eigen::Triplet<Scalar>(k * 3 + t, neighborIdx * 12 + t + 3, dv[1]));
584
+ coeff.push_back(Eigen::Triplet<Scalar>(k * 3 + t, neighborIdx * 12 + t + 6, dv[2]));
585
+ coeff.push_back(Eigen::Triplet<Scalar>(k * 3 + t, neighborIdx * 12 + t + 9, 1.0));
586
+ coeff.push_back(Eigen::Triplet<Scalar>(k * 3 + t, nodeIdx * 12 + t + 9, -1.0));
587
+ }
588
+
589
+ Scalar dist = dv.norm();
590
+ if(dist > 0)
591
+ {
592
+ smoothw[k * 3] = smoothw[k * 3 + 1] = smoothw[k * 3 + 2] = 1.0 / dist;
593
+ }
594
+ else
595
+ {
596
+ //smoothw[k * 3] = 0.0;
597
+ std::cout << "node repeat";
598
+ exit(1);
599
+ }
600
+ matD[k * 3] = dv[0];
601
+ matD[k * 3 + 1] = dv[1];
602
+ matD[k * 3 + 2] = dv[2];
603
+ edge_id++;
604
+ }
605
+ }
606
+ matB.setFromTriplets(coeff.begin(), coeff.end());
607
+ matD.conservativeResize(edge_id*3);
608
+ matB.conservativeResize(edge_id*3, matPV.cols());
609
+ smoothw.conservativeResize(edge_id*3);
610
+ smoothw *= edge_id/(smoothw.sum()/3.0);
611
+ }
612
+
613
+
614
+ void nodeSampler::print_nodes(Mesh & mesh, std::string file_path)
615
+ {
616
+ std::string namev = file_path + "nodes.obj";
617
+ std::ofstream out1(namev);
618
+ for (size_t i = 0; i < m_nodeContainer.size(); i++)
619
+ {
620
+ int vexid = m_nodeContainer[i].second;
621
+ out1 << "v " << mesh.point(mesh.vertex_handle(vexid))[0] << " " << mesh.point(mesh.vertex_handle(vexid))[1]
622
+ << " " << mesh.point(mesh.vertex_handle(vexid))[2] << std::endl;
623
+ }
624
+ Eigen::VectorXi nonzero_num = Eigen::VectorXi::Zero(m_nodeContainer.size());
625
+ for (size_t nodeIdx = 0; nodeIdx < m_nodeContainer.size(); ++nodeIdx)
626
+ {
627
+ for (auto &eachNeighbor : m_nodeGraph[nodeIdx])
628
+ {
629
+ size_t neighborIdx = eachNeighbor.first;
630
+ out1 << "l " << nodeIdx+1 << " " << neighborIdx+1 << std::endl;
631
+ }
632
+ nonzero_num[nodeIdx] = m_nodeGraph[nodeIdx].size();
633
+ }
634
+ std::cout << "node neighbor min = " << nonzero_num.minCoeff() << " max = "
635
+ << nonzero_num.maxCoeff() << " average = " << nonzero_num.mean() << std::endl;
636
+ out1.close();
637
+ }
638
+ }
cpp/tools/nodeSampler.h ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef NODESAMPLER_H
2
+ #define NODESAMPLER_H
3
+ #include "Types.h"
4
+
5
+ namespace svr
6
+ {
7
+ //------------------------------------------------------------------------
8
+ // Define neighborIter class
9
+ //------------------------------------------------------------------------
10
+ class neighborIter
11
+ {
12
+ public:
13
+ neighborIter(const std::map<size_t, Scalar> &nodeNeighbors)
14
+ {
15
+ m_neighborIter = nodeNeighbors.begin();
16
+ m_neighborEnd = nodeNeighbors.end();
17
+ }
18
+
19
+ neighborIter& operator++()
20
+ {
21
+ if (m_neighborIter != m_neighborEnd)
22
+ ++m_neighborIter;
23
+ return *this;
24
+ }
25
+
26
+ neighborIter operator++(int)
27
+ {
28
+ neighborIter tempIter(*this);
29
+ ++*this;
30
+ return tempIter;
31
+ }
32
+
33
+ const std::pair<const size_t, Scalar>& operator*() { return *m_neighborIter; }
34
+ std::map<size_t, Scalar>::const_iterator operator->() { return m_neighborIter; }
35
+ bool is_valid() { return m_neighborIter != m_neighborEnd; }
36
+ size_t getIndex() { return m_neighborIter->first; }
37
+ Scalar getWeight() { return m_neighborIter->second; }
38
+
39
+ private:
40
+ std::map<size_t, Scalar>::const_iterator m_neighborIter;
41
+ std::map<size_t, Scalar>::const_iterator m_neighborEnd;
42
+ };
43
+
44
+ //------------------------------------------------------------------------
45
+ // Define node sampling class
46
+ //------------------------------------------------------------------------
47
+ class nodeSampler
48
+ {
49
+ public:
50
+ enum sampleAxis { X_AXIS, Y_AXIS, Z_AXIS };
51
+ nodeSampler() {};
52
+
53
+ // return sample radius
54
+ Scalar SampleAndConstuct(Mesh &mesh, Scalar sampleRadiusRatio, const Matrix3X & src_points);
55
+ Scalar SampleAndConstuctAxis(Mesh &mesh, Scalar sampleRadiusRatio, sampleAxis axis);
56
+
57
+ Scalar SampleAndConstuctForSrcPoints(Mesh &mesh, Scalar sampleRadiusRatio, const Matrix3X & src_points, const Eigen::MatrixXi & src_knn_indices);
58
+ Scalar SampleAndConstuctFPS(Mesh &mesh, Scalar sampleRadiusRatio, const Matrix3X & src_points, const Eigen::MatrixXi & src_knn_indices, int num_vn, int num_nn);
59
+
60
+
61
+ void updateWeight(Mesh &mesh);
62
+ void constructGraph(bool is_uniform);
63
+
64
+ size_t nodeSize() const { return m_nodeContainer.size(); }
65
+
66
+ neighborIter getNodeNodeIter(size_t nodeIdx) const { return neighborIter(m_nodeGraph[nodeIdx]); }
67
+ neighborIter getVertexNodeIter(size_t vertexIdx) const { return neighborIter(m_vertexGraph[vertexIdx]); }
68
+ size_t getNodeVertexIdx(size_t nodeIdx) const { return m_nodeContainer.at(nodeIdx).second; }
69
+
70
+ size_t getVertexNeighborSize(size_t vertexIdx) const { return m_vertexGraph.at(vertexIdx).size(); }
71
+ size_t getNodeNeighborSize(size_t nodeIdx) const { return m_nodeGraph.at(nodeIdx).size(); }
72
+ void initWeight(RowMajorSparseMatrix& matPV, VectorX & matP,
73
+ RowMajorSparseMatrix& matB, VectorX& matD, VectorX& smoothw);
74
+ void print_nodes(Mesh & mesh, std::string file_path);
75
+
76
+ private:
77
+ size_t m_meshVertexNum = 0;
78
+ size_t m_meshEdgeNum = 0;
79
+ Scalar m_averageEdgeLen = 0.0f;
80
+ Scalar m_sampleRadius = 0.0f;
81
+ std::vector<Scalar> non_unisamples_Radius;
82
+ Mesh * m_mesh;
83
+
84
+ public:
85
+ std::vector<std::pair<size_t, size_t>> m_nodeContainer;
86
+ std::vector<std::map<size_t, Scalar>> m_vertexGraph; // vertex node graph
87
+ std::vector<std::map<size_t, Scalar>> m_nodeGraph;
88
+ std::vector<VectorX> m_geoDistContainer;
89
+ Eigen::VectorXi VertexNodeIdx;
90
+ Eigen::VectorXi projection_indices;
91
+
92
+ };
93
+ }
94
+ #endif
cpp/tools/parameters.h ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef PARAMETERS_H
2
+ #define PARAMETERS_H
3
+
4
+ #include "Types.h"
5
+
6
+ //Parameter repository for different methods
7
+
8
+ // parameters for FRICP
9
+ namespace ICP{
10
+ struct Parameters {
11
+ double p; /// paramter of the robust function/// para k
12
+ int max_icp; /// max ICP iteration
13
+ double stop; /// stopping criteria
14
+ std::string out_path;
15
+ int anderson_m;
16
+ MatrixXX init_trans;
17
+ MatrixXX gt_trans;
18
+ bool has_groundtruth;
19
+ double convergence_energy;
20
+ double convergence_gt_mse;
21
+ MatrixXX res_trans;
22
+ double nu_begin_k;
23
+ double nu_end_k;
24
+ bool use_init;
25
+ double nu_alpha;
26
+ Parameters() :
27
+ p(0.1),
28
+ max_icp(100),
29
+ stop(1e-5),
30
+ anderson_m(5),
31
+ has_groundtruth(false),
32
+ convergence_energy(0.0),
33
+ convergence_gt_mse(0.0),
34
+ nu_begin_k(3),
35
+ nu_end_k(1.0 / (3.0 * sqrt(3.0))),
36
+ use_init(false),
37
+ nu_alpha(1.0 / 2)
38
+ {
39
+ gt_trans = Eigen::Matrix4d::Identity();
40
+ init_trans = Eigen::MatrixXd(); // 或指定大小
41
+ res_trans = Eigen::MatrixXd();
42
+ }
43
+ };
44
+ }
45
+
46
+ namespace spare{
47
+ // parameters for spare non-rigid registration
48
+ struct Parameters
49
+ {
50
+ int max_outer_iters; // nonrigid max iters
51
+ Scalar w_smo; // smoothness weight
52
+ Scalar w_rot; // rotation matrix weight
53
+ Scalar w_arap_coarse; // ARAP weight for coarse alignment
54
+ Scalar w_arap_fine; // ARAP weight for fine alignment
55
+ bool use_landmark;
56
+ bool calc_gt_err; // calculate ground truth error (DEBUG)
57
+ std::vector<int> landmark_src;
58
+ std::vector<int> landmark_tar;
59
+ Scalar Data_nu;
60
+ Scalar Data_initk;
61
+ Scalar stop_coarse;
62
+ Scalar stop_fine;
63
+ // Sample para
64
+ Scalar uni_sample_radio; // uniform sample radio
65
+ bool use_geodesic_dist;
66
+ int num_sample_nodes;
67
+ // record the initial error
68
+ Scalar init_gt_mean_errs;
69
+ Scalar init_gt_max_errs;
70
+ Scalar mesh_scale;
71
+ Parameters() // default
72
+ {
73
+ max_outer_iters = 30;
74
+ w_smo = 0.01; // smooth
75
+ w_rot = 1e-4; // orth
76
+ w_arap_coarse = 500; // 10;
77
+ w_arap_fine = 200;
78
+ use_landmark = false;
79
+ calc_gt_err = true;
80
+ Data_nu = 0.0;
81
+ Data_initk = 1;
82
+ stop_coarse = 1e-3;
83
+ stop_fine = 1e-4;
84
+ // Sample para
85
+ uni_sample_radio = 10;
86
+ use_geodesic_dist = true;
87
+ init_gt_mean_errs = .0;
88
+ }
89
+ };
90
+ };
91
+
92
+ #endif // PARAMETERS_H
cpp/tools/robust_norm.h ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef ROBUST_NORM_H_
2
+ #define ROBUST_NORM_H_
3
+
4
+ #include "registration.h"
5
+
6
+ inline void welsch_weight(Eigen::VectorXd& r, double p)
7
+ {
8
+ #pragma omp parallel for
9
+ for (int i = 0; i < r.size(); ++i)
10
+ {
11
+ r[i] = (r[i] >= 0) ? std::exp(-r[i] / (2 * p * p)) : 0.0;
12
+ }
13
+ }
14
+
15
+ inline double welsch_energy(const Eigen::VectorXd& r, double p)
16
+ {
17
+ double energy = 0.0;
18
+ #pragma omp parallel for reduction(+:energy)
19
+ for (int i = 0; i < r.size(); ++i)
20
+ {
21
+ energy += 1.0 - std::exp(-r[i] / (2 * p * p));
22
+ }
23
+ return energy;
24
+ }
25
+
26
+ inline void robust_weight(Eigen::VectorXd& r, double p)
27
+ {
28
+ welsch_weight(r, p);
29
+ }
30
+
31
+ inline double get_energy(const Eigen::VectorXd& r, double p)
32
+ {
33
+ return welsch_energy(r, p);
34
+ }
35
+
36
+
37
+ #endif
cpp/tools/tools.cpp ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include "tools.h"
2
+ #include <iostream>
3
+ #include "pch.h"
4
+ #ifdef __linux__
5
+ #include <unistd.h>
6
+ #include <sys/types.h>
7
+ #include <sys/stat.h>
8
+ #endif
9
+
10
+
11
+ Scalar mesh_scaling(Mesh& src_mesh, Mesh& tar_mesh)
12
+ {
13
+ Vec3 max(-1e12, -1e12, -1e12);
14
+ Vec3 min(1e12, 1e12, 1e12);
15
+ for(auto it = src_mesh.vertices_begin(); it != src_mesh.vertices_end(); it++)
16
+ {
17
+ for(int j = 0; j < 3; j++)
18
+ {
19
+ if(src_mesh.point(*it)[j] > max[j])
20
+ {
21
+ max[j] = src_mesh.point(*it)[j];
22
+ }
23
+ if(src_mesh.point(*it)[j] < min[j])
24
+ {
25
+ min[j] = src_mesh.point(*it)[j];
26
+ }
27
+ }
28
+ }
29
+
30
+ for(auto it = tar_mesh.vertices_begin(); it != tar_mesh.vertices_end(); it++)
31
+ {
32
+ for(int j = 0; j < 3; j++)
33
+ {
34
+ if(tar_mesh.point(*it)[j] > max[j])
35
+ {
36
+ max[j] = tar_mesh.point(*it)[j];
37
+ }
38
+ if(tar_mesh.point(*it)[j] < min[j])
39
+ {
40
+ min[j] = tar_mesh.point(*it)[j];
41
+ }
42
+ }
43
+ }
44
+ Scalar scale = (max-min).norm();
45
+
46
+ for(auto it = src_mesh.vertices_begin(); it != src_mesh.vertices_end(); it++)
47
+ {
48
+ Vec3 p = src_mesh.point(*it);
49
+ p = p/scale;
50
+ src_mesh.set_point(*it, p);
51
+ }
52
+
53
+ for(auto it = tar_mesh.vertices_begin(); it != tar_mesh.vertices_end(); it++)
54
+ {
55
+ Vec3 p = tar_mesh.point(*it);
56
+ p = p/scale;
57
+ tar_mesh.set_point(*it, p);
58
+ }
59
+
60
+ return scale;
61
+ }
62
+
63
+
64
+
65
+ Vector3 Vec2Eigen(Vec3 s)
66
+ {
67
+ return Vector3(s[0], s[1], s[2]);
68
+ }
69
+
70
+
71
+ #ifdef __linux__
72
+ bool my_mkdir(std::string file_path)
73
+ {
74
+ if(access(file_path.c_str(), 06))
75
+ {
76
+ std::cout << "file_path : (" << file_path << ") didn't exist or no write ability!!" << std::endl;
77
+ if(mkdir(file_path.c_str(), S_IRWXU))
78
+ {
79
+ std::cout << "mkdir " << file_path << " is wrong! please check upper path " << std::endl;
80
+ exit(0);
81
+ }
82
+ std::cout<< "mkdir " << file_path << " success!! " << std::endl;
83
+ }
84
+ }
85
+ #endif
cpp/tools/tools.h ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef TOOL_H_
2
+ #define TOOL_H_
3
+ #include "Types.h"
4
+ // Convert Mesh to libigl format to calculate geodesic distance
5
+
6
+ Scalar mesh_scaling(Mesh& src_mesh, Mesh& tar_mesh);
7
+ Vector3 Vec2Eigen(Vec3 s);
8
+ #ifdef __linux__
9
+ bool my_mkdir(std::string file_path);
10
+ #endif
11
+
12
+ #endif
docs/.gitkeep ADDED
@@ -0,0 +1 @@
 
 
1
+ 0
examples/.gitkeep ADDED
@@ -0,0 +1 @@
 
 
1
+ 0
examples/c++/CMakeLists.txt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ cmake_minimum_required(VERSION 3.16)
2
+ project(MyExternalApp LANGUAGES CXX)
3
+
4
+ set(CMAKE_CXX_STANDARD 17)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+ if(NOT CMAKE_BUILD_TYPE)
7
+ set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
8
+ endif()
9
+
10
+
11
+
12
+
13
+ add_subdirectory(002hugging_face)
14
+ add_executable(MyApp main.cpp)
15
+ target_link_libraries(MyApp PRIVATE Lightweight3DRegLib)
16
+
examples/c++/main.cpp ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include <filesystem>
2
+ #include <iostream>
3
+ #include <string>
4
+
5
+ #include <OpenMesh/Core/IO/MeshIO.hh>
6
+ #include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
7
+ #include <Eigen/Dense>
8
+
9
+ #include "nonrigid_spare_registration.h"
10
+ #include "rigid_fricp_registration.h"
11
+ #include "Types.h"
12
+
13
+ namespace fs = std::filesystem;
14
+ typedef OpenMesh::TriMesh_ArrayKernelT<> MeshType;
15
+ using Matrix3X = Eigen::Matrix<double, 3, Eigen::Dynamic>;
16
+
17
+ // ---------------------- 辅助函数 ----------------------
18
+ bool EnsureDirectoryExists(const std::string& path)
19
+ {
20
+ if (!fs::exists(path)) {
21
+ if (!fs::create_directories(path)) {
22
+ std::cerr << "Failed to create directory: " << path << std::endl;
23
+ return false;
24
+ }
25
+ }
26
+ return true;
27
+ }
28
+
29
+ bool ReadMeshVerticesNormals(const std::string& filename, Matrix3X& vertices, Matrix3X& normals)
30
+ {
31
+ MeshType mesh;
32
+ OpenMesh::IO::Options opt = OpenMesh::IO::Options::VertexNormal;
33
+
34
+ if (!OpenMesh::IO::read_mesh(mesh, filename, opt)) {
35
+ std::cerr << "Failed to read mesh: " << filename << std::endl;
36
+ return false;
37
+ }
38
+
39
+ // 如果没有法向量,自动计算
40
+ if (!mesh.has_vertex_normals()) {
41
+ mesh.request_face_normals();
42
+ mesh.request_vertex_normals();
43
+ mesh.update_normals();
44
+ }
45
+
46
+ size_t n_vertices = mesh.n_vertices();
47
+ if (n_vertices == 0) {
48
+ std::cerr << "Error: mesh has no vertices!" << std::endl;
49
+ return false;
50
+ }
51
+
52
+ vertices.resize(3, n_vertices);
53
+ normals.resize(3, n_vertices);
54
+
55
+ for (auto v : mesh.vertices()) {
56
+ auto point = mesh.point(v);
57
+ auto normal = mesh.normal(v);
58
+ vertices.col(v.idx()) << point[0], point[1], point[2];
59
+ normals.col(v.idx()) << normal[0], normal[1], normal[2];
60
+ }
61
+
62
+ return true;
63
+ }
64
+
65
+ // ---------------------- main ----------------------
66
+ int main()
67
+ {
68
+ // 定义输出路径
69
+ std::string outpath0 = "./outpath0/";
70
+ std::string outpath1 = "./outpath1/";
71
+ std::string outpath2 = "./outpath2/";
72
+
73
+ // 创建输出文件夹
74
+ for (const auto& path : {outpath0, outpath1, outpath2}) {
75
+ if (!EnsureDirectoryExists(path)) return -1;
76
+ }
77
+
78
+ Matrix3X target_points, target_normals;
79
+ Matrix3X source_points, source_normals;
80
+ Matrix3X deformed_points;
81
+
82
+ // ------------------- 直接从文件注册 -------------------
83
+ {
84
+ RigidFricpRegistration fricp;
85
+ NonrigidSpareRegistration spare;
86
+
87
+ fricp.Reg("./data/target.ply","./data/source.ply", outpath0);
88
+ spare.Reg("./data/target.obj","./data/source.obj", outpath0);
89
+ } // fricp 和 spare 对象作用域结束,自动销毁
90
+
91
+ // ------------------- 带参数注册 -------------------
92
+ {
93
+ RigidFricpRegistration fricp;
94
+ NonrigidSpareRegistration spare;
95
+
96
+ fricp.Paras_init(false,"",80,1e-5);
97
+ fricp.Reg("./data/target.ply","./data/source.ply", outpath1);
98
+
99
+ spare.Paras_init(50,1e-4,1e-5);
100
+ spare.Reg("./data/target.obj","./data/source.obj", outpath1);
101
+ }
102
+
103
+ // ------------------- 使用顶点和法向量直接注册 -------------------
104
+ if (!ReadMeshVerticesNormals("./data/target.ply", target_points, target_normals)) return -1;
105
+ if (!ReadMeshVerticesNormals("./data/source.ply", source_points, source_normals)) return -1;
106
+
107
+ {
108
+ RigidFricpRegistration fricp;
109
+ fricp.Read_data(target_points, source_points, target_normals, source_normals);
110
+ fricp.Register();
111
+ deformed_points = fricp.deformed_points_3X_;
112
+ fricp.Output_data(outpath2, "fricp");
113
+ }
114
+
115
+ if (!ReadMeshVerticesNormals("./data/target.obj", target_points, target_normals)) return -1;
116
+ if (!ReadMeshVerticesNormals("./data/source.obj", source_points, source_normals)) return -1;
117
+
118
+ {
119
+ NonrigidSpareRegistration spare;
120
+ spare.Read_data(target_points, source_points, target_normals, source_normals);
121
+ spare.Register();
122
+ deformed_points = spare.deformed_points_3X_;
123
+ spare.Output_data(outpath2, "spare");
124
+ }
125
+
126
+ std::cout << "Registration finished successfully!" << std::endl;
127
+ return 0;
128
+ }
examples/data/fricp/fricpm3reg_pc.ply ADDED
The diff for this file is too large to render. See raw diff
 
examples/data/fricp/fricpm3trans.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ 0.804418 0.359408 -0.473008 -0.156803
2
+ -0.509781 0.826441 -0.238997 0.480169
3
+ 0.305016 0.433384 0.848023 0.0139896
4
+ 0 0 0 1
examples/data/fricp/source.ply ADDED
The diff for this file is too large to render. See raw diff
 
examples/data/fricp/target.ply ADDED
The diff for this file is too large to render. See raw diff
 
examples/data/spare/source.obj ADDED
The diff for this file is too large to render. See raw diff
 
examples/data/spare/target.obj ADDED
The diff for this file is too large to render. See raw diff
 
examples/python/example_python.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import numpy as np
3
+ import trimesh
4
+ from plyfile import PlyData
5
+ import pyregister
6
+
7
+
8
+ def read_vertices_normals(file_path):
9
+ """
10
+ Read vertices and normals from a point cloud or mesh file.
11
+ Supports formats such as .ply, .obj, .xyz, etc.
12
+ """
13
+ ext = os.path.splitext(file_path)[1].lower()
14
+
15
+ if ext == ".ply":
16
+ plydata = PlyData.read(file_path)
17
+ vertex_data = plydata['vertex']
18
+ vertices = np.vstack([vertex_data['x'], vertex_data['y'], vertex_data['z']]).astype(np.float64)
19
+
20
+ normals = None
21
+ if {'nx', 'ny', 'nz'}.issubset(vertex_data.data.dtype.names):
22
+ normals = np.vstack([vertex_data['nx'], vertex_data['ny'], vertex_data['nz']]).astype(np.float64)
23
+
24
+ else:
25
+ mesh = trimesh.load(file_path, process=False)
26
+ vertices = mesh.vertices.T.astype(np.float64)
27
+ normals = getattr(mesh, "vertex_normals", None)
28
+ if normals is not None and len(normals) > 0:
29
+ normals = normals.T.astype(np.float64)
30
+ else:
31
+ normals = np.empty((0, 0))
32
+
33
+ return vertices, normals
34
+
35
+
36
+ def run_rigid_file(file_target, file_source, output_path):
37
+ """
38
+ Perform rigid registration (FRICP method) by directly reading files.
39
+
40
+ Parameters
41
+ ----------
42
+ file_target : str
43
+ Path to the target point cloud or mesh file.
44
+ file_source : str
45
+ Path to the source point cloud or mesh file.
46
+ output_path : str
47
+ Output file path for the registered result.
48
+ """
49
+ print(f"\n[RIGID-FILE] Registering {file_source} → {file_target}")
50
+ reg = pyregister.RigidFricpRegistration()
51
+ reg.Paras_init(useinit=False, maxiter=100, stop=1e-5)
52
+ reg.Reg(file_target, file_source, output_path)
53
+ print(f"[RIGID-FILE] Completed → {output_path}")
54
+ print("Deformed points shape:", reg.deformed_points_3X_.shape)
55
+
56
+
57
+ def run_rigid_numpy(target_pts, source_pts, target_n, source_n, output_path):
58
+ """
59
+ Perform rigid registration (FRICP method) using NumPy matrices.
60
+
61
+ Parameters
62
+ ----------
63
+ target_pts : np.ndarray, shape (3, N)
64
+ Target point cloud coordinates.
65
+ source_pts : np.ndarray, shape (3, N)
66
+ Source point cloud coordinates.
67
+ target_n : np.ndarray, shape (3, N) or (0, 0)
68
+ Target normals.
69
+ source_n : np.ndarray, shape (3, N) or (0, 0)
70
+ Source normals.
71
+ output_path : str
72
+ Output file path for the registered result.
73
+ """
74
+ print(f"\n[RIGID-NUMPY] Registering from NumPy matrices")
75
+ reg = pyregister.RigidFricpRegistration()
76
+ reg.Paras_init()
77
+ reg.Read_data(target_pts, source_pts, target_n, source_n)
78
+ reg.Register()
79
+ reg.Output_data(output_path, "FRICP")
80
+ print(f"[RIGID-NUMPY] Completed → {output_path}")
81
+ print("Deformed points shape:", reg.deformed_points_3X_.shape)
82
+
83
+
84
+ def run_nonrigid_file(file_target, file_source, output_path):
85
+ """
86
+ Perform non-rigid registration (SPARE method) by directly reading files.
87
+
88
+ Parameters
89
+ ----------
90
+ file_target : str
91
+ Path to the target point cloud or mesh file.
92
+ file_source : str
93
+ Path to the source point cloud or mesh file.
94
+ output_path : str
95
+ Output file path for the registered result.
96
+ """
97
+ print(f"\n[NONRIGID-FILE] Registering {file_source} → {file_target}")
98
+ reg = pyregister.NonrigidSpareRegistration()
99
+ reg.Paras_init(iters = 30 ,stopcoarse=1e-3,stopfine=1e-4, uselandmark = False);
100
+ reg.Reg(file_target, file_source, output_path)
101
+ print(f"[NONRIGID-FILE] Completed → {output_path}")
102
+ print("Deformed points shape:", reg.deformed_points_3X_.shape)
103
+
104
+
105
+
106
+ def run_nonrigid_numpy(target_pts, source_pts, target_n, source_n, output_path):
107
+ """
108
+ Perform non-rigid registration (SPARE method) using NumPy matrices.
109
+
110
+ Parameters
111
+ ----------
112
+ target_pts : np.ndarray, shape (3, N)
113
+ Target point cloud coordinates.
114
+ source_pts : np.ndarray, shape (3, N)
115
+ Source point cloud coordinates.
116
+ target_n : np.ndarray, shape (3, N) or (0, 0)
117
+ Target normals.
118
+ source_n : np.ndarray, shape (3, N) or (0, 0)
119
+ Source normals.
120
+ output_path : str
121
+ Output file path for the registered result.
122
+ """
123
+ print(f"\n[NONRIGID-NUMPY] Registering from NumPy matrices")
124
+ reg = pyregister.NonrigidSpareRegistration()
125
+ reg.Paras_init()
126
+ reg.Read_data(target_pts, source_pts, target_n, source_n)
127
+ reg.Register()
128
+ reg.Output_data(output_path, "SPARE")
129
+ print(f"[NONRIGID-NUMPY] Completed → {output_path}")
130
+ print("Deformed points shape:", reg.deformed_points_3X_.shape)
131
+
132
+
133
+
134
+ def main():
135
+ folder = "data"
136
+ output_folder = os.path.join(folder, "results_register")
137
+ os.makedirs(output_folder, exist_ok=True)
138
+ target_ply = os.path.join(folder, "target.ply")
139
+
140
+ source_ply = os.path.join(folder, "source.ply")
141
+
142
+ target_obj = os.path.join(folder, "target.obj")
143
+
144
+ source_obj = os.path.join(folder, "source.obj")
145
+
146
+ # Rigid registration (file mode)
147
+ if os.path.exists(target_ply) and os.path.exists(source_ply):
148
+ run_rigid_file(target_ply, source_ply, os.path.join(output_folder, "rigid_file_result"))
149
+
150
+ # Load into NumPy and run in-memory registration
151
+ target_pts, target_n = read_vertices_normals(target_ply)
152
+ source_pts, source_n = read_vertices_normals(source_ply)
153
+ target_n = target_n if target_n is not None else np.empty((0, 0))
154
+ source_n = source_n if source_n is not None else np.empty((0, 0))
155
+ run_rigid_numpy(target_pts, source_pts, target_n, source_n,
156
+ os.path.join(output_folder, "rigid_numpy_result"))
157
+
158
+ # Non-rigid registration (file mode)
159
+ if os.path.exists( target_obj ) and os.path.exists(source_obj):
160
+ run_nonrigid_file( target_obj , source_obj, os.path.join(output_folder, "nonrigid_file_result"))
161
+
162
+ # Load into NumPy and run in-memory registration
163
+ target_pts, target_n = read_vertices_normals( target_obj )
164
+ source_pts, source_n = read_vertices_normals(source_obj)
165
+ target_n = target_n if target_n is not None else np.empty((0, 0))
166
+ source_n = source_n if source_n is not None else np.empty((0, 0))
167
+ run_nonrigid_numpy(target_pts, source_pts, target_n, source_n,
168
+ os.path.join(output_folder, "nonrigid_numpy_result"))
169
+
170
+
171
+ if __name__ == "__main__":
172
+ main()
output/.gitkeep ADDED
@@ -0,0 +1 @@
 
 
1
+ 0
python/pyregister.cpp ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include "rigid_fricp_registration.h"
2
+ #include "nonrigid_spare_registration.h"
3
+ #include "registration.h"
4
+ #include <pybind11/pybind11.h>
5
+ #include <pybind11/eigen.h>
6
+ #include <pybind11/stl.h> // ✅ 支持 std::vector<int>
7
+ #include <Eigen/Dense>
8
+
9
+ namespace py = pybind11;
10
+
11
+ // ======================= 模块定义 ==========================
12
+ PYBIND11_MODULE(pyregister, m) {
13
+
14
+ // -------- Registration 基类 --------
15
+ py::class_<Registration>(m, "Registration")
16
+ .def("Read_data",
17
+ py::overload_cast<const std::string&, const std::string&>(&Registration::Read_data),
18
+ py::arg("file_target"),
19
+ py::arg("file_source"))
20
+ .def("Read_data",
21
+ [](Registration &self,
22
+ const Eigen::Ref<const Eigen::MatrixXd> &target_p,
23
+ const Eigen::Ref<const Eigen::MatrixXd> &source_p,
24
+ const Eigen::Ref<const Eigen::MatrixXd> &target_n,
25
+ const Eigen::Ref<const Eigen::MatrixXd> &source_n)
26
+ {
27
+ std::cout << "[pybind] entering Registration::Read_data" << std::endl;
28
+
29
+ Matrix3X tp = target_p.topRows(3);
30
+ Matrix3X sp = source_p.topRows(3);
31
+ Matrix3X tn = target_n.topRows(3);
32
+ Matrix3X sn = source_n.topRows(3);
33
+
34
+ self.Read_data(tp, sp, tn, sn);
35
+
36
+ std::cout << "[pybind] leaving Registration::Read_data" << std::endl;
37
+ },
38
+ py::arg("target_p"),
39
+ py::arg("source_p"),
40
+ py::arg("target_n") = Eigen::MatrixXd(),
41
+ py::arg("source_n") = Eigen::MatrixXd()
42
+ )
43
+
44
+
45
+ .def("Register", &Registration::Register)
46
+ .def("Output_data", &Registration::Output_data,
47
+ py::arg("out_path"),
48
+ py::arg("method_name"));
49
+
50
+ // -------- 暴露 spare::Parameters --------
51
+ py::class_<spare::Parameters>(m, "SpareParameters")
52
+ .def(py::init<>())
53
+ .def_readwrite("use_landmark", &spare::Parameters::use_landmark)
54
+ .def_readwrite("landmark_src", &spare::Parameters::landmark_src)
55
+ .def_readwrite("landmark_tar", &spare::Parameters::landmark_tar);
56
+
57
+ // -------- Rigid FRICP Registration --------
58
+ py::class_<RigidFricpRegistration, Registration>(m, "RigidFricpRegistration")
59
+ .def(py::init<>())
60
+ .def("Reg", &RigidFricpRegistration::Reg,
61
+ py::arg("file_target"),
62
+ py::arg("file_source"),
63
+ py::arg("out_path"))
64
+ .def("Paras_init", &RigidFricpRegistration::Paras_init,
65
+ py::arg("useinit") = false,
66
+ py::arg("fileinit") = " ",
67
+ py::arg("maxiter") = 100,
68
+ py::arg("stop") = 1e-5)
69
+ .def_readwrite("deformed_points_3X_", &RigidFricpRegistration::deformed_points_3X_);
70
+
71
+ // -------- Nonrigid Spare Registration --------
72
+ py::class_<NonrigidSpareRegistration, Registration>(m, "NonrigidSpareRegistration")
73
+ .def(py::init<>())
74
+ .def("Reg", &NonrigidSpareRegistration::Reg,
75
+ py::arg("file_target"),
76
+ py::arg("file_source"),
77
+ py::arg("out_path"))
78
+ .def("Paras_init", &NonrigidSpareRegistration::Paras_init,
79
+ py::arg("iters") = 30,
80
+ py::arg("stopcoarse") = 1e-3,
81
+ py::arg("stopfine") = 1e-4,
82
+ py::arg("uselandmark") = false,
83
+ py::arg("src") = std::vector<int>(),
84
+ py::arg("tar") = std::vector<int>())
85
+
86
+ .def_readwrite("deformed_points_3X_", &NonrigidSpareRegistration::deformed_points_3X_)
87
+ .def_readwrite("paras", &NonrigidSpareRegistration::paras); // ✅ 暴露 paras
88
+ }
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ numpy
2
+ gradio
3
+ trimesh
4
+ plotly
5
+ plyfile
scripts/compile.sh ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ cd ..
2
+ mkdir build
3
+ cd build
4
+ cmake -DCMAKE_BUILD_TYPE=Release ..
5
+ make -j4