GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/limn/cam.c Lines: 0 202 0.0 %
Date: 2017-05-26 Branches: 0 138 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
25
#include "limn.h"
26
27
/*
28
******** limnCameraUpdate()
29
**
30
** sets in cam: W2V, V2W, U, V, N, vspNeer, vspFaar, vspDist
31
** and, if fov and aspect are set, this also sets uRange and vRange
32
**
33
** This does use biff to describe problems with camera settings
34
*/
35
int
36
limnCameraUpdate(limnCamera *cam) {
37
  static const char me[] = "limnCameraUpdate";
38
  double len, bb[4], uu[4], vv[4], nn[4], TT[16], RR[16];
39
40
  if (!cam) {
41
    biffAddf(LIMN, "%s: got NULL pointer", me);
42
    return 1;
43
  }
44
45
  ELL_4V_SET(uu, 0, 0, 0, 0);
46
  ELL_4V_SET(vv, 0, 0, 0, 0);
47
  ELL_4V_SET(nn, 0, 0, 0, 0);
48
  ELL_4V_SET(bb, 0, 0, 0, 1);
49
  ELL_3V_SUB(nn, cam->at, cam->from);
50
  len = ELL_3V_LEN(nn);
51
  if (!len) {
52
    biffAddf(LIMN, "%s: cam->at (%g,%g,%g) == cam->from", me,
53
             cam->at[0], cam->at[1], cam->at[2]);
54
    return 1;
55
  }
56
  if (cam->atRelative) {
57
    /* ctx->cam->{neer,dist} are "at" relative */
58
    cam->vspNeer = cam->neer + len;
59
    cam->vspFaar = cam->faar + len;
60
    cam->vspDist = cam->dist + len;
61
  }
62
  else {
63
    /* ctx->cam->{neer,dist} are eye relative */
64
    cam->vspNeer = cam->neer;
65
    cam->vspFaar = cam->faar;
66
    cam->vspDist = cam->dist;
67
  }
68
  if (!(cam->vspNeer > 0 && cam->vspDist > 0 && cam->vspFaar > 0)) {
69
    biffAddf(LIMN, "%s: eye-relative near (%g), dist (%g), or far (%g) <= 0",
70
             me, cam->vspNeer, cam->vspDist, cam->vspFaar);
71
    return 1;
72
  }
73
  if (!(cam->vspNeer <= cam->vspFaar)) {
74
    biffAddf(LIMN, "%s: eye-relative near (%g) further than far (%g)",
75
             me, cam->vspNeer, cam->vspFaar);
76
    return 1 ;
77
  }
78
  if (AIR_EXISTS(cam->fov)) {
79
    if (!( AIR_IN_OP(0.0, cam->fov, 180.0) )) {
80
      biffAddf(LIMN, "%s: cam->fov (%g) not in valid range between 0 and 180",
81
               me, cam->fov);
82
      return 1 ;
83
    }
84
    if (!AIR_EXISTS(cam->aspect)) {
85
      biffAddf(LIMN, "%s: cam->fov set, but cam->aspect isn't", me);
86
      return 1;
87
    }
88
    /* "fov" is half vertical angle */
89
    cam->vRange[0] = -tan(cam->fov*AIR_PI/360)*(cam->vspDist);
90
    cam->vRange[1] = -cam->vRange[0];
91
    cam->uRange[0] = cam->vRange[0]*(cam->aspect);
92
    cam->uRange[1] = -cam->uRange[0];
93
  }
94
  /* else cam->fov isn't set, but we're not going to complain if
95
     uRange and vRange aren't both set ... */
96
97
  ELL_3V_SCALE(nn, 1.0/len, nn);
98
  ELL_3V_CROSS(uu, nn, cam->up);
99
  len = ELL_3V_LEN(uu);
100
  if (!len) {
101
    biffAddf(LIMN, "%s: cam->up is co-linear with view direction", me);
102
    return 1 ;
103
  }
104
  ELL_3V_SCALE(uu, 1.0/len, uu);
105
106
  if (cam->rightHanded) {
107
    ELL_3V_CROSS(vv, nn, uu);
108
  }
109
  else {
110
    ELL_3V_CROSS(vv, uu, nn);
111
  }
112
113
  ELL_4V_COPY(cam->U, uu);
114
  ELL_4V_COPY(cam->V, vv);
115
  ELL_4V_COPY(cam->N, nn);
116
  ELL_4M_TRANSLATE_SET(TT, -cam->from[0], -cam->from[1], -cam->from[2]);
117
  ELL_4M_ROWS_SET(RR, uu, vv, nn, bb);
118
  ELL_4M_MUL(cam->W2V, RR, TT);
119
  ell_4m_inv_d(cam->V2W, cam->W2V);
120
121
  return 0;
122
}
123
124
/*
125
******** limnCameraAspectSet
126
**
127
** simply sets the "aspect" field of the cam.  Note that calling this
128
** does *not* automatically mean that the uRange and vRange in the camera
129
** will be set according to the "fov"- the "fov" has to actually be set
130
** (be non-NaN) for that to happen.  This allows dumber functions to
131
** call this whenever they have the information required to do so, even
132
** if the "aspect" is not going to be needed for a given camera use
133
*/
134
int
135
limnCameraAspectSet(limnCamera *cam, unsigned int horz, unsigned int vert,
136
                    int centering) {
137
  static const char me[] = "limnCameraAspectSet";
138
139
  if (!cam) {
140
    biffAddf(LIMN, "%s: got NULL pointer", me);
141
    return 1;
142
  }
143
  if (!( horz > 0 && vert > 0 )) {
144
    biffAddf(LIMN, "%s: bad image dimensions %ux%u", me, horz, vert);
145
    return 1;
146
  }
147
  if (airEnumValCheck(nrrdCenter, centering)) {
148
    biffAddf(LIMN, "%s: centering %d not valid", me, centering);
149
    return 1;
150
  }
151
152
  if (nrrdCenterCell == centering) {
153
    cam->aspect = ((double)horz)/vert;
154
  } else {
155
    cam->aspect = ((double)(horz-1))/(vert-1);
156
  }
157
158
  return 0;
159
}
160
161
/*
162
******** limnCameraPathMake
163
**
164
** uses limnSplines to do camera paths based on key-frames
165
**
166
** output: cameras at all "numFrames" frames are set in the
167
** PRE-ALLOCATED array of output cameras, "cam".
168
**
169
** input:
170
** keycam: array of keyframe cameras
171
** time: times associated with the key frames
172
** ---> both of these arrays are length "numKeys" <---
173
** trackWhat: takes values from the limnCameraPathTrack* enum
174
** quatType: spline to control camera orientations. This is needed for
175
**          tracking at or from, but not needed for limnCameraPathTrackBoth.
176
**          This is the only limnSplineTypeSpec* argument that can be NULL.
177
** posType: spline to control whichever of from, at, and up are needed for
178
**          the given style of tracking.
179
** distType: spline to control neer, faar, dist: positions of near clipping,
180
**          far clipping, and image plane, as well as the
181
**          distance between from and at (which is used if not doing
182
**          limnCameraPathTrackBoth)
183
** viewType: spline to control fov (and aspect, if you're crazy)
184
**
185
** NOTE: The "atRelative", "orthographic", and "rightHanded" fields
186
** are copied from keycam[0] into all output cam[i], but you still need
187
** to correctly set them for all keycam[i] for limnCameraUpdate to work
188
** as expected.  Also, for the sake of simplicity, this function only works
189
** with fov and aspect, instead of {u,v}Range, and hence both "fov" and
190
** "aspect" need to set in *all* the keycams, even if neither of them
191
** ever changes!
192
*/
193
int
194
limnCameraPathMake(limnCamera *cam, int numFrames,
195
                   limnCamera *keycam, double *time, int numKeys,
196
                   int trackWhat,
197
                   limnSplineTypeSpec *quatType,
198
                   limnSplineTypeSpec *posType,
199
                   limnSplineTypeSpec *distType,
200
                   limnSplineTypeSpec *viewType) {
201
  static const char me[]="limnCameraPathMake";
202
  char which[AIR_STRLEN_MED];
203
  airArray *mop;
204
  Nrrd *nquat, *nfrom, *natpt, *nupvc, *ndist, *nfova, *ntime, *nsample;
205
  double fratVec[3], *quat, *from, *atpt, *upvc, *dist, *fova,
206
    W2V[9], N[3], fratDist;
207
  limnSpline *timeSpline, *quatSpline, *fromSpline, *atptSpline, *upvcSpline,
208
    *distSpline, *fovaSpline;
209
  limnSplineTypeSpec *timeType;
210
  int ii, E;
211
212
  if (!( cam && keycam && time && posType && distType && viewType )) {
213
    biffAddf(LIMN, "%s: got NULL pointer", me);
214
    return 1;
215
  }
216
  if (!( AIR_IN_OP(limnCameraPathTrackUnknown, trackWhat,
217
                   limnCameraPathTrackLast) )) {
218
    biffAddf(LIMN, "%s: trackWhat %d not in valid range [%d,%d]", me,
219
             trackWhat, limnCameraPathTrackUnknown+1,
220
             limnCameraPathTrackLast-1);
221
    return 1;
222
  }
223
  if (limnCameraPathTrackBoth != trackWhat && !quatType) {
224
    biffAddf(LIMN, "%s: need the quaternion limnSplineTypeSpec if not "
225
             "doing trackBoth", me);
226
    return 1;
227
  }
228
229
  /* create and allocate nrrds.  For the time being, we're allocating
230
     more different nrrds, and filling their contents, than we need
231
     to-- nquat is not needed if we're doing limnCameraPathTrackBoth,
232
     for example.  However, we do make an effort to only do the spline
233
     evaluation on the things we actually need to know. */
234
  mop = airMopNew();
235
  airMopAdd(mop, nquat = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
236
  airMopAdd(mop, nfrom = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
237
  airMopAdd(mop, natpt = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
238
  airMopAdd(mop, nupvc = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
239
  airMopAdd(mop, ndist = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
240
  airMopAdd(mop, nfova = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
241
  airMopAdd(mop, ntime = nrrdNew(), (airMopper)nrrdNix, airMopAlways);
242
  if (nrrdWrap_va(ntime, time, nrrdTypeDouble, 1,
243
                  AIR_CAST(size_t, numKeys))) {
244
    biffMovef(LIMN, NRRD, "%s: trouble wrapping time values", me);
245
    airMopError(mop); return 1;
246
  }
247
  airMopAdd(mop, nsample = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
248
  timeType = limnSplineTypeSpecNew(limnSplineTypeTimeWarp);
249
  airMopAdd(mop, timeType, (airMopper)limnSplineTypeSpecNix, airMopAlways);
250
  if (nrrdMaybeAlloc_va(nquat, nrrdTypeDouble, 2,
251
                        AIR_CAST(size_t, 4), AIR_CAST(size_t, numKeys))
252
      || nrrdMaybeAlloc_va(nfrom, nrrdTypeDouble, 2,
253
                           AIR_CAST(size_t, 3), AIR_CAST(size_t, numKeys))
254
      || nrrdMaybeAlloc_va(natpt, nrrdTypeDouble, 2,
255
                           AIR_CAST(size_t, 3), AIR_CAST(size_t, numKeys))
256
      || nrrdMaybeAlloc_va(nupvc, nrrdTypeDouble, 2,
257
                           AIR_CAST(size_t, 3), AIR_CAST(size_t, numKeys))
258
      || nrrdMaybeAlloc_va(ndist, nrrdTypeDouble, 2,
259
                           AIR_CAST(size_t, 4), AIR_CAST(size_t, numKeys))
260
      || nrrdMaybeAlloc_va(nfova, nrrdTypeDouble, 2,
261
                           AIR_CAST(size_t, 2), AIR_CAST(size_t, numKeys))) {
262
    biffMovef(LIMN, NRRD, "%s: couldn't allocate buffer nrrds", me);
263
    airMopError(mop); return 1;
264
  }
265
  quat = (double*)(nquat->data);
266
  from = (double*)(nfrom->data);
267
  atpt = (double*)(natpt->data);
268
  upvc = (double*)(nupvc->data);
269
  dist = (double*)(ndist->data);
270
  fova = (double*)(nfova->data);
271
272
  /* check cameras, and put camera information into nrrds */
273
  for (ii=0; ii<numKeys; ii++) {
274
    if (limnCameraUpdate(keycam + ii)) {
275
      biffAddf(LIMN, "%s: trouble with camera at keyframe %d\n", me, ii);
276
      airMopError(mop); return 1;
277
    }
278
    if (!( AIR_EXISTS(keycam[ii].fov) && AIR_EXISTS(keycam[ii].aspect) )) {
279
      biffAddf(LIMN, "%s: fov, aspect not both defined on keyframe %d",
280
               me, ii);
281
      airMopError(mop); return 1;
282
    }
283
    ell_4m_to_q_d(quat + 4*ii, keycam[ii].W2V);
284
    if (ii) {
285
      if (0 > ELL_4V_DOT(quat + 4*ii, quat + 4*(ii-1))) {
286
        ELL_4V_SCALE(quat + 4*ii, -1, quat + 4*ii);
287
      }
288
    }
289
    ELL_3V_COPY(from + 3*ii, keycam[ii].from);
290
    ELL_3V_COPY(atpt + 3*ii, keycam[ii].at);
291
    ELL_3V_COPY(upvc + 3*ii, keycam[ii].up);
292
    ELL_3V_SUB(fratVec, keycam[ii].from, keycam[ii].at);
293
    fratDist = ELL_3V_LEN(fratVec);
294
    ELL_4V_SET(dist + 4*ii, fratDist,
295
               keycam[ii].neer, keycam[ii].dist, keycam[ii].faar);
296
    ELL_2V_SET(fova + 2*ii, keycam[ii].fov, keycam[ii].aspect);
297
  }
298
299
  /* create splines from nrrds */
300
  if (!( (strcpy(which, "quaternion"), quatSpline =
301
          limnSplineCleverNew(nquat, limnSplineInfoQuaternion, quatType))
302
         && (strcpy(which, "from point"), fromSpline =
303
             limnSplineCleverNew(nfrom, limnSplineInfo3Vector, posType))
304
         && (strcpy(which, "at point"), atptSpline =
305
             limnSplineCleverNew(natpt, limnSplineInfo3Vector, posType))
306
         && (strcpy(which, "up vector"), upvcSpline =
307
             limnSplineCleverNew(nupvc, limnSplineInfo3Vector, posType))
308
         && (strcpy(which, "plane distances"), distSpline =
309
             limnSplineCleverNew(ndist, limnSplineInfo4Vector, distType))
310
         && (strcpy(which, "field-of-view"), fovaSpline =
311
             limnSplineCleverNew(nfova, limnSplineInfo2Vector, viewType))
312
         && (strcpy(which, "time warp"), timeSpline =
313
             limnSplineCleverNew(ntime, limnSplineInfoScalar, timeType)) )) {
314
    biffAddf(LIMN, "%s: trouble creating %s spline", me, which);
315
    airMopError(mop); return 1;
316
  }
317
  airMopAdd(mop, quatSpline, (airMopper)limnSplineNix, airMopAlways);
318
  airMopAdd(mop, fromSpline, (airMopper)limnSplineNix, airMopAlways);
319
  airMopAdd(mop, atptSpline, (airMopper)limnSplineNix, airMopAlways);
320
  airMopAdd(mop, upvcSpline, (airMopper)limnSplineNix, airMopAlways);
321
  airMopAdd(mop, distSpline, (airMopper)limnSplineNix, airMopAlways);
322
  airMopAdd(mop, fovaSpline, (airMopper)limnSplineNix, airMopAlways);
323
  airMopAdd(mop, timeSpline, (airMopper)limnSplineNix, airMopAlways);
324
325
  /* evaluate splines */
326
  E = AIR_FALSE;
327
  if (!E) E |= limnSplineSample(nsample, timeSpline,
328
                                limnSplineMinT(timeSpline), numFrames,
329
                                limnSplineMaxT(timeSpline));
330
  quat = NULL;
331
  from = NULL;
332
  atpt = NULL;
333
  upvc = NULL;
334
  switch(trackWhat) {
335
  case limnCameraPathTrackAt:
336
    if (!E) E |= limnSplineNrrdEvaluate(natpt, atptSpline, nsample);
337
    if (!E) atpt = (double*)(natpt->data);
338
    if (!E) E |= limnSplineNrrdEvaluate(nquat, quatSpline, nsample);
339
    if (!E) quat = (double*)(nquat->data);
340
    break;
341
  case limnCameraPathTrackFrom:
342
    if (!E) E |= limnSplineNrrdEvaluate(nfrom, fromSpline, nsample);
343
    if (!E) from = (double*)(nfrom->data);
344
    if (!E) E |= limnSplineNrrdEvaluate(nquat, quatSpline, nsample);
345
    if (!E) quat = (double*)(nquat->data);
346
    break;
347
  case limnCameraPathTrackBoth:
348
    if (!E) E |= limnSplineNrrdEvaluate(nfrom, fromSpline, nsample);
349
    if (!E) from = (double*)(nfrom->data);
350
    if (!E) E |= limnSplineNrrdEvaluate(natpt, atptSpline, nsample);
351
    if (!E) atpt = (double*)(natpt->data);
352
    if (!E) E |= limnSplineNrrdEvaluate(nupvc, upvcSpline, nsample);
353
    if (!E) upvc = (double*)(nupvc->data);
354
    break;
355
  }
356
  dist = NULL;
357
  if (!E) E |= limnSplineNrrdEvaluate(ndist, distSpline, nsample);
358
  if (!E) dist = (double*)(ndist->data);
359
  fova = NULL;
360
  if (!E) E |= limnSplineNrrdEvaluate(nfova, fovaSpline, nsample);
361
  if (!E) fova = (double*)(nfova->data);
362
  if (E) {
363
    biffAddf(LIMN, "%s: trouble evaluating splines", me);
364
    airMopError(mop); return 1;
365
  }
366
367
  /* copy information from nrrds back into cameras */
368
  for (ii=0; ii<numFrames; ii++) {
369
    cam[ii].atRelative = keycam[0].atRelative;
370
    cam[ii].orthographic = keycam[0].orthographic;
371
    cam[ii].rightHanded = keycam[0].rightHanded;
372
    if (limnCameraPathTrackBoth == trackWhat) {
373
      ELL_3V_COPY(cam[ii].from, from + 3*ii);
374
      ELL_3V_COPY(cam[ii].at, atpt + 3*ii);
375
      ELL_3V_COPY(cam[ii].up, upvc + 3*ii);
376
    } else {
377
      fratDist = (dist + 4*ii)[0];
378
      ell_q_to_3m_d(W2V, quat + 4*ii);
379
      ELL_3MV_ROW1_GET(cam[ii].up, W2V);
380
      if (cam[ii].rightHanded) {
381
        ELL_3V_SCALE(cam[ii].up, -1, cam[ii].up);
382
      }
383
      ELL_3MV_ROW2_GET(N, W2V);
384
      if (limnCameraPathTrackFrom == trackWhat) {
385
        ELL_3V_COPY(cam[ii].from, from + 3*ii);
386
        ELL_3V_SCALE_ADD2(cam[ii].at, 1.0, cam[ii].from, fratDist, N);
387
      } else {
388
        ELL_3V_COPY(cam[ii].at, atpt + 3*ii);
389
        ELL_3V_SCALE_ADD2(cam[ii].from, 1.0, cam[ii].at, -fratDist, N);
390
      }
391
    }
392
    cam[ii].neer = (dist + 4*ii)[1];
393
    cam[ii].dist = (dist + 4*ii)[2];
394
    cam[ii].faar = (dist + 4*ii)[3];
395
    cam[ii].fov = (fova + 2*ii)[0];
396
    cam[ii].aspect = (fova + 2*ii)[1];
397
    if (limnCameraUpdate(cam + ii)) {
398
      biffAddf(LIMN, "%s: trouble with output camera %d\n", me, ii);
399
      airMopError(mop); return 1;
400
    }
401
  }
402
403
  airMopOkay(mop);
404
  return 0;
405
}