GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/ten/tendGlyph.c Lines: 104 329 31.6 %
Date: 2017-05-26 Branches: 2 90 2.2 %

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 postscript or ray-traced renderings of 3D glyphs"
28
static const char *_tend_glyphInfoL =
29
  (INFO
30
   ".  Whether the output is postscript or a ray-traced image is controlled "
31
   "by the initial \"rt\" flag (by default, the output is postscript). "
32
   "Because this is doing viz/graphics, many parameters need to be set. "
33
   "Use a response file to simplify giving the command-line options which "
34
   "aren't changing between invocations. "
35
   "The postscript output is an EPS file, suitable for including as a figure "
36
   "in LaTeX, or viewing with ghostview, or distilling into PDF. "
37
   "The ray-traced output is a 5 channel (R,G,B,A,T) float nrrd, suitable for "
38
   "\"unu crop -min 0 0 0 -max 2 M M \" followed by "
39
   "\"unu gamma\" and/or \"unu quantize -b 8\".");
40
41
#define _LIMNMAGIC "LIMN0000"
42
43
int
44
_tendGlyphReadCams(int imgSize[2], limnCamera **camP,
45
                   unsigned int *numCamsP, FILE *fin) {
46
  static const char me[]="_tendGlyphReadCams";
47
  char line[AIR_STRLEN_HUGE];
48
  int ki;
49
  double di, dn, df, fr[3], at[3], up[3], va, dwell;
50
  airArray *mop, *camA;
51
52
  if (!( 0 < airOneLine(fin, line, AIR_STRLEN_HUGE)
53
         && !strcmp(_LIMNMAGIC, line) )) {
54
    biffAddf(TEN, "%s: couldn't read first line or it wasn't \"%s\"",
55
             me, _LIMNMAGIC);
56
    return 1;
57
  }
58
  if (!( 0 < airOneLine(fin, line, AIR_STRLEN_HUGE)
59
         && 2 == (airStrtrans(airStrtrans(line, '{', ' '), '}', ' '),
60
                  sscanf(line, "imgSize %d %d", imgSize+0, imgSize+1)) )) {
61
    biffAddf(TEN, "%s: couldn't read second line or it wasn't "
62
             "\"imgSize <sizeX> <sizeY>\"", me);
63
    return 1;
64
  }
65
66
  mop = airMopNew();
67
  camA = airArrayNew((void **)camP, numCamsP, sizeof(limnCamera), 1);
68
  airMopAdd(mop, camA, (airMopper)airArrayNix, airMopAlways);
69
70
  while ( 0 < airOneLine(fin, line, AIR_STRLEN_HUGE) ) {
71
    airStrtrans(airStrtrans(line, '{', ' '), '}', ' ');
72
    ki = airArrayLenIncr(camA, 1);
73
    if (14 != sscanf(line, "cam.di %lg cam.at %lg %lg %lg "
74
                     "cam.up %lg %lg %lg cam.dn %lg cam.df %lg cam.va %lg "
75
                     "relDwell %lg cam.fr %lg %lg %lg",
76
                     &di, at+0, at+1, at+2,
77
                     up+0, up+1, up+2, &dn, &df, &va,
78
                     &dwell, fr+0, fr+1, fr+2)) {
79
      biffAddf(TEN, "%s: trouble parsing line %d: \"%s\"", me, ki, line);
80
      airMopError(mop); return 1;
81
    }
82
    (*camP)[ki].neer = dn;
83
    (*camP)[ki].faar = df;
84
    (*camP)[ki].dist = di;
85
    ELL_3V_COPY((*camP)[ki].from, fr);
86
    ELL_3V_COPY((*camP)[ki].at, at);
87
    ELL_3V_COPY((*camP)[ki].up, up);
88
    (*camP)[ki].fov = va;
89
    (*camP)[ki].aspect = (double)imgSize[0]/imgSize[1];
90
    (*camP)[ki].atRelative = AIR_FALSE;
91
    (*camP)[ki].orthographic = AIR_FALSE;
92
    (*camP)[ki].rightHanded = AIR_TRUE;
93
  }
94
95
  airMopOkay(mop);
96
  return 0;
97
}
98
99
int
100
tend_glyphMain(int argc, const char **argv, const char *me,
101
               hestParm *hparm) {
102
2
  int pret, doRT = AIR_FALSE;
103
1
  hestOpt *hopt = NULL;
104
1
  char *perr, *err;
105
  airArray *mop;
106
107
1
  Nrrd *nin, *emap, *nraw, *npos, *nslc;
108
1
  char *outS;
109
1
  limnCamera *cam, *hackcams;
110
  limnObject *glyph;
111
  limnWindow *win;
112
  echoObject *rect=NULL;
113
  echoScene *scene;
114
  echoRTParm *eparm;
115
  echoGlobalState *gstate;
116
  tenGlyphParm *gparm;
117
1
  float bg[3], edgeColor[3], buvne[5], shadow, creaseAngle;
118
1
  int ires[2], slice[2], nobg, ambocc, concave;
119
1
  unsigned int hackci, hacknumcam;
120
1
  size_t hackmin[3]={0,0,0}, hackmax[3]={2,0,0};
121
1
  char *hackFN, hackoutFN[AIR_STRLEN_SMALL];
122
  FILE *hackF;
123
  Nrrd *hacknpng, *hacknrgb;
124
  NrrdRange *hackrange;
125
126
1
  double v2w[9], ldir[3], edir[3], fdir[3], corn[3], len;
127
128
  /* so that command-line options can be read from file */
129
1
  hparm->respFileEnable = AIR_TRUE;
130
1
  hparm->elideSingleEmptyStringDefault = AIR_TRUE;
131
132
1
  mop = airMopNew();
133
1
  cam = limnCameraNew();
134
1
  airMopAdd(mop, cam, (airMopper)limnCameraNix, airMopAlways);
135
1
  glyph = limnObjectNew(1000, AIR_TRUE);
136
1
  airMopAdd(mop, glyph, (airMopper)limnObjectNix, airMopAlways);
137
1
  scene = echoSceneNew();
138
1
  airMopAdd(mop, scene, (airMopper)echoSceneNix, airMopAlways);
139
1
  win = limnWindowNew(limnDevicePS);
140
1
  airMopAdd(mop, win, (airMopper)limnWindowNix, airMopAlways);
141
1
  gparm = tenGlyphParmNew();
142
1
  airMopAdd(mop, gparm, (airMopper)tenGlyphParmNix, airMopAlways);
143
1
  eparm = echoRTParmNew();
144
1
  airMopAdd(mop, eparm, (airMopper)echoRTParmNix, airMopAlways);
145
146
  /* do postscript or ray-traced? */
147
1
  hestOptAdd(&hopt, "rt", NULL, airTypeFloat, 0, 0, &doRT, NULL,
148
             "generate ray-traced output.  By default (not using this "
149
             "option), postscript output is generated.");
150
151
1
  hestOptAdd(&hopt, "v", "level", airTypeInt, 1, 1, &(gparm->verbose), "0",
152
             "verbosity level");
153
154
  /* which points will rendered */
155
1
  hestOptAdd(&hopt, "ctr", "conf thresh", airTypeFloat, 1, 1,
156
1
             &(gparm->confThresh), "0.5",
157
             "Glyphs will be drawn only for tensors with confidence "
158
             "values greater than this threshold");
159
1
  hestOptAdd(&hopt, "a", "aniso", airTypeEnum, 1, 1,
160
1
             &(gparm->anisoType), "fa",
161
             "Which anisotropy metric to use for thresholding the data "
162
1
             "points to be drawn", NULL, tenAniso);
163
1
  hestOptAdd(&hopt, "atr", "aniso thresh", airTypeFloat, 1, 1,
164
1
             &(gparm->anisoThresh), "0.5",
165
             "Glyphs will be drawn only for tensors with anisotropy "
166
             "greater than this threshold");
167
1
  hestOptAdd(&hopt, "p", "pos array", airTypeOther, 1, 1, &npos, "",
168
             "Instead of being on a grid, tensors are at arbitrary locations, "
169
             "as defined by this 3-by-N array of floats. Doing this makes "
170
             "various other options moot", NULL, NULL,
171
1
             nrrdHestNrrd);
172
2
  hestOptAdd(&hopt, "m", "mask vol", airTypeOther, 1, 1, &(gparm->nmask), "",
173
             "Scalar volume (if any) for masking region in which glyphs are "
174
             "drawn, in conjunction with \"mtr\" flag. ", NULL, NULL,
175
1
             nrrdHestNrrd);
176
1
  hestOptAdd(&hopt, "mtr", "mask thresh", airTypeFloat, 1, 1,
177
1
             &(gparm->maskThresh),
178
             "0.5", "Glyphs will be drawn only for tensors with mask "
179
             "value greater than this threshold");
180
181
  /* how glyphs will be shaped */
182
1
  hestOptAdd(&hopt, "g", "glyph shape", airTypeEnum, 1, 1,
183
1
             &(gparm->glyphType), "box",
184
             "shape of glyph to use for display.  Possibilities "
185
             "include \"box\", \"sphere\", \"cylinder\", and "
186
1
             "\"superquad\"", NULL, tenGlyphType);
187
1
  hestOptAdd(&hopt, "sh", "sharpness", airTypeFloat, 1, 1,
188
1
             &(gparm->sqdSharp), "3.0",
189
             "for superquadric glyphs, how much to sharp edges form as a "
190
             "function of differences between eigenvalues.  Higher values "
191
             "mean that edges form more easily");
192
1
  hestOptAdd(&hopt, "gsc", "scale", airTypeFloat, 1, 1, &(gparm->glyphScale),
193
             "0.01", "over-all glyph size in world-space");
194
195
  /* how glyphs will be colored */
196
1
  hestOptAdd(&hopt, "c", "evector #", airTypeInt, 1, 1, &(gparm->colEvec), "0",
197
             "which eigenvector should determine coloring. "
198
             "(formally \"v\") "
199
             "\"0\", \"1\", \"2\" are principal, medium, and minor");
200
1
  hestOptAdd(&hopt, "sat", "saturation", airTypeFloat, 1, 1,
201
1
             &(gparm->colMaxSat), "1.0",
202
             "maximal saturation to use on glyph colors (use 0.0 to "
203
             "create a black and white image)");
204
1
  hestOptAdd(&hopt, "ga", "aniso", airTypeEnum, 1, 1,
205
1
             &(gparm->colAnisoType), "fa",
206
             "Which anisotropy metric to use for modulating the "
207
1
             "saturation of the glyph color", NULL, tenAniso);
208
1
  hestOptAdd(&hopt, "am", "aniso mod", airTypeFloat, 1, 1,
209
1
             &(gparm->colAnisoModulate),
210
             "0.0", "How much to modulate glyph color saturation by "
211
             "anisotropy (as chosen by \"-ga\").  "
212
             "If 1.0, then glyphs for zero anisotropy "
213
             "data points will have no hue. ");
214
1
  hestOptAdd(&hopt, "gg", "gray", airTypeFloat, 1, 1, &(gparm->colIsoGray),
215
             "1.0", "desaturating glyph color due to low anisotropy "
216
             "tends towards this gray level");
217
1
  hestOptAdd(&hopt, "gam", "gamma", airTypeFloat, 1, 1, &(gparm->colGamma),
218
             "0.7", "gamma to use on color components (after saturation)");
219
1
  hestOptAdd(&hopt, "emap", "env map", airTypeOther, 1, 1, &emap, "",
220
             "environment map to use for shading glyphs.  By default, "
221
1
             "there is no shading", NULL, NULL, nrrdHestNrrd);
222
1
  hestOptAdd(&hopt, "adsp", "phong", airTypeFloat, 4, 4, &(gparm->ADSP),
223
             "0 1 0 30", "phong ambient, diffuse, specular components, "
224
             "and specular power");
225
1
  hestOptAdd(&hopt, "bg", "background", airTypeFloat, 3, 3, bg, "1 1 1",
226
             "background RGB color; each component in range [0.0,1.0]");
227
1
  hestOptAdd(&hopt, "ec", "edge rgb", airTypeFloat, 3, 3, edgeColor, "0 0 0",
228
             "edge RGB color; each component in range [0.0,1.0]");
229
230
  /* parameters for showing a dataset slice */
231
1
  hestOptAdd(&hopt, "slc", "axis pos", airTypeInt, 2, 2, slice, "-1 -1",
232
             "For showing a gray-scale slice of anisotropy: the axis "
233
             "and position along which to slice.  Use \"-1 -1\" to signify "
234
             "that no slice should be shown");
235
1
  hestOptAdd(&hopt, "si", "slice image", airTypeOther, 1, 1, &nslc, "",
236
             "Instead of showing a slice of the anisotropy used to cull "
237
             "glyphs, show something else. ", NULL, NULL,
238
1
             nrrdHestNrrd);
239
1
  hestOptAdd(&hopt, "off", "slice offset", airTypeFloat, 1, 1,
240
1
             &(gparm->sliceOffset), "0.0",
241
             "Offset from slice position to render slice at (so that it "
242
             "doesn't occlude glyphs).");
243
1
  hestOptAdd(&hopt, "sg", "slice gamma", airTypeFloat, 1, 1,
244
1
             &(gparm->sliceGamma), "1.7",
245
             "Gamma to apply to values on slice.");
246
1
  hestOptAdd(&hopt, "sb", "slice bias", airTypeFloat, 1, 1,
247
1
             &(gparm->sliceBias), "0.05",
248
             "amount by which to bump up slice gray values prior to gamma.");
249
250
  /* camera */
251
1
  hestOptAdd(&hopt, "fr", "from point", airTypeDouble, 3, 3, cam->from, NULL,
252
             "position of camera, used to determine view vector");
253
1
  hestOptAdd(&hopt, "at", "at point", airTypeDouble, 3, 3, cam->at, "0 0 0",
254
             "camera look-at point, used to determine view vector");
255
1
  hestOptAdd(&hopt, "up", "up vector", airTypeDouble, 3, 3, cam->up, "0 0 1",
256
             "camera pseudo-up vector, used to determine view coordinates");
257
1
  hestOptAdd(&hopt, "rh", NULL, airTypeInt, 0, 0, &(cam->rightHanded), NULL,
258
             "use a right-handed UVN frame (V points down)");
259
1
  hestOptAdd(&hopt, "dn", "near clip", airTypeDouble, 1, 1, &(cam->neer), "-2",
260
             "position of near clipping plane, relative to look-at point");
261
1
  hestOptAdd(&hopt, "df", "far clip", airTypeDouble, 1, 1, &(cam->faar), "2",
262
             "position of far clipping plane, relative to look-at point");
263
1
  hestOptAdd(&hopt, "or", NULL, airTypeInt, 0, 0, &(cam->orthographic), NULL,
264
             "use orthogonal projection");
265
1
  hestOptAdd(&hopt, "ur", "uMin uMax", airTypeDouble, 2, 2, cam->uRange,
266
             "-1 1", "range in U direction of image plane");
267
1
  hestOptAdd(&hopt, "vr", "vMin vMax", airTypeDouble, 2, 2, cam->vRange,
268
             "-1 1", "range in V direction of image plane");
269
1
  hestOptAdd(&hopt, "fv", "fov", airTypeDouble, 1, 1, &(cam->fov), "nan",
270
             "if not NaN, vertical field-of-view, in degrees");
271
272
  /* postscript-specific options */
273
1
  hestOptAdd(&hopt, "gr", "glyph res", airTypeInt, 1, 1, &(gparm->facetRes),
274
             "10", "(* postscript only *) "
275
             "resolution of polygonalization of glyphs (all glyphs "
276
             "other than the default box)");
277
1
  hestOptAdd(&hopt, "wd", "3 widths", airTypeFloat, 3, 3, gparm->edgeWidth,
278
             "0.8 0.4 0.0",  "(* postscript only *) "
279
             "width of edges drawn for three kinds of glyph "
280
             "edges: silohuette, crease, non-crease");
281
1
  hestOptAdd(&hopt, "psc", "scale", airTypeFloat, 1, 1, &(win->scale), "300",
282
             "(* postscript only *) "
283
             "scaling from screen space units to postscript units "
284
             "(in points)");
285
1
  hestOptAdd(&hopt, "ca", "angle", airTypeFloat, 1, 1, &creaseAngle, "70",
286
             "(* postscript only *) "
287
             "minimum crease angle");
288
1
  hestOptAdd(&hopt, "nobg", NULL, airTypeInt, 0, 0, &nobg, NULL,
289
             "(* postscript only *) "
290
             "don't initially fill with background color");
291
1
  hestOptAdd(&hopt, "concave", NULL, airTypeInt, 0, 0, &concave, NULL,
292
             "use slightly buggy rendering method suitable for "
293
             "concave or self-occluding objects");
294
295
  /* ray-traced-specific options */
296
1
  hestOptAdd(&hopt, "is", "nx ny", airTypeInt, 2, 2, ires, "256 256",
297
             "(* ray-traced only *) "
298
             "image size (resolution) to render");
299
1
  hestOptAdd(&hopt, "ns", "# samp", airTypeInt, 1, 1, &(eparm->numSamples),"4",
300
             "(* ray-traced only *) "
301
             "number of samples per pixel (must be a square number)");
302
1
  if (airThreadCapable) {
303
1
    hestOptAdd(&hopt, "nt", "# threads", airTypeInt, 1, 1,
304
1
               &(eparm->numThreads), "1",
305
               "(* ray-traced only *) "
306
               "number of threads to be used for rendering");
307
1
  }
308
1
  hestOptAdd(&hopt, "al", "B U V N E", airTypeFloat, 5, 5, buvne,
309
             "0 -1 -1 -4 0.7",
310
             "(* ray-traced only *) "
311
             "brightness (B), view-space location (U V N), "
312
             "and length of edge (E) "
313
             "of a square area light source, for getting soft shadows. "
314
             "Requires lots more samples \"-ns\" to converge.  Use "
315
             "brightness 0 (the default) to turn this off, and use "
316
             "environment map-based shading (\"-emap\") instead. ");
317
1
  hestOptAdd(&hopt, "ao", NULL, airTypeInt, 0, 0, &ambocc, NULL,
318
             "set up 6 area lights in a box to approximate "
319
             "ambient occlusion");
320
1
  hestOptAdd(&hopt, "shadow", "s", airTypeFloat, 1, 1, &shadow, "1.0",
321
             "the extent to which shadowing occurs");
322
1
  hestOptAdd(&hopt, "hack", "hack", airTypeString, 1, 1, &hackFN, "",
323
             "don't mind me");
324
325
  /* input/output */
326
1
  hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
327
1
             "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd);
328
1
  hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
329
             "output file");
