CPPxDIC
C++ Digital Image Correlation library for fingertip 3D reconstruction
Loading...
Searching...
No Matches
strain_computation.cpp
Go to the documentation of this file.
1
7#include "logging.h"
8#include <iostream>
9#include <cmath>
10#include <limits>
11
12namespace cppxdic {
13
15 const std::vector<int>& faces,
16 const std::vector<Eigen::Vector3d>& vertices_ref,
17 const std::vector<Eigen::Vector3d>& vertices_cur) {
18
19 DeformationResult result;
20
21 if (faces.size() % 3 != 0) {
22 LOG_ERROR << "Faces must be Nx3 (got " << faces.size() << " elements)";
23 return result;
24 }
25
26 size_t n_faces = faces.size() / 3;
27
28 // Pre-allocate all vectors
29 result.D1.resize(n_faces);
30 result.D2.resize(n_faces);
31 result.D3.resize(n_faces);
32 result.d1.resize(n_faces);
33 result.d2.resize(n_faces);
34 result.d3.resize(n_faces);
35 result.Drec1.resize(n_faces);
36 result.Drec2.resize(n_faces);
37 result.Dnorm.resize(n_faces);
38 result.Fmat.resize(n_faces);
39 result.Cmat.resize(n_faces);
40 result.Lamda1.resize(n_faces);
41 result.Lamda2.resize(n_faces);
42 result.J.resize(n_faces);
43 result.Emat.resize(n_faces);
44 result.emat.resize(n_faces);
45 result.Emgn.resize(n_faces);
46 result.emgn.resize(n_faces);
47 result.Epc1.resize(n_faces);
48 result.Epc2.resize(n_faces);
49 result.Epc1vec.resize(n_faces);
50 result.Epc2vec.resize(n_faces);
51 result.Epc1vecCur.resize(n_faces);
52 result.Epc2vecCur.resize(n_faces);
53 result.epc1.resize(n_faces);
54 result.epc2.resize(n_faces);
55 result.epc1vec.resize(n_faces);
56 result.epc2vec.resize(n_faces);
57 result.EShearMax.resize(n_faces);
58 result.eShearMax.resize(n_faces);
59 result.EShearMaxVec1.resize(n_faces);
60 result.EShearMaxVec2.resize(n_faces);
61 result.EShearMaxVecCur1.resize(n_faces);
62 result.EShearMaxVecCur2.resize(n_faces);
63 result.eShearMaxVec1.resize(n_faces);
64 result.eShearMaxVec2.resize(n_faces);
65 result.Eeq.resize(n_faces);
66 result.eeq.resize(n_faces);
67 result.Area.resize(n_faces);
68
69 // Process each triangle
70 for (size_t itri = 0; itri < n_faces; ++itri) {
71 size_t idx = itri * 3;
72 int v0 = faces[idx];
73 int v1 = faces[idx + 1];
74 int v2 = faces[idx + 2];
75
76 // Check bounds (explicit negative check before implicit unsigned conversion)
77 if (v0 < 0 || v1 < 0 || v2 < 0 ||
78 static_cast<size_t>(v0) >= vertices_ref.size() || static_cast<size_t>(v1) >= vertices_ref.size() || static_cast<size_t>(v2) >= vertices_ref.size() ||
79 static_cast<size_t>(v0) >= vertices_cur.size() || static_cast<size_t>(v1) >= vertices_cur.size() || static_cast<size_t>(v2) >= vertices_cur.size()) {
80 if (itri < 5) {
81 LOG_WARN << "Invalid vertex index in triangle " << itri
82 << " (v0=" << v0 << " v1=" << v1 << " v2=" << v2
83 << ", nRef=" << vertices_ref.size() << " nCur=" << vertices_cur.size() << ")";
84 }
85 // Fill with NaN
86 result.Fmat[itri] = Eigen::Matrix3d::Constant(std::nan(""));
87 continue;
88 }
89
90 // Reference director vectors (MATLAB lines 145-158)
91 result.D1[itri] = vertices_ref[v1] - vertices_ref[v0];
92 result.D2[itri] = vertices_ref[v2] - vertices_ref[v0];
93 Eigen::Vector3d cross_D = result.D1[itri].cross(result.D2[itri]);
94 double norm_D = cross_D.norm();
95 if (norm_D < 1e-12) {
96 // Degenerate triangle
97 result.D3[itri] = Eigen::Vector3d(0, 0, 1);
98 } else {
99 result.D3[itri] = cross_D / norm_D;
100 }
101
102 // Current director vectors (MATLAB lines 161-164)
103 result.d1[itri] = vertices_cur[v1] - vertices_cur[v0];
104 result.d2[itri] = vertices_cur[v2] - vertices_cur[v0];
105 Eigen::Vector3d cross_d = result.d1[itri].cross(result.d2[itri]);
106 double norm_d = cross_d.norm();
107 if (norm_d < 1e-12) {
108 result.d3[itri] = Eigen::Vector3d(0, 0, 1);
109 } else {
110 result.d3[itri] = cross_d / norm_d;
111 }
112
113 // Reciprocal vectors (MATLAB lines 166-169)
114 result.Dnorm[itri] = cross_D.dot(result.D3[itri]);
115 if (std::abs(result.Dnorm[itri]) < 1e-12) {
116 // Degenerate triangle
117 result.Fmat[itri] = Eigen::Matrix3d::Constant(std::nan(""));
118 continue;
119 }
120
121 result.Drec1[itri] = result.D2[itri].cross(result.D3[itri]) / result.Dnorm[itri];
122 result.Drec2[itri] = result.D3[itri].cross(result.D1[itri]) / result.Dnorm[itri];
123
124 // Area uses current (deformed) configuration (MATLAB lines 161-162: dnorm = cross(d1,d2)*d3')
125 double dnorm_val = cross_d.dot(result.d3[itri]);
126 result.Area[itri] = 0.5 * dnorm_val;
127
128 // Deformation gradient tensor F (MATLAB lines 174-179)
129 result.Fmat[itri] = Eigen::Matrix3d::Zero();
130 for (int i = 0; i < 3; ++i) {
131 for (int j = 0; j < 3; ++j) {
132 result.Fmat[itri](i, j) =
133 result.d1[itri](i) * result.Drec1[itri](j) +
134 result.d2[itri](i) * result.Drec2[itri](j) +
135 result.d3[itri](i) * result.D3[itri](j);
136 }
137 }
138
139 // Check if F has NaNs (MATLAB line 181)
140 if (result.Fmat[itri].array().isNaN().any()) {
141 // Fill all measures with NaN
142 result.Cmat[itri] = Eigen::Matrix3d::Constant(std::nan(""));
143 result.Lamda1[itri] = std::nan("");
144 result.Lamda2[itri] = std::nan("");
145 result.J[itri] = std::nan("");
146 result.Emat[itri] = Eigen::Matrix3d::Constant(std::nan(""));
147 result.emat[itri] = Eigen::Matrix3d::Constant(std::nan(""));
148 result.Emgn[itri] = std::nan("");
149 result.emgn[itri] = std::nan("");
150 result.Epc1[itri] = std::nan("");
151 result.Epc2[itri] = std::nan("");
152 result.Epc1vec[itri] = Eigen::Vector3d::Constant(std::nan(""));
153 result.Epc2vec[itri] = Eigen::Vector3d::Constant(std::nan(""));
154 result.Epc1vecCur[itri] = Eigen::Vector3d::Constant(std::nan(""));
155 result.Epc2vecCur[itri] = Eigen::Vector3d::Constant(std::nan(""));
156 result.epc1[itri] = std::nan("");
157 result.epc2[itri] = std::nan("");
158 result.epc1vec[itri] = Eigen::Vector3d::Constant(std::nan(""));
159 result.epc2vec[itri] = Eigen::Vector3d::Constant(std::nan(""));
160 result.EShearMax[itri] = std::nan("");
161 result.eShearMax[itri] = std::nan("");
162 result.EShearMaxVec1[itri] = Eigen::Vector3d::Constant(std::nan(""));
163 result.EShearMaxVec2[itri] = Eigen::Vector3d::Constant(std::nan(""));
164 result.EShearMaxVecCur1[itri] = Eigen::Vector3d::Constant(std::nan(""));
165 result.EShearMaxVecCur2[itri] = Eigen::Vector3d::Constant(std::nan(""));
166 result.eShearMaxVec1[itri] = Eigen::Vector3d::Constant(std::nan(""));
167 result.eShearMaxVec2[itri] = Eigen::Vector3d::Constant(std::nan(""));
168 result.Eeq[itri] = std::nan("");
169 result.eeq[itri] = std::nan("");
170 continue;
171 }
172
173 // Cauchy-Green deformation tensor C = F^T * F (MATLAB line 184)
174 result.Cmat[itri] = result.Fmat[itri].transpose() * result.Fmat[itri];
175
176 // Principal stretches (MATLAB lines 185-191)
177 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eigC(result.Cmat[itri]);
178 Eigen::Vector3d eigvals_C = eigC.eigenvalues();
179 Eigen::Vector3d lamdas = eigvals_C.array().sqrt();
180
181 // Find and remove the stretch closest to 1
182 int min_idx = 0;
183 double min_diff = std::abs(lamdas(0) - 1.0);
184 for (int i = 1; i < 3; ++i) {
185 double diff = std::abs(lamdas(i) - 1.0);
186 if (diff < min_diff) {
187 min_diff = diff;
188 min_idx = i;
189 }
190 }
191
192 std::vector<double> lamda_vec;
193 for (int i = 0; i < 3; ++i) {
194 if (i != min_idx) lamda_vec.push_back(lamdas(i));
195 }
196 result.Lamda1[itri] = lamda_vec[0];
197 result.Lamda2[itri] = lamda_vec[1];
198
199 // Dilatation J = det(F) (MATLAB line 194)
200 result.J[itri] = result.Fmat[itri].determinant();
201
202 // Lagrangian finite strain tensor E = 0.5*(F^T*F - I) (MATLAB line 197)
203 result.Emat[itri] = 0.5 * (result.Fmat[itri].transpose() * result.Fmat[itri] - Eigen::Matrix3d::Identity());
204
205 // Eulerian-Almansi finite strain tensor e = 0.5*(I - inv(F*F^T)) (MATLAB line 200)
206 Eigen::Matrix3d FFT = result.Fmat[itri] * result.Fmat[itri].transpose();
207 result.emat[itri] = 0.5 * (Eigen::Matrix3d::Identity() - FFT.inverse());
208
209 // Strain magnitudes (Frobenius norm) (MATLAB lines 202-206)
210 result.Emgn[itri] = result.Emat[itri].norm();
211 result.emgn[itri] = result.emat[itri].norm();
212
213 // Lagrangian principal strains (MATLAB lines 208-222)
214 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eigE(result.Emat[itri]);
215 Eigen::Vector3d eigvals_E = eigE.eigenvalues();
216 Eigen::Matrix3d eigvecs_E = eigE.eigenvectors();
217
218 // Find eigenvector closest to D3 (normal direction)
219 int D3_idx = 0;
220 double max_dot = std::abs(eigvecs_E.col(0).dot(result.D3[itri]));
221 for (int i = 1; i < 3; ++i) {
222 double dot_val = std::abs(eigvecs_E.col(i).dot(result.D3[itri]));
223 if (dot_val > max_dot) {
224 max_dot = dot_val;
225 D3_idx = i;
226 }
227 }
228
229 // Get planar eigenvectors (not D3)
230 std::vector<int> plan_idx;
231 for (int i = 0; i < 3; ++i) {
232 if (i != D3_idx) plan_idx.push_back(i);
233 }
234
235 // Find min and max planar strains
236 double E_plan1 = eigvals_E(plan_idx[0]);
237 double E_plan2 = eigvals_E(plan_idx[1]);
238
239 if (E_plan1 < E_plan2) {
240 result.Epc1[itri] = E_plan1;
241 result.Epc2[itri] = E_plan2;
242 result.Epc1vec[itri] = eigvecs_E.col(plan_idx[0]);
243 result.Epc2vec[itri] = eigvecs_E.col(plan_idx[1]);
244 } else {
245 result.Epc1[itri] = E_plan2;
246 result.Epc2[itri] = E_plan1;
247 result.Epc1vec[itri] = eigvecs_E.col(plan_idx[1]);
248 result.Epc2vec[itri] = eigvecs_E.col(plan_idx[0]);
249 }
250
251 // Transform principal strain directions to current configuration
252 result.Epc1vecCur[itri] = result.Fmat[itri] * result.Epc1vec[itri];
253 result.Epc1vecCur[itri].normalize();
254 result.Epc2vecCur[itri] = result.Fmat[itri] * result.Epc2vec[itri];
255 result.Epc2vecCur[itri].normalize();
256
257 // Eulerian principal strains (MATLAB lines 224-233)
258 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eige(result.emat[itri]);
259 Eigen::Vector3d eigvals_e = eige.eigenvalues();
260 Eigen::Matrix3d eigvecs_e = eige.eigenvectors();
261
262 // Find eigenvector closest to d3
263 int d3_idx = 0;
264 max_dot = std::abs(eigvecs_e.col(0).dot(result.d3[itri]));
265 for (int i = 1; i < 3; ++i) {
266 double dot_val = std::abs(eigvecs_e.col(i).dot(result.d3[itri]));
267 if (dot_val > max_dot) {
268 max_dot = dot_val;
269 d3_idx = i;
270 }
271 }
272
273 plan_idx.clear();
274 for (int i = 0; i < 3; ++i) {
275 if (i != d3_idx) plan_idx.push_back(i);
276 }
277
278 double e_plan1 = eigvals_e(plan_idx[0]);
279 double e_plan2 = eigvals_e(plan_idx[1]);
280
281 if (e_plan1 < e_plan2) {
282 result.epc1[itri] = e_plan1;
283 result.epc2[itri] = e_plan2;
284 result.epc1vec[itri] = eigvecs_e.col(plan_idx[0]);
285 result.epc2vec[itri] = eigvecs_e.col(plan_idx[1]);
286 } else {
287 result.epc1[itri] = e_plan2;
288 result.epc2[itri] = e_plan1;
289 result.epc1vec[itri] = eigvecs_e.col(plan_idx[1]);
290 result.epc2vec[itri] = eigvecs_e.col(plan_idx[0]);
291 }
292
293 // Max shear strain (MATLAB lines 235-245)
294 const double inv_sqrt2 = 1.0 / std::sqrt(2.0);
295
296 // Lagrangian
297 result.EShearMax[itri] = 0.5 * (result.Epc2[itri] - result.Epc1[itri]);
298 result.EShearMaxVec1[itri] = inv_sqrt2 * (result.Epc1vec[itri] + result.Epc2vec[itri]);
299 result.EShearMaxVec2[itri] = inv_sqrt2 * (result.Epc2vec[itri] - result.Epc1vec[itri]);
300 result.EShearMaxVecCur1[itri] = inv_sqrt2 * (result.Epc1vecCur[itri] + result.Epc2vecCur[itri]);
301 result.EShearMaxVecCur2[itri] = inv_sqrt2 * (result.Epc2vecCur[itri] - result.Epc1vecCur[itri]);
302
303 // Eulerian
304 result.eShearMax[itri] = 0.5 * (result.epc2[itri] - result.epc1[itri]);
305 result.eShearMaxVec1[itri] = inv_sqrt2 * (result.epc1vec[itri] + result.epc2vec[itri]);
306 result.eShearMaxVec2[itri] = inv_sqrt2 * (result.epc2vec[itri] - result.epc1vec[itri]);
307
308 // Equivalent strain (von Mises) (MATLAB lines 247-251)
309 Eigen::Matrix3d Edev = result.Emat[itri] - (result.Emat[itri].trace() / 3.0) * Eigen::Matrix3d::Identity();
310 result.Eeq[itri] = std::sqrt((2.0 / 3.0) * (Edev.array() * Edev.array()).sum());
311
312 Eigen::Matrix3d edev = result.emat[itri] - (result.emat[itri].trace() / 3.0) * Eigen::Matrix3d::Identity();
313 result.eeq[itri] = std::sqrt((2.0 / 3.0) * (edev.array() * edev.array()).sum());
314 }
315
316 return result;
317}
318
320 const std::vector<int>& faces,
321 const std::vector<Eigen::Vector3d>& vertices_ref,
322 const std::vector<std::vector<Eigen::Vector3d>>& vertices_def,
323 bool cumulative) {
324
326 result.n_frames = vertices_def.size();
327 result.n_faces = faces.size() / 3;
328 result.frames.reserve(result.n_frames);
329
330 LOG_INFO << "Computing deformation for " << result.n_frames << " frames, "
331 << result.n_faces << " faces";
332
333 // Process each frame
334 for (size_t itime = 0; itime < result.n_frames; ++itime) {
335 // Determine reference configuration
336 const std::vector<Eigen::Vector3d>* ref_vertices;
337
338 if (cumulative) {
339 // Use initial frame as reference (MATLAB cum == 1)
340 ref_vertices = &vertices_ref;
341 } else {
342 // Use previous frame as reference (MATLAB cum == 0)
343 if (itime == 0) {
344 ref_vertices = &vertices_ref;
345 } else {
346 ref_vertices = &vertices_def[itime - 1];
347 }
348 }
349
350 // Compute deformation for this frame
352 faces,
353 *ref_vertices,
354 vertices_def[itime]
355 );
356
357 result.frames.push_back(std::move(frame_result));
358
359 if ((itime + 1) % 10 == 0 || itime == result.n_frames - 1) {
360 LOG_INFO << " Processed frame " << (itime + 1) << "/" << result.n_frames;
361 }
362 }
363
364 return result;
365}
366
367} // namespace cppxdic
Lightweight, dependency-free logging facility for CPPxDIC.
#define LOG_ERROR
Definition logging.h:172
#define LOG_WARN
Definition logging.h:171
#define LOG_INFO
Definition logging.h:170
DataSerializer - Abstract base class for format-agnostic DIC data I/O.
DeformationResult computeSingleFrameDeformation(const std::vector< int > &faces, const std::vector< Eigen::Vector3d > &vertices_ref, const std::vector< Eigen::Vector3d > &vertices_cur)
Compute deformation for a single frame.
FrameDeformationResult computeTriSurfaceDeformation(const std::vector< int > &faces, const std::vector< Eigen::Vector3d > &vertices_ref, const std::vector< std::vector< Eigen::Vector3d > > &vertices_def, bool cumulative=true)
Compute deformation and strain for triangular surface elements.
Deformation result structure Contains all computed deformation and strain measures.
std::vector< double > Area
std::vector< double > Epc1
std::vector< Eigen::Vector3d > EShearMaxVec2
std::vector< double > Epc2
std::vector< Eigen::Vector3d > d1
std::vector< Eigen::Vector3d > Epc2vec
std::vector< Eigen::Vector3d > D3
std::vector< Eigen::Vector3d > Epc2vecCur
std::vector< Eigen::Vector3d > d2
std::vector< double > Lamda1
std::vector< double > EShearMax
std::vector< Eigen::Vector3d > d3
std::vector< Eigen::Vector3d > Epc1vecCur
std::vector< Eigen::Matrix3d > emat
std::vector< Eigen::Vector3d > EShearMaxVec1
std::vector< Eigen::Vector3d > Drec1
std::vector< Eigen::Vector3d > Drec2
std::vector< Eigen::Vector3d > epc1vec
std::vector< double > epc1
std::vector< Eigen::Vector3d > D2
std::vector< Eigen::Vector3d > epc2vec
std::vector< double > Eeq
std::vector< Eigen::Vector3d > eShearMaxVec2
std::vector< double > Emgn
std::vector< Eigen::Matrix3d > Cmat
std::vector< Eigen::Vector3d > EShearMaxVecCur1
std::vector< double > eShearMax
std::vector< double > eeq
std::vector< double > Lamda2
std::vector< double > epc2
std::vector< Eigen::Matrix3d > Fmat
std::vector< double > Dnorm
std::vector< Eigen::Vector3d > Epc1vec
std::vector< Eigen::Vector3d > EShearMaxVecCur2
std::vector< double > emgn
std::vector< Eigen::Vector3d > eShearMaxVec1
std::vector< Eigen::Matrix3d > Emat
std::vector< Eigen::Vector3d > D1
Frame-based deformation result Contains deformation results for all frames.
std::vector< DeformationResult > frames