// Source file for GAPS scalar grid class



////////////////////////////////////////////////////////////////////////
// NOTE:
// Grid values are defined as samples at the grid positions ranging from
// (0, 0, 0) to (xres-1, yres-1, zres-1).  Grid values outside this range
// are undefined.
////////////////////////////////////////////////////////////////////////



// Include files

#include "R3Shapes/R3Shapes.h"



// Useful constants

const RNScalar R3_GRID_KEEP_VALUE = -987654321;



R3Grid::
R3Grid(int xresolution, int yresolution, int zresolution)
{
  // Set grid resolution
  grid_resolution[0] = xresolution;
  grid_resolution[1] = yresolution;
  grid_resolution[2] = zresolution;
  grid_row_size = xresolution;
  grid_sheet_size = grid_row_size * yresolution;
  grid_size = grid_sheet_size * zresolution;

  // Allocate grid values
  if (grid_size == 0) grid_values = NULL;
  else grid_values = new RNScalar [ grid_size ];
  assert(!grid_size || grid_values);

  // Set transformations
  grid_to_world_transform = R3identity_affine;
  world_to_grid_transform = R3identity_affine;
  world_to_grid_scale_factor = 1.0;
  grid_to_world_scale_factor = 1.0;

  // Set all values to zero
  Clear(0);
}



R3Grid::
R3Grid(int xresolution, int yresolution, int zresolution, const R3Box& bbox)
{
  // Set grid resolution
  grid_resolution[0] = xresolution;
  grid_resolution[1] = yresolution;
  grid_resolution[2] = zresolution;
  grid_row_size = xresolution;
  grid_sheet_size = grid_row_size * yresolution;
  grid_size = grid_sheet_size * zresolution;

  // Allocate grid values
  if (grid_size == 0) grid_values = NULL;
  else grid_values = new RNScalar [ grid_size ];
  assert(!grid_size || grid_values);

  // Set transformations
  SetWorldToGridTransformation(bbox);

  // Set all values to zero
  Clear(0);
}



R3Grid::
R3Grid(const R3Grid& voxels)
  : grid_values(NULL)
{
  // Copy everything
  *this = voxels;
}



R3Grid::
~R3Grid(void)
{
  // Deallocate memory for grid values
  if (grid_values) delete [] grid_values;
}



RNScalar R3Grid::
Variance(void) const
{
  // Return the variance of the values in the grid
  RNScalar sum = 0;
  RNScalar mean = Mean();
  RNScalar *grid_valuep = grid_values;
  for (int i = 0; i < grid_size; i++) {
    RNScalar delta = (*(grid_valuep++) - mean);
    sum += delta * delta;
  }
  return sum / grid_size;
}



RNInterval R3Grid::
Range(void) const
{
  // Find smallest and largest values
  RNScalar minimum = FLT_MAX;
  RNScalar maximum = -FLT_MAX;
  RNScalar *grid_valuep = grid_values;
  for (int i = 0; i < grid_size; i++) {
    if (*grid_valuep < minimum) minimum = *grid_valuep;
    if (*grid_valuep > maximum) maximum = *grid_valuep;
    grid_valuep++;
  }
  return RNInterval(minimum, maximum);
}



RNScalar R3Grid::
L1Norm(void) const
{
  // Return L1 norm of grid
  RNScalar sum = 0.0;
  RNScalar *grid_valuep = grid_values;
  for (int i = 0; i < grid_size; i++) 
    sum += *(grid_valuep++);
  return sum;
}



RNScalar R3Grid::
L2NormSquared(void) const
{
  // Return L2 norm of grid
  RNScalar sum = 0.0;
  RNScalar *grid_valuep = grid_values;
  for (int i = 0; i < grid_size; i++) {
    RNScalar value = *(grid_valuep++);
    sum += value * value;
  }
  return sum;
}




int R3Grid::
Cardinality(void) const
{
  // Return number of non-zero grid values
  int count = 0;
  RNScalar *grid_valuep = grid_values;
  for (int i = 0; i < grid_size; i++) 
    if (*(grid_valuep++) != 0.0) count++;
  return count;
}



RNScalar R3Grid::
GridValue(RNScalar x, RNScalar y, RNScalar z) const
{
  // Check if within bounds
  if ((x < 0) || (x > grid_resolution[0]-1)) return 0.0;
  if ((y < 0) || (y > grid_resolution[1]-1)) return 0.0;
  if ((z < 0) || (z > grid_resolution[2]-1)) return 0.0;

  // Trilinear interpolation
  int ix1 = (int) x;
  int iy1 = (int) y;
  int iz1 = (int) z;
  int ix2 = ix1 + 1;
  int iy2 = iy1 + 1;
  int iz2 = iz1 + 1;
  if (ix2 >= grid_resolution[0]) ix2 = ix1;
  if (iy2 >= grid_resolution[1]) iy2 = iy1;
  if (iz2 >= grid_resolution[2]) iz2 = iz1;
  RNScalar dx = x - ix1;
  RNScalar dy = y - iy1;
  RNScalar dz = z - iz1;
  RNScalar value = 0.0;
  value += GridValue(ix1, iy1, iz1) * (1.0-dx) * (1.0-dy) * (1.0-dz);
  value += GridValue(ix1, iy1, iz2) * (1.0-dx) * (1.0-dy) * dz;
  value += GridValue(ix1, iy2, iz1) * (1.0-dx) * dy * (1.0-dz);
  value += GridValue(ix1, iy2, iz2) * (1.0-dx) * dy * dz;
  value += GridValue(ix2, iy1, iz1) * dx * (1.0-dy) * (1.0-dz);
  value += GridValue(ix2, iy1, iz2) * dx * (1.0-dy) * dz;
  value += GridValue(ix2, iy2, iz1) * dx * dy * (1.0-dz);
  value += GridValue(ix2, iy2, iz2) * dx * dy * dz;
  return value;
}



RNScalar R3Grid::
GridValue(RNScalar x, RNScalar y, RNScalar z, RNLength sigma) const
{
  // Check if within bounds
  if ((x < 0) || (x > grid_resolution[0]-1)) return 0.0;
  if ((y < 0) || (y > grid_resolution[1]-1)) return 0.0;
  if ((z < 0) || (z > grid_resolution[2]-1)) return 0.0;

  // Determine Gaussian filter extent
  int f = (int) (3 * sigma + 0.5);
  int xmin = (int) (x - f + 0.5);
  int xmax = (int) (x + f + 0.5);
  int ymin = (int) (y - f + 0.5);
  int ymax = (int) (y + f + 0.5);
  int zmin = (int) (z - f + 0.5);
  int zmax = (int) (z + f + 0.5);
  if (xmin < 0) xmin = 0;
  if (xmax > XResolution()-1) xmax = XResolution()-1;
  if (ymin < 0) ymin = 0;
  if (ymax > YResolution()-1) ymax = YResolution()-1;
  if (zmin < 0) zmin = 0;
  if (zmax > ZResolution()-1) zmax = ZResolution()-1;

  // Compute Gaussian weighting variables
  const RNScalar sqrt_two_pi = sqrt(RN_TWO_PI);
  double a = sqrt_two_pi * sigma;
  double fac = 1.0 / (a * a * a);
  double denom = 2.0 * sigma * sigma;

  // Filter sample with Gaussian
  RNScalar value = 0.0;
  RNScalar weight = 0.0;
  for (int k = zmin; k <= zmax; k++) {
    for (int j = ymin; j <= ymax; j++) {
      for (int i = xmin; i <= xmax; i++) {
        RNScalar dx = x - i;
        RNScalar dy = y - j;
        RNScalar dz = z - k;
        RNScalar d = dx*dx + dy*dy + dz*dz;
        RNScalar w = fac * exp(-d / denom);
        value += w * GridValue(i, j, k);
        weight += w;
      }
    }
  }

  // Normalize value based on total weight of Gaussian samples
  if (RNIsNotZero(weight)) value /= weight;

  // Return value
  return value;
}



RNScalar R3Grid::
WorldValue(RNCoord x, RNCoord y, RNCoord z, RNLength sigma) const
{
  // Return value at world point using Gaussian filtering
  R3Point p = GridPosition(x, y, z);
  return GridValue(p[0], p[1], p[2], sigma * WorldToGridScaleFactor());
}



R3Point R3Grid::
GridCentroid(void) const
{
  // Compute weighted sum
  RNScalar total_value = 0;
  R3Point centroid(0,0,0);
  RNScalar *grid_valuesp = grid_values;
  for (int k = 0; k < grid_resolution[2]; k++) {
    for (int j = 0; j < grid_resolution[1]; j++) {
      for (int i = 0; i < grid_resolution[0]; i++) {
        R3Vector position(i, j, k);
        RNScalar value = *(grid_valuesp++);
        centroid += value * position;
        total_value += value;
      }
    }
  }

  // Divide by total value
  if (total_value > 0) centroid /= total_value;

  // Return centroid
  return centroid;
}



R3Triad R3Grid::
GridPrincipleAxes(const R3Point *grid_center, RNScalar *variances) const
{
  // Get centroid
  R3Point center = (grid_center) ? *grid_center : GridCentroid();

  // Compute covariance matrix
  RNScalar m[9] = { 0 };
  RNScalar total_value = 0;
  RNScalar *grid_valuesp = grid_values;
  for (int k = 0; k < grid_resolution[2]; k++) {
    for (int j = 0; j < grid_resolution[1]; j++) {
      for (int i = 0; i < grid_resolution[0]; i++) {
        R3Point position(i, j, k);
        RNScalar value = *(grid_valuesp++);
        RNScalar x = position[0] - center[0];
        RNScalar y = position[1] - center[1];
        RNScalar z = position[2] - center[2];
        m[0] += value * x*x;
        m[4] += value * y*y;
        m[8] += value * z*z;
        m[1] += value * x*y;
        m[3] += value * x*y;
        m[2] += value * x*z;
        m[6] += value * x*z;
        m[5] += value * y*z;
        m[7] += value * y*z;
        total_value += value;
      }
    }
  }

  // Normalize covariance matrix
  if (total_value == 0) return R3xyz_triad;
  for (int i = 0; i < 9; i++) m[i] /= total_value;

  // Compute eigenvalues and eigenvectors
  RNScalar U[9];
  RNScalar W[3];
  RNScalar Vt[9];
  RNSvdDecompose(3, 3, m, U, W, Vt);  // m == U . DiagonalMatrix(W) . Vt

  // Copy principle axes into more convenient form
  // W has eigenvalues (greatest to smallest) and Vt has eigenvectors (normalized)
  R3Vector axes[3];
  for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
      axes[i][j] = Vt[3*i+j];
    }
  }

  // Flip first two axes so that "heavier" on positive side 
  grid_valuesp = grid_values;
  RNScalar positive_count[2] = { 0, 0 };
  RNScalar negative_count[2] = { 0, 0 };
  for (int k = 0; k < grid_resolution[2]; k++) {
    for (int j = 0; j < grid_resolution[1]; j++) {
      for (int i = 0; i < grid_resolution[0]; i++) {
        R3Point position(i, j, k);
        RNScalar value = *(grid_valuesp++);
        R3Vector vector = position - center;
        for (int j = 0; j < 2; j++) {
          RNScalar dot = axes[j].Dot(vector);
          if (dot > 0.0) positive_count[j] += value;
          else negative_count[j] += value;
        }
      }
    }
  }
  for (int j =0; j < 2; j++) {
    if (positive_count[j] < negative_count[j]) {
      axes[j].Flip();
    }
  }

  // Set third axis to form orthonormal triad with other two
  axes[2] = axes[0] % axes[1];

  // Just checking
  assert(RNIsEqual(axes[0].Length(), 1.0, RN_BIG_EPSILON));
  assert(RNIsEqual(axes[1].Length(), 1.0, RN_BIG_EPSILON));
  assert(RNIsEqual(axes[2].Length(), 1.0, RN_BIG_EPSILON));
  assert(RNIsZero(axes[0].Dot(axes[1]), RN_BIG_EPSILON));
  assert(RNIsZero(axes[1].Dot(axes[2]), RN_BIG_EPSILON));
  assert(RNIsZero(axes[0].Dot(axes[2]), RN_BIG_EPSILON));

  // Return variances (eigenvalues)
  if (variances) {
    variances[0] = W[0];
    variances[1] = W[1];
    variances[2] = W[2];
  }

  // Return triad
  return R3Triad(axes[0], axes[1], axes[2]);
}



R3Grid& R3Grid::
operator=(const R3Grid& voxels) 
{
  // Delete old grid values
  if (grid_values) {
    if (grid_size != voxels.grid_size) {
      delete [] grid_values;
      grid_values = NULL;
    }
  }

  // Allocate new grid values
  if (!grid_values && (voxels.grid_size > 0)) {
    grid_values = new RNScalar [ voxels.grid_size ];
    assert(grid_values);
  }

  // Copy grid resolution
  grid_resolution[0] = voxels.grid_resolution[0];
  grid_resolution[1] = voxels.grid_resolution[1];
  grid_resolution[2] = voxels.grid_resolution[2];
  grid_row_size = voxels.grid_row_size;
  grid_sheet_size = voxels.grid_sheet_size;
  grid_size = voxels.grid_size;

  // Copy grid values
  for (int i = 0; i < grid_size; i++) {
    grid_values[i] = voxels.grid_values[i];
  }

  // Copy transforms
  grid_to_world_transform = voxels.grid_to_world_transform;
  world_to_grid_transform = voxels.world_to_grid_transform;
  world_to_grid_scale_factor = voxels.world_to_grid_scale_factor;
  grid_to_world_scale_factor = voxels.grid_to_world_scale_factor;

  // Return this
  return *this;
}



