GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/seek/extract.c Lines: 0 473 0.0 %
Date: 2017-05-26 Branches: 0 325 0.0 %

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 "seek.h"
25
#include "privateSeek.h"
26
27
static baggage *
28
baggageNew(seekContext *sctx) {
29
  baggage *bag;
30
  unsigned int sx;
31
32
  bag = AIR_CALLOC(1, baggage);
33
34
  /* this is basically the mapping from the 12 edges on each voxel to
35
     the 5 unique edges for each sample on the slab, based on the lay-out
36
     defined in the beginning of tables.c */
37
  sx = AIR_CAST(unsigned int, sctx->sx);
38
  /*                     X      Y */
39
  bag->evti[ 0] = 0 + 5*(0 + sx*0);
40
  bag->evti[ 1] = 1 + 5*(0 + sx*0);
41
  bag->evti[ 2] = 1 + 5*(1 + sx*0);
42
  bag->evti[ 3] = 0 + 5*(0 + sx*1);
43
  bag->evti[ 4] = 2 + 5*(0 + sx*0);
44
  bag->evti[ 5] = 2 + 5*(1 + sx*0);
45
  bag->evti[ 6] = 2 + 5*(0 + sx*1);
46
  bag->evti[ 7] = 2 + 5*(1 + sx*1);
47
  bag->evti[ 8] = 3 + 5*(0 + sx*0);
48
  bag->evti[ 9] = 4 + 5*(0 + sx*0);
49
  bag->evti[10] = 4 + 5*(1 + sx*0);
50
  bag->evti[11] = 3 + 5*(0 + sx*1);
51
52
  switch (sctx->type) {
53
  case seekTypeRidgeSurface:
54
  case seekTypeRidgeSurfaceOP:
55
  case seekTypeRidgeSurfaceT:
56
    bag->esIdx = 2;
57
    bag->modeSign = -1;
58
    break;
59
  case seekTypeValleySurface:
60
  case seekTypeValleySurfaceOP:
61
  case seekTypeValleySurfaceT:
62
    bag->esIdx = 0;
63
    bag->modeSign = +1;
64
    break;
65
  case seekTypeMaximalSurface:
66
    bag->esIdx = 0;
67
    bag->modeSign = -1;
68
    break;
69
  case seekTypeMinimalSurface:
70
    bag->esIdx = 2;
71
    bag->modeSign = +1;
72
    break;
73
  default:
74
    /* biffAddf(SEEK, "%s: feature type %s not handled", me,
75
       airEnumStr(seekType, sctx->type));
76
       return 1;
77
    */
78
    /* without biff, we get as nasty as possible */
79
    bag->esIdx = UINT_MAX;
80
    bag->modeSign = 0;
81
    break;
82
  }
83
84
  if (seekTypeIsocontour == sctx->type) {
85
    if (sctx->ninscl) {
86
      bag->scllup = nrrdDLookup[sctx->ninscl->type];
87
      bag->scldata = sctx->ninscl->data;
88
    } else {
89
      bag->scllup = nrrdDLookup[sctx->nsclDerived->type];
90
      bag->scldata = sctx->nsclDerived->data;
91
    }
92
  } else {
93
    bag->scllup = NULL;
94
    bag->scldata = NULL;
95
  }
96
97
  bag->xyzwArr = NULL;
98
  bag->normArr = NULL;
99
  bag->indxArr = NULL;
100
  return bag;
101
}
102
103
static baggage *
104
baggageNix(baggage *bag) {
105
106
  if (bag) {
107
    airArrayNix(bag->normArr);
108
    airArrayNix(bag->xyzwArr);
109
    airArrayNix(bag->indxArr);
110
111
    airFree(bag);
112
  }
113
  return NULL;
114
}
115
116
static int
117
outputInit(seekContext *sctx, baggage *bag, limnPolyData *lpld) {
118
  static const char me[]="outputInit";
119
  unsigned int estVertNum, estFaceNum, minI, maxI, valI, *spanHist;
120
  airPtrPtrUnion appu;
121
  int E;
122
123
  if (seekTypeIsocontour == sctx->type
124
      && AIR_IN_OP(sctx->range->min, sctx->isovalue, sctx->range->max)) {
125
    unsigned int estVoxNum=0;
126
    /* estimate number of voxels, faces, and vertices involved */
127
    spanHist = AIR_CAST(unsigned int*, sctx->nspanHist->data);
128
    valI = airIndex(sctx->range->min, sctx->isovalue, sctx->range->max,
129
                    sctx->spanSize);
130
    for (minI=0; minI<=valI; minI++) {
131
      for (maxI=valI; maxI<sctx->spanSize; maxI++) {
132
        estVoxNum += spanHist[minI + sctx->spanSize*maxI];
133
      }
134
    }
135
    estVertNum = AIR_CAST(unsigned int, estVoxNum*(sctx->vertsPerVoxel));
136
    estFaceNum = AIR_CAST(unsigned int, estVoxNum*(sctx->facesPerVoxel));
137
    if (sctx->verbose) {
138
      fprintf(stderr, "%s: estimated vox --> vert, face: %u --> %u, %u\n", me,
139
              estVoxNum, estVertNum, estFaceNum);
140
    }
141
  } else {
142
    estVertNum = 0;
143
    estFaceNum = 0;
144
  }
145
  /* need something non-zero so that pre-allocations below aren't no-ops */
146
  estVertNum = AIR_MAX(1, estVertNum);
147
  estFaceNum = AIR_MAX(1, estFaceNum);
148
149
  /* initialize limnPolyData with estimated # faces and vertices */
150
  /* we will manage the innards of the limnPolyData entirely ourselves */
151
  if (limnPolyDataAlloc(lpld, 0, 0, 0, 0)) {
152
    biffAddf(SEEK, "%s: trouble emptying given polydata", me);
153
    return 1;
154
  }
155
  bag->xyzwArr = airArrayNew((appu.f = &(lpld->xyzw), appu.v),
156
                             &(lpld->xyzwNum),
157
                             4*sizeof(float), sctx->pldArrIncr);
158
  if (sctx->normalsFind) {
159
    bag->normArr = airArrayNew((appu.f = &(lpld->norm), appu.v),
160
                               &(lpld->normNum),
161
                               3*sizeof(float), sctx->pldArrIncr);
162
  } else {
163
    bag->normArr = NULL;
164
  }
165
  bag->indxArr = airArrayNew((appu.ui = &(lpld->indx), appu.v),
166
                             &(lpld->indxNum),
167
                             sizeof(unsigned int), sctx->pldArrIncr);
168
  lpld->primNum = 1;  /* for now, its just triangle soup */
169
  lpld->type = AIR_CALLOC(lpld->primNum, unsigned char);
170
  lpld->icnt = AIR_CALLOC(lpld->primNum, unsigned int);
171
  lpld->type[0] = limnPrimitiveTriangles;
172
  lpld->icnt[0] = 0;  /* incremented below */
173
174
  E = 0;
175
  airArrayLenPreSet(bag->xyzwArr, estVertNum);
176
  E |= !(bag->xyzwArr->data);
177
  if (sctx->normalsFind) {
178
    airArrayLenPreSet(bag->normArr, estVertNum);
179
    E |= !(bag->normArr->data);
180
  }
181
  airArrayLenPreSet(bag->indxArr, 3*estFaceNum);
182
  E |= !(bag->indxArr->data);
183
  if (E) {
184
    biffAddf(SEEK, "%s: couldn't pre-allocate contour geometry (%p %p %p)", me,
185
             bag->xyzwArr->data,
186
             (sctx->normalsFind ? bag->normArr->data : NULL),
187
             bag->indxArr->data);
188
    return 1;
189
  }
190
191
  /* initialize output summary info */
192
  sctx->voxNum = 0;
193
  sctx->vertNum = 0;
194
  sctx->faceNum = 0;
195
196
  return 0;
197
}
198
199
static double
200
sclGet(seekContext *sctx, baggage *bag,
201
       unsigned int xi, unsigned int yi, unsigned int zi) {
202
203
  zi = AIR_MIN(sctx->sz-1, zi);
204
  return bag->scllup(bag->scldata, xi + sctx->sx*(yi + sctx->sy*zi));
205
}
206
207
void
208
_seekIdxProbe(seekContext *sctx, baggage *bag,
209
              double xi, double yi, double zi) {
210
  double idxOut[4], idxIn[4];
211
  AIR_UNUSED(bag);
212
213
  ELL_4V_SET(idxOut, xi, yi, zi, 1);
214
  ELL_4MV_MUL(idxIn, sctx->txfIdx, idxOut);
215
  ELL_4V_HOMOG(idxIn, idxIn);
216
  gageProbe(sctx->gctx, idxIn[0], idxIn[1], idxIn[2]);
217
  return;
218
}
219
220
/*
221
** this is one of the few things that has to operate on more than one
222
** zi plane at once, and it is honestly probably the primary motivation
223
** for putting zi into the baggage.
224
**
225
** NOTE: this is doing some bounds (on the positive x, y, z edges of the
226
** volume) that probably should be done closer to the caller
227
*/
228
static int
229
evecFlipProbe(seekContext *sctx, baggage *bag,
230
              signed char *flip,  /* OUTPUT HERE */
231
              unsigned int xi, unsigned int yi, unsigned int ziOff,
232
              unsigned int dx, unsigned int dy, unsigned int dz) {
233
  static const char me[]="evecFlipProbe";
234
  unsigned int sx, sy, sz;
235
  double u, du, dot, wantDot, minDu, mode;
236
  double current[3], next[3], posNext[3], posA[3], posB[3], evecA[3], evecB[3];
237
238
  sx = AIR_CAST(unsigned int, sctx->sx);
239
  sy = AIR_CAST(unsigned int, sctx->sy);
240
  sz = AIR_CAST(unsigned int, sctx->sz);
241
242
  if (!(xi + dx < sx
243
        && yi + dy < sy
244
        && bag->zi + ziOff + dz < sz)) {
245
    /* the edge we're being asked about is outside the volume */
246
    *flip = 0;
247
    return 0;
248
  }
249
250
  /* Note: Strength checking is no longer performed here.
251
   * TS 2009-08-18 */
252
253
  /* this edge is in bounds */
254
255
  ELL_3V_SET(posA, xi, yi, bag->zi+ziOff);
256
  ELL_3V_SET(posB, xi+dx, yi+dy, bag->zi+ziOff+dz);
257
  ELL_3V_COPY(evecA, sctx->evec
258
              + 3*(bag->esIdx + 3*(ziOff    + 2*(xi    + sx*yi))));
259
  ELL_3V_COPY(evecB, sctx->evec
260
              + 3*(bag->esIdx + 3*(ziOff+dz + 2*(xi+dx + sx*(yi+dy)))));
261
262
#define SETNEXT(uu)                                       \
263
  ELL_3V_SCALE_ADD2(posNext, 1.0-(uu), posA, (uu), posB);       \
264
  _seekIdxProbe(sctx, bag, posNext[0], posNext[1], posNext[2]); \
265
  ELL_3V_COPY(next, sctx->evecAns + 3*bag->esIdx);              \
266
  if (ELL_3V_DOT(current, next) < 0) {                          \
267
    ELL_3V_SCALE(next, -1, next);                               \
268
  }                                                             \
269
  dot = ELL_3V_DOT(current, next);                              \
270
  mode = bag->modeSign*airMode3_d(sctx->evalAns);
271
272
  ELL_3V_COPY(current, evecA);
273
  u = 0;
274
  du = 0.49999;
275
  wantDot = 0.9; /* between cos(pi/6) and cos(pi/8) */
276
  minDu = 0.0002;
277
  while (u + du < 1.0) {
278
    SETNEXT(u+du);
279
    /* Note: This was set to -0.8 in the original code. Again, I found
280
     * that increasing it could eliminate spurious holes in the
281
     * mesh. TS 2009-08-18 */
282
    if (mode < -0.99) {
283
      /* sorry, eigenvalue mode got too close to 2nd order isotropy */
284
      *flip = 0;
285
      return 0;
286
    }
287
    while (dot < wantDot) {
288
      /* angle between current and next is too big; reduce step */
289
      du /= 2;
290
      if (du < minDu) {
291
        fprintf(stderr, "%s: evector wild @ u=%g: du=%g < minDu=%g; "
292
                "dot=%g; mode = %g; "
293
                "(xi,yi,zi)=(%u,%u,%u+%u); (dx,dy,dz)=(%u,%u,%u) ",
294
                me, u, du, minDu,
295
                dot, mode,
296
                xi, yi, bag->zi, ziOff, dx, dy, dz);
297
        *flip = 0;
298
        return 0;
299
      }
300
      SETNEXT(u+du);
301
      if (mode < -0.99) {
302
        /* sorry, eigenvalue mode got too close to 2nd order isotropy */
303
        *flip = 0;
304
        return 0;
305
      }
306
    }
307
    /* current and next have a small angle between them */
308
    ELL_3V_COPY(current, next);
309
    u += du;
310
  }
311
  /* last fake iteration, to check endpoint explicitly */
312
  u = 1.0;
313
  SETNEXT(u);
314
  if (dot < wantDot) {
315
    biffAddf(SEEK, "%s: confused at end of edge", me);
316
    return 1;
317
  }
318
  ELL_3V_COPY(current, next);
319
320
#undef SETNEXT
321
322
  /* we have now tracked the eigenvector along the edge */
323
  dot = ELL_3V_DOT(current, evecB);
324
  *flip = (dot > 0
325
           ? +1
326
           : -1);
327
  return 0;
328
}
329
330
/*
331
** !!! this has to be done as a separate second pass because of how
332
** !!! the flip quantities correspond to the edges between voxels
333
**
334
** For efficiency, evecFlipProbe is only executed on request (by
335
** setting sctx->treated: 0x01 requests all edges of this voxel, 0x02
336
** states that unique edge 3 was treated, 0x04 for unique edge 4) TS
337
*/
338
static int
339
evecFlipShuffleProbe(seekContext *sctx, baggage *bag) {
340
  static const char me[]="evecFlipShuffleProbe";
341
  unsigned int xi, yi, sx, sy, si;
342
  signed char flipA, flipB, flipC;
343
344
  sx = AIR_CAST(unsigned int, sctx->sx);
345
  sy = AIR_CAST(unsigned int, sctx->sy);
346
347
  /* NOTE: these have to go all the way to sy-1 and sx-1, instead of
348
     sy-2 and sx-2 (like shuffleProbe() below) because of the need to
349
     set the flips on the edges at the positive X and Y volume
350
     boundary.  The necessary bounds checking happens in
351
     evecFlipProbe(): messy, I know */
352
  for (yi=0; yi<sy; yi++) {
353
    for (xi=0; xi<sx; xi++) {
354
      si = xi + sx*yi;
355
      /* ================================================= */
356
      if (sctx->treated[si]&0x02) { /* has been treated, just copy result */
357
        sctx->flip[0 + 5*si] = sctx->flip[3 + 5*si];
358
      } else if (sctx->treated[si]&0x01 ||
359
                 (yi!=0 && sctx->treated[xi+sx*(yi-1)]&0x01)) {
360
        /* need to treat this */
361
        if (evecFlipProbe(sctx, bag,    &flipA, xi, yi, 0, 1, 0, 0)) {
362
          biffAddf(SEEK, "%s: problem at (xi,yi) = (%u,%u), zi=0", me, xi, yi);
363
          return 1;
364
        }
365
        sctx->flip[0 + 5*si] = flipA;
366
      }
367
      if (sctx->treated[si]&0x04) { /* has been treated, just copy */
368
        sctx->flip[1 + 5*si] = sctx->flip[4 + 5*si];
369
      } else if (sctx->treated[si]&0x01 ||
370
                 (xi!=0 && sctx->treated[xi-1+sx*yi]&0x01)) {
371
        if (evecFlipProbe(sctx, bag, &flipB, xi, yi, 0, 0, 1, 0)) {
372
          biffAddf(SEEK, "%s: problem at (xi,yi) = (%u,%u), zi=0", me, xi, yi);
373
          return 1;
374
        }
375
        sctx->flip[1 + 5*si] = flipB;
376
      }
377
      if (sctx->treated[si]&0x01 || (xi!=0 && sctx->treated[xi-1+sx*yi]&0x01) ||
378
          (yi!=0 && sctx->treated[xi+sx*(yi-1)]&0x01) ||
379
          (xi!=0 && yi!=0 && sctx->treated[xi-1+sx*(yi-1)]&0x01)) {
380
        if (evecFlipProbe(sctx, bag,    &flipA, xi, yi, 0, 0, 0, 1)) {
381
          biffAddf(SEEK, "%s: problem at (xi,yi,zi) = (%u,%u,%u)", me,
382
                   xi, yi, bag->zi);
383
          return 1;
384
        }
385
        sctx->flip[2 + 5*si] = flipA;
386
      }
387
      if (sctx->treated[si]&0x01 ||
388
          (yi!=0 && sctx->treated[xi+sx*(yi-1)]&0x01)) {
389
        if (evecFlipProbe(sctx, bag, &flipB, xi, yi, 1, 1, 0, 0)) {
390
          biffAddf(SEEK, "%s: problem at (xi,yi,zi) = (%u,%u,%u)", me,
391
                   xi, yi, bag->zi);
392
          return 1;
393
        }
394
        sctx->flip[3 + 5*si] = flipB;
395
        sctx->treated[si]|=0x02;
396
      } else
397
        sctx->treated[si]&=0xFD;
398
      if (sctx->treated[si]&0x01 ||
399
          (xi!=0 && sctx->treated[xi-1+sx*yi]&0x01)) {
400
        if (evecFlipProbe(sctx, bag, &flipC, xi, yi, 1, 0, 1, 0)) {
401
          biffAddf(SEEK, "%s: problem at (xi,yi,zi) = (%u,%u,%u)", me,
402
                   xi, yi, bag->zi);
403
          return 1;
404
        }
405
        sctx->flip[4 + 5*si] = flipC;
406
        sctx->treated[si]|=0x04;
407
      } else
408
        sctx->treated[si]&=0xFB;
409
      /* ================================================= */
410
    }
411
  }
412
413
  return 0;
414
}
415
416
static int
417
shuffleProbe(seekContext *sctx, baggage *bag) {
418
  static const char me[]="shuffleProbe";
419
  unsigned int xi, yi, sx, sy, si, spi;
420
421
  sx = AIR_CAST(unsigned int, sctx->sx);
422
  sy = AIR_CAST(unsigned int, sctx->sy);
423
424
  if (!sctx->strengthUse) { /* just request all edges */
425
    memset(sctx->treated, 0x01, sizeof(char)*sctx->sx*sctx->sy);
426
  } else {
427
    if (!bag->zi) {
428
      /* clear full treated array */
429
      memset(sctx->treated, 0, sizeof(char)*sctx->sx*sctx->sy);
430
    } else {
431
      /* only clear requests for edge orientation */
432
      for (yi=0; yi<sy; yi++) {
433
        for (xi=0; xi<sx; xi++) {
434
          sctx->treated[xi+sx*yi] &= 0xFE;
435
        }
436
      }
437
    }
438
  }
439
440
  for (yi=0; yi<sy; yi++) {
441
    for (xi=0; xi<sx; xi++) {
442
      si = xi + sx*yi;
443
      spi = (xi+1) + (sx+2)*(yi+1);
444
      /* ================================================= */
445
      if (!bag->zi) {
446
        /* ----------------- set/probe bottom of initial slab */
447
        sctx->vidx[0 + 5*si] = -1;
448
        sctx->vidx[1 + 5*si] = -1;
449
        if (sctx->gctx) { /* HEY: need this check, what's the right way? */
450
          _seekIdxProbe(sctx, bag, xi, yi, 0);
451
        }
452
        if (sctx->strengthUse) {
453
          sctx->stng[0 + 2*si] = sctx->strengthSign*sctx->stngAns[0];
454
          if (!si) {
455
            sctx->strengthSeenMax = sctx->stng[0 + 2*si];
456
          }
457
          sctx->strengthSeenMax = AIR_MAX(sctx->strengthSeenMax,
458
                                          sctx->stng[0 + 2*si]);
459
        }
460
        switch (sctx->type) {
461
        case seekTypeIsocontour:
462
          sctx->sclv[0 + 4*spi] = (sclGet(sctx, bag, xi, yi, 0)
463
                                   - sctx->isovalue);
464
          sctx->sclv[1 + 4*spi] = (sclGet(sctx, bag, xi, yi, 0)
465
                                   - sctx->isovalue);
466
          sctx->sclv[2 + 4*spi] = (sclGet(sctx, bag, xi, yi, 1)
467
                                   - sctx->isovalue);
468
          break;
469
        case seekTypeRidgeSurface:
470
        case seekTypeValleySurface:
471
        case seekTypeMaximalSurface:
472
        case seekTypeMinimalSurface:
473
        case seekTypeRidgeSurfaceOP:
474
        case seekTypeValleySurfaceOP:
475
          ELL_3V_COPY(sctx->grad + 3*(0 + 2*si), sctx->gradAns);
476
          ELL_3V_COPY(sctx->eval + 3*(0 + 2*si), sctx->evalAns);
477
          ELL_3M_COPY(sctx->evec + 9*(0 + 2*si), sctx->evecAns);
478
          break;
479
        }
480
      } else {
481
        /* ------------------- shuffle to bottom from top of slab */
482
        sctx->vidx[0 + 5*si] = sctx->vidx[3 + 5*si];
483
        sctx->vidx[1 + 5*si] = sctx->vidx[4 + 5*si];
484
        if (sctx->strengthUse) {
485
          sctx->stng[0 + 2*si] = sctx->stng[1 + 2*si];
486
        }
487
        switch (sctx->type) {
488
        case seekTypeIsocontour:
489
          sctx->sclv[0 + 4*spi] = sctx->sclv[1 + 4*spi];
490
          sctx->sclv[1 + 4*spi] = sctx->sclv[2 + 4*spi];
491
          sctx->sclv[2 + 4*spi] = sctx->sclv[3 + 4*spi];
492
          break;
493
        case seekTypeRidgeSurface:
494
        case seekTypeValleySurface:
495
        case seekTypeMaximalSurface:
496
        case seekTypeMinimalSurface:
497
        case seekTypeRidgeSurfaceOP:
498
        case seekTypeValleySurfaceOP:
499
          ELL_3V_COPY(sctx->grad + 3*(0 + 2*si), sctx->grad + 3*(1 + 2*si));
500
          ELL_3V_COPY(sctx->eval + 3*(0 + 2*si), sctx->eval + 3*(1 + 2*si));
501
          ELL_3M_COPY(sctx->evec + 9*(0 + 2*si), sctx->evec + 9*(1 + 2*si));
502
          break;
503
        }
504
      }
505
      /* ----------------------- set/probe top of slab */
506
      sctx->vidx[2 + 5*si] = -1;
507
      sctx->vidx[3 + 5*si] = -1;
508
      sctx->vidx[4 + 5*si] = -1;
509
      if (sctx->gctx) { /* HEY: need this check, what's the right way? */
510
        _seekIdxProbe(sctx, bag, xi, yi, bag->zi+1);
511
      }
512
      if (sctx->strengthUse) {
513
        sctx->stng[1 + 2*si] = sctx->strengthSign*sctx->stngAns[0];
514
        sctx->strengthSeenMax = AIR_MAX(sctx->strengthSeenMax,
515
                                        sctx->stng[1 + 2*si]);
516
        if (sctx->stng[0+2*si]>sctx->strength ||
517
            sctx->stng[1+2*si]>sctx->strength) {
518
          /* mark up to four voxels as needed */
519
          sctx->treated[si] |= 0x01;
520
          if (xi!=0) sctx->treated[xi-1+sx*yi] |= 0x01;
521
          if (yi!=0) sctx->treated[xi+sx*(yi-1)] |= 0x01;
522
          if (xi!=0 && yi!=0) sctx->treated[xi-1+sx*(yi-1)] |= 0x01;
523
        }
524
      }
525
      switch (sctx->type) {
526
      case seekTypeIsocontour:
527
        sctx->sclv[3 + 4*spi] = (sclGet(sctx, bag, xi, yi, bag->zi+2)
528
                                 - sctx->isovalue);
529
        break;
530
      case seekTypeRidgeSurface:
531
      case seekTypeValleySurface:
532
      case seekTypeMaximalSurface:
533
      case seekTypeMinimalSurface:
534
      case seekTypeRidgeSurfaceOP:
535
      case seekTypeValleySurfaceOP:
536
        ELL_3V_COPY(sctx->grad + 3*(1 + 2*si), sctx->gradAns);
537
        ELL_3V_COPY(sctx->eval + 3*(1 + 2*si), sctx->evalAns);
538
        ELL_3M_COPY(sctx->evec + 9*(1 + 2*si), sctx->evecAns);
539
        break;
540
      }
541
      /* ================================================= */
542
    }
543
    /* copy ends of this scanline left/right to margin */
544
    if (seekTypeIsocontour == sctx->type) {
545
      ELL_4V_COPY(sctx->sclv + 4*(0    + (sx+2)*(yi+1)),
546
                  sctx->sclv + 4*(1    + (sx+2)*(yi+1)));
547
      ELL_4V_COPY(sctx->sclv + 4*(sx+1 + (sx+2)*(yi+1)),
548
                  sctx->sclv + 4*(sx   + (sx+2)*(yi+1)));
549
    }
550
  }
551
  /* copy top and bottom scanline up/down to margin */
552
  if (seekTypeIsocontour == sctx->type) {
553
    for (xi=0; xi<sx+2; xi++) {
554
      ELL_4V_COPY(sctx->sclv + 4*(xi + (sx+2)*0),
555
                  sctx->sclv + 4*(xi + (sx+2)*1));
556
      ELL_4V_COPY(sctx->sclv + 4*(xi + (sx+2)*(sy+1)),
557
                  sctx->sclv + 4*(xi + (sx+2)*sy));
558
    }
559
  }
560
561
  /* this is done as a separate pass because it looks at values between
562
     voxels (so its indexing is not trivial to fold into loops above) */
563
  if (seekTypeRidgeSurface == sctx->type
564
      || seekTypeValleySurface == sctx->type
565
      || seekTypeMaximalSurface == sctx->type
566
      || seekTypeMinimalSurface == sctx->type) {
567
    if (evecFlipShuffleProbe(sctx, bag)) {
568
      biffAddf(SEEK, "%s: trouble at zi=%u\n", me, bag->zi);
569
      return 1;
570
    }
571
  }
572
573
  return 0;
574
}
575
576
#define VAL(xx, yy, zz)  (val[4*( (xx) + (yy)*(sx+2) + spi) + (zz+1)])
577
static void
578
voxelGrads(double vgrad[8][3], double *val, int sx, int spi) {
579
  ELL_3V_SET(vgrad[0],
580
             VAL( 1,  0,  0) - VAL(-1,  0,  0),
581
             VAL( 0,  1,  0) - VAL( 0, -1,  0),
582
             VAL( 0,  0,  1) - VAL( 0,  0, -1));
583
  ELL_3V_SET(vgrad[1],
584
             VAL( 2,  0,  0) - VAL( 0,  0,  0),
585
             VAL( 1,  1,  0) - VAL( 1, -1,  0),
586
             VAL( 1,  0,  1) - VAL( 1,  0, -1));
587
  ELL_3V_SET(vgrad[2],
588
             VAL( 1,  1,  0) - VAL(-1,  1,  0),
589
             VAL( 0,  2,  0) - VAL( 0,  0,  0),
590
             VAL( 0,  1,  1) - VAL( 0,  1, -1));
591
  ELL_3V_SET(vgrad[3],
592
             VAL( 2,  1,  0) - VAL( 0,  1,  0),
593
             VAL( 1,  2,  0) - VAL( 1,  0,  0),
594
             VAL( 1,  1,  1) - VAL( 1,  1, -1));
595
  ELL_3V_SET(vgrad[4],
596
             VAL( 1,  0,  1) - VAL(-1,  0,  1),
597
             VAL( 0,  1,  1) - VAL( 0, -1,  1),
598
             VAL( 0,  0,  2) - VAL( 0,  0,  0));
599
  ELL_3V_SET(vgrad[5],
600
             VAL( 2,  0,  1) - VAL( 0,  0,  1),
601
             VAL( 1,  1,  1) - VAL( 1, -1,  1),
602
             VAL( 1,  0,  2) - VAL( 1,  0,  0));
603
  ELL_3V_SET(vgrad[6],
604
             VAL( 1,  1,  1) - VAL(-1,  1,  1),
605
             VAL( 0,  2,  1) - VAL( 0,  0,  1),
606
             VAL( 0,  1,  2) - VAL( 0,  1,  0));
607
  ELL_3V_SET(vgrad[7],
608
             VAL( 2,  1,  1) - VAL( 0,  1,  1),
609
             VAL( 1,  2,  1) - VAL( 1,  0,  1),
610
             VAL( 1,  1,  2) - VAL( 1,  1,  0));
611
}
612
#undef VAL
613
614
static void
615
vvalIsoSet(seekContext *sctx, baggage *bag, double vval[8],
616
           unsigned int xi, unsigned int yi) {
617
  unsigned int sx, si, spi, vi;
618
619
  AIR_UNUSED(bag);
620
  sx = AIR_CAST(unsigned int, sctx->sx);
621
  si = xi + sx*yi;
622
  spi = (xi+1) + (sx+2)*(yi+1);
623
624
  /* learn voxel values */
625
  /*                      X   Y                 Z */
626
  vval[0] = sctx->sclv[4*(0 + 0*(sx+2) + spi) + 1];
627
  vval[1] = sctx->sclv[4*(1 + 0*(sx+2) + spi) + 1];
628
  vval[2] = sctx->sclv[4*(0 + 1*(sx+2) + spi) + 1];
629
  vval[3] = sctx->sclv[4*(1 + 1*(sx+2) + spi) + 1];
630
  vval[4] = sctx->sclv[4*(0 + 0*(sx+2) + spi) + 2];
631
  vval[5] = sctx->sclv[4*(1 + 0*(sx+2) + spi) + 2];
632
  vval[6] = sctx->sclv[4*(0 + 1*(sx+2) + spi) + 2];
633
  vval[7] = sctx->sclv[4*(1 + 1*(sx+2) + spi) + 2];
634
  if (sctx->strengthUse) {
635
    double s, w, ssum, wsum;
636
    /*                 Z      X   Y   */
637
    ssum = wsum = 0;
638
#define ACCUM(vi) w = AIR_ABS(1.0/vval[vi]); ssum += w*s; wsum += w
639
    s = sctx->stng[0 + 2*(0 + 0*sx + si)]; ACCUM(0);
640
    s = sctx->stng[0 + 2*(1 + 0*sx + si)]; ACCUM(1);
641
    s = sctx->stng[0 + 2*(0 + 1*sx + si)]; ACCUM(2);
642
    s = sctx->stng[0 + 2*(1 + 1*sx + si)]; ACCUM(3);
643
    s = sctx->stng[1 + 2*(0 + 0*sx + si)]; ACCUM(4);
644
    s = sctx->stng[1 + 2*(1 + 0*sx + si)]; ACCUM(5);
645
    s = sctx->stng[1 + 2*(0 + 1*sx + si)]; ACCUM(6);
646
    s = sctx->stng[1 + 2*(1 + 1*sx + si)]; ACCUM(7);
647
#undef ACCUM
648
    if (ssum/wsum < sctx->strength) {
649
      for (vi=0; vi<8; vi++) {
650
        vval[vi] = 0;
651
      }
652
    }
653
  }
654
  return;
655
}
656
657
658
static void
659
vvalSurfSet(seekContext *sctx, baggage *bag, double vval[8],
660
            unsigned int xi, unsigned int yi) {
661
  /* static const char me[]="vvalSurfSet"; */
662
  double evec[8][3], grad[8][3], stng[8], maxStrength=0;
663
  signed char flip[12]={0,0,0,0,0,0,0,0,0,0,0,0}, flipProd;
664
  unsigned int sx, si, vi, ei, vrti[8];
665
666
  sx = AIR_CAST(unsigned int, sctx->sx);
667
  si = xi + sx*yi;
668
  vrti[0] = 0 + 2*(xi + 0 + sx*(yi + 0));
669
  vrti[1] = 0 + 2*(xi + 1 + sx*(yi + 0));
670
  vrti[2] = 0 + 2*(xi + 0 + sx*(yi + 1));
671
  vrti[3] = 0 + 2*(xi + 1 + sx*(yi + 1));
672
  vrti[4] = 1 + 2*(xi + 0 + sx*(yi + 0));
673
  vrti[5] = 1 + 2*(xi + 1 + sx*(yi + 0));
674
  vrti[6] = 1 + 2*(xi + 0 + sx*(yi + 1));
675
  vrti[7] = 1 + 2*(xi + 1 + sx*(yi + 1));
676
677
  /* Our strategy is to create all triangles of which at least some
678
   * part meets the strength criterion, and to trim them in a
679
   * post-process.  This avoids ragged boundaries */
680
  for (vi=0; vi<8; vi++) {
681
    ELL_3V_COPY(grad[vi], sctx->grad + 3*vrti[vi]);
682
    ELL_3V_COPY(evec[vi], sctx->evec + 3*(bag->esIdx + 3*vrti[vi]));
683
    if (sctx->strengthUse) {
684
      stng[vi] = sctx->stng[vrti[vi]];
685
      if (!vi) {
686
        maxStrength = stng[vi];
687
      } else {
688
        maxStrength = AIR_MAX(maxStrength, stng[vi]);
689
      }
690
    }
691
  }
692
  flipProd = 1;
693
  if (sctx->type!=seekTypeRidgeSurfaceOP &&
694
      sctx->type!=seekTypeValleySurfaceOP) {
695
    for (ei=0; ei<12; ei++) {
696
      flip[ei] = sctx->flip[bag->evti[ei] + 5*si];
697
      flipProd *= flip[ei];
698
    }
699
  }
700
701
  if ((sctx->strengthUse && maxStrength < sctx->strength)
702
      || !flipProd) {
703
    /* either the corners this voxel don't meet strength,
704
       or something else is funky */
705
    for (vi=0; vi<8; vi++) {
706
      vval[vi] = 0;
707
    }
708
  } else {
709
    if (sctx->type==seekTypeRidgeSurfaceOP ||
710
        sctx->type==seekTypeValleySurfaceOP) {
711
      /* find orientation based on outer product rule */
712
      double outer[9];
713
      double outerevals[3],outerevecs[9];
714
      ELL_3MV_OUTER(outer,evec[0],evec[0]);
715
      for (vi=1; vi<8; ++vi) {
716
        ELL_3MV_OUTER_INCR(outer,evec[vi],evec[vi]);
717
      }
718
      ell_3m_eigensolve_d(outerevals, outerevecs, outer, AIR_TRUE);
719
      for (vi=0; vi<8; ++vi) {
720
        if (ELL_3V_DOT(evec[vi],outerevecs)<0)
721
          ELL_3V_SCALE(evec[vi], -1.0, evec[vi]);
722
      }
723
    } else {
724
      ELL_3V_SCALE(evec[1], flip[0],                  evec[1]);
725
      ELL_3V_SCALE(evec[2], flip[1],                  evec[2]);
726
      ELL_3V_SCALE(evec[3], flip[0]*flip[2],          evec[3]);
727
      ELL_3V_SCALE(evec[4], flip[4],                  evec[4]);
728
      ELL_3V_SCALE(evec[5], flip[4]*flip[8],          evec[5]);
729
      ELL_3V_SCALE(evec[6], flip[4]*flip[9],          evec[6]);
730
      ELL_3V_SCALE(evec[7], flip[4]*flip[8]*flip[10], evec[7]);
731
    }
732
    for (vi=0; vi<8; vi++) {
733
      vval[vi] = ELL_3V_DOT(grad[vi], evec[vi]);
734
    }
735
  }
736
  return;
737
}
738
739
static int
740
triangulate(seekContext *sctx, baggage *bag, limnPolyData *lpld) {
741
  /* static const char me[]="triangulate"; */
742
  unsigned xi, yi, sx, sy, si, spi;
743
  /* ========================================================== */
744
  /* NOTE: these things must agree with information in tables.c */
745
  int e2v[12][2] = {        /* maps edge index to corner vertex indices */
746
    {0, 1},  /*  0 */
747
    {0, 2},  /*  1 */
748
    {1, 3},  /*  2 */
749
    {2, 3},  /*  3 */
750
    {0, 4},  /*  4 */
751
    {1, 5},  /*  5 */
752
    {2, 6},  /*  6 */
753
    {3, 7},  /*  7 */
754
    {4, 5},  /*  8 */
755
    {4, 6},  /*  9 */
756
    {5, 7},  /* 10 */
757
    {6, 7}   /* 11 */
758
  };
759
  double vccoord[8][3] = {  /* vertex corner coordinates */
760
    {0, 0, 0},  /* 0 */
761
    {1, 0, 0},  /* 1 */
762
    {0, 1, 0},  /* 2 */
763
    {1, 1, 0},  /* 3 */
764
    {0, 0, 1},  /* 4 */
765
    {1, 0, 1},  /* 5 */
766
    {0, 1, 1},  /* 6 */
767
    {1, 1, 1}   /* 7 */
768
  };
769
  /* ========================================================== */
770
771
  sx = AIR_CAST(unsigned int, sctx->sx);
772
  sy = AIR_CAST(unsigned int, sctx->sy);
773
774
  for (yi=0; yi<sy-1; yi++) {
775
    double vval[8], vgrad[8][3], vert[3], tvertA[4], tvertB[4], ww;
776
    unsigned char vcase;
777
    int ti, vi, ei, vi0, vi1, ecase;
778
    const int *tcase;
779
    unsigned int vii[3];
780
    for (xi=0; xi<sx-1; xi++) {
781
      si = xi + sx*yi;
782
      spi = (xi+1) + (sx+2)*(yi+1);
783
      switch (sctx->type) {
784
      case seekTypeIsocontour:
785
        vvalIsoSet(sctx, bag, vval, xi, yi);
786
        break;
787
      case seekTypeRidgeSurface:
788
      case seekTypeValleySurface:
789
      case seekTypeMaximalSurface:
790
      case seekTypeMinimalSurface:
791
      case seekTypeRidgeSurfaceOP:
792
      case seekTypeValleySurfaceOP:
793
        vvalSurfSet(sctx, bag, vval, xi, yi);
794
        break;
795
      }
796
      /* determine voxel and edge case */
797
      vcase = 0;
798
      for (vi=0; vi<8; vi++) {
799
        vcase |= (vval[vi] > 0) << vi;
800
      }
801
      if (0 == vcase || 255 == vcase) {
802
        /* no triangles added here */
803
        continue;
804
      }
805
      /* set voxel corner gradients */
806
      if (seekTypeIsocontour == sctx->type
807
          && sctx->normalsFind
808
          && !sctx->normAns) {
809
        voxelGrads(vgrad, sctx->sclv, sx, spi);
810
      }
811
      sctx->voxNum++;
812
      ecase = seekContour3DTopoHackEdge[vcase];
813
      /* create new vertices as needed */
814
      for (ei=0; ei<12; ei++) {
815
        if ((ecase & (1 << ei))
816
            && -1 == sctx->vidx[bag->evti[ei] + 5*si]) {
817
          int ovi;
818
          double tvec[3], grad[3], tlen;
819
          /* this edge is needed for triangulation,
820
             and, we haven't already created a vertex for it */
821
          vi0 = e2v[ei][0];
822
          vi1 = e2v[ei][1];
823
          ww = vval[vi0]/(vval[vi0] - vval[vi1]);
824
          ELL_3V_LERP(vert, ww, vccoord[vi0], vccoord[vi1]);
825
          ELL_4V_SET(tvertA, vert[0] + xi, vert[1] + yi, vert[2] + bag->zi, 1);
826
          ELL_4MV_MUL(tvertB, sctx->txfIdx, tvertA);
827
          /* tvertB is now in input index space */
828
          ELL_4MV_MUL(tvertA, sctx->shape->ItoW, tvertB);
829
          /* tvertA is now in input world space */
830
          ELL_4V_HOMOG(tvertA, tvertA);
831
          ELL_4V_HOMOG(tvertB, tvertB);
832
          ovi = sctx->vidx[bag->evti[ei] + 5*si] =
833
            airArrayLenIncr(bag->xyzwArr, 1);
834
          ELL_4V_SET_TT(lpld->xyzw + 4*ovi, float,
835
                        tvertA[0], tvertA[1], tvertA[2], 1.0);
836
          /*
837
          fprintf(stderr, "!%s: vert %u: %g %g %g\n", me, ovi,
838
                  tvertA[0], tvertA[1], tvertA[2]);
839
          */
840
          if (sctx->normalsFind) {
841
            airArrayLenIncr(bag->normArr, 1);
842
            if (sctx->normAns) {
843
              gageProbe(sctx->gctx, tvertB[0], tvertB[1], tvertB[2]);
844
              ELL_3V_SCALE_TT(lpld->norm + 3*ovi, float, -1, sctx->normAns);
845
              if (sctx->reverse) {
846
                ELL_3V_SCALE(lpld->norm + 3*ovi, -1, lpld->norm + 3*ovi);
847
              }
848
            } else {
849
              ELL_3V_LERP(grad, ww, vgrad[vi0], vgrad[vi1]);
850
              ELL_3MV_MUL(tvec, sctx->txfNormal, grad);
851
              ELL_3V_NORM_TT(lpld->norm + 3*ovi, float, tvec, tlen);
852
            }
853
          }
854
          sctx->vertNum++;
855
          /*
856
            fprintf(stderr, "%s: vert %d (edge %d) of (%d,%d,%d) "
857
            "at %g %g %g\n",
858
            me, sctx->vidx[bag->evti[ei] + 5*si], ei, xi, yi, zi,
859
            vert[0] + xi, vert[1] + yi, vert[2] + bag->zi);
860
          */
861
        }
862
      }
863
      /* add triangles */
864
      ti = 0;
865
      tcase = seekContour3DTopoHackTriangle[vcase];
866
      while (-1 != tcase[0 + 3*ti]) {
867
        unsigned iii;
868
        ELL_3V_SET(vii,
869
                   sctx->vidx[bag->evti[tcase[0 + 3*ti]] + 5*si],
870
                   sctx->vidx[bag->evti[tcase[1 + 3*ti]] + 5*si],
871
                   sctx->vidx[bag->evti[tcase[2 + 3*ti]] + 5*si]);
872
        if (sctx->reverse) {
873
          int tmpi;
874
          tmpi = vii[1]; vii[1] = vii[2]; vii[2] = tmpi;
875
        }
876
        iii = airArrayLenIncr(bag->indxArr, 3);
877
        ELL_3V_COPY(lpld->indx + iii, vii);
878
        /*
879
        fprintf(stderr, "!%s: tri %u: %u %u %u\n",
880
                me, iii/3, vii[0], vii[1], vii[2]);
881
        */
882
        lpld->icnt[0] += 3;
883
        sctx->faceNum++;
884
        ti++;
885
      }
886
    }
887
  }
888
  return 0;
889
}
890
891
static int
892
surfaceExtract(seekContext *sctx, limnPolyData *lpld) {
893
  static const char me[]="surfaceExtract";
894
  char done[AIR_STRLEN_SMALL];
895
  unsigned int zi, sz;
896
  baggage *bag;
897
898
  bag = baggageNew(sctx);
899
  sz = AIR_CAST(unsigned int, sctx->sz);
900
901
  /* this creates the airArrays in bag */
902
  if (outputInit(sctx, bag, lpld)) {
903
    biffAddf(SEEK, "%s: trouble", me);
904
    return 1;
905
  }
906
907
  if (sctx->verbose > 2) {
908
    fprintf(stderr, "%s: extracting ...       ", me);
909
  }
910
  for (zi=0; zi<sz-1; zi++) {
911
    char trouble=0;
912
    if (sctx->verbose > 2) {
913
      fprintf(stderr, "%s", airDoneStr(0, zi, sz-2, done));
914
      fflush(stderr);
915
    }
916
    bag->zi = zi;
917
    if (sctx->type==seekTypeRidgeSurfaceT ||
918
        sctx->type==seekTypeValleySurfaceT) {
919
      if (_seekShuffleProbeT(sctx, bag) ||
920
          _seekTriangulateT(sctx, bag, lpld))
921
        trouble = 1;
922
    } else {
923
      if (shuffleProbe(sctx, bag) ||
924
          triangulate(sctx, bag, lpld))
925
        trouble = 1;
926
    }
927
    if (trouble) {
928
      biffAddf(SEEK, "%s: trouble on zi = %u", me, zi);
929
      return 1;
930
    }
931
  }
932
  if (sctx->verbose > 2) {
933
    fprintf(stderr, "%s\n", airDoneStr(0, zi, sz-2, done));
934
  }
935
936
  /* this cleans up the airArrays in bag */
937
  baggageNix(bag);
938
939
  return 0;
940
}
941
942
int
943
seekExtract(seekContext *sctx, limnPolyData *lpld) {
944
  static const char me[]="seekExtract";
945
  double time0;
946
  int E;
947
948
  if (!( sctx && lpld )) {
949
    biffAddf(SEEK, "%s: got NULL pointer", me);
950
    return 1;
951
  }
952
953
  if (seekTypeIsocontour == sctx->type) {
954
    if (!AIR_EXISTS(sctx->isovalue)) {
955
      biffAddf(SEEK, "%s: didn't seem to ever set isovalue (now %g)", me,
956
               sctx->isovalue);
957
      return 1;
958
    }
959
  }
960
961
  if (sctx->verbose) {
962
    fprintf(stderr, "%s: --------------------\n", me);
963
    fprintf(stderr, "%s: flagResult = %d\n", me,
964
            sctx->flag[flagResult]);
965
  }
966
967
  /* reset max strength seen */
968
  sctx->strengthSeenMax = AIR_NAN;
969
970
  /* start time */
971
  time0 = airTime();
972
973
  switch(sctx->type) {
974
  case seekTypeIsocontour:
975
  case seekTypeRidgeSurface:
976
  case seekTypeValleySurface:
977
  case seekTypeMinimalSurface:
978
  case seekTypeMaximalSurface:
979
  case seekTypeRidgeSurfaceOP:
980
  case seekTypeRidgeSurfaceT:
981
  case seekTypeValleySurfaceOP:
982
  case seekTypeValleySurfaceT:
983
    E = surfaceExtract(sctx, lpld);
984
    break;
985
  default:
986
    biffAddf(SEEK, "%s: sorry, %s extraction not implemented", me,
987
             airEnumStr(seekType, sctx->type));
988
    return 1;
989
    break;
990
  }
991
  if (E) {
992
    biffAddf(SEEK, "%s: trouble", me);
993
    return 1;
994
  }
995
996
  /* end time */
997
  sctx->time = airTime() - time0;
998
999
  sctx->flag[flagResult] = AIR_FALSE;
1000
1001
  return 0;
1002
}