This request returns all the voxels intersected by a plane, optionally bounded by a subvolume. In addition to the resolution and bufferObj parameters, this request takes an SbBox3i32( C++ | Java ) specifying the bounds of the subvolume in IJK (voxel) coordinates and an SbPlane( C++ | Java ) specifying the plane. Although an SbPlane( C++ | Java ) is defined using floating point values, the plane is specified in IJK (voxel) coordinates. The plane can be defined using three points, a normal vector plus a point on the plane, or a normal vector plus the distance from the origin (this is the internal definition of the plane in all cases). For example a plane constructed using a normal vector and distance, like this: SbPlane( C++ | Java )(SbVec3f( C++ | Java )(0,0,1),0), will return the first Z slice of the volume. Of course the plane can have any orientation and position.
This request can be used to get the voxel values associated with a VolumeViz slice primitive. This may be useful for computation or to display the slice “unfolded” in a separate window. For an SoOrthoSlice( C++ | Java | .NET ), set the normal vector to the slice axis and the distance to the slice number. For an SoVolumeSkin( C++ | Java | .NET ), treat each face similar to SoOrthoSlice( C++ | Java | .NET ). For an SoObliqueSlice( C++ | Java | .NET ), convert the slice’s plane from XYZ coordinates to IJK coordinates (described below). For an SoFenceSlice( C++ | Java | .NET ), treat each segment of the fence similar to an SoObliqueSlice( C++ | Java | .NET ).
The request returns a DataInfoPlane struct containing:
errorFlag – Set to CORRECT if the request succeeded.
bufferSize – Returns the required total buffer size in bytes.
bufferDimension – Returns the 2D dimensions of the buffer in voxels.
numPolygonPoints – Returns the number of points in the polygon formed by intersecting the plane with the subvolume. If the plane is parallel to at least one axis of the volume there will be four points, but in the general case there will be six points.
polygonCoord – Returns the four or six points in the polygon shown in the diagram below.
quadCoord – Returns the four points in the quadrangle shown in the diagram below.
uAxis – Returns an index (0 = X, 1 = Y, 2 = Z) indicating the first dimension of the returned buffer.
vAxis – Returns an index (0 = X, 1 = Y, 2 = Z) indicating the second dimension of the returned buffer.
The data type is not included in this struct, but can be queried from the volume data node as shown in a previous section.
The following example shows how to extract a plane from a volume. As usual the example code does not show error checking. In production code the application should check the errorFlag parameter in the returned DataInfoBox struct defined in class SoLDMDataAccess( C++ | Java | .NET ).
// Get subvolume bounds equal to volume dimensions const SbVec3i32& volDim = pVolData->data.getSize(); const SbBox3i32 subVol( 0,0,0, volDim[0]-1,volDim[1]-1,volDim[2]-1 ); // This plane corresponds to the second Z slice of the volume SbVec3f pos(0,0,1); SbVec3f normal(0,0,1); SbPlane plane( normal, pos ); // Call with null to get size of data // Note: Casting "NULL" is required in C++ // (else method signature is ambiguous) int res = 0; SoLDMDataAccess& access = pVolData->getLdmDataAccess(); SoLDMDataAccess::DataInfoPlane info; info = access.getData( res, subVol, plane, (SoBufferObject*)NULL ); // Create a buffer and set size large enough for returned data SoRef<SoCpuBufferObject> pBuffer = new SoCpuBufferObject; pBuffer->setSize( info.bufferSize ); // Call to get the actual data info = access.getData( res, subVol, plane, pBuffer ); // Access the data then unmap the buffer unsigned int* pData = (unsigned int*)pBuffer->map(SoBufferObject::READ_ONLY); unsigned int value = pData[0]; . . . pBuffer->unmap();
// Get subvolume bounds equal to volume dimensions SbVec3i32 volDim = VolData.data.GetSize(); SbBox3i32 subVol = new SbBox3i32( 0,0,0, volDim[0]-1,volDim[1]-1,volDim[2]-1 ); // This plane corresponds to the second Z slice of the volume SbVec3f pos = new SbVec3f(0,0,1); SbVec3f normal = new SbVec3f(0,0,1); SbPlane plane = new SbPlane( normal, pos ); // Call with null to get size of data int res = 0; SoLDMDataAccess access = VolData.GetLdmDataAccess(); SoLDMDataAccess.DataInfoPlane info; info = access.GetData( res, subVol, plane ); // Create a buffer and set size large enough for returned data SoCpuBufferObject buffer = new SoCpuBufferObject(); buffer.SetSize((ulong)info.BufferSize); // Call to get the actual data info = access.GetData( res, subVol, plane, buffer ); // Access the data then unmap the buffer SoBufferObject.AccessModes acc_mode = SoBufferObject.AccessModes.READ_ONLY; SbNativeArray<uint> data = (SbNativeArray<uint>)buffer.Map(acc_mode); uint value = data[0]; . . . buffer.Unmap();
// Get subvolume bounds equal to volume dimensions SbVec3i32 volDim = volData.data.getSize(); SbVec3i32 fixedDim( volDim.getX()-1 volDim.getY()-1, volDim.getZ()-1 ); SbBox3i32 subVol = new SbBox3i32( 0,0,0, fixedDim.getX(),fixedDim.getY(),fixedDim.getZ() ); // This plane corresponds to the second Z slice of the volume SbVec3f pos = new SbVec3f(0,0,1); SbVec3f normal = new SbVec3f(0,0,1); SbPlane plane = new SbPlane( normal, pos ); // Call with null to get size of data int res = 0; SoLDMDataAccess access = volData.getLdmDataAccess(); SoLDMDataAccess.DataInfoPlane info; info = access.getData(res, subVol, plane, null); // Create a buffer and set size large enough for returned data ByteBuffer buffer = ByteBuffer.allocateDirect( info.bufferSize ); buffer.order(ByteOrder.nativeOrder()); // Call to get the actual data info = access.getData(res, subVol, plane, buffer); // Access the data IntBuffer ibuffer = buffer.asIntBuffer(); int value = ibuffer.get(0); . . .
Now suppose we want to extract the data on the plane defined by an SoObliqueSlice( C++ | Java | .NET ) node. The code will be the same as the example above. We just need to convert the plane definition from XYZ coordinates to IJK (voxel) coordinates. The following example code shows how to do that. Note that SoLDMDataAccess( C++ | Java | .NET ) provides a utility method XYZToVoxel(), but it may be useful to see how the conversion is done (and also that method returns an SbVec3i32( C++ | Java ), but SbVec3f( C++ | Java ) is more convenient here).
// Convert plane in XYZ coordinates to IJK (voxel) coordinates. // Normal vector will be the same. Just compute position of plane. SbPlane planeToIJK(SoVolumeData* pVolData, SbPlane plane) { SbVec3f extent_size = pVolData->extent.getValue().getSize(); SbVec3i32 data_size = pVolData->getDimension(); SbVec3f xyz = SbVec3f( extent_size[0] / (float)data_size[0], extent_size[1] / (float)data_size[1], extent_size[2] / (float)data_size[2]); SbVec3f Ixyz = SbVec3f(xyz[0], 0, 0); SbVec3f Jxyz = SbVec3f(0, xyz[1], 0); SbVec3f Kxyz = SbVec3f(0, 0, xyz[2]); SbVec3f Oxyz = pVolData->extent.getValue().getMin(); SbMatrix ijkToXyzMatrix = SbMatrix( Ixyz[0], Ixyz[1], Ixyz[2], 0, Jxyz[0], Jxyz[1], Jxyz[2], 0, Kxyz[0], Kxyz[1], Kxyz[2], 0, Oxyz[0], Oxyz[1], Oxyz[2], 1 ); SbPlane p = plane; p.transform(ijkToXyzMatrix); return p; } // Convert plane in IJK (voxel) coordinates to XYZ coordinates. // Normal vector will be the same. Just compute position of plane. SbPlane planeToXYZ(SoVolumeData* pVolData, SbPlane plane) { SbVec3f extent_size = pVolData->extent.getValue().getSize(); SbVec3i32 data_size = pVolData->getDimension(); SbVec3f xyz = SbVec3f(extent_size[0] / (float)data_size[0], extent_size[1] / (float)data_size[1], extent_size[2] / (float)data_size[2]); SbVec3f Ixyz = SbVec3f(xyz[0], 0, 0); SbVec3f Jxyz = SbVec3f(0, xyz[1], 0); SbVec3f Kxyz = SbVec3f(0, 0, xyz[2]); SbVec3f Oxyz = pVolData->extent.getValue().getMin(); SbMatrix ijkToXyzMatrix = SbMatrix( Ixyz[0], Ixyz[1], Ixyz[2], 0, Jxyz[0], Jxyz[1], Jxyz[2], 0, Kxyz[0], Kxyz[1], Kxyz[2], 0, Oxyz[0], Oxyz[1], Oxyz[2], 1 ); SbMatrix xyzToIjkMatrix = ijkToXyzMatrix.inverse(); SbPlane p = plane; p.transform(xyzToIjkMatrix); return p; }
// Convert plane in XYZ coordinates to IJK (voxel) coordinates. // Normal vector will be the same. Just compute position of plane. public SbPlane planeToIJK(SoVolumeData pVolData, SbPlane plane) { SbVec3f extent_size = pVolData.extent.Value.Size; SbVec3i32 data_size = pVolData.data.GetSize(); SbVec3f xyz = new SbVec3f( extent_size[0] / data_size[0], extent_size[1] / data_size[1], extent_size[2] / data_size[2]); SbVec3f Ixyz = new SbVec3f(xyz[0], 0, 0); SbVec3f Jxyz = new SbVec3f(0, xyz[1], 0); SbVec3f Kxyz = new SbVec3f(0, 0, xyz[2]); SbVec3f Oxyz = pVolData.extent.Value.Min; SbMatrix ijkToXyzMatrix = new SbMatrix( Ixyz[0], Ixyz[1], Ixyz[2], 0, Jxyz[0], Jxyz[1], Jxyz[2], 0, Kxyz[0], Kxyz[1], Kxyz[2], 0, Oxyz[0], Oxyz[1], Oxyz[2], 1 ); SbPlane p = plane; p.Transform(ijkToXyzMatrix); return p; } // Convert plane in IJK (voxel) coordinates to XYZ coordinates. // Normal vector will be the same. Just compute position of plane. public SbPlane planeToXYZ(SoVolumeData pVolData, SbPlane plane) { SbVec3f extent_size = pVolData.extent.Value.Size; SbVec3i32 data_size = pVolData.data.GetSize(); SbVec3f xyz = new SbVec3f( extent_size[0] / data_size[0], extent_size[1] / data_size[1], extent_size[2] / data_size[2]); SbVec3f Ixyz = new SbVec3f(xyz[0], 0, 0); SbVec3f Jxyz = new SbVec3f(0, xyz[1], 0); SbVec3f Kxyz = new SbVec3f(0, 0, xyz[2]); SbVec3f Oxyz = pVolData.extent.Value.Min; SbMatrix ijkToXyzMatrix = new SbMatrix( Ixyz[0], Ixyz[1], Ixyz[2], 0, Jxyz[0], Jxyz[1], Jxyz[2], 0, Kxyz[0], Kxyz[1], Kxyz[2], 0, Oxyz[0], Oxyz[1], Oxyz[2], 1 ); SbMatrix xyzToIjkMatrix = ijkToXyzMatrix; xyzToIjkMatrix.Inverse(); SbPlane p = plane; p.Transform(xyzToIjkMatrix); return p; }
//Convert plane in XYZ coordinates to IJK (voxel) coordinates. //Normal vector will be the same. Just compute position of plane. public static SbPlane planeToIJK(SoVolumeData pVolData, SbPlane plane) { SbVec3f ext_size = pVolData.extent.getValue().getSize(); SbVec3i32 data_size = pVolData.data.getSize(); SbVec3f xyz = new SbVec3f( ext_size.getX() /data_size.getX(), ext_size.getY() / data_size.getY(), ext_size.getZ() / data_size.getZ()); SbVec3f Ixyz = new SbVec3f(xyz.getX(), 0, 0); SbVec3f Jxyz = new SbVec3f(0, xyz.getY(), 0); SbVec3f Kxyz = new SbVec3f(0, 0, xyz.getZ()); SbVec3f Oxyz = pVolData.extent.getValue().getMin(); SbMatrix ijkToXyzMatrix = new SbMatrix( Ixyz.getX(), Ixyz.getY(), Ixyz.getZ(), 0, Jxyz.getX(), Jxyz.getY(), Jxyz.getZ(), 0, Kxyz.getX(), Kxyz.getY(), Kxyz.getZ(), 0, Oxyz.getX(), Oxyz.getY(), Oxyz.getZ(), 1 ); SbPlane p = new SbPlane(plane); p.transform(ijkToXyzMatrix); return p; } //Convert plane in IJK (voxel) coordinates to XYZ coordinates. //Normal vector will be the same. Just compute position of plane. public static SbPlane planeToXYZ(SoVolumeData pVolData, SbPlane plane) { SbVec3f ext_size = pVolData.extent.getValue().getSize(); SbVec3i32 data_size = pVolData.data.getSize(); SbVec3f xyz = new SbVec3f( ext_size.getX() /data_size.getX(), ext_size.getY() / data_size.getY(), ext_size.getZ() / data_size.getZ()); SbVec3f Ixyz = new SbVec3f(xyz.getX(), 0, 0); SbVec3f Jxyz = new SbVec3f(0, xyz.getY(), 0); SbVec3f Kxyz = new SbVec3f(0, 0, xyz.getZ()); SbVec3f Oxyz = pVolData.extent.getValue().getMin(); SbMatrix ijkToXyzMatrix = new SbMatrix( Ixyz.getX(), Ixyz.getY(), Ixyz.getZ(), 0, Jxyz.getX(), Jxyz.getY(), Jxyz.getZ(), 0, Kxyz.getX(), Kxyz.getY(), Kxyz.getZ(), 0, Oxyz.getX(), Oxyz.getY(), Oxyz.getZ(), 1 ); SbMatrix xyzToIjkMatrix = new SbMatrix(ijkToXyzMatrix); xyzToIjkMatrix.inverse(); SbPlane p = new SbPlane(plane); p.transform(xyzToIjkMatrix); return p; }