void R3Grid::
Abs(void) 
{
  // Take the absolute value of every grid value
  RNScalar *grid_valuep = grid_values;
  for (int i = 0; i < grid_size; i++) {
    *grid_valuep = fabs(*grid_valuep);
    grid_valuep++;
  }
}



void R3Grid::
Sqrt(void) 
{
  // Take sqrt of every grid value
  RNScalar *grid_valuep = grid_values;
  for (int i = 0; i < grid_size; i++) {
    *grid_valuep = sqrt(*grid_valuep);
    grid_valuep++;
  }
}



void R3Grid::
Square(void) 
{
  // Square every grid value
  RNScalar *grid_valuep = grid_values;
  for (int i = 0; i < grid_size; i++) {
    *grid_valuep = (*grid_valuep) * (*grid_valuep);
    grid_valuep++;
  }
}



void R3Grid::
Negate(void) 
{
  // Negate every grid value
  RNScalar *grid_valuep = grid_values;
  for (int i = 0; i < grid_size; i++) {
    *grid_valuep = -(*grid_valuep);
    grid_valuep++;
  }
}



void R3Grid::
Invert(void) 
{
  // Invert every grid value
  RNScalar *grid_valuep = grid_values;
  for (int i = 0; i < grid_size; i++) {
    if (RNIsNotZero(*grid_valuep), 1.0E-20) *grid_valuep = 1.0/(*grid_valuep);
    grid_valuep++;
  }
}



void R3Grid::
Normalize(void) 
{
  // Scale so that length of "vector" is one
  Divide(L2Norm());
}



void R3Grid::
Dilate(RNLength grid_distance) 
{
  // Set voxels (to one) within grid_distance from some non-zero voxel
  SquaredDistanceTransform();
  Threshold(grid_distance * grid_distance, 1, 0);
}



void R3Grid::
Erode(RNLength grid_distance) 
{
  // Keep only voxels at least distance from some zero voxel
  Threshold(1.0E-20, 1, 0);
  SquaredDistanceTransform();
  Threshold(grid_distance * grid_distance, 0, 1);
}



void R3Grid::
Blur(RNLength grid_sigma) 
{
  // Build filter
  RNScalar sigma = grid_sigma;
  int filter_radius = (int) (3 * sigma + 0.5);
  RNScalar *filter = new RNScalar [ filter_radius + 1 ];
  assert(filter);

  // Make buffer for temporary copy of row
  int res = XResolution();
  if (res < YResolution()) res = YResolution();
  if (res < ZResolution()) res = ZResolution();
  RNScalar *buffer = new RNScalar [ res ];
  assert(buffer);

  // Fill filter with Gaussian 
  const RNScalar sqrt_two_pi = sqrt(RN_TWO_PI);
  double a = sqrt_two_pi * sigma;
  double fac = 1.0 / (a * a * a);
  double denom = 2.0 * sigma * sigma;
  for (int i = 0; i <= filter_radius; i++) {
    filter[i] = fac * exp(-i * i / denom);
  }

  // Convolve grid with filter in X direction
  for (int k = 0; k < ZResolution(); k++) {
    for (int j = 0; j < YResolution(); j++) { 
      for (int i = 0; i < XResolution(); i++) 
        buffer[i] = GridValue(i, j, k); 
      for (int i = 0; i < XResolution(); i++) { 
        RNScalar sum = filter[0] * buffer[i];
        RNScalar weight = filter[0];
        int nsamples = i;
        if (nsamples > filter_radius) nsamples = filter_radius;
        for (int m = 1; m <= nsamples; m++) {
          sum += filter[m] * buffer[i - m];
          weight += filter[m];
        }
        nsamples = XResolution() - 1 - i;
        if (nsamples > filter_radius) nsamples = filter_radius;
        for (int m = 1; m <= nsamples; m++) {
          sum += filter[m] * buffer[i + m];
          weight += filter[m];
        }
        SetGridValue(i, j, k, sum / weight);
      }
    }
  }

  // Convolve grid with filter in Y direction
  for (int k = 0; k < ZResolution(); k++) {
    for (int j = 0; j < XResolution(); j++) { 
      for (int i = 0; i < YResolution(); i++) 
        buffer[i] = GridValue(j, i, k); 
      for (int i = 0; i < YResolution(); i++) { 
        RNScalar sum = filter[0] * buffer[i];
        RNScalar weight = filter[0];
        int nsamples = i;
        if (nsamples > filter_radius) nsamples = filter_radius;
        for (int m = 1; m <= nsamples; m++) {
          sum += filter[m] * buffer[i - m];
          weight += filter[m];
        }
        nsamples = YResolution() - 1 - i;
        if (nsamples > filter_radius) nsamples = filter_radius;
        for (int m = 1; m <= nsamples; m++) {
          sum += filter[m] * buffer[i + m];
          weight += filter[m];
        }
        SetGridValue(j, i, k, sum / weight);
      }
    }
  }

  // Convolve grid with filter in Z direction
  for (int k = 0; k < YResolution(); k++) {
    for (int j = 0; j < XResolution(); j++) { 
      for (int i = 0; i < ZResolution(); i++) 
        buffer[i] = GridValue(j, k, i); 
      for (int i = 0; i < ZResolution(); i++) { 
        RNScalar sum = filter[0] * buffer[i];
        RNScalar weight = filter[0];
        int nsamples = i;
        if (nsamples > filter_radius) nsamples = filter_radius;
        for (int m = 1; m <= nsamples; m++) {
          sum += filter[m] * buffer[i - m];
          weight += filter[m];
        }
        nsamples = ZResolution() - 1 - i;
        if (nsamples > filter_radius) nsamples = filter_radius;
        for (int m = 1; m <= nsamples; m++) {
          sum += filter[m] * buffer[i + m];
          weight += filter[m];
        }
        SetGridValue(j, k, i, sum / weight);
      }
    }
  }

  // Deallocate memory
  delete [] filter;
  delete [] buffer;
}



void R3Grid::
DetectEdges(void)
{
  // Just a simple method for now
  RNScalar filter[3][3][3] = { 
    { { -1, -1, -1 }, { -1, -1, -1 }, { -1, -1, -1 } }, 
    { { -1, -1, -1 }, { -1, 26, -1 }, { -1, -1, -1 } }, 
    { { -1, -1, -1 }, { -1, -1, -1 }, { -1, -1, -1 } } 
  };

  // Make temporary copy of grid
  R3Grid copy(*this);

  // Convolve grid with simple edge detection filter
  for (int k = 1; k < ZResolution()-1; k++) {
    for (int j = 1; j < YResolution()-1; j++) { 
      for (int i = 1; i < XResolution()-1; i++) { 
        RNScalar sum = 0;
        for (int dk = -1; dk <= 1; dk++) {
          for (int dj = -1; dj <= 1; dj++) {
            for (int di = -1; di <= 1; di++) {
              sum += filter[dk+1][dj+1][di+1] * copy.GridValue(i + di, j + dj, k + dk);
            }
          }
        }
        SetGridValue(i, j, k, fabs(sum));
      }
    }
  }
}



void R3Grid::
Clear(RNScalar value) 
{
  // Set all grid values to value
  RNScalar *grid_valuep = grid_values;
  for (int i = 0; i < grid_size; i++) 
    *(grid_valuep++) = value;
}



void R3Grid::
Add(RNScalar value) 
{
  // Add value to all grid values 
  RNScalar *grid_valuep = grid_values;
  for (int i = 0; i < grid_size; i++) 
    *(grid_valuep++) += value;
}



void R3Grid::
Copy(const R3Grid& voxels) 
{
  // Resolutions must be the same (for now)
  assert(grid_resolution[0] == voxels.grid_resolution[0]);
  assert(grid_resolution[1] == voxels.grid_resolution[1]);
  assert(grid_resolution[2] == voxels.grid_resolution[2]);

  // Copy passed grid values to corresponding entries of this grid
  for (int i = 0; i < grid_size; i++) 
    grid_values[i] = voxels.grid_values[i];
}



void R3Grid::
Add(const R3Grid& voxels) 
{
  // Resolutions must be the same (for now)
  assert(grid_resolution[0] == voxels.grid_resolution[0]);
  assert(grid_resolution[1] == voxels.grid_resolution[1]);
  assert(grid_resolution[2] == voxels.grid_resolution[2]);

  // Add passed grid values to corresponding entries of this grid
  for (int i = 0; i < grid_size; i++) 
    grid_values[i] += voxels.grid_values[i];
}



void R3Grid::
Add(const R3Grid& filter, const R3Point& grid_position, const R3Point& filter_position, RNScalar amplitude)
{
  // Determine extent of filter in grid coordinates
  int x1 = (int) (grid_position.X() - filter_position.X() + 1);
  int y1 = (int) (grid_position.Y() - filter_position.Y() + 1);
  int z1 = (int) (grid_position.Z() - filter_position.Z() + 1);
  int x2 = (int) (grid_position.X() + filter_position.X());
  int y2 = (int) (grid_position.Y() + filter_position.Y());
  int z2 = (int) (grid_position.Z() + filter_position.Z());
  if (x1 < 0) x1 = 0;
  if (y1 < 0) y1 = 0;
  if (z1 < 0) z1 = 0;
  if (x2 < 0) return;
  if (y2 < 0) return;
  if (z2 < 0) return;
  if (x1 >= XResolution()) return;
  if (y1 >= YResolution()) return;
  if (z1 >= ZResolution()) return;
  if (x2 >= XResolution()) x2 = XResolution()-1;
  if (y2 >= YResolution()) y2 = YResolution()-1;
  if (z2 >= ZResolution()) z2 = ZResolution()-1;

  // Add amplitude*filter to grid (aligning grid_position and filter_position)
  RNScalar sz = z1 - grid_position.Z() + filter_position.Z();
  assert((sz >= 0) && (sz < filter.ZResolution()));
  for (int gz = z1; gz <= z2; gz++, sz += 1) {
    RNScalar sy = y1 - grid_position.Y() + filter_position.Y();
    assert((sy >= 0) && (sy < filter.YResolution()));
    for (int gy = y1; gy <= y2; gy++, sy += 1) {
      RNScalar sx = x1 - grid_position.X() + filter_position.X();
      assert((sx >= 0) && (sx < filter.XResolution()));
      RNScalar *grid_valuesp = &grid_values[gz * grid_sheet_size + gy * grid_row_size + x1];
      for (int gx = x1; gx <= x2; gx++, sx += 1) {
        (*grid_valuesp++) += amplitude * filter.GridValue(sx, sy, sz);
      }
    }
  }
}



void R3Grid::
Subtract(RNScalar value) 
{
  // Add the opposite
  Add(-value);
}



void R3Grid::
Subtract(const R3Grid& voxels) 
{
  // Resolutions must be the same (for now)
  assert(grid_resolution[0] == voxels.grid_resolution[0]);
  assert(grid_resolution[1] == voxels.grid_resolution[1]);
  assert(grid_resolution[2] == voxels.grid_resolution[2]);

  // Subtract passed grid values from corresponding entries of this grid
  for (int i = 0; i < grid_size; i++) 
    grid_values[i] -= voxels.grid_values[i];
}



void R3Grid::
Multiply(RNScalar value) 
{
  // Multiply grid values by value
  RNScalar *grid_valuep = grid_values;
  for (int i = 0; i < grid_size; i++) 
    *(grid_valuep++) *= value;
}



void R3Grid::
Multiply(const R3Grid& voxels) 
{
  // Resolutions must be the same (for now)
  assert(grid_resolution[0] == voxels.grid_resolution[0]);
  assert(grid_resolution[1] == voxels.grid_resolution[1]);
  assert(grid_resolution[2] == voxels.grid_resolution[2]);

  // Multiply passed grid values by corresponding entries of this grid
  for (int i = 0; i < grid_size; i++) 
    grid_values[i] *= voxels.grid_values[i];
}



void R3Grid::
Divide(RNScalar value) 
{
  // Just checking
  if (RNIsZero(value, 1.0E-20)) return;

  // Multiply by recipricol
  Multiply(1.0 / value);
}



void R3Grid::
Divide(const R3Grid& voxels) 
{
  // Resolutions must be the same (for now)
  assert(grid_resolution[0] == voxels.grid_resolution[0]);
  assert(grid_resolution[1] == voxels.grid_resolution[1]);
  assert(grid_resolution[2] == voxels.grid_resolution[2]);

  // Divide passed grid values by corresponding entries of this grid
  for (int i = 0; i < grid_size; i++) {
    RNScalar value = voxels.grid_values[i];
    if (RNIsNotZero(value, 1.0E-20)) grid_values[i] /= value;
  }
}



