GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/limn/polymod.c Lines: 0 1155 0.0 %
Date: 2017-05-26 Branches: 0 750 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
  Copyright (C) 2012, 2011, 2010, 2009, 2008  Thomas Schultz
7
8
  This library is free software; you can redistribute it and/or
9
  modify it under the terms of the GNU Lesser General Public License
10
  (LGPL) as published by the Free Software Foundation; either
11
  version 2.1 of the License, or (at your option) any later version.
12
  The terms of redistributing and/or modifying this software also
13
  include exceptions to the LGPL that facilitate static linking.
14
15
  This library is distributed in the hope that it will be useful,
16
  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
  Lesser General Public License for more details.
19
20
  You should have received a copy of the GNU Lesser General Public License
21
  along with this library; if not, write to Free Software Foundation, Inc.,
22
  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
23
*/
24
25
26
#include "limn.h"
27
28
/*
29
** determines intersection of elements of srcA and srcB.
30
** assumes:
31
** - there are no repeats in either list
32
** - dstC is allocated for at least as long as the longer of srcA and srcB
33
*/
34
static unsigned int
35
flipListIntx(unsigned int *dstC,
36
             const unsigned int *_srcA, const unsigned int *_srcB) {
37
  const unsigned int *srcA, *srcB;
38
  unsigned int numA, numB, numC, idxA, idxB;
39
40
  numA = _srcA[0];
41
  srcA = _srcA + 1;
42
  numB = _srcB[0];
43
  srcB = _srcB + 1;
44
  numC = 0;
45
  for (idxA=0; idxA<numA; idxA++) {
46
    for (idxB=0; idxB<numB; idxB++) {
47
      if (srcA[idxA] == srcB[idxB]) {
48
        dstC[numC++] = srcA[idxA];
49
      }
50
    }
51
  }
52
  return numC;
53
}
54
55
/*
56
** given triangle identified by triIdx,
57
** set neighGot[] and neighInfo[][]
58
** neighbors are index 0,1,2;
59
** neighbor ii is on edge between vert ii and (ii+1)%3
60
** neighGot[ii] is non-zero iff there was such a neighbor
61
** neighInfo[ii][0]: index of the (triangle) neighbor
62
** neighInfo[ii][1], neighInfo[ii][2]: the two vertices shared with neighbor,
63
**    in the order that the *neighbor* should be traversing them
64
*/
65
static void
66
flipNeighborsGet(Nrrd *nTriWithVert, Nrrd *nVertWithTri,
67
                 unsigned int neighGot[3], unsigned int neighInfo[3][3],
68
                 unsigned int *intxBuff, unsigned int triIdx) {
69
  /* static const char me[]="flipNeighborsGet"; */
70
  unsigned int intxNum, vertA, vertB, neighIdx, maxTriPerVert,
71
    *vertWithTri, *triWithVert;
72
  int ii;
73
74
  vertWithTri = AIR_CAST(unsigned int*, nVertWithTri->data);
75
  triWithVert = AIR_CAST(unsigned int*, nTriWithVert->data);
76
  maxTriPerVert = nTriWithVert->axis[0].size - 1;
77
  for (ii=0; ii<3; ii++) {
78
    vertA = (vertWithTri + 3*triIdx)[ii];
79
    vertB = (vertWithTri + 3*triIdx)[AIR_MOD(ii+1, 3)];
80
    /*
81
    fprintf(stderr, "!%s: %u edge %u: vert{A,B} = %u %u\n", me,
82
            triIdx, ii, vertA, vertB);
83
    */
84
    /* find the intersection of the sets of {triangles using vertA}
85
       and {triangles using vertB}: for reasonable surfaces should
86
       be either 0 or 2 triangles, and if its 2, then triIdx
87
       should be one of them */
88
    intxNum = flipListIntx(intxBuff,
89
                           triWithVert + (1+maxTriPerVert)*vertA,
90
                           triWithVert + (1+maxTriPerVert)*vertB);
91
    if (2 == intxNum) {
92
      neighIdx = intxBuff[0];
93
      if (neighIdx == triIdx) {
94
        neighIdx = intxBuff[1];
95
      }
96
      neighGot[ii] = AIR_TRUE;
97
      neighInfo[ii][0] = neighIdx;
98
      neighInfo[ii][1] = vertB;
99
      neighInfo[ii][2] = vertA;
100
    } else {
101
      neighGot[ii] = AIR_FALSE;
102
    }
103
  }
104
  return;
105
}
106
107
/*
108
** determines if triIdx needs to be flipped, given that it should
109
** be seeing vertices vertA and vertB in that order
110
*/
111
static int
112
flipNeed(Nrrd *nVertWithTri, unsigned int triIdx,
113
         unsigned int vertA, unsigned int vertB) {
114
  unsigned int *vertWithTri, vert[3];
115
  int ai, bi;
116
117
  vertWithTri = AIR_CAST(unsigned int*, nVertWithTri->data);
118
  ELL_3V_COPY(vert, vertWithTri + 3*triIdx);
119
  for (ai=0; vert[ai] != vertA; ai++)
120
    ;
121
  for (bi=0; vert[bi] != vertB; bi++)
122
    ;
123
  return (1 != AIR_MOD(bi - ai, 3));
124
}
125
126
/*
127
** this is a weird dual-personality function that is the inner
128
** loop of both vertex winding fixing, and the learning stage of
129
** vertex splitting
130
**
131
** for flipping (!splitting)
132
** assumes that triIdx was just popped from "okay" stack
133
** (triIdx has just been fixed to have correct winding)
134
** then goes through the not-yet-done neighbors of triIdx,
135
** flipping them if needed, and
136
** then adding those neighbors to the stack.
137
** returns the number of tris added to stack
138
**
139
** NOTE: the "flipping" is done within the nVertWithTri representation,
140
** but *not* in the limnPolyData itself.
141
*/
142
static unsigned int
143
neighborsCheckPush(Nrrd *nTriWithVert, Nrrd *nVertWithTri,
144
                   unsigned char *triDone, airArray *okayArr,
145
                   unsigned int *intxBuff, airArray *splitArr,
146
                   unsigned int triIdx, int splitting) {
147
  /* static const char me[]="neighborsCheckPush"; */
148
  unsigned int neighGot[3], neighInfo[3][3], ii, *okay, okayIdx,
149
    *vertWithTri, pushedNum;
150
151
  vertWithTri = AIR_CAST(unsigned int*, nVertWithTri->data);
152
  flipNeighborsGet(nTriWithVert, nVertWithTri,
153
                   neighGot, neighInfo,
154
                   intxBuff, triIdx);
155
  /*
156
  for (ii=0; ii<3; ii++) {
157
    fprintf(stderr, "!%s: %u neigh[%u]: ", me, triIdx, ii);
158
    if (neighGot[ii]) {
159
      fprintf(stderr, "%u (%u %u) (done %u)\n",
160
              neighInfo[ii][0], neighInfo[ii][1], neighInfo[ii][2],
161
              triDone[neighInfo[ii][0]]);
162
    } else {
163
      fprintf(stderr, "nope\n");
164
    }
165
  }
166
  */
167
  pushedNum = 0;
168
  for (ii=0; ii<3; ii++) {
169
    /* WARNING: complicated logic WRT triDone, splitting, and need */
170
    if (neighGot[ii]) {
171
      unsigned int tmp, *idxLine, need;
172
      if (!splitting) {
173
        if (!triDone[neighInfo[ii][0]]) {
174
          /* we only take time to learn need if as yet undone */
175
          need = flipNeed(nVertWithTri, neighInfo[ii][0],
176
                          neighInfo[ii][1], neighInfo[ii][2]);
177
          if (need) {
178
            idxLine = vertWithTri + 3*neighInfo[ii][0];
179
            /* here is the vertex winding flip */
180
            ELL_SWAP2(idxLine[0], idxLine[1], tmp);
181
          }
182
        }
183
      } else {
184
        /* we're here for splitting  */
185
        /* we have to learn need regardless of done-ness */
186
        need = flipNeed(nVertWithTri, neighInfo[ii][0],
187
                        neighInfo[ii][1], neighInfo[ii][2]);
188
        if (need && triDone[neighInfo[ii][0]]) {
189
          /* we "need" to flip and yet we've already visited that triangle
190
             ==> edge between triIdx and neighInfo[ii][0] needs splitting.
191
             See if its a new split, and add it if so */
192
          unsigned int *split, splitIdx, splitNum, vert0, vert1;
193
          vert0 = AIR_MIN(neighInfo[ii][1], neighInfo[ii][2]);
194
          vert1 = AIR_MAX(neighInfo[ii][1], neighInfo[ii][2]);
195
          splitNum = splitArr->len;
196
          split = AIR_CAST(unsigned int*, splitArr->data);
197
          for (splitIdx=0; splitIdx<splitNum; splitIdx++) {
198
            if (split[2 + 5*splitIdx] == vert0
199
                && split[3 + 5*splitIdx] == vert1) {
200
              break;
201
            }
202
          }
203
          if (splitIdx == splitNum) {
204
            /* this is a new split, add it */
205
            /*
206
            fprintf(stderr, "!%s: new split(%u,%u) (have %u)\n",
207
                    me, vert0, vert1, splitArr->len);
208
            */
209
            splitIdx = airArrayLenIncr(splitArr, 1);
210
            split = AIR_CAST(unsigned int*, splitArr->data);
211
            split[0 + 5*splitIdx] = triIdx;
212
            split[1 + 5*splitIdx] = neighInfo[ii][0];
213
            split[2 + 5*splitIdx] = vert0;
214
            split[3 + 5*splitIdx] = vert1;
215
            split[4 + 5*splitIdx] = AIR_FALSE;
216
          }
217
        }
218
      }
219
      /* regardless of splitting, we push onto the okay stack all
220
         the un-done neighbors that we just processed */
221
      if (!triDone[neighInfo[ii][0]]) {
222
        triDone[neighInfo[ii][0]] = AIR_TRUE;
223
        okayIdx = airArrayLenIncr(okayArr, 1);
224
        okay = AIR_CAST(unsigned int*, okayArr->data);
225
        okay[okayIdx] = neighInfo[ii][0];
226
        ++pushedNum;
227
      }
228
    } /* if (neighGot[ii]) */
229
  } /* for ii */
230
  return pushedNum;
231
}
232
233
/*
234
** ONLY GOOD FOR limnPrimitiveTriangles!!
235
*/
236
static unsigned int
237
maxTrianglePerPrimitive(limnPolyData *pld) {
238
  unsigned int ret, primIdx;
239
240
  ret = 0;
241
  for (primIdx=0; primIdx<pld->primNum; primIdx++) {
242
    ret = AIR_MAX(ret, pld->icnt[primIdx]/3);
243
  }
244
  return ret;
245
}
246
247
/*
248
** fills nTriWithVert with 2D array about which triangles use which vertices
249
*/
250
static int
251
triangleWithVertex(Nrrd *nTriWithVert, limnPolyData *pld) {
252
  static const char me[]="triangleWithVertex";
253
  unsigned int *triWithVertNum,   /* vert ii has triWithVertNum[ii] tris */
254
    *triWithVert, baseVertIdx, primIdx, vertIdx,
255
    maxTriPerVert, totTriIdx;
256
  airArray *mop;
257
258
  if (!(nTriWithVert && pld)) {
259
    biffAddf(LIMN, "%s: got NULL pointer", me);
260
    return 1;
261
  }
262
  if ((1 << limnPrimitiveTriangles) != limnPolyDataPrimitiveTypes(pld)) {
263
    biffAddf(LIMN, "%s: sorry, can only handle %s primitives", me,
264
             airEnumStr(limnPrimitive, limnPrimitiveTriangles));
265
    return 1;
266
  }
267
268
  triWithVertNum = AIR_CAST(unsigned int*,
269
                            calloc(pld->xyzwNum, sizeof(unsigned int)));
270
  if (!triWithVertNum) {
271
    biffAddf(LIMN, "%s: couldn't allocate temp array", me);
272
    return 1;
273
  }
274
  mop = airMopNew();
275
  airMopAdd(mop, triWithVertNum, airFree, airMopAlways);
276
277
  /* fill in triWithVertNum */
278
  baseVertIdx = 0;
279
  for (primIdx=0; primIdx<pld->primNum; primIdx++) {
280
    unsigned int triNum, triIdx, *indxLine, ii;
281
    triNum = pld->icnt[primIdx]/3;
282
    for (triIdx=0; triIdx<triNum; triIdx++) {
283
      indxLine = pld->indx + baseVertIdx + 3*triIdx;
284
      for (ii=0; ii<3; ii++) {
285
        triWithVertNum[indxLine[ii]]++;
286
      }
287
    }
288
    baseVertIdx += pld->icnt[primIdx];
289
  }
290
291
  /* find max # tris per vert, allocate output */
292
  maxTriPerVert = 0;
293
  for (vertIdx=0; vertIdx<pld->xyzwNum; vertIdx++) {
294
    maxTriPerVert = AIR_MAX(maxTriPerVert, triWithVertNum[vertIdx]);
295
  }
296
  if (nrrdMaybeAlloc_va(nTriWithVert, nrrdTypeUInt, 2,
297
                        AIR_CAST(size_t, 1 + maxTriPerVert),
298
                        AIR_CAST(size_t, pld->xyzwNum))) {
299
    biffMovef(LIMN, NRRD, "%s: couldn't allocate output", me);
300
    airMopError(mop); return 1;
301
  }
302
  triWithVert = AIR_CAST(unsigned int*, nTriWithVert->data);
303
304
  baseVertIdx = 0;
305
  totTriIdx = 0;
306
  for (primIdx=0; primIdx<pld->primNum; primIdx++) {
307
    unsigned int triNum, *indxLine, *twvLine, ii, triIdx;
308
    triNum = pld->icnt[primIdx]/3;
309
    for (triIdx=0; triIdx<triNum; triIdx++) {
310
      indxLine = pld->indx + baseVertIdx + 3*triIdx;
311
      for (ii=0; ii<3; ii++) {
312
        twvLine = triWithVert + (1+maxTriPerVert)*indxLine[ii];
313
        twvLine[1+twvLine[0]] = totTriIdx;
314
        twvLine[0]++;
315
      }
316
      ++totTriIdx;
317
    }
318
    baseVertIdx += pld->icnt[primIdx];
319
  }
320
321
  airMopOkay(mop);
322
  return 0;
323
}
324
325
/*
326
** learns which (three vertices) are with which triangle
327
*/
328
static int
329
vertexWithTriangle(Nrrd *nVertWithTri, limnPolyData *pld) {
330
  static const char me[]="vertexWithTriangle";
331
  unsigned int baseVertIdx, primIdx, *vertWithTri, triNum;
332
333
  if (!(nVertWithTri && pld)) {
334
    biffAddf(LIMN, "%s: got NULL pointer", me);
335
    return 1;
336
  }
337
  if ((1 << limnPrimitiveTriangles) != limnPolyDataPrimitiveTypes(pld)) {
338
    biffAddf(LIMN, "%s: sorry, can only handle %s primitives", me,
339
             airEnumStr(limnPrimitive, limnPrimitiveTriangles));
340
    return 1;
341
  }
342
343
  triNum = limnPolyDataPolygonNumber(pld);
344
  if (nrrdMaybeAlloc_va(nVertWithTri, nrrdTypeUInt, 2,
345
                        AIR_CAST(size_t, 3),
346
                        AIR_CAST(size_t, triNum))) {
347
    biffMovef(LIMN, NRRD, "%s: couldn't allocate output", me);
348
    return 1;
349
  }
350
  vertWithTri = AIR_CAST(unsigned int*, nVertWithTri->data);
351
352
  baseVertIdx = 0;
353
  for (primIdx=0; primIdx<pld->primNum; primIdx++) {
354
    unsigned int triIdx, *indxLine, totTriIdx, ii;
355
    triNum = pld->icnt[primIdx]/3;
356
    for (triIdx=0; triIdx<triNum; triIdx++) {
357
      totTriIdx = triIdx + baseVertIdx/3;
358
      indxLine = pld->indx + baseVertIdx + 3*triIdx;
359
      for (ii=0; ii<3; ii++) {
360
        (vertWithTri + 3*totTriIdx)[ii] = indxLine[ii];
361
      }
362
    }
363
    baseVertIdx += pld->icnt[primIdx];
364
  }
365
366
  return 0;
367
}
368
369
static int
370
splitListExtract(unsigned int *listLenP,
371
                 airArray *edgeArr, unsigned char *hitCount,
372
                 unsigned int firstVertIdx, unsigned int edgeDoneNum) {
373
  static const char me[]="splitListExtract";
374
  unsigned int *edgeData, edgeNum, *edgeLine, edgeIdx, edgeTmp[5],
375
    tmp, nextVertIdx, listLen;
376
377
  edgeNum = edgeArr->len;
378
  edgeData = AIR_CAST(unsigned int*, edgeArr->data);
379
  edgeNum -= edgeDoneNum;
380
  edgeData += 5*edgeDoneNum;
381
382
  /* put first edge in first position */
383
  for (edgeIdx=0; edgeIdx<edgeNum; edgeIdx++) {
384
    edgeLine = edgeData + 5*edgeIdx;
385
    if (edgeLine[2] == firstVertIdx || edgeLine[3] == firstVertIdx) {
386
      break;
387
    }
388
  }
389
  if (edgeIdx == edgeNum) {
390
    biffAddf(LIMN, "%s: never found first vertex %u", me, firstVertIdx);
391
    return 1;
392
  }
393
  if (edgeLine[3] == firstVertIdx) {
394
    ELL_SWAP2(edgeLine[2], edgeLine[3], tmp);
395
  }
396
  ELL_5V_COPY(edgeTmp, edgeData);
397
  ELL_5V_COPY(edgeData, edgeLine);
398
  ELL_5V_COPY(edgeLine, edgeTmp);
399
400
  /* start looking for the rest */
401
  listLen = 1;
402
  hitCount[firstVertIdx]--;
403
  nextVertIdx = edgeData[3];
404
  hitCount[nextVertIdx]--;
405
  /*
406
  fprintf(stderr, "!%s: found first %u --> %u (tris %u %u)\n", me,
407
          firstVertIdx, nextVertIdx,
408
          edgeData[0], edgeData[1]);
409
  */
410
411
  /* the search start progresses so that we don't see the same edge twice */
412
#define SEARCH \
413
  for (edgeIdx=listLen; edgeIdx<edgeNum; edgeIdx++) { \
414
    edgeLine = edgeData + 5*edgeIdx; \
415
    if (edgeLine[2] == nextVertIdx || edgeLine[3] == nextVertIdx) { \
416
      break; \
417
    } \
418
  }
419
  SEARCH;
420
  while (edgeIdx < edgeNum) {
421
    if (edgeLine[3] == nextVertIdx) {
422
      ELL_SWAP2(edgeLine[2], edgeLine[3], tmp);
423
    }
424
    ELL_5V_COPY(edgeTmp, edgeData + 5*listLen);
425
    ELL_5V_COPY(edgeData + 5*listLen, edgeLine);
426
    ELL_5V_COPY(edgeLine, edgeTmp);
427
    hitCount[nextVertIdx]--;
428
    /*
429
    fprintf(stderr, "!%s: (len %u) found %u --> %u  (tris %u %u)\n", me,
430
            listLen, nextVertIdx,
431
            (edgeData + 5*listLen)[3],
432
            (edgeData + 5*listLen)[0],
433
            (edgeData + 5*listLen)[1]);
434
    */
435
    nextVertIdx = (edgeData + 5*listLen)[3];
436
    hitCount[nextVertIdx]--;
437
    listLen++;
438
    SEARCH;
439
  }
440
  /*
441
  fprintf(stderr, "!%s: finishing with Len %u, ended at %u\n", me,
442
          listLen, nextVertIdx);
443
  */
444
  *listLenP = listLen;
445
  return 0;
446
#undef SEARCH
447
}
448
449
/*
450
** returns the element of vert[] that is not v0 or v1
451
*/
452
static unsigned int
453
sweepVertNext(unsigned int *vert, unsigned int v0, unsigned int v1) {
454
  unsigned int v2;
455
456
  v2 = vert[0];
457
  if (v2 == v0 || v2 == v1) {
458
    v2 = vert[1];
459
  }
460
  if (v2 == v0 || v2 == v1) {
461
    v2 = vert[2];
462
  }
463
  return v2;
464
}
465
466
/*
467
** returns non-zero iff A and B are in {v[0],v[1],v[2]}
468
*/
469
static int
470
sweepHave2(unsigned int v[3], unsigned int A, unsigned B) {
471
  int haveA, haveB;
472
473
  haveA = (A == v[0] || A == v[1] || A == v[2]);
474
  haveB = (B == v[0] || B == v[1] || B == v[2]);
475
  return (haveA && haveB);
476
}
477
478
/*
479
** returns UINT_MAX if there is no other triangle
480
*/
481
static unsigned int
482
sweepTriNext(unsigned int *triLine, unsigned int v0, unsigned int v1,
483
             unsigned int triNot, Nrrd *nVertWithTri) {
484
  unsigned int triIdx, ret, *vertLine, *vertWithTri;
485
486
  vertWithTri = AIR_CAST(unsigned int*, nVertWithTri->data);
487
488
  for (triIdx=0; triIdx<triLine[0]; triIdx++) {
489
    if (triLine[1+triIdx] == triNot) {
490
      continue;
491
    }
492
    vertLine = vertWithTri + 3*triLine[1+triIdx];
493
    if (sweepHave2(vertLine, v0, v1)) {
494
      break;
495
    }
496
  }
497
  if (triIdx == triLine[0]) {
498
    ret = UINT_MAX;
499
  } else {
500
    ret = triLine[1+triIdx];
501
  }
502
  return ret;
503
}
504
505
/*
506
** the sweep does NOT include triStart, but it does include whichever
507
**  triStop it hit (if any)
508
** returns: length of sweep
509
** sweep: output (does not include triStart)
510
** triStartIdx: what triangle to start at
511
** vertPivotIdx, vertStartIdx: two vertices of start triangle; sweep
512
**     proceeds around the pivot index
513
** triStop{0,1}Idx: triangles to stop sweeping at
514
*/
515
static unsigned int
516
splitTriSweep(unsigned int *sweep,
517
              unsigned int triStart,
518
              unsigned int vertPivot, unsigned int vertStart,
519
              unsigned int triStop0, unsigned int triStop1,
520
              Nrrd *nTriWithVert, Nrrd *nVertWithTri) {
521
  /* static const char me[]="splitTriSweep"; */
522
  unsigned int sweepLen;
523
  unsigned int maxTriPerVert, *triWithVert,
524
    *vertWithTri, *triLine, *vertLine, triCurr, vertLast, vertNext;
525
526
  maxTriPerVert = AIR_CAST(unsigned int, nTriWithVert->axis[0].size-1);
527
  triWithVert = AIR_CAST(unsigned int*, nTriWithVert->data);
528
  vertWithTri = AIR_CAST(unsigned int*, nVertWithTri->data);
529
530
  /*
531
  fprintf(stderr, "!%s:  hi,  triStart %u, pivot %u, start %u, "
532
          "stop = %u, %u\n", me,
533
          triStart, vertPivot, vertStart, triStop0, triStop1);
534
  */
535
  if (triStart == triStop0 || triStart == triStop1) {
536
    /* nowhere to go */
537
    return 0;
538
  }
539
540
  triLine = triWithVert + (1+maxTriPerVert)*vertPivot;
541
  vertLast = vertStart;
542
  triCurr = triStart;
543
  sweepLen = 0;
544
  do {
545
    if (!(triCurr == triStart)) {
546
      sweep[sweepLen++] = triCurr;
547
      /*
548
      fprintf(stderr, "!%s:       saving sweep[%u] = %u\n", me,
549
              sweepLen-1, triCurr);
550
      */
551
    }
552
    vertLine = vertWithTri + 3*triCurr;
553
    vertNext = sweepVertNext(vertLine, vertPivot, vertLast);
554
    /*
555
    fprintf(stderr, "!%s:       vertNext(%u,%u) = %u\n", me,
556
            vertPivot, vertLast, vertNext);
557
    */
558
    triCurr = sweepTriNext(triLine, vertPivot, vertNext,
559
                           triCurr, nVertWithTri);
560
    /*
561
    fprintf(stderr, "!%s:       triNext(%u,%u) = %u\n", me,
562
            vertPivot, vertNext, triCurr);
563
    */
564
    vertLast = vertNext;
565
  } while (!( UINT_MAX == triCurr
566
              || triStart == triCurr
567
              || triStop0 == triCurr
568
              || triStop1 == triCurr ));
569
  if (!( UINT_MAX == triCurr )) {
570
    sweep[sweepLen++] = triCurr;
571
    /*
572
    fprintf(stderr, "!%s:       saving sweep[%u] = %u\n", me,
573
            sweepLen-1, triCurr);
574
    */
575
  }
576
577
  return sweepLen;
578
}
579
580
/*
581
** track0: first triangle track, length *track0LenP
582
** track1: first triangle track, length *track1LenP
583
** sweep: buffer for sweep
584
**
585
** NOTE: triangles may be internally repeated in a track
586
**
587
** when vert path a loop on a non-orientable surface (e.g. mobius strip),
588
** then track0 will NOT include the endpoint triangles
589
** (or its not supposed to), and track1 will include them.
590
*/
591
static int
592
splitTriTrack(unsigned int *track0, unsigned int *track0LenP,
593
              unsigned int *track1, unsigned int *track1LenP,
594
              unsigned int *sweep,
595
              Nrrd *nTriWithVert, Nrrd *nVertWithTri,
596
              airArray *edgeArr, unsigned startIdx, unsigned int listLen,
597
              int looping) {
598
  static const char me[]="splitTriTrack";
599
  unsigned int len0, len1, *edgeData, *edgeLine, edgeIdx, triIdx,
600
    /* maxTriPerVert, *triWithVert, *vertWithTri, */
601
    sweepLen, loopEnd0, loopEnd1, loopStart0, loopStart1;
602
  int doBack0, doBack1;
603
604
  len0 = len1 = 0;
605
  edgeData = AIR_CAST(unsigned int*, edgeArr->data);
606
  edgeData += 5*startIdx;
607
  /* maxTriPerVert = AIR_CAST(unsigned int, nTriWithVert->axis[0].size-1); */
608
  /* triWithVert = AIR_CAST(unsigned int*, nTriWithVert->data); */
609
  /* vertWithTri = AIR_CAST(unsigned int*, nVertWithTri->data); */
610
611
  if (looping) {
612
    loopStart0 = (edgeData)[0];
613
    loopStart1 = (edgeData)[1];
614
    loopEnd0 = (edgeData + 5*(listLen - 1))[0];
615
    loopEnd1 = (edgeData + 5*(listLen - 1))[1];
616
    /*
617
    fprintf(stderr, "!%s: loop start = %u, %u, end = %u,%u\n", me,
618
            loopStart0, loopStart1, loopEnd0, loopEnd1);
619
    */
620
  } else {
621
    loopStart0 = loopStart1 = UINT_MAX;
622
    loopEnd0 = loopEnd1 = UINT_MAX;
623
  }
624
625
  /* ,,,,,,,,,,,,,,,,,,,,,
626
  fprintf(stderr, "!%s: 1st 2 tris %u %u, verts %u %u\n", me,
627
          edgeData[0], edgeData[1], edgeData[2], edgeData[3]);
628
  fprintf(stderr, "!%s: triangles at start vert %u:\n", me, edgeData[2]);
629
  triLine = triWithVert + edgeData[2]*(1+maxTriPerVert);
630
  for (triIdx=0; triIdx<triLine[0]; triIdx++) {
631
    unsigned int *vertLine;
632
    vertLine = vertWithTri + 3*triLine[1+triIdx];
633
    fprintf(stderr, "!%s: %u:  %u  (verts %u %u %u)\n",
634
            me, triIdx, triLine[1+triIdx],
635
            vertLine[0], vertLine[1], vertLine[2]);
636
  }
637
  ````````````````````` */
638
639
  /* we turn on backward sweeping for the initial edge;
640
     doBack{0,1} will be set explicitly at each edge thereafter */
641
  doBack0 = doBack1 = AIR_TRUE;
642
  for (edgeIdx=0; edgeIdx<(looping
643
                           ? listLen-1
644
                           : listLen); edgeIdx++) {
645
    unsigned int stop0, stop1;
646
    edgeLine = edgeData + 5*edgeIdx;
647
    /* ,,,,,,,,,,,,,,,,,,,,,
648
    fprintf(stderr, "!%s: edge %u: vert %u->%u, tris %u, %u\n", me,
649
            edgeIdx, edgeLine[2], edgeLine[3],
650
            edgeLine[0], edgeLine[1]);
651
    fprintf(stderr, "!%s:   triangles at next vert %u:\n", me, edgeLine[3]);
652
    triLine = triWithVert + edgeLine[3]*(1+maxTriPerVert);
653
    for (triIdx=0; triIdx<triLine[0]; triIdx++) {
654
      vertLine = vertWithTri + 3*triLine[1+triIdx];
655
      fprintf(stderr, "!%s:   %u:  %u  (verts %u %u %u)\n",
656
              me, triIdx, triLine[1+triIdx],
657
              vertLine[0], vertLine[1], vertLine[2]);
658
    }
659
    ````````````````````` */
660
    if (0 == edgeIdx && looping) {
661
      /* sweeps from 1st link on loop are stopped by a tris on last edge */
662
      stop0 = loopEnd0;
663
      stop1 = loopEnd1;
664
    } else {
665
      stop0 = UINT_MAX;
666
      stop1 = UINT_MAX;
667
    }
668
    if (doBack0) {
669
      sweepLen = splitTriSweep(sweep, edgeLine[0], edgeLine[2], edgeLine[3],
670
                               stop0, stop1, nTriWithVert, nVertWithTri);
671
      if (0 == edgeIdx && looping && sweepLen > 0) {
672
        /* don't include either stop triangle on track 0 */
673
        for (triIdx=0; triIdx<sweepLen-1; triIdx++) {
674
          track0[len0++] = sweep[sweepLen-2-triIdx];
675
        }
676
      } else {
677
        for (triIdx=0; triIdx<sweepLen; triIdx++) {
678
          track0[len0++] = sweep[sweepLen-1-triIdx];
679
        }
680
      }
681
      track0[len0++] = edgeLine[0];
682
    }
683
    if (doBack1) {
684
      sweepLen = splitTriSweep(sweep, edgeLine[1], edgeLine[2], edgeLine[3],
685
                               stop0, stop1, nTriWithVert, nVertWithTri);
686
      /* on this side we *do* include the stop triangle */
687
      for (triIdx=0; triIdx<sweepLen; triIdx++) {
688
        track1[len1++] = sweep[sweepLen-1-triIdx];
689
      }
690
      track1[len1++] = edgeLine[1];
691
    }
692
693
    if (edgeIdx<listLen-1) {
694
      stop0 = (edgeLine + 5)[0];
695
      stop1 = (edgeLine + 5)[1];
696
    } else {
697
      if (looping) {
698
        stop0 = loopStart0;
699
        stop1 = loopStart1;
700
      } else {
701
        stop0 = UINT_MAX;
702
        stop1 = UINT_MAX;
703
      }
704
    }
705
    sweepLen = splitTriSweep(sweep, edgeLine[0], edgeLine[3], edgeLine[2],
706
                             stop0, stop1, nTriWithVert, nVertWithTri);
707
    for (triIdx=0; triIdx<sweepLen; triIdx++) {
708
      track0[len0++] = sweep[triIdx];
709
    }
710
    sweepLen = splitTriSweep(sweep, edgeLine[1], edgeLine[3], edgeLine[2],
711
                             stop0, stop1, nTriWithVert, nVertWithTri);
712
    for (triIdx=0; triIdx<sweepLen; triIdx++) {
713
      track1[len1++] = sweep[triIdx];
714
    }
715
    if (edgeIdx<listLen-1) {
716
      unsigned int *nextLine, tmp;
717
      /* re-arrange the next edgeLine according to sweep results */
718
      nextLine = edgeData + 5*(1 + edgeIdx);
719
      if (track0[len0-1] == nextLine[0]
720
          && track1[len1-1] == nextLine[1]) {
721
        /* fprintf(stderr, "!%s: tracking went 0->0, 1->1\n", me); */
722
        doBack0 = doBack1 = AIR_FALSE;
723
      } else if (track0[len0-1] == nextLine[1]
724
                 && track1[len1-1] == nextLine[0]) {
725
        /* fprintf(stderr, "!%s: tracking went 0->1, 0->1\n", me); */
726
        ELL_SWAP2(nextLine[0], nextLine[1], tmp);
727
        doBack0 = doBack1 = AIR_FALSE;
728
      } else if (track0[len0-1] == nextLine[0]) {
729
        /* fprintf(stderr, "!%s: tracking went 0->0, 1->x\n", me); */
730
        doBack0 = AIR_FALSE;
731
        doBack1 = AIR_TRUE;
732
      } else if (track1[len1-1] == nextLine[1]) {
733
        /* fprintf(stderr, "!%s: tracking went 0->x, 1->1\n", me); */
734
        doBack0 = AIR_TRUE;
735
        doBack1 = AIR_FALSE;
736
      } else if (track0[len0-1] == nextLine[1]) {
737
        /* fprintf(stderr, "!%s: tracking went 0->1, 1->x\n", me); */
738
        ELL_SWAP2(nextLine[0], nextLine[1], tmp);
739
        doBack0 = AIR_FALSE;
740
        doBack1 = AIR_TRUE;
741
      } else if (track1[len1-1] == nextLine[0]) {
742
        /* fprintf(stderr, "!%s: tracking went 0->x, 1->0\n", me); */
743
        ELL_SWAP2(nextLine[0], nextLine[1], tmp);
744
        doBack0 = AIR_TRUE;
745
        doBack1 = AIR_FALSE;
746
      } else {
747
        biffAddf(LIMN, "%s: edge %u/%u, sweep ends %u,%u != want %u,%u", me,
748
                 edgeIdx, listLen, track0[len0-1], track1[len1-1],
749
                 nextLine[0], nextLine[1]);
750
        return 1;
751
      }
752
    } else {
753
      doBack0 = doBack1 = AIR_FALSE;
754
    }
755
  }
756
  if (looping) {
757
    /* the end of track0 shouldn't include the stop */
758
    len0--;
759
  }
760
761
  *track0LenP = len0;
762
  *track1LenP = len1;
763
  return 0;
764
}
765
766
static int
767
splitVertDup(limnPolyData *pld, airArray *edgeArr,
768
             unsigned int edgeDoneNum, unsigned int listLen,
769
             unsigned int *track, unsigned int trackLen,
770
             int looping) {
771
  static const char me[]="splitVertDup";
772
  unsigned int *vixLut, ii, vixLutLen, oldVertNum, newVertNum, *edgeData,
773
    bitflag, trackIdx, vert0, vert1;
774
  airArray *mop;
775
  limnPolyData pldTmp;
776
777
  mop = airMopNew();
778
  edgeData = AIR_CAST(unsigned int*, edgeArr->data);
779
  edgeData += 5*edgeDoneNum;
780
  oldVertNum = pld->xyzwNum;
781
  vixLutLen = looping ? listLen : listLen+1;
782
  newVertNum = oldVertNum + vixLutLen;
783
784
  /* quiet compiler warnings */
785
  pldTmp.rgba = NULL;
786
  pldTmp.norm = NULL;
787
  pldTmp.tex2 = NULL;
788
  pldTmp.tang = NULL;
789
790
  if (looping) {
791
    vert0 = edgeData[2]; /* don't use dupe of this on first triangle */
792
    vert1 = edgeData[3]; /* don't use dupe of this on last triangle */
793
  } else {
794
    vert0 = vert1 = UINT_MAX;
795
  }
796
797
  /* HEY: sneakily preserve the old per-vertex arrays; we own them now */
798
  pldTmp.xyzw = pld->xyzw;
799
  airMopAdd(mop, pldTmp.xyzw, airFree, airMopAlways);
800
  pld->xyzw = NULL;
801
  pld->xyzwNum = 0;
802
  bitflag = limnPolyDataInfoBitFlag(pld);
803
  if ((1 << limnPolyDataInfoRGBA) & bitflag) {
804
    pldTmp.rgba = pld->rgba;
805
    airMopAdd(mop, pldTmp.rgba, airFree, airMopAlways);
806
    pld->rgba = NULL;
807
    pld->rgbaNum = 0;
808
  }
809
  if ((1 << limnPolyDataInfoNorm) & bitflag) {
810
    pldTmp.norm = pld->norm;
811
    airMopAdd(mop, pldTmp.norm, airFree, airMopAlways);
812
    pld->norm = NULL;
813
    pld->normNum = 0;
814
  }
815
  if ((1 << limnPolyDataInfoTex2) & bitflag) {
816
    pldTmp.tex2 = pld->tex2;
817
    airMopAdd(mop, pldTmp.tex2, airFree, airMopAlways);
818
    pld->tex2 = NULL;
819
    pld->tex2Num = 0;
820
  }
821
  if ((1 << limnPolyDataInfoTang) & bitflag) {
822
    pldTmp.tang = pld->tang;
823
    airMopAdd(mop, pldTmp.tang, airFree, airMopAlways);
824
    pld->tang = NULL;
825
    pld->tangNum = 0;
826
  }
827
  if (limnPolyDataAlloc(pld, bitflag, newVertNum,
828
                        pld->indxNum, pld->primNum)) {
829
    biffAddf(LIMN, "%s: couldn't allocate new vert # %u", me, newVertNum);
830
    airMopError(mop); return 1;
831
  }
832
833
  /* copy old data */
834
  memcpy(pld->xyzw, pldTmp.xyzw, oldVertNum*4*sizeof(float));
835
  if ((1 << limnPolyDataInfoRGBA) & bitflag) {
836
    memcpy(pld->rgba, pldTmp.rgba, oldVertNum*4*sizeof(unsigned char));
837
  }
838
  if ((1 << limnPolyDataInfoNorm) & bitflag) {
839
    memcpy(pld->norm, pldTmp.norm, oldVertNum*3*sizeof(float));
840
  }
841
  if ((1 << limnPolyDataInfoTex2) & bitflag) {
842
    memcpy(pld->tex2, pldTmp.tex2, oldVertNum*2*sizeof(float));
843
  }
844
  if ((1 << limnPolyDataInfoTang) & bitflag) {
845
    memcpy(pld->tang, pldTmp.tang, oldVertNum*3*sizeof(float));
846
  }
847
848
  vixLut = AIR_CAST(unsigned int *, calloc(2*vixLutLen,
849
                                           sizeof(unsigned int)));
850
  airMopAdd(mop, vixLut, airFree, airMopAlways);
851
  if (looping) {
852
    for (ii=0; ii<vixLutLen; ii++) {
853
      vixLut[0 + 2*ii] = edgeData[2 + 5*ii];
854
      vixLut[1 + 2*ii] = oldVertNum + ii;
855
    }
856
  } else {
857
    for (ii=0; ii<vixLutLen-1; ii++) {
858
      vixLut[0 + 2*ii] = edgeData[2 + 5*ii];
859
      vixLut[1 + 2*ii] = oldVertNum + ii;
860
    }
861
    /* now ii == vixLutLen-1 == listLen */
862
    vixLut[0 + 2*ii] = edgeData[3 + 5*(ii-1)];
863
    vixLut[1 + 2*ii] = oldVertNum + ii;
864
  }
865
866
  /* copy pld's vertex information to duped vertices */
867
  for (ii=0; ii<vixLutLen; ii++) {
868
    ELL_4V_COPY(pld->xyzw + 4*vixLut[1 + 2*ii],
869
                pld->xyzw + 4*vixLut[0 + 2*ii]);
870
    if ((1 << limnPolyDataInfoRGBA) & bitflag) {
871
      ELL_4V_COPY(pld->rgba + 4*vixLut[1 + 2*ii],
872
                  pld->rgba + 4*vixLut[0 + 2*ii]);
873
    }
874
    if ((1 << limnPolyDataInfoNorm) & bitflag) {
875
      ELL_3V_COPY(pld->norm + 3*vixLut[1 + 2*ii],
876
                  pld->norm + 3*vixLut[0 + 2*ii]);
877
    }
878
    if ((1 << limnPolyDataInfoTex2) & bitflag) {
879
      ELL_2V_COPY(pld->tex2 + 2*vixLut[1 + 2*ii],
880
                  pld->tex2 + 2*vixLut[0 + 2*ii]);
881
    }
882
    if ((1 << limnPolyDataInfoTang) & bitflag) {
883
      ELL_3V_COPY(pld->tang + 3*vixLut[1 + 2*ii],
884
                  pld->tang + 3*vixLut[0 + 2*ii]);
885
    }
886
  }
887
888
  /* for triangles in track, update indices of duped vertices */
889
  /* we do this updating ONLY in the limnPolyData, and that's okay:
890
     the split information is computed entirely from nVertWithTri
891
     and nTriWithVert (which were based on the original polydata),
892
     but not the current polydata */
893
  /* HEY: this is one place where we really exploit the fact that we only
894
     have triangles: it makes the indxLine computation much much easier */
895
  for (trackIdx=0; trackIdx<trackLen; trackIdx++) {
896
    unsigned int *indxLine, jj;
897
    indxLine = pld->indx + 3*track[trackIdx];
898
    for (ii=0; ii<vixLutLen; ii++) {
899
      for (jj=0; jj<3; jj++) {
900
        if (indxLine[jj] == vixLut[0 + 2*ii]
901
            && !((0 == trackIdx && indxLine[jj] == vert0)
902
                 || (trackLen-1 == trackIdx && indxLine[jj] == vert1))) {
903
          indxLine[jj] = vixLut[1 + 2*ii];
904
        }
905
      }
906
    }
907
  }
908
909
  airMopOkay(mop);
910
  return 0;
911
}
912
913
/*
914
**  edge[0, 1]: two neighboring triangles,
915
**  edge[2, 3]: their shared vertices
916
**  edge[4]: non-zero if this split has been processed
917
**
918
** this really makes no effort to be fast (or comprehensible)
919
**
920
** HEY should we be returning some statistics (e.g. how many points added)?
921
*/
922
static int
923
doSplitting(limnPolyData *pld, Nrrd *nTriWithVert, Nrrd *nVertWithTri,
924
            airArray *edgeArr) {
925
  static const char me[]="doSplitting";
926
  unsigned int edgeIdx, *edgeData,
927
    *edgeLine=NULL, vertIdx, vertNum, splitNum, edgeDoneNum, listLen=0,
928
    *track0, track0Len=0, *track1, *sweep, track1Len=0, maxTriPerVert;
929
  unsigned char *hitCount;
930
  airArray *mop;
931
  int passIdx;
932
933
  if (!edgeArr->len) {
934
    /* actually, no splitting was required! */
935
    return 0;
936
  }
937
938
  mop = airMopNew();
939
  /* NOTE: It is necessary to save out the number of (initial)
940
     number of vertices here, because as we do the splitting
941
     (which is done once per track, as tracks are computed),
942
     pld->xyzwNum will increase ... */
943
  vertNum = pld->xyzwNum;
944
  hitCount = AIR_CAST(unsigned char *, calloc(vertNum,
945
                                              sizeof(unsigned char)));
946
  maxTriPerVert = AIR_CAST(unsigned int, nTriWithVert->axis[0].size - 1);
947
  track0 = AIR_CAST(unsigned int *, calloc(maxTriPerVert*edgeArr->len,
948
                                           sizeof(unsigned int)));
949
  track1 = AIR_CAST(unsigned int *, calloc(maxTriPerVert*edgeArr->len,
950
                                           sizeof(unsigned int)));
951
  sweep = AIR_CAST(unsigned int *, calloc(maxTriPerVert,
952
                                          sizeof(unsigned int)));
953
  if (!(hitCount && track0 && track1 && sweep)) {
954
    biffAddf(LIMN, "%s: couldn't alloc buffers", me);
955
    airMopError(mop); return 1;
956
  }
957
  airMopAdd(mop, hitCount, airFree, airMopAlways);
958
  airMopAdd(mop, track0, airFree, airMopAlways);
959
  airMopAdd(mop, track1, airFree, airMopAlways);
960
  airMopAdd(mop, sweep, airFree, airMopAlways);
961
962
  edgeData = AIR_CAST(unsigned int*, edgeArr->data);
963
964
  /* initialize hitCount */
965
  for (edgeIdx=0; edgeIdx<edgeArr->len; edgeIdx++) {
966
    unsigned int ha, hb;
967
    edgeLine = edgeData + 5*edgeIdx;
968
    ha = hitCount[edgeLine[2]]++;
969
    hb = hitCount[edgeLine[3]]++;
970
    if (ha > 2 || hb > 2) {
971
      biffAddf(LIMN, "%s: edge %u (vert %u %u) created hit counts %u %u", me,
972
               edgeIdx, edgeLine[2], edgeLine[3], ha, hb);
973
      airMopError(mop); return 1;
974
    }
975
  }
976
977
  /* scan hitCount */
978
#define SEARCH(x)                               \
979
  for (vertIdx=0; vertIdx<vertNum; vertIdx++) { \
980
    if ((x) == hitCount[vertIdx]) {             \
981
      break;                                    \
982
    }                                           \
983
  }
984
985
  splitNum = 0;
986
  edgeDoneNum = 0;
987
  /* pass 0: look for singleton hits ==> non-loop tracks
988
     pass 1: look for hitCount[2] ==> loop tracks
989
  */
990
  for (passIdx=0; passIdx<2; passIdx++) {
991
    if (0 == passIdx) {
992
      SEARCH(1);
993
    } else {
994
      SEARCH(2);
995
    }
996
    while (vertIdx < vertNum) {
997
      unsigned int E;
998
      E = 0;
999
      if (1) {
1000
        unsigned int hitIdx, hitSum;
1001
        hitSum = 0;
1002
        for (hitIdx=0; hitIdx<vertNum; hitIdx++) {
1003
          hitSum += hitCount[hitIdx];
1004
        }
1005
        /*
1006
        fprintf(stderr, "!%s: PRE hitSum = %u (pass %u)\n", me,
1007
                hitSum, passIdx);
1008
        */
1009
      }
1010
      if (!E) E |= splitListExtract(&listLen, edgeArr, hitCount,
1011
                                    vertIdx, edgeDoneNum);
1012
      /* HEY: should do a splitListShorten() that cuts across repeated
1013
         triangles, and then shifting downward the rest of the list.
1014
         take care with loops.  iterate until there is no shortening */
1015
      /*
1016
      if (1) {
1017
        unsigned int hitIdx, hitSum;
1018
        hitSum = 0;
1019
        for (hitIdx=0; hitIdx<vertNum; hitIdx++) {
1020
          hitSum += hitCount[hitIdx];
1021
        }
1022
        fprintf(stderr, "!%s: (%d) POST hitSum = %u (pass %u)\n", me, E,
1023
                hitSum, passIdx);
1024
      }
1025
      if (1 == passIdx) {
1026
        fprintf(stderr, "!%s: loop len %u, verts %u,%u --- %u,%u\n"
1027
                "         tris %u,%u --- %u,%u\n", me,
1028
                listLen,
1029
                (edgeData + 5*(edgeDoneNum + listLen - 1))[2],
1030
                (edgeData + 5*(edgeDoneNum + listLen - 1))[3],
1031
                (edgeData + 5*edgeDoneNum)[2],
1032
                (edgeData + 5*edgeDoneNum)[3],
1033
                (edgeData + 5*(edgeDoneNum + listLen - 1))[0],
1034
                (edgeData + 5*(edgeDoneNum + listLen - 1))[1],
1035
                (edgeData + 5*edgeDoneNum)[0],
1036
                (edgeData + 5*edgeDoneNum)[1]);
1037
      }
1038
      */
1039
      if (!E) E |= splitTriTrack(track0, &track0Len, track1, &track1Len,
1040
                                 sweep, nTriWithVert, nVertWithTri,
1041
                                 edgeArr, edgeDoneNum, listLen, passIdx);
1042
      /* ,,,,,,,,,,,,,,,,,,,,,
1043
      if (!E) {
1044
        fprintf(stderr, "!%s: track0:\n", me);
1045
        for (triIdx=0; triIdx<track0Len; triIdx++) {
1046
          fprintf(stderr, "!%s:  %u: %u\n", me, triIdx, track0[triIdx]);
1047
        }
1048
        fprintf(stderr, "!%s: track1:\n", me);
1049
        for (triIdx=0; triIdx<track1Len; triIdx++) {
1050
          fprintf(stderr, "!%s:  %u: %u\n", me, triIdx, track1[triIdx]);
1051
        }
1052
      }
1053
      ````````````````````` */
1054
      /* see- this is the only time pld is used (so it can be modified) */
1055
      /* HEY: we should be using track1, since that's the one that includes
1056
         the endpoint triangles, but on a mobius strip demo it looked worse...
1057
         this still needs debugging */
1058
      if (!E) E |= splitVertDup(pld, edgeArr, edgeDoneNum, listLen,
1059
                                track0, track0Len, passIdx);
1060
      if (E) {
1061
        biffAddf(LIMN, "%s: trouble on split %u (done %u/%u)", me,
1062
                 splitNum, edgeDoneNum, AIR_CAST(unsigned int, edgeArr->len));
1063
        return 1;
1064
      }
1065
      edgeDoneNum += listLen;
1066
      /*
1067
        fprintf(stderr, "!%s: edgeDoneNum now %u (%u)\n", me,
1068
        edgeDoneNum, AIR_CAST(unsigned int, edgeArr->len));
1069
      */
1070
      if (0 == passIdx) {
1071
        SEARCH(1);
1072
      } else {
1073
        SEARCH(2);
1074
      }
1075
    }
1076
  }
1077
#undef SEARCH
1078
  airMopOkay(mop);
1079
  return 0;
1080
}
1081
1082
int
1083
_limnPolyDataVertexWindingProcess(limnPolyData *pld, int splitting) {
1084
  static const char me[]="limnPolyDataVertexWindingProcess";
1085
  unsigned int
1086
    primIdx,         /* for indexing through primitives */
1087
    triIdx,          /* for indexing through triangles in each primitive */
1088
    maxTriPerPrim,   /* max # triangles per primitive, which is essential for
1089
                        the indexing of each triangle (in each primitive)
1090
                        into a single triangle index */
1091
    totTriIdx,       /* another triangle index */
1092
    totTriNum,       /* total # triangles */
1093
    trueTriNum,      /* correct total # triangles in all primitives */
1094
    baseVertIdx,     /* first vertex for current primitive */
1095
    maxTriPerVert,   /* max # of tris on single vertex */
1096
    /* *triWithVert,    2D array ((1+maxTriPerVert) x pld->xyzwNum)
1097
                        of per-vertex triangles */
1098
    *vertWithTri,    /* 3D array (3 x maxTriPerPrim x pld->primNum)
1099
                        of per-tri vertices (vertex indices), which is just
1100
                        a repackaging of the information in the lpld */
1101
    doneTriNum,      /* # triangles finished so far */
1102
    *intxBuff,       /* stupid buffer */
1103
    *okay,           /* the stack of triangles with okay (possibly fixed)
1104
                        winding, but with some neighbors that may as yet
1105
                        need fixing */
1106
    *split;          /* stack of 5-tuples about edges needing vertex splits:
1107
                        split[0, 1]: two neighboring triangles,
1108
                        split[2, 3]: their shared vertices
1109
                        split[4]: non-zero if this split has been processed */
1110
  unsigned char
1111
    *triDone;        /* 1D array (len totTriNum) record of done-ness */
1112
  Nrrd *nTriWithVert, *nVertWithTri;
1113
  airArray *mop,     /* house-keeping */
1114
    *okayArr,        /* airArray around "okay" */
1115
    *splitArr;       /* airArray around "split" */
1116
  airPtrPtrUnion appu;
1117
  /*
1118
    fprintf(stderr, "!%s: hi\n", me);
1119
  */
1120
  if (!pld) {
1121
    biffAddf(LIMN, "%s: got NULL pointer", me);
1122
    return 1;
1123
  }
1124
1125
  if (!(pld->xyzwNum && pld->primNum)) {
1126
    /* this is empty? */
1127
    return 0;
1128
  }
1129
1130
  if ((1 << limnPrimitiveTriangles) != limnPolyDataPrimitiveTypes(pld)) {
1131
    biffAddf(LIMN, "%s: sorry, can only handle %s primitives", me,
1132
             airEnumStr(limnPrimitive, limnPrimitiveTriangles));
1133
    return 1;
1134
  }
1135
1136
  maxTriPerPrim = maxTrianglePerPrimitive(pld);
1137
  totTriNum = limnPolyDataPolygonNumber(pld);
1138
1139
  mop = airMopNew();
1140
  triDone = AIR_CAST(unsigned char *, calloc(totTriNum,
1141
                                             sizeof(unsigned char)));
1142
  airMopAdd(mop, triDone, airFree, airMopAlways);
1143
  if (!triDone) {
1144
    biffAddf(LIMN, "%s: couldn't allocate temp array", me);
1145
    airMopError(mop); return 1;
1146
  }
1147
1148
  /* allocate TriWithVert, VertWithTri, intxBuff */
1149
  nTriWithVert = nrrdNew();
1150
  airMopAdd(mop, nTriWithVert, (airMopper)nrrdNuke, airMopAlways);
1151
  nVertWithTri = nrrdNew();
1152
  airMopAdd(mop, nVertWithTri, (airMopper)nrrdNuke, airMopAlways);
1153
  if (triangleWithVertex(nTriWithVert, pld)
1154
      || vertexWithTriangle(nVertWithTri, pld)) {
1155
    biffAddf(LIMN, "%s: couldn't set nTriWithVert or nVertWithTri", me);
1156
    airMopError(mop); return 1;
1157
  }
1158
  vertWithTri = AIR_CAST(unsigned int*, nVertWithTri->data);
1159
  /* triWithVert = AIR_CAST(unsigned int*, nTriWithVert->data); */
1160
1161
  maxTriPerVert = nTriWithVert->axis[0].size - 1;
1162
  intxBuff = AIR_CAST(unsigned int*, calloc(maxTriPerVert,
1163
                                            sizeof(unsigned int)));
1164
  if (!intxBuff) {
1165
    biffAddf(LIMN, "%s: failed to alloc an itty bitty buffer", me);
1166
    airMopError(mop); return 1;
1167
  }
1168
  airMopAdd(mop, intxBuff, airFree, airMopAlways);
1169
1170
  /*
1171
  nrrdSave("triWithVert.nrrd", nTriWithVert, NULL);
1172
  nrrdSave("vertWithTri.nrrd", nVertWithTri, NULL);
1173
  */
1174
1175
  /* create the stack of recently fixed triangles */
1176
  appu.ui = &okay;
1177
  okayArr = airArrayNew(appu.v, NULL, sizeof(unsigned int),
1178
                        maxTriPerPrim);
1179
  airMopAdd(mop, okayArr, (airMopper)airArrayNuke, airMopAlways);
1180
  if (splitting) {
1181
    appu.ui = &split;
1182
    splitArr = airArrayNew(appu.v, NULL, 5*sizeof(unsigned int),
1183
                           maxTriPerPrim);
1184
    /* split set as it is used */
1185
  } else {
1186
    splitArr = NULL;
1187
    split = NULL;
1188
  }
1189
1190
  /* the skinny */
1191
  doneTriNum = 0;
1192
  trueTriNum = 0;
1193
  for (primIdx=0; primIdx<pld->primNum; primIdx++) {
1194
    trueTriNum += pld->icnt[primIdx]/3;
1195
  }
1196
  /*
1197
  fprintf(stderr, "!%s: trueTriNum %u; other tri num %u\n", me,
1198
          trueTriNum, limnPolyDataPolygonNumber(pld));
1199
  */
1200
  while (doneTriNum < trueTriNum) {
1201
    /* find first undone triangle, which should be on a different
1202
       connected component than any processed so far */
1203
    for (totTriIdx=0; triDone[totTriIdx]; totTriIdx++)
1204
      ;
1205
    /* we use the winding of this triangle to determine the correct
1206
       winding of all neighboring trianges, so this one is now done */
1207
    triDone[totTriIdx] = AIR_TRUE;
1208
    ++doneTriNum;
1209
    /*
1210
    fprintf(stderr, "!%s: considering tri %u done (%u)\n",
1211
            me, totTriIdx, doneTriNum);
1212
    */
1213
    doneTriNum += neighborsCheckPush(nTriWithVert, nVertWithTri,
1214
                                     triDone, okayArr, intxBuff, splitArr,
1215
                                     totTriIdx, splitting);
1216
    while (okayArr->len) {
1217
      unsigned int popped;
1218
      popped = okay[okayArr->len-1];
1219
      airArrayLenIncr(okayArr, -1);
1220
      /*
1221
      fprintf(stderr, "!%s: popped %u\n", me, popped);
1222
      */
1223
      doneTriNum += neighborsCheckPush(nTriWithVert, nVertWithTri,
1224
                                       triDone, okayArr, intxBuff, splitArr,
1225
                                       popped, splitting);
1226
    }
1227
  }
1228
1229
  if (splitting) {
1230
    if (doSplitting(pld, nTriWithVert, nVertWithTri, splitArr)) {
1231
      biffAddf(LIMN, "%s: problem doing vertex splitting", me);
1232
      return 1;
1233
    }
1234
  } else {
1235
    /* Copy from nVertWithTri back into polydata */
1236
    baseVertIdx = 0;
1237
    for (primIdx=0; primIdx<pld->primNum; primIdx++) {
1238
      unsigned int triNum, *indxLine, ii;
1239
      triNum = pld->icnt[primIdx]/3;
1240
      for (triIdx=0; triIdx<triNum; triIdx++) {
1241
        totTriIdx = triIdx + baseVertIdx/3;
1242
        indxLine = pld->indx + baseVertIdx + 3*triIdx;
1243
        for (ii=0; ii<3; ii++) {
1244
          indxLine[ii] = (vertWithTri + 3*totTriIdx)[ii];
1245
        }
1246
      }
1247
      baseVertIdx += pld->icnt[primIdx];
1248
    }
1249
  }
1250
1251
  airMopOkay(mop);
1252
  return 0;
1253
}
1254
1255
/*
1256
** with non-zero splitting, this does vertex splitting so that
1257
** non-orientable surfaces can be rendered without seams. Took longer
1258
** to implement than intended.
1259
**
1260
** HEY: still has a bug in handling which triangles get which
1261
** (new) vertices when the seam in the non-orientable surface
1262
** is a closed loop.  Can be debugged later...
1263
*/
1264
int
1265
limnPolyDataVertexWindingFix(limnPolyData *pld, int splitting) {
1266
  static const char me[]="limnPolyDataVertexWindingFix";
1267
1268
  if (!splitting) {
1269
    if (_limnPolyDataVertexWindingProcess(pld, AIR_FALSE)) {
1270
      biffAddf(LIMN, "%s: trouble", me);
1271
      return 1;
1272
    }
1273
  } else {
1274
    if (_limnPolyDataVertexWindingProcess(pld, AIR_FALSE)
1275
        || _limnPolyDataVertexWindingProcess(pld, AIR_TRUE)) {
1276
      biffAddf(LIMN, "%s: trouble", me);
1277
      return 1;
1278
    }
1279
  }
1280
  return 0;
1281
}
1282
1283
int
1284
limnPolyDataCCFind(limnPolyData *pld) {
1285
  static const char me[]="limnPolyDataCCFind";
1286
  unsigned int realTriNum, *triMap, *triWithVert, vertIdx,
1287
    *indxOld, *indxNew, primNumOld, *icntOld, *icntNew, *baseIndx,
1288
    primIdxNew, primNumNew, passIdx, eqvNum=0;
1289
  unsigned char *typeOld, *typeNew;
1290
  Nrrd *nTriWithVert, *nccSize, *nTriMap;
1291
  airArray *mop, *eqvArr;
1292
1293
  if (!pld) {
1294
    biffAddf(LIMN, "%s: got NULL pointer", me);
1295
    return 1;
1296
  }
1297
  if (!(pld->xyzwNum && pld->primNum)) {
1298
    /* this is empty? */
1299
    return 0;
1300
  }
1301
1302
  if ((1 << limnPrimitiveTriangles) != limnPolyDataPrimitiveTypes(pld)) {
1303
    biffAddf(LIMN, "%s: sorry, can only handle %s primitives", me,
1304
             airEnumStr(limnPrimitive, limnPrimitiveTriangles));
1305
    return 1;
1306
  }
1307
1308
  mop = airMopNew();
1309
1310
  realTriNum = limnPolyDataPolygonNumber(pld);
1311
1312
  eqvArr = airArrayNew(NULL, NULL, 2*sizeof(unsigned int),
1313
                       /* this is only a heuristic */ pld->xyzwNum);
1314
  airMopAdd(mop, eqvArr, (airMopper)airArrayNuke, airMopAlways);
1315
1316
  nTriWithVert = nrrdNew();
1317
  airMopAdd(mop, nTriWithVert, (airMopper)nrrdNuke, airMopAlways);
1318
  if (triangleWithVertex(nTriWithVert, pld)) {
1319
    biffAddf(LIMN, "%s: couldn't set nTriWithVert", me);
1320
    airMopError(mop); return 1;
1321
  }
1322
1323
  /* simple profiling showed that stupid amount of time was spent
1324
     adding the equivalences.  So we go in two passes- first two see
1325
     how many equivalences are needed, and then actually adding them */
1326
  /* yea, so, its like you don't really even need an airArray ... */
1327
  triWithVert = AIR_CAST(unsigned int*, nTriWithVert->data);
1328
  for (passIdx=0; passIdx<2; passIdx++) {
1329
    if (0 == passIdx) {
1330
      eqvNum = 0;
1331
    } else {
1332
      airArrayLenPreSet(eqvArr, eqvNum);
1333
    }
1334
    for (vertIdx=0; vertIdx<nTriWithVert->axis[1].size; vertIdx++) {
1335
      unsigned int *triLine, triIdx;
1336
      triLine = triWithVert + vertIdx*(nTriWithVert->axis[0].size);
1337
      for (triIdx=1; triIdx<triLine[0]; triIdx++) {
1338
        if (0 == passIdx) {
1339
          ++eqvNum;
1340
        } else {
1341
          airEqvAdd(eqvArr, triLine[1], triLine[1+triIdx]);
1342
        }
1343
      }
1344
    }
1345
  }
1346
1347
  nTriMap = nrrdNew();
1348
  airMopAdd(mop, nTriMap, (airMopper)nrrdNuke, airMopAlways);
1349
  nccSize = nrrdNew();
1350
  airMopAdd(mop, nccSize, (airMopper)nrrdNuke, airMopAlways);
1351
  if (nrrdMaybeAlloc_va(nTriMap, nrrdTypeUInt, 1,
1352
                        AIR_CAST(size_t, realTriNum))) {
1353
    biffMovef(LIMN, NRRD, "%s: couldn't allocate equivalence map", me);
1354
    airMopError(mop); return 1;
1355
  }
1356
  triMap = AIR_CAST(unsigned int*, nTriMap->data);
1357
  primNumNew = airEqvMap(eqvArr, triMap, realTriNum);
1358
  if (nrrdHisto(nccSize, nTriMap, NULL, NULL, primNumNew, nrrdTypeUInt)) {
1359
    biffMovef(LIMN, NRRD, "%s: couldn't histogram CC map", me);
1360
    airMopError(mop); return 1;
1361
  }
1362
1363
  /* indxNumOld == indxNumNew */
1364
  indxOld = pld->indx;
1365
  primNumOld = pld->primNum;
1366
  if (1 != primNumOld) {
1367
    biffAddf(LIMN, "%s: sorry! stupid implementation can't "
1368
             "do primNum %u (only 1)",
1369
             me, primNumOld);
1370
    airMopError(mop); return 1;
1371
  }
1372
  typeOld = pld->type;
1373
  icntOld = pld->icnt;
1374
  indxNew = AIR_CAST(unsigned int*,
1375
                     calloc(pld->indxNum, sizeof(unsigned int)));
1376
  typeNew = AIR_CAST(unsigned char*,
1377
                     calloc(primNumNew, sizeof(unsigned char)));
1378
  icntNew = AIR_CAST(unsigned int*,
1379
                     calloc(primNumNew, sizeof(unsigned int)));
1380
  if (!(indxNew && typeNew && icntNew)) {
1381
    biffAddf(LIMN, "%s: couldn't allocate new polydata arrays", me);
1382
    airMopError(mop); return 1;
1383
  }
1384
  pld->indx = indxNew;
1385
  pld->primNum = primNumNew;
1386
  pld->type = typeNew;
1387
  pld->icnt = icntNew;
1388
  airMopAdd(mop, indxOld, airFree, airMopAlways);
1389
  airMopAdd(mop, typeOld, airFree, airMopAlways);
1390
  airMopAdd(mop, icntOld, airFree, airMopAlways);
1391
1392
  /* this multi-pass thing is really stupid
1393
     (and assumes stupid primNumOld = 1) */
1394
  baseIndx = pld->indx;
1395
  for (primIdxNew=0; primIdxNew<pld->primNum; primIdxNew++) {
1396
    unsigned int realTriIdx;
1397
    pld->type[primIdxNew] = limnPrimitiveTriangles;
1398
    pld->icnt[primIdxNew] = 0;
1399
    for (realTriIdx=0; realTriIdx<realTriNum; realTriIdx++) {
1400
      if (triMap[realTriIdx] == primIdxNew) {
1401
        ELL_3V_COPY(baseIndx, indxOld + 3*realTriIdx);
1402
        baseIndx += 3;
1403
        pld->icnt[primIdxNew] += 3;
1404
      }
1405
    }
1406
  }
1407
1408
  airMopOkay(mop);
1409
  return 0;
1410
}
1411
1412
int
1413
limnPolyDataPrimitiveSort(limnPolyData *pld, const Nrrd *_nval) {
1414
  static const char me[]="limnPolyDataPrimitiveSort";
1415
  Nrrd *nval, *nrec;
1416
  const Nrrd *ntwo[2];
1417
  airArray *mop;
1418
  double *rec;
1419
  unsigned int primIdx, **startIndx, *indxNew, *baseIndx, *icntNew;
1420
  unsigned char *typeNew;
1421
  int E;
1422
1423
  if (!(pld && _nval)) {
1424
    biffAddf(LIMN, "%s: got NULL pointer", me);
1425
    return 1;
1426
  }
1427
  if (!(1 == _nval->dim
1428
        && nrrdTypeBlock != _nval->type
1429
        && _nval->axis[0].size == pld->primNum)) {
1430
    biffAddf(LIMN, "%s: need 1-D %u-len scalar nrrd "
1431
             "(not %u-D type %s, axis[0].size %u)", me,
1432
             pld->primNum,
1433
             _nval->dim, airEnumStr(nrrdType, _nval->type),
1434
             AIR_CAST(unsigned int, _nval->axis[0].size));
1435
    return 1;
1436
  }
1437
1438
  mop = airMopNew();
1439
  nval = nrrdNew();
1440
  airMopAdd(mop, nval, (airMopper)nrrdNuke, airMopAlways);
1441
  nrec = nrrdNew();
1442
  airMopAdd(mop, nrec, (airMopper)nrrdNuke, airMopAlways);
1443
  E = 0;
1444
  if (!E) E |= nrrdConvert(nval, _nval, nrrdTypeDouble);
1445
  ntwo[0] = nval;
1446
  ntwo[1] = nval;
1447
  if (!E) E |= nrrdJoin(nrec, ntwo, 2, 0, AIR_TRUE);
1448
  if (E) {
1449
    biffMovef(LIMN, NRRD, "%s: problem creating records", me);
1450
    airMopError(mop); return 1;
1451
  }
1452
  rec = AIR_CAST(double *, nrec->data);
1453
  for (primIdx=0; primIdx<pld->primNum; primIdx++) {
1454
    rec[1 + 2*primIdx] = primIdx;
1455
  }
1456
  qsort(rec, pld->primNum, 2*sizeof(double),
1457
        nrrdValCompareInv[nrrdTypeDouble]);
1458
1459
  startIndx = AIR_CAST(unsigned int**, calloc(pld->primNum,
1460
                                              sizeof(unsigned int*)));
1461
  indxNew = AIR_CAST(unsigned int*, calloc(pld->indxNum,
1462
                                           sizeof(unsigned int)));
1463
  icntNew = AIR_CAST(unsigned int*, calloc(pld->primNum,
1464
                                           sizeof(unsigned int)));
1465
  typeNew = AIR_CAST(unsigned char*, calloc(pld->primNum,
1466
                                            sizeof(unsigned char)));
1467
  if (!(startIndx && indxNew && icntNew && typeNew)) {
1468
    biffAddf(LIMN, "%s: couldn't allocated temp buffers", me);
1469
    airMopError(mop); return 1;
1470
  }
1471
  airMopAdd(mop, startIndx, airFree, airMopAlways);
1472
1473
  baseIndx = pld->indx;
1474
  for (primIdx=0; primIdx<pld->primNum; primIdx++) {
1475
    startIndx[primIdx] = baseIndx;
1476
    baseIndx += pld->icnt[primIdx];
1477
  }
1478
  baseIndx = indxNew;
1479
  for (primIdx=0; primIdx<pld->primNum; primIdx++) {
1480
    unsigned int sortIdx;
1481
    sortIdx = AIR_CAST(unsigned int, rec[1 + 2*primIdx]);
1482
    memcpy(baseIndx, startIndx[sortIdx],
1483
           pld->icnt[sortIdx]*sizeof(unsigned int));
1484
    icntNew[primIdx] = pld->icnt[sortIdx];
1485
    typeNew[primIdx] = pld->type[sortIdx];
1486
    baseIndx += pld->icnt[sortIdx];
1487
  }
1488
1489
  airFree(pld->indx);
1490
  pld->indx = indxNew;
1491
  airFree(pld->type);
1492
  pld->type = typeNew;
1493
  airFree(pld->icnt);
1494
  pld->icnt = icntNew;
1495
1496
  airMopOkay(mop);
1497
  return 0;
1498
}
1499
1500
int
1501
limnPolyDataVertexWindingFlip(limnPolyData *pld) {
1502
  static const char me[]="limnPolyDataVertexWindingFlip";
1503
  unsigned int baseVertIdx, primIdx;
1504
1505
  if (!pld) {
1506
    biffAddf(LIMN, "%s: got NULL pointer", me);
1507
    return 1;
1508
  }
1509
  if ((1 << limnPrimitiveTriangles) != limnPolyDataPrimitiveTypes(pld)) {
1510
    biffAddf(LIMN, "%s: sorry, can only handle %s primitives", me,
1511
             airEnumStr(limnPrimitive, limnPrimitiveTriangles));
1512
    return 1;
1513
  }
1514
1515
  baseVertIdx = 0;
1516
  for (primIdx=0; primIdx<pld->primNum; primIdx++) {
1517
    unsigned int triNum, triIdx, *indxLine, tmpIdx;
1518
    triNum = pld->icnt[primIdx]/3;
1519
    for (triIdx=0; triIdx<triNum; triIdx++) {
1520
      indxLine = pld->indx + baseVertIdx + 3*triIdx;
1521
      ELL_SWAP2(indxLine[0], indxLine[2], tmpIdx);
1522
    }
1523
    baseVertIdx += pld->icnt[primIdx];
1524
  }
1525
1526
  return 0;
1527
}
1528
1529
int
1530
limnPolyDataPrimitiveSelect(limnPolyData *pldOut,
1531
                            const limnPolyData *pldIn,
1532
                            const Nrrd *_nmask) {
1533
  static const char me[]="limnPolyDataPrimitiveSelect";
1534
  Nrrd *nmask;
1535
  double *mask;
1536
  unsigned int oldBaseVertIdx, oldPrimIdx, oldVertIdx, bitflag,
1537
    *old2newMap, *new2oldMap,
1538
    newPrimNum, newBaseVertIdx, newPrimIdx, newIndxNum, newVertIdx, newVertNum;
1539
  unsigned char *vertUsed;
1540
  airArray *mop;
1541
1542
  if (!(pldOut && pldIn && _nmask)) {
1543
    biffAddf(LIMN, "%s: got NULL pointer", me);
1544
    return 1;
1545
  }
1546
  if (!(1 == _nmask->dim
1547
        && nrrdTypeBlock != _nmask->type
1548
        && _nmask->axis[0].size == pldIn->primNum)) {
1549
    biffAddf(LIMN, "%s: need 1-D %u-len scalar nrrd "
1550
             "(not %u-D type %s, axis[0].size %u)", me,
1551
             pldIn->primNum, _nmask->dim, airEnumStr(nrrdType, _nmask->type),
1552
             AIR_CAST(unsigned int, _nmask->axis[0].size));
1553
    return 1;
1554
  }
1555
1556
  mop = airMopNew();
1557
  nmask = nrrdNew();
1558
  airMopAdd(mop, nmask, (airMopper)nrrdNuke, airMopAlways);
1559
  if (nrrdConvert(nmask, _nmask, nrrdTypeDouble)) {
1560
    biffMovef(LIMN, NRRD, "%s: trouble converting mask to %s", me,
1561
              airEnumStr(nrrdType, nrrdTypeDouble));
1562
    return 1;
1563
  }
1564
  mask = AIR_CAST(double *, nmask->data);
1565
1566
  old2newMap = AIR_CAST(unsigned int *, calloc(pldIn->xyzwNum,
1567
                                               sizeof(unsigned int)));
1568
  airMopAdd(mop, old2newMap, airFree, airMopAlways);
1569
  vertUsed = AIR_CAST(unsigned char *, calloc(pldIn->xyzwNum,
1570
                                              sizeof(unsigned char)));
1571
  airMopAdd(mop, vertUsed, airFree, airMopAlways);
1572
1573
  /* initialize all verts as unused */
1574
  for (oldVertIdx=0; oldVertIdx<pldIn->xyzwNum; oldVertIdx++) {
1575
    vertUsed[oldVertIdx] = AIR_FALSE;
1576
  }
1577
  /* mark the used verts, and count # new indices and primitives  */
1578
  oldBaseVertIdx = 0;
1579
  newPrimNum = 0;
1580
  newIndxNum = 0;
1581
  for (oldPrimIdx=0; oldPrimIdx<pldIn->primNum; oldPrimIdx++) {
1582
    unsigned indxIdx;
1583
    if (mask[oldPrimIdx]) {
1584
      for (indxIdx=0; indxIdx<pldIn->icnt[oldPrimIdx]; indxIdx++) {
1585
        vertUsed[(pldIn->indx + oldBaseVertIdx)[indxIdx]] = AIR_TRUE;
1586
      }
1587
      newIndxNum += pldIn->icnt[oldPrimIdx];
1588
      newPrimNum++;
1589
    }
1590
    oldBaseVertIdx += pldIn->icnt[oldPrimIdx];
1591
  }
1592
  /* count the used verts, and set up map from old to new indices */
1593
  newVertNum = 0;
1594
  for (oldVertIdx=0; oldVertIdx<pldIn->xyzwNum; oldVertIdx++) {
1595
    if (vertUsed[oldVertIdx]) {
1596
      old2newMap[oldVertIdx] = newVertNum++;
1597
    }
1598
  }
1599
  /* allocate and fill reverse map */
1600
  new2oldMap = AIR_CAST(unsigned int *, calloc(newVertNum,
1601
                                               sizeof(unsigned int)));
1602
  airMopAdd(mop, new2oldMap, airFree, airMopAlways);
1603
  newVertIdx = 0;
1604
  for (oldVertIdx=0; oldVertIdx<pldIn->xyzwNum; oldVertIdx++) {
1605
    if (vertUsed[oldVertIdx]) {
1606
      new2oldMap[newVertIdx++] = oldVertIdx;
1607
    }
1608
  }
1609
1610
  /* allocate output polydata */
1611
  bitflag = limnPolyDataInfoBitFlag(pldIn);
1612
  if (limnPolyDataAlloc(pldOut, bitflag, newVertNum, newIndxNum, newPrimNum)) {
1613
    biffAddf(LIMN, "%s: trouble allocating output", me);
1614
    return 1;
1615
  }
1616
1617
  /* transfer per-primitive information from old to new */
1618
  oldBaseVertIdx = 0;
1619
  newBaseVertIdx = 0;
1620
  newPrimIdx = 0;
1621
  for (oldPrimIdx=0; oldPrimIdx<pldIn->primNum; oldPrimIdx++) {
1622
    if (mask[oldPrimIdx]) {
1623
      unsigned indxIdx;
1624
      pldOut->icnt[newPrimIdx] = pldIn->icnt[oldPrimIdx];
1625
      pldOut->type[newPrimIdx] = pldIn->type[oldPrimIdx];
1626
      for (indxIdx=0; indxIdx<pldIn->icnt[oldPrimIdx]; indxIdx++) {
1627
        oldVertIdx = (pldIn->indx + oldBaseVertIdx)[indxIdx];
1628
        (pldOut->indx + newBaseVertIdx)[indxIdx] = old2newMap[oldVertIdx];
1629
      }
1630
      newBaseVertIdx += pldIn->icnt[oldPrimIdx];
1631
      newPrimIdx++;
1632
    }
1633
    oldBaseVertIdx += pldIn->icnt[oldPrimIdx];
1634
  }
1635
  /* transfer per-vertex info */
1636
  for (newVertIdx=0; newVertIdx<newVertNum; newVertIdx++) {
1637
    oldVertIdx = new2oldMap[newVertIdx];
1638
    ELL_4V_COPY(pldOut->xyzw + 4*newVertIdx, pldIn->xyzw + 4*oldVertIdx);
1639
    if ((1 << limnPolyDataInfoRGBA) & bitflag) {
1640
      ELL_4V_COPY(pldOut->rgba + 4*newVertIdx, pldIn->rgba + 4*oldVertIdx);
1641
    }
1642
    if ((1 << limnPolyDataInfoNorm) & bitflag) {
1643
      ELL_3V_COPY(pldOut->norm + 3*newVertIdx, pldIn->norm + 3*oldVertIdx);
1644
    }
1645
    if ((1 << limnPolyDataInfoTex2) & bitflag) {
1646
      ELL_3V_COPY(pldOut->tex2 + 2*newVertIdx, pldIn->tex2 + 2*oldVertIdx);
1647
    }
1648
    if ((1 << limnPolyDataInfoTang) & bitflag) {
1649
      ELL_3V_COPY(pldOut->tang + 3*newVertIdx, pldIn->tang + 3*oldVertIdx);
1650
    }
1651
  }
1652
1653
  airMopOkay(mop);
1654
  return 0;
1655
}
1656
1657
/* Helper function for limnPolyDataClipMulti - clips the edge between
1658
 * disc and kept that partially fulfills the thresholds and maintains
1659
 * a data structure that keeps track of edges we have clipped already,
1660
 * to avoid creating duplicate vertices.
1661
 */
