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