1 |
|
|
/* |
2 |
|
|
Teem: Tools to process and visualize scientific data and images . |
3 |
|
|
Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago |
4 |
|
|
Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann |
5 |
|
|
Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah |
6 |
|
|
|
7 |
|
|
This library is free software; you can redistribute it and/or |
8 |
|
|
modify it under the terms of the GNU Lesser General Public License |
9 |
|
|
(LGPL) as published by the Free Software Foundation; either |
10 |
|
|
version 2.1 of the License, or (at your option) any later version. |
11 |
|
|
The terms of redistributing and/or modifying this software also |
12 |
|
|
include exceptions to the LGPL that facilitate static linking. |
13 |
|
|
|
14 |
|
|
This library is distributed in the hope that it will be useful, |
15 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 |
|
|
Lesser General Public License for more details. |
18 |
|
|
|
19 |
|
|
You should have received a copy of the GNU Lesser General Public License |
20 |
|
|
along with this library; if not, write to Free Software Foundation, Inc., |
21 |
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 |
|
|
*/ |
23 |
|
|
|
24 |
|
|
|
25 |
|
|
#include "ten.h" |
26 |
|
|
#include "privateTen.h" |
27 |
|
|
|
28 |
|
|
int tenVerbose = 0; |
29 |
|
|
|
30 |
|
|
void |
31 |
|
|
tenRotateSingle_f(float tenOut[7], const float rot[9], const float tenIn[7]) { |
32 |
|
|
float rotT[9], matIn[9], tmp[9], matOut[9]; |
33 |
|
|
|
34 |
|
|
ELL_3M_TRANSPOSE(rotT, rot); |
35 |
|
|
TEN_T2M(matIn, tenIn); |
36 |
|
|
ELL_3M_MUL(tmp, matIn, rotT); |
37 |
|
|
ELL_3M_MUL(matOut, rot, tmp); |
38 |
|
|
TEN_M2T_TT(tenOut, float, matOut); |
39 |
|
|
tenOut[0] = tenIn[0]; |
40 |
|
|
return; |
41 |
|
|
} |
42 |
|
|
|
43 |
|
|
/* |
44 |
|
|
******** tenTensorCheck() |
45 |
|
|
** |
46 |
|
|
** describes if the given nrrd could be a diffusion tensor dataset, |
47 |
|
|
** either the measured DWI data or the calculated tensor data. |
48 |
|
|
** |
49 |
|
|
** We've been using 7 floats for BOTH kinds of tensor data- both the |
50 |
|
|
** measured DWI and the calculated tensor matrices. The measured data |
51 |
|
|
** comes as one anatomical image and 6 DWIs. For the calculated tensors, |
52 |
|
|
** in addition to the 6 matrix components, we keep a "threshold" value |
53 |
|
|
** which is based on the sum of all the DWIs, which describes if the |
54 |
|
|
** calculated tensor means anything or not. |
55 |
|
|
** |
56 |
|
|
** useBiff controls if biff is used to describe the problem |
57 |
|
|
*/ |
58 |
|
|
int |
59 |
|
|
tenTensorCheck(const Nrrd *nin, int wantType, int want4D, int useBiff) { |
60 |
|
|
static const char me[]="tenTensorCheck"; |
61 |
|
|
|
62 |
✗✓ |
32 |
if (!nin) { |
63 |
|
|
if (useBiff) biffAddf(TEN, "%s: got NULL pointer", me); |
64 |
|
|
return 1; |
65 |
|
|
} |
66 |
✓✗✓✗
|
32 |
if (wantType) { |
67 |
✗✓ |
32 |
if (nin->type != wantType) { |
68 |
|
|
if (useBiff) biffAddf(TEN, "%s: wanted type %s, got type %s", me, |
69 |
|
|
airEnumStr(nrrdType, wantType), |
70 |
|
|
airEnumStr(nrrdType, nin->type)); |
71 |
|
|
return 1; |
72 |
|
|
} |
73 |
|
|
} |
74 |
|
|
else { |
75 |
|
|
if (!(nin->type == nrrdTypeFloat || nin->type == nrrdTypeShort)) { |
76 |
|
|
if (useBiff) biffAddf(TEN, "%s: need data of type float or short", me); |
77 |
|
|
return 1; |
78 |
|
|
} |
79 |
|
|
} |
80 |
✓✗✗✓
|
32 |
if (want4D && !(4 == nin->dim)) { |
81 |
|
|
if (useBiff) |
82 |
|
|
biffAddf(TEN, "%s: given dimension is %d, not 4", me, nin->dim); |
83 |
|
|
return 1; |
84 |
|
|
} |
85 |
✗✓ |
16 |
if (!(7 == nin->axis[0].size)) { |
86 |
|
|
if (useBiff) { |
87 |
|
|
char stmp[AIR_STRLEN_SMALL]; |
88 |
|
|
biffAddf(TEN, "%s: axis 0 has size %s, not 7", me, |
89 |
|
|
airSprintSize_t(stmp, nin->axis[0].size)); |
90 |
|
|
} |
91 |
|
|
return 1; |
92 |
|
|
} |
93 |
|
16 |
return 0; |
94 |
|
16 |
} |
95 |
|
|
|
96 |
|
|
int |
97 |
|
|
tenMeasurementFrameReduce(Nrrd *nout, const Nrrd *nin) { |
98 |
|
|
static const char me[]="tenMeasurementFrameReduce"; |
99 |
|
|
double MF[9], MFT[9], tenMeasr[9], tenWorld[9]; |
100 |
|
|
float *tdata; |
101 |
|
|
size_t ii, nn; |
102 |
|
|
unsigned int si, sj; |
103 |
|
|
|
104 |
|
|
if (!(nout && nin)) { |
105 |
|
|
biffAddf(TEN, "%s: got NULL pointer", me); |
106 |
|
|
return 1; |
107 |
|
|
} |
108 |
|
|
if (tenTensorCheck(nin, nrrdTypeFloat, AIR_TRUE, AIR_TRUE)) { |
109 |
|
|
biffAddf(TEN, "%s: ", me); |
110 |
|
|
return 1; |
111 |
|
|
} |
112 |
|
|
if (3 != nin->spaceDim) { |
113 |
|
|
biffAddf(TEN, "%s: input nrrd needs 3-D (not %u-D) space dimension", |
114 |
|
|
me, nin->spaceDim); |
115 |
|
|
return 1; |
116 |
|
|
} |
117 |
|
|
/* |
118 |
|
|
[0] [1] [2] [0][0] [1][0] [2][0] |
119 |
|
|
[3] [4] [5] = [0][1] [1][1] [2][1] |
120 |
|
|
[6] [7] [8] [0][2] [1][2] [2][2] |
121 |
|
|
*/ |
122 |
|
|
MF[0] = nin->measurementFrame[0][0]; |
123 |
|
|
MF[1] = nin->measurementFrame[1][0]; |
124 |
|
|
MF[2] = nin->measurementFrame[2][0]; |
125 |
|
|
MF[3] = nin->measurementFrame[0][1]; |
126 |
|
|
MF[4] = nin->measurementFrame[1][1]; |
127 |
|
|
MF[5] = nin->measurementFrame[2][1]; |
128 |
|
|
MF[6] = nin->measurementFrame[0][2]; |
129 |
|
|
MF[7] = nin->measurementFrame[1][2]; |
130 |
|
|
MF[8] = nin->measurementFrame[2][2]; |
131 |
|
|
if (!ELL_3M_EXISTS(MF)) { |
132 |
|
|
biffAddf(TEN, "%s: 3x3 measurement frame doesn't exist", me); |
133 |
|
|
return 1; |
134 |
|
|
} |
135 |
|
|
ELL_3M_TRANSPOSE(MFT, MF); |
136 |
|
|
|
137 |
|
|
if (nout != nin) { |
138 |
|
|
if (nrrdCopy(nout, nin)) { |
139 |
|
|
biffAddf(TEN, "%s: trouble with initial copy", me); |
140 |
|
|
return 1; |
141 |
|
|
} |
142 |
|
|
} |
143 |
|
|
nn = nrrdElementNumber(nout)/nout->axis[0].size; |
144 |
|
|
tdata = (float*)(nout->data); |
145 |
|
|
for (ii=0; ii<nn; ii++) { |
146 |
|
|
TEN_T2M(tenMeasr, tdata); |
147 |
|
|
ell_3m_mul_d(tenWorld, MF, tenMeasr); |
148 |
|
|
ell_3m_mul_d(tenWorld, tenWorld, MFT); |
149 |
|
|
TEN_M2T_TT(tdata, float, tenWorld); |
150 |
|
|
tdata += 7; |
151 |
|
|
} |
152 |
|
|
for (si=0; si<NRRD_SPACE_DIM_MAX; si++) { |
153 |
|
|
for (sj=0; sj<NRRD_SPACE_DIM_MAX; sj++) { |
154 |
|
|
nout->measurementFrame[si][sj] = AIR_NAN; |
155 |
|
|
} |
156 |
|
|
} |
157 |
|
|
for (si=0; si<3; si++) { |
158 |
|
|
for (sj=0; sj<3; sj++) { |
159 |
|
|
nout->measurementFrame[si][sj] = (si == sj); |
160 |
|
|
} |
161 |
|
|
} |
162 |
|
|
|
163 |
|
|
return 0; |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
int |
167 |
|
|
tenExpand2D(Nrrd *nout, const Nrrd *nin, double scale, double thresh) { |
168 |
|
|
static const char me[]="tenExpand2D"; |
169 |
|
|
size_t N, I, sx, sy; |
170 |
|
|
float *masked, *redund; |
171 |
|
|
|
172 |
|
|
if (!( nout && nin && AIR_EXISTS(thresh) )) { |
173 |
|
|
biffAddf(TEN, "%s: got NULL pointer or non-existent threshold", me); |
174 |
|
|
return 1; |
175 |
|
|
} |
176 |
|
|
if (nout == nin) { |
177 |
|
|
biffAddf(TEN, "%s: sorry, need different nrrds for input and output", me); |
178 |
|
|
return 1; |
179 |
|
|
} |
180 |
|
|
/* HEY copy and paste and tweak of tenTensorCheck */ |
181 |
|
|
if (!nin) { |
182 |
|
|
biffAddf(TEN, "%s: got NULL pointer", me); |
183 |
|
|
return 1; |
184 |
|
|
} |
185 |
|
|
if (nin->type != nrrdTypeFloat) { |
186 |
|
|
biffAddf(TEN, "%s: wanted type %s, got type %s", me, |
187 |
|
|
airEnumStr(nrrdType, nrrdTypeFloat), |
188 |
|
|
airEnumStr(nrrdType, nin->type)); |
189 |
|
|
return 1; |
190 |
|
|
} else { |
191 |
|
|
if (!(nin->type == nrrdTypeFloat || nin->type == nrrdTypeShort)) { |
192 |
|
|
biffAddf(TEN, "%s: need data of type float or short", me); |
193 |
|
|
return 1; |
194 |
|
|
} |
195 |
|
|
} |
196 |
|
|
if (3 != nin->dim) { |
197 |
|
|
biffAddf(TEN, "%s: given dimension is %u, not 3", me, nin->dim); |
198 |
|
|
return 1; |
199 |
|
|
} |
200 |
|
|
if (!(4 == nin->axis[0].size)) { |
201 |
|
|
char stmp[AIR_STRLEN_SMALL]; |
202 |
|
|
biffAddf(TEN, "%s: axis 0 has size %s, not 4", me, |
203 |
|
|
airSprintSize_t(stmp, nin->axis[0].size)); |
204 |
|
|
return 1; |
205 |
|
|
} |
206 |
|
|
|
207 |
|
|
sx = nin->axis[1].size; |
208 |
|
|
sy = nin->axis[2].size; |
209 |
|
|
N = sx*sy; |
210 |
|
|
if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 3, |
211 |
|
|
AIR_CAST(size_t, 4), sx, sy)) { |
212 |
|
|
biffMovef(TEN, NRRD, "%s: trouble", me); |
213 |
|
|
return 1; |
214 |
|
|
} |
215 |
|
|
for (I=0; I<=N-1; I++) { |
216 |
|
|
masked = (float*)(nin->data) + I*4; |
217 |
|
|
redund = (float*)(nout->data) + I*4; |
218 |
|
|
if (masked[0] < thresh) { |
219 |
|
|
ELL_4V_ZERO_SET(redund); |
220 |
|
|
continue; |
221 |
|
|
} |
222 |
|
|
redund[0] = masked[1]; |
223 |
|
|
redund[1] = masked[2]; |
224 |
|
|
redund[2] = masked[2]; |
225 |
|
|
redund[3] = masked[3]; |
226 |
|
|
ELL_4V_SCALE(redund, AIR_CAST(float, scale), redund); |
227 |
|
|
} |
228 |
|
|
if (nrrdAxisInfoCopy(nout, nin, NULL, |
229 |
|
|
NRRD_AXIS_INFO_SIZE_BIT)) { |
230 |
|
|
biffMovef(TEN, NRRD, "%s: trouble", me); |
231 |
|
|
return 1; |
232 |
|
|
} |
233 |
|
|
/* by call above we just copied axis-0 kind, which might be wrong; |
234 |
|
|
we actually know the output kind now, so we might as well set it */ |
235 |
|
|
nout->axis[0].kind = nrrdKind2DMatrix; |
236 |
|
|
if (nrrdBasicInfoCopy(nout, nin, |
237 |
|
|
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE)) { |
238 |
|
|
biffAddf(TEN, "%s:", me); |
239 |
|
|
return 1; |
240 |
|
|
} |
241 |
|
|
return 0; |
242 |
|
|
} |
243 |
|
|
|
244 |
|
|
int |
245 |
|
|
tenExpand(Nrrd *nout, const Nrrd *nin, double scale, double thresh) { |
246 |
|
|
static const char me[]="tenExpand"; |
247 |
|
|
size_t N, I, sx, sy, sz; |
248 |
|
|
float *seven, *nine; |
249 |
|
|
|
250 |
|
|
if (!( nout && nin && AIR_EXISTS(thresh) )) { |
251 |
|
|
biffAddf(TEN, "%s: got NULL pointer or non-existent threshold", me); |
252 |
|
|
return 1; |
253 |
|
|
} |
254 |
|
|
if (nout == nin) { |
255 |
|
|
biffAddf(TEN, "%s: sorry, need different nrrds for input and output", me); |
256 |
|
|
return 1; |
257 |
|
|
} |
258 |
|
|
if (tenTensorCheck(nin, nrrdTypeFloat, AIR_TRUE, AIR_TRUE)) { |
259 |
|
|
biffAddf(TEN, "%s: ", me); |
260 |
|
|
return 1; |
261 |
|
|
} |
262 |
|
|
|
263 |
|
|
sx = nin->axis[1].size; |
264 |
|
|
sy = nin->axis[2].size; |
265 |
|
|
sz = nin->axis[3].size; |
266 |
|
|
N = sx*sy*sz; |
267 |
|
|
if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 4, |
268 |
|
|
AIR_CAST(size_t, 9), sx, sy, sz)) { |
269 |
|
|
biffMovef(TEN, NRRD, "%s: trouble", me); |
270 |
|
|
return 1; |
271 |
|
|
} |
272 |
|
|
for (I=0; I<=N-1; I++) { |
273 |
|
|
seven = (float*)(nin->data) + I*7; |
274 |
|
|
nine = (float*)(nout->data) + I*9; |
275 |
|
|
if (seven[0] < thresh) { |
276 |
|
|
ELL_3M_ZERO_SET(nine); |
277 |
|
|
continue; |
278 |
|
|
} |
279 |
|
|
TEN_T2M(nine, seven); |
280 |
|
|
ELL_3M_SCALE(nine, AIR_CAST(float, scale), nine); |
281 |
|
|
} |
282 |
|
|
if (nrrdAxisInfoCopy(nout, nin, NULL, |
283 |
|
|
NRRD_AXIS_INFO_SIZE_BIT)) { |
284 |
|
|
biffMovef(TEN, NRRD, "%s: trouble", me); |
285 |
|
|
return 1; |
286 |
|
|
} |
287 |
|
|
/* by call above we just copied axis-0 kind, which might be wrong; |
288 |
|
|
we actually know the output kind now, so we might as well set it */ |
289 |
|
|
nout->axis[0].kind = nrrdKind3DMatrix; |
290 |
|
|
if (nrrdBasicInfoCopy(nout, nin, |
291 |
|
|
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE)) { |
292 |
|
|
biffAddf(TEN, "%s:", me); |
293 |
|
|
return 1; |
294 |
|
|
} |
295 |
|
|
/* Tue Sep 13 18:36:45 EDT 2005: why did I do this? |
296 |
|
|
nout->axis[0].label = (char *)airFree(nout->axis[0].label); |
297 |
|
|
nout->axis[0].label = airStrdup("matrix"); |
298 |
|
|
*/ |
299 |
|
|
|
300 |
|
|
return 0; |
301 |
|
|
} |
302 |
|
|
|
303 |
|
|
int |
304 |
|
|
tenShrink(Nrrd *tseven, const Nrrd *nconf, const Nrrd *tnine) { |
305 |
|
|
static const char me[]="tenShrink"; |
306 |
|
|
size_t I, N, sx, sy, sz; |
307 |
|
|
float *seven, *conf, *nine; |
308 |
|
|
|
309 |
|
|
if (!(tseven && tnine)) { |
310 |
|
|
biffAddf(TEN, "%s: got NULL pointer", me); |
311 |
|
|
return 1; |
312 |
|
|
} |
313 |
|
|
if (tseven == tnine) { |
314 |
|
|
biffAddf(TEN, "%s: sorry, need different nrrds for input and output", me); |
315 |
|
|
return 1; |
316 |
|
|
} |
317 |
|
|
if (!( nrrdTypeFloat == tnine->type && |
318 |
|
|
4 == tnine->dim && |
319 |
|
|
9 == tnine->axis[0].size )) { |
320 |
|
|
char stmp[AIR_STRLEN_SMALL]; |
321 |
|
|
biffAddf(TEN, "%s: type not %s (was %s) or dim not 4 (was %d) " |
322 |
|
|
"or first axis size not 9 (was %s)", me, |
323 |
|
|
airEnumStr(nrrdType, nrrdTypeFloat), |
324 |
|
|
airEnumStr(nrrdType, tnine->type), tnine->dim, |
325 |
|
|
airSprintSize_t(stmp, tnine->axis[0].size)); |
326 |
|
|
return 1; |
327 |
|
|
} |
328 |
|
|
sx = tnine->axis[1].size; |
329 |
|
|
sy = tnine->axis[2].size; |
330 |
|
|
sz = tnine->axis[3].size; |
331 |
|
|
if (nconf) { |
332 |
|
|
if (!( nrrdTypeFloat == nconf->type && |
333 |
|
|
3 == nconf->dim && |
334 |
|
|
sx == nconf->axis[0].size && |
335 |
|
|
sy == nconf->axis[1].size && |
336 |
|
|
sz == nconf->axis[2].size )) { |
337 |
|
|
biffAddf(TEN, "%s: confidence type not %s (was %s) or dim not 3 (was %d) " |
338 |
|
|
"or dimensions didn't match tensor volume", me, |
339 |
|
|
airEnumStr(nrrdType, nrrdTypeFloat), |
340 |
|
|
airEnumStr(nrrdType, nconf->type), |
341 |
|
|
nconf->dim); |
342 |
|
|
return 1; |
343 |
|
|
} |
344 |
|
|
} |
345 |
|
|
if (nrrdMaybeAlloc_va(tseven, nrrdTypeFloat, 4, |
346 |
|
|
AIR_CAST(size_t, 7), sx, sy, sz)) { |
347 |
|
|
biffMovef(TEN, NRRD, "%s: trouble allocating output", me); |
348 |
|
|
return 1; |
349 |
|
|
} |
350 |
|
|
seven = (float *)tseven->data; |
351 |
|
|
conf = nconf ? (float *)nconf->data : NULL; |
352 |
|
|
nine = (float *)tnine->data; |
353 |
|
|
N = sx*sy*sz; |
354 |
|
|
for (I=0; I<N; I++) { |
355 |
|
|
TEN_M2T_TT(seven, float, nine); |
356 |
|
|
seven[0] = conf ? conf[I] : 1.0f; |
357 |
|
|
seven += 7; |
358 |
|
|
nine += 9; |
359 |
|
|
} |
360 |
|
|
if (nrrdAxisInfoCopy(tseven, tnine, NULL, |
361 |
|
|
NRRD_AXIS_INFO_SIZE_BIT)) { |
362 |
|
|
biffMovef(TEN, NRRD, "%s: trouble", me); |
363 |
|
|
return 1; |
364 |
|
|
} |
365 |
|
|
/* by call above we just copied axis-0 kind, which might be wrong; |
366 |
|
|
we actually know the output kind now, so we might as well set it */ |
367 |
|
|
tseven->axis[0].kind = nrrdKind3DMaskedSymMatrix; |
368 |
|
|
if (nrrdBasicInfoCopy(tseven, tnine, |
369 |
|
|
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE)) { |
370 |
|
|
biffAddf(TEN, "%s:", me); |
371 |
|
|
return 1; |
372 |
|
|
} |
373 |
|
|
/* Wed Dec 3 11:22:32 EST 2008: no real need to set label string */ |
374 |
|
|
|
375 |
|
|
return 0; |
376 |
|
|
} |
377 |
|
|
|
378 |
|
|
/* |
379 |
|
|
******** tenEigensolve_f |
380 |
|
|
** |
381 |
|
|
** uses ell_3m_eigensolve_d to get the eigensystem of a single tensor |
382 |
|
|
** disregards the confidence value t[0] |
383 |
|
|
** |
384 |
|
|
** return is same as ell_3m_eigensolve_d, which is same as ell_cubic |
385 |
|
|
** |
386 |
|
|
** NOTE: Even in the post-Teem-1.7 switch from column-major to |
387 |
|
|
** row-major- its still the case that the eigenvectors are at |
388 |
|
|
** evec+0, evec+3, evec+6: this means that they USED to be the |
389 |
|
|
** "columns" of the matrix, and NOW they're the rows. |
390 |
|
|
** |
391 |
|
|
** This does NOT use biff |
392 |
|
|
*/ |
393 |
|
|
int |
394 |
|
|
tenEigensolve_f(float _eval[3], float _evec[9], const float t[7]) { |
395 |
|
|
double m[9], eval[3], evec[9], trc, iso[9]; |
396 |
|
|
int ret; |
397 |
|
|
|
398 |
|
|
TEN_T2M(m, t); |
399 |
|
|
trc = ELL_3M_TRACE(m)/3.0; |
400 |
|
|
ELL_3M_IDENTITY_SET(iso); |
401 |
|
|
ELL_3M_SCALE_SET(iso, -trc, -trc, -trc); |
402 |
|
|
ELL_3M_ADD2(m, m, iso); |
403 |
|
|
if (_evec) { |
404 |
|
|
ret = ell_3m_eigensolve_d(eval, evec, m, AIR_TRUE); |
405 |
|
|
if (tenVerbose > 4) { |
406 |
|
|
fprintf(stderr, "---- cubic ret = %d\n", ret); |
407 |
|
|
fprintf(stderr, "tensor = {\n"); |
408 |
|
|
fprintf(stderr, " % 15.7f,\n", t[1]); |
409 |
|
|
fprintf(stderr, " % 15.7f,\n", t[2]); |
410 |
|
|
fprintf(stderr, " % 15.7f,\n", t[3]); |
411 |
|
|
fprintf(stderr, " % 15.7f,\n", t[4]); |
412 |
|
|
fprintf(stderr, " % 15.7f,\n", t[5]); |
413 |
|
|
fprintf(stderr, " % 15.7f}\n", t[6]); |
414 |
|
|
fprintf(stderr, "roots = %d:\n", ret); |
415 |
|
|
fprintf(stderr, " % 31.15f\n", trc + eval[0]); |
416 |
|
|
fprintf(stderr, " % 31.15f\n", trc + eval[1]); |
417 |
|
|
fprintf(stderr, " % 31.15f\n", trc + eval[2]); |
418 |
|
|
} |
419 |
|
|
ELL_3V_SET_TT(_eval, float, eval[0] + trc, eval[1] + trc, eval[2] + trc); |
420 |
|
|
ELL_3M_COPY_TT(_evec, float, evec); |
421 |
|
|
if (ell_cubic_root_single_double == ret) { |
422 |
|
|
/* this was added to fix a stupid problem with very nearly |
423 |
|
|
isotropic glyphs, used for demonstration figures */ |
424 |
|
|
if (eval[0] == eval[1]) { |
425 |
|
|
ELL_3V_CROSS(_evec+6, _evec+0, _evec+3); |
426 |
|
|
} else { |
427 |
|
|
ELL_3V_CROSS(_evec+0, _evec+3, _evec+6); |
428 |
|
|
} |
429 |
|
|
} |
430 |
|
|
if ((tenVerbose > 1) && _eval[2] < 0) { |
431 |
|
|
fprintf(stderr, "tenEigensolve_f -------------\n"); |
432 |
|
|
fprintf(stderr, "% 15.7f % 15.7f % 15.7f\n", |
433 |
|
|
t[1], t[2], t[3]); |
434 |
|
|
fprintf(stderr, "% 15.7f % 15.7f % 15.7f\n", |
435 |
|
|
t[2], t[4], t[5]); |
436 |
|
|
fprintf(stderr, "% 15.7f % 15.7f % 15.7f\n", |
437 |
|
|
t[3], t[5], t[6]); |
438 |
|
|
fprintf(stderr, " --> % 15.7f % 15.7f % 15.7f\n", |
439 |
|
|
_eval[0], _eval[1], _eval[2]); |
440 |
|
|
} |
441 |
|
|
} else { |
442 |
|
|
/* caller only wants eigenvalues */ |
443 |
|
|
ret = ell_3m_eigenvalues_d(eval, m, AIR_TRUE); |
444 |
|
|
ELL_3V_SET_TT(_eval, float, eval[0] + trc, eval[1] + trc, eval[2] + trc); |
445 |
|
|
} |
446 |
|
|
return ret; |
447 |
|
|
} |
448 |
|
|
|
449 |
|
|
/* HEY: cut and paste !! */ |
450 |
|
|
int |
451 |
|
|
tenEigensolve_d(double _eval[3], double evec[9], const double t[7]) { |
452 |
|
|
double m[9], eval[3], trc, iso[9]; |
453 |
|
|
int ret; |
454 |
|
|
|
455 |
|
|
TEN_T2M(m, t); |
456 |
|
|
trc = ELL_3M_TRACE(m)/3.0; |
457 |
|
|
ELL_3M_SCALE_SET(iso, -trc, -trc, -trc); |
458 |
|
|
ELL_3M_ADD2(m, m, iso); |
459 |
|
|
/* |
460 |
|
|
printf("!%s: t = %g %g %g; %g %g; %g\n", "tenEigensolve_f", |
461 |
|
|
t[1], t[2], t[3], t[4], t[5], t[6]); |
462 |
|
|
printf("!%s: m = %g %g %g; %g %g %g; %g %g %g\n", "tenEigensolve_f", |
463 |
|
|
m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); |
464 |
|
|
*/ |
465 |
|
|
if (evec) { |
466 |
|
|
ret = ell_3m_eigensolve_d(eval, evec, m, AIR_TRUE); |
467 |
|
|
ELL_3V_SET(_eval, eval[0] + trc, eval[1] + trc, eval[2] + trc); |
468 |
|
|
if (ell_cubic_root_single_double == ret) { |
469 |
|
|
/* this was added to fix a stupid problem with very nearly |
470 |
|
|
isotropic glyphs, used for demonstration figures */ |
471 |
|
|
if (eval[0] == eval[1]) { |
472 |
|
|
ELL_3V_CROSS(evec+6, evec+0, evec+3); |
473 |
|
|
} else { |
474 |
|
|
ELL_3V_CROSS(evec+0, evec+3, evec+6); |
475 |
|
|
} |
476 |
|
|
} |
477 |
|
|
} else { |
478 |
|
|
/* caller only wants eigenvalues */ |
479 |
|
|
ret = ell_3m_eigenvalues_d(eval, m, AIR_TRUE); |
480 |
|
|
ELL_3V_SET(_eval, eval[0] + trc, eval[1] + trc, eval[2] + trc); |
481 |
|
|
} |
482 |
|
|
return ret; |
483 |
|
|
} |
484 |
|
|
|
485 |
|
|
|
486 |
|
|
|
487 |
|
|
/* lop A |
488 |
|
|
fprintf(stderr, "################################### I = %d\n", (int)I); |
489 |
|
|
tenEigensolve(teval, tevec, out); |
490 |
|
|
fprintf(stderr, "evals: (%g %g %g) %g %g %g --> %g %g %g\n", |
491 |
|
|
AIR_ABS(eval[0] - teval[0]) + 1, |
492 |
|
|
AIR_ABS(eval[1] - teval[1]) + 1, |
493 |
|
|
AIR_ABS(eval[2] - teval[2]) + 1, |
494 |
|
|
eval[0], eval[1], eval[2], |
495 |
|
|
teval[0], teval[1], teval[2]); |
496 |
|
|
fprintf(stderr, " tevec lens: %g %g %g\n", ELL_3V_LEN(tevec+3*0), |
497 |
|
|
ELL_3V_LEN(tevec+3*1), ELL_3V_LEN(tevec+3*2)); |
498 |
|
|
ELL_3V_CROSS(tmp1, evec+3*0, evec+3*1); tmp2[0] = ELL_3V_LEN(tmp1); |
499 |
|
|
ELL_3V_CROSS(tmp1, evec+3*0, evec+3*2); tmp2[1] = ELL_3V_LEN(tmp1); |
500 |
|
|
ELL_3V_CROSS(tmp1, evec+3*1, evec+3*2); tmp2[2] = ELL_3V_LEN(tmp1); |
501 |
|
|
fprintf(stderr, " evec[0] = %g %g %g\n", |
502 |
|
|
(evec+3*0)[0], (evec+3*0)[1], (evec+3*0)[2]); |
503 |
|
|
fprintf(stderr, " evec[1] = %g %g %g\n", |
504 |
|
|
(evec+3*1)[0], (evec+3*1)[1], (evec+3*1)[2]); |
505 |
|
|
fprintf(stderr, " evec[2] = %g %g %g\n", |
506 |
|
|
(evec+3*2)[0], (evec+3*2)[1], (evec+3*2)[2]); |
507 |
|
|
fprintf(stderr, " evec crosses: %g %g %g\n", |
508 |
|
|
tmp2[0], tmp2[1], tmp2[2]); |
509 |
|
|
ELL_3V_CROSS(tmp1, tevec+3*0, tevec+3*1); tmp2[0] = ELL_3V_LEN(tmp1); |
510 |
|
|
ELL_3V_CROSS(tmp1, tevec+3*0, tevec+3*2); tmp2[1] = ELL_3V_LEN(tmp1); |
511 |
|
|
ELL_3V_CROSS(tmp1, tevec+3*1, tevec+3*2); tmp2[2] = ELL_3V_LEN(tmp1); |
512 |
|
|
fprintf(stderr, " tevec[0] = %g %g %g\n", |
513 |
|
|
(tevec+3*0)[0], (tevec+3*0)[1], (tevec+3*0)[2]); |
514 |
|
|
fprintf(stderr, " tevec[1] = %g %g %g\n", |
515 |
|
|
(tevec+3*1)[0], (tevec+3*1)[1], (tevec+3*1)[2]); |
516 |
|
|
fprintf(stderr, " tevec[2] = %g %g %g\n", |
517 |
|
|
(tevec+3*2)[0], (tevec+3*2)[1], (tevec+3*2)[2]); |
518 |
|
|
fprintf(stderr, " tevec crosses: %g %g %g\n", |
519 |
|
|
tmp2[0], tmp2[1], tmp2[2]); |
520 |
|
|
if (tmp2[1] < 0.5) { |
521 |
|
|
fprintf(stderr, "(panic)\n"); |
522 |
|
|
exit(0); |
523 |
|
|
} |
524 |
|
|
*/ |
525 |
|
|
|
526 |
|
|
void |
527 |
|
|
tenMakeSingle_f(float ten[7], float conf, const float eval[3], const float evec[9]) { |
528 |
|
|
double tmpMat1[9], tmpMat2[9], diag[9], evecT[9]; |
529 |
|
|
|
530 |
|
|
ELL_3M_ZERO_SET(diag); |
531 |
|
|
ELL_3M_DIAG_SET(diag, eval[0], eval[1], eval[2]); |
532 |
|
|
ELL_3M_TRANSPOSE(evecT, evec); |
533 |
|
|
ELL_3M_MUL(tmpMat1, diag, evec); |
534 |
|
|
ELL_3M_MUL(tmpMat2, evecT, tmpMat1); |
535 |
|
|
ten[0] = conf; |
536 |
|
|
TEN_M2T_TT(ten, float, tmpMat2); |
537 |
|
|
return; |
538 |
|
|
} |
539 |
|
|
|
540 |
|
|
/* HEY: copy and paste! */ |
541 |
|
|
void |
542 |
|
|
tenMakeSingle_d(double ten[7], double conf, const double eval[3], const double evec[9]) { |
543 |
|
|
double tmpMat1[9], tmpMat2[9], diag[9], evecT[9]; |
544 |
|
|
|
545 |
|
|
ELL_3M_ZERO_SET(diag); |
546 |
|
|
ELL_3M_DIAG_SET(diag, eval[0], eval[1], eval[2]); |
547 |
|
|
ELL_3M_TRANSPOSE(evecT, evec); |
548 |
|
|
ELL_3M_MUL(tmpMat1, diag, evec); |
549 |
|
|
ELL_3M_MUL(tmpMat2, evecT, tmpMat1); |
550 |
|
|
ten[0] = conf; |
551 |
|
|
TEN_M2T_TT(ten, float, tmpMat2); |
552 |
|
|
return; |
553 |
|
|
} |
554 |
|
|
|
555 |
|
|
/* |
556 |
|
|
******** tenMake |
557 |
|
|
** |
558 |
|
|
** create a tensor nrrd from nrrds of confidence, eigenvalues, and |
559 |
|
|
** eigenvectors |
560 |
|
|
*/ |
561 |
|
|
int |
562 |
|
|
tenMake(Nrrd *nout, const Nrrd *nconf, const Nrrd *neval, const Nrrd *nevec) { |
563 |
|
|
static const char me[]="tenTensorMake"; |
564 |
|
|
size_t I, N, sx, sy, sz; |
565 |
|
|
float *out, *conf, *eval, *evec; |
566 |
|
|
int map[4]; |
567 |
|
|
/* float teval[3], tevec[9], tmp1[3], tmp2[3]; */ |
568 |
|
|
char stmp[7][AIR_STRLEN_SMALL]; |
569 |
|
|
|
570 |
|
|
if (!(nout && nconf && neval && nevec)) { |
571 |
|
|
biffAddf(TEN, "%s: got NULL pointer", me); |
572 |
|
|
return 1; |
573 |
|
|
} |
574 |
|
|
if (nrrdCheck(nconf) || nrrdCheck(neval) || nrrdCheck(nevec)) { |
575 |
|
|
biffMovef(TEN, NRRD, "%s: didn't get three valid nrrds", me); |
576 |
|
|
return 1; |
577 |
|
|
} |
578 |
|
|
if (!( 3 == nconf->dim && nrrdTypeFloat == nconf->type )) { |
579 |
|
|
biffAddf(TEN, "%s: first nrrd not a confidence volume " |
580 |
|
|
"(dim = %d, not 3; type = %s, not %s)", me, |
581 |
|
|
nconf->dim, airEnumStr(nrrdType, nconf->type), |
582 |
|
|
airEnumStr(nrrdType, nrrdTypeFloat)); |
583 |
|
|
return 1; |
584 |
|
|
} |
585 |
|
|
sx = nconf->axis[0].size; |
586 |
|
|
sy = nconf->axis[1].size; |
587 |
|
|
sz = nconf->axis[2].size; |
588 |
|
|
if (!( 4 == neval->dim && 4 == nevec->dim && |
589 |
|
|
nrrdTypeFloat == neval->type && |
590 |
|
|
nrrdTypeFloat == nevec->type )) { |
591 |
|
|
biffAddf(TEN, "%s: second and third nrrd aren't both 4-D (%d and %d) " |
592 |
|
|
"and type %s (%s and %s)", |
593 |
|
|
me, neval->dim, nevec->dim, |
594 |
|
|
airEnumStr(nrrdType, nrrdTypeFloat), |
595 |
|
|
airEnumStr(nrrdType, neval->type), |
596 |
|
|
airEnumStr(nrrdType, nevec->type)); |
597 |
|
|
return 1; |
598 |
|
|
} |
599 |
|
|
if (!( 3 == neval->axis[0].size && |
600 |
|
|
sx == neval->axis[1].size && |
601 |
|
|
sy == neval->axis[2].size && |
602 |
|
|
sz == neval->axis[3].size )) { |
603 |
|
|
biffAddf(TEN, "%s: second nrrd sizes wrong: " |
604 |
|
|
"(%s,%s,%s,%s) not (3,%s,%s,%s)", me, |
605 |
|
|
airSprintSize_t(stmp[0], neval->axis[0].size), |
606 |
|
|
airSprintSize_t(stmp[1], neval->axis[1].size), |
607 |
|
|
airSprintSize_t(stmp[2], neval->axis[2].size), |
608 |
|
|
airSprintSize_t(stmp[3], neval->axis[3].size), |
609 |
|
|
airSprintSize_t(stmp[4], sx), |
610 |
|
|
airSprintSize_t(stmp[5], sy), |
611 |
|
|
airSprintSize_t(stmp[6], sz)); |
612 |
|
|
return 1; |
613 |
|
|
} |
614 |
|
|
if (!( 9 == nevec->axis[0].size && |
615 |
|
|
sx == nevec->axis[1].size && |
616 |
|
|
sy == nevec->axis[2].size && |
617 |
|
|
sz == nevec->axis[3].size )) { |
618 |
|
|
biffAddf(TEN, "%s: third nrrd sizes wrong: " |
619 |
|
|
"(%s,%s,%s,%s) not (9,%s,%s,%s)", me, |
620 |
|
|
airSprintSize_t(stmp[0], nevec->axis[0].size), |
621 |
|
|
airSprintSize_t(stmp[1], nevec->axis[1].size), |
622 |
|
|
airSprintSize_t(stmp[2], nevec->axis[2].size), |
623 |
|
|
airSprintSize_t(stmp[3], nevec->axis[3].size), |
624 |
|
|
airSprintSize_t(stmp[4], sx), |
625 |
|
|
airSprintSize_t(stmp[5], sy), |
626 |
|
|
airSprintSize_t(stmp[6], sz)); |
627 |
|
|
return 1; |
628 |
|
|
} |
629 |
|
|
|
630 |
|
|
/* finally */ |
631 |
|
|
if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 4, |
632 |
|
|
AIR_CAST(size_t, 7), sx, sy, sz)) { |
633 |
|
|
biffMovef(TEN, NRRD, "%s: couldn't allocate output", me); |
634 |
|
|
return 1; |
635 |
|
|
} |
636 |
|
|
N = sx*sy*sz; |
637 |
|
|
conf = (float *)(nconf->data); |
638 |
|
|
eval = (float *)neval->data; |
639 |
|
|
evec = (float *)nevec->data; |
640 |
|
|
out = (float *)nout->data; |
641 |
|
|
for (I=0; I<N; I++) { |
642 |
|
|
tenMakeSingle_f(out, conf[I], eval, evec); |
643 |
|
|
/* lop A */ |
644 |
|
|
out += 7; |
645 |
|
|
eval += 3; |
646 |
|
|
evec += 9; |
647 |
|
|
} |
648 |
|
|
ELL_4V_SET(map, -1, 0, 1, 2); |
649 |
|
|
if (nrrdAxisInfoCopy(nout, nconf, map, NRRD_AXIS_INFO_SIZE_BIT)) { |
650 |
|
|
biffMovef(TEN, NRRD, "%s: trouble", me); |
651 |
|
|
return 1; |
652 |
|
|
} |
653 |
|
|
nout->axis[0].label = (char *)airFree(nout->axis[0].label); |
654 |
|
|
nout->axis[0].label = airStrdup("tensor"); |
655 |
|
|
nout->axis[0].kind = nrrdKind3DMaskedSymMatrix; |
656 |
|
|
if (nrrdBasicInfoCopy(nout, nconf, |
657 |
|
|
NRRD_BASIC_INFO_DATA_BIT |
658 |
|
|
| NRRD_BASIC_INFO_TYPE_BIT |
659 |
|
|
| NRRD_BASIC_INFO_BLOCKSIZE_BIT |
660 |
|
|
| NRRD_BASIC_INFO_DIMENSION_BIT |
661 |
|
|
| NRRD_BASIC_INFO_CONTENT_BIT |
662 |
|
|
| NRRD_BASIC_INFO_COMMENTS_BIT |
663 |
|
|
| (nrrdStateKeyValuePairsPropagate |
664 |
|
|
? 0 |
665 |
|
|
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { |
666 |
|
|
biffMovef(TEN, NRRD, "%s:", me); |
667 |
|
|
return 1; |
668 |
|
|
} |
669 |
|
|
|
670 |
|
|
return 0; |
671 |
|
|
} |
672 |
|
|
|
673 |
|
|
int |
674 |
|
|
tenSlice(Nrrd *nout, const Nrrd *nten, unsigned int axis, |
675 |
|
|
size_t pos, unsigned int dim) { |
676 |
|
|
static const char me[]="tenSlice"; |
677 |
|
|
Nrrd *nslice, **ncoeff=NULL; |
678 |
|
|
int ci[4]; |
679 |
|
|
airArray *mop; |
680 |
|
|
char stmp[2][AIR_STRLEN_SMALL]; |
681 |
|
|
|
682 |
|
|
if (!(nout && nten)) { |
683 |
|
|
biffAddf(TEN, "%s: got NULL pointer", me); |
684 |
|
|
return 1; |
685 |
|
|
} |
686 |
|
|
if (tenTensorCheck(nten, nrrdTypeDefault, AIR_TRUE, AIR_TRUE)) { |
687 |
|
|
biffAddf(TEN, "%s: didn't get a valid tensor field", me); |
688 |
|
|
return 1; |
689 |
|
|
} |
690 |
|
|
if (!(2 == dim || 3 == dim)) { |
691 |
|
|
biffAddf(TEN, "%s: given dim (%d) not 2 or 3", me, dim); |
692 |
|
|
return 1; |
693 |
|
|
} |
694 |
|
|
if (!( axis <= 2 )) { |
695 |
|
|
biffAddf(TEN, "%s: axis %u not in valid range [0,1,2]", me, axis); |
696 |
|
|
return 1; |
697 |
|
|
} |
698 |
|
|
if (!( pos < nten->axis[1+axis].size )) { |
699 |
|
|
biffAddf(TEN, "%s: slice position %s not in valid range [0..%s]", me, |
700 |
|
|
airSprintSize_t(stmp[0], pos), |
701 |
|
|
airSprintSize_t(stmp[1], nten->axis[1+axis].size-1)); |
702 |
|
|
return 1; |
703 |
|
|
} |
704 |
|
|
|
705 |
|
|
/* |
706 |
|
|
** threshold 0 |
707 |
|
|
** Dxx Dxy Dxz 1 2 3 |
708 |
|
|
** Dxy Dyy Dyz = (2) 4 5 |
709 |
|
|
** Dxz Dyz Dzz (3) (5) 6 |
710 |
|
|
*/ |
711 |
|
|
mop = airMopNew(); |
712 |
|
|
airMopAdd(mop, nslice=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); |
713 |
|
|
if (3 == dim) { |
714 |
|
|
if (nrrdSlice(nslice, nten, axis+1, pos) |
715 |
|
|
|| nrrdAxesInsert(nout, nslice, axis+1)) { |
716 |
|
|
biffMovef(TEN, NRRD, "%s: trouble making slice", me); |
717 |
|
|
airMopError(mop); return 1; |
718 |
|
|
} |
719 |
|
|
} else { |
720 |
|
|
/* HEY: this used to be ncoeff[4], but its passing to nrrdJoin caused |
721 |
|
|
"dereferencing type-punned pointer might break strict-aliasing rules" |
722 |
|
|
warning; GLK not sure how else to fix it */ |
723 |
|
|
ncoeff = AIR_CALLOC(4, Nrrd*); |
724 |
|
|
airMopAdd(mop, ncoeff, airFree, airMopAlways); |
725 |
|
|
airMopAdd(mop, ncoeff[0]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); |
726 |
|
|
airMopAdd(mop, ncoeff[1]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); |
727 |
|
|
airMopAdd(mop, ncoeff[2]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); |
728 |
|
|
airMopAdd(mop, ncoeff[3]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); |
729 |
|
|
switch(axis) { |
730 |
|
|
case 0: |
731 |
|
|
ELL_4V_SET(ci, 0, 4, 5, 6); |
732 |
|
|
break; |
733 |
|
|
case 1: |
734 |
|
|
ELL_4V_SET(ci, 0, 1, 3, 6); |
735 |
|
|
break; |
736 |
|
|
case 2: |
737 |
|
|
ELL_4V_SET(ci, 0, 1, 2, 4); |
738 |
|
|
break; |
739 |
|
|
default: |
740 |
|
|
biffAddf(TEN, "%s: axis %d bogus", me, axis); |
741 |
|
|
airMopError(mop); return 1; |
742 |
|
|
break; |
743 |
|
|
} |
744 |
|
|
if (nrrdSlice(nslice, nten, axis+1, pos) |
745 |
|
|
|| nrrdSlice(ncoeff[0], nslice, 0, ci[0]) |
746 |
|
|
|| nrrdSlice(ncoeff[1], nslice, 0, ci[1]) |
747 |
|
|
|| nrrdSlice(ncoeff[2], nslice, 0, ci[2]) |
748 |
|
|
|| nrrdSlice(ncoeff[3], nslice, 0, ci[3]) |
749 |
|
|
|| nrrdJoin(nout, (const Nrrd *const*)ncoeff, 4, 0, AIR_TRUE)) { |
750 |
|
|
biffMovef(TEN, NRRD, "%s: trouble collecting coefficients", me); |
751 |
|
|
airMopError(mop); return 1; |
752 |
|
|
} |
753 |
|
|
nout->axis[0].kind = nrrdKind2DMaskedSymMatrix; |
754 |
|
|
} |
755 |
|
|
|
756 |
|
|
airMopOkay(mop); |
757 |
|
|
return 0; |
758 |
|
|
} |
759 |
|
|
|
760 |
|
|
#define Txx (ten[1]) |
761 |
|
|
#define Txy (ten[2]) |
762 |
|
|
#define Txz (ten[3]) |
763 |
|
|
#define Tyy (ten[4]) |
764 |
|
|
#define Tyz (ten[5]) |
765 |
|
|
#define Tzz (ten[6]) |
766 |
|
|
|
767 |
|
|
#define SQRT_1_OVER_2 0.70710678118654752440 |
768 |
|
|
#define SQRT_1_OVER_3 0.57735026918962576450 |
769 |
|
|
#define SQRT_2_OVER_3 0.81649658092772603272 |
770 |
|
|
#define SQRT_1_OVER_6 0.40824829046386301635 |
771 |
|
|
|
772 |
|
|
/* |
773 |
|
|
** very special purpose: compute tensor-valued gradient |
774 |
|
|
** of eigenvalue skewness, but have to be given two |
775 |
|
|
** other invariant gradients, NORMALIZED, to which |
776 |
|
|
** eigenvalue skewness should be perpendicular |
777 |
|
|
*/ |
778 |
|
|
void |
779 |
|
|
_tenEvalSkewnessGradient_d(double skw[7], |
780 |
|
|
const double perp1[7], |
781 |
|
|
const double perp2[7], |
782 |
|
|
const double ten[7], |
783 |
|
|
const double minnorm) { |
784 |
|
|
/* static const char me[]="_tenEvalSkewnessGradient_d"; */ |
785 |
|
|
double dot, scl, norm; |
786 |
|
|
|
787 |
|
|
/* start with gradient of determinant */ |
788 |
|
|
TEN_T_SET(skw, ten[0], |
789 |
|
|
Tyy*Tzz - Tyz*Tyz, Txz*Tyz - Txy*Tzz, Txy*Tyz - Txz*Tyy, |
790 |
|
|
Txx*Tzz - Txz*Txz, Txy*Txz - Tyz*Txx, |
791 |
|
|
Txx*Tyy - Txy*Txy); |
792 |
|
|
/* this normalization is so that minnorm comparison below |
793 |
|
|
is meaningful regardless of scale of input */ |
794 |
|
|
/* HEY: should have better handling of case where determinant |
795 |
|
|
gradient magnitude is near zero */ |
796 |
|
|
scl = 1.0/(DBL_EPSILON + TEN_T_NORM(skw)); |
797 |
|
|
TEN_T_SCALE(skw, scl, skw); |
798 |
|
|
dot = TEN_T_DOT(skw, perp1); |
799 |
|
|
TEN_T_SCALE_INCR(skw, -dot, perp1); |
800 |
|
|
dot = TEN_T_DOT(skw, perp2); |
801 |
|
|
TEN_T_SCALE_INCR(skw, -dot, perp2); |
802 |
|
|
norm = TEN_T_NORM(skw); |
803 |
|
|
if (norm < minnorm) { |
804 |
|
|
/* skw is at an extremum, should diagonalize */ |
805 |
|
|
double eval[3], evec[9], matA[9], matB[9], matC[9], mev, third; |
806 |
|
|
|
807 |
|
|
tenEigensolve_d(eval, evec, ten); |
808 |
|
|
mev = (eval[0] + eval[1] + eval[2])/3; |
809 |
|
|
eval[0] -= mev; |
810 |
|
|
eval[1] -= mev; |
811 |
|
|
eval[2] -= mev; |
812 |
|
|
third = (eval[0]*eval[0]*eval[0] |
813 |
|
|
+ eval[1]*eval[1]*eval[1] |
814 |
|
|
+ eval[2]*eval[2]*eval[2])/3; |
815 |
|
|
if (third > 0) { |
816 |
|
|
/* skw is positive: linear: eval[1] = eval[2] */ |
817 |
|
|
ELL_3MV_OUTER(matA, evec + 1*3, evec + 1*3); |
818 |
|
|
ELL_3MV_OUTER(matB, evec + 2*3, evec + 2*3); |
819 |
|
|
} else { |
820 |
|
|
/* skw is negative: planar: eval[0] = eval[1] */ |
821 |
|
|
ELL_3MV_OUTER(matA, evec + 0*3, evec + 0*3); |
822 |
|
|
ELL_3MV_OUTER(matB, evec + 1*3, evec + 1*3); |
823 |
|
|
} |
824 |
|
|
ELL_3M_SCALE_ADD2(matC, SQRT_1_OVER_2, matA, -SQRT_1_OVER_2, matB); |
825 |
|
|
TEN_M2T(skw, matC); |
826 |
|
|
/* have to make sure that this contrived tensor |
827 |
|
|
is indeed orthogonal to perp1 and perp2 */ |
828 |
|
|
dot = TEN_T_DOT(skw, perp1); |
829 |
|
|
TEN_T_SCALE_INCR(skw, -dot, perp1); |
830 |
|
|
dot = TEN_T_DOT(skw, perp2); |
831 |
|
|
TEN_T_SCALE_INCR(skw, -dot, perp2); |
832 |
|
|
norm = TEN_T_NORM(skw); |
833 |
|
|
} |
834 |
|
|
TEN_T_SCALE(skw, 1.0/norm, skw); |
835 |
|
|
return; |
836 |
|
|
} |
837 |
|
|
|
838 |
|
|
void |
839 |
|
|
tenInvariantGradientsK_d(double mu1[7], double mu2[7], double skw[7], |
840 |
|
|
const double ten[7], const double minnorm) { |
841 |
|
|
double dot, norm; |
842 |
|
|
|
843 |
|
|
TEN_T_SET(mu1, ten[0], |
844 |
|
|
SQRT_1_OVER_3, 0, 0, |
845 |
|
|
SQRT_1_OVER_3, 0, |
846 |
|
|
SQRT_1_OVER_3); |
847 |
|
|
|
848 |
|
|
TEN_T_SET(mu2, ten[0], |
849 |
|
|
2*Txx - Tyy - Tzz, 3*Txy, 3*Txz, |
850 |
|
|
2*Tyy - Txx - Tzz, 3*Tyz, |
851 |
|
|
2*Tzz - Txx - Tyy); |
852 |
|
|
norm = TEN_T_NORM(mu2); |
853 |
|
|
if (norm < minnorm) { |
854 |
|
|
/* they gave us a diagonal matrix */ |
855 |
|
|
TEN_T_SET(mu2, ten[0], |
856 |
|
|
SQRT_2_OVER_3, 0, 0, |
857 |
|
|
-SQRT_1_OVER_6, 0, |
858 |
|
|
-SQRT_1_OVER_6); |
859 |
|
|
} |
860 |
|
|
/* next two lines shouldn't really be necessary */ |
861 |
|
|
dot = TEN_T_DOT(mu2, mu1); |
862 |
|
|
TEN_T_SCALE_INCR(mu2, -dot, mu1); |
863 |
|
|
norm = TEN_T_NORM(mu2); |
864 |
|
|
TEN_T_SCALE(mu2, 1.0/norm, mu2); |
865 |
|
|
_tenEvalSkewnessGradient_d(skw, mu1, mu2, ten, minnorm); |
866 |
|
|
|
867 |
|
|
return; |
868 |
|
|
} |
869 |
|
|
|
870 |
|
|
void |
871 |
|
|
tenInvariantGradientsR_d(double R1[7], double R2[7], double R3[7], |
872 |
|
|
const double ten[7], const double minnorm) { |
873 |
|
|
double dot, dev[7], norm, tenNorm, devNorm; |
874 |
|
|
|
875 |
|
|
TEN_T_COPY(R1, ten); |
876 |
|
|
tenNorm = norm = TEN_T_NORM(R1); |
877 |
|
|
if (norm < minnorm) { |
878 |
|
|
TEN_T_SET(R1, ten[0], |
879 |
|
|
SQRT_1_OVER_3, 0, 0, |
880 |
|
|
SQRT_1_OVER_3, 0, |
881 |
|
|
SQRT_1_OVER_3); |
882 |
|
|
norm = TEN_T_NORM(R1); |
883 |
|
|
} |
884 |
|
|
TEN_T_SCALE(R1, 1.0/norm, R1); |
885 |
|
|
|
886 |
|
|
TEN_T_SET(dev, ten[0], |
887 |
|
|
(2*Txx - Tyy - Tzz)/3, Txy, Txz, |
888 |
|
|
(2*Tyy - Txx - Tzz)/3, Tyz, |
889 |
|
|
(2*Tzz - Txx - Tyy)/3); |
890 |
|
|
devNorm = TEN_T_NORM(dev); |
891 |
|
|
if (devNorm < minnorm) { |
892 |
|
|
/* they gave us a diagonal matrix */ |
893 |
|
|
TEN_T_SET(R2, ten[0], |
894 |
|
|
SQRT_2_OVER_3, 0, 0, |
895 |
|
|
-SQRT_1_OVER_6, 0, |
896 |
|
|
-SQRT_1_OVER_6); |
897 |
|
|
} else { |
898 |
|
|
TEN_T_SCALE_ADD2(R2, tenNorm/devNorm, dev, -devNorm/tenNorm, ten); |
899 |
|
|
} |
900 |
|
|
/* next two lines shouldn't really be necessary */ |
901 |
|
|
dot = TEN_T_DOT(R2, R1); |
902 |
|
|
TEN_T_SCALE_INCR(R2, -dot, R1); |
903 |
|
|
norm = TEN_T_NORM(R2); |
904 |
|
|
if (norm < minnorm) { |
905 |
|
|
/* Traceless tensor */ |
906 |
|
|
TEN_T_SET(R2, ten[0], |
907 |
|
|
SQRT_2_OVER_3, 0, 0, |
908 |
|
|
-SQRT_1_OVER_6, 0, |
909 |
|
|
-SQRT_1_OVER_6); |
910 |
|
|
} else { |
911 |
|
|
TEN_T_SCALE(R2, 1.0/norm, R2); |
912 |
|
|
} |
913 |
|
|
_tenEvalSkewnessGradient_d(R3, R1, R2, ten, minnorm); |
914 |
|
|
|
915 |
|
|
return; |
916 |
|
|
} |
917 |
|
|
|
918 |
|
|
/* |
919 |
|
|
** evec must be pre-computed (unit-length eigenvectors) and given to us |
920 |
|
|
*/ |
921 |
|
|
void |
922 |
|
|
tenRotationTangents_d(double phi1[7], |
923 |
|
|
double phi2[7], |
924 |
|
|
double phi3[7], |
925 |
|
|
const double evec[9]) { |
926 |
|
|
double outA[9], outB[9], mat[9]; |
927 |
|
|
|
928 |
|
|
if (phi1) { |
929 |
|
|
phi1[0] = 1.0; |
930 |
|
|
ELL_3MV_OUTER(outA, evec + 1*3, evec + 2*3); |
931 |
|
|
ELL_3MV_OUTER(outB, evec + 2*3, evec + 1*3); |
932 |
|
|
ELL_3M_SCALE_ADD2(mat, SQRT_1_OVER_2, outA, SQRT_1_OVER_2, outB); |
933 |
|
|
TEN_M2T(phi1, mat); |
934 |
|
|
} |
935 |
|
|
|
936 |
|
|
if (phi2) { |
937 |
|
|
phi2[0] = 1.0; |
938 |
|
|
ELL_3MV_OUTER(outA, evec + 0*3, evec + 2*3); |
939 |
|
|
ELL_3MV_OUTER(outB, evec + 2*3, evec + 0*3); |
940 |
|
|
ELL_3M_SCALE_ADD2(mat, SQRT_1_OVER_2, outA, SQRT_1_OVER_2, outB); |
941 |
|
|
TEN_M2T(phi2, mat); |
942 |
|
|
} |
943 |
|
|
|
944 |
|
|
if (phi3) { |
945 |
|
|
phi3[0] = 1.0; |
946 |
|
|
ELL_3MV_OUTER(outA, evec + 0*3, evec + 1*3); |
947 |
|
|
ELL_3MV_OUTER(outB, evec + 1*3, evec + 0*3); |
948 |
|
|
ELL_3M_SCALE_ADD2(mat, SQRT_1_OVER_2, outA, SQRT_1_OVER_2, outB); |
949 |
|
|
TEN_M2T(phi3, mat); |
950 |
|
|
} |
951 |
|
|
|
952 |
|
|
return; |
953 |
|
|
} |
954 |
|
|
|
955 |
|
|
void |
956 |
|
|
tenInv_f(float inv[7], const float ten[7]) { |
957 |
|
|
float det; |
958 |
|
|
|
959 |
|
|
TEN_T_INV(inv, ten, det); |
960 |
|
|
} |
961 |
|
|
|
962 |
|
|
void |
963 |
|
|
tenInv_d(double inv[7], const double ten[7]) { |
964 |
|
|
double det; |
965 |
|
|
|
966 |
|
|
TEN_T_INV(inv, ten, det); |
967 |
|
|
} |
968 |
|
|
|
969 |
|
|
void |
970 |
|
|
tenLogSingle_d(double logten[7], const double ten[7]) { |
971 |
|
|
double eval[3], evec[9]; |
972 |
|
|
unsigned int ii; |
973 |
|
|
|
974 |
|
|
tenEigensolve_d(eval, evec, ten); |
975 |
|
|
for (ii=0; ii<3; ii++) { |
976 |
|
|
eval[ii] = log(eval[ii]); |
977 |
|
|
if (!AIR_EXISTS(eval[ii])) { |
978 |
|
|
eval[ii] = -FLT_MAX; /* making stuff up */ |
979 |
|
|
} |
980 |
|
|
} |
981 |
|
|
tenMakeSingle_d(logten, ten[0], eval, evec); |
982 |
|
|
} |
983 |
|
|
|
984 |
|
|
void |
985 |
|
|
tenLogSingle_f(float logten[7], const float ten[7]) { |
986 |
|
|
float eval[3], evec[9]; |
987 |
|
|
unsigned int ii; |
988 |
|
|
|
989 |
|
|
tenEigensolve_f(eval, evec, ten); |
990 |
|
|
for (ii=0; ii<3; ii++) { |
991 |
|
|
eval[ii] = AIR_CAST(float, log(eval[ii])); |
992 |
|
|
if (!AIR_EXISTS(eval[ii])) { |
993 |
|
|
eval[ii] = -FLT_MAX/10; /* still making stuff up */ |
994 |
|
|
} |
995 |
|
|
} |
996 |
|
|
tenMakeSingle_f(logten, ten[0], eval, evec); |
997 |
|
|
} |
998 |
|
|
|
999 |
|
|
void |
1000 |
|
|
tenExpSingle_d(double expten[7], const double ten[7]) { |
1001 |
|
|
double eval[3], evec[9]; |
1002 |
|
|
unsigned int ii; |
1003 |
|
|
|
1004 |
|
|
tenEigensolve_d(eval, evec, ten); |
1005 |
|
|
for (ii=0; ii<3; ii++) { |
1006 |
|
|
eval[ii] = exp(eval[ii]); |
1007 |
|
|
} |
1008 |
|
|
tenMakeSingle_d(expten, ten[0], eval, evec); |
1009 |
|
|
} |
1010 |
|
|
|
1011 |
|
|
void |
1012 |
|
|
tenExpSingle_f(float expten[7], const float ten[7]) { |
1013 |
|
|
float eval[3], evec[9]; |
1014 |
|
|
unsigned int ii; |
1015 |
|
|
|
1016 |
|
|
tenEigensolve_f(eval, evec, ten); |
1017 |
|
|
for (ii=0; ii<3; ii++) { |
1018 |
|
|
eval[ii] = AIR_CAST(float, exp(eval[ii])); |
1019 |
|
|
} |
1020 |
|
|
tenMakeSingle_f(expten, ten[0], eval, evec); |
1021 |
|
|
} |
1022 |
|
|
|
1023 |
|
|
void |
1024 |
|
|
tenSqrtSingle_d(double sqrtten[7], const double ten[7]) { |
1025 |
|
|
double eval[3], evec[9]; |
1026 |
|
|
unsigned int ii; |
1027 |
|
|
|
1028 |
|
|
tenEigensolve_d(eval, evec, ten); |
1029 |
|
|
for (ii=0; ii<3; ii++) { |
1030 |
|
|
eval[ii] = eval[ii] > 0 ? sqrt(eval[ii]) : 0; |
1031 |
|
|
} |
1032 |
|
|
tenMakeSingle_d(sqrtten, ten[0], eval, evec); |
1033 |
|
|
} |
1034 |
|
|
|
1035 |
|
|
void |
1036 |
|
|
tenSqrtSingle_f(float sqrtten[7], const float ten[7]) { |
1037 |
|
|
float eval[3], evec[9]; |
1038 |
|
|
unsigned int ii; |
1039 |
|
|
|
1040 |
|
|
tenEigensolve_f(eval, evec, ten); |
1041 |
|
|
for (ii=0; ii<3; ii++) { |
1042 |
|
|
eval[ii] = AIR_CAST(float, eval[ii] > 0 ? sqrt(eval[ii]) : 0); |
1043 |
|
|
} |
1044 |
|
|
tenMakeSingle_f(sqrtten, ten[0], eval, evec); |
1045 |
|
|
} |
1046 |
|
|
|
1047 |
|
|
void |
1048 |
|
|
tenPowSingle_d(double powten[7], const double ten[7], double power) { |
1049 |
|
|
double eval[3], _eval[3], evec[9]; |
1050 |
|
|
unsigned int ii; |
1051 |
|
|
|
1052 |
|
|
tenEigensolve_d(_eval, evec, ten); |
1053 |
|
|
for (ii=0; ii<3; ii++) { |
1054 |
|
|
eval[ii] = pow(_eval[ii], power); |
1055 |
|
|
} |
1056 |
|
|
tenMakeSingle_d(powten, ten[0], eval, evec); |
1057 |
|
|
} |
1058 |
|
|
|
1059 |
|
|
void |
1060 |
|
|
tenPowSingle_f(float powten[7], const float ten[7], float power) { |
1061 |
|
|
float eval[3], evec[9]; |
1062 |
|
|
unsigned int ii; |
1063 |
|
|
|
1064 |
|
|
tenEigensolve_f(eval, evec, ten); |
1065 |
|
|
for (ii=0; ii<3; ii++) { |
1066 |
|
|
eval[ii] = AIR_CAST(float, pow(eval[ii], power)); |
1067 |
|
|
} |
1068 |
|
|
tenMakeSingle_f(powten, ten[0], eval, evec); |
1069 |
|
|
} |
1070 |
|
|
|
1071 |
|
|
double |
1072 |
|
|
tenDoubleContract_d(double a[7], double T[21], double b[7]) { |
1073 |
|
|
double ret; |
1074 |
|
|
|
1075 |
|
|
ret = (+ 1*1*T[ 0]*a[1]*b[1] + 1*2*T[ 1]*a[2]*b[1] + 1*2*T[ 2]*a[3]*b[1] + 1*1*T[ 3]*a[4]*b[1] + 1*2*T[ 4]*a[5]*b[1] + 1*1*T[ 5]*a[6]*b[1] + |
1076 |
|
|
+ 2*1*T[ 1]*a[1]*b[2] + 2*2*T[ 6]*a[2]*b[2] + 2*2*T[ 7]*a[3]*b[2] + 2*1*T[ 8]*a[4]*b[2] + 2*2*T[ 9]*a[5]*b[2] + 2*1*T[10]*a[6]*b[2] + |
1077 |
|
|
+ 2*1*T[ 2]*a[1]*b[3] + 2*2*T[ 7]*a[2]*b[3] + 2*2*T[11]*a[3]*b[3] + 2*1*T[12]*a[4]*b[3] + 2*2*T[13]*a[5]*b[3] + 2*1*T[14]*a[6]*b[3] + |
1078 |
|
|
+ 1*1*T[ 3]*a[1]*b[4] + 1*2*T[ 8]*a[2]*b[4] + 1*2*T[12]*a[3]*b[4] + 1*1*T[15]*a[4]*b[4] + 1*2*T[16]*a[5]*b[4] + 1*1*T[17]*a[6]*b[4] + |
1079 |
|
|
+ 2*1*T[ 4]*a[1]*b[5] + 2*2*T[ 9]*a[2]*b[5] + 2*2*T[13]*a[3]*b[5] + 2*1*T[16]*a[4]*b[5] + 2*2*T[18]*a[5]*b[5] + 2*1*T[19]*a[6]*b[5] + |
1080 |
|
|
+ 1*1*T[ 5]*a[1]*b[6] + 1*2*T[10]*a[2]*b[6] + 1*2*T[14]*a[3]*b[6] + 1*1*T[17]*a[4]*b[6] + 1*2*T[19]*a[5]*b[6] + 1*1*T[20]*a[6]*b[6]); |
1081 |
|
|
|
1082 |
|
|
return ret; |
1083 |
|
|
} |