330
331
1
  airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
332
2
  USAGE(_tend_glyphInfoL);
333
  PARSE();
334
  airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);
335
336
  /* set up slicing stuff */
337
  if (!( -1 == slice[0] && -1 == slice[1] )) {
338
    gparm->doSlice = AIR_TRUE;
339
    gparm->sliceAxis = slice[0];
340
    gparm->slicePos = slice[1];
341
    gparm->sliceAnisoType = gparm->anisoType;
342
    /* gparm->sliceOffset set by hest */
343
  }
344
345
  if (npos) {
346
    fprintf(stderr, "!%s: have npos --> turning off onlyPositive \n", me);
347
    gparm->onlyPositive = AIR_FALSE;
348
  }
349
350
  if (gparm->verbose) {
351
    fprintf(stderr, "%s: verbose = %d\n", me, gparm->verbose);
352
  }
353
  if (tenGlyphGen(doRT ? NULL : glyph,
354
                  doRT ? scene : NULL,
355
                  gparm,
356
                  nin, npos, nslc)) {
357
    airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways);
358
    fprintf(stderr, "%s: trouble generating glyphs:\n%s\n", me, err);
359
    airMopError(mop); return 1;
360
  }
361
  if (AIR_EXISTS(cam->fov)) {
362
    if (limnCameraAspectSet(cam, ires[0], ires[1], nrrdCenterCell)) {
363
      airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
364
      fprintf(stderr, "%s: trouble with camera:\n%s\n", me, err);
365
      airMopError(mop); return 1;
366
    }
367
  }
