35
35
*********************************************************************************/
36
36
#include < cinolib/ARAP.h>
37
37
#include < cinolib/parallel_for.h>
38
+ #include < cinolib/laplacian.h>
38
39
39
40
namespace cinolib
40
41
{
@@ -43,236 +44,137 @@ template<class M, class V, class E, class P>
43
44
CINO_INLINE
44
45
void ARAP (AbstractMesh<M,V,E,P> & m, ARAP_data & data)
45
46
{
47
+ // ///////////////////////////////////////////////////////////////////////
48
+
46
49
auto init = [&]()
47
50
{
48
51
assert (m.mesh_type ()==TRIMESH || m.mesh_type ()==TETMESH);
49
52
50
- data.init = false ; // don't init next time
51
- data.xyz_loc .resize (m.num_polys ()*m.verts_per_poly (0 ));
53
+ data.init = false ;
54
+
55
+ data.R .resize (m.num_verts ());
56
+ data.xyz_ref = m.vector_verts ();
52
57
53
- // per edge weights
54
58
data.w .resize (m.num_edges ());
55
59
for (uint eid=0 ; eid<m.num_edges (); ++eid)
56
60
{
57
61
data.w .at (eid) = m.edge_weight (eid,data.w_type );
58
62
}
59
63
60
- // compute a map between matrix columns and mesh vertices
61
- // if hard constraints are used, boundary conditions will
62
- // map to -1, meaning that they do not correspond to any
63
- // column in the matrix
64
- data.col_map .resize (m.num_verts (),0 );
65
- if (!data.use_soft_constraints )
66
- {
67
- for (const auto & bc : data.bcs )
68
- {
69
- data.col_map .at (bc.first ) = -1 ;
70
- }
71
- }
72
- uint fresh_id = 0 ;
73
- for (uint vid=0 ; vid<m.num_verts (); ++vid)
74
- {
75
- if (data.col_map .at (vid)==0 )
76
- {
77
- data.col_map .at (vid) = fresh_id++;
78
- }
79
- }
80
-
81
- // Compute the Laplacian matrix and pre-factorize it
82
- typedef Eigen::Triplet<double > Entry;
83
- std::vector<Entry> entries;
84
- uint size = m.num_verts () - uint (data.bcs .size ());
85
- if (data.use_soft_constraints )
64
+ if (data.hard_constrain_handles )
86
65
{
87
- size = m.num_verts () + uint (data.bcs .size ());
88
- data.W .resize (size);
66
+ data.A = -laplacian (m,data.w_type ,1 );
89
67
}
90
- for (uint vid=0 ; vid<m.num_verts (); ++vid)
91
- {
92
- int col = data.col_map .at (vid);
93
- if (col==-1 ) continue ; // skip, hard BC
94
- if (data.use_soft_constraints ) data.W [col] = data.w_laplace ;
95
- for (uint nbr : m.adj_v2v (vid))
96
- {
97
- int col_nbr = data.col_map .at (nbr);
98
- int eid = m.edge_id (vid,nbr);
99
- assert (eid>=0 );
100
- entries.push_back (Entry (col, col, data.w .at (eid)));
101
- if (col_nbr==-1 ) continue ; // skip, hard BC
102
- entries.push_back (Entry (col, col_nbr, -data.w .at (eid)));
103
- }
104
- }
105
- if (data.use_soft_constraints )
106
- {
107
- // models equation => x_bc = bc_value * w_constr
108
- uint new_row = m.num_verts ();
109
- for (auto bc : data.bcs )
110
- {
111
- entries.push_back (Entry (new_row,bc.first ,1 ));
112
- data.W [new_row] = data.w_constr ;
113
- ++new_row;
114
- }
115
- }
116
- data.A = Eigen::SparseMatrix<double >(size, (data.use_soft_constraints ) ? m.num_verts () : size);
117
- data.A .setFromTriplets (entries.begin (), entries.end ());
118
- if (data.use_soft_constraints ) data.cache .derived ().compute (data.A .transpose ()*data.W .asDiagonal ()*data.A );
119
- else data.cache .derived ().compute (data.A );
120
-
121
- if (data.warm_start_with_laplacian )
68
+ else
122
69
{
123
- Eigen::VectorXd rhs_x = Eigen::VectorXd::Zero (size );
124
- Eigen::VectorXd rhs_y = Eigen::VectorXd::Zero ( size);
125
- Eigen::VectorXd rhs_z = Eigen::VectorXd::Zero (size) ;
70
+ uint nv = m. num_verts ( );
71
+ uint nh = data. handles . size ( );
72
+ std::vector< Eigen::Triplet< double >> entries ;
126
73
for (uint vid=0 ; vid<m.num_verts (); ++vid)
127
74
{
128
- int col = data.col_map .at (vid);
129
- if (col==-1 ) continue ; // skip BC
130
- for (uint eid : m.adj_v2e (vid))
75
+ double diag = 0 ;
76
+ for (uint nbr : m.adj_v2v (vid))
131
77
{
132
- uint nbr = m.vert_opposite_to (eid,vid);
133
- rhs_x[col] += data.w .at (eid) * (m.vert (vid).x () - m.vert (nbr).x ());
134
- rhs_y[col] += data.w .at (eid) * (m.vert (vid).y () - m.vert (nbr).y ());
135
- rhs_z[col] += data.w .at (eid) * (m.vert (vid).z () - m.vert (nbr).z ());
136
- // move the contribution of BCs to the RHS
137
- if (data.col_map .at (nbr)==-1 )
138
- {
139
- vec3d p = data.bcs .at (nbr);
140
- rhs_x[col] += data.w .at (eid) * p.x ();
141
- rhs_y[col] += data.w .at (eid) * p.y ();
142
- rhs_z[col] += data.w .at (eid) * p.z ();
143
- }
78
+ int eid = m.edge_id (vid,nbr);
79
+ entries.emplace_back (vid,nbr,-data.w .at (eid));
80
+ diag += data.w .at (eid);
144
81
}
82
+ entries.emplace_back (vid,vid,diag);
145
83
}
146
- if (data.use_soft_constraints )
84
+ uint off = 0 ;
85
+ for (uint vid : data.handles )
147
86
{
148
- auto x = data.cache .solve (data.A .transpose ()*data.W .asDiagonal ()*rhs_x).eval ();
149
- auto y = data.cache .solve (data.A .transpose ()*data.W .asDiagonal ()*rhs_y).eval ();
150
- auto z = data.cache .solve (data.A .transpose ()*data.W .asDiagonal ()*rhs_z).eval ();
151
- data.xyz_out .resize (m.num_verts ());
152
- for (uint vid=0 ; vid<m.num_verts (); ++vid)
153
- {
154
- data.xyz_out [vid] = vec3d (x[vid],y[vid],z[vid]);
155
- }
156
- }
157
- else
158
- {
159
- auto x = data.cache .solve (rhs_x).eval ();
160
- auto y = data.cache .solve (rhs_y).eval ();
161
- auto z = data.cache .solve (rhs_z).eval ();
162
- data.xyz_out .resize (m.num_verts ());
163
- for (uint vid=0 ; vid<m.num_verts (); ++vid)
164
- {
165
- int col = data.col_map [vid];
166
- if (col>=0 ) data.xyz_out [vid] = vec3d (x[col],y[col],z[col]);
167
- }
168
- for (const auto & bc : data.bcs )
169
- {
170
- data.xyz_out [bc.first ] = bc.second ;
171
- }
87
+ entries.emplace_back (nv+off,vid,1.0 );
88
+ off++;
172
89
}
173
- }
174
- else
175
- {
176
- data.xyz_out = m.vector_verts ();
177
- for (const auto & bc : data.bcs ) data.xyz_out .at (bc.first ) = bc.second ;
90
+ data.A .resize (nv+nh,nv);
91
+ data.A .setFromTriplets (entries.begin (), entries.end ());
92
+ data.cache .derived ().compute (data.A .transpose ()*data.A );
178
93
}
179
94
};
180
95
96
+ // ///////////////////////////////////////////////////////////////////////
97
+
181
98
auto local_step = [&]()
182
99
{
183
- PARALLEL_FOR (0 , m.num_polys (), 1000 , [&](uint pid )
100
+ PARALLEL_FOR (0 , m.num_verts (), 1000 , [&](uint vid )
184
101
{
185
- mat3d cov = mat3d::ZERO ();
186
- for (uint eid : m.adj_p2e (pid ))
102
+ mat3d cov = mat3d::ZERO ();
103
+ for (uint nbr : m.adj_v2v (vid ))
187
104
{
188
- uint v0 = m.edge_vert_id (eid, 0 );
189
- uint v1 = m. edge_vert_id (eid, 1 );
190
- vec3d e_cur = data. xyz_out . at (v0 ) - data. xyz_out . at (v1 );
191
- vec3d e_ref = m. vert (v0) - m. vert (v1);
105
+ int eid = m.edge_id (vid,nbr );
106
+ vec3d e_ref = (data. xyz_ref . at (vid) - data. xyz_ref . at (nbr) );
107
+ vec3d e_cur = (m. vert (vid ) - m. vert (nbr) );
108
+
192
109
cov += data.w .at (eid) * (e_cur * e_ref.transpose ());
193
110
}
194
111
195
- // find closest rotation and store rotated point
196
- uint off = pid*m.verts_per_poly (pid);
197
- mat3d rot = cov.closest_orthogonal_matrix (true );
198
- for (uint i=0 ; i<m.verts_per_poly (pid); ++i)
199
- {
200
- data.xyz_loc .at (off+i) = rot * m.poly_vert (pid,i);
201
- }
112
+ mat3d UU,VV;
113
+ vec3d S;
114
+ cov.SVD (UU,S,VV);
115
+ mat3d I = mat3d::DIAG (1 );
116
+ I (2 ,2 ) = (UU*VV.transpose ()).det ();
117
+ data.R .at (vid) = UU*I*VV.transpose ();
202
118
});
203
119
};
204
120
121
+ // ///////////////////////////////////////////////////////////////////////
122
+
205
123
auto global_step = [&]()
206
124
{
207
- uint size = (data.use_soft_constraints ) ? m.num_verts () + uint (data.bcs .size ())
208
- : m.num_verts () - uint (data.bcs .size ());
209
- Eigen::VectorXd rhs_x = Eigen::VectorXd::Zero (size);
210
- Eigen::VectorXd rhs_y = Eigen::VectorXd::Zero (size);
211
- Eigen::VectorXd rhs_z = Eigen::VectorXd::Zero (size);
125
+ uint nv = m.num_verts ();
126
+ uint nh = (data.hard_constrain_handles ) ? 0 : data.handles .size ();
127
+
128
+ Eigen::VectorXd rhs_x (nv+nh);
129
+ Eigen::VectorXd rhs_y (nv+nh);
130
+ Eigen::VectorXd rhs_z (nv+nh);
131
+
212
132
for (uint vid=0 ; vid<m.num_verts (); ++vid)
213
133
{
214
- int col = data.col_map .at (vid);
215
- if (col==-1 ) continue ; // skip, vert is BC
134
+ vec3d rhs (0 ,0 ,0 );
216
135
for (uint nbr : m.adj_v2v (vid))
217
136
{
218
- int eid = m.edge_id (vid,nbr);
219
- double w = 1.0 /m.adj_e2p (eid).size ();
220
- for (uint pid : m.adj_e2p (eid))
221
- {
222
- uint i = m.poly_vert_offset (pid,vid);
223
- uint j = m.poly_vert_offset (pid,nbr);
224
- vec3d Re = data.xyz_loc .at (pid*m.verts_per_poly (pid)+i) - data.xyz_loc .at (pid*m.verts_per_poly (pid)+j);
225
- rhs_x[col] += w * data.w .at (eid) * Re[0 ];
226
- rhs_y[col] += w * data.w .at (eid) * Re[1 ];
227
- rhs_z[col] += w * data.w .at (eid) * Re[2 ];
228
- }
229
- // if nbr is a hard BC sum its contibution to the Laplacian matrix to the rhs
230
- if (data.col_map .at (nbr)==-1 )
231
- {
232
- vec3d p = data.bcs .at (nbr);
233
- rhs_x[col] += data.w .at (eid) * p.x ();
234
- rhs_y[col] += data.w .at (eid) * p.y ();
235
- rhs_z[col] += data.w .at (eid) * p.z ();
236
- }
137
+ mat3d Ravg = (data.R .at (vid)+data.R .at (nbr))/2.0 ;
138
+ vec3d e = (data.xyz_ref .at (vid) - data.xyz_ref .at (nbr));
139
+ int eid = m.edge_id (vid,nbr);
140
+
141
+ rhs += data.w .at (eid) * Ravg * e;
237
142
}
143
+ rhs_x[vid] = rhs.x ();
144
+ rhs_y[vid] = rhs.y ();
145
+ rhs_z[vid] = rhs.z ();
238
146
}
239
- if (data.use_soft_constraints )
147
+
148
+ Eigen::VectorXd x,y,z;
149
+ if (data.hard_constrain_handles )
240
150
{
241
- // models rhs of equation => x_bc = bc_value
242
- uint new_row = m.num_verts ();
243
- for (auto bc : data.bcs )
244
- {
245
- rhs_x[new_row] = bc.second .x ();
246
- rhs_y[new_row] = bc.second .y ();
247
- rhs_z[new_row] = bc.second .z ();
248
- ++new_row;
249
- }
250
- auto x = data.cache .solve (data.A .transpose ()*data.W .asDiagonal ()*rhs_x).eval ();
251
- auto y = data.cache .solve (data.A .transpose ()*data.W .asDiagonal ()*rhs_y).eval ();
252
- auto z = data.cache .solve (data.A .transpose ()*data.W .asDiagonal ()*rhs_z).eval ();
253
- for (uint vid=0 ; vid<m.num_verts (); ++vid)
254
- {
255
- data.xyz_out [vid] = vec3d (x[vid],y[vid],z[vid]);
256
- }
151
+ // TODO: use caching also with hard constraints!
152
+ solve_square_system_with_bc (data.A , rhs_x, x, data.handles_x );
153
+ solve_square_system_with_bc (data.A , rhs_y, y, data.handles_y );
154
+ solve_square_system_with_bc (data.A , rhs_z, z, data.handles_z );
257
155
}
258
156
else
259
157
{
260
- auto x = data.cache .solve (rhs_x).eval ();
261
- auto y = data.cache .solve (rhs_y).eval ();
262
- auto z = data.cache .solve (rhs_z).eval ();
263
- for (uint vid=0 ; vid<m.num_verts (); ++vid)
158
+ uint off = 0 ;
159
+ for (uint vid : data.handles )
264
160
{
265
- int col = data.col_map [vid];
266
- if (col>=0 ) data.xyz_out [vid] = vec3d (x[col],y[col],z[col]);
267
- }
268
- for (const auto & bc : data.bcs )
269
- {
270
- data.xyz_out [bc.first ] = bc.second ;
161
+ rhs_x[nv+off] = data.handles_x .at (vid);
162
+ rhs_y[nv+off] = data.handles_y .at (vid);
163
+ rhs_z[nv+off] = data.handles_z .at (vid);
164
+ ++off;
271
165
}
166
+ x = data.cache .solve (data.A .transpose ()*rhs_x).eval ();
167
+ y = data.cache .solve (data.A .transpose ()*rhs_y).eval ();
168
+ z = data.cache .solve (data.A .transpose ()*rhs_z).eval ();
169
+ }
170
+
171
+ for (uint vid=0 ; vid<m.num_verts (); ++vid)
172
+ {
173
+ m.vert (vid) = vec3d (x[vid],y[vid],z[vid]);
272
174
}
273
175
};
274
176
275
- // ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
177
+ // ////////////////////////////////////////////////////////////////////////
276
178
277
179
if (data.init ) init ();
278
180
@@ -281,8 +183,6 @@ void ARAP(AbstractMesh<M,V,E,P> & m, ARAP_data & data)
281
183
local_step ();
282
184
global_step ();
283
185
}
284
-
285
- m.vector_verts () = data.xyz_out ;
286
186
m.update_normals ();
287
187
}
288
188
0 commit comments