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 #include "ten.h"
25 #include "privateTen.h"
26 
27 tenGlyphParm *
tenGlyphParmNew()28 tenGlyphParmNew() {
29   tenGlyphParm *parm;
30 
31   parm = (tenGlyphParm *)calloc(1, sizeof(tenGlyphParm));
32   if (parm) {
33     parm->verbose = 0;
34     parm->nmask = NULL;
35     parm->anisoType = tenAnisoUnknown;
36     parm->onlyPositive = AIR_TRUE;
37     parm->confThresh = AIR_NAN;
38     parm->anisoThresh = AIR_NAN;
39     parm->maskThresh = AIR_NAN;
40 
41     parm->glyphType = tenGlyphTypeUnknown;
42     parm->facetRes = 10;
43     parm->glyphScale = 1.0;
44     parm->sqdSharp = 3.0;
45     ELL_5V_SET(parm->edgeWidth, 0.0f, 0.0f, 0.4f, 0.2f, 0.1f);
46 
47     parm->colEvec = 0;  /* first */
48     parm->colMaxSat = 1;
49     parm->colGamma = 1;
50     parm->colIsoGray = 1;
51     parm->colAnisoType = tenAnisoUnknown;
52     parm->colAnisoModulate = 0;
53     ELL_4V_SET(parm->ADSP, 0, 1, 0, 30);
54 
55     parm->doSlice = AIR_FALSE;
56     parm->sliceAxis = 0;
57     parm->slicePos = 0;
58     parm->sliceAnisoType = tenAnisoUnknown;
59     parm->sliceOffset = 0.0;
60     parm->sliceBias = 0.05f;
61     parm->sliceGamma = 1.0;
62   }
63   return parm;
64 }
65 
66 tenGlyphParm *
tenGlyphParmNix(tenGlyphParm * parm)67 tenGlyphParmNix(tenGlyphParm *parm) {
68 
69   airFree(parm);
70   return NULL;
71 }
72 
73 int
tenGlyphParmCheck(tenGlyphParm * parm,const Nrrd * nten,const Nrrd * npos,const Nrrd * nslc)74 tenGlyphParmCheck(tenGlyphParm *parm,
75                   const Nrrd *nten, const Nrrd *npos, const Nrrd *nslc) {
76   static const char me[]="tenGlyphParmCheck";
77   int duh;
78   size_t tenSize[3];
79   char stmp[5][AIR_STRLEN_SMALL];
80 
81   if (!(parm && nten)) {
82     biffAddf(TEN, "%s: got NULL pointer", me);
83     return 1;
84   }
85   if (airEnumValCheck(tenAniso, parm->anisoType)) {
86     biffAddf(TEN, "%s: unset (or invalid) anisoType (%d)",
87              me, parm->anisoType);
88     return 1;
89   }
90   if (airEnumValCheck(tenAniso, parm->colAnisoType)) {
91     biffAddf(TEN, "%s: unset (or invalid) colAnisoType (%d)",
92              me, parm->colAnisoType);
93     return 1;
94   }
95   if (!( parm->facetRes >= 3 )) {
96     biffAddf(TEN, "%s: facet resolution %d not >= 3", me, parm->facetRes);
97     return 1;
98   }
99   if (!( AIR_IN_OP(tenGlyphTypeUnknown, parm->glyphType,
100                    tenGlyphTypeLast) )) {
101     biffAddf(TEN, "%s: unset (or invalid) glyphType (%d)",
102              me, parm->glyphType);
103     return 1;
104   }
105   if (!( parm->glyphScale > 0)) {
106     biffAddf(TEN, "%s: glyphScale must be > 0 (not %g)", me, parm->glyphScale);
107     return 1;
108   }
109   if (parm->nmask) {
110     if (npos) {
111       biffAddf(TEN, "%s: can't do masking with explicit coordinate list", me);
112       return 1;
113     }
114     if (!( 3 == parm->nmask->dim
115            && parm->nmask->axis[0].size == nten->axis[1].size
116            && parm->nmask->axis[1].size == nten->axis[2].size
117            && parm->nmask->axis[2].size == nten->axis[3].size )) {
118       biffAddf(TEN, "%s: mask isn't 3-D or doesn't have sizes (%s,%s,%s)", me,
119                airSprintSize_t(stmp[0], nten->axis[1].size),
120                airSprintSize_t(stmp[1], nten->axis[2].size),
121                airSprintSize_t(stmp[2], nten->axis[3].size));
122       return 1;
123     }
124     if (!(AIR_EXISTS(parm->maskThresh))) {
125       biffAddf(TEN, "%s: maskThresh hasn't been set", me);
126       return 1;
127     }
128   }
129   if (!( AIR_EXISTS(parm->anisoThresh)
130          && AIR_EXISTS(parm->confThresh) )) {
131     biffAddf(TEN, "%s: anisoThresh and confThresh haven't both been set", me);
132     return 1;
133   }
134   if (parm->doSlice) {
135     if (npos) {
136       biffAddf(TEN, "%s: can't do slice with explicit coordinate list", me);
137       return 1;
138     }
139     if (!( parm->sliceAxis <=2 )) {
140       biffAddf(TEN, "%s: slice axis %d invalid", me, parm->sliceAxis);
141       return 1;
142     }
143     if (!( parm->slicePos < nten->axis[1+parm->sliceAxis].size )) {
144       biffAddf(TEN, "%s: slice pos %s not in valid range [0..%s]", me,
145                airSprintSize_t(stmp[0], parm->slicePos),
146                airSprintSize_t(stmp[1], nten->axis[1+parm->sliceAxis].size-1));
147       return 1;
148     }
149     if (nslc) {
150       if (2 != nslc->dim) {
151         biffAddf(TEN, "%s: explicit slice must be 2-D (not %d)",
152                  me, nslc->dim);
153         return 1;
154       }
155       tenSize[0] = nten->axis[1].size;
156       tenSize[1] = nten->axis[2].size;
157       tenSize[2] = nten->axis[3].size;
158       for (duh=parm->sliceAxis; duh<2; duh++) {
159         tenSize[duh] = tenSize[duh+1];
160       }
161       if (!( tenSize[0] == nslc->axis[0].size
162              && tenSize[1] == nslc->axis[1].size )) {
163         biffAddf(TEN, "%s: axis %u slice of %sx%sx%s volume != %sx%s", me,
164                  parm->sliceAxis,
165                  airSprintSize_t(stmp[0], nten->axis[1].size),
166                  airSprintSize_t(stmp[1], nten->axis[2].size),
167                  airSprintSize_t(stmp[2], nten->axis[3].size),
168                  airSprintSize_t(stmp[3], nslc->axis[0].size),
169                  airSprintSize_t(stmp[4], nslc->axis[1].size));
170         return 1;
171       }
172     } else {
173       if (airEnumValCheck(tenAniso, parm->sliceAnisoType)) {
174         biffAddf(TEN, "%s: unset (or invalid) sliceAnisoType (%d)",
175                  me, parm->sliceAnisoType);
176         return 1;
177       }
178     }
179   }
180   return 0;
181 }
182 
183 int
tenGlyphGen(limnObject * glyphsLimn,echoScene * glyphsEcho,tenGlyphParm * parm,const Nrrd * nten,const Nrrd * npos,const Nrrd * nslc)184 tenGlyphGen(limnObject *glyphsLimn, echoScene *glyphsEcho,
185             tenGlyphParm *parm,
186             const Nrrd *nten, const Nrrd *npos, const Nrrd *nslc) {
187   static const char me[]="tenGlyphGen";
188   gageShape *shape;
189   airArray *mop;
190   float *tdata, eval[3], evec[9], *cvec, rotEvec[9], mA_f[16],
191     absEval[3], glyphScl[3];
192   double pI[3], pW[3], cl, cp, sRot[16], mA[16], mB[16], msFr[9], tmpvec[3],
193     R, G, B, qA, qB, qC, glyphAniso, sliceGray;
194   unsigned int duh;
195   int slcCoord[3], idx, glyphIdx, axis, numGlyphs,
196     svRGBAfl=AIR_FALSE;
197   limnLook *look; int lookIdx;
198   echoObject *eglyph, *inst, *list=NULL, *split, *esquare;
199   echoPos_t eM[16], originOffset[3], edge0[3], edge1[3];
200   char stmp[AIR_STRLEN_SMALL];
201   /*
202   int eret;
203   double tmp1[3], tmp2[3];
204   */
205 
206   if (!( (glyphsLimn || glyphsEcho) && nten && parm)) {
207     biffAddf(TEN, "%s: got NULL pointer", me);
208     return 1;
209   }
210   mop = airMopNew();
211   shape = gageShapeNew();
212   shape->defaultCenter = nrrdCenterCell;
213   airMopAdd(mop, shape, (airMopper)gageShapeNix, airMopAlways);
214   if (npos) {
215     if (!( 2 == nten->dim && 7 == nten->axis[0].size )) {
216       biffAddf(TEN, "%s: nten isn't 2-D 7-by-N array", me);
217       airMopError(mop); return 1;
218     }
219     if (!( 2 == npos->dim && 3 == npos->axis[0].size
220            && nten->axis[1].size == npos->axis[1].size )) {
221       biffAddf(TEN, "%s: npos isn't 2-D 3-by-%s array", me,
222                airSprintSize_t(stmp, nten->axis[1].size));
223       airMopError(mop); return 1;
224     }
225     if (!( nrrdTypeFloat == nten->type && nrrdTypeFloat == npos->type )) {
226       biffAddf(TEN, "%s: nten and npos must be %s, not %s and %s", me,
227                airEnumStr(nrrdType, nrrdTypeFloat),
228                airEnumStr(nrrdType, nten->type),
229                airEnumStr(nrrdType, npos->type));
230       airMopError(mop); return 1;
231     }
232   } else {
233     if (tenTensorCheck(nten, nrrdTypeFloat, AIR_TRUE, AIR_TRUE)) {
234       biffAddf(TEN, "%s: didn't get a valid DT volume", me);
235       airMopError(mop); return 1;
236     }
237   }
238   if (tenGlyphParmCheck(parm, nten, npos, nslc)) {
239     biffAddf(TEN, "%s: trouble", me);
240     airMopError(mop); return 1;
241   }
242   if (!npos) {
243     if (gageShapeSet(shape, nten, tenGageKind->baseDim)) {
244       biffMovef(TEN, GAGE, "%s: trouble", me);
245       airMopError(mop); return 1;
246     }
247   }
248   if (parm->doSlice) {
249     ELL_3V_COPY(edge0, shape->spacing);
250     ELL_3V_COPY(edge1, shape->spacing);
251     edge0[parm->sliceAxis] = edge1[parm->sliceAxis] = 0.0;
252     switch(parm->sliceAxis) {
253     case 0:
254       edge0[1] = edge1[2] = 0;
255       ELL_4M_ROTATE_Y_SET(sRot, AIR_PI/2);
256       break;
257     case 1:
258       edge0[0] = edge1[2] = 0;
259       ELL_4M_ROTATE_X_SET(sRot, AIR_PI/2);
260       break;
261     case 2: default:
262       edge0[0] = edge1[1] = 0;
263       ELL_4M_IDENTITY_SET(sRot);
264       break;
265     }
266     ELL_3V_COPY(originOffset, shape->spacing);
267     ELL_3V_SCALE(originOffset, -0.5, originOffset);
268     originOffset[parm->sliceAxis] *= -2*parm->sliceOffset;
269   }
270   if (glyphsLimn) {
271     /* create limnLooks for diffuse and ambient-only shading */
272     /* ??? */
273     /* hack: save old value of setVertexRGBAFromLook, and set to true */
274     svRGBAfl = glyphsLimn->setVertexRGBAFromLook;
275     glyphsLimn->setVertexRGBAFromLook = AIR_TRUE;
276   }
277   if (glyphsEcho) {
278     list = echoObjectNew(glyphsEcho, echoTypeList);
279   }
280   if (npos) {
281     numGlyphs = AIR_UINT(nten->axis[1].size);
282   } else {
283     numGlyphs = shape->size[0] * shape->size[1] * shape->size[2];
284   }
285   /* find measurement frame transform */
286   if (3 == nten->spaceDim
287       && AIR_EXISTS(nten->measurementFrame[0][0])) {
288     /*     msFr        nten->measurementFrame
289     **   0  1  2      [0][0]   [1][0]   [2][0]
290     **   3  4  5      [0][1]   [1][1]   [2][1]
291     **   6  7  8      [0][2]   [1][2]   [2][2]
292     */
293     msFr[0] = nten->measurementFrame[0][0];
294     msFr[3] = nten->measurementFrame[0][1];
295     msFr[6] = nten->measurementFrame[0][2];
296     msFr[1] = nten->measurementFrame[1][0];
297     msFr[4] = nten->measurementFrame[1][1];
298     msFr[7] = nten->measurementFrame[1][2];
299     msFr[2] = nten->measurementFrame[2][0];
300     msFr[5] = nten->measurementFrame[2][1];
301     msFr[8] = nten->measurementFrame[2][2];
302   } else {
303     ELL_3M_IDENTITY_SET(msFr);
304   }
305   for (idx=0; idx<numGlyphs; idx++) {
306     tdata = (float*)(nten->data) + 7*idx;
307     if (parm->verbose >= 2) {
308       fprintf(stderr, "%s: glyph %d/%d: hello %g    %g %g %g %g %g %g\n",
309               me, idx, numGlyphs, tdata[0],
310               tdata[1], tdata[2], tdata[3],
311               tdata[4], tdata[5], tdata[6]);
312     }
313     if (!( TEN_T_EXISTS(tdata) )) {
314       /* there's nothing we can do here */
315       if (parm->verbose >= 2) {
316         fprintf(stderr, "%s: glyph %d/%d: non-existent data\n",
317                 me, idx, numGlyphs);
318       }
319       continue;
320     }
321     if (npos) {
322       ELL_3V_COPY(pW, (float*)(npos->data) + 3*idx);
323       if (!( AIR_EXISTS(pW[0]) && AIR_EXISTS(pW[1]) && AIR_EXISTS(pW[2]) )) {
324         /* position doesn't exist- perhaps because its from the push
325            library, which might kill points by setting coords to nan */
326         continue;
327       }
328     } else {
329       NRRD_COORD_GEN(pI, shape->size, 3, idx);
330       /* this does take into account full orientation */
331       gageShapeItoW(shape, pW, pI);
332       if (parm->nmask) {
333         if (!( nrrdFLookup[parm->nmask->type](parm->nmask->data, idx)
334                >= parm->maskThresh )) {
335           if (parm->verbose >= 2) {
336             fprintf(stderr, "%s: glyph %d/%d: doesn't meet mask thresh\n",
337                     me, idx, numGlyphs);
338           }
339           continue;
340         }
341       }
342     }
343     tenEigensolve_f(eval, evec, tdata);
344     /* transform eigenvectors by measurement frame */
345     ELL_3MV_MUL(tmpvec, msFr, evec + 0);
346     ELL_3V_COPY_TT(evec + 0, float, tmpvec);
347     ELL_3MV_MUL(tmpvec, msFr, evec + 3);
348     ELL_3V_COPY_TT(evec + 3, float, tmpvec);
349     ELL_3MV_MUL(tmpvec, msFr, evec + 6);
350     ELL_3V_COPY_TT(evec + 6, float, tmpvec);
351     ELL_3V_CROSS(tmpvec, evec + 0, evec + 3);
352     if (0 > ELL_3V_DOT(tmpvec, evec + 6)) {
353       ELL_3V_SCALE(evec + 6, -1, evec + 6);
354     }
355     ELL_3M_TRANSPOSE(rotEvec, evec);
356     if (parm->doSlice
357         && pI[parm->sliceAxis] == parm->slicePos) {
358       /* set sliceGray */
359       if (nslc) {
360         /* we aren't masked by confidence, as anisotropy slice is */
361         for (duh=0; duh<parm->sliceAxis; duh++) {
362           slcCoord[duh] = (int)(pI[duh]);
363         }
364         for (duh=duh<parm->sliceAxis; duh<2; duh++) {
365           slcCoord[duh] = (int)(pI[duh+1]);
366         }
367         /* HEY: GLK has no idea what's going here */
368         slcCoord[0] = (int)(pI[0]);
369         slcCoord[1] = (int)(pI[1]);
370         slcCoord[2] = (int)(pI[2]);
371         sliceGray =
372           nrrdFLookup[nslc->type](nslc->data, slcCoord[0]
373                                   + nslc->axis[0].size*slcCoord[1]);
374       } else {
375         if (!( tdata[0] >= parm->confThresh )) {
376           if (parm->verbose >= 2) {
377             fprintf(stderr, "%s: glyph %d/%d (slice): conf %g < thresh %g\n",
378                     me, idx, numGlyphs, tdata[0], parm->confThresh);
379           }
380           continue;
381         }
382         sliceGray = tenAnisoEval_f(eval, parm->sliceAnisoType);
383       }
384       if (parm->sliceGamma > 0) {
385         sliceGray = AIR_AFFINE(0, sliceGray, 1, parm->sliceBias, 1);
386         sliceGray = pow(sliceGray, 1.0/parm->sliceGamma);
387       } else {
388         sliceGray = AIR_AFFINE(0, sliceGray, 1, 0, 1-parm->sliceBias);
389         sliceGray = 1.0 - pow(sliceGray, -1.0/parm->sliceGamma);
390       }
391       /* make slice contribution */
392       /* HEY: this is *NOT* aware of shape->fromOrientation */
393       if (glyphsLimn) {
394         lookIdx = limnObjectLookAdd(glyphsLimn);
395         look = glyphsLimn->look + lookIdx;
396         ELL_4V_SET_TT(look->rgba, float, sliceGray, sliceGray, sliceGray, 1);
397         ELL_3V_SET(look->kads, 1, 0, 0);
398         look->spow = 0;
399         glyphIdx = limnObjectSquareAdd(glyphsLimn, lookIdx);
400         ELL_4M_IDENTITY_SET(mA);
401         ell_4m_post_mul_d(mA, sRot);
402         if (!npos) {
403           ELL_4M_SCALE_SET(mB,
404                            shape->spacing[0],
405                            shape->spacing[1],
406                            shape->spacing[2]);
407         }
408         ell_4m_post_mul_d(mA, mB);
409         ELL_4M_TRANSLATE_SET(mB, pW[0], pW[1], pW[2]);
410         ell_4m_post_mul_d(mA, mB);
411         ELL_4M_TRANSLATE_SET(mB,
412                              originOffset[0],
413                              originOffset[1],
414                              originOffset[2]);
415         ell_4m_post_mul_d(mA, mB);
416         ELL_4M_COPY_TT(mA_f, float, mA);
417         limnObjectPartTransform(glyphsLimn, glyphIdx, mA_f);
418       }
419       if (glyphsEcho) {
420         esquare = echoObjectNew(glyphsEcho,echoTypeRectangle);
421         ELL_3V_ADD2(((echoRectangle*)esquare)->origin, pW, originOffset);
422         ELL_3V_COPY(((echoRectangle*)esquare)->edge0, edge0);
423         ELL_3V_COPY(((echoRectangle*)esquare)->edge1, edge1);
424         echoColorSet(esquare,
425                      AIR_CAST(echoCol_t, sliceGray),
426                      AIR_CAST(echoCol_t, sliceGray),
427                      AIR_CAST(echoCol_t, sliceGray), 1);
428         /* this is pretty arbitrary- but I want shadows to have some effect.
429            Previously, the material was all ambient: (A,D,S) = (1,0,0),
430            which avoided all shadow effects. */
431         echoMatterPhongSet(glyphsEcho, esquare, 0.4f, 0.6f, 0, 40);
432         echoListAdd(list, esquare);
433       }
434     }
435     if (parm->onlyPositive) {
436       if (eval[2] < 0) {
437         /* didn't have all positive eigenvalues, its outta here */
438         if (parm->verbose >= 2) {
439           fprintf(stderr, "%s: glyph %d/%d: not all evals %g %g %g > 0\n",
440                   me, idx, numGlyphs, eval[0], eval[1], eval[2]);
441         }
442         continue;
443       }
444     }
445     if (!( tdata[0] >= parm->confThresh )) {
446       if (parm->verbose >= 2) {
447         fprintf(stderr, "%s: glyph %d/%d: conf %g < thresh %g\n",
448                 me, idx, numGlyphs, tdata[0], parm->confThresh);
449       }
450       continue;
451     }
452     if (!( tenAnisoEval_f(eval, parm->anisoType) >= parm->anisoThresh )) {
453       if (parm->verbose >= 2) {
454         fprintf(stderr, "%s: glyph %d/%d: aniso[%d] %g < thresh %g\n",
455                 me, idx, numGlyphs, parm->anisoType,
456                 tenAnisoEval_f(eval, parm->anisoType), parm->anisoThresh);
457       }
458       continue;
459     }
460     glyphAniso = tenAnisoEval_f(eval, parm->colAnisoType);
461     /*
462       fprintf(stderr, "%s: eret = %d; evals = %g %g %g\n", me,
463       eret, eval[0], eval[1], eval[2]);
464       ELL_3V_CROSS(tmp1, evec+0, evec+3); tmp2[0] = ELL_3V_LEN(tmp1);
465       ELL_3V_CROSS(tmp1, evec+0, evec+6); tmp2[1] = ELL_3V_LEN(tmp1);
466       ELL_3V_CROSS(tmp1, evec+3, evec+6); tmp2[2] = ELL_3V_LEN(tmp1);
467       fprintf(stderr, "%s: crosses = %g %g %g\n", me,
468       tmp2[0], tmp2[1], tmp2[2]);
469     */
470 
471     /* set transform (in mA) */
472     ELL_3V_ABS(absEval, eval);
473     ELL_4M_IDENTITY_SET(mA);                        /* reset */
474     ELL_3V_SCALE(glyphScl, parm->glyphScale, absEval); /* scale by evals */
475     ELL_4M_SCALE_SET(mB, glyphScl[0], glyphScl[1], glyphScl[2]);
476 
477     ell_4m_post_mul_d(mA, mB);
478     ELL_43M_INSET(mB, rotEvec);                     /* rotate by evecs */
479     ell_4m_post_mul_d(mA, mB);
480     ELL_4M_TRANSLATE_SET(mB, pW[0], pW[1], pW[2]);  /* translate */
481     ell_4m_post_mul_d(mA, mB);
482 
483     /* set color (in R,G,B) */
484     cvec = evec + 3*(AIR_CLAMP(0, parm->colEvec, 2));
485     R = AIR_ABS(cvec[0]);                           /* standard mapping */
486     G = AIR_ABS(cvec[1]);
487     B = AIR_ABS(cvec[2]);
488     /* desaturate by colMaxSat */
489     R = AIR_AFFINE(0.0, parm->colMaxSat, 1.0, parm->colIsoGray, R);
490     G = AIR_AFFINE(0.0, parm->colMaxSat, 1.0, parm->colIsoGray, G);
491     B = AIR_AFFINE(0.0, parm->colMaxSat, 1.0, parm->colIsoGray, B);
492     /* desaturate some by anisotropy */
493     R = AIR_AFFINE(0.0, parm->colAnisoModulate, 1.0,
494                    R, AIR_AFFINE(0.0, glyphAniso, 1.0, parm->colIsoGray, R));
495     G = AIR_AFFINE(0.0, parm->colAnisoModulate, 1.0,
496                    G, AIR_AFFINE(0.0, glyphAniso, 1.0, parm->colIsoGray, G));
497     B = AIR_AFFINE(0.0, parm->colAnisoModulate, 1.0,
498                    B, AIR_AFFINE(0.0, glyphAniso, 1.0, parm->colIsoGray, B));
499     /* clamp and do gamma */
500     R = AIR_CLAMP(0.0, R, 1.0);
501     G = AIR_CLAMP(0.0, G, 1.0);
502     B = AIR_CLAMP(0.0, B, 1.0);
503     R = pow(R, parm->colGamma);
504     G = pow(G, parm->colGamma);
505     B = pow(B, parm->colGamma);
506 
507     /* find axis, and superquad exponents qA and qB */
508     if (eval[2] > 0) {
509       /* all evals positive */
510       cl = AIR_MIN(0.99, tenAnisoEval_f(eval, tenAniso_Cl1));
511       cp = AIR_MIN(0.99, tenAnisoEval_f(eval, tenAniso_Cp1));
512       if (cl > cp) {
513         axis = 0;
514         qA = pow(1-cp, parm->sqdSharp);
515         qB = pow(1-cl, parm->sqdSharp);
516       } else {
517         axis = 2;
518         qA = pow(1-cl, parm->sqdSharp);
519         qB = pow(1-cp, parm->sqdSharp);
520       }
521       qC = qB;
522     } else if (eval[0] < 0) {
523       /* all evals negative */
524       float aef[3];
525       aef[0] = absEval[2];
526       aef[1] = absEval[1];
527       aef[2] = absEval[0];
528       cl = AIR_MIN(0.99, tenAnisoEval_f(aef, tenAniso_Cl1));
529       cp = AIR_MIN(0.99, tenAnisoEval_f(aef, tenAniso_Cp1));
530       if (cl > cp) {
531         axis = 2;
532         qA = pow(1-cp, parm->sqdSharp);
533         qB = pow(1-cl, parm->sqdSharp);
534       } else {
535         axis = 0;
536         qA = pow(1-cl, parm->sqdSharp);
537         qB = pow(1-cp, parm->sqdSharp);
538       }
539       qC = qB;
540     } else {
541 #define OOSQRT2 0.70710678118654752440
542 #define OOSQRT3 0.57735026918962576451
543       /* double poleA[3]={OOSQRT3, OOSQRT3, OOSQRT3}; */
544       double poleB[3]={1, 0, 0};
545       double poleC[3]={OOSQRT2, OOSQRT2, 0};
546       double poleD[3]={OOSQRT3, -OOSQRT3, -OOSQRT3};
547       double poleE[3]={OOSQRT2, 0, -OOSQRT2};
548       double poleF[3]={OOSQRT3, OOSQRT3, -OOSQRT3};
549       double poleG[3]={0, -OOSQRT2, -OOSQRT2};
550       double poleH[3]={0, 0, -1};
551       /* double poleI[3]={-OOSQRT3, -OOSQRT3, -OOSQRT3}; */
552       double funk[3]={0,4,2}, thrn[3]={1,4,4};
553       double octa[3]={0,2,2}, cone[3]={1,2,2};
554       double evalN[3], tmp, bary[3];
555       double qq[3];
556 
557       ELL_3V_NORM(evalN, eval, tmp);
558       if (eval[1] >= -eval[2]) {
559         /* inside B-F-C */
560         ell_3v_barycentric_spherical_d(bary, poleB, poleF, poleC, evalN);
561         ELL_3V_SCALE_ADD3(qq, bary[0], octa, bary[1], thrn, bary[2], cone);
562         axis = 2;
563       } else if (eval[0] >= -eval[2]) {
564         /* inside B-D-F */
565         if (eval[1] >= 0) {
566           /* inside B-E-F */
567           ell_3v_barycentric_spherical_d(bary, poleB, poleE, poleF, evalN);
568           ELL_3V_SCALE_ADD3(qq, bary[0], octa, bary[1], funk, bary[2], thrn);
569           axis = 2;
570         } else {
571           /* inside B-D-E */
572           ell_3v_barycentric_spherical_d(bary, poleB, poleD, poleE, evalN);
573           ELL_3V_SCALE_ADD3(qq, bary[0], cone, bary[1], thrn, bary[2], funk);
574           axis = 0;
575         }
576       } else if (eval[0] < -eval[1]) {
577         /* inside D-G-H */
578         ell_3v_barycentric_spherical_d(bary, poleD, poleG, poleH, evalN);
579         ELL_3V_SCALE_ADD3(qq, bary[0], thrn, bary[1], cone, bary[2], octa);
580         axis = 0;
581       } else if (eval[1] < 0) {
582         /* inside E-D-H */
583         ell_3v_barycentric_spherical_d(bary, poleE, poleD, poleH, evalN);
584         ELL_3V_SCALE_ADD3(qq, bary[0], funk, bary[1], thrn, bary[2], octa);
585         axis = 0;
586       } else {
587         /* inside F-E-H */
588         ell_3v_barycentric_spherical_d(bary, poleF, poleE, poleH, evalN);
589         ELL_3V_SCALE_ADD3(qq, bary[0], thrn, bary[1], funk, bary[2], cone);
590         axis = 2;
591       }
592       qA = qq[0];
593       qB = qq[1];
594       qC = qq[2];
595 #undef OOSQRT2
596 #undef OOSQRT3
597     }
598 
599     /* add the glyph */
600     if (parm->verbose >= 2) {
601       fprintf(stderr, "%s: glyph %d/%d: the glyph stays!\n",
602               me, idx, numGlyphs);
603     }
604     if (glyphsLimn) {
605       lookIdx = limnObjectLookAdd(glyphsLimn);
606       look = glyphsLimn->look + lookIdx;
607       ELL_4V_SET_TT(look->rgba, float, R, G, B, 1);
608       ELL_3V_SET(look->kads, parm->ADSP[0], parm->ADSP[1], parm->ADSP[2]);
609       look->spow = 0;
610       switch(parm->glyphType) {
611       case tenGlyphTypeBox:
612         glyphIdx = limnObjectCubeAdd(glyphsLimn, lookIdx);
613         break;
614       case tenGlyphTypeSphere:
615         glyphIdx = limnObjectPolarSphereAdd(glyphsLimn, lookIdx, axis,
616                                             2*parm->facetRes, parm->facetRes);
617         break;
618       case tenGlyphTypeCylinder:
619         glyphIdx = limnObjectCylinderAdd(glyphsLimn, lookIdx, axis,
620                                          parm->facetRes);
621         break;
622       case tenGlyphTypeSuperquad:
623       default:
624         glyphIdx =
625           limnObjectPolarSuperquadFancyAdd(glyphsLimn, lookIdx, axis,
626                                            AIR_CAST(float, qA),
627                                            AIR_CAST(float, qB),
628                                            AIR_CAST(float, qC), 0,
629                                            2*parm->facetRes,
630                                            parm->facetRes);
631         break;
632       }
633       ELL_4M_COPY_TT(mA_f, float, mA);
634       limnObjectPartTransform(glyphsLimn, glyphIdx, mA_f);
635     }
636     if (glyphsEcho) {
637       switch(parm->glyphType) {
638       case tenGlyphTypeBox:
639         eglyph = echoObjectNew(glyphsEcho, echoTypeCube);
640         /* nothing else to set */
641         break;
642       case tenGlyphTypeSphere:
643         eglyph = echoObjectNew(glyphsEcho, echoTypeSphere);
644         echoSphereSet(eglyph, 0, 0, 0, 1);
645         break;
646       case tenGlyphTypeCylinder:
647         eglyph = echoObjectNew(glyphsEcho, echoTypeCylinder);
648         echoCylinderSet(eglyph, axis);
649         break;
650       case tenGlyphTypeSuperquad:
651       default:
652         eglyph = echoObjectNew(glyphsEcho, echoTypeSuperquad);
653         echoSuperquadSet(eglyph, axis, qA, qB);
654         break;
655       }
656       echoColorSet(eglyph,
657                    AIR_CAST(echoCol_t, R),
658                    AIR_CAST(echoCol_t, G),
659                    AIR_CAST(echoCol_t, B), 1);
660       echoMatterPhongSet(glyphsEcho, eglyph,
661                          parm->ADSP[0], parm->ADSP[1],
662                          parm->ADSP[2], parm->ADSP[3]);
663       inst = echoObjectNew(glyphsEcho, echoTypeInstance);
664       ELL_4M_COPY(eM, mA);
665       echoInstanceSet(inst, eM, eglyph);
666       echoListAdd(list, inst);
667     }
668   }
669   if (glyphsLimn) {
670     glyphsLimn->setVertexRGBAFromLook = svRGBAfl;
671   }
672   if (glyphsEcho) {
673     split = echoListSplit3(glyphsEcho, list, 10);
674     echoObjectAdd(glyphsEcho, split);
675   }
676 
677   airMopOkay(mop);
678   return 0;
679 }
680 
681 /*
682 ** Zone from Eval
683 */
684 unsigned int
tenGlyphBqdZoneEval(const double eval[3])685 tenGlyphBqdZoneEval(const double eval[3]) {
686   double x, y, z;
687   unsigned int zone;
688 
689   x = eval[0];
690   y = eval[1];
691   z = eval[2];
692   if (y > 0) {   /* 0 1 2 3 4 */
693     if (z > 0) { /* 0 1 */
694       if (x - y > y - z) {
695         zone = 0;
696       } else {
697         zone = 1;
698       }
699     } else {     /* 2 3 4 */
700       if (y > -z) {
701         zone = 2;
702       } else if (x > -z) {
703         zone = 3;
704       } else {
705         zone = 4;
706       }
707     }
708   } else {       /* 5 6 7 8 9 */
709     if (x > 0) { /* 5 6 7 */
710       if (x > -z) {
711         zone = 5;
712       } else if (x > -y) {
713         zone = 6;
714       } else {
715         zone = 7;
716       }
717     } else {     /* 8 9 */
718       if (x - y > y - z) {
719         zone = 8;
720       } else {
721         zone = 9;
722       }
723     }
724   }
725   return zone;
726 }
727 
728 /*
729 ** UV from Eval
730 */
731 void
tenGlyphBqdUvEval(double uv[2],const double eval[3])732 tenGlyphBqdUvEval(double uv[2], const double eval[3]) {
733   double xx, yy, zz, ax, ay, az, mm;
734 
735   ax = AIR_ABS(eval[0]);
736   ay = AIR_ABS(eval[1]);
737   az = AIR_ABS(eval[2]);
738   mm = AIR_MAX(ax, AIR_MAX(ay, az));
739   if (mm==0) { /* do not divide */
740     uv[0]=uv[1]=0;
741     return;
742   }
743   xx = eval[0]/mm;
744   yy = eval[1]/mm;
745   zz = eval[2]/mm;
746   uv[0] = AIR_AFFINE(-1, yy, 1, 0, 1);
747   if (xx > -zz) {
748     uv[1] = AIR_AFFINE(-1, zz, 1, 0, 1) - uv[0] + 1;
749   } else {
750     uv[1] = AIR_AFFINE(-1, xx, 1, -1, 0) - uv[0] + 1;
751   }
752   return;
753 }
754 
755 /*
756 ** Eval from UV
757 */
758 void
tenGlyphBqdEvalUv(double eval[3],const double uv[2])759 tenGlyphBqdEvalUv(double eval[3], const double uv[2]) {
760   double xx, yy, zz, ll;
761 
762   yy = AIR_AFFINE(0, uv[0], 1, -1, 1);
763   if (uv[0] + uv[1] > 1) {
764     zz = AIR_AFFINE(0, uv[1], 1, -1, 1) - 1 + yy;
765     xx = 1;
766   } else {
767     xx = AIR_AFFINE(0, uv[1], 1, -1, 1) + yy + 1;
768     zz = -1;
769   }
770   ELL_3V_SET(eval, xx, yy, zz);
771   ELL_3V_NORM(eval, eval, ll);
772   return;
773 }
774 
775 /*
776 ** Zone from UV
777 */
778 unsigned int
tenGlyphBqdZoneUv(const double uv[2])779 tenGlyphBqdZoneUv(const double uv[2]) {
780   /* the use of "volatile" here, as well as additional variables for
781      expressions involving u and v, is based on browsing this summary of the
782      subtleties of IEEE 754: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323
783      In this function, "if (u + v > 0.5)" returned one thing for cygwin, and
784      something else for other platforms.  Adding volatile and more variables
785      for expressions brings cygwin back into line with the other platforms */
786   volatile double u, v, upv, tupv;
787   unsigned int zone;
788 
789   u = uv[0];
790   v = uv[1];
791   upv = u + v;
792   tupv = 2*u + v;
793   if (u > 0.5) {       /* 0 1 2 3 4 */
794     if (upv > 1.5) { /* 0 1 */
795       if (u < v) {
796         zone = 0;
797       } else {
798         zone = 1;
799       }
800     } else {           /* 2 3 4 */
801       if (tupv > 2) {
802         zone = 2;
803       } else if (upv > 1) {
804         zone = 3;
805       } else {
806         zone = 4;
807       }
808     }
809   } else {             /* 5 6 7 8 9 */
810     if (upv > 0.5) { /* 5 6 7 */
811       if (upv > 1) {
812         zone = 5;
813       } else if (tupv > 1) {
814         zone = 6;
815       } else {
816         zone = 7;
817       }
818     } else {           /* 8 9 */
819       if (u < v) {
820         zone = 8;
821       } else {
822         zone = 9;
823       }
824     }
825   }
826   return zone;
827 }
828 
829 static void
baryFind(double bcoord[3],const double uvp[2],const double uv0[2],const double uv1[2],const double uv2[2])830 baryFind(double bcoord[3], const double uvp[2],
831          const double uv0[2],
832          const double uv1[2],
833          const double uv2[2]) {
834   double mat[9], a, a01, a02, a12;
835 
836   ELL_3M_SET(mat,
837              uv0[0], uv0[1], 1,
838              uv1[0], uv1[1], 1,
839              uvp[0], uvp[1], 1);
840   a01 = ELL_3M_DET(mat); a01 = AIR_ABS(a01);
841 
842   ELL_3M_SET(mat,
843              uv0[0], uv0[1], 1,
844              uv2[0], uv2[1], 1,
845              uvp[0], uvp[1], 1);
846   a02 = ELL_3M_DET(mat); a02 = AIR_ABS(a02);
847 
848   ELL_3M_SET(mat,
849              uv1[0], uv1[1], 1,
850              uv2[0], uv2[1], 1,
851              uvp[0], uvp[1], 1);
852   a12 = ELL_3M_DET(mat); a12 = AIR_ABS(a12);
853 
854   a = a01 + a02 + a12;
855   ELL_3V_SET(bcoord, a12/a, a02/a, a01/a);
856   return;
857 }
858 
859 static void
baryBlend(double abc[3],const double co[3],const double abc0[3],const double abc1[3],const double abc2[3])860 baryBlend(double abc[3], const double co[3],
861           const double abc0[3],
862           const double abc1[3],
863           const double abc2[3]) {
864   unsigned int ii;
865 
866   for (ii=0; ii<3; ii++) {
867     abc[ii] = co[0]*abc0[ii] + co[1]*abc1[ii] + co[2]*abc2[ii];
868   }
869   return;
870 }
871 
872 void
tenGlyphBqdAbcUv(double abc[3],const double uv[2],double betaMax)873 tenGlyphBqdAbcUv(double abc[3], const double uv[2], double betaMax) {
874   static const unsigned int vertsZone[10][3] = {{0, 1, 2},   /* 0 */
875                                                 {0, 2, 3},   /* 1 */
876                                                 {1, 3, 4},   /* 2 */
877                                                 {1, 4, 5},   /* 3 */
878                                                 {4, 5, 9},   /* 4 */
879                                                 {1, 5, 6},   /* 5 */
880                                                 {5, 6, 9},   /* 6 */
881                                                 {6, 7, 9},   /* 7 */
882                                                 {7, 8, 10},  /* 8 */
883                                                 {8, 9, 10}}; /* 9 */
884   static const double uvVert[11][2] = {{1.00, 1.00},   /* 0 */
885                                        {0.50, 1.00},   /* 1 */
886                                        {0.75, 0.75},   /* 2 */
887                                        {1.00, 0.50},   /* 3 */
888                                        {1.00, 0.00},   /* 4 */
889                                        {0.50, 0.50},   /* 5 */
890                                        {0.00, 1.00},   /* 6 */
891                                        {0.00, 0.50},   /* 7 */
892                                        {0.25, 0.25},   /* 8 */
893                                        {0.50, 0.00},   /* 9 */
894                                        {0.00, 0.00}};  /* 10 */
895   double abcBall[3], abcCyli[3], abcFunk[3], abcThrn[3],
896     abcOcta[3], abcCone[3], abcHalf[3];
897   /* old compile-time setting
898   const double *abcAll[10][11] = {
899      zone \ vert 0      1        2        3        4        5        6        7        8        9       10
900       0    {abcBall, abcCyli, abcHalf,  NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL   },
901       1    {abcBall,  NULL,   abcHalf, abcCyli,  NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL   },
902       2    { NULL,   abcOcta,  NULL,   abcCone, abcThrn,  NULL,    NULL,    NULL,    NULL,    NULL,    NULL   },
903       3    { NULL,   abcOcta,  NULL,    NULL,   abcThrn, abcFunk,  NULL,    NULL,    NULL,    NULL,    NULL   },
904       4    { NULL,    NULL,    NULL,    NULL,   abcThrn, abcFunk,  NULL,    NULL,    NULL,   abcCone,  NULL   },
905       5    { NULL,   abcCone,  NULL,    NULL,    NULL,   abcFunk, abcThrn,  NULL,    NULL,    NULL,    NULL   },
906       6    { NULL,    NULL,    NULL,    NULL,    NULL,   abcFunk, abcThrn,  NULL,    NULL,   abcOcta,  NULL   },
907       7    { NULL,    NULL,    NULL,    NULL,    NULL,    NULL,   abcThrn, abcCone,  NULL,   abcOcta,  NULL   },
908       8    { NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,   abcCyli, abcHalf,  NULL,   abcBall },
909       9    { NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,   abcHalf, abcCyli, abcBall }};
910   */
911   const double *abcAll[10][11];
912   unsigned int pvi[3], zone, vert;
913   double bcoord[3];
914 
915   ELL_3V_SET(abcBall, 1, 1, 1);
916   ELL_3V_SET(abcCyli, 1, 0, 0);
917   ELL_3V_SET(abcFunk, 0, betaMax, 2);  /* only one with c != b  */
918   ELL_3V_SET(abcThrn, 1, betaMax, 3);
919   ELL_3V_SET(abcOcta, 0, 2, 2);
920   ELL_3V_SET(abcCone, 1, 2, 2);
921   ELL_3V_SET(abcHalf, 0.5, 0.5, 0.5); /* alpha is half-way between alpha of
922                                          octa and cone and beta has to be
923                                          the same as alpha at for the
924                                          seam to be shape-continuous */
925   /* run-time setting of abcAll[][]; compile-time setting (comments above)
926      gives "initializer element is not computable at load time" warnings */
927   for (zone=0; zone<10; zone++) {
928     for (vert=0; vert<11; vert++) {
929       abcAll[zone][vert]=NULL;
930     }
931   }
932 #define SET(zi, vi0, vi1, vi2, sh0, sh1, sh2) \
933   abcAll[zi][vi0] = abc##sh0; \
934   abcAll[zi][vi1] = abc##sh1; \
935   abcAll[zi][vi2] = abc##sh2
936 
937   SET(0, 0, 1, 2, Ball, Cyli, Half);
938   SET(1, 0, 2, 3, Ball, Half, Cyli);
939   SET(2, 1, 3, 4, Octa, Cone, Thrn);
940   SET(3, 1, 4, 5, Octa, Thrn, Funk);
941   SET(4, 4, 5, 9, Thrn, Funk, Cone);
942   SET(5, 1, 5, 6, Cone, Funk, Thrn);
943   SET(6, 5, 6, 9, Funk, Thrn, Octa);
944   SET(7, 6, 7, 9, Thrn, Cone, Octa);
945   SET(8, 7, 8,10, Cyli, Half, Ball);
946   SET(9, 8, 9,10, Half, Cyli, Ball);
947 
948 #undef SET
949 
950   zone = tenGlyphBqdZoneUv(uv);
951   ELL_3V_COPY(pvi, vertsZone[zone]);
952   baryFind(bcoord, uv, uvVert[pvi[0]], uvVert[pvi[1]], uvVert[pvi[2]]);
953   baryBlend(abc, bcoord,
954             abcAll[zone][pvi[0]],
955             abcAll[zone][pvi[1]],
956             abcAll[zone][pvi[2]]);
957   return;
958 }
959 
960