void R3Grid::
Pow(RNScalar exponent) 
{
  // Raise each grid value to exponent
  RNScalar *grid_valuep = grid_values;
  for (int i = 0; i < grid_size; i++) {
    RNScalar value = *grid_valuep;
    if (value < 0) value = -value;
    *(grid_valuep++) = pow(value, exponent);
  }
}



void R3Grid::
Mask(const R3Grid& mask) 
{
  // Resolutions must be the same (for now)
  assert(grid_resolution[0] == mask.grid_resolution[0]);
  assert(grid_resolution[1] == mask.grid_resolution[1]);
  assert(grid_resolution[2] == mask.grid_resolution[2]);

  // Set values to zero where mask is zero
  for (int i = 0; i < grid_size; i++) 
    if (mask.grid_values[i] == 0) grid_values[i] = 0;
}



void R3Grid::
Threshold(RNScalar threshold, RNScalar low, RNScalar high) 
{
  // Set grid value to low (high) if less/equal (greater) than threshold
  RNScalar *grid_valuep = grid_values;
  for (int i = 0; i < grid_size; i++) {
    if (*grid_valuep <= threshold) {
      if (low != R3_GRID_KEEP_VALUE) *grid_valuep = low;
    }
    else {
      if (high != R3_GRID_KEEP_VALUE) *grid_valuep = high;
    }
    grid_valuep++;
  }
}



void R3Grid::
Voronoi(R3Grid *squared_distance_grid)
{
  int dist;
  int* old_dist;
  int* new_dist;
  RNScalar value;
  RNScalar *old_value;
  RNScalar *new_value;
  R3Grid *dgrid;
  int res,square,tmp_dist,first;
  int x,y,z,s,t,i;

  // Allocate distance grid
  if (squared_distance_grid) dgrid = squared_distance_grid;
  else dgrid = new R3Grid(XResolution(), YResolution(), ZResolution());
  assert(dgrid);
  dgrid->SetWorldToGridTransformation(WorldToGridTransformation());

  // Allocate temporary buffers
  res = XResolution();
  if (res < YResolution()) res = YResolution();
  if (res < ZResolution()) res = ZResolution();
  old_dist = new int[res];
  new_dist = new int[res];
  old_value = new RNScalar[res];
  new_value = new RNScalar[res];
  assert(old_dist && new_dist && old_value && new_value);

  // Initalize distance grid values (0 if was set, max_value if not)
  RNScalar max_value = 3 * (res+1) * (res+1) * (res+1);
  for (i = 0; i < grid_size; i++) {
    if (grid_values[i] == 0.0) dgrid->grid_values[i] = max_value;
    else dgrid->grid_values[i] = 0.0;
  }

  // Scan along z axis
  for (x = 0; x < XResolution(); x++) {
    for (y = 0; y < YResolution(); y++) {
      first = 1;
      value = 0;
      dist = 0;
      for (z = 0; z < ZResolution(); z++) {
        if (dgrid->GridValue(x,y,z) == 0.0) {
          first=0;
          dist=0;
          value = GridValue(x,y,z);
          assert(value != 0);
        }
        else if (first == 0) {
          dist++;
          square = dist*dist;
          dgrid->SetGridValue(x, y, z, square);
          SetGridValue(x, y, z, value);
          assert(value != 0);
        }
      }
			
      // backward scan
      dist = 0;
      first = 1;
      for (z = ZResolution()-1; z >= 0; z--) {
        if (dgrid->GridValue(x,y,z) == 0.0){
          dist = 0;
          first = 0;
          value = GridValue(x,y,z);
          assert(value != 0);
        }
        else if (first == 0) {
          dist++;
          square = dist*dist;
          if (square < dgrid->GridValue(x, y, z)) {
            dgrid->SetGridValue(x, y, z, square);
            SetGridValue(x, y, z, value);
            assert(value != 0);
          }
        }
      }
    }
  } 

  // Scan along x axis
  for (z = 0; z < ZResolution(); z++) {
    for (y = 0; y < YResolution(); y++) {
      // Copy grid values
      for(x = 0; x < XResolution(); x++) {
        old_dist[x] = (int) (dgrid->GridValue(x, y, z) + 0.5);
        old_value[x] = GridValue(x, y, z);
      }
		
      // forward scan
      s = 0;
      for (x = 0; x < XResolution(); x++) {
        dist = old_dist[x];
        value = old_value[x];
        if (dist) {
          for(t = s; t <= x; t++) {
            tmp_dist = old_dist[t] + (x - t) * (x - t);
            if (tmp_dist <= dist) {
              dist = tmp_dist;
              value = old_value[t];
              s = t;
            }
          }
        }
        else {
          s = x;
        }
        new_dist[x] = dist;
        new_value[x] = value;
      }
			
      // backwards scan
      s = XResolution() - 1;
      for (x = XResolution()-1; x >= 0 ; x--) {
        dist = new_dist[x];
        value = new_value[x];
        if (dist) {
          for (t = s; t >= x; t--) {
            tmp_dist = old_dist[t] + (x - t) * (x - t);
            if (tmp_dist <= dist) {
              dist = tmp_dist;
              value = old_value[t];
              s = t;
            }
          }
          dgrid->SetGridValue(x, y, z, dist);
          SetGridValue(x, y, z, value);
        }
        else {
          s=x;
        }
      }
    }
  }
		
  // along y axis
  for (z = 0; z < ZResolution(); z++) {
    for (x = 0; x < XResolution(); x++) {
      // Copy grid values
      for (y = 0; y < YResolution(); y++) {
        old_dist[y] = (int) (dgrid->GridValue(x, y, z));
        old_value[y] = GridValue(x, y, z);
      }
			
      // forward scan
      s = 0;
      for (y = 0; y < YResolution(); y++) {
        dist = old_dist[y];
        value = old_value[y];
        if (dist) {
          for (t = s; t <= y ; t++) {
            tmp_dist = old_dist[t] + (y - t) * (y - t);
            if (tmp_dist <= dist){
              dist = tmp_dist;
              value = old_value[t];
              s = t;
            }
          }
        }
        else { 
          s = y;
        }
        new_dist[y] = dist;
        new_value[y] = value;
      }

      // backward scan
      s = YResolution() - 1;
      for(y = YResolution()-1; y >=0 ; y--) {
        dist = new_dist[y];
        value = new_value[y];
        if (dist) {
          for (t = s; t > y ; t--) {
            tmp_dist = old_dist[t] + (y - t) * (y - t);
            if (tmp_dist <= dist){
              dist = tmp_dist;
              value = old_value[t];
              s = t;
            }
          }
          dgrid->SetGridValue(x, y, z, dist);
          SetGridValue(x, y, z, value);
        }
        else { 
          s = y; 
        }
      }
    }
  }
	
  // Delete temporary buffers
  if (!squared_distance_grid) delete dgrid;
  delete[] old_dist;
  delete[] new_dist;
  delete[] old_value;
  delete[] new_value;
}



void R3Grid::
SquaredDistanceTransform(void)
{
  int x,y,z,s,t;
  int dist,square,new_dist;
  int* oldBuffer;
  int* newBuffer;
  int first;
  int i;

  // Allocate temporary buffers
  int res = XResolution();
  if (res < YResolution()) res = YResolution();
  if (res < ZResolution()) res = ZResolution();
  oldBuffer = new int[res];
  assert(oldBuffer);
  newBuffer = new int[res];
  assert(newBuffer);

  // Initalize values (0 if was set, max_value if not)
  RNScalar max_value = 3 * (res+1) * (res+1) * (res+1);
  RNScalar *grid_valuesp = grid_values;
  for (i = 0; i < grid_size; i++) {
    if (*grid_valuesp == 0.0) *grid_valuesp = max_value;
    else *grid_valuesp = 0.0;
    grid_valuesp++;
  }

  // Scan along z axis
  for (x = 0; x < XResolution(); x++) {
    for (y = 0; y < YResolution(); y++) {
      first = 1;
      dist = 0;
      for (z = 0; z < ZResolution(); z++) {
        if (GridValue(x,y,z) == 0.0) {
          dist=0;
          first=0;
          SetGridValue(x, y, z, 0);
        }
        else if (first == 0) {
          dist++;
          square = dist*dist;
          SetGridValue(x, y, z, square);
        }
      }
			
      // backward scan
      dist = 0;
      first = 1;
      for (z = ZResolution()-1; z >= 0; z--) {
        if (GridValue(x,y,z) == 0.0){
          dist = 0;
          first = 0;
          SetGridValue(x, y, z, 0);
        }
        else if (first == 0) {
          dist++;
          square = dist*dist;
          if (square < GridValue(x, y, z)) {
            SetGridValue(x, y, z, square);
          }
        }
      }
    }
  } 

  // Scan along x axis
  for (z = 0; z < ZResolution(); z++) {
    for (y = 0; y < YResolution(); y++) {
      // Copy grid values
      for(x = 0; x < XResolution(); x++) 
        oldBuffer[x] = (int) (GridValue(x, y, z) + 0.5);
		
      // forward scan
      s = 0;
      for (x = 0; x < XResolution(); x++) {
        dist = oldBuffer[x];
        if (dist) {
          for(t = s; t <= x; t++) {
            new_dist = oldBuffer[t] + (x - t) * (x - t);
            if (new_dist <= dist) {
              dist = new_dist;
              s = t;
            }
          }
        }
        else {
          s = x;
        }
        newBuffer[x] = dist;
      }
			
      // backwards scan
      s = XResolution() - 1;
      for (x = XResolution()-1; x >= 0 ; x--) {
        dist = newBuffer[x];
        if (dist) {
          for (t = s; t >= x; t--) {
            new_dist = oldBuffer[t] + (x - t) * (x - t);
            if (new_dist <= dist) {
              dist = new_dist;
              s = t;
            }
          }
          SetGridValue(x, y, z, dist);
        }
        else {
          s=x;
        }
      }
    }
  }
		
  // along y axis
  for (z = 0; z < ZResolution(); z++) {
    for (x = 0; x < XResolution(); x++) {
      // Copy grid values
      for (y = 0; y < YResolution(); y++)
        oldBuffer[y] = (int) (GridValue(x, y, z) + 0.5);
			
      // forward scan
      s = 0;
      for (y = 0; y < YResolution(); y++) {
        dist = oldBuffer[y];
        if (dist) {
          for (t = s; t <= y ; t++) {
            new_dist = oldBuffer[t] + (y - t) * (y - t);
            if (new_dist <= dist){
              dist = new_dist;
              s = t;
            }
          }
        }
        else { 
          s = y;
        }
        newBuffer[y] = dist;
      }

      // backward scan
      s = YResolution() - 1;
      for(y = YResolution()-1; y >=0 ; y--) {
        dist = newBuffer[y];
        if (dist) {
          for (t = s; t > y ; t--) {
            new_dist = oldBuffer[t] + (y - t) * (y - t);
            if (new_dist <= dist){
              dist = new_dist;
              s = t;
            }
          }
          SetGridValue(x, y, z, dist);
        }
        else { 
          s = y; 
        }
      }
    }
  }
	
  // Delete temporary buffers
  delete[] oldBuffer;
  delete[] newBuffer;
}



void R3Grid::
Gauss(RNLength sigma, RNBoolean square)
{
  // Check sigma
  if (sigma == 0.0) return;    

  // Replace each grid value with Gaussian of what was there before
  const RNScalar sqrt_two_pi = sqrt(RN_TWO_PI);
  RNScalar a = sqrt_two_pi * sigma;
  RNScalar fac = 1.0 / (a * a * a);
  RNScalar denom = -2.0 * sigma * sigma;
  if (RNIsZero(denom, 1.0E-6)) return;
  for (int i = 0; i < grid_size; i++) {
    RNScalar value = grid_values[i];
    if (square) value *= value;
    grid_values[i] = fac * exp( value / denom );
  }
}



void R3Grid::
Resample(int xresolution, int yresolution, int zresolution)
{
  // Resample grid values at new resolution
  RNScalar *new_grid_values = NULL;
  int new_grid_size = xresolution * yresolution * zresolution;
  if (new_grid_size > 0) {
    new_grid_values = new RNScalar [ new_grid_size ];
    assert(new_grid_values);
    RNScalar *new_grid_valuesp = new_grid_values;
    RNScalar xscale = (RNScalar) (grid_resolution[0]-1) / (RNScalar) (xresolution - 1);
    RNScalar yscale = (RNScalar) (grid_resolution[1]-1) / (RNScalar) (yresolution - 1);
    RNScalar zscale = (RNScalar) (grid_resolution[2]-1) / (RNScalar) (zresolution - 1);
    for (int k = 0; k < zresolution; k++) {
      RNScalar z = (k == zresolution-1) ? grid_resolution[2]-1 : k * zscale;
      for (int j = 0; j < yresolution; j++) {
        RNScalar y = (j == yresolution-1) ? grid_resolution[1]-1 : j * yscale;
        for (int i = 0; i < xresolution; i++) {
          RNScalar x = (i == xresolution-1) ? grid_resolution[0]-1 : i * xscale;
          *(new_grid_valuesp++) = GridValue(x, y, z);
        }
      }
    }
  }

  // Reset grid variables
  grid_resolution[0] = xresolution;
  grid_resolution[1] = yresolution;
  grid_resolution[2] = zresolution;
  grid_row_size = xresolution;
  grid_sheet_size = grid_row_size * yresolution;
  grid_size = grid_sheet_size * zresolution;
  if (grid_values) delete [] grid_values;
  grid_values = new_grid_values;
}



