/* * vp_rle.c * * Routines for run-length encoding classified volume data. * * Copyright (c) 1994 The Board of Trustees of The Leland Stanford * Junior University. All rights reserved. * * Permission to use, copy, modify and distribute this software and its * documentation for any purpose is hereby granted without fee, provided * that the above copyright notice and this permission notice appear in * all copies of this software and that you do not sell the software. * Commercial licensing is available by contacting the author. * * THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * Author: * Phil Lacroute * Computer Systems Laboratory * Electrical Engineering Dept. * Stanford University */ /* * $Date$ * $Revision$ */ #include "vp_global.h" static void EncodeScanline ANSI_ARGS((vpContext *vpc, void *voxels, MinMaxOctree *octree, MMOctreeLevel level_stack[VP_MAX_OCTREE_LEVELS])); static void InitRLE ANSI_ARGS((vpContext *vpc)); static void CacheVoxel ANSI_ARGS((vpContext *vpc, double opacity, void *rawvoxel)); static void CacheLength ANSI_ARGS((vpContext *vpc, int length)); static void CountNonZeroVoxel ANSI_ARGS((RunData *rundata, int index, int end_of_scan, vpContext *vpc)); static void RepackClassifiedVolume ANSI_ARGS((vpContext *vpc)); static RLEVoxels *CreateEmptyRLEVoxels ANSI_ARGS((vpContext *vpc, int ilen, int jlen, int klen)); static RLEVoxels *CreateRLEVoxelsFromRunData ANSI_ARGS((vpContext *vpc, int ilen, int jlen, int klen, int non_zero_count, RunData *run_data, int rle_bytes_per_voxel)); static void StoreNonZeroVoxel ANSI_ARGS((void *voxel, int rle_bytes_per_voxel, void *data, unsigned char *lengths, RunData *rundata, int index)); static void PadScanlines ANSI_ARGS((int ilen, int jlen, int klen, RunData *run_data, unsigned char *lengths)); static ConstructionBuffer *CreateConstructionBuffer ANSI_ARGS(( vpContext *vpc)); static void DestroyConstructionBuffer ANSI_ARGS((vpContext *vpc, ConstructionBuffer *cbuf)); static GBuffer *CreateGBuffer ANSI_ARGS((vpContext *vpc)); static void DestroyGBuffer ANSI_ARGS((vpContext *vpc, GBuffer *gbuf)); #ifdef INDEX_VOLUME static vpResult ComputeIndex ANSI_ARGS((vpContext *vpc, RLEVoxels *rle_voxels)); #endif static vpResult ComputeScanOffsets ANSI_ARGS((vpContext *vpc, RLEVoxels *rle_voxels)); #ifdef DEBUG static void ValidateRLEVoxels ANSI_ARGS((vpContext *vpc, RLEVoxels *rle, int istride, int jstride, int kstride, int axis)); #endif /* * vpClassifyScalars * * Classify an array of scalars and store the result in the classified volume. */ vpResult vpClassifyScalars(vpc, scalar_data, length, scalar_field, grad_field, norm_field) vpContext *vpc; /* context */ unsigned char *scalar_data; /* 3D array of scalar data */ int length; /* number of scalars in scalar_data */ int scalar_field; /* voxel field for scalar, or VP_SKIP_FIELD */ int grad_field; /* voxel field for gradient, or VP_SKIP_FIELD */ int norm_field; /* voxel field for normal, or VP_SKIP_FIELD */ { int xlen, ylen, zlen; /* volume dimensions */ int y, z; /* loop indices */ unsigned char *scalar; /* pointer to current scalar */ int scalar_ystride; /* stride to next scalar scanline */ int scalar_zstride; /* stride to next scalar slice */ char *voxel; /* pointer to current voxel */ unsigned char *s_py, *s_my, *s_pz, *s_mz; /* ptrs to adjacent scans */ int retcode; /* return code from vpScanlineNormals */ void *voxel_scan; /* buffer for storing one scan of raw voxels */ /* check for errors */ xlen = vpc->xlen; ylen = vpc->ylen; zlen = vpc->zlen; if (xlen == 0 || ylen == 0 || zlen == 0 || vpc->raw_bytes_per_voxel == 0) return(VPSetError(vpc, VPERROR_BAD_VOLUME)); if (xlen * ylen * zlen != length) return(VPSetError(vpc, VPERROR_BAD_SIZE)); /* initialize */ scalar = scalar_data; scalar_ystride = xlen; scalar_zstride = xlen*ylen; Alloc(vpc, voxel_scan, void *, xlen*vpc->raw_bytes_per_voxel,"voxel_scan"); /* compute volume data */ for (z = 0; z < zlen; z++) { ReportStatus(vpc, (double)z / (double)zlen); for (y = 0; y < ylen; y++) { s_my = (y == 0) ? NULL : scalar - scalar_ystride; s_py = (y == ylen-1) ? NULL : scalar + scalar_ystride; s_mz = (z == 0) ? NULL : scalar - scalar_zstride; s_pz = (z == zlen-1) ? NULL : scalar + scalar_zstride; voxel = voxel_scan; retcode = vpScanlineNormals(vpc, xlen, scalar, s_my, s_py, s_mz, s_pz, voxel, scalar_field, grad_field, norm_field); if (retcode != VP_OK) { Dealloc(vpc, voxel_scan); return(retcode); } retcode = vpClassifyScanline(vpc, voxel_scan); if (retcode != VP_OK) { Dealloc(vpc, voxel_scan); return(retcode); } scalar += scalar_ystride; } scalar += scalar_zstride - ylen*scalar_ystride; } ReportStatus(vpc, 1.0); Dealloc(vpc, voxel_scan); return(VP_OK); } /* * vpClassifyVolume * * Classify the current raw volume and store the result in the * classified volume. */ vpResult vpClassifyVolume(vpc) vpContext *vpc; /* context */ { int xlen, ylen, zlen; /* volume dimensions */ int y, z; /* loop indices */ char *voxel; /* pointer to current voxel */ int voxel_ystride; /* stride to next voxel scanline */ int voxel_zstride; /* stride to next voxel slice */ int retcode; /* return code */ MinMaxOctree *octree; /* octree for fast classification */ MMOctreeLevel level_stack[VP_MAX_OCTREE_LEVELS]; /* stack for octree */ /* check for errors */ if ((retcode = VPCheckRawVolume(vpc)) != VP_OK) return(retcode); if ((retcode = VPCheckClassifier(vpc)) != VP_OK) return(retcode); /* initialize */ vpDestroyClassifiedVolume(vpc); InitRLE(vpc); xlen = vpc->xlen; ylen = vpc->ylen; zlen = vpc->zlen; voxel = vpc->raw_voxels; voxel_ystride = vpc->ystride; voxel_zstride = vpc->zstride; octree = vpc->mm_octree; if (octree != NULL) { VPComputeSummedAreaTable(vpc); VPClassifyOctree(vpc); } /* compute volume data */ for (z = 0; z < zlen; z++) { ReportStatus(vpc, (double)z / (double)zlen); if (octree != NULL) VPInitOctreeLevelStack(vpc, level_stack, VP_Z_AXIS, z); for (y = 0; y < ylen; y++) { EncodeScanline(vpc, voxel, octree, level_stack); voxel += voxel_ystride; } voxel += voxel_zstride - ylen*voxel_ystride; } ReportStatus(vpc, 1.0); return(VP_OK); } /* * vpClassifyScanline * * Apply the classification function to a scanline of raw voxels and append * it to the classified volume. */ vpResult vpClassifyScanline(vpc, voxels) vpContext *vpc; /* context */ void *voxels; /* voxel scanline */ { int retcode; /* initialize if this is the first scanline */ if (vpc->cbuf == NULL) { if ((retcode = VPCheckClassifier(vpc)) != VP_OK) return(retcode); vpDestroyClassifiedVolume(vpc); InitRLE(vpc); } /* encode scanline */ EncodeScanline(vpc, voxels, NULL, NULL); return(VP_OK); } /* * EncodeScanline * * Classify and run-length encode one scanline of voxels. */ static void EncodeScanline(vpc, voxels, octree, level_stack) vpContext *vpc; /* context */ void *voxels; /* voxel scanline */ MinMaxOctree *octree; /* octree for fast classification */ MMOctreeLevel level_stack[VP_MAX_OCTREE_LEVELS]; /* stack for octree */ { ConstructionBuffer *cbuf; /* state preserved between calls */ RunData rundata_x; /* statistics for current x-axis run */ RunData *rundata_y; /* statistics for all y-axis runs */ RunData *rundata_z; /* statistics for all z-axis runs */ int skip_rle_x; /* if true, do not compute rle_x */ int skip_rle_y; /* if true, do not compute rle_y */ int skip_rle_z; /* if true, do not compute rle_z */ int y, z; /* y and z coordinates of scanline */ int x; /* index of current voxel in scanline */ int xlen, ylen, zlen; /* volume dimensions */ float opacity; /* current value of the opacity (0.0-1.0) */ float min_opacity; /* low opacity threshold */ int raw_bytes_per_voxel; /* bytes in unclassified voxel */ int run_length; /* length of last run */ char *rawvoxel; /* current unclassified voxel */ unsigned char *octree_run_ptr=NULL; /* pointer to current run */ int voxel_count; /* voxels remaining in current run */ int retcode; /* initialize */ cbuf = vpc->cbuf; xlen = vpc->xlen; ylen = vpc->ylen; zlen = vpc->zlen; bzero(&rundata_x, sizeof(RunData)); rundata_y = &cbuf->rundata_y[cbuf->next_z]; rundata_z = &cbuf->rundata_z[cbuf->next_y * xlen]; skip_rle_x = vpc->skip_rle_x; skip_rle_y = vpc->skip_rle_y; skip_rle_z = vpc->skip_rle_z; min_opacity = vpc->min_opacity; raw_bytes_per_voxel = vpc->raw_bytes_per_voxel; rawvoxel = voxels; y = cbuf->next_y; z = cbuf->next_z; if (octree != NULL) { if (cbuf->octree_scans_left == 0) { cbuf->octree_scans_left = VPComputeScanRuns(vpc, level_stack, cbuf->octree_runs, VP_Z_AXIS, y, xlen); } cbuf->octree_scans_left--; octree_run_ptr = cbuf->octree_runs; } /* loop over voxels in the scanline */ x = 0; while (x < xlen) { if (octree == NULL) { /* no octree available, so process all of the voxels in the scan */ voxel_count = xlen; } else do { /* skip over a run of zero voxels */ voxel_count = *octree_run_ptr++; rundata_y += zlen * voxel_count; rundata_z += voxel_count; rawvoxel += raw_bytes_per_voxel * voxel_count; x += voxel_count; /* get length of nonzero voxel run */ voxel_count = *octree_run_ptr++; } while (voxel_count == 0 && x < xlen); /* process the voxels in the nonzero run */ while (voxel_count-- > 0) { /* compute opacity */ opacity = VPClassifyVoxel(vpc, rawvoxel); /* compare opacity to threshold */ if (opacity > min_opacity) { /* voxel is non-transparent, so save it */ CacheVoxel(vpc, opacity, rawvoxel); if (!skip_rle_x) { rundata_y->p.p1.non_zero_count++; CountNonZeroVoxel(rundata_y, y, 0, NULL); } if (!skip_rle_y) { rundata_z->p.p1.non_zero_count++; CountNonZeroVoxel(rundata_z, z, 0, NULL); } rundata_x.p.p1.non_zero_count++; CountNonZeroVoxel(&rundata_x, x, 0, vpc); } rundata_y += zlen; rundata_z++; rawvoxel += raw_bytes_per_voxel; x++; } /* while (voxel_count) */ } /* for x */ /* finish off the statistics for the scanline */ CountNonZeroVoxel(&rundata_x, x, 1, vpc); /* update saved state */ cbuf->non_zero_count += rundata_x.p.p1.non_zero_count; cbuf->x_run_count += rundata_x.p.p1.run_count; /* check if this is the last scanline in the volume */ if (++cbuf->next_y == ylen) { cbuf->next_y = 0; cbuf->octree_scans_left = 0; if (++cbuf->next_z == zlen) { RepackClassifiedVolume(vpc); DestroyConstructionBuffer(vpc, vpc->cbuf); vpc->cbuf = NULL; #ifdef DEBUG printf("\r"); if (!skip_rle_x) { printf("Checking X scanline offsets....\n"); VPCheckScanOffsets(vpc->rle_x, vpc->rle_bytes_per_voxel); } if (!skip_rle_y) { printf("Checking Y scanline offsets....\n"); VPCheckScanOffsets(vpc->rle_y, vpc->rle_bytes_per_voxel); } if (!skip_rle_z) { printf("Checking Z scanline offsets....\n"); VPCheckScanOffsets(vpc->rle_z, vpc->rle_bytes_per_voxel); } VPValidateClassifiedVolume(vpc); #endif } } } /* * InitRLE * * Initialize in preparation for creating a new run-length encoded volume. */ static void InitRLE(vpc) vpContext *vpc; { int f; int rle_bytes_per_voxel, size, offset; int maxsize = 0; /* find out how many bytes of the raw voxel are used for shading */ rle_bytes_per_voxel = 0; for (f = 0; f < vpc->num_shade_fields; f++) { size = vpc->field_size[f]; offset = vpc->field_offset[f] + size; if (offset > rle_bytes_per_voxel) rle_bytes_per_voxel = offset; if (size > maxsize) maxsize = size; } /* add one byte for opacity and then pad to the byte boundary of the largest field in the voxel; this ensures alignment; the opacity is always stored in the last byte (so the padding is in between the shading fields and the opacity field) */ rle_bytes_per_voxel++; rle_bytes_per_voxel = (rle_bytes_per_voxel + maxsize-1) & ~(maxsize-1); vpc->rle_bytes_per_voxel = rle_bytes_per_voxel; /* initialize construction buffer */ vpc->cbuf = CreateConstructionBuffer(vpc); } /* * CacheVoxel * * Cache one voxel's data in the ConstructionBuffer. */ static void CacheVoxel(vpc, opacity, rawvoxel) vpContext *vpc; /* context */ double opacity; /* voxel's opacity */ void *rawvoxel; /* raw voxel data */ { ConstructionBuffer *cbuf; /* state during construction of volume */ GBuffer *data_buf; /* storage for cached voxels */ void *data_ptr; /* pointer to current voxel's storage */ int rle_bytes_per_voxel; /* bytes per voxel after classification */ int opc_int; /* quantized opacity */ /* initialize */ cbuf = vpc->cbuf; data_buf = cbuf->data_buf_tail; rle_bytes_per_voxel = vpc->rle_bytes_per_voxel; /* allocate more memory if necessary */ if (data_buf->bytes_left < rle_bytes_per_voxel) { /* allocate more memory */ data_buf->next = CreateGBuffer(vpc); data_buf = data_buf->next; cbuf->data_buf_tail = data_buf; } data_ptr = data_buf->data_ptr; /* copy the voxel fields required for shading */ bcopy(rawvoxel, data_ptr, rle_bytes_per_voxel-1); /* quantize and store the opacity */ opc_int = opacity*255.; if (opc_int > 255) opc_int = 255; else if (opc_int < 0) opc_int = 0; ByteField(data_ptr, rle_bytes_per_voxel-1) = opc_int; data_buf->data_ptr += rle_bytes_per_voxel; data_buf->bytes_left -= rle_bytes_per_voxel; } /* * CacheLength * * Cache one run length in the ConstructionBuffer. */ static void CacheLength(vpc, length) vpContext *vpc; int length; { GBuffer *lengths_buf; lengths_buf = vpc->cbuf->lengths_buf_tail; if (lengths_buf->bytes_left == 0) { /* allocate more memory */ lengths_buf->next = CreateGBuffer(vpc); lengths_buf = lengths_buf->next; vpc->cbuf->lengths_buf_tail = lengths_buf; } *(lengths_buf->data_ptr)++ = length; lengths_buf->bytes_left--; } /* * CountNonZeroVoxel * * Update the run count and nonzero voxel count for a voxel scanline. * This routine adds one non-zero voxel to the scanline. Index * indicates the position of the voxel in the scanline. If that * position is not immediately adjacent to the last non-zero voxel then * a run of zero voxels is added as well. * * If the vpc argument is non-NULL then the lengths of any completed * runs are written out to the run length buffer. */ static void CountNonZeroVoxel(rundata, index, end_of_scan, vpc) RunData *rundata; /* statistics for the scanline */ int index; /* index of voxel in scanline */ int end_of_scan; /* if true then finish the scanline instead of adding a voxel */ vpContext *vpc; /* context in which run lengths should be stored */ { int run_length; if (rundata->next_index != index) { /* a run of zero voxels intervenes between the current index and the last nonzero voxel that was processed */ if (rundata->next_index != 0) { /* add the last nonzero run to the statistics */ run_length = rundata->run_length; while (run_length > 255) { /* run is too long, so split it */ run_length -= 255; rundata->p.p1.run_count += 2; if (vpc != NULL) { CacheLength(vpc, 255); CacheLength(vpc, 0); } } rundata->p.p1.run_count++; if (vpc != NULL) CacheLength(vpc, run_length); } /* add the last zero run to the statistics */ run_length = index - rundata->next_index; while (run_length > 255) { /* run is too long, so split it */ run_length -= 255; rundata->p.p1.run_count += 2; if (vpc != NULL) { CacheLength(vpc, 255); CacheLength(vpc, 0); } } rundata->p.p1.run_count++; if (vpc != NULL) CacheLength(vpc, run_length); if (end_of_scan) { /* add a zero-length nonzero run to finish the scanline */ rundata->p.p1.run_count++; if (vpc != NULL) CacheLength(vpc, 0); } else { /* start the new run */ rundata->run_length = 1; rundata->next_index = index + 1; } } else if (!end_of_scan) { /* add a nonzero voxel to the current run */ if (rundata->next_index == 0) { rundata->p.p1.run_count++; /* count initial zero run */ if (vpc != NULL) CacheLength(vpc, 0); } rundata->run_length++; rundata->next_index = index + 1; } else { /* scanline ends with a nonzero voxel run */ run_length = rundata->run_length; while (run_length > 255) { /* run is too long, so split it */ run_length -= 255; rundata->p.p1.run_count += 2; if (vpc != NULL) { CacheLength(vpc, 255); CacheLength(vpc, 0); } } rundata->p.p1.run_count++; if (vpc != NULL) CacheLength(vpc, run_length); } } /* * RepackClassifiedVolume * * Repack the data in the ConstructionBuffer after the last call to * vpClassifyScanline. This procedure creates the three run-length * encoded copies of the classified voxels. */ static void RepackClassifiedVolume(vpc) vpContext *vpc; { int xlen, ylen, zlen; /* volume dimensions */ int x, y, z; /* voxel coordinates */ int non_zero_count; /* number of nonzero voxels in volume */ int rle_bytes_per_voxel; /* bytes per classified voxel */ int skip_rle_x; /* if true, compute rle_x */ int skip_rle_y; /* if true, compute rle_y */ int skip_rle_z; /* if true, compute rle_z */ char *x_data=NULL; /* voxel data for x viewpoint */ char *y_data=NULL; /* voxel data for y viewpoint */ char *z_data=NULL; /* voxel data for z viewpoint */ unsigned char *x_lengths=NULL; /* run length for x viewpoint */ unsigned char *y_lengths=NULL; /* run length for y viewpoint */ unsigned char *z_lengths=NULL; /* run length for z viewpoint */ int z_data_offset; /* offset to current data value in z volume */ int z_length_offset; /* offset to current length value in z volume*/ GBuffer *data_buf; /* next GBuffer containing voxel data */ char *data; /* pointer to next voxel */ int data_bytes_left; /* bytes of data left in data buffer */ GBuffer *lengths_buf; /* next GBuffer containing length data */ unsigned char *lengths; /* pointer to next length */ int lengths_bytes_left; /* bytes of data left in lengths buffer */ int x_run_length; /* length of current x-scanline run */ int is_non_zero; /* true if current x-scanline run is nonzero */ RunData *rundata_y; /* statistics for y-axis runs */ RunData *rundata_z; /* statistics for z-axis runs */ /* initialize */ xlen = vpc->xlen; ylen = vpc->ylen; zlen = vpc->zlen; non_zero_count = vpc->cbuf->non_zero_count; rle_bytes_per_voxel = vpc->rle_bytes_per_voxel; skip_rle_x = vpc->skip_rle_x; skip_rle_y = vpc->skip_rle_y; skip_rle_z = vpc->skip_rle_z; /* check for empty volume */ if (non_zero_count == 0) { if (!skip_rle_x) vpc->rle_x = CreateEmptyRLEVoxels(vpc, ylen, zlen, xlen); if (!skip_rle_y) vpc->rle_y = CreateEmptyRLEVoxels(vpc, zlen, xlen, ylen); if (!skip_rle_z) vpc->rle_z = CreateEmptyRLEVoxels(vpc, xlen, ylen, zlen); return; } /* allocate space for y-axis runs (used for the x viewing axis) */ if (!skip_rle_x) { vpc->rle_x = CreateRLEVoxelsFromRunData(vpc, ylen, zlen, xlen, non_zero_count, vpc->cbuf->rundata_y, rle_bytes_per_voxel); x_data = vpc->rle_x->data; x_lengths = vpc->rle_x->run_lengths; } /* allocate space for z-axis runs (used for the y viewing axis) */ if (!skip_rle_y) { vpc->rle_y = CreateRLEVoxelsFromRunData(vpc, zlen, xlen, ylen, non_zero_count, vpc->cbuf->rundata_z, rle_bytes_per_voxel); y_data = vpc->rle_y->data; y_lengths = vpc->rle_y->run_lengths; } /* allocate space for x-axis runs (used for the z viewing axis) */ if (!skip_rle_z) { vpc->rle_z = VPCreateRLEVoxels(vpc, xlen, ylen, zlen, non_zero_count, vpc->cbuf->x_run_count, rle_bytes_per_voxel); Alloc(vpc, vpc->rle_z->scan_offsets, ScanOffset *, zlen*sizeof(ScanOffset), "scan_offsets"); vpc->rle_z->scan_offsets_per_slice = 1; z_data = vpc->rle_z->data; z_lengths = vpc->rle_z->run_lengths; z_data_offset = 0; z_length_offset = 0; } /* copy data into the three RLEVoxels structures */ data_buf = vpc->cbuf->data_buf_head; data = NULL; data_bytes_left = 0; lengths_buf = vpc->cbuf->lengths_buf_head; lengths = NULL; lengths_bytes_left = 0; x_run_length = 0; is_non_zero = 1; for (z = 0; z < zlen; z++) { ReportStatus(vpc, (double)z / (double)zlen); if (!skip_rle_z) { vpc->rle_z->scan_offsets[z].first_data = z_data_offset; vpc->rle_z->scan_offsets[z].first_len = (z_length_offset & 0x1) ? z_length_offset + 1 : z_length_offset; } rundata_z = vpc->cbuf->rundata_z; for (y = 0; y < ylen; y++) { rundata_y = &vpc->cbuf->rundata_y[z]; for (x = 0; x < xlen; x++) { while (x_run_length == 0) { /* find length of next run */ if (lengths_bytes_left <= 0) { /* go to next lengths buffer */ lengths = (unsigned char *)lengths_buf->data; lengths_bytes_left = GBUFFER_SIZE - lengths_buf->bytes_left; lengths_buf = lengths_buf->next; if (!skip_rle_z) { bcopy(lengths, z_lengths, lengths_bytes_left); z_lengths += lengths_bytes_left; } } x_run_length = *lengths++; lengths_bytes_left--; is_non_zero = !is_non_zero; z_length_offset++; } x_run_length--; /* consume one voxel */ if (is_non_zero) { /* find the data for this voxel */ if (data_bytes_left <= 0) { data = data_buf->data; data_bytes_left = GBUFFER_SIZE - data_buf->bytes_left; data_buf = data_buf->next; if (!skip_rle_z) { bcopy(data, z_data, data_bytes_left); z_data = (char *)z_data + data_bytes_left; } } /* store voxel */ if (!skip_rle_x) { StoreNonZeroVoxel(data, rle_bytes_per_voxel, x_data, x_lengths, rundata_y, y); } if (!skip_rle_y) { StoreNonZeroVoxel(data, rle_bytes_per_voxel, y_data, y_lengths, rundata_z, z); } data += rle_bytes_per_voxel; data_bytes_left -= rle_bytes_per_voxel; z_data_offset += rle_bytes_per_voxel; } rundata_y += zlen; rundata_z++; } /* for x */ } /* for y */ } /* for z */ ReportStatus(vpc, 1.0); if (!skip_rle_x) PadScanlines(ylen, zlen, xlen, vpc->cbuf->rundata_y, x_lengths); if (!skip_rle_y) PadScanlines(zlen, xlen, ylen, vpc->cbuf->rundata_z, y_lengths); } /* * CreateEmptyRLEVoxels * * Create an empty RLEVoxels object (all voxels transparent). */ static RLEVoxels * CreateEmptyRLEVoxels(vpc, ilen, jlen, klen) vpContext *vpc; int ilen, jlen, klen; { RLEVoxels *rle_voxels; int j, k; unsigned char *run_lengths; ScanOffset *scan_offsets; rle_voxels = VPCreateRLEVoxels(vpc, ilen, jlen, klen, 1, 2*jlen*klen, 1); Alloc(vpc, rle_voxels->scan_offsets, ScanOffset *, klen*sizeof(ScanOffset), "scan_offsets"); rle_voxels->scan_offsets_per_slice = 1; run_lengths = rle_voxels->run_lengths; scan_offsets = rle_voxels->scan_offsets; for (k = 0; k < klen; k++) { scan_offsets->first_len = k*jlen*2; scan_offsets->first_data = 0; scan_offsets++; for (j = 0; j < jlen; j++) { *run_lengths++ = ilen; *run_lengths++ = 0; } } return(rle_voxels); } /* * CreateRLEVoxelsFromRunData * * Allocate an RLEVoxels structure using the data in a RunData array * in order to determine the required size. Also reinitialize the RunData * array with pointers to the RLEVoxels data for the beginning of * each scanline. */ static RLEVoxels * CreateRLEVoxelsFromRunData(vpc, ilen, jlen, klen, non_zero_count, run_data, rle_bytes_per_voxel) vpContext *vpc; /* context */ int ilen, jlen, klen; /* size of volume in rotated object space */ int non_zero_count; /* number of nonzero voxels in volume */ RunData *run_data; /* array of run statistics (jlen*klen entries) */ int rle_bytes_per_voxel;/* number of bytes to allocate for each voxel */ { int j, k; /* scanline and slice number */ int scan_run_count; /* runs in current scanline */ int run_count; /* runs in entire volume */ int scan_non_zero_count; /* nonzero voxels in scanline */ int data_offset; /* scanline's offset in RLEVoxels->data */ int length_offset; /* scanline's offset in RLEVoxels->run_lengths */ ScanOffset *slice_offset; /* offsets for each slice */ RLEVoxels *rle_voxels; /* return value */ Alloc(vpc, slice_offset, ScanOffset *, klen*sizeof(ScanOffset), "scan_offsets"); /* accumulate the statistics for the last run in each scanline, count the total number of runs, and store the data and length offsets for the beginning of the scanline */ data_offset = 0; length_offset = 0; run_count = 0; for (k = 0; k < klen; k++) { slice_offset[k].first_data = data_offset; slice_offset[k].first_len = length_offset; for (j = 0; j < jlen; j++) { CountNonZeroVoxel(run_data, ilen, 1, NULL); scan_non_zero_count = run_data->p.p1.non_zero_count; scan_run_count = run_data->p.p1.run_count; run_data->run_length = 0; run_data->next_index = 0; run_data->p.p2.data_offset = data_offset; run_data->p.p2.length_offset = length_offset; data_offset += scan_non_zero_count * rle_bytes_per_voxel; length_offset += scan_run_count * sizeof(unsigned char); run_count += scan_run_count; run_data++; } } /* allocate space */ rle_voxels = VPCreateRLEVoxels(vpc, ilen, jlen, klen, non_zero_count, run_count, rle_bytes_per_voxel); rle_voxels->scan_offsets_per_slice = 1; rle_voxels->scan_offsets = slice_offset; return(rle_voxels); } /* * StoreNonZeroVoxel * * Store a nonzero voxel in an RLEVoxels object. This function is * just like CountNonZeroVoxel except that it actually stores voxel data. */ static void StoreNonZeroVoxel(voxel, rle_bytes_per_voxel, data, lengths, rundata, index) void *voxel; /* input voxel data */ int rle_bytes_per_voxel;/* size of voxel */ void *data; /* location to store voxel */ unsigned char *lengths; /* location to store run lengths */ RunData *rundata; /* run length statistics for current voxel scanline */ int index; /* index of voxel in scanline */ { int run_length; /* store the voxel */ if (voxel != NULL) { bcopy(voxel, (char *)data + rundata->p.p2.data_offset, rle_bytes_per_voxel); rundata->p.p2.data_offset += rle_bytes_per_voxel; } /* update run lengths */ if (rundata->next_index != index) { /* a run of zero voxels intervenes between the current index and the last nonzero voxel that was processed */ if (rundata->next_index != 0) { /* add the last nonzero run to the statistics */ run_length = rundata->run_length; while (run_length > 255) { /* run is too long, so split it */ run_length -= 255; lengths[rundata->p.p2.length_offset++] = 255; lengths[rundata->p.p2.length_offset++] = 0; } lengths[rundata->p.p2.length_offset++] = run_length; } /* add the last zero run to the statistics */ run_length = index - rundata->next_index; while (run_length > 255) { /* run is too long, so split it */ run_length -= 255; lengths[rundata->p.p2.length_offset++] = 255; lengths[rundata->p.p2.length_offset++] = 0; } lengths[rundata->p.p2.length_offset++] = run_length; if (voxel == NULL) { /* add a zero-length nonzero run to finish the scanline */ lengths[rundata->p.p2.length_offset++] = 0; } else { /* start the new run */ rundata->run_length = 1; rundata->next_index = index + 1; } } else if (voxel != NULL) { /* add a nonzero voxel to the current run */ if (rundata->next_index == 0) { lengths[rundata->p.p2.length_offset++] = 0; } rundata->run_length++; rundata->next_index = index + 1; } else { /* scanline ends with a nonzero voxel run */ run_length = rundata->run_length; while (run_length > 255) { /* run is too long, so split it */ run_length -= 255; lengths[rundata->p.p2.length_offset++] = 255; lengths[rundata->p.p2.length_offset++] = 0; } lengths[rundata->p.p2.length_offset++] = run_length; } } /* * PadScanlines * * Make sure each scanline has an even number of runs. */ static void PadScanlines(ilen, jlen, klen, run_data, lengths) int ilen, jlen, klen; /* size of volume in rotated object space */ RunData *run_data; /* array of run statistics (jlen*klen entries) */ unsigned char *lengths; /* beginning of run lengths array */ { int scan_count; /* number of scanlines */ int scan_run_count; /* number of runs in scanline */ int scan; /* current scanline number */ scan_count = jlen * klen; for (scan = 0; scan < scan_count; scan++) { StoreNonZeroVoxel(NULL, 0, NULL, lengths, run_data, ilen); run_data++; } } /* * VPCreateRLEVoxels * * * Allocate a new RLEVoxels object. */ RLEVoxels * VPCreateRLEVoxels(vpc, ilen, jlen, klen, data_count, run_count, rle_bytes_per_voxel) vpContext *vpc; /* context */ int ilen, jlen, klen; /* dimensions in rotated object space */ int data_count; /* number of nonzero voxels */ int run_count; /* number of runs */ int rle_bytes_per_voxel;/* bytes of storage for one voxel */ { RLEVoxels *rle_voxels; Alloc(vpc, rle_voxels, RLEVoxels *, sizeof(RLEVoxels), "RLEVoxels"); rle_voxels->ilen = ilen; rle_voxels->jlen = jlen; rle_voxels->klen = klen; rle_voxels->run_count = run_count; if (run_count > 0) { Alloc(vpc, rle_voxels->run_lengths, unsigned char *, run_count, "run_lengths"); } else { rle_voxels->run_lengths = NULL; } rle_voxels->data_count = data_count; if (data_count > 0) { Alloc(vpc, rle_voxels->data, void *, data_count * rle_bytes_per_voxel, "voxeldata"); } else { rle_voxels->data = NULL; } rle_voxels->scan_offsets_per_slice = 0; rle_voxels->scan_offsets = NULL; rle_voxels->mmapped = 0; #ifdef INDEX_VOLUME rle_voxels->voxel_index = NULL; #endif return(rle_voxels); } /* * VPDestroyRLEVoxels * * Destroy an RLEVoxels object. */ void VPDestroyRLEVoxels(vpc, rle_voxels) vpContext *vpc; RLEVoxels *rle_voxels; { if (!rle_voxels->mmapped) { if (rle_voxels->run_lengths != NULL) Dealloc(vpc, rle_voxels->run_lengths); if (rle_voxels->data != NULL) Dealloc(vpc, rle_voxels->data); if (rle_voxels->scan_offsets != NULL) Dealloc(vpc, rle_voxels->scan_offsets); } #ifdef INDEX_VOLUME if (rle_voxels->voxel_index != NULL) Dealloc(vpc, rle_voxels->voxel_index); #endif Dealloc(vpc, rle_voxels); } /* * CreateConstructionBuffer * * Create a ConstructionBuffer object. */ static ConstructionBuffer * CreateConstructionBuffer(vpc) vpContext *vpc; { ConstructionBuffer *cbuf; int xlen, ylen, zlen; xlen = vpc->xlen; ylen = vpc->ylen; zlen = vpc->zlen; Alloc(vpc, cbuf, ConstructionBuffer *, sizeof(ConstructionBuffer), "ConstructionBuffer"); Alloc(vpc, cbuf->rundata_y, RunData *, xlen*zlen*sizeof(RunData), "rundata_y"); Alloc(vpc, cbuf->rundata_z, RunData *, ylen*xlen*sizeof(RunData), "rundata_z"); bzero(cbuf->rundata_y, xlen*zlen*sizeof(RunData)); bzero(cbuf->rundata_z, ylen*xlen*sizeof(RunData)); cbuf->data_buf_head = CreateGBuffer(vpc); cbuf->data_buf_tail = cbuf->data_buf_head; cbuf->lengths_buf_head = CreateGBuffer(vpc); cbuf->lengths_buf_tail = cbuf->lengths_buf_head; cbuf->non_zero_count = 0; cbuf->x_run_count = 0; cbuf->octree_scans_left = 0; cbuf->next_z = 0; cbuf->next_y = 0; return(cbuf); } /* * DestroyConstructionBuffer * * Destroy a ConstructionBuffer object. */ static void DestroyConstructionBuffer(vpc, cbuf) vpContext *vpc; ConstructionBuffer *cbuf; { GBuffer *gbuf, *next_gbuf; Dealloc(vpc, cbuf->rundata_y); Dealloc(vpc, cbuf->rundata_z); for (gbuf = cbuf->data_buf_head; gbuf != NULL; gbuf = next_gbuf) { next_gbuf = gbuf->next; DestroyGBuffer(vpc, gbuf); } for (gbuf = cbuf->lengths_buf_head; gbuf != NULL; gbuf = next_gbuf) { next_gbuf = gbuf->next; DestroyGBuffer(vpc, gbuf); } Dealloc(vpc, cbuf); } /* * CreateGBuffer * * Create a GBuffer object. */ static GBuffer * CreateGBuffer(vpc) vpContext *vpc; { GBuffer *gbuf; Alloc(vpc, gbuf, GBuffer *, sizeof(GBuffer), "GBuffer"); gbuf->bytes_left = GBUFFER_SIZE; gbuf->data_ptr = gbuf->data; gbuf->next = NULL; return(gbuf); } /* * DestroyGBuffer * * Destroy a GBuffer. */ static void DestroyGBuffer(vpc, gbuf) vpContext *vpc; GBuffer *gbuf; { Dealloc(vpc, gbuf); } /* * vpDestroyClassifiedVolume * * Free all memory associated with a classified volume. */ vpResult vpDestroyClassifiedVolume(vpc) vpContext *vpc; { if (vpc->cbuf != NULL) { DestroyConstructionBuffer(vpc, vpc->cbuf); vpc->cbuf = NULL; } if (vpc->rle_x != NULL) { VPDestroyRLEVoxels(vpc, vpc->rle_x); vpc->rle_x = NULL; } if (vpc->rle_y != NULL) { VPDestroyRLEVoxels(vpc, vpc->rle_y); vpc->rle_y = NULL; } if (vpc->rle_z != NULL) { VPDestroyRLEVoxels(vpc, vpc->rle_z); vpc->rle_z = NULL; } return(VP_OK); } #ifdef INDEX_VOLUME /* * vpComputeRLEIndex * * Compute indexes for the classified volume data in a context. */ vpResult vpComputeRLEIndex(vpc) vpContext *vpc; { vpResult result; if ((result = VPComputeRLEScanOffsets(vpc)) != VP_OK) return(result); if (vpc->rle_x != NULL) { if ((result = ComputeIndex(vpc, vpc->rle_x)) != VP_OK) return(result); } if (vpc->rle_y != NULL) { if ((result = ComputeIndex(vpc, vpc->rle_y)) != VP_OK) return(result); } if (vpc->rle_z != NULL) { if ((result = ComputeIndex(vpc, vpc->rle_z)) != VP_OK) return(result); } return(VP_OK); } /* * ComputeIndex * * Compute an index that maps 3D voxel coordinates to the RLE run data * for the corresponding voxel. The values stored in the index are * byte offsets to the beginning of the run containing the voxel, * plus a count indicating the position of the voxel in the run. * Return value is a result code. */ static vpResult ComputeIndex(vpc, rle_voxels) vpContext *vpc; RLEVoxels *rle_voxels; { int ilen, jlen, klen; /* size of volume */ unsigned char *RLElen; /* pointer to current run length */ VoxelLocation *index; /* pointer to current index entry */ int i, j, k; /* current voxel coordinates */ unsigned len_offset; /* offset in bytes from beginning of scanline to current run length */ unsigned data_offset; /* offset in bytes from beginning of scanline to current voxel data */ int run_is_zero; /* true if current run is a zero run */ int run_count; /* voxels left in current run */ int voxel_size; /* size of a voxel in bytes */ ilen = rle_voxels->ilen; jlen = rle_voxels->jlen; klen = rle_voxels->klen; RLElen = rle_voxels->run_lengths; if (rle_voxels->scan_offsets_per_slice != jlen) return(VPERROR_BAD_VOLUME); if (rle_voxels->voxel_index == NULL) { Alloc(vpc, rle_voxels->voxel_index, VoxelLocation *, ilen * jlen * klen * sizeof(VoxelLocation), "voxel_index"); } index = rle_voxels->voxel_index; voxel_size = vpc->rle_bytes_per_voxel; run_is_zero = 0; run_count = 0; for (k = 0; k < klen; k++) { for (j = 0; j < jlen; j++) { ASSERT(run_is_zero == 0); ASSERT(run_count == 0); len_offset = 0; data_offset = 0; for (i = 0; i < ilen; i++) { /* record index for current voxel */ if (len_offset > 256) { Dealloc(vpc, rle_voxels->voxel_index); rle_voxels->voxel_index = NULL; return(VPERROR_LIMIT_EXCEEDED); } index->run_count = run_count; index->len_offset = len_offset; if (run_is_zero) index->data_offset = data_offset | INDEX_RUN_IS_ZERO; else index->data_offset = data_offset; index++; /* go on to next voxel */ while (run_count == 0) { run_count = *RLElen++; run_is_zero = !run_is_zero; len_offset++; } run_count--; if (!run_is_zero) data_offset += voxel_size; } ASSERT(run_count == 0); if (run_is_zero) { run_count = *RLElen++; run_is_zero = !run_is_zero; len_offset++; } } } return(VP_OK); } #endif /* INDEX_VOLUME */ /* * VPComputeRLEScanOffsets * * Recompute the scan_offsets arrays for the classified volume data in * a context. Return value is a result code. */ vpResult VPComputeRLEScanOffsets(vpc) vpContext *vpc; { vpResult result; if (vpc->rle_x != NULL) { if ((result = ComputeScanOffsets(vpc, vpc->rle_x)) != VP_OK) return(result); #ifdef DEBUG VPCheckScanOffsets(vpc->rle_x, vpc->rle_bytes_per_voxel); #endif } if (vpc->rle_y != NULL) { if ((result = ComputeScanOffsets(vpc, vpc->rle_y)) != VP_OK) return(result); #ifdef DEBUG VPCheckScanOffsets(vpc->rle_y, vpc->rle_bytes_per_voxel); #endif } if (vpc->rle_z != NULL) { if ((result = ComputeScanOffsets(vpc, vpc->rle_z)) != VP_OK) return(result); #ifdef DEBUG VPCheckScanOffsets(vpc->rle_z, vpc->rle_bytes_per_voxel); #endif } return(VP_OK); } /* * ComputeScanOffsets * * Recompute the scan_offsets array for a classified volume. * Return value is a result code. */ static vpResult ComputeScanOffsets(vpc, rle_voxels) vpContext *vpc; RLEVoxels *rle_voxels; { int ilen, jlen, klen; /* size of volume */ unsigned char *RLElen; /* pointer to current run length */ ScanOffset *scan_offset; /* pointer to current scanline offset */ int i, j, k; /* current voxel coordinates */ unsigned len_offset; /* offset in bytes from beginning of run lengths to current run length */ unsigned data_offset; /* offset in bytes from beginning of voxel data to current voxel data */ int voxel_size; /* size of a voxel in bytes */ int zerocount, nonzerocount; if (rle_voxels->mmapped) return(VPERROR_IO); ilen = rle_voxels->ilen; jlen = rle_voxels->jlen; klen = rle_voxels->klen; RLElen = rle_voxels->run_lengths; if (rle_voxels->scan_offsets_per_slice != jlen) { if (rle_voxels->scan_offsets != NULL) Dealloc(vpc, rle_voxels->scan_offsets); Alloc(vpc, rle_voxels->scan_offsets, ScanOffset *, klen * jlen * sizeof(ScanOffset), "scan_offsets"); rle_voxels->scan_offsets_per_slice = jlen; } scan_offset = rle_voxels->scan_offsets; len_offset = 0; data_offset = 0; voxel_size = vpc->rle_bytes_per_voxel; for (k = 0; k < klen; k++) { for (j = 0; j < jlen; j++) { scan_offset->first_len = len_offset; scan_offset->first_data = data_offset; scan_offset++; for (i = 0; i < ilen; ) { zerocount = *RLElen++; /* get length of run of zeros */ nonzerocount = *RLElen++;/* get length of run of non-zeros */ len_offset += 2; data_offset += nonzerocount * voxel_size; i += zerocount + nonzerocount; } ASSERT(i == ilen); } } return(VP_OK); } #ifdef DEBUG /* * VPCheckScanOffsets * * Check the scan_offsets field of an RLEVolume for internal consistency. */ void VPCheckScanOffsets(rle_voxels, rle_bytes_per_voxel) RLEVoxels *rle_voxels; { int i, j, k; int ilen, jlen, klen; int run_length; int is_non_zero; unsigned char *run_length_ptr; int length_offset; int data_offset; int scan_offsets_per_slice; ScanOffset *scan_offset; scan_offsets_per_slice = rle_voxels->scan_offsets_per_slice; if (scan_offsets_per_slice == 0) return; ilen = rle_voxels->ilen; jlen = rle_voxels->jlen; klen = rle_voxels->klen; run_length_ptr = rle_voxels->run_lengths; run_length = 0; is_non_zero = 1; length_offset = 0; data_offset = 0; for (k = 0; k < klen; k++) { for (j = 0; j < jlen; j++) { if (j < scan_offsets_per_slice) { scan_offset = &rle_voxels->scan_offsets[ k*scan_offsets_per_slice + j]; if (scan_offset->first_len != length_offset) { printf("Bad length offset on slice %d, scanline %d: ",k,j); printf("%d should be %d\n", scan_offset->first_len, length_offset); } if (scan_offset->first_data != data_offset) { printf("Bad data offset on slice %d, scanline %d: ",k,j); printf("%d should be %d\n", scan_offset->first_data, data_offset); } } for (i = 0; i < ilen; i++) { while (run_length == 0) { run_length = *run_length_ptr++; is_non_zero = !is_non_zero; length_offset++; } run_length--; if (is_non_zero) data_offset += rle_bytes_per_voxel; } if (run_length != 0) { printf("Run did not terminate at end of scanline "); printf("on slice %d, scanline %d\n", k, j); } if (!is_non_zero) { if (*run_length_ptr++ != 0) { printf("Missing zero run at end of scanline "); printf("on slice %d, scanline %d\n", k, j); } is_non_zero = !is_non_zero; length_offset++; } } } } /* * VPValidateClassifiedVolume * * Compare the classified volume to the unclassified volume. */ void VPValidateClassifiedVolume(vpc) vpContext *vpc; { if (vpc->raw_voxels == NULL) return; if (vpc->rle_z != NULL) { printf("Checking Z view....\n"); ValidateRLEVoxels(vpc, vpc->rle_z, vpc->xstride, vpc->ystride, vpc->zstride, VP_Z_AXIS); } if (vpc->rle_y != NULL) { printf("Checking Y view....\n"); ValidateRLEVoxels(vpc, vpc->rle_y, vpc->zstride, vpc->xstride, vpc->ystride, VP_Y_AXIS); } if (vpc->rle_x != NULL) { printf("Checking X view....\n"); ValidateRLEVoxels(vpc, vpc->rle_x, vpc->ystride, vpc->zstride, vpc->xstride, VP_X_AXIS); } } static void ValidateRLEVoxels(vpc, rle, istride, jstride, kstride, axis) vpContext *vpc; RLEVoxels *rle; int istride, jstride, kstride; int axis; { char *rawvoxel; char *rlevoxel; unsigned char *lengths; int i, j, k; int count; int is_non_zero; int num_runs; float opacity; int ilen, jlen, klen; int founderror; int raw_opc_int; int rle_opc_int; int rle_bytes_per_voxel; rawvoxel = (char *)vpc->raw_voxels; rlevoxel = (char *)rle->data; lengths = rle->run_lengths; ilen = rle->ilen; jlen = rle->jlen; klen = rle->klen; rle_bytes_per_voxel = vpc->rle_bytes_per_voxel; founderror = 0; for (k = 0; k < klen; k++) { for (j = 0; j < jlen; j++) { count = 0; is_non_zero = 1; num_runs = 0; for (i = 0; i < ilen; i++) { while (count == 0) { count = *lengths++; is_non_zero = !is_non_zero; if (++num_runs > rle->ilen) VPBug("runaway scan detected by ValidateRLEVoxels"); } opacity = VPClassifyVoxel(vpc, rawvoxel); if (is_non_zero) { if (opacity <= vpc->min_opacity && fabs(opacity - vpc->min_opacity) > 0.001) { printf("\n"); printf("**** zero rawvoxel in nonzero rlerun ****\n"); printf("voxel (i,j,k)=(%d,%d,%d), viewaxis %d\n", i, j, k, axis); printf("Actual opacity: %17.15f\n", opacity); printf("Threshold: %17.15f\n", vpc->min_opacity); founderror = 1; } raw_opc_int = (int)rint(opacity*255.); rle_opc_int = ByteField(rlevoxel, rle_bytes_per_voxel-1); if (abs(raw_opc_int - rle_opc_int) > 1) { printf("\n"); printf("**** rawvoxel and rlevoxel disagree ****\n"); printf("voxel (i,j,k)=(%d,%d,%d), viewaxis %d\n", i, j, k, axis); printf("Raw opacity: %3d\n", raw_opc_int); printf("RLE opacity: %3d\n", rle_opc_int); founderror = 1; } rlevoxel += rle_bytes_per_voxel; } else { if (opacity > vpc->min_opacity && fabs(opacity - vpc->min_opacity) > 0.001) { printf("\n"); printf("**** nonzero rawvoxel in zero rlerun ****\n"); printf("voxel (i,j,k)=(%d,%d,%d), viewaxis %d\n", i, j, k, axis); printf("Actual opacity: %17.15f\n", opacity); printf("Threshold: %17.15f\n", vpc->min_opacity); founderror = 1; } } if (founderror) { VPDumpClassifier(vpc); VPBug("ValidateRLEVoxels found a problem"); } rawvoxel += istride; count--; } if (count != 0) VPBug("Run did not terminate at end of scanline"); if (!is_non_zero) { if (*lengths++ != 0) VPBug("Missing zero run at end of scanline"); is_non_zero = !is_non_zero; } rawvoxel += jstride - ilen*istride; } rawvoxel += kstride - jlen*jstride; } } #endif void VPDumpView(vpc) vpContext *vpc; { int c; printf("MODEL:\n"); for (c = 0; c < 4; c++) { printf(" %12.6f %12.6f %12.6f %12.6f\n", vpc->transforms[VP_MODEL][c][0], vpc->transforms[VP_MODEL][c][1], vpc->transforms[VP_MODEL][c][2], vpc->transforms[VP_MODEL][c][3]); } printf("VIEW:\n"); for (c = 0; c < 4; c++) { printf(" %12.6f %12.6f %12.6f %12.6f\n", vpc->transforms[VP_MODEL][c][0], vpc->transforms[VP_MODEL][c][1], vpc->transforms[VP_MODEL][c][2], vpc->transforms[VP_MODEL][c][3]); } printf("PROJECT:\n"); for (c = 0; c < 4; c++) { printf(" %12.6f %12.6f %12.6f %12.6f\n", vpc->transforms[VP_MODEL][c][0], vpc->transforms[VP_MODEL][c][1], vpc->transforms[VP_MODEL][c][2], vpc->transforms[VP_MODEL][c][3]); } } void VPDumpClassifier(vpc) vpContext *vpc; { int c, d; for (d = 0; d < vpc->num_clsfy_params; d++) { printf("CLASSIFIER PARAM %d:\n ", d); for (c = 0; c < vpc->field_max[vpc->param_field[d]]; c++) { printf(" %8.6f", vpc->clsfy_table[d][c]); if (c % 8 == 7) printf("\n "); } printf("\n"); } }