Volume data access methods. More...
#include <LDM/SoLDMDataAccess.h>
Classes | |
struct | CopyInfo |
struct | DataInfo |
Information about returned data when directly accessing the data rather than copying the data. More... | |
struct | DataInfoBox |
Information about data returned for an arbitrary box (subvolume). More... | |
struct | DataInfoLine |
Information about data returned for an arbitrary line. More... | |
struct | DataInfoPlane |
Information about data returned for an arbitrary plane. More... | |
class | DataInfoPolyLine |
Information about data returned for an arbitrary polyline. More... | |
struct | DataInfoTrace |
Information about data returned for an arbitrary trace (column of voxels). More... | |
Public Types | |
enum | ErrorValue { CORRECT, INCORRECT_RESOLUTION, INCORRECT_SUBVOLUME, INCORRECT_LINE, INCORRECT_POLYLINE, INCORRECT_PLANE, INCORRECT_POSITION, INCORRECT_COORDINATE, MEMORY_FULL, REQUEST_NOT_COMPLETED, REQUEST_NOT_FOUND, INCORRECT_BUFFER } |
enum | GetDataMode { CACHE, DIRECT, DIRECT_AND_PREFETCH } |
Public Member Functions | |
SoLDMDataAccess () | |
virtual | ~SoLDMDataAccess () |
void | setDataSet (SoDataSet *v) |
SoDataSet * | getDataSet () |
DataInfoBox | getData (int resolution, const SbBox3i32 &subVolume0, SoBufferObject *bufferObj=NULL) |
DataInfoPlane | getData (int resolution, const SbBox3i32 &subVolume0, const SbPlane &plane, SoBufferObject *bufferObj=NULL) |
DataInfoLine | getData (int resolution, const SbBox3i32 &subVolume0, const SbLine &line, SoBufferObject *bufferObj=NULL) |
DataInfoTrace | getData (int resolution, const SbBox3i32 &subVolume, const SbVec2i32 coord, SoBufferObject *bufferObj=NULL) |
void | getData (DataInfoPolyLine &infoPolyline, int resolution, const SbBox3i32 &subVolume, int numPoints, const SbVec3i32 *polyline, SoBufferObject *bufferObj=NULL) |
DataInfo | getData (int resolution, const SbVec3i32 &dataPosition) |
void | releaseData (SoLDMTileID tileID) |
SbBool | isTileUniform (int resolution, const SbVec3i32 &dataPosition, double &uniformValue) |
int | requestData (int resolution, const SbBox3i32 &box, SoBufferObject *bufferObj) |
void | getRequestedData (int requestId, DataInfoBox &infoBox) |
int | requestData (int resolution, const SbBox3i32 &subVolume, const SbPlane &plane, SoBufferObject *bufferObj) |
void | getRequestedData (int requestId, DataInfoPlane &infoPlane) |
int | requestData (int resolution, const SbBox3i32 &subVolume0, const SbLine &line, SoBufferObject *bufferObj) |
void | getRequestedData (int requestId, DataInfoLine &infoLine) |
int | requestData (int resolution, const SbBox3i32 &subVolume, int numPoints, const SbVec3i32 *polyline, SoBufferObject *bufferObj) |
void | getRequestedData (int requestId, DataInfoPolyLine &infoPolyline) |
int | requestData (int resolution, const SbBox3i32 &subVolume0, const SbVec2i32 &coord, SoBufferObject *bufferObj) |
void | getRequestedData (int requestId, DataInfoTrace &infoTrace) |
virtual void | endRequest (int requestId) |
SbVec3f | voxelToXYZ (const SbVec3i32 &dataPosition) |
SbBox3f | voxelToXYZ (const SbBox3i32 &boxIJK) |
SbVec3i32 | XYZToVoxel (const SbVec3f &dataPosition) |
SbBox3i32 | XYZToVoxel (const SbBox3f &boxXYZ) |
bool | setGetDataMode (const GetDataMode getDataMode) |
GetDataMode | getGetDataMode () |
Deprecated | |
| |
SoDEPRECATED DataInfoBox | getData (int resolution, const SbBox3i32 &subVolume, void *buffer) |
SoDEPRECATED DataInfoPlane | getData (int resolution, const SbBox3i32 &subVolume, const SbPlane &plane, void *buffer) |
SoDEPRECATED DataInfoLine | getData (int resolution, const SbBox3i32 &subVolume, const SbLine &line, void *buffer) |
SoDEPRECATED DataInfoTrace | getData (int resolution, const SbBox3i32 &subVolume, const SbVec2i32 coord, void *buffer) |
SoDEPRECATED void | getData (DataInfoPolyLine &infoPolyline, int resolution, const SbBox3i32 &subVolume, int numPoints, const SbVec3i32 *polyline, void *buffer) |
SoDEPRECATED int | requestData (int resolution, const SbBox3i32 &subVolume, void *buffer) |
SoDEPRECATED int | requestData (int resolution, const SbBox3i32 &subVolume, const SbPlane &plane, void *buffer) |
SoDEPRECATED int | requestData (int resolution, const SbBox3i32 &subVolume, const SbLine &line, void *buffer) |
SoDEPRECATED int | requestData (int resolution, const SbBox3i32 &subVolume, int numPoints, const SbVec3i32 *polyline, void *buffer) |
SoDEPRECATED int | requestData (int resolution, const SbBox3i32 &subVolume, const SbVec2i32 &coord, void *buffer) |
The Data Access API provides methods to conveniently extract data from a volume. This is useful, for example, to extract data for computation, for segmentation or for display using other primitives. The data is accessible whether the SoVolumeData is part of a scene graph or not. These methods are only valid in LDM mode (which is the default mode). The Data Access API automatically takes advantage of LDM multi-threaded data loading when multiple tiles (not already in cache) are needed to complete the request.
The Data Access API automatically loads all data required to satisfy the request and can be invoked asynchronously to allow simultaneous loading and computation. The application can request data at any resolution level, e.g. full resolution (level 0) data, independent of the resolution level currently being used for rendering. For some requests, e.g. line and plane, the application can also specify a subvolume (region of interest) that limits the extent of the data request.
SoLDMDataAccess provides the following data requests:
Each data request returns information struct specific to the request type. For example, SoLDMDataAccess::DataInfoBox is returned by the subvolume request. All these structs contain an errorFlag member containing the result of the request and a bufferSize member containing the amount of data (in bytes) returned. The application should always check these values. Note that even when errorFlag is CORRECT, bufferSize may be zero, meaning no data was returned. This happens, for example, if the specified plane is completely outside the volume.
The application is responsible for allocating and freeing the memory to store the requested data. Calling the data request method with a null bufferObject will return the size of the memory needed, in bytes, in the bufferSize member of the information struct. Once the buffer is allocated, a second call will extract the data from the volume. Since Open Inventor 8.0 we recommend using the methods that return data into an SoBufferObject. Because this class abstracts access to data stored on different devices it is possible, for example, to conveniently fetch data into CPU memory or directly into GPU memory.
Each data request can be either synchronous or asynchronous. Synchronous means that all the data will be loaded before the function returns. Asynchronous means that the function will return immediately and you will be notified when the data is actually available. This allows the application to continue to work while the data is being loaded, for example to overlap data loading and computation. Synchronous access is done using the getData methods.
To do asynchronous access, you must create a new class derived from SoLDMDataAccess and implement your own version of the endRequest method. Begin a request by calling the appropriate requestData method. This method will return a requestId.
Three different modes are available to control how data is requested from the volume reader when the necessary tiles are not already present in LDM cache memory:
Note that DIRECT access requires that specific methods are implemented in the data set reader, for example readXSliceInTile (see SoLDMReader and SoVolumeReader). The default LDM reader implements these methods for the standard Open Inventor LDM file format. Custom LDM readers may need to be enhanced.
// Create a volume data object and attach to a data file SoVolumeData* volData = new SoVolumeData(); volData->fileName = "<path-to-filename>"; // Call to get size of data int resolution = 0; // Full resolution data SbBox3i32 subvolume = SbBox3i32(0,0,0,63,63,63); SoLDMDataAccess& access = volData->getLdmDataAccess(); SoLDMDataAccess::DataInfoBox info = access.getData( resolution, subvolume ); if (info.errorFlag == SoLDMDataAccess::CORRECT) { // Create and size a buffer to store the data SoRef<SoCpuBufferObject> buffer = new SoCpuBufferObject; buffer->setSize( info.bufferSize ); // Call to get the actual data info = access.getData( resolution, subvolume, buffer.ptr() ); // Access the data then unmap the buffer unsigned int* pData = (unsigned int*)buffer->map(SoBufferObject::READ_ONLY); unsigned int value = pData[0]; . . . buffer->unmap(); }
See $OIVHOME/examples/source/VolumeViz/examples/getLDMData/asyncGetDataBox.cxx for a more complete example.
MedicalComputeHistogram, MedicalGetDataBox, MedicalGetDataBox_MultiThread, MedicalGetDataLine, MedicalGetDataPlane, MedicalGetDataPolyLine, ComputeHistogram, GetDataLine, GetDataPolyLine, GetDataPlane, GetDataBox, AsyncGetDataBox, GetDataBox_MultiThread, GetDataTrace, VolumeDataEditing
Error Code values.
Specifies the way data is requested from the volume reader when the necessary tiles are not already present in LDM cache memory.
See the class description for more info. See setGetDataMode() method.
SoLDMDataAccess::SoLDMDataAccess | ( | ) |
Constructor.
Application must call the setDataSet method before using any other methods of this object.
virtual SoLDMDataAccess::~SoLDMDataAccess | ( | ) | [virtual] |
Destructor.
void SoLDMDataAccess::endRequest | ( | int | requestId | ) | [inline, virtual] |
This method is called each time a data request is finished, meaning that the data requested asynchronously is now available.
This method should be overloaded in a derived class in order to handle loaded data.
SoDEPRECATED void SoLDMDataAccess::getData | ( | DataInfoPolyLine & | infoPolyline, | |
int | resolution, | |||
const SbBox3i32 & | subVolume, | |||
int | numPoints, | |||
const SbVec3i32 * | polyline, | |||
void * | buffer | |||
) |
SoDEPRECATED DataInfoTrace SoLDMDataAccess::getData | ( | int | resolution, | |
const SbBox3i32 & | subVolume, | |||
const SbVec2i32 | coord, | |||
void * | buffer | |||
) |
SoDEPRECATED DataInfoLine SoLDMDataAccess::getData | ( | int | resolution, | |
const SbBox3i32 & | subVolume, | |||
const SbLine & | line, | |||
void * | buffer | |||
) |
SoDEPRECATED DataInfoPlane SoLDMDataAccess::getData | ( | int | resolution, | |
const SbBox3i32 & | subVolume, | |||
const SbPlane & | plane, | |||
void * | buffer | |||
) |
SoDEPRECATED DataInfoBox SoLDMDataAccess::getData | ( | int | resolution, | |
const SbBox3i32 & | subVolume, | |||
void * | buffer | |||
) |
Returns a pointer to the block of data (LDM tile) which contains the voxel at dataPosition.
The resolution is the power of 2 of the desired subsampling level (0:1/1, 1:1/2, 2:1/4, ...). In most cases you will pass 0 to request full resolution data.
The data is not copied. The pointer is valid until releaseData() is called with the corresponding tile id or until the associated volumeData node is destroyed.
The LDM tile is locked in CPU memory until releaseData() is called. The application may lock multiple tiles in memory using this method. However these locked tiles count against the maximum CPU memory allowed for the volume. Locking too many tiles may reduce the rendered image quality because LDM does not have enough memory to load other tiles. A released tile will remain in memory (allowing quick re-access) for some period of time, depending on whether it is needed for rendering and (if not) on the value of SoLDMResourceParameters::tileHalfLife.
This method is a good solution when the application needs to get the values of multiple voxels that are not part of a line, plane or box. After locking a tile, for each subsequent voxel, check if the voxel position is contained in the currently locked tile. You can use the intersect() method on the SbBox3i32 tilePosition member of the DataInfo struct for this test. If the voxel is in the current tile, compute the offset into the data buffer and get its value. If not, release the current tile (using releaseData) and get the appropriate tile by calling getData() again.
// Compute the address of a voxel value in a tile. // We know the position of the desired voxel in absolute IJK coords and we // know the location of the tile in absolute IJK coords, so we can compute // the voxel position relative to the tile. But note that tiles on a "face" // of the volume are normally partial tiles, so we have to consider both the // min and max corners of the tile. SbVec3i32 voxelPos; // Absolute IJK position of desired voxel DataInfo dataInfo; // Tile info returned by getData() method SbVec3i32 tileSize = dataInfo.tileDimension; SbVec3i32 tilePosMin, tilePosMax; dataInfo.tilePosition.getBounds( tilePosMin, tilePosMax ); SbVec3i32 relPos; // Relative IJK position of desired voxel relPos[0] = (voxelPos[0] - tilePosMin[0]) * tileSize[0] / (tilePosMax[0] - tilePosMin[0] + 1); relPos[1] = (voxelPos[1] - tilePosMin[1]) * tileSize[1] / (tilePosMax[1] - tilePosMin[1] + 1); relPos[2] = (voxelPos[2] - tilePosMin[2]) * tileSize[2] / (tilePosMax[2] - tilePosMin[2] + 1); // Offset into tile data. First in voxels, then in bytes. int64_t offset = (relPos[2]*tileSize[1] + relPos[1])*tileSize[0] + relPos[0]; offset *= volumeData->getDatumSize(); // Address of voxel value unsigned char* voxelPtr = ((unsigned char *)dataInfo.tileData) + offset;
void SoLDMDataAccess::getData | ( | DataInfoPolyLine & | infoPolyline, | |
int | resolution, | |||
const SbBox3i32 & | subVolume, | |||
int | numPoints, | |||
const SbVec3i32 * | polyline, | |||
SoBufferObject * | bufferObj = NULL | |||
) |
Given a subvolume in voxel coordinates and a polyline (set of connected line segments), copies the data intersecting each line segment with the subvolume into an application buffer.
The resolution is the power of 2 of the desired subsampling level (0:1/1, 1:1/2, 2:1/4, ...) NOTE: The data is copied.
All information is returned in the struct DataInfoPolyline. Call this method with buffer = NULL to get the required size of the application buffer (in the bufferSize member of DataInfoLine).
DataInfoTrace SoLDMDataAccess::getData | ( | int | resolution, | |
const SbBox3i32 & | subVolume, | |||
const SbVec2i32 | coord, | |||
SoBufferObject * | bufferObj = NULL | |||
) |
Data values in a single seismic trace (a row of voxels along the volume X axis) are copied into an application buffer.
The trace is identified by a YZ voxel coordinate. The range of values returned is the intersection of the trace with the specified subvolume. The resolution is the power of 2 of the desired subsampling level (0:1/1, 1:1/2, 2:1/4, ...) NOTE: The data is copied.
Returns a DataInfoTrace struct containing errorFlag, bufferSize (number of bytes) and bufferDimension (number of values). Call this method with buffer = NULL to get the required size of the application buffer (in the bufferSize member of DataInfoTrace).
DataInfoLine SoLDMDataAccess::getData | ( | int | resolution, | |
const SbBox3i32 & | subVolume0, | |||
const SbLine & | line, | |||
SoBufferObject * | bufferObj = NULL | |||
) |
Given a subvolume in voxel coordinates and a line, copies the data intersecting the line and the subvolume into an application buffer.
The resolution is the power of 2 of the desired subsampling level (0:1/1, 1:1/2, 2:1/4, ...) NOTE: The data is copied.
All information is returned in the struct DataInfoLine. Call this method with buffer = NULL to get the required size of the application buffer (in the bufferSize member of DataInfoLine).
DataInfoPlane SoLDMDataAccess::getData | ( | int | resolution, | |
const SbBox3i32 & | subVolume0, | |||
const SbPlane & | plane, | |||
SoBufferObject * | bufferObj = NULL | |||
) |
Given a subvolume in voxel coordinates and a plane, copies the data intersecting the plane and the subvolume into an application buffer.
The resolution is the power of 2 of the desired subsampling level (0:1/1, 1:1/2, 2:1/4, ...) NOTE: The data is copied.
All information is returned in the struct DataInfoPlane. Call this method with buffer = NULL to get the required size of the application buffer (in the bufferSize member of DataInfoPlane).
DataInfoBox SoLDMDataAccess::getData | ( | int | resolution, | |
const SbBox3i32 & | subVolume0, | |||
SoBufferObject * | bufferObj = NULL | |||
) |
Given a subvolume in voxel coordinates, copies the associated data into an application buffer.
The resolution is the power of 2 of the desired subsampling level (0:1/1, 1:1/2, 2:1/4, ...). NOTE: The data is copied.
All information is returned in the struct DataInfoBox. Call this method with buffer = NULL to get the required size of the application buffer (in the bufferSize member of DataInfoBox).
SoDataSet * SoLDMDataAccess::getDataSet | ( | ) | [inline] |
Returns the associated dataset.
SoLDMDataAccess::GetDataMode SoLDMDataAccess::getGetDataMode | ( | ) | [inline] |
Returns the GetDataMode (see setGetDataMode).
void SoLDMDataAccess::getRequestedData | ( | int | requestId, | |
DataInfoTrace & | infoTrace | |||
) |
Returns the data associated with requestID into infoTrace .
void SoLDMDataAccess::getRequestedData | ( | int | requestId, | |
DataInfoPolyLine & | infoPolyline | |||
) |
Returns the data associated with requestID into infoPolyline.
void SoLDMDataAccess::getRequestedData | ( | int | requestId, | |
DataInfoLine & | infoLine | |||
) |
Returns the data associated with requestID into infoLine.
void SoLDMDataAccess::getRequestedData | ( | int | requestId, | |
DataInfoPlane & | infoPlane | |||
) |
Returns the data associated with requestID into infoPlane.
void SoLDMDataAccess::getRequestedData | ( | int | requestId, | |
DataInfoBox & | infoBox | |||
) |
Returns the data associated with requestID into infoBox.
SbBool SoLDMDataAccess::isTileUniform | ( | int | resolution, | |
const SbVec3i32 & | dataPosition, | |||
double & | uniformValue | |||
) |
Returns TRUE if the tile containing the data located at position dataPosition (IJK voxel coordinates) at the specified resolution is uniform (all voxels have the same value).
Returns FALSE if not or if accessing the tile failed (e.g. invalid coordinates). If TRUE then uniformValue contains the uniform value of the tile, else it is undefined.
Since Open Inventor 9.1void SoLDMDataAccess::releaseData | ( | SoLDMTileID | tileID | ) |
Tells VolumeViz that this block of data (returned by getData above) is no longer in use by the application.
SoDEPRECATED int SoLDMDataAccess::requestData | ( | int | resolution, | |
const SbBox3i32 & | subVolume, | |||
const SbVec2i32 & | coord, | |||
void * | buffer | |||
) |
SoDEPRECATED int SoLDMDataAccess::requestData | ( | int | resolution, | |
const SbBox3i32 & | subVolume, | |||
int | numPoints, | |||
const SbVec3i32 * | polyline, | |||
void * | buffer | |||
) |
SoDEPRECATED int SoLDMDataAccess::requestData | ( | int | resolution, | |
const SbBox3i32 & | subVolume, | |||
const SbLine & | line, | |||
void * | buffer | |||
) |
SoDEPRECATED int SoLDMDataAccess::requestData | ( | int | resolution, | |
const SbBox3i32 & | subVolume, | |||
const SbPlane & | plane, | |||
void * | buffer | |||
) |
SoDEPRECATED int SoLDMDataAccess::requestData | ( | int | resolution, | |
const SbBox3i32 & | subVolume, | |||
void * | buffer | |||
) |
int SoLDMDataAccess::requestData | ( | int | resolution, | |
const SbBox3i32 & | subVolume0, | |||
const SbVec2i32 & | coord, | |||
SoBufferObject * | bufferObj | |||
) |
Data values in a single seismic trace (a row of voxels along the volume X axis) are asynchronously copied into an application buffer.
The trace is identified by a YZ voxel coordinate. The range of values returned is the intersection of the trace with the specified subvolume (which may be the full dimensions of the volume). The resolution is the power of 2 of the desired subsampling level (0:1/1, 1:1/2, 2:1/4, ...). NOTE: The data is copied.
Returns a request ID. If requestID is positive, this value will be passed to the endRequest method when the requested data is ready, and can then be used with the getRequestedData method to complete the transaction. If requestID is negative, the data is already in memory, so endRequest will not be called, and -requestID (a positive value) should be used with the getRequestedData method.
Call this method with buffer = NULL to get the required size of the application buffer (in the bufferSize member of DataInfoTrace). Limitations :
int SoLDMDataAccess::requestData | ( | int | resolution, | |
const SbBox3i32 & | subVolume, | |||
int | numPoints, | |||
const SbVec3i32 * | polyline, | |||
SoBufferObject * | bufferObj | |||
) |
Given a subvolume in voxel coordinates and a stack of line, asynchronously copies the data intersecting each line and the subvolume into an application buffer.
The resolution is the power of 2 of the desired subsampling level (0:1/1, 1:1/2, 2:1/4, ...). NOTE: The data is copied.
Returns a request ID. If requestID is positive, this value will be passed to the endRequest method when the requested data is ready, and can then be used with the getRequestedData method to complete the transaction. If requestID is negative, the data is already in memory, so endRequest will not be called, and -requestID (a positive value) should be used with the getRequestedData method.
Call this method with buffer = NULL to get the required size of the application buffer (in the bufferSize member of DataInfoLine). Limitations :
int SoLDMDataAccess::requestData | ( | int | resolution, | |
const SbBox3i32 & | subVolume0, | |||
const SbLine & | line, | |||
SoBufferObject * | bufferObj | |||
) |
Given a subvolume in voxel coordinates and a line, asynchronously copies the data intersecting the line and the subvolume into an application buffer.
The resolution is the power of 2 of the desired subsampling level (0:1/1, 1:1/2, 2:1/4, ...). NOTE: The data is copied.
Returns a request ID. If requestID is positive, this value will be passed to the endRequest method when the requested data is ready, and can then be used with the getRequestedData method to complete the transaction. If requestID is negative, the data is already in memory, so endRequest will not be called, and -requestID (a positive value) should be used with the getRequestedData method.
Call this method with buffer = NULL to get the required size of the application buffer (in the bufferSize member of DataInfoLine). Limitations :
int SoLDMDataAccess::requestData | ( | int | resolution, | |
const SbBox3i32 & | subVolume, | |||
const SbPlane & | plane, | |||
SoBufferObject * | bufferObj | |||
) |
Given a subvolume in voxel coordinates and a plane, asynchronously copies the data intersecting the plane and the subvolume into an application buffer.
The resolution is the power of 2 of the desired subsampling level (0:1/1, 1:1/2, 2:1/4, ...). NOTE: The data is copied.
Returns a request ID. If requestID is positive, this value will be passed to the endRequest method when the requested data is ready, and can then be used with the getRequestedData method to complete the transaction. If requestID is negative, the data is already in memory, so endRequest will not be called, and -requestID (a positive value) should be used with the getRequestedData method.
Call this method with buffer = NULL to get the required size of the application buffer (in the bufferSize member of DataInfoPlane). Limitations :
int SoLDMDataAccess::requestData | ( | int | resolution, | |
const SbBox3i32 & | box, | |||
SoBufferObject * | bufferObj | |||
) |
Given a subvolume in voxel coordinates, asynchronously copies the associated data into an application buffer.
The resolution is the power of 2 of the desired subsampling level (0:1/1, 1:1/2, 2:1/4, ...). NOTE: The data is copied.
Returns a request ID. If requestID is positive, this value will be passed to the endRequest method when the requested data is ready, and can then be used with the getRequestedData method to complete the transaction. If requestID is negative, the data is already in memory, so endRequest will not be called, and -requestID (a positive value) should be used with the getRequestedData method.
Call this method with buffer = NULL to get the required size of the application buffer.
Limitations :
void SoLDMDataAccess::setDataSet | ( | SoDataSet * | v | ) |
Set dataset to fetch data from.
bool SoLDMDataAccess::setGetDataMode | ( | const GetDataMode | getDataMode | ) |
Set the GetDataMode.
This controls how data is requested from the volume reader when the necessary tiles are not already present in LDM cache memory. Default is CACHE.
Converts the specified box in voxel coordinates (I,J,K) to geometric coordinates (X,Y,Z).
The geometric coordinates are expressed in "extent" space, where voxel coordinates are mapped to the box defined by the volume extent.
Correctly converts coordinates that are outside the volume extent, but the resulting voxel coordinate is outside the volume dimensions.
Converts a voxel coordinate (I,J,K) to a geometry coordinate (X,Y,Z).
The geometric coordinates are expressed in "extent" space, where voxel coordinates are mapped to the box defined by the volume extent.
Correctly converts coordinates that are outside the volume extent, but the resulting voxel coordinate is outside the volume dimensions.
Converts the specified box in geometric coordinates (X,Y,Z) to voxel coordinates (I,J,K).
The geometric coordinates are expressed in "extent" space, where voxel coordinates are mapped to the box defined by the volume extent.
Correctly converts coordinates that are outside the volume extent, but the resulting voxel coordinate is outside the volume dimensions.
Converts a geometry coordinate (X,Y,Z) to a voxel coordinate (I,J,K).
The geometric coordinates are expressed in "extent" space, where voxel coordinates are mapped to the box defined by the volume extent.
Correctly converts coordinates that are outside the volume extent, but the resulting voxel coordinate is outside the volume dimensions.