void R3Grid::
RasterizeGridPoint(RNScalar x, RNScalar y, RNScalar z, RNScalar value)
{
  // Check if within bounds
  if ((x < 0) || (x > grid_resolution[0]-1)) return;
  if ((y < 0) || (y > grid_resolution[1]-1)) return;
  if ((z < 0) || (z > grid_resolution[2]-1)) return;

  // Trilinear interpolation
  int ix1 = (int) x;
  int iy1 = (int) y;
  int iz1 = (int) z;
  int ix2 = ix1 + 1;
  int iy2 = iy1 + 1;
  int iz2 = iz1 + 1;
  if (ix2 >= grid_resolution[0]) ix2 = ix1;
  if (iy2 >= grid_resolution[1]) iy2 = iy1;
  if (iz2 >= grid_resolution[2]) iz2 = iz1;
  RNScalar dx = x - ix1;
  RNScalar dy = y - iy1;
  RNScalar dz = z - iz1;
  (*this)(ix1, iy1, iz1) += value * (1.0-dx) * (1.0-dy) * (1.0-dz);
  (*this)(ix1, iy1, iz2) += value * (1.0-dx) * (1.0-dy) * dz;
  (*this)(ix1, iy2, iz1) += value * (1.0-dx) * dy * (1.0-dz);
  (*this)(ix1, iy2, iz2) += value * (1.0-dx) * dy * dz;
  (*this)(ix2, iy1, iz1) += value * dx * (1.0-dy) * (1.0-dz);
  (*this)(ix2, iy1, iz2) += value * dx * (1.0-dy) * dz;
  (*this)(ix2, iy2, iz1) += value * dx * dy * (1.0-dz);
  (*this)(ix2, iy2, iz2) += value * dx * dy * dz;
}



void R3Grid::
RasterizeGridPoint(RNScalar x, RNScalar y, RNScalar z, RNScalar value, RNLength sigma)
{
  // Check if within bounds
  if ((x < 0) || (x > grid_resolution[0]-1)) return;
  if ((y < 0) || (y > grid_resolution[1]-1)) return;
  if ((z < 0) || (z > grid_resolution[2]-1)) return;

  // Determine filter extent
  int f = (int) (3 * sigma + 0.5);
  int xmin = (int) (x - f + 0.5);
  int xmax = (int) (x + f + 0.5);
  int ymin = (int) (y - f + 0.5);
  int ymax = (int) (y + f + 0.5);
  int zmin = (int) (z - f + 0.5);
  int zmax = (int) (z + f + 0.5);
  if (xmin < 0) xmin = 0;
  if (xmax > XResolution()-1) xmax = XResolution()-1;
  if (ymin < 0) ymin = 0;
  if (ymax > YResolution()-1) ymax = YResolution()-1;
  if (zmin < 0) zmin = 0;
  if (zmax > ZResolution()-1) zmax = ZResolution()-1;

  // Compute Gaussian weighting variables
  double a = sqrt(2.0 * RN_PI) * sigma;
  double fac = 1.0 / (a * a * a);
  double denom = 2.0 * sigma * sigma;

  // Compute total weight of Gaussian filter
  RNScalar weight = 0.0;
  for (int k = zmin; k <= zmax; k++) {
    for (int j = ymin; j <= ymax; j++) {
      for (int i = xmin; i <= xmax; i++) {
        RNScalar dx = x - i;
        RNScalar dy = y - j;
        RNScalar dz = z - k;
        RNScalar d = sqrt(dx*dx + dy*dy + dz*dz);
        RNScalar w = fac * exp(-d * d / denom);
        value += w * GridValue(i, j, k);
        weight += w;
      }
    }
  }

  // Splat values into grid (scaled by total weight of Gaussian filter)
  if (RNIsPositive(weight)) {
    for (int k = zmin; k <= zmax; k++) {
      for (int j = ymin; j <= ymax; j++) {
        for (int i = xmin; i <= xmax; i++) {
          RNScalar dx = x - i;
          RNScalar dy = y - j;
          RNScalar dz = z - k;
          RNScalar d = sqrt(dx*dx + dy*dy + dz*dz);
          RNScalar w = fac * exp(-d * d / denom);
          AddGridValue(i, j, k, w * value / weight);
        }
      }
    }
  }
}



void R3Grid::
RasterizeWorldPoint(RNCoord x, RNCoord y, RNCoord z, RNScalar value, RNScalar sigma)
{
  // Splat value at world point with Gaussian filtering
  R3Point p = GridPosition(x, y, z);
  RasterizeGridPoint(p[0], p[1], p[2], value, sigma * WorldToGridScaleFactor());
}



void R3Grid::
RasterizeGridSpan(const int p1[3], const int p2[3], RNScalar value)
{
  // Get some convenient variables
  int d[3],p[3],dd[3],s[3];
  for (int i = 0; i < 3; i++) {
    d[i]= p2[i] - p1[i];
    if(d[i]<0){
      dd[i] = -d[i];
      s[i] = -1;
    }
    else{
      dd[i] = d[i];
      s[i] = 1;
    }
    p[i] = p1[i];
  }

  // Choose dimensions
  int i1=0;
  if(dd[1]>dd[i1]){i1=1;}
  if(dd[2]>dd[i1]){i1=2;}
  int i2=(i1+1)%3;
  int i3=(i1+2)%3;

  // Check span extent
  if(dd[i1]==0){
    // Span is a point - rasterize it
    if (((p[0] >= 0) && (p[0] < grid_resolution[0])) &&
        ((p[1] >= 0) && (p[1] < grid_resolution[1])) &&
        ((p[2] >= 0) && (p[2] < grid_resolution[2]))) {
      AddGridValue(p[0], p[1], p[2], value);
    }
  }
  else {
    // Step along span
    int off[3] = { 0, 0, 0 };
    for (int i = 0; i <= dd[i1]; i++) {
      if (((p[0] >= 0) && (p[0] < grid_resolution[0])) &&
          ((p[1] >= 0) && (p[1] < grid_resolution[1])) &&
          ((p[2] >= 0) && (p[2] < grid_resolution[2]))) {
        AddGridValue(p[0], p[1], p[2], value);
      }
      off[i2]+=dd[i2];
      off[i3]+=dd[i3];
      p[i1]+=s[i1];
      p[i2]+=s[i2]*off[i2]/dd[i1];
      p[i3]+=s[i3]*off[i3]/dd[i1];
      off[i2]%=dd[i1];
      off[i3]%=dd[i1];
    }
  }
}



void R3Grid::
RasterizeGridTriangle(const int p1[3], const int p2[3], const int p3[3], RNScalar value)
{
  int i,j;

  // Figure out the min, max, and delta in each dimension
  int mn[3], mx[3], delta[3];
  for (i = 0; i < 3; i++) {
    mx[i]=mn[i]=p1[i];
    if (p2[i] < mn[i]) mn[i]=p2[i];
    if (p3[i] < mn[i]) mn[i]=p3[i];
    if (p2[i] > mx[i]) mx[i]=p2[i];
    if (p3[i] > mx[i]) mx[i]=p3[i];
    delta[i] = mx[i] - mn[i];
  }

  // Determine direction of maximal delta
  int d = 0;
  if ((delta[1] > delta[0]) && (delta[1] > delta[2])) d = 1;
  else if (delta[2] > delta[0]) d = 2;

  // Sort by d-value
  const int *q1,*q2,*q3;
  if(p1[d]>=p2[d] && p1[d]>=p3[d]){
    q1=p1;
    if(p2[d]>=p3[d]){
      q2=p2;
      q3=p3;
    }
    else{
      q2=p3;
      q3=p2;
    }
  }
  else if(p2[d]>=p1[d] && p2[d]>=p3[d]){
    q1=p2;
    if(p1[d]>=p3[d]){
      q2=p1;
      q3=p3;
    }
    else{
      q2=p3;
      q3=p1;
    }
  }
  else{
    q1=p3;
    if(p1[d]>=p2[d]){
      q2=p1;
      q3=p2;
    }
    else{
      q2=p2;
      q3=p1;
    }
  }

  // Init state
  int dx,dx1,dx2,ddx;
  dx=q1[d]-q2[d];
  dx1=q1[d]-q2[d];
  dx2=q1[d]-q3[d];
  ddx=dx1*dx2;

  int r1[3],r2[3];
  int last1[3],last2[3];
  int off1[3],off2[3];
  for(i=0;i<3;i++){
    last1[i]=q1[i];
    last2[i]=q1[i];

    off1[i]=0;
    off2[i]=0;

    r1[i]=(-q1[i]+q2[i])*dx2;
    r2[i]=(-q1[i]+q3[i])*dx1;
  }

  // Draw Top triangle
  if(dx==0){
    for(i=0;i<3;i++){
      last1[i]=q1[i];
      last2[i]=q2[i];
    }
  }
  else{
    for(i=0;i<dx;i++){
      RasterizeGridSpan(last1,last2,value);
      for(j=0;j<3;j++){
        off1[j]+=r1[j];
        off2[j]+=r2[j];

        last1[j]+=off1[j]/ddx;
        if(off1[j]<0){off1[j]=-((-off1[j])%ddx);}
        else{off1[j]%=ddx;}

        last2[j]+=off2[j]/ddx;
        if(off2[j]<0){off2[j]=-((-off2[j])%ddx);}
        else{off2[j]%=ddx;}
      }
    }
  }

  // Init
  dx=q2[d]-q3[d];
  dx1=last1[d]-q3[d];
  dx2=last2[d]-q3[d];
  ddx=dx1*dx2;
  if(dx==0){
    RasterizeGridSpan(q2,q3,value);
    return;
  }

  for(i=0;i<3;i++){
    off1[i]=0;
    off2[i]=0;
    r1[i]=(-last1[i]+q3[i])*dx2;
    r2[i]=(-last2[i]+q3[i])*dx1;
  }

  // Draw Bottom parrallelogram
  for(i=0;i<=dx;i++){
    RasterizeGridSpan(last1,last2,value);
    for(j=0;j<3;j++){
      off1[j]+=r1[j];
      off2[j]+=r2[j];

      last1[j]+=off1[j]/ddx;
      if(off1[j]<0){off1[j]=-((-off1[j])%ddx);}
      else{off1[j]%=ddx;}

      last2[j]+=off2[j]/ddx;
      if(off2[j]<0){off2[j]=-((-off2[j])%ddx);}
      else{off2[j]%=ddx;}
    }
  }
}



void R3Grid::
RasterizeGridSphere(const R3Point& center, RNLength radius, RNScalar value, RNBoolean solid)
{
  // Figure out the min and max in each dimension
  int mn[3], mx[3];
  for (int i = 0; i < 3; i++) {
    mx[i]= (int) (center[i]+radius);
    if (mx[i] < 0) return;
    if (mx[i] > Resolution(i)-1) mx[i] = Resolution(i)-1;
    mn[i]= (int) (center[i]-radius);
    if (mn[i] > Resolution(i)-1) return;
    if (mn[i] < 0) mn[i] = 0;
  }

  // Rasterize sphere
  RNScalar radius_squared = radius * radius;
  for (int k = mn[2]; k <= mx[2]; k++) {
    RNCoord z = (int) (k - center[2]);
    RNCoord z_squared = z*z;
    RNLength xy_radius_squared = radius_squared - z_squared;
    RNLength y = sqrt(xy_radius_squared);
    int y1 = (int) (center[1] - y + 0.5);
    int y2 = (int) (center[1] + y + 0.5);
    if (y1 < mn[1]) y1 = mn[1];
    if (y2 > mx[1]) y2 = mx[1];
    for (int j = y1; j <= y2; j++) {
      RNCoord y = (int) (j - center[1]);
      RNCoord y_squared = y*y;
      RNLength x_squared = xy_radius_squared - y_squared;
      RNLength x = sqrt(x_squared);
      int x1 = (int) (center[0] - x + 0.5);
      int x2 = (int) (center[0] + x + 0.5);
      if (x1 < mn[0]) x1 = mn[0];
      if (x2 > mx[0]) x2 = mx[0];
      if (solid || (j == y1) || (j == y2)) {
        for (int i = x1; i <= x2; i++) {
          AddGridValue(i, j, k, value);
        }
      }
      else {
        AddGridValue(x1, j, k, value);
        AddGridValue(x2, j, k, value);
      }
    }
  }
}



RNScalar R3Grid::
Dot(const R3Grid& voxels) const
{
  // Resolutions and transforms must be the same (for now)
  assert(grid_resolution[0] == voxels.grid_resolution[0]);
  assert(grid_resolution[1] == voxels.grid_resolution[1]);
  assert(grid_resolution[2] == voxels.grid_resolution[2]);

  // Compute dot product between this and grid
  RNScalar dot = 0.0;
  for (int i = 0; i < grid_size; i++) 
    dot += grid_values[i] * voxels.grid_values[i];
  return dot;
}



