|
From: Gordon K. <gl...@cs...> - 2025-09-10 17:50:15
|
Hello, I still use Teem, so I figured I should share with teem-users a decision I made recently. The next release of Teem will be Teem version 2.0.0. Some breaking ABI changes in hest (specifically the addition of support for --help with revision 6994 in June 2023) helped me recognize the necessity of bumping the major version number, and with that change, I'm taking the opportunity to clean up and update things that have long bothered me. I don't know when Teem v2 will be finished and released. I'm aiming for sometime before 2026. Below is the draft of the change log (still in-progress) that will accompany that eventual release. Also, I'd like to try Discord as a way of maintaining communication with Teem users, so here's an invite: https://discord.gg/xBBqZGXkF7 Let me know if that link doesn't work for you. On Discord, please share if there is anything important for you to see in a Teem v2 release, or ask questions, or just say hi :) best regards, Gordon --------------------------------------------- This is a new Teem release, version 2.0.0. The previous completed release was version 1.11.0, from 20 Dec, 2012 (and Homebrew is at this time using that version 1.11.0). At some point in 2014, the Teem source code (the svn trunk) started declaring itself version 1.12.0, probably in hopeful anticipation of a release that never actually happened. Some linux packages of Teem started using a version of the 1.12 code. However, Teem version 1.12.0 never was actually finished and released by the main developer of Teem (me, Gordon Kindlmann, GLK), and I have no plans to do so. Unlike what might be assumed with a major version increment, the API changes with this version 2.0 are modest. Teem basically works the same as before: there are no huge disruptive changes in how the most useful functions of Teem behave, or how they use the various structs defined in the Teem headers. There are, however, breaking ABI changes. To clearly distinguish this new version from the decade of "1.12" versions floating around, the version number is now incremented to 2.0.0. The first big ABI change was in hest (adding something to hestOpt to support "--help"), and I learned first-hand how baffling the resulting errors can be. even though the API-using code needed no modifications. To play well with the expections around the semantic versioning https://semver.org/ that Teem bought into in 2005 with version 1.9.0, the major version number must be incremented. Upon realizing that this would be a major version change, I took the opportunity to tweak some other things that were long annoying, most by simplifying things, also unlikely to break Teem-using code, but all lovingly documented below, as tagged with "API CHANGE". The major items are delineatd with •s I have had less time for Teem development than when I was a student or post-doc, but my day-to-day reliance on Teem has not decreased, nor has my interest in its long-term utility, nor my commitment to the ongoing reproducibility of all past research results that depended on Teem. Once this release is finished, I hope to find and coordinate with whomever created the Teem distributions for Homebrew and Linux, so that they can be updated. Please let me (gl...@uc... <mailto:gl...@uc...>) know if you know how to facilitate this. All the changes and bug fixes, from version 2012's 1.11.0 to today's version 2.0.0, are described below. The log suffers from some inconsistency of format due to being written over 13 years (!); hopefully you find it informative. The ordering of items within sections is roughly from most interesting or important to least so, rather than chronological. Search for "BUG" to see info about bugs fixed. Search for "API CHANGE" to see changes to the APIs of the libraries (in particular: biff, hest, unrrdu, bane, gage, seek, moss, and meet) Since March 2001, Teem code version control has been hosted on Sourceforge, which has been more or less reliable over more than *two decades*. The awful DevShare malware stuff that Sourceforge started (under new ownership) in 2012 was stopped (under different new ownership) in 2016, and their server reliability issues are fully resolved, as far as I can tell. Teem code will continue to be hosted on Sourceforge for the time being, even well after most open source development has moved to GitHub. Please read https://githubcopilotlitigation.com/. I have deep reservations about GitHub's corporate owner, and note that GitHub is now a leaderless component of an AI platform: https://www.theverge.com/news/757461/microsoft-github-thomas-dohmke-resignation-coreai-team-transition This is a sad conclusion to the original idealistic project of GitHub. ========= use of CMake and GNUmake ========= ........... STILL IN PROGRESS ... new CMake files have "-v2" in filename ............. • 2025-08-14 22:27:17 +0000 (Thu, 14 Aug 2025) Revision: 7269 Everything about how Teem uses CMake has been completely re-written (or rather re-written again, beyond the 2019 improvements described below) by me (GLK), with help from ChatGPT. This is the first time I've really tried to understand the CMake files, and the first time I've tried anything non-trivial with ChatGPT. Current generative AI tools tend to normalize and mechanize intellectual property theft, with a huge carbon footprint. In this case my deep reservations about ChatGPT were overcome by my deeper distaste for CMake. ChatGPT's training likely consumed countless CMakeLists.txt files and all available online discussion about CMake. I apologize to all those people whose past generous CMake online activity empowers ChatGPT but who receive no compensation from OpenAI. The changes to Teem's CMake machinery and its outputs are as follows: -- teem/CMakeLists.txt completely re-written: the CMake usage has been generally modernized (the required version is bumped to 3.25, released November 16, 2022), the sections are better organized, and the implementation has less copy-pasta. Many of the options exposed have been renamed to be more idiomatic (API CHANGE): * BUILD_HEX --> Teem_BUILD_HEX_UTILS (build stand-alone enhex/dhex utils) * Teem_BZIP2 --> Teem_USE_BZIP2 (build Nrrd with bzip2 data encoding support) * Teem_FFTW3 --> Teem_USE_FFTW3 (allow nrrdFFT and "unu fft" to work) * Teem_LEVMAR --> Teem_USE_LEVMAR (build Teem with levmar-based optimization) * Teem_PNG --> Teem_USE_PNG (build Nrrd with PNG file format support) * Teem_PTHREAD --> Teem_USE_PTHREAD (built Teem with multi-threading support) * Teem_ZLIB --> Teem_USE_ZLIB (build Nrrd with zlib data encoding support) * USER_DIRECTIO --> X (removed all remnants of SGI DirectIO support) * Teem_USE_LIB_INSTALL_SUBDIR --> Teem_INSTALL_VERSIONED_PREFIX (in install directory, create a subdir named with Teem version number; see below) -- A Teem install via old CMake files would create files in installdir/lib/cmake/teem (note "t") TeemBuildSettings.cmake TeemConfig.cmake TeemLibraryDepends-noconfig.cmake TeemLibraryDepends.cmake TeemUse.cmake A find_package(Teem) would set variables like Teem_USE_FILE, Teem_EXECUTABLE_DIRS, Teem_LIBRARY_DIRS, Teem_INCLUDE_DIRS, Teem_LIBRARIES, etc. This was adhoc. Now, a Teem install via the new Cmake files puts things in installdir/lib/cmake/Teem/ (note "T") TeemConfig.cmake TeemConfigVersion.cmake TeemTargets-release.cmake TeemTargets.cmake which defines a proper namespaced target "Teem::Teem". Use Teem with the modern idiom: find_package(Teem REQUIRED) target_link_libraries(myprog PRIVATE Teem::Teem) You don't need to worry about include directories, library paths, or defines. The new Teem package is generated with modern configure_package_config_file(), and uses find_dependency(...) for the optional dependencies PNG, ZLIB, etc. But all these are now encoded for you in the Teem::Teem target properties. The old TeemUse.cmake and TeemBuildSettings.cmake files are gone. -- Teem_INSTALL_VERSIONED_SUBDIRS is different than what Teem_USE_LIB_INSTALL_SUBDIR did: This is the top-level structure of what Teem_USE_LIB_INSTALL_SUBDIR=ON made in the install directory, for version X.Y.Z: installdir/bin/unu # and the other command-line tools installdir/include/teem/airExistsConf.h installdir/include/teem/air.h # and all the other Teem lib.h installdir/lib/Teem-X.Y.Z/libteem.X.Y.Z.dylib # and associated symlinks installdir/lib/Teem-X.Y.Z/cmake/teem/*.cmake But now (API CHANGE?), if Teem_INSTALL_VERSIONED_PREFIX is ON, you get: installdir/Teem-X.Y.Z/bin/unu ... installdir/Teem-X.Y.Z/include/teem/airExistsConf.h installdir/Teem-X.Y.Z/include/teem/air.h ... installdir/Teem-X.Y.Z/lib/libteem.X.Y.Z.dylib ... installdir/Teem-X.Y.Z/lib/cmake/Teem/*.cmake This layout is much more conventional and more logical: if versioning matters for the libraries, it certainly matters too for the headers defining the API! -- The result of a CMake-powered Teem installation FINALLY include artifacts that facilitate Teem usage from non-CMake-using projects: * installdir/lib/pkgconfig/teem.pc allows https://en.wikipedia.org/wiki/Pkg-config to generate info via commands like: pkg-config --cflags teem # -> (e.g.) -I/usr/local/include pkg-config --libs teem # -> -L/usr/local/lib -lteem -lz -lpng -lm ... * installdir/bin/teem-config is an old-school config script supporting: teem-config --cflags teem-config --libs teem-config --version -- Header locations are exported as a FILE_SET for the sake of IDEs. -- Compiling a .c file in a Teem-using project used to find Teem headers with: -I/path/to/teeminstall/include but now it uses: -isystem /path/to/teeminstall/include which has the effect of silencing Teem-header-related warnings, which is probably the right choice, and seems to be the CMake convention? -- How Teem uses CTest has also been modernized. The `teem/Testing/` directory that previously held everything about using CTest is now (API CHANGE?): `teem/tests/ctest/` to recognize that Python tests are now possible with the CFFI wrappers. It is likely that the CTest files were not actually working anymore (the top-level CMakeLists.txt has made BUILD_TESTING default to OFF), but now all existing tests are running again. The newly re-written teem/tests/ctest/CMakeLists.txt use (via a new mini library testutil) environment variables (instead of a configured testDataPath.h.in) to communicate paths into the `teem/data` directory (teemTestDataPath()) or a new per-test-executable tmp directory (teemTestTmpPath()). The top-level DartConfig.cmake has been removed; anything about a dashboard (not that Teem has one now) can be done via the re-written CTestConfig.cmake. TestBuild.cmake is no longer used and will be removed soon. -- I fixed a long-standing and serious problem with CMake checks the teem/src/air.h AIR_EXISTS macro at configure-time. The AIR_EXISTS macro tries to do what isfinite() does in modern C: answer if a given floating point value is an IEEE754 special value (NaN or infinity). AIR_EXISTS is still in use in Teem because AIR_EXISTS predates wide availability of isfinite(). Since compilers can do unexpected things at high optimization levels (especially with things like -ffast-math enabled), it is important to test if AIR_EXISTS is really working. That is the job of teem/CMake/TestAIR_EXISTS.c , which is used by CMake configure-time tests to transform teem/CMake/airExistsConf.h to installdir/include/teem/airExistsConf.h. *However*: the long-standing and serious problem is that ever since this test was first created in 2015 (r6225 https://sourceforge.net/p/teem/code/6225/ ), how TestAIR_EXISTS.c was compiled (exactly which compiler flags) was likely different than the rest of how Teem was compiled, which undermines the meaningfulness of the test (!). With the update, great care is taken to ensure that how TestAIR_EXISTS.c is compiled matches how the rest of Teem will be compiled (given the current release type and associated flags), and now the compilation commands are shown for full transparency. -- Starting with revision r3717 on 2007-12-17, how CMake compiled and installed Teem had a "BUILD_EXPERIMENTAL" and later "BUILD_EXPERIMENTAL_LIBS" and "BUILD_EXPERIMENTAL_APPS". This started with a concern about "libraries that are in more active/unstable development". But that was almost 20 years ago. There are no such libraries anymore. Even for libraries that are being improved more actively, please refer to the "WITHOUT ANY WARRANTY" sections of the license. The boundary between experimental and not experimental had to be propagated into many lower source files that were thereby needlessly complicated, which became especially annoying for the one person keeping Teem development going. Also, with newly increased reliance on python/cffi wrapping (see below), the fact that the API of Teem can change depending on this experimental-or-not setting created stupid complications, e.g. how can Python code doing an "import teem" gracefully learn if that `teem` has the "experimental" bits or not? Its an unpythonic mess. Removing this was overdue, and a Teem v2 release was an additionally motivating opportunity. • 2025-08-28 05:17:10 +0000 (Thu, 28 Aug 2025) Revision: 7329 With a thorough modernization of how CMake is used to build Teem, one might think that the older GNUmake-based build system is obsolete. Turns out, GLK still prefers it for Teem hacking, since it offers more incremental per-library builds, and building of per-library test programs. The GNUmake build system was first written in ~2001 (before CMake was useful or popular) but was needlessly complicated, in part because it was designed for having multiple architectures share a networked filesystem. For Teem V2, GLK revisited all the GNUmakefiles and simplified and cleaned them up: -- there are no more per-archicture teem/src/make/foo.mk files (for foo in aix cygwin darwin irix6 linux netbsd solaris win32), and no more per-architecture (and per-sub-architecture) subdirectories in teem/arch. Instead, a new single teem/make/arch.mk contains the information that used to be in the per-arch foo.mk, or at least variables from which to hang this info. -- Everything about externals (that is, libraries we can optionally depend on: PNG ZLIB BZIP2 PTHREAD LEVMAR FFTW3) is moved into teem/src/make/externals.mk, because there is no need for setting that info in a architecture-dependent way. -------- !!!NOTE!!! variables like TEEM_PNG_IPATH and TEEM_PNG_LPATH (and TEEM_EXT_IPATH and TEEM_EXT_LPATH for all EXT) are still used, but are now set in teem/src/make/externals.mk (or on the command-line as variable set in the environment prior to "make"). More importantly, now they are true to their name: (API_CHANGE?) THE VALUES SET HERE SHOULD NO LONGER START WITH "-I" (for _IPATH) or "-L" (for _LPATH) Just set them to the path you'd use with -I and -L -- teem/make/errorCheck.mk removed (the system became so simple, there was nothing left to error check) -- there is no effort to make the monolithic ("megalib") libteem library, or any kind of shared library (CMake can do that) -- also removed: env var TEEM_ARCH env var TEEM_DEST (because now any "install" should be done via CMake) make vars ARCH, SUBARCH everything about shared libraries: TEEM_LINK_SHARED, SHEXT make var KNOWN_ARCH, make funcs checkArchSet, checkArchValid, checkArchLinux, checkArchLinux64, checkArchNetbsd64, checkArchDarwin • 2022-09-08 14:57:26 +0000 (Thu, 08 Sep 2022) Revision: 6818 ... 2022-09-08 11:39:47 +0000 (Thu, 08 Sep 2022) Revision: 6814 Other improvements to CMakeLists.txt and how it tests the AIR_EXISTS macro - Used to have "project(Teem ... LANGUAGES C CXX)", even though there is no C++ in Teem. Now fixed to be "LANGUAGES C" - CMake/testAirExists.cxx is now CMake/TestAIR_EXISTS.c, and it is a much more complete test of AIR_EXISTS behavior on finite and non-finite values - CMakeLists.txt (finally) tests whether TestAIR_EXISTS.c could compile ok, and makes it a FATAL_ERROR if not - Adds diagnostic messages about the outcome of the AIR_EXISTS test • 2019-09-16 21:39:15 +0000 (Mon, 16 Sep 2019) Revision: 6355 ... 2019-09-23 17:10:45 +0000 (Mon, 23 Sep 2019) Revision: 6356 Major modernization of how CMake is used to build Teem, by Zhuokai Zhao (during his internship at Kitware). From the outside things should look the same, but now the per-libary "sources.cmake" file is a "CMakeLists.txt". Updated the required version of CMake from 2.8 to 3.13...3.15. ========= how code is formatted ========= • 2022-07-09 16:33:32 +0000 (Sat, 09 Jul 2022) Revision: 6503 The formatting of code is now done via clang-format: https://clang.llvm.org/docs/ClangFormat.htm This has been applied to all the .h and .c files in all the libraries (though not as yet to the .c files in the test or Testing directories). The new .clang-format file in top-level of this repo is used with: clang-format -style=file This format file was developed to best approximate how formatting was already done in Teem; in some files the only changes relate to adding whitespace around operators. However, since clang-format works by parsing (with clang's parser) the code into an AST, and then re-writing it to respect the column limit, there are a number of places where clang-format can't preserve some important useful visual structure in the code created by whitespace or line breaks. This is especially true in the creation of airEnums (e.g. to clarify the relationship between equivalent strings and their enum values), and in the complicated declaration of gageKinds (as a kind of table). In these places the code is bracketed with: /* clang-format off */ ... /* clang-format on */ There are other places where some empty comments /* */ have been added to the end of short lines to preserve the appearance of a list. With time and experience the reliance on these tricks can hopefully be reduced. Despite these annoyances, GLK encourages you to use clang-format whenever you are editing Teem code, preferably with an editor that automatically applies formatting upon saves. As part of this reformatting, the number of columns for code has changed from 80 to 90. Not even GLK is using an 80x24 terminal anymore, and 80 columns is quite limiting: many things that were awkward-looking in 80 columns look cleaner in 90 columns after clang reformatting. 100 columns might make even more sense (and changing 90-->100 columns will be a smaller change than 80-->90); time will tell. ========= all libraries in general ========= "teem/python" has long had a "ctypes" subdir for an (automatically generated) ctypes-based Python module for all of Teem. Unfortunately, this relied on a hacked up version of gcc-xml, but gcc-xml is longer maintained (Kitware has moved on to https://github.com/CastXML/CastXML), so it is no longer convenient to regenerate this wrapper. There is a new clang-based approach: https://pypi.org/project/ctypeslib2/ but the scripts for massaging Teem header files will need to be updated to work with this. This hasn't happened yet. HOWEVER, there is a new/faster/better way of doing Python wrapping, via CFFI https://cffi.readthedocs.io/en/latest/; this is in teem/python/cffi Basically, you: - compile Teem with (cmake-powered) "make install" (NOTE: with BUILD_SHARED_LIBS ON in CMake) - cd teem/python/cffi - ./build_teem.py <teem-install-dir> (or "python3 ./build_teem.py <teem-install-dir>) which will generate a _teem Python extension module as a .so shared object, which in turn will want to link (at run-time) with the cmake-compiled libteem shared library. Then, the already-existing teem.py wraps _teem, with the benefit of new machinery for turning biff error messages into Python exceptions (something the ctypes wrappers were always missing). build_teem.py depends on exult.py: CFFI EXtension module Utilities for Libraries depending on Teem in the same directory, which also facilitates creating extension modules for other libraries that depend on Teem. As part of working on the new CFFI python wrappers, a new systematic effort was made to make sure that source files do not needlessly leave symbols as externally visible (in the libteem library), by making functions "static" and data "static const" whenever possible; much older code in Teem was written before GLK understood what "static" meant at the file scope. Also, many variables that were intentionally global were unintentionally variable (you could change their value); which was fixed with adding many more "const"s. Also, this ensured that header files do not declare things that are never actually defined. This is all in the new teem/src/_util/scan-symbols.py . An analogous teem/src/_util/scan-defines.py makes sure that things #defined really start with an upper-case library name. ========= air ========= • 2025-08-15 23:13:13 +0000 (Fri, 15 Aug 2025) Revision: 7275 API CHANGE: Dropping quiet NaN (qnan) / signalling NaN (snan) distinction. Removed/renamed some things in air.h: These enum values are gone: airFP_SNAN, /* 1: signalling NaN */ airInsane_QNaNHiBit, /* 7: airMyQNaNHiBit is wrong */ with this enum value renamed: airFP_QNAN --> airFP_NAN #define AIR_FP_MAX changed 10 --> 9 #define AIR_INSANE_MAX changed 11 --> 10 Gone: extern const unsigned int airMyQNaNHiBit; #define AIR_QNANHIBIT (airMyQNaNHiBit) extern const airFloat airFloatSNaN; #define AIR_SNAN (airFloatSNaN.f) Renamed: extern const airFloat airFloatQNaN --> airFloatNaN; #define AIR_QNAN --> AIR_NAN • 2025-08-15 18:01:56 +0000 (Fri, 15 Aug 2025) Revision: 7273 API CHANGE: Removed support for SGI IRIX Direct I/O (a faster data I/O to bypass kernel buffers), by removing air/dio.c, and everything related from air.h: airInsane_dio enum value gone #define AIR_DIO gone #define AIR_INSANE_MAX 12 --> 11 These functions gone: extern const char *airNoDioErr(int noDio); extern const int airMyDio; extern int airDisableDio; extern void airDioInfo(int *align, int *min, int *max, int fd); extern int airDioTest(int fd, const void *ptr, size_t size); extern void *airDioMalloc(size_t size, int fd); extern size_t airDioRead(int fd, void *ptr, size_t size); extern size_t airDioWrite(int fd, const void *ptr, size_t size); Also, in nrrd.h's NrrdFormat struct, removed the third integer field: usesDIO; /* this format can use Direct IO */ since it is moot now. The functions airDioTest(), airDioMalloc(), airDioRead(), and airDioWrite() involved calls to fcntl(fd, F_DIOINFO, &dioinfo) with a struct dioattr dioinfo to learn the DirectI/O specifics (of data size and pointer aligment), before calling fcntl(fd, F_SETFL, ... FDIRECT)) to enable it prior to read() and write() calls. This API was different from and predates using the O_DIRECT flag to open(), which is current practice on Linux (at least). Given current differences in how various platforms do fast I/O, it would be nice if Teem had some functions for handling that in a platform-independent way. Some day. Removing 25-year-old SGI IRIX Direct I/O code is not a step backward from that goal, because no current hardware uses that exact API. Even if it's really IRIX running, but in emulation on non-SGI hardware, the concept of extra-fast I/O would be moot. • 2023-07-07 15:01:09 -0500 (Fri, 07 Jul 2023) Revision: 7034 In air.h and in most other Teem libraries: something that is an API CHANGE (but weirdly not an ABI change) to fix a long-standing annoyance. air.h has always defined some hacky small, medium, large, and huge string lengths. The idea with these is that some code is simpler to write if string buffers don't have to be heap-allocated and later freed (buffers within stack go away on their own), and/or, calling string-handling functions is simpler if the function does not allocate strings that the caller has to free later. In the old code there was: #define AIR_STRLEN_SMALL 129 #define AIR_STRLEN_MED 257 #define AIR_STRLEN_LARGE 513 #define AIR_STRLEN_HUGE 1025 About these, air.h has long said: * The possibly unfortunate convention that has become established in Teem is code using * these tends to NOT add the +1 to explicitly indicate the space for 0-termination, and * instead assumes it is part of the numbers below, even though this is at the cost of * confusion about how the maximal strlen() will be less than each of * these numbers. This will be addressed in Teem 2.0. Now is the time to fix this. Now they are: #define AIR_STRLEN_SMALL 128 #define AIR_STRLEN_MED 256 #define AIR_STRLEN_LARGE 512 #define AIR_STRLEN_HUGE 1024 It was never the case that the memory-safety of Teem code rested on any numerical relationship between these lengths: Teem code that writes into a fixed-length buffer is still careful to not over-write the buffer (i.e. SMALL, MED, LARGE, HUGE merely describe the intended utility of the buffer). However, now all Teem code has been revisited and revised so that any use of these now adds "+ 1" to the code, e.g.: - char stmp[AIR_STRLEN_SMALL]; + char stmp[AIR_STRLEN_SMALL + 1]; So, the compiled buffer size ends up being the same (hence not an ABI change). Still, any non-Teem Teem-using code that has explicitly used an AIR_STRLEN should now also add "+ 1". Specifically, existing code that calls Teem functions that take a pre-allocated string buffer (to write into) should be tweaked so that the passed buffer is matches the expected size. Here is a complete list: int airEnumCheck(char err[AIR_STRLEN_LARGE + 1], const airEnum *enm); void airTeemVersionSprint(char buff[AIR_STRLEN_LARGE + 1]); char *airSprintSize_t(char str[AIR_STRLEN_SMALL + 1], size_t val); char *airPrettySprintSize_t(char str[AIR_STRLEN_SMALL + 1], size_t v); char *airSprintPtrdiff_t(char str[AIR_STRLEN_SMALL + 1], ptrdiff_t v); const char airTypeStr[AIR_TYPE_MAX + 1][AIR_STRLEN_SMALL + 1]; int nrrdBoundarySpecSprint(char str[AIR_STRLEN_LARGE + 1], const NrrdBoundarySpec *bspec); int nrrdBoundarySpecCompare(const NrrdBoundarySpec *bspecA, const NrrdBoundarySpec *bspecB, int *differ, char explain[AIR_STRLEN_LARGE + 1]); int nrrdCompare(const Nrrd *ninA, const Nrrd *ninB, int onlyData, double epsilon, int *differ, char explain[AIR_STRLEN_LARGE + 1]); int nrrdAxisInfoCompare(const NrrdAxisInfo *axisA, const NrrdAxisInfo *axisB, int *differ, char explain[AIR_STRLEN_LARGE + 1]); int nrrdArrayCompare(int type, const void *valA, const void *valB, size_t valNum, double epsilon, int *differ, char explain[AIR_STRLEN_LARGE + 1]); int nrrdKernelSpecSprint(char str[AIR_STRLEN_LARGE + 1], const NrrdKernelSpec *ksp); int nrrdKernelSprint(char str[AIR_STRLEN_LARGE + 1], const NrrdKernel *kernel, const double kparm[NRRD_KERNEL_PARMS_NUM]); int nrrdKernelCompare(const NrrdKernel *kernA, const double parmA[NRRD_KERNEL_PARMS_NUM], const NrrdKernel *kernB, const double parmB[NRRD_KERNEL_PARMS_NUM], int *differ, char explain[AIR_STRLEN_LARGE + 1]); int nrrdKernelSpecCompare(const NrrdKernelSpec *aa, const NrrdKernelSpec *bb, int *differ, char explain[AIR_STRLEN_LARGE + 1]); int gageStackBlurParmCompare(const gageStackBlurParm *sbpA, const char *nameA, const gageStackBlurParm *sbpB, const char *nameB, int *differ, char explain[AIR_STRLEN_LARGE + 1]); int gageStackBlurParmSprint(char str[AIR_STRLEN_LARGE + 1], const gageStackBlurParm *sbp, int extraFlag[256], char *extraParm); int pullInfoSpecSprint(char str[AIR_STRLEN_LARGE + 1], const pullContext *pctx, const pullInfoSpec *ispec); int meetPullVolLeechable(const meetPullVol *lchr, const meetPullVol *orig, int *can, char explain[AIR_STRLEN_HUGE + 1]); There are also many Teem structs with buffers with an AIR_STRLEN size. Existing code using these structs does NOT have to be modified to use the new struct. But if you are creating your own instances of these structs (e.g. creating your own hestCB to parse something weird from the command-line), then your definition of the struct should also be adjusted. Here is the full struct list: hestCB: int (*parse)(void *ptr, const char *str, char err[AIR_STRLEN_HUGE + 1]); NrrdFormat: char name[AIR_STRLEN_SMALL + 1]; NrrdEncoding: char name[AIR_STRLEN_SMALL + 1], suffix[AIR_STRLEN_SMALL + 1]; NrrdKernel: char name[AIR_STRLEN_SMALL + 1]; gageContext: char errStr[AIR_STRLEN_LARGE + 1]; gageKind: char name[AIR_STRLEN_SMALL + 1]; baneRange: char name[AIR_STRLEN_SMALL + 1]; baneInc: char name[AIR_STRLEN_SMALL + 1]; baneClip: name[AIR_STRLEN_SMALL + 1]; baneMeasr: char name[AIR_STRLEN_SMALL + 1]; tenModelParmDesc: char name[AIR_STRLEN_SMALL + 1]; tenModel: char name[AIR_STRLEN_SMALL + 1], char *(*sprint)(char str[AIR_STRLEN_MED + 1], const double *parm); pullEnergy: char name[AIR_STRLEN_SMALL + 1]; coilKind: char name[AIR_STRLEN_SMALL + 1]; coilMethod: char name[AIR_STRLEN_SMALL + 1]; pushEnergy: char name[AIR_STRLEN_SMALL + 1]; miteUser: char shadeStr[AIR_STRLEN_MED + 1], normalStr[AIR_STRLEN_MED + 1]; • fixed BUG in airOneLinify reported by Michael Götz <m.goetz@*.de>: I found a bug in the NrrdIO-library. It is in the file NrrdIO/string.c in the function airOneLinify() (be more specific in line 274-278) If a non-printable character is in the given string, every element it is replaced by copying the following elements back by one and reading the given position again. The problem is, that \0 will also be copied and therefore s[len-1] will be \0. Since \0 is non-printable the algorithm will continue to repeat the "remove-nonprintable"-part again and again. -> Endless loop. The same bug was reported 2014-04-02 (with patch) by dperry: https://sourceforge.net/p/teem/bugs/13/ The fix currently in the code is based on the dperry patch. • 2014-01-27 06:01:56 +0000 (Mon, 27 Jan 2014) Revision: 6161 Fixing long-standing BUG in airIndex(), seen when clamping output in nrrdArithAffine and nrrdArithIterAffine, in the case that the min output value is larger than the max output value (that is, the caller wishes, completely legitimately, to reverse the sense of the values). Also affects "unu affine" • 2022-09-08 16:07:36 +0000 (Thu, 08 Sep 2022) Revision: 6820 - removed from airSanity() the static int _airSanity, effectively a global variable that meant that airSanity() could only do its work once. Things like floating point rounding mode can be changed at run-time, which makes it more reasonable to re-run the FP-related tests. The non-FP tests are simple and should be fast to do. If profiling reveals this to be a bottleneck we can reconsider. - air.h, sane.c: added airInsane_ExistsBad to airInsane enum, which airSanity returns when some finite numbers are described by AIR_EXISTS as non-existant • 2022-08-16 06:36:42 -0500 (Tue, 16 Aug 2022) Revision: 6763 Ever since this revision: r5612 | kindlmann | 2012-09-23 17:05:07 the FP_SET_F macro of 754.c was broken, because GLK used "<" instead of "<<" to create a bit mask. The bit mask was apparently created to quiet some warnings, but it completely broke the code, 10 years ago (yikes). As part of this BUG FIX, GLK added some things: - a new const airEnum *const airFPClass_ae to contain the values in the airFP_* enum - new AIR_FP_MAX #define in air.h for max valid value of airFP_* enum and changed some things: - airMyQNaNHiBit [SINCE REMOVED!] changed from const int to const unsigned int, because any kind of bit pattern useful for bit-level hacking should be unsigned (though that became moot later, with dropping qnan-vs-snan support) - 754.c was largely re-written, to totally avoid the use of any endian- specific structs. FP_SET_F no longer exists, replaced with PARTSHIFT_F. There is much less copy-pasta as a result, and the code is more legible. • API NEW: Added airTeemVersionSprint() as a uniform way to generate information about the Teem version. • 2023-08-04 17:18:29 +0000 (Fri, 04 Aug 2023) Revision: 7147 API NEW: Added functions based on "Jenkins Fast Small" RNG: http://burtleburtle.net/bob/rand/smallprng.html Timing tests haven't yet been done, but these are certainly much smaller, and likely faster than the Mersenne-Twister-based functions that have been in Teem for a long time (see air.h airRandMTState). New tiny struct and functions: (air.h) typedef struct { unsigned int a, b, c, d; } airJSFRand; airJSFRand *airJSFRandNew(unsigned int seed); airJSFRand *airJSFRandNix(airJSFRand *jsf); void airJSFRandSeed(airJSFRand *jsf, unsigned int seed); unsigned int airJSFRandVal(airJSFRand *jsf); unsigned int airJSFRandValMod(airJSFRand *jsf, unsigned int N); float airJSFRandUni_f(airJSFRand *jsf); /* [0,1) */ float airJSFRandBiUni_f(airJSFRand *jsf); /* (-1,1) */ float airJSFRandNormal_f(airJSFRand *jsf); void airJSFRandNormal2_f(airJSFRand *jsf, float val[2]); double airJSFRandUni_d(airJSFRand *jsf); /* [0,1) */ double airJSFRandBiUni_d(airJSFRand *jsf); /* (-1,1) */ double airJSFRandNormal_d(airJSFRand *jsf); void airJSFRandNormal2_d(airJSFRand *jsf, double val[2]); int airJSFRandSanity(void); The airJSFRand{Uni,BiUni}_{f,d} functions use a very principled approach to generating random floats inside the interval: NOT simply taking the 32-bit unsigned int and dividing by 2^32. This means they can in in principle generate denormal values, which means airJSFRandNormal_{f,d} can sample far into the tails of the Gaussian. Reproducibility concerns prevent the large-scale replacement of the airRandMTState based random number use in Teem with airJSFRand (at least in nrrd and unu), but changes may happen in other libraries. • Added new wrappers around AIR_CAST: #define AIR_UCHAR(x) AIR_CAST(unsigned char, x) #define AIR_USHORT(x) AIR_CAST(unsigned short, x) #define AIR_FLOAT(x) AIR_CAST(float, x) #define AIR_DOUBLE(x) AIR_CAST(double, x) Some of these are more used than others, but they do tend to make code less verbose, and the are pretty legible once you know about them. AIR_USHORT in particular is common when going from size_t values to uint values that are less annoying to print and work with. Some efforts have been made to make their use more uniform through-out the code, but these are incomplete and the results are likely insufficiently tested. • 2015-02-21 17:18:55 +0000 (Sat, 21 Feb 2015) Revision 6216 Added airBitsSet() to count how many bits are set in an unsigned int • 2015-05-14 01:06:15 +0000 (Thu, 14 May 2015) Revision: 6249 airSingleSscanf(), which handles parsing a floating-point value from a string, now parses "-pi" as -AIR_PI. • The body of some math functions have moved from gage to air: gageTauOfTee(tee) --> airTauOfTime(tee) gageTeeOfTau(tau) --> airTimeOfTau(tau) gageSigOfTau(tau) --> airSigmaOfTau(tau) gageTauOfSig(sig) --> airTauOfSigma(sig) The gage functions are now #defines for the air functions. This change allows unrrdu/unu to access these (see below). • 2018-01-02 22:42:59 +0000 (Tue, 02 Jan 2018) Revision: 6329 Made airSanity() work even with floating-point rounding mode changes. Quoting from a comment: On at least one computer GLK used, if fesetround(FE_DOWNWARD) has been called, then the above run-time generation of positive infinity fails; it instead rounds down to DBL_MAX. We err on the side of permissiveness, and opt not to try to detect the current rounding mode (because doing so in a C89-compliant way would be a pain), and thus flag AIR_EXISTS(pinf) as a problem only if pinf != DBL_MAX. On that one computer, fesetround(FE_UPWARD) did not hamper the above run-time negative infinity generation • 2015-11-05 07:05:15 +0000 (Thu, 05 Nov 2015) Revision: 6257 Added to airIndexULL and airIndexClampULL the logic for min > max that has already been part of airIndex and airIndexClamp since version 1.11 • 2018-07-22 12:39:29 +0000 (Sun, 22 Jul 2018) Revision: 6333 fixed airFPFprintf_f and airFPFprintf_d so that decimal expansion is of sufficient accuracy to indicate that same value • 2022-07-15 22:40:14 +0000 (Fri, 15 Jul 2022) Revision: 6511 added airIndexMirror64 and airIndexMirror32 which are for "mirroring" an index I to be within valid indices [0...N-1], which is used for -b mirror with unu resample and unu pad ========= biff ========= • 2013-10-25 07:51:39 +0000 (Fri, 25 Oct 2013) Revision: 6145 Fixing huge BUGs (discovered by clang checker) in biffMsgAdd(), biffMsgMove(), biffMsgStrSet() (maybe others?): the exit(1)'s that had been removed on the principle that teem shouldn't exit, were NOT replaced with a return, or anything that would terminate function execution, so the function would continue onward past the fatal condition. wow • 2022-08-04 22:09:25 +0000 (Thu, 04 Aug 2022) Revision: 6691 API CHANGE: moving all the biffMsg-related functions stuff into privateBiff.h, since nothing else outside of biff was using them (a few functions could then also become static). • Not in biff itself, but in how other Teem code uses biff, and self-documents its use: There is now an automated way (with teem/src/_util/scan-symbols.py -biff) to annotate functions, with a short comment at the start of their definition to document their usage (or not) of biff, and how to interpret the return values. This is described in the new file teem/src/biff/README.txt . This annotation allows the new CFFI Python wrappers to do error checking, and convert biff errors into Python exceptions. Developing this recovery of biff usage (called the "biff auto-scan" for no good reason) uncovered many many little BUGs throughout Teem (inconsistent return values upon error, or no return after a biff call, or biff message being added to wrong key, or biffAddf called instead of biffGetDone at end, ...); these were all fixed but no detailed log has been recorded here. A second script teem/src/_util/gen_biffdata.py generates teem/python/cffi/biffdata/*.csv files by scanning Biff annotations, and this also confirms that each biff-using functions correctly name itself with a static const char me[] definition (and this uncovered many other little BUGs). NOTE: A consequence of this annotation is that now the space after a function return type (and qualifiers), and before the next line with the start of the function definition, is now reserved for Biff: annotations, described in detail in teem/src/biff/README.txt. In the future, other annotations may go there. The use of clang-format helps ensure that that space is uniformly created. ========= hest ========= • 2023-07-11 21:18:05 +0000 (Tue, 11 Jul 2023) Revision: 7071 API NEW:Many (85 to be exact) non-var-args alternatives to hestOptAdd, also usefully type-specific for the type of value to be parsed in a way that (non-var-args) hestOptAdd_nva cannot match. These capture all the common uses (and them some) of hest within Teem (and Teem-using non-Teem code that GLK maintains). They functions can be categorized, like hestOpt->kind, in terms of the min and max number of (type T) parameters to the option: min == max == 0 hestOptAdd_Flag stand-alone flag; no parameters min == max == 1 hestOptAdd_1_T single fixed parameter min == max >= 2 hestOptAdd_{2,3,4,N}_T multiple fixed parameters min == 0; max == 1 hestOptAdd_1v_T single variable parameter min < max; max >= 2 hestOptAdd_Nv_T multiple variable parameters T is one of: Bool (int), Int, UInt, LongInt, ULongInt, Size_t, Float, Double, Char, String (char *), Enum (int), or Other (void*) (wherein the C type is either obvious or indicated parenthetically). These functions are generated mostly automatically by C pre-processor gymnastics (see src/hest/adder.c), but the resulting functions require no trickery to use. An airEnum* is passed for _Enum options; or a hestCB* for _Other options. The number of parameters *sawP that hestParm saw on the command-line is passed for the _Nv_ options. Using hestOptAdd() and even the non-var-args hestOptAdd_nva() has always been harrowing because they could do no useful typechecking on the arguments that received the values parsed from the command-line. Those days are now thankfully over. • 2023-06-24 15:48:11 +0000 (Sat, 24 Jun 2023) Revision: 6994 ABI CHANGE and API NEW: FINALLY added consistent handling of "--help" as a command-line argument. Embarrassing that it took this long. Implementing this was actually easy, once some hest code was reorganized and renamed in a more sensible way. This is really a breaking ABI change (need to re-compiling and re-linking), but not a breaking API change: there are new struct members hestParm->respectDashDashHelp and hestOpt->helpWanted. GLK doesn't see a way of optionally enabling this functionality without adding struct members. However, hestParse still behaves the same way, in that it returns: - zero to mean that hestParse could set values for all the options (either from the command-line or from supplied defaults), and - non-zero to mean that there was an error parsing the command-line arguments But seeing and recognizing "--help" means that options have NOT had values set, and yet, it's not an error, which is outside the zero/non-zero dichotomy. But that dichotomy is the precedent, so we should work with it if possible. Now, with hestParm->respectDashDashHelp (which is NOT true by default), upon seeing "--help", hestParse returns 0, and sets helpWanted in the first hestOpt, so the caller will have to know to check for that. This logic is handled by (a completely re-written) hestParseOrDie, but maybe in the future there can be a different top-level parser function that turns on parm->respectDashDashHelp and knows how to check the results. Recall that hestParse is passed an array of hestOpt structs (not an array of pointers to hestOpt structs), like "hestOpt *opt". Information is set inside each hestOpt by the action of hestParse, parsing the corresponding command-line option. There is no other top-level information for the outcome of hestParse, so GLK opted to add a "helpWanted" field to the hestOpt struct, so that the user of hestParse can then check opt[0].helpWanted or opt->helpWanted. With the re-write of hestParseOrDie, there another tiny change (API CHANGE?) * the exit() status is now 0 (unix for "no error") in some cases when it used to be 1 ("oops error"): when "--help" is the only element of the given argv, and when the given argv is empty (the implicit request for usage info). • 2023-08-11 06:50:47 +0000 (Fri, 11 Aug 2023) Revision: 7152 API NEW: added hestParmColumnsIoctl() as a function for containing all the smarts about using ioctl() to get the current terminal width, and set hestParm->columns to that, so that all the usage information looks more useful. A few programs (mainly unu and tend) had contained the same smarts, but it makes much more sense to make it into a function. • 2023-06-23 18:25:02 +0000 (Fri, 23 Jun 2023) Revision: 6985 Fixed a BUG that could cause hestParse() to crash when "--" (or rather "-" followed by hparm->varParamStopFlag) was seen prior to getting all the parameters to a flagged option. Later work (ending with r7018 on 2023-07-02) would clarify that "--" is the marker for the end of parameters to a *flagged* multiple variable parameter option, and can be used once per such option. It is not a brick wall that ends all hestParse action. • 2023-06-21 12:37:49 +0000 (Wed, 21 Jun 2023) Revision: 6964 ... 2023-06-21 13:50:33 +0000 (Wed, 21 Jun 2023) Revision: 6965 As part of revisiting all hest code (with an eye to implementing --help), realized that hest was the only Teem library to have its default globals not named with "Def" or "Default". Rather than avoid fixing this (after a decade of no releases) for fear of the API CHANGE that it clearly is, GLK opted to fix it now, since likely zero other people will notice (all these controls were more likely set via the corresponding hestParm fields). So these global variables are renamed: int hestVerbosity --> hestDefaultVerbosity int hestRespFileEnable --> hestDefaultRespFileEnable int hestElideSingleEnumType --> hestDefaultElideSingleEnumType; int hestElideSingleOtherType --> hestDefaultElideSingleOtherType int hestElideSingleOtherDefault --> hestDefaultElideSingleOtherDefault int hestElideSingleNonExistFloatDefault --> hestDefaultElideSingleNonExistFloatDefault int hestElideMultipleNonExistFloatDefault --> hestDefaultElideMultipleNonExistFloatDefault int hestElideSingleEmptyStringDefault --> hestDefaultElideSingleEmptyStringDefault int hestElideMultipleEmptyStringDefault --> hestDefaultElideMultipleEmptyStringDefault int hestNoArgsIsNoProblem --> hestDefaultNoArgsIsNoProblem int hestGreedySingleString --> hestDefaultGreedySingleString int hestCleverPluralizeOtherY --> hestDefaultCleverPluralizeOtherY unsigned int hestColumns --> hestDefaultColumns char hestRespFileFlag --> hestDefaultRespFileFlag char hestRespFileComment --> hestDefaultRespFileComment char hestVarParamStopFlag --> hestDefaultVarParamStopFlag char hestMultiFlagSep --> hestDefaultMultiFlagSep • 2016-11-22 20:02:54 +0000 (Tue, 22 Nov 2016) Revision: 6295 in hest/parseHest.c _hestSetValues(): fixed long-standing BUG: no longer call calloc(0,n) when there are zero items in a parse-0-or-more situation • Two small API NEWs that facilitate some long-wanted functionality (1) (ABI CHANGE): in the hestOpt struct there is now a new source field to indicate how it was that that option got its information. The values come from the new hestSource* enum (e.g. hestSourceDefault vs hestSourceUser). (2) API CHANGE? hestOptAdd returns the index of the just-added option in the opt array; to facilitate accessing it and its source field later. Now, its easy to see which options were actually satisfied by explicit info from the user, vs being filled with default info. As previously noted in teem/src/TODO.txt; "hest NEEDS a way to learn if a given option was learned from the command-line, or from the given default string. Various tricks have been used to work around this limitation, but its gotten too annoying," so this is apparently a long-wanted improvement. It turns out that it only took a few lines of code. The age and impenetrability of the hest code likely is at fault. • 2019-12-09 10:10:51 +0000 (Mon, 09 Dec 2019) Revision: 6382 API NEW: added hestParm->dieLessVerbose and hestParm->noBlankLineBeforeUsage to permit tightening up how usage information is printed. dieLessVerbose works in hestParseOrDie to skip printing the info and the glossary in case of parse error; these are still shown with --help. noBlankLineBeforeUsage just skips printing the one blank line prior to Usage, but as a hack it is added to the end of Info to keep the two separated. These are currently not actually used anywhere within Teem, but GLK uses them for his SciVis class code. • Added const-correctness to hestParm *hparm usage in hestParse(), hestUsage(), hestGlossary(), and hestInfo(). Some const-correctness for hestOpt *opt usage was also added for private functions. • 2019-08-07 22:37:37 +0000 (Wed, 07 Aug 2019) Revision: 6348 More long overdue hest-related const correctness: the hestCB struct's parser function should have been taking a const char * as a second arg (various files in other libraries updated to conform). ========= nrrd ========= These first two are opportunistic fixes now that the major version is getting bumped: • 2025-09-09 15:18:04 +0000 (Tue, 09 Sep 2025) Revision: 7377 API CHANGE: NrrdKernel->numParm (the number of parameters to the kernel) is now renamed NrrdKernel->parmNum. This is for consistency with naming conventions everywhere else in Teem • 2025-08-15 19:04:55 -0500 (Fri, 15 Aug 2025) Revision: 7283 API CHANGE (related to Direct I/O change in "air" above) In nrrd.h's NrrdFormat struct, removed the third integer field: usesDIO; /* this format can use Direct IO */ since it is moot now. • API NEW (ABI CHANGE): added new field to NrrdIoState: declineStdioOnTTY, described as follows in nrrd.h: ON READ and ON WRITE: If nrrdLoad is about to read from filename "-" (via nrrdRead), or nrrdSave is about to write to "-" (via nrrdWrite), regardless of file format (which isn't known on read, though may be known on write): if this is non-zero, decline to nrrdRead from stdin or nrrdWrite to stdout IF stdin/stdout seems to be a terminal (as per isatty()). On read, this avoids cryptic stalls as something tries to read from the terminal (where a human is unlikely to be typing the file contents), and on write, this avoids clobbering the terminal with screens of non-printing characters. Using filename "-=" is a sneaky filename to name stdin/stdout while over-riding this declination and force that IO to happen. While nrrdHestNrrdNoTTY largely handled this problem on input when a plain Nrrd is being read via hest, it didn't help with situations where the thing being input is a some struct different from but wrapped around Nrrd. The terminal being clobbered by a NRRD file being written is also a long-standing annoyance that really needed fixing. Thus, the new (global!) nrrdDefaultDeclineStdioOnTTY is set to AIR_TRUE, which sets the default value of NrrdIoState->declineStdioOnTTY. • NRRD FILE FORMAT CHANGE: There is a new "magic", NRRD0006, used for three new things: - new ZRL encoding - new 2D image orientation spaces nrrdSpaceRightUp and nrrdSpaceRightDown - new "data file: SKIPLIST" capability, which is like "data file: LIST" but every filename is preceded by a number of bytes to skip into that file (and then a space) These are described more below • 2023-07-13 22:28:30 +0000 (Thu, 13 Jul 2023) Revision: 7092 API NEW: nrrdHestNrrdNoTTY for parsing a Nrrd via hest callbacks (hestCB), but with the restriction that "-" does not work as a synonym for stdin if stdin is a terminal: we only want to try to read from files being piped in. This enables some LONG-wanted functionality in unu, wherein the -i input can default to "-", while still allowing "unu cmd" to trigger some usage info. After this was implemented, similar functionality was added deeper, in nrrdLoad and nrrdSave, controlled by the NrrdIoState->declineStdioOnTTY (see above) • 2022-09-08 16:49:23 +0000 (Thu, 08 Sep 2022) Revision: 6822 (similar change in airSanity() above) removing from nrrd/simple.c nrrdSanity() the static int _nrrdSanity which meant that nrrdSanity() can only go through its tests once. Considering that nrrdSanity() is called by _nrrdRead (which underlies nrrdRead and nrrdLoad, there is a small possibility that this slows down something that rapidly reads in many many files. It will be great to see profiling results that reveal nrrdSanity() to be a bottleneck; in the mean time it is much more sane to have less global state. • 2021-11-27 11:17:32 +0000 (Sat, 27 Nov 2021) Revision: 6485 Adding long-wanted functionality in specifying ranges (for histograms and quantization): zero-centering, so that range is centered around zero even if input is not, because sometimes the values are logically centered around zero (e.g. an array of vorticity values) and processing should respect that. New boolean-ish zeroCenter argument added to nrrdRangePercentileFromStringSet(), and accessed via new -zc command-line option to unu quantize, histo, histax • 2020-10-09 11:54:45 +0000 (Fri, 09 Oct 2020) Revision: 6435 (BUG fixed) It turns out that nrrdSliceSelect (and the unu sselect that calls it) never fully worked: it only worked for slicing along the slowest axis. It now seems to work along any axis • 2022-09-02 03:30:26 +0000 (Fri, 02 Sep 2022) Revision: 6796 While using "unu subst" to help enumerate some possibilities for Wordle 439, GLK realized that nrrdApply1DSubstitution had a BUG from 2004: if value0 was mapped to value1 via the substitution table, you should stop traversing the table. Bug was that with continued table traversal, value1 could be mapped to something else, counter to intended semantics. • 2022-10-04 08:45:45 +0000 (Tue, 04 Oct 2022) Revision: 6839 fixing very long-standing BUGs in the nrrdFClamp[] and nrrdDClamp[] function arrays, which are supposed to clamp float or double args to the representational range of a given nrrdType. However, because, for example, the extrema INT_MAX is not exactly representable as a float, you could have the clamping completely failing, by creating a positive value that, upon casting to int, would overflow the representation and become negative. • (Jan 2015) added nrrdEncodingZRL, a very simple run-length encoding scheme, which only compresses strings of zeros, but not other values (ZRL = zero run-length). Based on code from (University of Chicago Physics) Prof. William Irvine's lab. NRRD headers can say "encoding: zrl" to use this. • 2015-01-19 16:24:09 +0000 (Mon, 19 Jan 2015) Revision: 6209 for describing array orientation, added nrrdSpaceRightUp (oriented like upper right Cartesian quadrant, number I) and nrrdSpaceRightDown (oriented like typical raster coordinates) • 2016-05-25 13:07:59 -0500 (Wed, 25 May 2016) Revision 6287 Added nrrdNaNSet() for setting all values in floating-point nrrd to NaN • 2019-12-01 23:33:30 +0000 (Sun, 01 Dec 2019) Revision: 6361 fixing a REALLY (~20 year) old BUG in how single-precision floats are sprintf'ed to a string: should be using %.9g, not %.8g ! The consequence of this is that one array, saved a nrrdTypeFloat array to a NRRD file with ascii encoding would result in loss of information; yikes! The same bug hobbled saving to plain text files, though until the NrrdIoState->moreThanFloatInText update, expectations for lossless representations in plain text were probably (hopefully?) low. • Fixing long-standing BUG when clamping output in nrrdArithAffine and nrrdArithIterAffine, in the case that the min output value is larger than the max output value (that is, the caller wishes, completely legitimately, to reverse the sense of the values). Affects "unu affine" • Sun Mar 22 20:58:15 CDT 2015 revision 6220 fixed BUG caused by saving a 3D 1-by-X-by-Y array to an image format like PNG, with fields like space directions that can be usefully preserved in the image meta data: 3 space directions were saved in the image, not two. This fix is a pretty simplistic one, but it is used in a very targeted way, so it seems safe • 2016-06-24 15:12:54 +0000 (Fri, 24 Jun 2016) Revision: 6289 Fixed long-standing BUG that nrrdQuantize didn't corrently handle min > max. These days airIndex and airIndexClamp do handle min > max, but old code in nrrdQuantize used AIR_CLAMP prior to airIndex, which defeated those smarts • 2017-03-24 19:13:31 +0000 (Fri, 24 Mar 2017) Revision: 6305 in nrrdWrite: fixed basic BUG of unknown duration: units are only saved out if there were also labels. Now units are written if there are units to write • 2019-03-03 00:14:23 +0000 (Sun, 03 Mar 2019) Revision: 6346 fixing what seems to be a long-standing BUG in nrrd/apply1D.c: unu rmap crashed when given a map with only a single control point • 2015-05-05 06:10:39 +0000 (Tue, 05 May 2015) Revision: 6246 fixing long-standing BUG in nrrd/encodingAscii.c that put extra newline at end of ascii encoding of 2D array • 2015-01-26 16:27:48 +0000 (Mon, 26 Jan 2015) Revision: 6213 fixed long-stading BUG in nrrd/formatText.c: key/value pairs were never saved correctly in "plain" text files • 2022-11-11 16:38:15 +0000 (Fri, 11 Nov 2022) Revision: 6863 with revision 2368, which was part of adding new "space" orientation meta-data, nrrdField_space_directions WERE said to be valid in text files, but not space, space dimension, space units, or space origin; this was probably a BUG. Now that nio->bareText can be false, these should all be allowed in text files. • 2019-12-01 23:54:30 +0000 (Sun, 01 Dec 2019) revision 6363 API NEW (ABI CHANGE): Added NrrdIoState->moreThanFloatInText and associated enums and globals, as well as nrrdStringValsParse[] for parsing nrrdType values from a string, so that plain text files can have a "#type:" field that records the type of values in the text file being something other than just single-precision float. This capability can be enabled per-shell with: export NRRD_DEFAULT_WRITE_BARE_TEXT=false export NRRD_DEFAULT_WRITE_MORE_THAN_FLOAT_IN_TEXT=true "unu env" now prints info about nrrdDefaultWriteMoreThanFloatInText. • 2016-02-19 06:37:10 +0000 (Fri, 19 Feb 2016) Revision: 6269 Added nrrdBinaryOpAddClamp, nrrdBinaryOpSubtractClamp, nrrdBinaryOpMultiplyClamp (available as unu 2op +c, -c, xc) for clamping arithmetic output to value range of output type • 2016-02-14 03:59:58 +0000 (Sun, 14 Feb 2016) Revision: 6267 - Added nrrdKernelParm0IsScale() to answer an important question about a NrrdKernel that is unfortunately not knowable from the kernel itself: is parm[0] something that behaves like a scale parameter (for stretching or shrinking the kernel) or is it some other parameter of kernel shape. Various annoyances have originated with not remembering this about each kernel. - fixed long-standing BUG that prevented "unu resample" from correctly downsampling when using parameter-free kernels like ctmr or bspln3. This in turn exposes a deficiency in the NrrdKernel struct: there's no reliable way to know if parm[0] has the same effect it does as with older kernels like nrrdKernelTent, which is what motivated adding nrrdKernelParm0IsScale(). The fact of needed such a function is a sign that the NrrdKernel should be more self-aware. • 2023-06-14 21:22:09 +0000 (Wed, 14 Jun 2023) Revision: 6933 Added utility function nrrdKernelDerivative() which tries to determine the (1st) derivative of the given nrrdKernel. If it cannot (or doesn't know for sure), it returns a new fake kernel, nrrdKernelFlag. • 2023-06-20 09:24:42 +0000 (Tue, 20 Jun 2023) Revision: 6953 Completely re-wrote: nrrdKernelHann, nrrdKernelHannD, nrrdKernelHannDD nrrdKernelBlackman, nrrdKernelBlackmanD, nrrdKernelBlackmanDD nrrdKernelHann had an incorrect Taylor expansion, which meant that for the past 20 years it evaluated to 1.1 near and at 0, not 1.0; yikes. But actually, no Taylor expansion was needed for nrrdKernelHann and nrrdKernelBlackman, and the Taylor approximations for the other kernels are now C0 with the correct formula, via a new mechanism for scaling the higher Taylor term. With the improved quality of these functions, and the new punting of doing purely single-precision evaluation (see the commit message), meetNrrdKernelAllCheck can stop giving such an usually huge margin-of-error to the test of these kernels (fixed with revision 6954). • 2015-03-23 01:56:42 +0000 (Mon, 23 Mar 2015) Revision: 6222 fixed BUG caused by saving a 3D 1-by-X-by-Y array to an image format like PNG, with fields like space directions that can be usefully preserved in the image meta data: 3 space directions were saved in the image, not two. • Added nrrdKernelDiscreteGaussianGoodSigmaMax and removed gageDefStackBlurSigmaMax (or gageDefStackBlurGoodSigmaMax?), which gave the same information but was stupidly named and located • Added nrrdCastClampRound(), which combines casting with optional clamping and optional rounding • Added nrrdMeasureL4 (basis of unu project -m l4): the L4 vector norm • The support of nrrdKernelDiscreteGaussian can now be no smaller than 2.5; it used to be as small as 0.5. The problem is that the current calculation... [truncated message content] |