1662
static int
1663
clipEdge(int disc, int kept, Nrrd *nval, double *thresh, int *newIdx,
1664
         airArray *llistArr, limnPolyData *pld, unsigned int bitflag,
1665
         limnPolyData *newpld, airArray *xyzwArr, airArray *rgbaArr,
1666
         airArray *normArr, airArray *tex2Arr, airArray *tangArr) {
1667
  int ref=-1, *llist=(int*)llistArr->data;
1668
  int next=newIdx[disc];
1669
  double alpha=0;
1670
  unsigned int i,q,p,nk;
1671
  double (*lup)(const void *v, size_t I)=nrrdDLookup[nval->type];
1672
  /* check if we clipped the edge previously */
1673
  while (next!=-1) {
1674
    if (llist[next]==kept) /* found the desired vertex */
1675
      return llist[next+1];
1676
    ref=next+2;
1677
    next=llist[next+2];
1678
  }
1679
  /* we need to interpolate - find the weight */
1680
  nk=(nval->dim==1)?1:nval->axis[0].size;
1681
  for (i=0; i<nk; i++) {
1682
    double discval = lup(nval->data, nk*disc+i);
1683
    double keptval = lup(nval->data, nk*kept+i);
1684
    double thisalpha = AIR_AFFINE(discval,thresh[i],keptval,0.0,1.0);
1685
    if (thisalpha<1.0 && thisalpha>alpha)
1686
      alpha=thisalpha;
1687
  }
1688
  /* add interpolated vertex */
1689
  q=airArrayLenIncr(xyzwArr, 1);
1690
  ELL_4V_LERP_TT(newpld->xyzw+4*q, float, alpha, pld->xyzw+4*disc, pld->xyzw+4*kept);
1691
  if ((1 << limnPolyDataInfoRGBA) & bitflag) {
1692
    airArrayLenIncr(rgbaArr, 1);
1693
    ELL_4V_LERP_TT(newpld->rgba+4*q, unsigned char, alpha, pld->rgba+4*disc, pld->rgba+4*kept);
1694
  }
1695
  if ((1 << limnPolyDataInfoNorm) & bitflag) {
1696
    float fnorm[3];
1697
    double len;
1698
    /* take special care to treat non-orientable surface normals correctly */
1699
    if (ELL_3V_DOT(pld->norm+3*disc, pld->norm+3*kept)<0) {
1700
      ELL_3V_SCALE_TT(fnorm, float, -1.0, pld->norm+3*kept);
1701
    } else {
1702
      ELL_3V_COPY(fnorm, pld->norm+3*kept);
1703
    }
1704
    airArrayLenIncr(normArr, 1);
1705
    ELL_3V_LERP_TT(newpld->norm+3*q, float, alpha, pld->norm+3*disc, fnorm);
1706
    /* re-normalize */
1707
    len=ELL_3V_LEN(newpld->norm+3*q);
1708
    if (len>1e-20) {
1709
      ELL_3V_SCALE_TT(newpld->norm+3*q, float, 1.0/len, newpld->norm+3*q);
1710
    }
1711
  }
1712
  if ((1 << limnPolyDataInfoTex2) & bitflag) {
1713
    airArrayLenIncr(tex2Arr, 1);
1714
    ELL_2V_LERP_TT(newpld->tex2+2*q, float, alpha, pld->tex2+2*disc, pld->tex2+2*kept);
1715
  }
1716
  if ((1 << limnPolyDataInfoTang) & bitflag) {
1717
    airArrayLenIncr(tangArr, 1);
1718
    ELL_3V_LERP_TT(newpld->tang+3*q, float, alpha, pld->tang+3*disc, pld->tang+3*kept);
1719
  }
1720
  /* add new vertex to linked list */
1721
  p=airArrayLenIncr(llistArr, 1);
1722
  llist=(int*)llistArr->data; /* update in case of re-allocation */
1723
  llist[3*p]=kept;
1724
  llist[3*p+1]=q;
1725
  llist[3*p+2]=-1;
1726
  if (ref==-1) newIdx[disc]=3*p;
1727
  else llist[ref]=3*p;
1728
  return q;
1729
}
1730
1731
/*
1732
 * Clips the given triangles (limnPrimitiveTriangles) according to the
1733
 * input matrix nval and the threshold array thresh. First axis of
1734
 * nval are different clipping criteria, second axis are vertex
1735
 * indices. The length of thresh has to equal the size of the first
1736
 * axis, the vertex count in pld has to equal the size of the second
1737
 * axis. If nval is 1D, it is assumed to have a single criterion.
1738
 *
1739
 * A vertex is preserved if all values are >= the respective
1740
 * threshold; triangles with partially discarded vertices are clipped,
1741
 * potentially generating a quad that is then triangulated arbitrarily.
1742
 */