368
  cam->dist = 0;
369
  cam->atRelative = AIR_TRUE;
370
  if (limnCameraUpdate(cam)) {
371
    airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
372
    fprintf(stderr, "%s: trouble with camera:\n%s\n", me, err);
373
    airMopError(mop); return 1;
374
  }
375
  if (doRT) {
376
    nraw = nrrdNew();
377
    airMopAdd(mop, nraw, (airMopper)nrrdNuke, airMopAlways);
378
    gstate = echoGlobalStateNew();
379
    airMopAdd(mop, gstate, (airMopper)echoGlobalStateNix, airMopAlways);
380
    eparm->shadow = shadow;
381
    if (buvne[0] > 0) {
382
      ELL_34M_EXTRACT(v2w, cam->V2W);
383
      ELL_3MV_MUL(ldir, v2w, buvne+1);
384
      ell_3v_perp_d(edir, ldir);
385
      ELL_3V_NORM(edir, edir, len);
386
      ELL_3V_CROSS(fdir, ldir, edir);
387
      ELL_3V_NORM(fdir, fdir, len);
388
      ELL_3V_SCALE(edir, buvne[4]/2, edir);
389
      ELL_3V_SCALE(fdir, buvne[4]/2, fdir);
390
      ELL_3V_ADD4(corn, cam->at, ldir, edir, fdir);
391
      rect = echoObjectNew(scene, echoTypeRectangle);
392
      echoRectangleSet(rect,
393
                       corn[0], corn[1], corn[2],
394
                       -edir[0]*2, -edir[1]*2, -edir[2]*2,
395
                       -fdir[0]*2, -fdir[1]*2, -fdir[2]*2);
396
      echoColorSet(rect, 1, 1, 1, 1);
397
      echoMatterLightSet(scene, rect, buvne[0], 0);
398
      echoObjectAdd(scene, rect);
399
    }
400
    if (ambocc) {
401
      double eye[3], llen;
402
      ELL_3V_SUB(eye, cam->from, cam->at);
403
      llen = 4*ELL_3V_LEN(eye);
404
405
      ELL_3V_COPY(corn, cam->at);
406
      corn[0] -= llen/2;
407
      corn[1] -= llen/2;
408
      corn[2] -= llen/2;
409
      rect = echoObjectNew(scene, echoTypeRectangle);
410
      echoRectangleSet(rect, corn[0], corn[1], corn[2],
411
                       llen, 0, 0,       0, llen, 0);
412
      echoColorSet(rect, 1, 1, 1, 1);
413
      echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen));
