GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/ten/tendHelix.c Lines: 123 130 94.6 %
Date: 2017-05-26 Branches: 19 26 73.1 %

Line Branch Exec Source
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
#include "ten.h"
25
#include "privateTen.h"
26
27
#define INFO "Generate twisting helical tensor field"
28
static const char *_tend_helixInfoL =
29
  (INFO
30
   ". The main utility of such a field is to debug handling of coordinate "
31
   "systems in tensor field visualization.  The \"space directions\" and "
32
   "\"space origin\" fields of the NRRD header determines the mapping from "
33
   "coordinates in the index space of the image to coordinates in the "
34
   "world space in which the image is "
35
   "sampled.  The \"measurement frame\" field determines the mapping from "
36
   "the coordinates of the tensor itself, to coordinates of the world space. "
37
   "When these are correctly handled, the "
38
   "region of high anisotropy is a right-handed helix (same as DNA). "
39
   "Using differing axes sizes (via \"-s\") helps make sure that the "
40
   "raster ordering of axes is correct.  In addition, the tensors twist "
41
   "relative to the helix, which exposes handling of the measurement frame. "
42
   "If you trace paths guided by the principal eigenvector of the tensors, "
43
   "along the surface of the helical cylinder, you get another "
44
   "right-handed helix, as if the the tensor field is modeling the result "
45
   "if twisting a set of fibers into single-stranded helical bundle. ");