RNScalar R3Grid::
L1Distance(const R3Grid& voxels) const
{
  // Compute distance between this and grid
  RNScalar distance = 0.0;
  for (int i = 0; i < grid_size; i++) 
    distance += fabs(grid_values[i] - voxels.grid_values[i]);
  return distance;
}



RNScalar R3Grid::
L2DistanceSquared(const R3Grid& voxels) const
{
  // Compute distance between this and grid
  RNScalar distance_squared = 0.0;
  for (int i = 0; i < grid_size; i++) {
    RNScalar delta = (grid_values[i] - voxels.grid_values[i]);
    distance_squared += delta * delta;
  }

  // Return result
  return distance_squared;
}



void R3Grid::
SetWorldToGridTransformation(const R3Affine& affine)
{
  // Set transformations
  world_to_grid_transform = affine;
  grid_to_world_transform = affine.Inverse();
  world_to_grid_scale_factor = affine.ScaleFactor();
  grid_to_world_scale_factor = (world_to_grid_scale_factor != 0) ? 1 / world_to_grid_scale_factor : 1.0;
}



void R3Grid::
SetWorldToGridTransformation(const R3Box& world_box)
{
  // Just checking
  if (grid_size == 0) return;
  if (world_box.NDimensions() < 3) return;

  // Compute grid origin
  //R3Vector grid_diagonal(XResolution()-1, YResolution()-1, ZResolution()-1);
  R3Vector grid_diagonal(XResolution(), YResolution(), ZResolution());
  R3Vector grid_origin = 0.5 * grid_diagonal;

  // Compute world origin
  R3Vector world_diagonal(world_box.XLength(), world_box.YLength(), world_box.ZLength());
  R3Vector world_origin = world_box.Centroid().Vector();

  // Compute scale
  RNScalar scale = FLT_MAX;
  RNScalar xscale = grid_diagonal[0] / world_diagonal[0];
  if (xscale < scale) scale = xscale;
  RNScalar yscale = grid_diagonal[1] / world_diagonal[1];
  if (yscale < scale) scale = yscale;
  RNScalar zscale = grid_diagonal[2] / world_diagonal[2];
  if (zscale < scale) scale = zscale;

  // Compute world-to-grid transformation
  R3Affine affine(R3identity_affine);
  affine.Translate(grid_origin);
  affine.Scale(scale);
  affine.Translate(-world_origin);

  // Set transformations
  SetWorldToGridTransformation(affine);
}



void R3Grid::
SetWorldToGridTransformation(const R3Point& world_origin, const R3Vector& world_axis1, const R3Vector& world_axis2, RNLength world_radius)
{
  // Just checking
  if (grid_size == 0) return;

  // Compute grid origin
  R3Vector grid_diagonal(XResolution()-1, YResolution()-1, ZResolution()-1);
  R3Vector grid_origin = 0.5 * grid_diagonal;
  RNScalar grid_radius = grid_origin[0];
  if (grid_origin[1] < grid_radius) grid_radius = grid_origin[1];
  if (grid_origin[2] < grid_radius) grid_radius = grid_origin[2];

  // Compute scale
  if (RNIsZero(world_radius)) return;
  if (RNIsZero(grid_radius)) return;
  RNScalar scale = grid_radius / world_radius;

  // Compute rotation
  R3Triad world_triad(world_axis1, world_axis2);
  R3Affine rotation(world_triad.InverseMatrix());

  // Compute world-to-grid transformation
  R3Affine affine(R3identity_affine);
  affine.Translate(grid_origin);
  affine.Transform(rotation);
  affine.Scale(scale);
  affine.Translate(-(world_origin.Vector()));

  // Set transformations
  SetWorldToGridTransformation(affine);
}



RNScalar R3Grid::
WorldSpacing(RNDimension dim) const
{
  // Return distance between grid samples in dimension
  assert((0 <= dim) && (dim <= 2));
  RNScalar m0 = grid_to_world_transform.Matrix()[0][dim];
  RNScalar m1 = grid_to_world_transform.Matrix()[1][dim];
  RNScalar m2 = grid_to_world_transform.Matrix()[2][dim];
  RNScalar spacing_squared = m0*m0 + m1*m1 + m2*m2;
  return sqrt(spacing_squared);
}



R3Point R3Grid::
WorldPosition(RNCoord x, RNCoord y, RNCoord z) const
{
  // Transform point from grid coordinates to world coordinates
  R3Point world_point(x, y, z);
  world_point.Transform(grid_to_world_transform);
  return world_point;

}



R3Point R3Grid::
GridPosition(RNCoord x, RNCoord y, RNCoord z) const
{
  // Transform point from world coordinates to grid coordinates
  R3Point grid_point(x, y, z);
  grid_point.Transform(world_to_grid_transform);
  return grid_point;
}



int R3Grid::
ReadFile(const char *filename)
{
  // Parse input filename extension
  const char *extension;
  if (!(extension = strrchr(filename, '.'))) {
    printf("Filename %s has no extension (e.g., .ply)\n", filename);
    return 0;
  }

  // Read file of appropriate type
  int status = 0;
  if (!strncmp(extension, ".grd", 4)) 
    status = ReadGridFile(filename);
  else if (!strncmp(extension, ".phi", 4)) 
    status = ReadDelphiFile(filename);
  else if (!strncmp(extension, ".ins", 4)) 
    status = ReadInsightFile(filename);
  else if (!strncmp(extension, ".dx", 3)) 
    status = ReadDXFile(filename);
  else {
    RNFail("Unable to read file %s (unrecognized extension: %s)\n", filename, extension);
    return 0;
  }

  // Return status
  return status;
}



int R3Grid::
WriteFile(const char *filename) const
{

  // Parse input filename extension
  const char *extension;
  if (!(extension = strrchr(filename, '.'))) {
    printf("Filename %s has no extension (e.g., .ply)\n", filename);
    return 0;
  } 

  // Write file of appropriate type
  int status = 0;
  if (!strncmp(extension, ".grd", 4)) 
    status = WriteGridFile(filename);
  else {
    RNFail("Unable to write file %s (unrecognized extension: %s)\n", filename, extension);
    return 0;
  }

  // Return status
  return status;
}



int R3Grid::
ReadGridFile(const char *filename)
{
  // Open file
  FILE *fp = fopen(filename, "rb");
  if (!fp) {
    RNFail("Unable to open voxel file %s", filename);
    return 0;
  }

  // Read 
  int status = ReadGrid(fp);

  // Close file
  fclose(fp);

  // Return status
  return status;
}



int R3Grid::
WriteGridFile(const char *filename) const
{
  // Open file
  FILE *fp = fopen(filename, "wb");
  if (!fp) {
    RNFail("Unable to open file %s", filename);
    return 0;
  }

  // Write
  int status = WriteGrid(fp);

  // Close file
  fclose(fp);

  // Return status
  return status;
}

int R3Grid::
WriteGridDXFile(const char *filename) const
{
  // Open file
  FILE *fp = fopen(filename, "wb");
  if (!fp) {
    RNFail("Unable to open file %s", filename);
    return 0;
  }

  // Write
  int status = WriteDX(fp);

  // Close file
  fclose(fp);

  // Return status
  return status;
}

int R3Grid::
WriteGridPDBFile(const char *filename)
{
  // Open file
  FILE *fp = fopen(filename, "wb");
  if (!fp) {
    RNFail("Unable to open file %s", filename);
    return 0;
  }

  // Write
  int status = WritePDB(fp);

  // Close file
  fclose(fp);

  // Return status
  return status;
}



int R3Grid::
ReadGrid(FILE *fp)
{
  // Check file
  if (!fp) fp = stdin;

  // Read grid resolution from file
  int res[3];
  if (fread(res, sizeof(int), 3, fp) != 3) {
    RNFail("Unable to read resolution from file");
    return 0;
  }

  // Re-allocate grid values
  int new_size = res[0] * res[1] * res[2];
  if (!grid_values || (new_size > grid_size)) { 
    if (grid_values) delete [] grid_values;
    grid_values = new RNScalar [ new_size ];
    assert(grid_values);
  }

  // Update grid resolution variables
  grid_resolution[0] = res[0];
  grid_resolution[1] = res[1];
  grid_resolution[2] = res[2];
  grid_row_size = grid_resolution[0];
  grid_sheet_size = grid_row_size * grid_resolution[1];
  grid_size = grid_sheet_size * grid_resolution[2];
  if (grid_size <= 0) {
    RNFail("Invalid grid size (%d) in file", grid_size);
    return 0;
  }

  // Read world_to_grid transformation to file
  RNScalar *m = (RNScalar *) &(world_to_grid_transform.Matrix()[0][0]);
  for (int i = 0; i < 16; i++) {
    float value;
    if (fread(&value, sizeof(float), 1, fp) != 1) {
      RNFail("Unable to read transformation matrix value to file");
      return 0;
    }
    else {
      *(m++) = (RNScalar) value;
    }
  }

  // Read grid values
  RNScalar *grid_valuesp = grid_values;
  for (int i = 0; i < grid_size; i++) {
    float value;
    if (fread(&value, sizeof(float), 1, fp) != 1) {
      RNFail("Unable to read grid value %d of %d from file", i, grid_size);
      return 0;
    }
    else {
      *(grid_valuesp++) = (RNScalar) value;
    }
  }

  // Update transformation variables
  world_to_grid_scale_factor = world_to_grid_transform.ScaleFactor();
  grid_to_world_scale_factor = (world_to_grid_scale_factor != 0) ? 1 / world_to_grid_scale_factor : 1.0;
  grid_to_world_transform = world_to_grid_transform.Inverse();

  // Return number of grid values read
  return grid_size;
}



int R3Grid::
WriteGrid(FILE *fp) const
{
  // Check file
  if (!fp) fp = stdout;

  // Write grid resolution from file
  if (fwrite(&grid_resolution, sizeof(int), 3, fp) != 3) {
    RNFail("Unable to write resolution to file");
    return 0;
  }

  // Write world_to_grid transformation to file
  const RNScalar *m = &(world_to_grid_transform.Matrix()[0][0]);
  for (int i = 0; i < 16; i++) {
    float value = (float) *(m++);
    if (fwrite(&value, sizeof(float), 1, fp) != 1) {
      RNFail("Unable to write transformation matrix value to file");
      return 0;
    }
  }

  // Write grid values
  const RNScalar *grid_valuesp = grid_values;
  for (int i = 0; i < grid_size; i++) {
    float value = (float) *(grid_valuesp++);
    if (fwrite(&value, sizeof(float), 1, fp) != 1) {
      RNFail("Unable to write grid value to file");
      return 0;
    }
  }

  // Return number of grid values written
  return grid_size;
}