1743
int
1744
limnPolyDataClipMulti(limnPolyData *pld, Nrrd *nval, double *thresh) {
1745
  static const char me[]="limnPolyDataClipMulti";
1746
  unsigned char *keepVert=NULL;
1747
  airArray *mop;
1748
  unsigned int E, i, idx=0;
1749
  double (*lup)(const void *v, size_t I);
1750
  airArray *xyzwArr, *rgbaArr=NULL, *normArr=NULL, *tex2Arr=NULL,
1751
    *tangArr=NULL, *indxArr, *typeArr, *icntArr, *llistArr=NULL;
1752
  limnPolyData *newpld=NULL;
1753
  int *newIdx=NULL, *llist=NULL;
1754
  unsigned int bitflag, nk, nvert;
1755
  airPtrPtrUnion appu;
1756
1757
  if (!(pld && nval)) {
1758
    biffAddf(LIMN, "%s: got NULL pointer", me);
1759
    return 1;
1760
  }
1761
1762
  if (nrrdTypeBlock == nval->type) {
1763
    biffAddf(LIMN, "%s: need scalar type (not %s)", me,
1764
             airEnumStr(nrrdType, nval->type));
1765
    return 1;
1766
  }
1767
1768
  if (nval->dim==1) {
1769
    nk=1; nvert=nval->axis[0].size;
1770
  } else if (nval->dim==2) {
1771
    nk=nval->axis[0].size; nvert=nval->axis[1].size;
1772
  } else {
1773
    biffAddf(LIMN, "%s: need 1D or 2D input array, got %uD", me, nval->dim);
1774
    return 1;
1775
  }
1776
  if (nvert!=pld->xyzwNum) {
1777
    biffAddf(LIMN, "%s: # verts %u != # values %u", me,
1778
             pld->xyzwNum, nvert);
1779
    return 1;
1780
  }
1781
  if ((1 << limnPrimitiveTriangles) != limnPolyDataPrimitiveTypes(pld)) {
1782
    biffAddf(LIMN, "%s: sorry, can only handle %s primitives", me,
1783
             airEnumStr(limnPrimitive, limnPrimitiveTriangles));
1784
    return 1;
1785
  }
1786
1787
  /* Memory allocation in C IS a headache */
1788
  mop=airMopNew();
1789
  E = AIR_FALSE;
1790
  if (!E) {
1791
    E|=!(keepVert = AIR_CAST(unsigned char *,
1792
                             calloc(pld->xyzwNum, sizeof(char))));
1793
  }
1794
  if (!E) {
1795
    airMopAdd(mop, keepVert, airFree, airMopAlways);
1796
    E|=!(newIdx = AIR_CAST(int *, malloc(pld->xyzwNum*sizeof(int))));
1797
  }
1798
  if (!E) {
1799
    unsigned int incr;
1800
    airMopAdd(mop, newIdx, airFree, airMopAlways);
1801
    memset(newIdx, -1, sizeof(int)*pld->xyzwNum);
1802
    /* This setting of incr is arbitrary and was not optimized in any way: */
1803
    incr = pld->xyzwNum/10; /* 10% of previous vertex count... */
1804
    if (incr<50) incr=50; /* ...but at least 50. */
1805
    appu.i = &llist;
1806
    E|=!(llistArr=airArrayNew(appu.v, NULL, 3*sizeof(int), incr));
1807
  }
1808
  if (!E) {
1809
    airMopAdd(mop, llistArr, (airMopper)airArrayNuke, airMopAlways);
1810
    E|=!(newpld = limnPolyDataNew());
1811
  }
1812
  bitflag = limnPolyDataInfoBitFlag(pld);
1813
  if (!E) {
1814
    unsigned int incr;
1815
    airMopAdd(mop, newpld, airFree, airMopAlways); /* "shallow" free */
1816
    incr = pld->xyzwNum/20; /* 5% of previous vertex count... */
1817
    if (incr<10) incr=10; /* ...but at least 10. */
1818
    appu.f = &(newpld->xyzw);
1819
    E|=!(xyzwArr=airArrayNew(appu.v, &(newpld->xyzwNum),
1820
                             4*sizeof(float), incr));
1821
    if (!E) {
1822
      airMopAdd(mop, xyzwArr, (airMopper)airArrayNuke, airMopOnError);
1823
      airMopAdd(mop, xyzwArr, (airMopper)airArrayNix, airMopOnOkay);
1824
    }
1825
    if (!E && (1 << limnPolyDataInfoRGBA) & bitflag) {
1826
      appu.uc = &(newpld->rgba);
1827
      E|=!(rgbaArr=airArrayNew(appu.v, &(newpld->rgbaNum),
1828
                               4*sizeof(unsigned char), incr));
1829
      if (!E) {
1830
        airMopAdd(mop, rgbaArr, (airMopper)airArrayNuke, airMopOnError);
1831
        airMopAdd(mop, rgbaArr, (airMopper)airArrayNix, airMopOnOkay);
1832
      }
1833
    }
1834
    if (!E && (1 << limnPolyDataInfoNorm) & bitflag) {
1835
      appu.f = &(newpld->norm);
1836
      E|=!(normArr=airArrayNew(appu.v, &(newpld->normNum),
1837
                               3*sizeof(float), incr));
1838
      if (!E) {
1839
        airMopAdd(mop, normArr, (airMopper)airArrayNuke, airMopOnError);
1840
        airMopAdd(mop, normArr, (airMopper)airArrayNix, airMopOnOkay);
1841
      }
1842
    }
1843
    if (!E && (1 << limnPolyDataInfoTex2) & bitflag) {
1844
      appu.f = &(newpld->tex2);
1845
      E|=!(tex2Arr=airArrayNew(appu.v, &(newpld->tex2Num),
1846
                               2*sizeof(float), incr));
1847
      if (!E) {
1848
        airMopAdd(mop, tex2Arr, (airMopper)airArrayNuke, airMopOnError);
1849
        airMopAdd(mop, tex2Arr, (airMopper)airArrayNix, airMopOnOkay);
1850
      }
1851
    }
1852
    if (!E && (1 << limnPolyDataInfoTang) & bitflag) {
1853
      appu.f = &(newpld->tang);
1854
      E|=!(tangArr=airArrayNew(appu.v, &(newpld->tangNum),
1855
                               3*sizeof(float), incr));
1856
      if (!E) {
1857
        airMopAdd(mop, tangArr, (airMopper)airArrayNuke, airMopOnError);
1858
        airMopAdd(mop, tangArr, (airMopper)airArrayNix, airMopOnOkay);
1859
      }
1860
    }
1861
    if (!E) {
1862
      incr = pld->indxNum/20; /* 5% of previous index count... */
1863
      if (incr<10) incr=10; /* ...but at least 10. */
1864
      appu.ui = &(newpld->indx);
1865
      E|=!(indxArr=airArrayNew(appu.v, &(newpld->indxNum),
1866
                               sizeof(unsigned int), incr));
1867
      if (!E) {
1868
        airMopAdd(mop, indxArr, (airMopper)airArrayNuke, airMopOnError);
1869
        airMopAdd(mop, indxArr, (airMopper)airArrayNix, airMopOnOkay);
1870
      }
1871
    }
1872
    if (!E) {
1873
      incr = pld->primNum/10; /* 10% of previous primNum... */
1874
      if (incr<1) incr=1; /* ...but at least 1. */
1875
      appu.uc = &(newpld->type);
1876
      E|=!(typeArr=airArrayNew(appu.v, &(newpld->primNum),
1877
                               sizeof(unsigned char), incr));
1878
      if (!E) {
1879
        airMopAdd(mop, typeArr, (airMopper)airArrayNuke, airMopOnError);
1880
        airMopAdd(mop, typeArr, (airMopper)airArrayNix, airMopOnOkay);
1881
      }
1882
      appu.ui = &(newpld->icnt);
1883
      E|=!(icntArr=airArrayNew(appu.v, NULL,
1884
                               sizeof(unsigned int), incr));
1885
      if (!E) {
1886
        airMopAdd(mop, icntArr, (airMopper)airArrayNuke, airMopOnError);
1887
        airMopAdd(mop, icntArr, (airMopper)airArrayNix, airMopOnOkay);
1888
      }
1889
    }
1890
  }
1891
  if (E) {
1892
    biffAddf(LIMN, "%s: couldn't allocate buffers", me);
1893
    airMopError(mop); return 1;
1894
  }
1895
1896
  /* mark vertices, leaving at 0 means "discard" */
1897
  lup = nrrdDLookup[nval->type];
1898
  for (i=0; i<pld->xyzwNum; i++) {
1899
    unsigned int j, keep = AIR_TRUE;
1900
    for (j=0; j<nk; j++, idx++) {
1901
      if (lup(nval->data, idx) < thresh[j])
1902
        keep = AIR_FALSE;
1903
    }
1904
    if (keep) {
1905
      keepVert[i]=AIR_TRUE;
1906
    }
1907
  }
1908
1909
  /* now, iterate over all primitives and triangles */
1910
1911
  /* Note: If keepVert[i]==AIR_TRUE, newIdx[i] is its new index; else, it is
1912
   * an index j into llist, which is a linked list:
1913
   * llist[j] == other (kept) end of the edge
1914
   * llist[j+1] == index of new vertex for that edge
1915
   * llist[j+2] == next index into llist
1916
   */
1917
1918
  /* TODO: All the airArray stuff should have allocation error checking */
1919
  idx=0;
1920
  for (i=0; i<pld->primNum; i++) {
1921
    int j, oldTriNum=pld->icnt[i]/3, newTriNum=0;
1922
    unsigned int kept=0, disck=0; /* index of last kept / discarded vertex */
1923
    for (j=0; j<oldTriNum; j++, idx+=3) {
1924
      unsigned int p, quad[4];
1925
      int k, keepN=0;
1926
      for (k=0; k<3; k++) {
1927
        unsigned int oldidx=pld->indx[idx+k];
1928
        if (keepVert[oldidx]) {
1929
          keepN++; kept=oldidx;
1930
          /* make sure the vertex is copied over */
1931
          if (newIdx[oldidx]==-1) {
1932
            unsigned int q=newIdx[oldidx]=airArrayLenIncr(xyzwArr, 1);
1933
            ELL_4V_COPY(newpld->xyzw+4*q, pld->xyzw+4*oldidx);
1934
            if ((1 << limnPolyDataInfoRGBA) & bitflag) {
1935
              airArrayLenIncr(rgbaArr, 1);
1936
              ELL_4V_COPY(newpld->rgba+4*q, pld->rgba+4*oldidx);
1937
            }
1938
            if ((1 << limnPolyDataInfoNorm) & bitflag) {
1939
              airArrayLenIncr(normArr, 1);
1940
              ELL_3V_COPY(newpld->norm+3*q, pld->norm+3*oldidx);
1941
            }
1942
            if ((1 << limnPolyDataInfoTex2) & bitflag) {
1943
              airArrayLenIncr(tex2Arr, 1);
1944
              ELL_2V_COPY(newpld->tex2+2*q, pld->tex2+2*oldidx);
1945
            }
1946
            if ((1 << limnPolyDataInfoTang) & bitflag) {
1947
              airArrayLenIncr(tangArr, 1);
1948
              ELL_3V_COPY(newpld->tang+3*q, pld->tang+3*oldidx);
1949
            }
1950
          }
1951
        } else {
1952
          disck=k;
1953
        }
1954
      }
1955
      switch (keepN) {
1956
      case 0: /* nothing to be done; discard this triangle */
1957
        break;
1958
      case 1: /* result of clipping is a single triangle */
1959
        newTriNum++;
1960
        p=airArrayLenIncr(indxArr, 3);
1961
        for (k=0; k<3; k++) {
1962
          if (keepVert[pld->indx[idx+k]])
1963
            newpld->indx[p+k]=newIdx[pld->indx[idx+k]];
1964
          else
1965
            newpld->indx[p+k]=clipEdge(pld->indx[idx+k], kept, nval, thresh,
1966
                                       newIdx, llistArr, pld, bitflag, newpld,
1967
                                       xyzwArr, rgbaArr, normArr,
1968
                                       tex2Arr, tangArr);
1969
        }
1970
        break;
1971
      case 2: /* result of clipping is a quad, triangulate */
1972
        newTriNum+=2;
1973
        p=0;
1974
        for (k=0; k<3; k++) {
1975
          if (keepVert[pld->indx[idx+k]]) quad[p++]=newIdx[pld->indx[idx+k]];
1976
          else {
1977
            quad[p++]=clipEdge(pld->indx[idx+k], pld->indx[idx+(disck+2)%3],
1978
                               nval, thresh, newIdx, llistArr,
1979
                               pld, bitflag, newpld, xyzwArr, rgbaArr,
1980
                               normArr, tex2Arr, tangArr);
1981
            quad[p++]=clipEdge(pld->indx[idx+k], pld->indx[idx+(disck+1)%3],
1982
                               nval, thresh, newIdx, llistArr,
1983
                               pld, bitflag, newpld, xyzwArr, rgbaArr,
1984
                               normArr, tex2Arr, tangArr);
1985
          }
1986
        }
1987
        p=airArrayLenIncr(indxArr, 6);
1988
        ELL_3V_SET(newpld->indx+p, quad[0], quad[1], quad[3]);
1989
        ELL_3V_SET(newpld->indx+p+3, quad[1], quad[2], quad[3]);
1990
        break;
1991
      case 3: /* simply copy the existing triangle */
1992
        newTriNum++;
1993
        p=airArrayLenIncr(indxArr, 3);
1994
        for (k=0; k<3; k++) {
1995
          newpld->indx[p+k]=newIdx[pld->indx[idx+k]];
1996
        }
1997
        break;
1998
      }
1999
    }
2000
    if (newTriNum>0) {
2001
      unsigned int p=airArrayLenIncr(typeArr, 1);
2002
      airArrayLenIncr(icntArr, 1);
2003
      newpld->type[p]=limnPrimitiveTriangles;
2004
      newpld->icnt[p]=newTriNum*3;
2005
    }
2006
  }
2007
2008
  /* finally, replace contents of pld with new data */
2009
  airFree(pld->xyzw);
2010
  airFree(pld->rgba);
2011
  airFree(pld->norm);
2012
  airFree(pld->tex2);
2013
  airFree(pld->tang);
2014
  airFree(pld->indx);
2015
  airFree(pld->type);
2016
  airFree(pld->icnt);
2017
  memcpy(pld, newpld, sizeof(limnPolyData));
2018
2019
  airMopOkay(mop);
2020
  return 0;
2021
}
2022
2023
/* Simple wrapper around limnPolyDataClipMulti, in case of only one
2024
 * clipping criterion.
2025
 */