414
      echoObjectAdd(scene, rect);
415
      rect = echoObjectNew(scene, echoTypeRectangle);
416
      echoRectangleSet(rect, corn[0], corn[1], corn[2],
417
                       0, 0, llen,       llen, 0, 0);
418
      echoColorSet(rect, 1, 1, 1, 1);
419
      echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen));
420
      echoObjectAdd(scene, rect);
421
      rect = echoObjectNew(scene, echoTypeRectangle);
422
      echoRectangleSet(rect, corn[0], corn[1], corn[2],
423
                       0, llen, 0,      0, 0, llen);
424
      echoColorSet(rect, 1, 1, 1, 1);
425
      echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen));
426
      echoObjectAdd(scene, rect);
427
428
      corn[0] += llen/2;
429
      corn[1] += llen/2;
430
      corn[2] += llen/2;
431
      rect = echoObjectNew(scene, echoTypeRectangle);
432
      echoRectangleSet(rect, corn[0], corn[1], corn[2],
433
                       0, -llen, 0,         -llen, 0, 0);
434
      echoColorSet(rect, 1, 1, 1, 1);
435
      echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen));
436
      echoObjectAdd(scene, rect);
437
      rect = echoObjectNew(scene, echoTypeRectangle);
438
      echoRectangleSet(rect, corn[0], corn[1], corn[2],
439
                       -llen, 0, 0,        0, 0, -llen);
