GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/hoover/rays.c Lines: 0 226 0.0 %
Date: 2017-05-26 Branches: 0 112 0.0 %

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 "hoover.h"
25
26
/*
27
** learned: if you're going to "simplify" code which computes some
28
** floating point value within a loop using AFFINE() on the loop
29
** control variable, by simply incrementing that value with the
30
** correct amount iteration, BE SURE THAT THE INCREMENTING IS DONE in
31
** every possible control path of the loop (wasn't incrementing ray
32
** sample position if first sample wasn't inside the volume)
33
*/
34
35
/*
36
** _hooverLearnLengths()
37
**
38
** This is where we enforce the constraint that the volume always fit
39
** inside a cube with edge length 2, centered at the origin.
40
**
41
** volHLen[i] is the HALF the length of the volume along axis i
42
**
43
** NOTE: none of this comes into play if we have ctx->shape
44
*/
45
void
46
_hooverLearnLengths(double volHLen[3], double voxLen[3], hooverContext *ctx) {
47
  double maxLen;
48
  int numSamples[3], numElements[3];
49
50
  ELL_3V_COPY(numSamples, ctx->volSize);
51
  if (nrrdCenterNode == ctx->volCentering) {
52
    numElements[0] = numSamples[0]-1;
53
    numElements[1] = numSamples[1]-1;
54
    numElements[2] = numSamples[2]-1;
55
  } else {
56
    numElements[0] = numSamples[0];
57
    numElements[1] = numSamples[1];
58
    numElements[2] = numSamples[2];
59
  }
60
  volHLen[0] = numElements[0]*ctx->volSpacing[0];
61
  volHLen[1] = numElements[1]*ctx->volSpacing[1];
62
  volHLen[2] = numElements[2]*ctx->volSpacing[2];
63
  maxLen = AIR_MAX(volHLen[0], volHLen[1]);
64
  maxLen = AIR_MAX(volHLen[2], maxLen);
65
  volHLen[0] /= maxLen;
66
  volHLen[1] /= maxLen;
67
  volHLen[2] /= maxLen;
68
  voxLen[0] = 2*volHLen[0]/numElements[0];
69
  voxLen[1] = 2*volHLen[1]/numElements[1];
70
  voxLen[2] = 2*volHLen[2]/numElements[2];
71
}
72
73
/*
74
** _hooverExtraContext struct
75
**
76
** Like hooverContext, this is READ-ONLY information which is not specific
77
** to any thread.
78
** Unlike hooverContext, it is solely for the benefit of the calculations
79
** done in _hooverThreadBody.
80
**
81
** No one outside hoover should need to know about this.
82
*/
83
typedef struct {
84
  double volHLen[3],     /* length of x,y,z edges of volume bounding box */
85
    voxLen[3],           /* length of x,y,z edges of voxels */
86
    uBase, uCap,         /* uMin and uMax as seen on the near cutting plane */
87
    vBase, vCap,         /* analogous to uBase and uCap */
88
    rayZero[3];          /* location of near plane, line of sight interxion */
89
} _hooverExtraContext;
90
91
_hooverExtraContext *
92
_hooverExtraContextNew(hooverContext *ctx) {
93
  _hooverExtraContext *ec;
94
95
  ec = (_hooverExtraContext *)calloc(1, sizeof(_hooverExtraContext));
96
  if (ec) {
97
    if (ctx->shape) {
98
      ELL_3V_NAN_SET(ec->volHLen);
99
      ELL_3V_NAN_SET(ec->voxLen);
100
    } else {
101
      _hooverLearnLengths(ec->volHLen, ec->voxLen, ctx);
102
    }
103
    ELL_3V_SCALE_ADD2(ec->rayZero,
104
                      1.0, ctx->cam->from,
105
                      ctx->cam->vspNeer, ctx->cam->N);
106
  }
107
  return ec;
108
}
109
110
_hooverExtraContext *
111
_hooverExtraContextNix(_hooverExtraContext *ec) {
112
113
  if (ec) {
114
    free(ec);
115
  }
116
  return NULL;
117
}
118
119
/*
120
** _hooverThreadArg struct
121
**
122
** A pointer to this is passed to _hooverThreadBody.  It contains all the
123
** information which is not thread-specific, and all the thread-specific
124
** information known at the level of hooverRender.
125
**
126
** For simplicity sake, a pointer to a struct of this type is also
127
** returned from _hooverThreadBody, so this is where we store an
128
** error-signaling return value (errCode), and what function had
129
** trouble (whichErr).
130
*/
131
typedef struct {
132
  /* ----------------------- input */
133
  hooverContext *ctx;
134
  _hooverExtraContext *ec;
135
  void *render;
136
  int whichThread;
137
  /* ----------------------- output */
138
  int whichErr;
139
  int errCode;
140
} _hooverThreadArg;
141
142
void *
143
_hooverThreadBody(void *_arg) {
144
  _hooverThreadArg *arg;
145
  void *thread;
146
  int ret,               /* to catch return values from callbacks */
147
    sampleI,             /* which sample we're on */
148
    inside,              /* we're inside the volume */
149
    vI, uI;              /* integral coords in image */
150
  double tmp,
151
    mm,                  /* lowest position in index space, for all axes */
152
    Mx, My, Mz,          /* highest position in index space on each axis */
153
    u, v,                /* floating-point coords in image */
154
    uvScale,             /* how to scale (u,v) to go from image to
155
                            near plane, according to ortho or perspective */
156
    lx, ly, lz,          /* half edge-lengths of volume */
157
    rayLen=0,            /* length of segment formed by ray line intersecting
158
                            the near and far clipping planes */
159
    rayT,                /* current position along ray (world-space) */
160
    rayDirW[3],          /* unit-length ray direction (world-space) */
161
    rayDirI[3],          /* rayDirW transformed into index space;
162
                            not unit length, but a unit change in
163
                            world space along rayDirW translates to
164
                            this change in index space along rayDirI */
165
    rayPosW[3],          /* current ray location (world-space) */
166
    rayPosI[3],          /* current ray location (index-space) */
167
    rayStartW[3],        /* ray start on near plane (world-space) */
168
    rayStartI[3],        /* ray start on near plane (index-space) */
169
    rayStep,             /* distance between samples (world-space) */
170
    vOff[3], uOff[3];    /* offsets in arg->ec->wU and arg->ec->wV
171
                            directions towards start of ray */
172
173
  arg = (_hooverThreadArg *)_arg;
174
  if ( (ret = (arg->ctx->threadBegin)(&thread,
175
                                      arg->render,
176
                                      arg->ctx->user,
177
                                      arg->whichThread)) ) {
178
    arg->errCode = ret;
179
    arg->whichErr = hooverErrThreadBegin;
180
    return arg;
181
  }
182
  if (arg->ctx->shape) {
183
    lx = ly = lz = AIR_NAN;
184
    if (nrrdCenterNode == arg->ctx->shape->center) {
185
      mm = 0;
186
      Mx = arg->ctx->shape->size[0]-1;
187
      My = arg->ctx->shape->size[1]-1;
188
      Mz = arg->ctx->shape->size[2]-1;
189
    } else {
190
      mm = -0.5;
191
      Mx = arg->ctx->shape->size[0]-0.5;
192
      My = arg->ctx->shape->size[1]-0.5;
193
      Mz = arg->ctx->shape->size[2]-0.5;
194
    }
195
  } else {
196
    lx = arg->ec->volHLen[0];
197
    ly = arg->ec->volHLen[1];
198
    lz = arg->ec->volHLen[2];
199
    if (nrrdCenterNode == arg->ctx->volCentering) {
200
      mm = 0;
201
      Mx = arg->ctx->volSize[0]-1;
202
      My = arg->ctx->volSize[1]-1;
203
      Mz = arg->ctx->volSize[2]-1;
204
    } else {
205
      mm = -0.5;
206
      Mx = arg->ctx->volSize[0]-0.5;
207
      My = arg->ctx->volSize[1]-0.5;
208
      Mz = arg->ctx->volSize[2]-0.5;
209
    }
210
  }
211
212
  if (arg->ctx->cam->orthographic) {
213
    ELL_3V_COPY(rayDirW, arg->ctx->cam->N);
214
    if (arg->ctx->shape) {
215
      double zeroW[3], zeroI[3];
216
      ELL_3V_SET(zeroW, 0, 0, 0);
217
      gageShapeWtoI(arg->ctx->shape, zeroI, zeroW);
218
      gageShapeWtoI(arg->ctx->shape, rayDirI, rayDirW);
219
      ELL_3V_SUB(rayDirI, rayDirI, zeroI);
220
    } else {
221
      rayDirI[0] = AIR_DELTA(-lx, rayDirW[0], lx, mm, Mx);
222
      rayDirI[1] = AIR_DELTA(-ly, rayDirW[1], ly, mm, My);
223
      rayDirI[2] = AIR_DELTA(-lz, rayDirW[2], lz, mm, Mz);
224
    }
225
    rayLen = arg->ctx->cam->vspFaar - arg->ctx->cam->vspNeer;
226
    uvScale = 1.0;
227
  } else {
228
    uvScale = arg->ctx->cam->vspNeer/arg->ctx->cam->vspDist;
229
  }
230
231
  while (1) {
232
    /* the work assignment is simply the next scanline to be rendered:
233
       the result of all this is setting vI */
234
    if (arg->ctx->workMutex) {
235
      airThreadMutexLock(arg->ctx->workMutex);
236
    }
237
    vI = arg->ctx->workIdx;
238
    if (arg->ctx->workIdx < arg->ctx->imgSize[1]) {
239
      arg->ctx->workIdx += 1;
240
    }
241
    if (arg->ctx->workMutex) {
242
      airThreadMutexUnlock(arg->ctx->workMutex);
243
    }
244
    if (vI == arg->ctx->imgSize[1]) {
245
      /* we're done! */
246
      break;
247
    }
248
249
    if (nrrdCenterCell == arg->ctx->imgCentering) {
250
      v = uvScale*AIR_AFFINE(-0.5, vI, arg->ctx->imgSize[1]-0.5,
251
                             arg->ctx->cam->vRange[0],
252
                             arg->ctx->cam->vRange[1]);
253
    } else {
254
      v = uvScale*AIR_AFFINE(0.0, vI, arg->ctx->imgSize[1]-1.0,
255
                             arg->ctx->cam->vRange[0],
256
                             arg->ctx->cam->vRange[1]);
257
    }
258
    ELL_3V_SCALE(vOff, v, arg->ctx->cam->V);
259
    for (uI=0; uI<arg->ctx->imgSize[0]; uI++) {
260
      if (nrrdCenterCell == arg->ctx->imgCentering) {
261
        u = uvScale*AIR_AFFINE(-0.5, uI, arg->ctx->imgSize[0]-0.5,
262
                               arg->ctx->cam->uRange[0],
263
                               arg->ctx->cam->uRange[1]);
264
      } else {
265
        u = uvScale*AIR_AFFINE(0.0, uI, arg->ctx->imgSize[0]-1.0,
266
                               arg->ctx->cam->uRange[0],
267
                               arg->ctx->cam->uRange[1]);
268
      }
269
      ELL_3V_SCALE(uOff, u, arg->ctx->cam->U);
270
      ELL_3V_ADD3(rayStartW, uOff, vOff, arg->ec->rayZero);
271
      if (arg->ctx->shape) {
272
        gageShapeWtoI(arg->ctx->shape, rayStartI, rayStartW);
273
      } else {
274
        rayStartI[0] = AIR_AFFINE(-lx, rayStartW[0], lx, mm, Mx);
275
        rayStartI[1] = AIR_AFFINE(-ly, rayStartW[1], ly, mm, My);
276
        rayStartI[2] = AIR_AFFINE(-lz, rayStartW[2], lz, mm, Mz);
277
      }
278
      if (!arg->ctx->cam->orthographic) {
279
        ELL_3V_SUB(rayDirW, rayStartW, arg->ctx->cam->from);
280
        ELL_3V_NORM(rayDirW, rayDirW, tmp);
281
        if (arg->ctx->shape) {
282
          double zeroW[3], zeroI[3];
283
          ELL_3V_SET(zeroW, 0, 0, 0);
284
          gageShapeWtoI(arg->ctx->shape, zeroI, zeroW);
285
          gageShapeWtoI(arg->ctx->shape, rayDirI, rayDirW);
286
          ELL_3V_SUB(rayDirI, rayDirI, zeroI);
287
        } else {
288
          rayDirI[0] = AIR_DELTA(-lx, rayDirW[0], lx, mm, Mx);
289
          rayDirI[1] = AIR_DELTA(-ly, rayDirW[1], ly, mm, My);
290
          rayDirI[2] = AIR_DELTA(-lz, rayDirW[2], lz, mm, Mz);
291
        }
292
        rayLen = ((arg->ctx->cam->vspFaar - arg->ctx->cam->vspNeer)/
293
                  ELL_3V_DOT(rayDirW, arg->ctx->cam->N));
294
      }
295
      if ( (ret = (arg->ctx->rayBegin)(thread,
296
                                       arg->render,
297
                                       arg->ctx->user,
298
                                       uI, vI, rayLen,
299
                                       rayStartW, rayStartI,
300
                                       rayDirW, rayDirI)) ) {
301
        arg->errCode = ret;
302
        arg->whichErr = hooverErrRayBegin;
303
        return arg;
304
      }
305
306
      sampleI = 0;
307
      rayT = 0;
308
      while (1) {
309
        ELL_3V_SCALE_ADD2(rayPosW, 1.0, rayStartW, rayT, rayDirW);
310
        if (arg->ctx->shape) {
311
          gageShapeWtoI(arg->ctx->shape, rayPosI, rayPosW);
312
        } else {
313
          ELL_3V_SCALE_ADD2(rayPosI, 1.0, rayStartI, rayT, rayDirI);
314
        }
315
        inside = (AIR_IN_CL(mm, rayPosI[0], Mx) &&
316
                  AIR_IN_CL(mm, rayPosI[1], My) &&
317
                  AIR_IN_CL(mm, rayPosI[2], Mz));
318
        rayStep = (arg->ctx->sample)(thread,
319
                                     arg->render,
320
                                     arg->ctx->user,
321
                                     sampleI, rayT,
322
                                     inside,
323
                                     rayPosW, rayPosI);
324
        if (!AIR_EXISTS(rayStep)) {
325
          /* sampling failed */
326
          arg->errCode = 0;
327
          arg->whichErr = hooverErrSample;
328
          return arg;
329
        }
330
        if (!rayStep) {
331
          /* ray decided to finish itself */
332
          break;
333
        }
334
        /* else we moved to a new location along the ray */
335
        rayT += rayStep;
336
        if (!AIR_IN_CL(0, rayT, rayLen)) {
337
          /* ray stepped outside near-far clipping region, its done. */
338
          break;
339
        }
340
        sampleI++;
341
      }
342
343
      if ( (ret = (arg->ctx->rayEnd)(thread,
344
                                     arg->render,
345
                                     arg->ctx->user)) ) {
346
        arg->errCode = ret;
347
        arg->whichErr = hooverErrRayEnd;
348
        return arg;
349
      }
350
    }  /* end this scanline */
351
  } /* end while(1) assignment of scanlines */
352
353
  if ( (ret = (arg->ctx->threadEnd)(thread,
354
                                    arg->render,
355
                                    arg->ctx->user)) ) {
356
    arg->errCode = ret;
357
    arg->whichErr = hooverErrThreadEnd;
358
    return arg;
359
  }
360
361
  /* returning NULL actually indicates that there was NOT an error */
362
  return NULL;
363
}
364
365
typedef union {
366
  _hooverThreadArg **h;
367
  void **v;
368
} _htpu;
369
370
/*
371
******** hooverRender()
372
**
373
** because of the biff usage(), only one thread can call hooverRender(),
374
** and no promises if the threads themselves call biff...
375
*/
376
int
377
hooverRender(hooverContext *ctx, int *errCodeP, int *errThreadP) {
378
  static const char me[]="hooverRender";
379
  _hooverExtraContext *ec;
380
  _hooverThreadArg args[HOOVER_THREAD_MAX];
381
  _hooverThreadArg *errArg;
382
  airThread *thread[HOOVER_THREAD_MAX];
383
  _htpu u;
384
385
  void *render;
386
  int ret;
387
  airArray *mop;
388
  unsigned int threadIdx;
389
390
  if (!( errCodeP && errThreadP )) {
391
    biffAddf(HOOVER, "%s: got NULL int return pointer", me);
392
    return hooverErrInit;
393
  }
394
395
  /* this calls limnCameraUpdate() */
396
  if (hooverContextCheck(ctx)) {
397
    biffAddf(HOOVER, "%s: problem detected in given context", me);
398
    *errCodeP = 0;
399
    *errThreadP = 0;
400
    return hooverErrInit;
401
  }
402
403
  if (!(ec = _hooverExtraContextNew(ctx))) {
404
    biffAddf(HOOVER, "%s: problem creating thread context", me);
405
    *errCodeP = 0;
406
    *errThreadP = 0;
407
    return hooverErrInit;
408
  }
409
  mop = airMopNew();
410
  airMopAdd(mop, ec, (airMopper)_hooverExtraContextNix, airMopAlways);
411
  if ( (ret = (ctx->renderBegin)(&render, ctx->user)) ) {
412
    *errCodeP = ret;
413
    *errCodeP = 0;
414
    *errThreadP = 0;
415
    airMopError(mop);
416
    return hooverErrRenderBegin;
417
  }
418
419
  for (threadIdx=0; threadIdx<ctx->numThreads; threadIdx++) {
420
    args[threadIdx].ctx = ctx;
421
    args[threadIdx].ec = ec;
422
    args[threadIdx].render = render;
423
    args[threadIdx].whichThread = threadIdx;
424
    args[threadIdx].whichErr = hooverErrNone;
425
    args[threadIdx].errCode = 0;
426
    thread[threadIdx] = airThreadNew();
427
  }
428
  ctx->workIdx = 0;
429
  if (1 < ctx->numThreads) {
430
    ctx->workMutex = airThreadMutexNew();
431
  } else {
432
    ctx->workMutex = NULL;
433
  }
434
435
  /* (done): call airThreadStart() once per thread, passing the
436
     address of a distinct (and appropriately intialized)
437
     _hooverThreadArg to each.  If return of airThreadStart() is
438
     non-zero, put its return in *errCodeP, the number of the
439
     problematic in *errThreadP, and return hooverErrThreadCreate.
440
     Then call airThreadJoin() on all the threads, passing &errArg as
441
     "retval". On non-zero return, set *errCodeP and *errThreadP,
442
     and return hooverErrThreadJoin. If return of airThreadJoin() is
443
     zero, but the errArg is non-NULL, then assume that this errArg
444
     is actually just the passed _hooverThreadArg returned to us, and
445
     from this copy errArg->errCode into *errCodeP, and return
446
     errArg->whichErr */
447
448
  if (1 < ctx->numThreads && !airThreadCapable) {
449
    fprintf(stderr, "%s: WARNING: not multi-threaded; will do %d "
450
            "\"threads\" serially !!!\n", me, ctx->numThreads);
451
  }
452
453
  for (threadIdx=0; threadIdx<ctx->numThreads; threadIdx++) {
454
    if ((ret = airThreadStart(thread[threadIdx], _hooverThreadBody,
455
                              (void *) &args[threadIdx]))) {
456
      *errCodeP = ret;
457
      *errThreadP = threadIdx;
458
      airMopError(mop);
459
      return hooverErrThreadCreate;
460
    }
461
  }
462
463
  for (threadIdx=0; threadIdx<ctx->numThreads; threadIdx++) {
464
    u.h = &errArg;
465
    if ((ret = airThreadJoin(thread[threadIdx], u.v))) {
466
      *errCodeP = ret;
467
      *errThreadP = threadIdx;
468
      airMopError(mop);
469
      return hooverErrThreadJoin;
470
    }
471
    if (errArg != NULL) {
472
      *errCodeP = errArg->errCode;
473
      *errThreadP = threadIdx;
474
      return errArg->whichErr;
475
    }
476
    thread[threadIdx] = airThreadNix(thread[threadIdx]);
477
  }
478
479
  if (1 < ctx->numThreads) {
480
    ctx->workMutex = airThreadMutexNix(ctx->workMutex);
481
  }
482
483
  if ( (ret = (ctx->renderEnd)(render, ctx->user)) ) {
484
    *errCodeP = ret;
485
    *errThreadP = -1;
486
    return hooverErrRenderEnd;
487
  }
488
  render = NULL;
489
  airMopOkay(mop);
490
491
  *errCodeP = 0;
492
  *errThreadP = 0;
493
  return hooverErrNone;
494
}