2026
int
2027
limnPolyDataClip(limnPolyData *pld, Nrrd *nval, double thresh) {
2028
  return limnPolyDataClipMulti(pld, nval, &thresh);
2029
}
2030
2031
/* limnPolyDataCompress:
2032
 * returns a "compressed" copy of the given limnPolyData pld that only
2033
 *  contains vertices that are referenced by some primitive
2034
 * returns NULL and adds a message to biff upon error
2035
 */
2036
limnPolyData *
2037
limnPolyDataCompress(const limnPolyData *pld) {
2038
  static const char me[]="limnPolyDataCompress";
2039
  limnPolyData *ret = NULL;
2040
  unsigned int infoBitFlag=0, vertNum=0, i, used_indxNum=0;
2041
  int *vertMap;
2042
  if (pld==NULL) {
2043
    biffAddf(LIMN, "%s: got NULL pointer", me);
2044
    return NULL;
2045
  }
2046
  infoBitFlag=limnPolyDataInfoBitFlag(pld);
2047
  vertMap=(int*) calloc(pld->xyzwNum,sizeof(int));
2048
  if (vertMap==NULL) {
2049
    biffAddf(LIMN, "%s: could not allocate memory", me);
2050
    return NULL;
2051
  }
2052
  /* how many indices are actually used? */
2053
  for (i=0; i<pld->primNum; i++) {
2054
    used_indxNum+=pld->icnt[i];
2055
  }
2056
  /* loop over all indices and mark referenced vertices in vertMap */
2057
  for (i=0; i<used_indxNum; i++) {
2058
    vertMap[pld->indx[i]]=1;
2059
  }
2060
  /* turn vertMap into an index map */
2061
  for (i=0; i<pld->xyzwNum; i++) {
2062
    if (vertMap[i]==0)
2063
      vertMap[i]=-1;
2064
    else
2065
      vertMap[i]=vertNum++;
2066
  }
2067
  /* allocate new limnPolyData */
2068
  if (NULL == (ret=limnPolyDataNew()) ||
2069
      0!=limnPolyDataAlloc(ret, infoBitFlag, vertNum,
2070
                           used_indxNum, pld->primNum)) {
2071
    biffAddf(LIMN, "%s: Could not allocate result", me);
2072
    free(vertMap);
2073
    return NULL;
2074
  }
2075
  /* fill the newly allocated structure */
2076
  for (i=0; i<pld->xyzwNum; i++) {
2077
    if (vertMap[i]>=0) {
2078
      ELL_4V_COPY(ret->xyzw+4*vertMap[i], pld->xyzw+4*i);
2079
    }
2080
  }
2081
  if (ret->rgba!=NULL) {
2082
    for (i=0; i<pld->xyzwNum; i++) {
2083
      if (vertMap[i]>=0) {
2084
        ELL_4V_COPY(ret->rgba+4*vertMap[i], pld->rgba+4*i);
2085
      }
2086
    }
2087
  }
2088
  if (ret->norm!=NULL) {
2089
    for (i=0; i<pld->xyzwNum; i++) {
2090
      if (vertMap[i]>=0) {
2091
        ELL_3V_COPY(ret->norm+3*vertMap[i], pld->norm+3*i);
2092
      }
2093
    }
2094
  }
2095
  if (ret->tex2!=NULL) {
2096
    for (i=0; i<pld->xyzwNum; i++) {
2097
      if (vertMap[i]>=0) {
2098
        ELL_2V_COPY(ret->tex2+2*vertMap[i], pld->tex2+2*i);
2099
      }
2100
    }
2101
  }
2102
  if (ret->tang!=NULL) {
2103
    for (i=0; i<pld->xyzwNum; i++) {
2104
      if (vertMap[i]>=0) {
2105
        ELL_3V_COPY(ret->tang+3*vertMap[i], pld->tang+3*i);
2106
      }
2107
    }
2108
  }
2109
  for (i=0; i<used_indxNum; i++) {
2110
    ret->indx[i]=vertMap[pld->indx[i]];
2111
  }
2112
  memcpy(ret->type, pld->type, sizeof(char)*pld->primNum);
2113
  memcpy(ret->icnt, pld->icnt, sizeof(int)*pld->primNum);
2114
2115
  free(vertMap);
2116
2117
  return ret;
2118
}
2119
2120
/* limnPolyDataJoin:
2121
 * concatenates the primitives in all num limnPolyDatas given in plds
2122
 *  and returns the result as a newly allocated limnPolyData
2123
 * the new limnPolyData will only have color/normals/texture coordinates
2124
 *  if _all_ input limnPolyDatas had the respective attribute
2125
 * returns NULL and adds a message to biff upon error
2126
 */