440
      echoColorSet(rect, 1, 1, 1, 1);
441
      echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen));
442
      echoObjectAdd(scene, rect);
443
      rect = echoObjectNew(scene, echoTypeRectangle);
444
      echoRectangleSet(rect, corn[0], corn[1], corn[2],
445
                       0, 0, -llen,      0, -llen, 0);
446
      echoColorSet(rect, 1, 1, 1, 1);
447
      echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen));
448
      echoObjectAdd(scene, rect);
449
450
    }
451
    eparm->imgResU = ires[0];
452
    eparm->imgResV = ires[1];
453
    eparm->jitterType = (eparm->numSamples > 1
454
                         ? echoJitterJitter
455
                         : echoJitterNone);
456
    eparm->aperture = 0;
457
    eparm->renderBoxes = AIR_FALSE;
458
    eparm->seedRand = AIR_FALSE;
459
    eparm->renderLights = AIR_FALSE;
460
    ELL_3V_COPY(scene->bkgr, bg);
461
    scene->envmap = emap;
462
    if (!airStrlen(hackFN)) {
463
      /* normal operation: one ray-tracing for one invocation */
464
      if (echoRTRender(nraw, cam, scene, eparm, gstate)) {
465
        airMopAdd(mop, err = biffGetDone(ECHO), airFree, airMopAlways);
466
        fprintf(stderr, "%s: trouble ray-tracing %s\n", me, err);
467
        airMopError(mop);
468
        return 1;
469
      }
470
      if (nrrdSave(outS, nraw, NULL)) {
471
        airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways);
472
        fprintf(stderr, "%s: trouble saving ray-tracing output %s\n", me, err);
473
        airMopError(mop);
474
        return 1;
475
      }
