1 /*
2   Teem: Tools to process and visualize scientific data and images             .
3   Copyright (C) 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
limnCameraUpdate(limnCamera * cam)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
limnCameraAspectSet(limnCamera * cam,unsigned int horz,unsigned int vert,int centering)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
limnCameraPathMake(limnCamera * cam,int numFrames,limnCamera * keycam,double * time,int numKeys,int trackWhat,limnSplineTypeSpec * quatType,limnSplineTypeSpec * posType,limnSplineTypeSpec * distType,limnSplineTypeSpec * viewType)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 }
406