2127
limnPolyData *limnPolyDataJoin(const limnPolyData **plds,
2128
                               unsigned int num) {
2129
  static const char me[]="limnPolyDataJoin";
2130
  limnPolyData *ret = NULL;
2131
  unsigned int infoBitFlag=(1 << limnPolyDataInfoRGBA) |
2132
    (1 << limnPolyDataInfoNorm) |
2133
    (1 << limnPolyDataInfoTex2) |
2134
    (1 << limnPolyDataInfoTang); /* by default, assume we have all these */
2135
  unsigned int vertNum=0, indxNum=0, primNum=0;
2136
  unsigned int i;
2137
  if (plds==NULL) {
2138
    biffAddf(LIMN, "%s: got NULL pointer", me);
2139
    return NULL;
2140
  }
2141
  /* loop over all input plds to find infoBitFlag and the total number of
2142
   * vertices / indices / primitives */
2143
  for (i=0; i<num; i++) {
2144
    if (plds[i]==NULL) {
2145
      biffAddf(LIMN, "%s: plds[%d] is a NULL pointer", me, i);
2146
      return NULL;
2147
    }
2148
    infoBitFlag &= limnPolyDataInfoBitFlag(plds[i]);
2149
    vertNum += plds[i]->xyzwNum;
2150
    indxNum += plds[i]->indxNum;
2151
    primNum += plds[i]->primNum;
2152
  }
2153
  if (NULL == (ret=limnPolyDataNew()) ||
2154
      0!=limnPolyDataAlloc(ret, infoBitFlag, vertNum, indxNum, primNum)) {
2155
    biffAddf(LIMN, "%s: Could not allocate result", me);
2156
    return NULL;
2157
  }
2158
  /* loop again over all input plds and fill the newly allocated structure */
2159
  vertNum=indxNum=primNum=0;
2160
  for (i=0; i<num; i++) {
2161
    unsigned int j, used_indxNum=0;
2162
    memcpy(ret->xyzw+4*vertNum, plds[i]->xyzw,
2163
           sizeof(float)*4*plds[i]->xyzwNum);
2164
    if (ret->rgba!=NULL) {
2165
      memcpy(ret->rgba+4*vertNum, plds[i]->rgba,
2166
             sizeof(unsigned char)*4*plds[i]->xyzwNum);
2167
    }
2168
    if (ret->norm!=NULL) {
2169
      memcpy(ret->norm+3*vertNum, plds[i]->norm,
2170
             sizeof(float)*3*plds[i]->xyzwNum);
2171
    }
2172
    if (ret->tex2!=NULL) {
2173
      memcpy(ret->tex2+2*vertNum, plds[i]->tex2,
2174
             sizeof(float)*2*plds[i]->xyzwNum);
2175
    }
2176
    if (ret->tang!=NULL) {
2177
      memcpy(ret->tang+3*vertNum, plds[i]->tang,
2178
             sizeof(float)*3*plds[i]->xyzwNum);
2179
    }
2180
    for (j=0; j<plds[i]->indxNum; j++) {
2181
      ret->indx[indxNum+j]=vertNum+plds[i]->indx[j];
2182
    }
2183
    for (j=0; j<plds[i]->primNum; j++) {
2184
      ret->type[primNum+j]=plds[i]->type[j];
2185
      ret->icnt[primNum+j]=plds[i]->icnt[j];
2186
      /* need to keep track of how many indices are actually used */
2187
      used_indxNum+=plds[i]->icnt[j];
2188
    }
2189
    vertNum+=plds[i]->xyzwNum;
2190
    indxNum+=used_indxNum;
2191
    primNum+=plds[i]->primNum;
2192
  }
2193
  return ret;
2194
}
2195
2196
int
2197
limnPolyDataEdgeHalve(limnPolyData *pldOut,
2198
                      const limnPolyData *pldIn) {
2199
  static const char me[]="limnPolyDataEdgeHalve";
2200
  Nrrd *nnewvert;
2201
  unsigned int *newvert, nvold, nvidx, triidx, trinum, vlo, vhi, bitflag;
2202
  airArray *mop;
2203
2204
  if ((1 << limnPrimitiveTriangles) != limnPolyDataPrimitiveTypes(pldIn)) {
2205
    biffAddf(LIMN, "%s: sorry, can only handle %s primitives", me,
2206
             airEnumStr(limnPrimitive, limnPrimitiveTriangles));
2207
    return 1;
2208
  }
2209
  if (1 != pldIn->primNum) {
2210
    biffAddf(LIMN, "%s: sorry, can only handle a single primitive", me);
2211
    return 1;
2212
  }
2213
  mop = airMopNew();
2214
  nnewvert = nrrdNew();
2215
  airMopAdd(mop, nnewvert, AIR_CAST(airMopper, nrrdNuke), airMopAlways);
2216
  nvold = pldIn->xyzwNum;
2217
  if (nrrdMaybeAlloc_va(nnewvert, nrrdTypeUInt, 2, nvold, nvold)) {
2218
    biffMovef(LIMN, NRRD, "%s: couldn't allocate buffer", me);
2219
    airMopError(mop); return 1;
2220
  }
2221
  newvert = AIR_CAST(unsigned int*, nnewvert->data);
2222
2223
  /* run through triangles, recording edges with the new vertex index */
2224
  nvidx = nvold;
2225
  trinum = pldIn->indxNum/3;
2226
  for (triidx=0; triidx<trinum; triidx++) {
2227
    vlo = pldIn->indx[0 + 3*triidx];
2228
    vhi = pldIn->indx[1 + 3*triidx];
2229
    if (!newvert[vlo + nvold*vhi]) {
2230
      newvert[vlo + nvold*vhi] = newvert[vhi + nvold*vlo] = nvidx++;
2231
    }
2232
    vlo = pldIn->indx[1 + 3*triidx];
2233
    vhi = pldIn->indx[2 + 3*triidx];
2234
    if (!newvert[vlo + nvold*vhi]) {
2235
      newvert[vlo + nvold*vhi] = newvert[vhi + nvold*vlo] = nvidx++;
2236
    }
2237
    vlo = pldIn->indx[2 + 3*triidx];
2238
    vhi = pldIn->indx[0 + 3*triidx];
2239
    if (!newvert[vlo + nvold*vhi]) {
2240
      newvert[vlo + nvold*vhi] = newvert[vhi + nvold*vlo] = nvidx++;
2241
    }
2242
  }
2243
2244
  /* allocate output */
2245
  bitflag = limnPolyDataInfoBitFlag(pldIn);
2246
  if (limnPolyDataAlloc(pldOut, bitflag, nvidx, 3*4*trinum, 1)) {
2247
    biffAddf(LIMN, "%s: trouble allocating output", me);
2248
    airMopError(mop); return 1;
2249
  }
2250
  pldOut->type[0] = limnPrimitiveTriangles;
2251
  pldOut->icnt[0] = 3*4*trinum;
2252
2253
  /* set output indx */
2254
  for (triidx=0; triidx<trinum; triidx++) {
2255
    unsigned int aa, ab, bb, bc, cc, ac;
2256
    aa = pldIn->indx[0 + 3*triidx];
2257
    bb = pldIn->indx[1 + 3*triidx];
2258
    cc = pldIn->indx[2 + 3*triidx];
2259
    ab = newvert[aa + nvold*bb];
2260
    bc = newvert[bb + nvold*cc];
2261
    ac = newvert[aa + nvold*cc];
2262
    ELL_3V_SET(pldOut->indx + 3*(0 + 4*triidx), aa, ab, ac);
2263
    ELL_3V_SET(pldOut->indx + 3*(1 + 4*triidx), ab, bc, ac);
2264
    ELL_3V_SET(pldOut->indx + 3*(2 + 4*triidx), ab, bb, bc);
2265
    ELL_3V_SET(pldOut->indx + 3*(3 + 4*triidx), ac, bc, cc);
2266
  }
2267
2268
  /* set output vertex info */
2269
  for (vlo=0; vlo<nvold; vlo++) {
2270
    ELL_4V_COPY(pldOut->xyzw + 4*vlo, pldIn->xyzw + 4*vlo);
2271
    if ((1 << limnPolyDataInfoRGBA) & bitflag) {
2272
      ELL_4V_COPY(pldOut->rgba + 4*vlo, pldIn->rgba + 4*vlo);
2273
    }
2274
    if ((1 << limnPolyDataInfoNorm) & bitflag) {
2275
      ELL_3V_COPY(pldOut->norm + 3*vlo, pldIn->norm + 3*vlo);
2276
    }
2277
    if ((1 << limnPolyDataInfoTex2) & bitflag) {
2278
      ELL_2V_COPY(pldOut->tex2 + 2*vlo, pldIn->tex2 + 2*vlo);
2279
    }
2280
    if ((1 << limnPolyDataInfoTang) & bitflag) {
2281
      ELL_3V_COPY(pldOut->tang + 3*vlo, pldIn->tang + 3*vlo);
2282
    }
2283
    for (vhi=vlo+1; vhi<nvold; vhi++) {
2284
      unsigned int mid;
2285
      mid = newvert[vlo + nvold*vhi];
2286
      if (!mid) {
2287
        continue;
2288
      }
2289
      ELL_4V_LERP(pldOut->xyzw + 4*mid, 0.5f,
2290
                  pldIn->xyzw + 4*vlo,
2291
                  pldIn->xyzw + 4*vhi);
2292
      if ((1 << limnPolyDataInfoRGBA) & bitflag) {
2293
        ELL_4V_LERP_TT(pldOut->rgba + 4*mid, unsigned char, 0.5f,
2294
                       pldIn->rgba + 4*vlo,
2295
                       pldIn->rgba + 4*vhi);
2296
      }
2297
      if ((1 << limnPolyDataInfoNorm) & bitflag) {
2298
        float tmp;
2299
        ELL_3V_LERP(pldOut->norm + 3*mid, 0.5f,
2300
                    pldIn->norm + 3*vlo,
2301
                    pldIn->norm + 3*vhi);
2302
        ELL_3V_NORM_TT(pldOut->norm + 3*mid, float,
2303
                       pldOut->norm + 3*mid, tmp);
2304
      }
2305
      if ((1 << limnPolyDataInfoTex2) & bitflag) {
2306
        ELL_2V_LERP(pldOut->tex2 + 2*mid, 0.5f,
2307
                    pldIn->tex2 + 2*vlo,
2308
                    pldIn->tex2 + 2*vhi);
2309
      }
2310
      if ((1 << limnPolyDataInfoTang) & bitflag) {
2311
        float tmp;
2312
        ELL_3V_LERP(pldOut->tang + 3*mid, 0.5f,
2313
                    pldIn->tang + 3*vlo,
2314
                    pldIn->tang + 3*vhi);
2315
        ELL_3V_NORM_TT(pldOut->tang + 3*mid, float,
2316
                       pldOut->tang + 3*mid, tmp);
2317
      }
2318
    }
2319
  }
2320
2321
  airMopOkay(mop);
2322
  return 0;
2323
}
2324
2325
/* helper function for the limnPolyDataNeighborList below */
2326
static void
2327
registerNeighbor(unsigned int *nblist, size_t *len, unsigned int *maxnb,
2328
                 unsigned int u, unsigned int v) {
2329
  unsigned int idx=nblist[u], pointer=u, depth=1;
2330
  while (idx!=0) {
2331
    if (nblist[idx]==v)
2332
      return; /* has already been registered */
2333
    pointer=idx+1;
2334
    idx=nblist[pointer];
2335
    depth++;
2336
  }
2337
  if (depth>*maxnb)
2338
    *maxnb=depth;
2339
  /* do the registration */
2340
  nblist[pointer]=*len;
2341
  nblist[*len]=v;
2342
  nblist[*len+1]=0;
2343
  (*len)+=2;
2344
  /* now the other way around */
2345
  idx=nblist[v]; pointer=v;
2346
  while (idx!=0) {
2347
    /* do not have to check nblist[idx]==u due to symmetry */
2348
    pointer=idx+1;
2349
    idx=nblist[pointer];
2350
  }
2351
  nblist[pointer]=*len;
2352
  nblist[*len]=u;
2353
  nblist[*len+1]=0;
2354
  (*len)+=2;
2355
}
2356
2357
/* Here's the thing with all these limnPolyDataNeighbor* functions:
2358
 *
2359
 * *List is used for building up the information and called by all others
2360
 * *Array is a great representation if all vertices have a similar number
2361
 *        of neighbors (in particular, no gross outliers)
2362
 *        The output of this is used by glyph coloring in elf.
2363
 * *ArrayComp is a compressed representation that is best if vertices have
2364
 *            a more variable number of neighbors
2365
 *            The output of this is used by surface smoothing in limn.
2366
 */