476
    } else {
477
      /* hack: multiple renderings per invocation */
478
      if (!(hackF = airFopen(hackFN, stdin, "rb"))) {
479
        fprintf(stderr, "%s: couldn't fopen(\"%s\",\"rb\"): %s\n",
480
                me, hackFN, strerror(errno));
481
        airMopError(mop); return 1;
482
      }
483
      if (_tendGlyphReadCams(ires, &hackcams, &hacknumcam, hackF)) {
484
        airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways);
485
        fprintf(stderr, "%s: trouble reading frames %s\n", me, err);
486
        airMopError(mop);
487
        return 1;
488
      }
489
      eparm->imgResU = ires[0];
490
      eparm->imgResV = ires[1];
491
      hackmax[1] = ires[0]-1;
492
      hackmax[2] = ires[1]-1;
493
      hacknrgb = nrrdNew();
494
      hacknpng = nrrdNew();
495
      airMopAdd(mop, hacknrgb, (airMopper)nrrdNuke, airMopAlways);
496
      airMopAdd(mop, hacknpng, (airMopper)nrrdNuke, airMopAlways);
497
      hackrange = nrrdRangeNew(0.0, 1.0);
498
      airMopAdd(mop, hackrange, (airMopper)nrrdRangeNix, airMopAlways);
