1 /*
2   Teem: Tools to process and visualize scientific data and images             .
3   Copyright (C) 2012, 2011, 2010, 2009  University of Chicago
4   Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
5   Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
6 
7   This library is free software; you can redistribute it and/or
8   modify it under the terms of the GNU Lesser General Public License
9   (LGPL) as published by the Free Software Foundation; either
10   version 2.1 of the License, or (at your option) any later version.
11   The terms of redistributing and/or modifying this software also
12   include exceptions to the LGPL that facilitate static linking.
13 
14   This library is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   Lesser General Public License for more details.
18 
19   You should have received a copy of the GNU Lesser General Public License
20   along with this library; if not, write to Free Software Foundation, Inc.,
21   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 */
23 
24 #include "pull.h"
25 #include "privatePull.h"
26 
27 /*
28 ** HEY: this messes with the points' idtag, and pctx->idtagNext,
29 ** even though it really shouldn't have to
30 */
31 int
pullCCFind(pullContext * pctx)32 pullCCFind(pullContext *pctx) {
33   static const char me[]="pullCCFind";
34   airArray *mop, *eqvArr;
35   unsigned int passIdx, binIdx, pointIdx, neighIdx, eqvNum,
36     pointNum, *idmap;
37   pullBin *bin;
38   pullPoint *point, *her;
39 
40   if (!pctx) {
41     biffAddf(PULL, "%s: got NULL pointer", me);
42     return 1;
43   }
44   if (_pullIterate(pctx, pullProcessModeNeighLearn)) {
45     biffAddf(PULL, "%s: trouble with %s for CC", me,
46              airEnumStr(pullProcessMode, pullProcessModeNeighLearn));
47     return 1;
48   }
49 
50   mop = airMopNew();
51   pointNum = pullPointNumber(pctx);
52   eqvArr = airArrayNew(NULL, NULL, 2*sizeof(unsigned int), pointNum);
53   airMopAdd(mop, eqvArr, (airMopper)airArrayNuke, airMopAlways);
54   idmap = AIR_CAST(unsigned int *, calloc(pointNum, sizeof(unsigned int)));
55   airMopAdd(mop, idmap, airFree, airMopAlways);
56 
57   /* to be safe, renumber all points, so that we know that the
58      idtags are contiguous, starting at 0. HEY: this should handled
59      by having a map from real point->idtag to a point number assigned
60      just for the sake of doing CCs */
61   pctx->idtagNext = 0;
62   for (binIdx=0; binIdx<pctx->binNum; binIdx++) {
63     bin = pctx->bin + binIdx;
64     for (pointIdx=0; pointIdx<bin->pointNum; pointIdx++) {
65       point = bin->point[pointIdx];
66       point->idtag = pctx->idtagNext++;
67     }
68   }
69 
70   /* same stupidity copied from limn/polymod.c:limnPolyDataCCFind */
71   eqvNum = 0;
72   for (passIdx=0; passIdx<2; passIdx++) {
73     if (passIdx) {
74       airArrayLenPreSet(eqvArr, eqvNum);
75     }
76     for (binIdx=0; binIdx<pctx->binNum; binIdx++) {
77       bin = pctx->bin + binIdx;
78       for (pointIdx=0; pointIdx<bin->pointNum; pointIdx++) {
79         point = bin->point[pointIdx];
80         for (neighIdx=0; neighIdx<point->neighPointNum; neighIdx++) {
81           if (0 == passIdx) {
82             ++eqvNum;
83           } else {
84             her = point->neighPoint[neighIdx];
85             airEqvAdd(eqvArr, point->idtag, her->idtag);
86           }
87         }
88       }
89     }
90   }
91 
92   /* do the CC analysis */
93   pctx->CCNum = airEqvMap(eqvArr, idmap, pointNum);
94 
95   /* assign idcc's */
96   for (binIdx=0; binIdx<pctx->binNum; binIdx++) {
97     bin = pctx->bin + binIdx;
98     for (pointIdx=0; pointIdx<bin->pointNum; pointIdx++) {
99       point = bin->point[pointIdx];
100       point->idCC = idmap[point->idtag];
101     }
102   }
103 
104   airMopOkay(mop);
105   return 0;
106 }
107 
108 /*
109 ** measure something about connected componts already found
110 **
111 ** measrInfo can be 0 to say "measure # particles in CC", or
112 ** it can be a scalar pullInfo
113 **
114 ** rho == 0: only size, rho == 1: only measrInfo
115 */
116 int
pullCCMeasure(pullContext * pctx,Nrrd * nmeasr,int measrInfo,double rho)117 pullCCMeasure(pullContext *pctx, Nrrd *nmeasr, int measrInfo, double rho) {
118   static const char me[]="pullCCMeasure";
119   airArray *mop;
120   unsigned int binIdx, pointIdx, ii;
121   double *meas, *size;
122   pullBin *bin;
123   pullPoint *point;
124 
125   if (!( pctx && nmeasr )) {
126     biffAddf(PULL, "%s: got NULL pointer", me);
127     return 1;
128   }
129   if (!pctx->CCNum) {
130     biffAddf(PULL, "%s: CCNum == 0: haven't yet learned CCs?", me);
131     return 1;
132   }
133   if (measrInfo) {
134     if (airEnumValCheck(pullInfo, measrInfo)) {
135       biffAddf(PULL, "%s: measrInfo %d not a valid %s", me,
136                measrInfo, pullInfo->name);
137       return 1;
138     }
139     if (1 != pullInfoLen(measrInfo)) {
140       biffAddf(PULL, "%s: measrInfo %s (%d) isn't a scalar (len %u)", me,
141                airEnumStr(pullInfo, measrInfo), measrInfo,
142                pullInfoLen(measrInfo));
143       return 1;
144     }
145     if (!pctx->ispec[measrInfo]) {
146       biffAddf(PULL, "%s: no ispec set for measrInfo %s (%d)", me,
147                airEnumStr(pullInfo, measrInfo), measrInfo);
148       return 1;
149     }
150   } /* else measrInfo is zero, they want to know # points */
151   /* in any case nmeasr is allocated for doubles */
152   if (nrrdMaybeAlloc_va(nmeasr, nrrdTypeDouble, 1,
153                         AIR_CAST(size_t, pctx->CCNum))) {
154     biffMovef(PULL, NRRD, "%s: couldn't alloc nmeasr", me);
155     return 1;
156   }
157   meas = AIR_CAST(double *, nmeasr->data);
158 
159   mop = airMopNew();
160   /* HEY: don't actually need to allocate and set size[],
161      if measrInfo == 0 */
162   if (!(size = AIR_CAST(double *,
163                         calloc(pctx->CCNum, sizeof(double))))) {
164     biffAddf(PULL, "%s: couldn't alloc size", me);
165     airMopError(mop); return 1;
166   }
167   airMopAdd(mop, size, airFree, airMopAlways);
168 
169   /* finally, do measurement */
170   for (binIdx=0; binIdx<pctx->binNum; binIdx++) {
171     bin = pctx->bin + binIdx;
172     for (pointIdx=0; pointIdx<bin->pointNum; pointIdx++) {
173       point = bin->point[pointIdx];
174       size[point->idCC]++;
175       meas[point->idCC] += (measrInfo
176                             ? pullPointScalar(pctx, point, measrInfo,
177                                               NULL, NULL)
178                             : 1);
179     }
180   }
181   if (measrInfo) {
182     for (ii=0; ii<pctx->CCNum; ii++) {
183       meas[ii] /= size[ii];
184       meas[ii] = AIR_LERP(rho, size[ii], meas[ii]);
185     }
186   }
187 
188   airMopOkay(mop);
189   return 0;
190 }
191 
192 typedef struct {
193   unsigned int i;
194   double d;
195 } ccpair;
196 
197 /* we intend to sort in *descending* order */
198 static int
ccpairCompare(const void * _a,const void * _b)199 ccpairCompare(const void *_a, const void *_b) {
200   const ccpair *a, *b;
201 
202   a = AIR_CAST(const ccpair *, _a);
203   b = AIR_CAST(const ccpair *, _b);
204   return (a->d < b->d
205           ? +1
206           : (a->d > b->d
207              ? -1
208              : 0));
209 }
210 
211 /*
212 ******** pullCCSort
213 **
214 ** sort CCs in pull context
215 **
216 ** measrInfo == 0: sort by size, else,
217 ** sort by blend of size and measrInfo
218 ** rho == 0: only size, rho == 1: only measrInfo
219 */
220 int
pullCCSort(pullContext * pctx,int measrInfo,double rho)221 pullCCSort(pullContext *pctx, int measrInfo, double rho) {
222   static const char me[]="pullCCSort";
223   ccpair *pair;
224   Nrrd *nmeasr;
225   airArray *mop;
226   unsigned int ii, *revm, binIdx, pointIdx;
227   double *measr;
228   pullBin *bin;
229   pullPoint *point;
230   int E;
231 
232   if (!pctx) {
233     biffAddf(PULL, "%s: got NULL pointer", me);
234     return 1;
235   }
236   if (!pctx->CCNum) {
237     biffAddf(PULL, "%s: haven't yet learned CCs?", me);
238     return 1;
239   }
240 
241 #define CALLOC(T) (AIR_CAST(T *, calloc(pctx->CCNum, sizeof(T))))
242   mop = airMopNew();
243   if (!(nmeasr = nrrdNew())
244       || airMopAdd(mop, nmeasr, (airMopper)nrrdNuke, airMopAlways)
245       || !(pair = CALLOC(ccpair))
246       || airMopAdd(mop, pair, airFree, airMopAlways)
247       || !(revm = CALLOC(unsigned int))
248       || airMopAdd(mop, revm, airFree, airMopAlways)) {
249     biffAddf(PULL, "%s: couldn't allocate everything", me);
250     airMopError(mop); return 1;
251   }
252 #undef CALLOC
253   if (!measrInfo) {
254     /* sorting by size */
255     E = pullCCMeasure(pctx, nmeasr, 0, 0.0);
256   } else {
257     /* sorting by some blend of size and pullInfo */
258     E = pullCCMeasure(pctx, nmeasr, measrInfo, rho);
259   }
260   if (E) {
261     biffAddf(PULL, "%s: problem measuring CCs", me);
262     airMopError(mop); return 1;
263   }
264 
265   measr = AIR_CAST(double *, nmeasr->data);
266   for (ii=0; ii<pctx->CCNum; ii++) {
267     pair[ii].i = ii;
268     pair[ii].d = measr[ii];
269   }
270   qsort(pair, pctx->CCNum, sizeof(ccpair), ccpairCompare);
271   for (ii=0; ii<pctx->CCNum; ii++) {
272     revm[pair[ii].i] = ii;
273   }
274 
275   for (binIdx=0; binIdx<pctx->binNum; binIdx++) {
276     bin = pctx->bin + binIdx;
277     for (pointIdx=0; pointIdx<bin->pointNum; pointIdx++) {
278       point = bin->point[pointIdx];
279       point->idCC = revm[point->idCC];
280     }
281   }
282 
283   airMopOkay(mop);
284   return 0;
285 }
286