int R3Grid::
ReadDelphiFile(const char *filename)
{
  // Delphi writes phi files as follows.
  // Each fortran write appears as <leading_record_size> data <trailing_record_size>
  // write(14)'now starting phimap '
  // write(14)nxtlbl,toplbl
  // write(14)phimap
  // write(14)' end of phimap  '
  // write(14)scale,oldmid,igrid
 
  // Open file
  FILE *fp = fopen(filename, "rb");
  if (!fp) {
    RNFail("Unable to open delphi file %s\n", filename);
    return 0;
  }

  // Read leading record size for uplbl
  int uplbl_record_size;
  if (fread(&uplbl_record_size, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read uplbl_record_size from delphi file: %s", filename);
    return 0;
  }
  else if (uplbl_record_size != 20) {
    RNFail("Wrong uplbl record size (%d) in delphi file: %s\n", uplbl_record_size, filename);
    return 0;
  }

  // Read uplbl from file 
  char uplbl[32];
  if (fread(uplbl, sizeof(char), 20, fp) != 20) {
    RNFail("Unable to read uplbl from delphi file: %s\n", filename);
    return 0;
  }

  // Read trailing record size for uplbl
  if (fread(&uplbl_record_size, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read uplbl_record_size from delphi file: %s", filename);
    return 0;
  }
  else if (uplbl_record_size != 20) {
    RNFail("Wrong uplbl record size (%d) in delphi file: %s\n", uplbl_record_size, filename);
    return 0;
  }

  // Read leading record size for nextlbl and toplbl
  int nextlbl_record_size;
  if (fread(&nextlbl_record_size, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read nextlbl_record_size from delphi file: %s", filename);
    return 0;
  }
  else if (nextlbl_record_size != 70) {
    RNFail("Wrong nextlbl record size (%d) in delphi file: %s\n", nextlbl_record_size, filename);
    return 0;
  }

  // Read nextlbl from file 
  char nextlbl[32];
  if (fread(nextlbl, sizeof(char), 10, fp) != 10) {
    RNFail("Unable to read nextlbl from delphi file: %s\n", filename);
    return 0;
  }

  // Read toplbl from file 
  char toplbl[32];
  if (fread(toplbl, sizeof(char), 60, fp) != 60) {
    RNFail("Unable to read toplbl from delphi file: %s\n", filename);
    return 0;
  }

  // Read trailing record size for nextlbl and toplbl
  if (fread(&nextlbl_record_size, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read nextlbl_record_size from delphi file: %s", filename);
    return 0;
  }
  else if (nextlbl_record_size != 70) {
    RNFail("Wrong nextlbl record size (%d) in delphi file: %s\n", nextlbl_record_size, filename);
    return 0;
  }

  // Read leading record size for grid values
  int res = 65;
  unsigned int values_record_size;
  if (fread(&values_record_size, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read values_record_size from delphi file: %s", filename);
    return 0;
  }
  else if (values_record_size != res*res*res*sizeof(float)) {
    RNFail("Wrong values record size (%d) in delphi file: %s\n", values_record_size, filename);
    return 0;
  }

  // Re-allocate grid values
  int new_size = res * res * res;
  if (!grid_values || (new_size > grid_size)) { 
    if (grid_values) delete [] grid_values;
    grid_values = new RNScalar [ new_size ];
    assert(grid_values);
  }
  // Update grid resolution variables
  grid_resolution[0] = res;
  grid_resolution[1] = res;
  grid_resolution[2] = res;
  grid_row_size = grid_resolution[0];
  grid_sheet_size = grid_row_size * grid_resolution[1];
  grid_size = grid_sheet_size * grid_resolution[2];
  if (grid_size <= 0) {
    RNFail("Invalid grid size (%d) in file", grid_size);
    return 0;
  }

  // Read grid values
  RNScalar *grid_valuesp = grid_values;
  for (int i = 0; i < grid_size; i++) {
    float value;
    if (fread(&value, sizeof(float), 1, fp) != 1) {
      RNFail("Unable to read grid value %d of %d from file", i, grid_size);
      return 0;
    }
    else {
      *(grid_valuesp++) = (RNScalar) value;
    }
  }

  // Read trailing record size for grid values
  if (fread(&values_record_size, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read values_record_size from delphi file: %s", filename);
    return 0;
  }
  else if (values_record_size != res*res*res*sizeof(float)) {
    RNFail("Wrong values record size (%d) in delphi file: %s\n", values_record_size, filename);
    return 0;
  }

  // Read leading record size for trailer text
  int trailer_record_size;
  if (fread(&trailer_record_size, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read trailer_record_size from delphi file: %s", filename);
    return 0;
  }
  else if (trailer_record_size != 16) {
    RNFail("Wrong trailer record size (%d) in delphi file: %s\n", trailer_record_size, filename);
    return 0;
  }

  // Read trailer from file 
  char trailer[128];
  if (fread(trailer, sizeof(char), 16, fp) != 16) {
    RNFail("Unable to read trailer from delphi file: %s\n", filename);
    return 0;
  }

  // Read trailing record size for trailer text
  if (fread(&trailer_record_size, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read trailer_record_size from delphi file: %s", filename);
    return 0;
  }
  else if (trailer_record_size != 16) {
    RNFail("Wrong trailer record size (%d) in delphi file: %s\n", trailer_record_size, filename);
    return 0;
  }

  // Read leading record size for scale, center, and igrid
  int scale_record_size;
  if (fread(&scale_record_size, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read scale_record_size from delphi file: %s", filename);
    return 0;
  }
  else if (scale_record_size != 20) {
    RNFail("Wrong scale record size (%d) in delphi file: %s\n", scale_record_size, filename);
    return 0;
  }

  // Read scale
  float scale;
  if (fread(&scale, sizeof(float), 1, fp) != 1) {
    RNFail("Unable to read scale from delphi file");
    return 0;
  }

  // Read center
  float center[3];
  if (fread(center, sizeof(float), 3, fp) != 3) {
    RNFail("Unable to read center from delphi file");
    return 0;
  }

  // Read grid resolution
  int igrid;
  if (fread(&igrid, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read igrid from delphi file");
    return 0;
  }

  // Check grid resolution
  if (igrid != 65) {
    RNFail("Phi grid file has invalid igrid: %d\n", igrid);
    return 0;
  }

  // Read trailing record size for scale, center, and igrid
  if (fread(&scale_record_size, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read scale_record_size from delphi file: %s", filename);
    return 0;
  }
  else if (scale_record_size != 20) {
    RNFail("Wrong scale record size (%d) in delphi file: %s\n", scale_record_size, filename);
    return 0;
  }

  // Make sure at end of file
  int count = 0;
  char dummytrailer;
  while (fread(&dummytrailer, sizeof(char), 1, fp) == 1) count++;
  if (count != 0) {
    fprintf(stderr, "%d bytes of extra data at end of delphi file %s\n", count, filename);
    return 0;
  }

  // Determine world-grid transformation
  R3Point start_point(center[0] - 32 / scale, center[1] - 32 / scale, center[2] - 32 / scale);
  R3Point end_point(center[0] + 32 / scale, center[1] + 32 / scale, center[2] + 32 / scale);
  R3Box world_box(start_point, end_point);
  SetWorldToGridTransformation(world_box);

  // Close file
  fclose(fp);

  // Return number of grid values read
  return grid_size;
}



int R3Grid::
ReadInsightFile(const char *filename)
{
  // Delphi writes insight files as follows.
  // Each fortran write appears as <leading_record_size> data <trailing_record_size>
  // write(14)toplbl
  // write(14)ivary,nbyte,intdat,extent,extent,extent,
  //   xang,yang,zang,xstart,xend,ystart,yend,zstart,
  //   zend,intx,inty,intz
  // do 9041 k = 1,igrid
  //   do 9042 j = 1,igrid
  //     write(14)(phimap(i,j,k),i=1,igrid)

  // Open file
  FILE *fp = fopen(filename, "rb");
  if (!fp) {
    RNFail("Unable to open insight file %s\n", filename);
    return 0;
  }

  // Read leading record size for toplbl
  int toplbl_record_size;
  if (fread(&toplbl_record_size, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read toplbl_record_size from insight file: %s", filename);
    return 0;
  }
  else if (toplbl_record_size != 60) {
    RNFail("Wrong toplbl record size (%d) in insight file: %s\n", toplbl_record_size, filename);
    return 0;
  }

  // Read toplbl from file 
  char toplabel[128];
  if (fread(toplabel, sizeof(char), 60, fp) != 60) {
    RNFail("Unable to read toplabel from insight file: %s\n", filename);
    return 0;
  }

  // Read trailing record size for toplbl
  if (fread(&toplbl_record_size, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read toplbl_record_size from insight file: %s", filename);
    return 0;
  }
  else if (toplbl_record_size != 60) {
    RNFail("Wrong toplbl record size (%d) in insight file: %s\n", toplbl_record_size, filename);
    return 0;
  }

  // Read leading record size for ivary-intervals
  int ivary_record_size;
  if (fread(&ivary_record_size, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read ivary_record_size from insight file: %s", filename);
    return 0;
  }
  else if (ivary_record_size != 72) {
    RNFail("Wrong ivary record size (%d) in insight file: %s\n", ivary_record_size, filename);
    return 0;
  }

  // Read ivary (0 means x varies most quickly)
  int ivary;
  if (fread(&ivary, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read ivary from insight file");
    return 0;
  }

  // Read nbyte (# of bytes in data (per entry?)
  int nbyte;
  if (fread(&nbyte, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read nbyte from insight file");
    return 0;
  }

  // Read intdat (0 means floating point)
  int intdat;
  if (fread(&intdat, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read intdat from insight file");
    return 0;
  }

  // Read extent
  float extent[3];
  if (fread(extent, sizeof(float), 3, fp) != 3) {
    RNFail("Unable to read extent from insight file");
    return 0;
  }

  // Read unit cell angle (90,90,90)
  float unit_cell_angle[3];
  if (fread(unit_cell_angle, sizeof(float), 3, fp) != 3) {
    RNFail("Unable to read cell angle from insight file");
    return 0;
  }

  // Read start and end values
  float start[3], end[3];
  for (int i = 0; i < 3; i++) {
    if (fread(&start[i], sizeof(float), 1, fp) != 1) {
      RNFail("Unable to read start coordinate from insight file");
      return 0;
    }
    if (fread(&end[i], sizeof(float), 1, fp) != 1) {
      RNFail("Unable to read end coordinate from insight file");
      return 0;
    }
  }

  // Read grid resolution
  int intervals[3];
  if (fread(intervals, sizeof(int), 3, fp) != 3) {
    RNFail("Unable to read number of intervals from insight file");
    return 0;
  }

  // Read trailing record size for ivary-intervals
  if (fread(&ivary_record_size, sizeof(int), 1, fp) != 1) {
    RNFail("Unable to read ivary_record_size from insight file: %s", filename);
    return 0;
  }
  else if (ivary_record_size != 72) {
    RNFail("Wrong ivary record size (%d) in insight file: %s\n", ivary_record_size, filename);
    return 0;
  }

  // Determine resolution
  int res[3];
  res[0] = intervals[0]+1;
  res[1] = intervals[1]+1;
  res[2] = intervals[2]+1;

  // Check standard values
  if ((ivary != 0) || (nbyte != 4) || (intdat != 0) ||
      (unit_cell_angle[0] != 90) || (unit_cell_angle[1] != 90) || (unit_cell_angle[2] != 90)) {
    RNFail("Problem with header info in insight file: %d %d %d (should be 0 4 0 90 90 90)\n", 
           ivary, nbyte, intdat, unit_cell_angle[0], unit_cell_angle[1], unit_cell_angle[2]);
    return 0;
  }

#if 0
  printf("%d %d %d   (%g %g %g)   (%g %g %g)    (%g %g %g) (%g %g %g)    (%d %d %d)   (%d %d %d)\n",
    ivary, nbyte, intdat,
    extent[0], extent[1], extent[2],
    unit_cell_angle[0], unit_cell_angle[1], unit_cell_angle[2],
    start[0], start[1], start[2],
    end[0], end[1], end[2],
    intervals[0], intervals[1], intervals[2], 
    res[0], res[1], res[2]);
#endif

  // Re-allocate grid values
  int new_size = res[0] * res[1] * res[2];
  if (!grid_values || (new_size > grid_size)) { 
    if (grid_values) delete [] grid_values;
    grid_values = new RNScalar [ new_size ];
    assert(grid_values);
  }
  // Update grid resolution variables
  grid_resolution[0] = res[0];
  grid_resolution[1] = res[1];
  grid_resolution[2] = res[2];
  grid_row_size = grid_resolution[0];
  grid_sheet_size = grid_row_size * grid_resolution[1];
  grid_size = grid_sheet_size * grid_resolution[2];
  if (grid_size <= 0) {
    RNFail("Invalid grid size (%d) in file", grid_size);
    return 0;
  }

  // Read grid values
  RNScalar *grid_valuesp = grid_values;
  for (int k = 0; k < grid_resolution[2]; k++) {
    for (int j = 0; j < grid_resolution[1]; j++) {
      // Read leading record size for grid row
      unsigned int row_record_size;
      if (fread(&row_record_size, sizeof(int), 1, fp) != 1) {
        RNFail("Unable to read row_record_size from insight file: %s", filename);
        return 0;
      }
      else if (row_record_size != grid_resolution[0]*sizeof(float)) {
        RNFail("Wrong row record size (%d) in insight file: %s\n", row_record_size, filename);
        return 0;
      }

      // Read row
      for (int i = 0; i < grid_resolution[0]; i++) {
        float value;
        if (fread(&value, sizeof(float), 1, fp) != 1) {
          RNFail("Unable to read grid value %d of %d from file", i, grid_size);
          return 0;
        }
        else {
          *(grid_valuesp++) = (RNScalar) value;
        }
      }

      // Read trailing record size for grid row
      if (fread(&row_record_size, sizeof(int), 1, fp) != 1) {
        RNFail("Unable to read row_record_size from insight file: %s", filename);
        return 0;
      }
      else if (row_record_size != grid_resolution[0]*sizeof(float)) {
        RNFail("Wrong row record size (%d) in insight file: %s\n", row_record_size, filename);
        return 0;
      }
    }
  }

  // Make sure at end of file
  int count = 0;
  char dummytrailer;
  while (fread(&dummytrailer, sizeof(char), 1, fp) == 1) count++;
  if (count != 0) {
    fprintf(stderr, "%d bytes of extra data at end of delphi file %s\n", count, filename);
    return 0;
  }

  // Determine world-grid transformation
  R3Point start_point(start[0]*extent[0], start[1]*extent[1], start[2]*extent[2]);
  R3Point end_point(end[0]*extent[0], end[1]*extent[1], end[2]*extent[2]);
  R3Box world_box(start_point, end_point);
  SetWorldToGridTransformation(world_box);

  // Close file
  fclose(fp);

  // Return number of grid values read
  return grid_size;
}



int R3Grid::
ReadDXFile(const char *filename)
{
  // Open file
  FILE *fp = fopen(filename, "r");
  if (!fp) {
    fprintf(stderr, "Unable to open file %s\n", filename);
    return 0;
  }

  // Skip comments at top
  char buffer[1024];
  do {
    if (!fgets(buffer, 1024, fp)) {
       fprintf(stderr, "Error reading comments in %s\n", filename);
       return 0;
    }
  } while (buffer[0] == '#');

  // Read dimensions
  int res[3];
  if (sscanf(buffer, "object 1 class gridpositions counts %d %d %d", &res[0], &res[1], &res[2]) != 3) {
    fprintf(stderr, "Unable to read dimensions in %s\n", filename);
    return 0;
  }

  // Read origin
  if (!fgets(buffer, 1023, fp)) {
    fprintf(stderr, "Unable to read origin in %s\n", filename);
    return 0;
  }

  // Parse origin
  R3Point origin;
  if (sscanf(buffer, "origin %lf %lf %lf", &origin[0], &origin[1], &origin[2]) != 3) {
    fprintf(stderr, "Unable to parse origin in %s\n", filename);
    return 0;
  }

  // Read delta for each dimension
  R3Vector delta[3];
  for (RNDimension dim = RN_X; dim <= RN_Z; dim++) {
    // Read delta
    if (!fgets(buffer, 1023, fp)) {
      fprintf(stderr, "Unable to read delta in %s\n", filename);
      return 0;
    }

    // Parse delta
    if (sscanf(buffer, "delta %lf %lf %lf", &delta[dim][0], &delta[dim][1], &delta[dim][2]) != 3) {
      fprintf(stderr, "Unable to parse delta in %s\n", filename);
      return 0;
    }
  }

  // Read object 2 line
  if (!fgets(buffer, 1023, fp)) {
    fprintf(stderr, "Unable to read object 2 line in %s\n", filename);
    return 0;
  }

  // Read object 3 line
  if (!fgets(buffer, 1023, fp)) {
    fprintf(stderr, "Unable to read object 3 line in %s\n", filename);
    return 0;
  }

  // Re-allocate grid values
  int new_size = res[0] * res[1] * res[2];
  if (!grid_values || (new_size > grid_size)) { 
    if (grid_values) delete [] grid_values;
    grid_values = new RNScalar [ new_size ];
    assert(grid_values);
  }

  // Update grid resolution variables
  grid_resolution[0] = res[0];
  grid_resolution[1] = res[1];
  grid_resolution[2] = res[2];
  grid_row_size = grid_resolution[0];
  grid_sheet_size = grid_row_size * grid_resolution[1];
  grid_size = grid_sheet_size * grid_resolution[2];
  if (grid_size <= 0) {
    RNFail("Invalid grid size (%d) in file", grid_size);
    return 0;
  }

  // Read data
  RNScalar *grid_valuesp  = grid_values;
  for (int i = 0; i < grid_size; i++) {
    if (fscanf(fp, "%lf", grid_valuesp++) != 1) {
      fprintf(stderr, "Error reading grid values from %s\n", filename);
      return 0;
    }
  }

  // Determine world-grid transformation
  R3Vector extent(delta[0][0] * (res[0]-1), delta[1][1] * (res[1]-1), delta[2][2] * (res[2]-1));
  R3Box world_box(origin, origin + extent);
  SetWorldToGridTransformation(world_box);

  // Return success
  return 1;
}



int R3Grid::
WriteText(FILE *fp) const
{
  // Check file
  if (!fp) fp = stdout;

  // Print values
  for (int k = 0; k < ZResolution(); k++) {
    for (int j = 0; j < YResolution(); j++) {
      for (int i = 0; i < XResolution(); i++) {
        fprintf(fp, "%g ", GridValue(i, j, k));
      }
      fprintf(fp, "\n");
    }
    fprintf(fp, "\n");
  }

  // Return number of grid values written
  return grid_size;
}


//ConnectedComponents(RNScalar isolevel, int max_components, int *seeds, int *sizes, int *grid_components)
int R3Grid::
WritePDB(FILE *fp)
{
  // Based on ConnectedComponents so that we can print the pocket assignments.
  // Not pretty and kind of a hack, but it works.

  // Check file
  if (!fp) fp = stdout;

  int isolevel = 1.0E-20;

  // Allocate array of component identifiers
  int *components = new int [ grid_size ];
  assert(components);

  // Initialize array of components
  for (int i = 0; i < grid_size; i++) 
    components[i] = -1;

  // Find connected components
  int ncomponents = 0;
  int next_seed = 0;
  int n = 0;
  while (TRUE){
    // Find unmarked seed
    int seed = next_seed;
    while (seed < grid_size) { 
      if ((components[seed] < 0) && (grid_values[seed] > isolevel)) break;
      seed++;
    }

    // Check if found a seed
    if (seed >= grid_size) break;

    // Flood fill marking all grid entries 6-connected to seed
    int x, y, z, neighbor;
    int size = 0;
    RNArray<RNScalar *> stack;
    stack.Insert(&grid_values[seed]);
    components[seed] = ncomponents;
    while (!stack.IsEmpty()) {
      // Pop top of stack
      RNScalar *c = stack.Tail();
      stack.RemoveTail();

      // Add grid entry to component
      int index = c - grid_values;
      assert(index >= 0);
      assert(index < grid_size);
      assert(components[index] == ncomponents);
      assert(grid_values[index] > isolevel);

      IndexToIndices(index, x, y, z);

      R3Point world_position = WorldPosition(x, y, z);	  
      fprintf(fp, "%-6s%5d %-4s%c%3s %1s%4d%c   %8.3f%8.3f%8.3f%6.2f%6.2f          %2s\n", "HETATM", n, "H", ' ', "POC", "1", ncomponents, ' ', world_position.X(), world_position.Y(), world_position.Z(), grid_values[index], grid_values[index], "");
      n += 1;

      // Add neighbors to stack
      if (x > 0) {
        IndicesToIndex(x-1, y, z, neighbor);
        if ((components[neighbor] < 0) && (grid_values[neighbor] > isolevel)) {
          components[neighbor] = ncomponents;
          stack.Insert(&grid_values[neighbor]);
        }
      }
      if (x < XResolution()-1) {
        IndicesToIndex(x+1, y, z, neighbor);
        if ((components[neighbor] < 0) && (grid_values[neighbor] > isolevel)) {
          components[neighbor] = ncomponents;
          stack.Insert(&grid_values[neighbor]);
        }
      }
      if (y > 0) {
        IndicesToIndex(x, y-1, z, neighbor);
        if ((components[neighbor] < 0) && (grid_values[neighbor] > isolevel)) {
          components[neighbor] = ncomponents;
          stack.Insert(&grid_values[neighbor]);
        }
      }
      if (y < YResolution()-1) {
        IndicesToIndex(x, y+1, z, neighbor);
        if ((components[neighbor] < 0) && (grid_values[neighbor] > isolevel)) { 
          components[neighbor] = ncomponents;
          stack.Insert(&grid_values[neighbor]);
        }
      }
      if (z > 0) {
        IndicesToIndex(x, y, z-1, neighbor);
        if ((components[neighbor] < 0) && (grid_values[neighbor] > isolevel)) {
          components[neighbor] = ncomponents;
          stack.Insert(&grid_values[neighbor]);
        }
      }
      if (z < ZResolution()-1) {
        IndicesToIndex(x, y, z+1, neighbor);
        if ((components[neighbor] < 0) && (grid_values[neighbor] > isolevel)) {
          components[neighbor] = ncomponents;
          stack.Insert(&grid_values[neighbor]);
        }
      }

      // Increment component_size
      size++;
    }

    // Increment the number of components
    ncomponents++;
  }

  // Return number of connected components found
  return ncomponents;
}



int R3Grid::
WritePDBOld(FILE *fp) const
{
  // Check file
  if (!fp) fp = stdout;

  // Print values
  int n = 0;
  for (int k = 0; k < ZResolution(); k++) {
    for (int j = 0; j < YResolution(); j++) {
      for (int i = 0; i < XResolution(); i++) {
	if (GridValue(i,j, k) > 0){
	  R3Point world_position = WorldPosition(i, j, k);	  
	  fprintf(fp, "%-6s%5d %-4s%c%3s %1s%4d%c   %8.3f%8.3f%8.3f%6.2f%6.2f          %2s\n", "HETATM", n, "H", ' ', "POC", "1", n, ' ', world_position.X(), world_position.Y(), world_position.Z(), GridValue(i, j, k), GridValue(i, j, k), "");
	  n++;
		  /*		   atom->Serial(), atom->Name(), atom->AlternateLocation(),
		   residue->Name(), chain->Name(), residue->Sequence(), residue->InsertionCode(), 
		   atom->Position().X(), atom->Position().Y(), atom->Position().Z(),
		   atom->Occupancy(), atom->TempFactor(), (element) ? element->Name() : "");*/
	}
      }
    }
  }

  // Return number of grid values written
  return grid_size;
}



int R3Grid::
WriteDX(FILE *fp) const
{
  // Check file
  if (!fp) fp = stdout;

  // Print header lines
  const R3Box bbox = WorldBox();
  const RNScalar delta = GridToWorldScaleFactor();
  fprintf(fp, "object 1 class gridpositions counts %d %d %d\n", XResolution(), YResolution(), ZResolution());
  fprintf(fp, "origin %lf %lf %lf\n", bbox[0][0], bbox[0][1], bbox[0][2]);
  fprintf(fp, "delta %lf 0 0\ndelta 0 %lf 0\ndelta 0 0 %lf\n", delta, delta, delta);
  fprintf(fp, "\nobject 2 class gridconnections counts %d %d %d\n", XResolution(), YResolution(), ZResolution());
  fprintf(fp, "\nobject 3 class array type double rank 0 items %d data follows\n", (XResolution() * YResolution() * ZResolution()));

  // Print values
  for (int i = 0; i < XResolution(); i++) {
    for (int j = 0; j < YResolution(); j++) {
      for (int k = 0; k < ZResolution(); k++) {
	fprintf(fp, "%lf\n", GridValue(i, j, k));
      }
    }
  }

  fprintf(fp, "\nend\n");

  // Return number of grid values written
  return grid_size;
}





////////////////////////////////////////////////////////////////////////
// Marching cubes code
// From http://astronomy.swin.edu.au/~pbourke/modelling/polygonise/
////////////////////////////////////////////////////////////////////////

static int 
MarchingCubes(R3Point corner_points[8], RNScalar corner_levels[8], RNScalar isolevel, R3Point *points)
{
  // Given a grid cell (defined by its corner points and corner_levels)
  // and an isolevel, calculate the triangular facets required to 
  // represent the isosurface through the cell.
  // Return the number of points added to the array "points", which
  // will be loaded up with the vertices for at most 5 triangular facets.
  // 0 will be returned if the grid cell is either totally above
  // or totally below the isolevel.

  // Initialize marching cubes edge table
  static int edgeTable[256]={
    0x0  , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
    0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
    0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
    0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
    0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c,
    0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
    0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac,
    0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
    0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c,
    0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
    0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc,
    0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
    0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c,
    0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
    0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc ,
    0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
    0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
    0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
    0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
    0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
    0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
    0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
    0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
    0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
    0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
    0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
    0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
    0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
    0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
    0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
    0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
    0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0   
  };

  // Initialize marching cubes triangle table
  static int triTable[256][16] =
    {{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
     {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
     {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
     {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
     {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
     {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
     {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
     {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
     {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
     {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
     {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
     {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
     {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
     {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
     {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
     {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
     {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
     {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
     {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
     {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
     {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
     {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
     {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
     {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
     {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
     {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
     {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
     {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
     {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
     {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
     {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
     {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
     {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
     {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
     {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
     {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
     {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
     {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
     {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
     {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
     {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
     {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
     {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
     {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
     {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
     {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
     {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
     {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
     {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
     {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
     {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
     {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
     {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
     {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
     {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
     {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
     {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
     {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
     {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
     {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
     {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
     {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
     {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
     {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
     {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
     {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
     {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
     {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
     {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
     {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
     {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
     {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
     {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
     {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
     {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
     {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
     {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
     {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
     {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
     {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
     {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
     {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
     {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
     {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
     {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
     {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
     {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
     {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
     {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
     {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
     {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
     {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
     {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
     {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
     {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
     {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
     {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
     {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
     {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
     {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
     {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
     {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
     {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
     {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
     {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
     {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
     {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
     {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
     {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
     {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
     {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
     {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
     {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
     {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
     {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
     {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
     {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
     {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
     {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
     {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
     {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
     {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
     {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
     {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
     {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
     {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
     {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
     {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
     {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
     {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
     {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
     {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
     {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
     {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
     {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
     {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
     {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
     {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
     {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
     {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
     {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
     {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
     {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
     {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
     {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
     {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
     {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
     {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
     {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
     {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
     {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
     {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
     {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
     {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
     {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
     {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
     {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
     {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
     {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
     {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
     {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
     {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
     {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
     {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
     {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
     {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
     {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
     {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
     {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
     {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
     {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
     {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
     {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
     {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
     {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
     {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
     {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
     {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
     {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
     {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
     {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
     {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
     {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
     {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
     {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
     {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
     {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
     {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
     {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}};

  /*
    Determine the index into the edge table which
    tells us which vertices are inside of the surface
  */
  int cubeindex = 0;
  if (corner_levels[0] < isolevel) cubeindex |= 1;
  if (corner_levels[1] < isolevel) cubeindex |= 2;
  if (corner_levels[2] < isolevel) cubeindex |= 4;
  if (corner_levels[3] < isolevel) cubeindex |= 8;
  if (corner_levels[4] < isolevel) cubeindex |= 16;
  if (corner_levels[5] < isolevel) cubeindex |= 32;
  if (corner_levels[6] < isolevel) cubeindex |= 64;
  if (corner_levels[7] < isolevel) cubeindex |= 128;

  /* Cube is entirely in/out of the surface */
  if (edgeTable[cubeindex] == 0)
    return(0);

  /* Find the vertices where the surface intersects the cube */
  R3Point vertlist[12];
  if (edgeTable[cubeindex] & 1)
    vertlist[0] =
      R3Interpolate(corner_points[0],corner_points[1],corner_levels[0],corner_levels[1],isolevel);
  if (edgeTable[cubeindex] & 2)
    vertlist[1] =
      R3Interpolate(corner_points[1],corner_points[2],corner_levels[1],corner_levels[2],isolevel);
  if (edgeTable[cubeindex] & 4)
    vertlist[2] =
      R3Interpolate(corner_points[2],corner_points[3],corner_levels[2],corner_levels[3],isolevel);
  if (edgeTable[cubeindex] & 8)
    vertlist[3] =
      R3Interpolate(corner_points[3],corner_points[0],corner_levels[3],corner_levels[0],isolevel);
  if (edgeTable[cubeindex] & 16)
    vertlist[4] =
      R3Interpolate(corner_points[4],corner_points[5],corner_levels[4],corner_levels[5],isolevel);
  if (edgeTable[cubeindex] & 32)
    vertlist[5] =
      R3Interpolate(corner_points[5],corner_points[6],corner_levels[5],corner_levels[6],isolevel);
  if (edgeTable[cubeindex] & 64)
    vertlist[6] =
      R3Interpolate(corner_points[6],corner_points[7],corner_levels[6],corner_levels[7],isolevel);
  if (edgeTable[cubeindex] & 128)
    vertlist[7] =
      R3Interpolate(corner_points[7],corner_points[4],corner_levels[7],corner_levels[4],isolevel);
  if (edgeTable[cubeindex] & 256)
    vertlist[8] =
      R3Interpolate(corner_points[0],corner_points[4],corner_levels[0],corner_levels[4],isolevel);
  if (edgeTable[cubeindex] & 512)
    vertlist[9] =
      R3Interpolate(corner_points[1],corner_points[5],corner_levels[1],corner_levels[5],isolevel);
  if (edgeTable[cubeindex] & 1024)
    vertlist[10] =
      R3Interpolate(corner_points[2],corner_points[6],corner_levels[2],corner_levels[6],isolevel);
  if (edgeTable[cubeindex] & 2048)
    vertlist[11] =
      R3Interpolate(corner_points[3],corner_points[7],corner_levels[3],corner_levels[7],isolevel);

  /* Create the triangle */
  int npoints = 0;
  for (int i = 0; triTable[cubeindex][i] != -1; i+=3) {
    points[npoints++] = vertlist[triTable[cubeindex][i  ]];
    points[npoints++] = vertlist[triTable[cubeindex][i+1]];
    points[npoints++] = vertlist[triTable[cubeindex][i+2]];
  }

  // Just checking
  assert((npoints % 3) == 0);
  assert(npoints <= 3*5);

  // Return number of points
  return npoints;
}



int R3Grid::
ConnectedComponents(RNScalar isolevel, int max_components, int *seeds, int *sizes, int *grid_components)
{
  // Allocate array of component identifiers
  int *components = grid_components;
  if (!grid_components) {
    components = new int [ grid_size ];
    assert(components);
  }

  // Initialize array of components
  for (int i = 0; i < grid_size; i++) 
    components[i] = -1;

  // Find connected components
  int ncomponents = 0;
  int next_seed = 0;
  while (TRUE){
    // Find unmarked seed
    int seed = next_seed;
    while (seed < grid_size) { 
      if ((components[seed] < 0) && (grid_values[seed] > isolevel)) break;
      seed++;
    }

    // Check if found a seed
    if (seed >= grid_size) break;

    // Flood fill marking all grid entries 6-connected to seed
    int x, y, z, neighbor;
    int size = 0;
    RNArray<RNScalar *> stack;
    stack.Insert(&grid_values[seed]);
    components[seed] = ncomponents;
    while (!stack.IsEmpty()) {
      // Pop top of stack
      RNScalar *c = stack.Tail();
      stack.RemoveTail();

      // Add grid entry to component
      int index = c - grid_values;
      assert(index >= 0);
      assert(index < grid_size);
      assert(components[index] == ncomponents);
      assert(grid_values[index] > isolevel);

      // Add neighbors to stack
      IndexToIndices(index, x, y, z);
      if (x > 0) {
        IndicesToIndex(x-1, y, z, neighbor);
        if ((components[neighbor] < 0) && (grid_values[neighbor] > isolevel)) {
          components[neighbor] = ncomponents;
          stack.Insert(&grid_values[neighbor]);
        }
      }
      if (x < XResolution()-1) {
        IndicesToIndex(x+1, y, z, neighbor);
        if ((components[neighbor] < 0) && (grid_values[neighbor] > isolevel)) {
          components[neighbor] = ncomponents;
          stack.Insert(&grid_values[neighbor]);
        }
      }
      if (y > 0) {
        IndicesToIndex(x, y-1, z, neighbor);
        if ((components[neighbor] < 0) && (grid_values[neighbor] > isolevel)) {
          components[neighbor] = ncomponents;
          stack.Insert(&grid_values[neighbor]);
        }
      }
      if (y < YResolution()-1) {
        IndicesToIndex(x, y+1, z, neighbor);
        if ((components[neighbor] < 0) && (grid_values[neighbor] > isolevel)) { 
          components[neighbor] = ncomponents;
          stack.Insert(&grid_values[neighbor]);
        }
      }
      if (z > 0) {
        IndicesToIndex(x, y, z-1, neighbor);
        if ((components[neighbor] < 0) && (grid_values[neighbor] > isolevel)) {
          components[neighbor] = ncomponents;
          stack.Insert(&grid_values[neighbor]);
        }
      }
      if (z < ZResolution()-1) {
        IndicesToIndex(x, y, z+1, neighbor);
        if ((components[neighbor] < 0) && (grid_values[neighbor] > isolevel)) {
          components[neighbor] = ncomponents;
          stack.Insert(&grid_values[neighbor]);
        }
      }

      // Increment component_size
      size++;
    }

    // Update output variables
    if (ncomponents < max_components) {
      if (seeds) seeds[ncomponents] = seed;
      if (sizes) sizes[ncomponents] = size;
    }

    // Increment the number of components
    ncomponents++;
  }


  // Delete components
  if (!grid_components) delete [] components;

  // Return number of connected components found
  return ncomponents;
}



int R3Grid::
GenerateIsoSurface(RNScalar isolevel, R3Point *points, int max_points) const
{
  // Initialize pointer into array of points
  R3Point *pointsp = points;

  // Fill array of points with triplets forming triangles of isosurface
  int npoints = 0;
  R3Point corner_points[8];
  RNScalar corner_levels[8];
  for (int k = 0; k < ZResolution()-1; k++) {
    corner_points[0][2] = corner_points[1][2] = corner_points[4][2] = corner_points[5][2] = k;
    corner_points[2][2] = corner_points[3][2] = corner_points[6][2] = corner_points[7][2] = k+1;
    for (int j = 0; j < YResolution()-1; j++) {
      corner_points[0][1] = corner_points[1][1] = corner_points[2][1] = corner_points[3][1] = j;
      corner_points[4][1] = corner_points[5][1] = corner_points[6][1] = corner_points[7][1] = j+1;
      for (int i = 0; i < XResolution()-1; i++) {
        corner_points[0][0] = corner_points[3][0] = corner_points[4][0] = corner_points[7][0] = i;
        corner_points[1][0] = corner_points[2][0] = corner_points[5][0] = corner_points[6][0] = i+1;

        // Check if points array is full (five tris is max for any cell)
        if (npoints >= max_points - 3*5) return npoints;

        // Fill in grid values
        corner_levels[0] = GridValue(i,j,k);
        corner_levels[1] = GridValue(i+1,j,k);
        corner_levels[2] = GridValue(i+1,j,k+1);
        corner_levels[3] = GridValue(i,j,k+1);
        corner_levels[4] = GridValue(i,j+1,k);
        corner_levels[5] = GridValue(i+1,j+1,k);
        corner_levels[6] = GridValue(i+1,j+1,k+1);
        corner_levels[7] = GridValue(i,j+1,k+1);

        // Create triangles
        int n = MarchingCubes(corner_points, corner_levels, isolevel, pointsp);
        pointsp += n;
        npoints += n;
      }
    }
  }

  // Just checking
  assert((npoints % 3) == 0);
  assert(npoints < max_points);

  // Return number of points
  return npoints;
}



void R3Grid::
DrawIsoSurface(RNScalar isolevel) const
{
  // Allocate storage for isosurface
  static const R3Grid *isosurface_grid = NULL;
  static RNScalar isosurface_level = -12345679;
  static const int isosurface_max_points = 8*1024*1024;
  static R3Point *isosurface_points = NULL;
  static int isosurface_npoints = 0;

  // Allocate points for isosurface
  if (!isosurface_points) { 
    isosurface_points = new R3Point [isosurface_max_points];
    assert(isosurface_points);
  }

  // Generate isosurface
  if ((this != isosurface_grid) || (isosurface_level != isolevel)) {
    isosurface_npoints = GenerateIsoSurface(isolevel, isosurface_points, isosurface_max_points);
    isosurface_grid = this;
    isosurface_level = isolevel;
  }

  // Draw isosurface
  R3Point *pointsp = isosurface_points;
  for (int i = 0; i < isosurface_npoints; i += 3) {
    glBegin(GL_POLYGON);
    R3LoadPoint(*(pointsp++));
    R3LoadPoint(*(pointsp++));
    R3LoadPoint(*(pointsp++));
    glEnd();
  }
}



void R3Grid::
DrawSlice(RNDimension dim, int coord) const
{
  // Check coordinates
  if ((dim < 0) || (dim > 2)) return;
  if ((coord < 0) || (coord >= Resolution(dim))) return;

  // Get useful variables
  RNDimension dim1 = (dim+1) % 3;
  RNDimension dim2 = (dim+2) % 3;

  // Determine width and height of texture (must be power of 2)
  int width = 16; while (width < Resolution(dim1)) width *= 2;
  int height = 16; while (height < Resolution(dim2)) height *= 2;
  if (width * height > 1024 * 1024) return;

  // Determine texture coordinates
  RNScalar tx = (RNScalar) Resolution(dim1) / (RNScalar) width;
  RNScalar ty = (RNScalar) Resolution(dim2) / (RNScalar) height;
  RNScalar zero = 0;

  // Define slice texture
  static const R3Grid *previous_grid[3] = { NULL, NULL, NULL };
  static int previous_coord[3] = { -1, -1, -1 };
  static GLuint texture_id[3] = { 0, 0, 0 };
  if ((this != previous_grid[dim]) || (coord != previous_coord[dim])) {
    if (texture_id[dim] != 0) glDeleteTextures(1, &texture_id[dim]);
    glGenTextures(1, &texture_id[dim]);
    glBindTexture(GL_TEXTURE_2D, texture_id[dim]);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    RNScalar scale = Maximum();
    static GLfloat pixels[1024 * 1024];
    int p[3]; p[dim] = coord;
    for (p[dim2] = 0; p[dim2] < Resolution(dim2); p[dim2]++) {
      GLfloat *pixelsp = pixels + p[dim2] * height;
      for (p[dim1] = 0; p[dim1] < Resolution(dim1); p[dim1]++) {
        RNScalar value = GridValue(p[0], p[1], p[2]);
        *(pixelsp++) = value / scale;
      }
    }  
    glTexImage2D(GL_TEXTURE_2D, 0, 1, width, height, 0, GL_LUMINANCE, GL_FLOAT, pixels);
    previous_coord[dim] = coord;
    previous_grid[dim] = this;
  }

  // Set OpenGL modes
  assert(texture_id[dim]);
  glBindTexture(GL_TEXTURE_2D, texture_id[dim]);
  glEnable(GL_TEXTURE_2D);

  // Create quad
  R3Point p0, p1, p2, p3;
  p0[dim] = coord; p0[dim1] = 0;                  p0[dim2] = 0;
  p1[dim] = coord; p1[dim1] = Resolution(dim1)-1; p1[dim2] = 0;
  p2[dim] = coord; p2[dim1] = Resolution(dim1)-1; p2[dim2] = Resolution(dim2)-1;
  p3[dim] = coord; p3[dim1] = 0;                  p3[dim2] = Resolution(dim2)-1;

  // Draw quad 
  R3BeginPolygon();
  R3LoadTextureCoords(zero, zero);
  R3LoadPoint(p0[0], p0[1], p0[2]);
  R3LoadTextureCoords(tx, zero);
  R3LoadPoint(p1[0], p1[1], p1[2]);
  R3LoadTextureCoords(tx, ty);
  R3LoadPoint(p2[0], p2[1], p2[2]);
  R3LoadTextureCoords(zero, ty);
  R3LoadPoint(p3[0], p3[1], p3[2]);
  R3EndPolygon();

  // Reset OpenGL modes
  glDisable(GL_TEXTURE_2D);
}