499
      for (hackci=0; hackci<hacknumcam; hackci++) {
500
        memcpy(cam, hackcams + hackci, sizeof(limnCamera));
501
        /* rightHanded and orthographic not handled nicely */
502
503
        if (rect) {
504
          if (limnCameraUpdate(cam)) {
505
            airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
506
            fprintf(stderr, "%s: trouble with camera:\n%s\n", me, err);
507
            airMopError(mop); return 1;
508
          }
509
          ELL_34M_EXTRACT(v2w, cam->V2W);
510
          ELL_3MV_MUL(ldir, v2w, buvne+1);
511
          ell_3v_perp_d(edir, ldir);
512
          ELL_3V_NORM(edir, edir, len);
513
          ELL_3V_CROSS(fdir, ldir, edir);
514
          ELL_3V_NORM(fdir, fdir, len);
515
          ELL_3V_SCALE(edir, buvne[4]/2, edir);
516
          ELL_3V_SCALE(fdir, buvne[4]/2, fdir);
517
          ELL_3V_ADD4(corn, cam->at, ldir, edir, fdir);
518
          echoRectangleSet(rect,
519
                           corn[0], corn[1], corn[2],
520
                           edir[0]*2, edir[1]*2, edir[2]*2,
521
                           fdir[0]*2, fdir[1]*2, fdir[2]*2);
522
        }
523
524
        if (echoRTRender(nraw, cam, scene, eparm, gstate)) {
525
          airMopAdd(mop, err = biffGetDone(ECHO), airFree, airMopAlways);
526
          fprintf(stderr, "%s: trouble ray-tracing %s\n", me, err);
527
          airMopError(mop);
528
          return 1;
529
        }
530
        sprintf(hackoutFN, "%04d.png", hackci);
531
        if (nrrdCrop(hacknrgb, nraw, hackmin, hackmax)
532
            || nrrdQuantize(hacknpng, hacknrgb, hackrange, 8)
533
            || nrrdSave(hackoutFN, hacknpng, NULL)) {
534
          airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways);
535
          fprintf(stderr, "%s: trouble saving output %s\n", me, err);
536
          airMopError(mop);
537
          return 1;
538
        }