2367
2368
/* Mallocs *nblist and fills it with a linked list that contains all neighbors
2369
 * of all n vertices in the given limnPolyData. The format is as follows:
2370
 * For v<n, (*nblist)[v] is an index i (i>=n) into *nblist, or 0 if vertex v
2371
 *   does not have any neighbors.
2372
 * For an index i obtained this way, (*nblist)[i] is a neighbor of v,
2373
 *   (*nblist)[i+1] is the index of the next list element (or 0).
2374
 * If non-NULL, *len is set to the required size of *nblist - the initial malloc
2375
 *   makes a conservative guess and you may want to realloc the result to *len
2376
 *   bytes in order to free memory that ended up unused
2377
 * If non-NULL, *m is set to the maximum number of neighbors (over all vertices)
2378
 * Return value is 0 upon success, -1 upon error. Biff is used for errors.
2379
 */
2380
int
2381
limnPolyDataNeighborList(unsigned int **nblist, size_t *len,
2382
                         unsigned int *maxnb, const limnPolyData *pld) {
2383
  static const char me[]="limnPolyDataNeighborList";
2384
  unsigned int i, j, m, estimate=0, *indx;
2385
  size_t last;
2386
  /* estimate the maximum number of neighborhood relations */
2387
  for (i=0; i<pld->primNum; i++) {
2388
    switch (pld->type[i]) {
2389
    case limnPrimitiveTriangles:
2390
    case limnPrimitiveQuads:
2391
      estimate+=pld->icnt[i]*2;
2392
      break;
2393
    case limnPrimitiveTriangleStrip:
2394
    case limnPrimitiveTriangleFan:
2395
      estimate+=4*(pld->icnt[i]-2)+2;
2396
      break;
2397
    case limnPrimitiveLineStrip:
2398
      estimate+=2*(pld->icnt[i]-1);
2399
      break;
2400
    case limnPrimitiveLines:
2401
      estimate+=pld->icnt[i];
2402
      break;
2403
    default: /* should be a noop; silently ignore it */
2404
      break;
2405
    }
2406
  }
2407
  /* allocate *nblist */
2408
  *nblist = (unsigned int*) malloc(sizeof(unsigned int)*
2409
                                   (pld->xyzwNum+2*estimate));
2410
  if (*nblist==NULL) {
2411
    biffAddf(LIMN, "%s: couldn't allocate nblist buffer", me);
2412
    return -1;
2413
  }
2414
  /* populate the list */
2415
  memset(*nblist, 0, sizeof(unsigned int)*pld->xyzwNum);
2416
  last=pld->xyzwNum; m=0; indx=pld->indx;
2417
  for (i=0; i<pld->primNum; i++) {
2418
    switch (pld->type[i]) {
2419
    case limnPrimitiveTriangles:
2420
      for (j=0; j<pld->icnt[i]; j+=3) { /* go through all triangles */
2421
        registerNeighbor(*nblist, &last, &m, indx[j], indx[j+1]);
2422
        registerNeighbor(*nblist, &last, &m, indx[j+1], indx[j+2]);
2423
        registerNeighbor(*nblist, &last, &m, indx[j+2], indx[j]);
2424
      }
2425
      break;
2426
    case limnPrimitiveTriangleStrip:
2427
      if (pld->icnt[i]>0)
2428
        registerNeighbor(*nblist, &last, &m, indx[0], indx[1]);
2429
      for (j=0; j<pld->icnt[i]-2; j++) {
2430
        registerNeighbor(*nblist, &last, &m, indx[j], indx[j+2]);
2431
        registerNeighbor(*nblist, &last, &m, indx[j+1], indx[j+2]);
2432
      }
2433
      break;
2434
    case limnPrimitiveTriangleFan:
2435
      if (pld->icnt[i]>0)
2436
        registerNeighbor(*nblist, &last, &m, indx[0], indx[1]);
2437
      for (j=0; j<pld->icnt[i]-2; j++) {
2438
        registerNeighbor(*nblist, &last, &m, indx[0], indx[j+2]);
2439
        registerNeighbor(*nblist, &last, &m, indx[j+1], indx[j+2]);
2440
      }
2441
      break;
2442
    case limnPrimitiveQuads:
2443
      for (j=0; j<pld->icnt[i]; j+=4) { /* go through all quads */
2444
        registerNeighbor(*nblist, &last, &m, indx[j], indx[j+1]);
2445
        registerNeighbor(*nblist, &last, &m, indx[j+1], indx[j+2]);
2446
        registerNeighbor(*nblist, &last, &m, indx[j+2], indx[j+3]);
2447
        registerNeighbor(*nblist, &last, &m, indx[j+3], indx[j]);
2448
      }
2449
      break;
2450
    case limnPrimitiveLineStrip:
2451
      for (j=0; j<pld->icnt[i]-1; j++) {
2452
        registerNeighbor(*nblist, &last, &m, indx[j], indx[j+1]);
2453
      }
2454
      break;
2455
    case limnPrimitiveLines:
2456
      for (j=0; j<pld->icnt[i]; j+=2) {
2457
        registerNeighbor(*nblist, &last, &m, indx[j], indx[j+1]);
2458
      }
2459
      break;
2460
    default: /* should be a noop; silently ignore it */
2461
      break;
2462
    }
2463
    indx+=pld->icnt[i];
2464
  }
2465
  if (len!=NULL) *len=last*sizeof(unsigned int);
2466
  if (maxnb!=NULL) *maxnb=m;
2467
  return 0;
2468
}
2469
2470
/* Over the set of all n vertices in a given limnPolyData, finds the
2471
 * maximum number m of neighbors. Sets *neighbors to a malloc'ed block
2472
 * of (n*m) indices and lists the neighbors of vertex v at position
2473
 * (v*m) of the list, padded with -1s.
2474
 * *maxnb will be set to m (and is assumed to be non-NULL!)
2475
 * Returns -1 and adds a biff error if there was a problem allocating memory.
2476
 */