46
47
void
48
tend_helixDoit(Nrrd *nout, double bnd,
49
               double orig[3], double i2w[9], double mf[9],
50
               double r, double R, double S, double angle, int incrtwist,
51
               double ev[3], double bgEval, int verbose) {
52
  int sx, sy, sz, xi, yi, zi;
53
  double th, t0, t1, t2, t3, v1, v2,
54
    wpos[3], vpos[3], mfT[9],
55
    W2H[9], H2W[9], H2C[9], C2H[9], fv[3], rv[3], uv[3], mA[9], mB[9], inside,
56
    tmp[3], len;
57
  float *out;
58
59
16
  sx = nout->axis[1].size;
60
8
  sy = nout->axis[2].size;
61
8
  sz = nout->axis[3].size;
62
8
  out = (float*)nout->data;
63
8
  ELL_3M_TRANSPOSE(mfT, mf);
64
768
  for (zi=0; zi<sz; zi++) {
65
376
    if (verbose) {
66
      fprintf(stderr, "zi = %d/%d\n", zi, sz);
67
    }
68
35344
    for (yi=0; yi<sy; yi++) {
69
1591232
      for (xi=0; xi<sx; xi++) {
70
778320
        ELL_3V_SET(tmp, xi, yi, zi);
71
778320
        ELL_3MV_MUL(vpos, i2w, tmp);
72
778320
        ELL_3V_INCR(vpos, orig);
73
74
#define WPOS(pos, th) ELL_3V_SET((pos),R*cos(th), R*sin(th), S*(th)/(2*AIR_PI))
75
#define VAL(th) (WPOS(wpos, th), ELL_3V_DIST(wpos, vpos))
76
#define RR 0.61803399
77
#define CC (1.0-RR)
78
#define SHIFT3(a,b,c,d) (a)=(b); (b)=(c); (c)=(d)
79
#define SHIFT2(a,b,c)   (a)=(b); (b)=(c)
80
81
778320
        th = atan2(vpos[1], vpos[0]);
82
778320
        th += 2*AIR_PI*floor(0.5 + vpos[2]/S - th/(2*AIR_PI));
83
778320
        if (S*th/(2*AIR_PI) > vpos[2]) {
84
389160
          t0 = th - AIR_PI; t3 = th;
85
389160
        } else {
86
389160
          t0 = th; t3 = th + AIR_PI;
87
        }
88
778320
        t1 = RR*t0 + CC*t3;
89
778320
        t2 = CC*t0 + RR*t3;
90
778320
        v1 = VAL(t1);
91
778320
        v2 = VAL(t2);
92
23577680
        while ( t3-t0 > 0.000001*(AIR_ABS(t1)+AIR_ABS(t2)) ) {
93
22021040
          if (v1 < v2) {
94
11009664
            SHIFT3(t3, t2, t1, CC*t0 + RR*t2);
95
11009664
            SHIFT2(v2, v1, VAL(t1));
96
11009664
          } else {
97
11011376
            SHIFT3(t0, t1, t2, RR*t1 + CC*t3);
98
11011376
            SHIFT2(v1, v2, VAL(t2));
99
          }
100
        }
101
        /* t1 (and t2) are now the th for which the point on the helix
102
           (R*cos(th), R*sin(th), S*(th)/(2*AIR_PI)) is closest to vpos */
103
104
778320
        WPOS(wpos, t1);
105
778320
        ELL_3V_SUB(wpos, vpos, wpos);
106
778320
        ELL_3V_SET(fv, -R*sin(t1), R*cos(t1), S/AIR_PI);  /* helix tangent */
107
778320
        ELL_3V_NORM(fv, fv, len);
108
        ELL_3V_COPY(rv, wpos);
109
778320
        ELL_3V_NORM(rv, rv, len);
110
778320
        len = ELL_3V_DOT(rv, fv);
111
778320
        ELL_3V_SCALE(tmp, -len, fv);
112
778320
        ELL_3V_ADD2(rv, rv, tmp);
113
778320
        ELL_3V_NORM(rv, rv, len);  /* rv now normal to helix, closest to
114
                                      pointing to vpos */
115
778320
        ELL_3V_CROSS(uv, rv, fv);
116
778320
        ELL_3V_NORM(uv, uv, len);  /* (rv,fv,uv) now right-handed frame */
117
        ELL_3MV_ROW0_SET(W2H, uv); /* as is (uv,rv,fv) */
118
        ELL_3MV_ROW1_SET(W2H, rv);
119
        ELL_3MV_ROW2_SET(W2H, fv);
120
        ELL_3M_TRANSPOSE(H2W, W2H);
121
778320
        inside = 0.5 - 0.5*airErf((ELL_3V_LEN(wpos)-r)/(bnd + 0.0001));
122
778320
        if (incrtwist) {
123
778320
          th = angle*ELL_3V_LEN(wpos)/r;
124
778320
        } else {
125
          th = angle;
126
        }
127
778320
        ELL_3M_ROTATE_Y_SET(H2C, th);
128
        ELL_3M_TRANSPOSE(C2H, H2C);
129
778320
        ELL_3M_SCALE_SET(mA,
130
                         AIR_LERP(inside, bgEval, ev[1]),
131
                         AIR_LERP(inside, bgEval, ev[2]),
132
                         AIR_LERP(inside, bgEval, ev[0]));
133
778320
        ELL_3M_MUL(mB, mA, H2C);
134
778320
        ELL_3M_MUL(mA, mB, W2H);
135
778320
        ELL_3M_MUL(mB, mA, mf);
136
778320
        ELL_3M_MUL(mA, C2H, mB);
137
778320
        ELL_3M_MUL(mB, H2W, mA);
138
778320
        ELL_3M_MUL(mA, mfT, mB);
139
140
778320
        TEN_M2T_TT(out, float, mA);
141
778320
        out[0] = 1.0;
142
778320
        out += 7;
143
      }
144
    }
145
  }
146
  return;
147
8
}
148
149
int
150
tend_helixMain(int argc, const char **argv, const char *me,
151
               hestParm *hparm) {
152
  int pret;
153
18
  hestOpt *hopt = NULL;
154
9
  char *perr, *err;
155
  airArray *mop;
156
157
9
  int size[3], nit, verbose;
158
  Nrrd *nout;
159
9
  double R, r, S, bnd, angle, ev[3], ip[3], iq[4], mp[3], mq[4], tmp[9],
160
    orig[3], i2w[9], rot[9], mf[9], spd[4][3], bge;
161
9
  char *outS;
162
163
9
  hestOptAdd(&hopt, "s", "size", airTypeInt, 3, 3, size, NULL,
164
             "sizes along fast, medium, and slow axes of the sampled volume, "
165
             "often called \"X\", \"Y\", and \"Z\".  It is best to use "
166
             "slightly different sizes here, to expose errors in interpreting "
167
             "axis ordering (e.g. \"-s 39 40 41\")");
168
9
  hestOptAdd(&hopt, "ip", "image orientation", airTypeDouble, 3, 3, ip,
169
             "0 0 0",
170
             "quaternion quotient space orientation of image");
171
9
  hestOptAdd(&hopt, "mp", "measurement orientation", airTypeDouble, 3, 3, mp,
172
             "0 0 0",
173
             "quaternion quotient space orientation of measurement frame");
174
9
  hestOptAdd(&hopt, "b", "boundary", airTypeDouble, 1, 1, &bnd, "10",
175
             "parameter governing how fuzzy the boundary between high and "
176
             "low anisotropy is. Use \"-b 0\" for no fuzziness");
177
9
  hestOptAdd(&hopt, "r", "little radius", airTypeDouble, 1, 1, &r, "30",
178
             "(minor) radius of cylinder tracing helix");
179
9
  hestOptAdd(&hopt, "R", "big radius", airTypeDouble, 1, 1, &R, "50",
180
             "(major) radius of helical turns");
181
9
  hestOptAdd(&hopt, "S", "spacing", airTypeDouble, 1, 1, &S, "100",
182
             "spacing between turns of helix (along its axis)");
183
9
  hestOptAdd(&hopt, "a", "angle", airTypeDouble, 1, 1, &angle, "60",
184
             "maximal angle of twist of tensors along path.  There is no "
185
             "twist at helical core of path, and twist increases linearly "
186
             "with radius around this path.  Positive twist angle with "
187
             "positive spacing resulting in a right-handed twist around a "
188
             "right-handed helix. ");
189
9
  hestOptAdd(&hopt, "nit", NULL, airTypeInt, 0, 0, &nit, NULL,
190
             "changes behavior of twist angle as function of distance from "
191
             "center of helical core: instead of increasing linearly as "
192
             "describe above, be at a constant angle");
193
9
  hestOptAdd(&hopt, "ev", "eigenvalues", airTypeDouble, 3, 3, ev,
194
             "0.006 0.002 0.001",
195
             "eigenvalues of tensors (in order) along direction of coil, "
196
             "circumferential around coil, and radial around coil. ");
197
9
  hestOptAdd(&hopt, "bg", "background", airTypeDouble, 1, 1, &bge, "0.5",
198
             "eigenvalue of isotropic background");
199
9
  hestOptAdd(&hopt, "v", "verbose", airTypeInt, 1, 1, &verbose, "1",
200
             "verbose output");
201
9
  hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
202
             "output file");