539
      }
540
    }
541
  } else {
542
    if (!(win->file = airFopen(outS, stdout, "wb"))) {
543
      fprintf(stderr, "%s: couldn't fopen(\"%s\",\"wb\"): %s\n",
544
              me, outS, strerror(errno));
545
      airMopError(mop); return 1;
546
    }
547
    airMopAdd(mop, win->file, (airMopper)airFclose, airMopAlways);
548
    cam->neer = -0.000000001;
549
    cam->faar = 0.0000000001;
550
    win->ps.lineWidth[limnEdgeTypeBackFacet] = 0;
551
    win->ps.lineWidth[limnEdgeTypeBackCrease] = 0;
552
    win->ps.lineWidth[limnEdgeTypeContour] = gparm->edgeWidth[0];
553
    win->ps.lineWidth[limnEdgeTypeFrontCrease] = gparm->edgeWidth[1];
554
    win->ps.lineWidth[limnEdgeTypeFrontFacet] = gparm->edgeWidth[2];
555
    win->ps.lineWidth[limnEdgeTypeBorder] = 0;
556
      /* win->ps.lineWidth[limnEdgeTypeFrontCrease]; */
557
    win->ps.creaseAngle = creaseAngle;
558
    win->ps.noBackground = nobg;
559
    ELL_3V_COPY(win->ps.bg, bg);
560
    ELL_3V_COPY(win->ps.edgeColor, edgeColor);
561
    if (limnObjectRender(glyph, cam, win)
562
        || (concave
563
            ? limnObjectPSDrawConcave(glyph, cam, emap, win)
564
            : limnObjectPSDraw(glyph, cam, emap, win))) {
565
      airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
566
      fprintf(stderr, "%s: trouble drawing glyphs:\n%s\n", me, err);
567
      airMopError(mop); return 1;
568
    }
569
  }
570
571
  airMopOkay(mop);
572
  return 0;
573
1
}
574
TEND_CMD(glyph, INFO);