File Release Notes and Changelog
Release Name: 1.7.0
Notes:
Changes:
---------------------------------------------------------------------
All public teem header files:
For the sake of static (or was it dll?) Windows builds, there used to
be a #define macro for each library: <lib>_import (or was it
<lib>_export?) which would enable linking to happen properly given
Teem's use global variables, and the sort of "def file" that was
created with cygwin's help. I think.
In any case, having seen that this was causing some confusion
in ITK's use of NrrdIO, the change was made to create a TEEM_API
macro that expands as necessary, depending on how Teem is being
built. TEEM_API MUST BE USED ON ALL "PUBLIC" SYMBOLS: BOTH
GLOBALS AND FUNCTIONS.
/* define TEEM_API */
#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(TEEM_STATIC)
# if defined(TEEM_BUILD)
# define TEEM_API extern __declspec(dllexport)
# else
# define TEEM_API extern __declspec(dllimport)
# endif
#else /* TEEM_STATIC || UNIX */
# define TEEM_API extern
#endif
---------------------------------------------------------------------
NrrdIO:
This is an effort to create a simple way for other programs to
do basic create/destroy input/output operations on nrrds and NRRD
file, started to enable ITK to handle NRRD files. This is a seperate
teem CVS module, but it is built automatically from the source files
in teem, from the air, biff, and nrrd libraries. The API presented in
NrrdIO is a struct subset of the API in teem.
To facilitate lopping out the portions of source files that are not
needed for NrrdIO, there are some new comments that delimit regions
of files (both .c and .h) not needed:
/* ---- BEGIN non-NrrdIO */
/* ---- END non-NrrdIO */
---------------------------------------------------------------------
air:
adapted/simplified the drand48() implementation from glibc-2.3, in new
functions airDrand48(), airSrand48() and re-entrant version
airDrand48_r(). This became an issue as soon as echo (the ray-tracer)
become genuinely multi-threaded. As part of this, I removed airRand()
and airSrand(), which were so stupid. Note that airDrand48() is
mathematically identical to glibc-2.3's drand48(), although the
default initial seeding scheme is different.
API GONE: the AIR_MEMCPY macros have been redacted, since they aren't
being used, and they were always a stupid idea.
---------------------------------------------------------------------
biff:
FILE RENAME:
biff/biff.c --> biff/biffbiff.c
---------------------------------------------------------------------
ell:
Matrices are now represented as row-major:
0 1 2
3 4 5
6 7 8
instead of column-major:
0 3 6
1 4 7
2 5 8
I thought I had written some further documentation about this change
elsewhere, but apparently not. Alas.
---------------------------------------------------------------------
limn:
added two new fields to the limnCamera, "fov" and "aspect",
augmented the semantics of limnCameraUpdate, added
limnCameraAspectSet, and made an effort to fix/debug the
limnCameraPathMake() function.
"fov" is like the "fovy" argument to gluPerspective()- the angle in
degrees vertically subtended by the view window, and "aspect" is the
ratio of horizontal to vertical size of the view window.
Now, when limCameraUpdate() is called, it will see if both fov and aspect
are set (non-NaN), and if so will update uRange[] and vRange[] accordingly.
Otherwise, uRange[] and vRange[] will not be touched.
limnCameraAspectSet() simply sets cam->aspect according to the given
number of pixels and the centering of the pixels
limnCameraPathMake() is not a new function, but it never worked usefully.
Now it can be used to make cool quaternion-spline-based camera paths,
with arbitrary key-frame spacing in time.
API RENAME: limnQN_xxx ---> limnQNxxx
added simple (and incomplete!) support for reading and writing OFF files
working on: drawing non-convex objects
---------------------------------------------------------------------
nrrd:
Two major changes, which were done simultaneously:
1) Some renamings related to NrrdAxis, nrrdAxisInfo, and so on
2) Addition of the nrrdKind information to the NrrdAxisInfo struct
In detail:
1) Some renamings related to NrrdAxis, nrrdAxisInfo, and so on:
struct:
NrrdAxis --> NrrdAxisInfo (NB: Nrrd struct still has field "axis")
functions:
nrrdAxisPos() --> nrrdAxisInfoPos()
nrrdAxisIdx() --> nrrdAxisInfoIdx()
nrrdAxisPosRange() --> nrrdAxisInfoPosRange()
nrrdAxisIdxRange() --> nrrdAxisInfoIdxRange()
nrrdAxisSpacingSet() --> nrrdAxisInfoSpacingSet()
nrrdAxisMinMaxSet() --> nrrdAxisInfoMinMaxSet()
Now, ANYTHING having to do with setting, getting, or manipulating the
meta-information about the axes starts with "nrrdAxisInfo". The single
exception to this is that the Nrrd struct's "axis" field was not
renamed to "axisInfo", since this seemed more annoying that it was worth.
Also, (this isn't new) ANYTHING having to do the set of axes, the
shape of the array, or the per-axis ordering of samples, starts with
"nrrdAxes". The nrrdAxes functions are NOT merely for manipulating
the per-axis meta information-- something about the array as a whole,
or the set of axes as a whole, is changing. Yes, this means that
there's a function called "nrrdAxesInsert", even though it only
inserts a single axis, but I figured the consistency generated by
strict adherence to the nrrdAxes and nrrdAxisInfo prefixes would be
better in the long run.
2) Addition of the nrrdKind information to the NrrdAxisInfo struct
This is nrrd's latest and probably most dangerous step towards putting
real semantic information in the array representation (that is, the
"nearly raw" philosophy is getting stretched). The best way to think
about this is that its a *hint*: an extra bit of information, kind of
like the label or the unit, that you may want to associated with the
axis, and which has a sufficiently general nature to warrent direct
representation.
So, new to the (newly renamed) NrrdAxisInfo struct:
int kind; /* what kind of information is along this
axis (from the nrrdKind* enum) */
Accordingly, there is change to the file format, which of course means
that the magic is once again incremented. If you write a nrrd with
known kinds on any axis, the magic will be "NRRD0003". The new kinds
field looks like (for-example)
kinds: 3-color domain domain
for an RGB image. Like other per-axis fields, its specification is
plural (its "kinds:", not "kind:"), and like centering, you use "???"
to mean, "I don't know", for example:
kinds: ??? domain domain
To make this hint more useful, there's a new function nrrdKindSize()
which returns what nrrd thinks is the suggested axis size for the
different nrrdKind's. This function will return 0 to indicate
"I have no advice for you on how big this axis should be". The reason
for using 0 instead of -1 is that someday nrrd may change the axis size
type from int to unsigned int.
To offer some control of the nrrdKind behavior, there is a new global
"state" variable: nrrdStateKindNoop. Currently, this is AIR_TRUE by
default. You can set it to AIR_FALSE to enable some minimal cleverness
on the part of nrrd. The nature of that minimal cleverness is almost
certainly likely to change with time.
Due to the nature of nrrdAxisInfoCopy, anything which doesn't explicitly
ask the axis information NOT to be copied, will have it be copied. For
example, nrrdSlice will propogate through the kinds on the axes that
it didn't touch, same with nrrdAxesPermute, and the like.
Regardless of NrrdKindStateNoop, the output of these functions will set
nrrdKindUnknown on all the axes that experienced a non-trivial change:
nrrdAxesSplit, nrrdAxesMerge, nrrdReshape, nrrdReshape_nva,
nrrdUnblock, nrrdJoin, nrrdApply1DLut, nrrdApply1DRegMap,
and nrrdApply1DIrregMap.
These functions respect nrrdKindStateNoop:
nrrdCrop, nrrdPad, nrrdShuffle, nrrdFlip, nrrdSpatialResample:
if (nrrdKindStateNoop) {
If the axis operated on was not a nrrdKindDomain or nrrdKindList,
the resulting kind is nrrdKindUnknown
Else, the resulting kind is the same as the input kind.
This is implemented through _nrrdKindAltered()
} else {
output kind is a copy of the input kind
}
nrrdHisto, nrrdHistoAxis, nrrdHistoJoint:
if (nrrdKindStateNoop) {
histogram axes are nrrdKindDomain
} else {
histogram axes are nrrdKindUnknown
}
nrrdAxisInsert:
if (nrrdKindStateNoop) {
new axis is nrrdKindStub
} else {
new axis is nrrdKindUnknown
}
For the record, this is what this change entailed:
added:
- global int nrrdStateKindNoop
- nrrdKindSize()
- airEnum *nrrdKind (maps between strings like "3color" and nrrdKind* values)
- NRRD_KIND_MAX #define
augmented:
- NrrdAxisInfo struct (now has "kind" field)
- _nrrdFieldInteresting(), _nrrdSprintFieldInfo(), _nrrdFormatNRRD_write()
with things relating to new field ("kinds:") and magic ("NRRD0003")
- _nrrdFieldValidInImage[], _nrrdFieldOnePerAxis[], _nrrdFieldValidInText[],
_nrrdFieldRequired[]
- _nrrdReadNrrdParseInfo[] parser callback, with new
_nrrdReadNrrdParse_kinds()
- airEnum *nrrdField
- nrrdAxisInfo C enum (for passing to nrrdAxisInfo{Set,Get})
- nrrdField* C enum
- nrrdStateGetenv(): looks for NRRD_STATE_KIND_NOOP" environment variable
One annoying bit:
I didn't add a new flag to "unu make" to learn the axis kinds because
I *can't*. This is a basic limitation in hest which is gotten pretty
annoying: there is no direct way to see if an option was given on the
command line or not. Every option has to have a value supplied,
either via the command-line or via the default value string, but once
its all been parsed, you don't know where it came from, and nor can
you tell hest "this option is really really optional, there is no
default, and its okay if the user never gives you a value." This will
only be fixed with a hest re-write.
Actually, I have to rethink this. It may be possible to add the "-k"
option to "unu make", it will just be a wild hack.
IMPROVED FUNCTIONALITY:
Prior to teem-1.7, a nrrd that was already already valid and allocated
with some amount of memory would be stripped of that memory (the memory
was freed) by nrrdRead(). The various encodings would allocate new memory
via _nrrdCalloc. This meant that even when the caller to nrrdRead knew
in advance that the given nrrd could hold the data that would be read from
the given file, the memory was freed and re-allocated. This presented a
problem for ITK's use of nrrd in NrrdIO.
Now, near the beginning of nrrdRead(), any existing memory (via nrrd->data
being non-NULL) and its allocated size (via nrrd->type and nrrd->axis[i].size)
are remembered in the NrrdIoState struct (void *oldData, size_t oldDataSize),
which is then referred to via _nrrdCalloc, or directly by the format's reader.
The result is that existing memory is used whenever the allocated size
matches the size described by the new nrrd's header. This change has been
made in all existing formats, which involved more modifications for
formatText.c and formatPNG.c, since these do not use (nor should they)
functions in the existing encodingXXX.c files.
Near the end of nrrdRead(), the old data is freed if it turns out that it
wasn't re-used.
API CHANGE:
nrrdCheapMedian now takes a new argument, "pad", which tells it to
pad the input by the radius of the filter, prior to filtering, and then
crops the output. Without this, the samples along the boundary are left
unprocessed. Note: this functionality has migrated from "unu cmedian"
down into nrrd.
"unu cmedian", on the other hand, has a new command-line flag, "-c",
which tells it to slice along axis 0, run filtering on all slices, and
then join the results together. I was too chicken to add this the nrrd
API directly, but it may eventually migrate there.
---------------------------------------------------------------------
gage:
massive internal changes, but hopefully only the following are visible:
API CHANGE: unsigned int queries are gone; now using gageQuery, a
bit-vector based on an array of uchars. So, where you used to say:
unsigned int query;
...
gageQuerySet(ctx, pvl, 1 << gageSclValue | 1 << gageSclGradMag)
you'll now say:
gageQuery query;
...
GAGE_QUERY_RESET(query);
GAGE_QUERY_ITEM_ON(query, gageSclValue);
GAGE_QUERY_ITEM_ON(query, gageSclGradMag);
gageQuerySet(ctx, pvl, query);
or, less annoyingly (perhaps):
gageQueryReset(ctx, pvl);
gageQueryItemOn(ctx, pvl, gageSclValue);
gageQueryItemOn(ctx, pvl, gageSclGradMag);
One unfortunate consequence of this change is that gage queries can no
longer be conveniently set at compile-time. This was an issue for bane,
which had to be changed to comply, inadvertently triggering a massive
re-write, still unfinished.
API CHANGE:
many of the arrays in the gageKind have been replaced by
a single array of things called gageItemEntry, which is must easier
to maintain by hand. See gage.h for details.
---------------------------------------------------------------------
bane:
Sorry, bane is currently broken. The gage overhaul was a bit too
much of a shock, and it never fully recovered.
---------------------------------------------------------------------
ten:
API RENAME:
some function renamings, to make way for future move to
doubles, or at least away from float-specificity:
tenEigensolve --> tenEigensolve_f
tenMakeOne --> tenMakeOne_f
tenSimulateOne --> tenSimulateOne_f
tenAnisoCalc --> tenAnisoCalc_f
tenEvqOne --> tenEvqOne_f
tenEstimateLinearSingle --> tenEstimateLinearSingle_f
API NEW:
tenEigensolve_d
API CHANGE: Functions xxx which haven't been renamed to xxx_f, and
which took float arguments (not a pointer to float), now take a double
instead. This is not a change that anyone will notice. Affected
functions are:
tenEstimateLinear3D()
tenEstimateLinear4D()
tenExpand()
tenSimulate()
tenAnisoVolume()
tenSizeScale()
tenAnisoScale()
tenEigenvaluePower()
tenEigenvalueClamp()
tenEigenvalueAdd()
API CHANGE:
More of a big deal: this guy took a float array ("weight"), and
now its a double array:
tenSizeNormalize(Nrrd *nout, Nrrd *nin, double weight[3],
double amount, double target);
FILE RENAME:
ten/tenPrivate.h --> ten/privateTen.h
API NEW: tend helix
New tend comand for making cool twisting helix tensor datasets
---------------------------------------------------------------------
mite:
massive internal reorganization to now support volume rendering of
vector and tensor fields. Still using nrrds as transfer function
LUTs, and all existing axis labels should work, although they're
deprecated. Good luck finding documentation.
---------------------------------------------------------------------
echo:
now properly multi-threaded
---------------------------------------------------------------------