203
204
9
  mop = airMopNew();
205
9
  airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
206
10
  USAGE(_tend_helixInfoL);
207

8
  JUSTPARSE();
208
8
  airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);
209
210
8
  nout = nrrdNew();
211
8
  airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways);
212
8
  if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 4,
213
                        AIR_CAST(size_t, 7),
214
8
                        AIR_CAST(size_t, size[0]),
215
8
                        AIR_CAST(size_t, size[1]),
216
8
                        AIR_CAST(size_t, size[2]))) {
217
    airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways);
218
    fprintf(stderr, "%s: trouble allocating output:\n%s\n", me, err);
219
    airMopError(mop); return 1;
220
  }
221
222
8
  ELL_4V_SET(iq, 1.0, ip[0], ip[1], ip[2]);
223
8
  ell_q_to_3m_d(rot, iq);
224
8
  ELL_3V_SET(orig,
225
             -2*R + 2*R/size[0],
226
             -2*R + 2*R/size[1],
227
             -2*R + 2*R/size[2]);
228
8
  ELL_3M_ZERO_SET(i2w);
229
8
  ELL_3M_DIAG_SET(i2w, 4*R/size[0], 4*R/size[1], 4*R/size[2]);
230
8
  ELL_3MV_MUL(tmp, rot, orig);
231
8
  ELL_3V_COPY(orig, tmp);
232
8
  ELL_3M_MUL(tmp, rot, i2w);
233
8
  ELL_3M_COPY(i2w, tmp);
234
8
  ELL_4V_SET(mq, 1.0, mp[0], mp[1], mp[2]);
235
8
  ell_q_to_3m_d(mf, mq);
236
16
  tend_helixDoit(nout, bnd,
237
8
                 orig, i2w, mf,
238
8
                 r, R, S, angle*AIR_PI/180, !nit, ev, bge,
239
8
                 verbose);
240
8
  nrrdSpaceSet(nout, nrrdSpaceRightAnteriorSuperior);
241
8
  nrrdSpaceOriginSet(nout, orig);
242
8
  ELL_3V_SET(spd[0], AIR_NAN, AIR_NAN, AIR_NAN);
243
8
  ELL_3MV_COL0_GET(spd[1], i2w);
244
8
  ELL_3MV_COL1_GET(spd[2], i2w);
245
8
  ELL_3MV_COL2_GET(spd[3], i2w);
246
8
  nrrdAxisInfoSet_va(nout, nrrdAxisInfoSpaceDirection,
247
8
                     spd[0], spd[1], spd[2], spd[3]);
248
8
  nrrdAxisInfoSet_va(nout, nrrdAxisInfoCenter,
249
                     nrrdCenterUnknown, nrrdCenterCell,
250
                     nrrdCenterCell, nrrdCenterCell);
251
8
  nrrdAxisInfoSet_va(nout, nrrdAxisInfoKind,
252
                     nrrdKind3DMaskedSymMatrix, nrrdKindSpace,
253
                     nrrdKindSpace, nrrdKindSpace);
254
8
  nout->measurementFrame[0][0] = mf[0];
255
8
  nout->measurementFrame[1][0] = mf[1];
256
8
  nout->measurementFrame[2][0] = mf[2];
257
8
  nout->measurementFrame[0][1] = mf[3];
258
8
  nout->measurementFrame[1][1] = mf[4];
259
8
  nout->measurementFrame[2][1] = mf[5];
260
8
  nout->measurementFrame[0][2] = mf[6];
261
8
  nout->measurementFrame[1][2] = mf[7];
262
8
  nout->measurementFrame[2][2] = mf[8];
263
264
8
  if (nrrdSave(outS, nout, NULL)) {
265
    airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways);
266
    fprintf(stderr, "%s: trouble writing:\n%s\n", me, err);
267
    airMopError(mop); return 1;
268
  }
269
270
8
  airMopOkay(mop);
271
8
  return 0;
272
9
}
273
TEND_CMD(helix, INFO);