int unrrdu_fftMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *_nin, *nout; int pret; airArray *mop; int sign, rigor, rescale, realInput; char *wispath; FILE *fwise; unsigned int *axes, axesLen; hestOptAdd(&opt, NULL, "dir", airTypeEnum, 1, 1, &sign, NULL, "forward (\"forw\", \"f\") or backward/inverse " "(\"back\", \"b\") transform ", NULL, direction_enm); hestOptAdd(&opt, "a,axes", "ax0", airTypeUInt, 1, -1, &axes, NULL, "the one or more axes that should be transformed", &axesLen); hestOptAdd(&opt, "pr,planrigor", "pr", airTypeEnum, 1, 1, &rigor, "est", "rigor with which fftw plan is constructed. Options include:\n " "\b\bo \"e\", \"est\", \"estimate\": only an estimate\n " "\b\bo \"m\", \"meas\", \"measure\": standard amount of " "measurements of system properties\n " "\b\bo \"p\", \"pat\", \"patient\": slower, more measurements\n " "\b\bo \"x\", \"ex\", \"exhaustive\": slowest, most measurements", NULL, nrrdFFTWPlanRigor); hestOptAdd(&opt, "r,rescale", "bool", airTypeBool, 1, 1, &rescale, "true", "scale fftw output (by sqrt(1/N)) so that forward and backward " "transforms will get back to original values"); hestOptAdd(&opt, "w,wisdom", "filename", airTypeString, 1, 1, &wispath, "", "A filename here is used to read in fftw wisdom (if the file " "exists already), and is used to save out updated wisdom " "after the transform. By default (not using this option), " "no wisdom is read or saved. Note: no wisdom is gained " "(that is, learned by FFTW) with planning rigor \"estimate\"."); OPT_ADD_NIN(_nin, "input nrrd"); hestOptAdd(&opt, "ri,realinput", NULL, airTypeInt, 0, 0, &realInput, NULL, "input is real-valued, so insert new length-2 axis 0 " "and set complex component to 0.0. Axes to transform " "(indicated by \"-a\") will be incremented accordingly."); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); if (nrrdFFTWEnabled) { USAGE(_unrrdu_fftInfoL_yes); } else { USAGE(_unrrdu_fftInfoL_no); } PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (realInput) { ptrdiff_t minPad[NRRD_DIM_MAX], maxPad[NRRD_DIM_MAX]; unsigned int axi; Nrrd *ntmp; ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); if (nrrdAxesInsert(ntmp, _nin, 0)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error creating complex axis:\n%s", me, err); airMopError(mop); return 1; } nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); minPad[0] = 0; maxPad[0] = 1; for (axi=1; axi<ntmp->dim; axi++) { minPad[axi] = 0; maxPad[axi] = AIR_CAST(ptrdiff_t, ntmp->axis[axi].size-1); } if (nrrdPad_nva(nin, ntmp, minPad, maxPad, nrrdBoundaryPad, 0.0)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error padding out complex axis:\n%s", me, err); airMopError(mop); return 1; } /* increment specified axes to transform */ for (axi=0; axi<axesLen; axi++) { axes[axi]++; } /* ntmp is really done with, we can free up the space now; this is one of the rare times we want airMopSub */ airMopSub(mop, ntmp, (airMopper)nrrdNuke); nrrdNuke(ntmp); } else { /* input is apparently already complex */ nin = _nin; } if (airStrlen(wispath) && nrrdFFTWEnabled) { fwise = fopen(wispath, "r"); if (fwise) { if (nrrdFFTWWisdomRead(fwise)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error with fft wisdom:\n%s", me, err); airMopError(mop); return 1; } fclose(fwise); } else { fprintf(stderr, "%s: (\"%s\" couldn't be opened, will try to save " "wisdom afterwards)", me, wispath); } } if (nrrdFFT(nout, nin, axes, axesLen, sign, rescale, rigor)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error with fft:\n%s", me, err); airMopError(mop); return 1; } if (airStrlen(wispath) && nrrdFFTWEnabled) { if (!(fwise = fopen(wispath, "w"))) { fprintf(stderr, "%s: couldn't open %s for writing: %s\n", me, wispath, strerror(errno)); airMopError(mop); return 1; } if (nrrdFFTWWisdomWrite(fwise)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error with fft wisdom:\n%s", me, err); airMopError(mop); return 1; } fclose(fwise); } SAVE(out, nout, NULL); airMopOkay(mop); return 0; }
int main(int argc, char *argv[]) { char *me, *err, *outS; hestOpt *hopt=NULL; airArray *mop; int numGrth, numDtdi, numGrgr, numDtgr, numNrrd, ni; plotPS pps; plotParm pparm; Nrrd **_ndata, **ndata; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, "i", "data", airTypeOther, 1, -1, &_ndata, NULL, "input nrrd containing data to plot", &numNrrd, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "dbox", "minX minY maxX maxY", airTypeDouble, 4, 4, pparm.dbox, NULL, "bounding box, in data space"); hestOptAdd(&hopt, "bbox", "minX minY maxX maxY", airTypeDouble, 4, 4, pps.bbox, NULL, "bounding box, in graph space"); hestOptAdd(&hopt, "psc", "PS scale", airTypeDouble, 1, 1, &(pps.psc), "300", "scaling from graph space to PostScript points"); hestOptAdd(&hopt, "nobg", NULL, airTypeInt, 0, 0, &(pps.nobg), NULL, "don't fill with background color"); hestOptAdd(&hopt, "bg", "background", airTypeDouble, 3, 3, &(pps.bgColor), "1 1 1", "background RGB color; each component in range [0.0,1.0]"); hestOptAdd(&hopt, "grth", "graph thickness", airTypeDouble, 1, -1, &(pparm.graphThick), "0.01", "thickness of line for graph, or \"0\" for no graph line", &numGrth); hestOptAdd(&hopt, "grgr", "graph gray", airTypeDouble, 1, -1, &(pparm.graphGray), "0", "grayscale to use for graph", &numGrgr); hestOptAdd(&hopt, "dtdi", "dot diameter", airTypeDouble, 1, -1, &(pparm.dotDiameter), "0.1", "radius of dot drawn at data points, or \"0\" for no dots", &numDtdi); hestOptAdd(&hopt, "dtgr", "dot gray", airTypeDouble, 1, -1, &(pparm.dotGray), "0", "grayscale to use for dots", &numDtgr); hestOptAdd(&hopt, "dtid", "dot inner diam frac", airTypeDouble, 1, 1, &(pparm.dotInnerDiameterFraction), "0.0", "fractional radius of white dot drawn within dot"); hestOptAdd(&hopt, "tihz", "pos", airTypeDouble, 0, -1, &(pparm.horzTick), "", "locations for tickmarks on horizontal axis", &(pparm.numHorzTick)); hestOptAdd(&hopt, "tivt", "pos", airTypeDouble, 0, -1, &(pparm.vertTick), "", "locations for tickmarks on vertical axis", &(pparm.numVertTick)); hestOptAdd(&hopt, "tiho", "offset", airTypeDouble, 1, 1, &(pparm.horzTickLabelOffset), "0", "horizontal tick label offset"); hestOptAdd(&hopt, "tivo", "offset", airTypeDouble, 1, 1, &(pparm.vertTickLabelOffset), "0", "vertical tick label offset"); hestOptAdd(&hopt, "tils", "size", airTypeDouble, 1, 1, &(pparm.tickLabelSize), "0", "font size for labels on tick marks, or \"0\" for no labels"); hestOptAdd(&hopt, "tith", "tick thickness", airTypeDouble, 1, 1, &(pparm.tickThick), "0.01", "thickness of lines for tick marks"); hestOptAdd(&hopt, "tiln", "tick length", airTypeDouble, 1, 1, &(pparm.tickLength), "0.08", "length of lines for tick marks"); hestOptAdd(&hopt, "axth", "axis thickness", airTypeDouble, 1, 1, &(pparm.axisThick), "0.01", "thickness of lines for axes"); hestOptAdd(&hopt, "axor", "axis origin", airTypeDouble, 2, 2, &(pparm.axisOrig), "0 0", "origin of lines for axes, in data space"); hestOptAdd(&hopt, "axhl", "horiz axis label", airTypeString, 1, 1, &(pparm.axisHorzLabel), "", "label on horizontal axis"); hestOptAdd(&hopt, "axvl", "vert axis label", airTypeString, 1, 1, &(pparm.axisVertLabel), "", "label on vertical axis"); hestOptAdd(&hopt, "o", "output PS", airTypeString, 1, 1, &outS, "out.ps", "output file to render postscript into"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!( numGrth == numDtdi && numDtdi == numGrgr && numGrgr == numDtgr )) { fprintf(stderr, "%s: number of arguments given to grth (%d), dtdi (%d), " "grgr (%d), dtgr (%d) not all equal\n", me, numGrth, numDtdi, numGrgr, numDtgr); airMopError(mop); return 1; } if (!( numNrrd == numGrth )) { fprintf(stderr, "%s: number of nrrds (%d) != number graph options (%d)\n", me, numNrrd, numGrth); airMopError(mop); return 1; } /* check nrrds */ for (ni=0; ni<numNrrd; ni++) { if (!( (1 == _ndata[ni]->dim || 2 == _ndata[ni]->dim) && nrrdTypeBlock != _ndata[ni]->type )) { fprintf(stderr, "%s: input nrrd must be 1-D or 2-D array of scalars", me); airMopError(mop); return 1; } } ndata = (Nrrd**)calloc(numNrrd, sizeof(Nrrd *)); airMopAdd(mop, ndata, airFree, airMopAlways); for (ni=0; ni<numNrrd; ni++) { ndata[ni] = nrrdNew(); airMopAdd(mop, ndata[ni], (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(ndata[ni], _ndata[ni], nrrdTypeDouble)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't convert input %d to %s:\n%s\n", me, ni, airEnumStr(nrrdType, nrrdTypeDouble), err); airMopError(mop); return 1; } if (1 == ndata[ni]->dim) { if (nrrdAxesInsert(ndata[ni], ndata[ni], 0)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't insert axis 0 on nrrd %d:\n%s\n", me, ni, err); airMopError(mop); return 1; } } /* currently assuming node centering */ if (!AIR_EXISTS(ndata[ni]->axis[1].min)) { ndata[ni]->axis[1].min = 0; } if (!AIR_EXISTS(ndata[ni]->axis[1].max)) { ndata[ni]->axis[1].max = ndata[ni]->axis[1].size-1; } } if (!(pps.file = airFopen(outS, stdout, "wb"))) { fprintf(stderr, "%s: couldn't open output file\n", me); airMopError(mop); return 1; } airMopAdd(mop, pps.file, (airMopper)airFclose, airMopAlways); plotPreamble(&pps, &pparm); plotAxes(&pps, &pparm, ndata[0]); for (ni=0; ni<numNrrd; ni++) { plotGraph(&pps, &pparm, ndata, ni); plotDots(&pps, &pparm, ndata, ni); } plotEpilog(&pps, &pparm); airMopOkay(mop); return 0; }
/* ******** nrrdSlice() ** ** slices a nrrd along a given axis, at a given position. ** ** This is a newer version of the procedure, which is simpler, faster, ** and requires less memory overhead than the first one. It is based ** on the observation that any slice is a periodic square-wave pattern ** in the original data (viewed as a one- dimensional array). The ** characteristics of that periodic pattern are how far from the ** beginning it starts (offset), the length of the "on" part (length), ** the period (period), and the number of periods (numper). */ int nrrdSlice(Nrrd *nout, const Nrrd *cnin, unsigned int saxi, size_t pos) { static const char me[]="nrrdSlice", func[]="slice"; size_t I, rowLen, /* length of segment */ colStep, /* distance between start of each segment */ colLen, /* number of periods */ szOut[NRRD_DIM_MAX]; unsigned int ai, outdim; int map[NRRD_DIM_MAX]; const char *src; char *dest, stmp[2][AIR_STRLEN_SMALL]; airArray *mop; Nrrd *nin; if (!(cnin && nout)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == cnin) { biffAddf(NRRD, "%s: nout==nin disallowed", me); return 1; } if (1 == cnin->dim) { if (0 != saxi) { biffAddf(NRRD, "%s: slice axis must be 0, not %u, for 1-D array", me, saxi); return 1; } } else { if (!( saxi < cnin->dim )) { biffAddf(NRRD, "%s: slice axis %d out of bounds (0 to %d)", me, saxi, cnin->dim-1); return 1; } } if (!( pos < cnin->axis[saxi].size )) { biffAddf(NRRD, "%s: position %s out of bounds (0 to %s)", me, airSprintSize_t(stmp[0], pos), airSprintSize_t(stmp[1], cnin->axis[saxi].size-1)); return 1; } /* this shouldn't actually be necessary .. */ if (!nrrdElementSize(cnin)) { biffAddf(NRRD, "%s: nrrd reports zero element size!", me); return 1; } /* HEY: copy and paste from measure.c/nrrdProject */ mop = airMopNew(); if (1 == cnin->dim) { /* There are more efficient ways of dealing with this case; this way is easy to implement because it leaves most of the established code below only superficially changed; uniformly replacing nin with (nin ? nin : cnin), even if pointlessly so; this expression that can't be assigned to a new variable because of the difference in const. */ nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); if (nrrdAxesInsert(nin, cnin, 1)) { biffAddf(NRRD, "%s: trouble inserting axis on 1-D array", me); airMopError(mop); return 1; } } else { nin = NULL; } /* set up control variables */ rowLen = colLen = 1; for (ai=0; ai<(nin ? nin : cnin)->dim; ai++) { if (ai < saxi) { rowLen *= (nin ? nin : cnin)->axis[ai].size; } else if (ai > saxi) { colLen *= (nin ? nin : cnin)->axis[ai].size; } } rowLen *= nrrdElementSize(nin ? nin : cnin); colStep = rowLen*(nin ? nin : cnin)->axis[saxi].size; outdim = (nin ? nin : cnin)->dim-1; for (ai=0; ai<outdim; ai++) { map[ai] = AIR_INT(ai) + (ai >= saxi); szOut[ai] = (nin ? nin : cnin)->axis[map[ai]].size; } nout->blockSize = (nin ? nin : cnin)->blockSize; if (nrrdMaybeAlloc_nva(nout, (nin ? nin : cnin)->type, outdim, szOut)) { biffAddf(NRRD, "%s: failed to create slice", me); airMopError(mop); return 1; } /* the skinny */ src = AIR_CAST(const char *, (nin ? nin : cnin)->data); dest = AIR_CAST(char *, nout->data); src += rowLen*pos; for (I=0; I<colLen; I++) { /* HEY: replace with AIR_MEMCPY() or similar, when applicable */ memcpy(dest, src, rowLen); src += colStep; dest += rowLen; } /* copy the peripheral information */ if (nrrdAxisInfoCopy(nout, (nin ? nin : cnin), map, NRRD_AXIS_INFO_NONE)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } if (nrrdContentSet_va(nout, func, cnin /* hide possible axinsert*/, "%d,%d", saxi, pos)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } if (nrrdBasicInfoCopy(nout, (nin ? nin : cnin), NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_SPACEORIGIN_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } /* translate origin if this was a spatial axis, otherwise copy */ /* note that if there is no spatial info at all, this is all harmless */ if (AIR_EXISTS((nin ? nin : cnin)->axis[saxi].spaceDirection[0])) { nrrdSpaceVecScaleAdd2(nout->spaceOrigin, 1.0, (nin ? nin : cnin)->spaceOrigin, AIR_CAST(double, pos), (nin ? nin : cnin)->axis[saxi].spaceDirection); } else {