2477
int
2478
limnPolyDataNeighborArray(int **neighbors, unsigned int *maxnb,
2479
                          const limnPolyData *pld) {
2480
  static const char me[]="limnPolyDataNeighborArray";
2481
  unsigned int i, *nblist, m;
2482
  /* get the neighbors as a linked list */
2483
  if (-1==limnPolyDataNeighborList(&nblist, NULL, &m, pld)) {
2484
    return -1;
2485
  }
2486
  /* convert the result into an array */
2487
  *neighbors = (int *) malloc(sizeof(int)*m*pld->xyzwNum);
2488
  if (NULL==*neighbors) {
2489
    biffAddf(LIMN, "%s: couldn't allocate neighbors buffer", me);
2490
    free(nblist); return -1;
2491
  }
2492
  for (i=0; i<pld->xyzwNum; i++) {
2493
    unsigned int aidx=0, lidx=nblist[i];
2494
    while (lidx!=0) {
2495
      (*neighbors)[m*i+aidx]=nblist[lidx];
2496
      lidx=nblist[lidx+1];
2497
      aidx++;
2498
    }
2499
    while (aidx<m) {
2500
      (*neighbors)[m*i+aidx]=-1;
2501
      aidx++;
2502
    }
2503
  }
2504
  *maxnb=m;
2505
  free(nblist);
2506
  return 0;
2507
}
2508
2509
/* Returns a compressed form of the above, rather than padding with -1s.
2510
 * *neighbors is malloc'ed to an array that holds the indices of all neighbors
2511
 * *idx is malloc'ed to an array of length pld->xyzwNum+1;
2512
 * (*idx)[i] will give you the position in *neighbors at which the neighbors of
2513
 * vertex i start
2514
 * Returns -1 and adds a biff error if there was a problem allocating memory.
2515
 */
