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