2516
int
2517
limnPolyDataNeighborArrayComp(int **neighbors, int **idx,
2518
                              const limnPolyData *pld) {
2519
  static const char me[]="limnPolyDataNeighborArrayComp";
2520
  unsigned int i, ct, *nblist;
2521
  size_t len;
2522
  airArray *mop;
2523
  mop = airMopNew();
2524
  /* get the neighbors as a linked list */
2525
  if (-1==limnPolyDataNeighborList(&nblist, &len, NULL, pld)) {
2526
    return -1;
2527
  }
2528
  airMopAdd(mop, nblist, airFree, airMopAlways);
2529
  /* convert the result into compressed form */
2530
  *neighbors = (int *) malloc(sizeof(int)*(len-pld->xyzwNum)/2);
2531
  if (NULL==*neighbors) {
2532
    biffAddf(LIMN, "%s: couldn't allocate neighbors buffer", me);
2533
    airMopError(mop); return -1;
2534
  }
2535
  airMopAdd(mop, neighbors, airFree, airMopOnError);
2536
  *idx = (int *) malloc(sizeof(int)*(pld->xyzwNum+1));
2537
  if (NULL==*idx) {
2538
    biffAddf(LIMN, "%s: couldn't allocate index buffer", me);
2539
    airMopError(mop); return -1;
2540
  }
2541
  airMopAdd(mop, idx, airFree, airMopOnError);
2542
  for (ct=i=0; i<pld->xyzwNum; i++) {
2543
    unsigned int lidx=nblist[i];
2544
    (*idx)[i]=ct;
2545
    while (lidx!=0) {
2546
      (*neighbors)[ct]=nblist[lidx];
2547
      lidx=nblist[lidx+1];
2548
      ct++;
2549
    }
2550
  }
2551
  (*idx)[pld->xyzwNum]=ct;
2552
  airMopOkay(mop);
2553
  return 0;
2554
}