view libsrc2/dimension.c @ 1844:2b0402e87958

Documentation fixes, world_indices implementation, remove test harness.
author bert <bert>
date Mon, 21 Jun 2004 18:47:09 +0000
parents f512ca3023db
children 6e68ccd11169
line wrap: on
line source

/** \file dimension.c
 * \brief MINC 2.0 Dimension Functions
 * \author Leila Baghdadi
 * 
 * Functions to create, destroy, and manipulate MINC dimension objects.
 ************************************************************************/
#include <stdlib.h>
#include <hdf5.h>
#include "minc2.h"
#include "minc2_private.h"
#include "minc.h"

/**
 * \defgroup mi2Dim MINC 2.0 Dimension Functions
 */


/*! Figure out whether a dimension is associated with a volume.
 * \ingroup mi2Dim
 */

int 
miget_volume_from_dimension(midimhandle_t dimension, mihandle_t *volume)
{
    if (dimension == NULL) {
        return (MI_ERROR);
    }  

    if (dimension->volume_handle != NULL) {
        *volume = dimension->volume_handle;
    }
    else {
        return (MI_ERROR);
    }

    return (MI_NOERROR);
}

/*! Create a copy of a given dimension.
 * \ingroup mi2Dim
 */

int 
micopy_dimension(midimhandle_t dim_ptr, midimhandle_t *new_dim_ptr)
{
  int i;
  midimhandle_t handle;
  
  if (dim_ptr == NULL) {
    return (MI_ERROR);
  }

  /* Allocate storage for the structure
   */
  handle = (midimhandle_t)malloc(sizeof(struct midimension));
  if (handle == NULL) {
    return (MI_ERROR);
  }
  handle->attr = dim_ptr->attr;
  handle->class = dim_ptr->class;
  /* Copy direction cosines */
  handle->direction_cosines[MI2_X] = dim_ptr->direction_cosines[0];
  handle->direction_cosines[MI2_Y] = dim_ptr->direction_cosines[1];
  handle->direction_cosines[MI2_Z] = dim_ptr->direction_cosines[2];
 
  switch (dim_ptr->flipping_order) {
  case MI_FILE_ORDER:
    handle->flipping_order = MI_FILE_ORDER;
    break;
  case MI_COUNTER_FILE_ORDER:
    handle->flipping_order = MI_COUNTER_FILE_ORDER;
    break;
  default:
    return (MI_ERROR);
  }
  
  handle->name =strdup(dim_ptr->name);
  handle->length = dim_ptr->length;
  if (dim_ptr->offsets != NULL) {
    handle->offsets = (double *) malloc(dim_ptr->length*sizeof(double));
    if (handle->offsets == NULL) {
      return (MI_ERROR);
    }
    for (i=0; i < dim_ptr->length; i++) {
      handle->offsets[i] = dim_ptr->offsets[i];
    }
  }
  else {
    handle->offsets = NULL;
  }
  handle->start = dim_ptr->start;
  handle->step = dim_ptr->step;
  if (dim_ptr->units != NULL) {
    handle->units = strdup(dim_ptr->units);
  }
  else {
    handle->units = NULL;
  }
  handle->width = dim_ptr->width;
  if (dim_ptr->widths != NULL) {
    handle->widths = (double *) malloc(dim_ptr->length*sizeof(double));
    if (handle->widths == NULL) {
      return (MI_ERROR);
    }
    for (i=0; i < dim_ptr->length; i++) {
      handle->widths[i] = dim_ptr->widths[i];
    }
  }
  else {
    handle->widths = NULL;
  }
  handle->volume_handle = dim_ptr->volume_handle;

  *new_dim_ptr = handle;
  
  return (MI_NOERROR);
}

/*! Define a new dimension in a MINC volume.
 * \ingroup mi2Dim
 */

int 
micreate_dimension(const char *name, midimclass_t class, midimattr_t attr, 
		   unsigned long length, midimhandle_t *new_dim_ptr)
{  
  
  midimhandle_t handle;
  int i;
  /* Allocate space for the new dimension
   */
  handle = (midimhandle_t)malloc(sizeof(struct midimension));
  if (handle == NULL) {
    return (MI_ERROR);
  }


  /* Duplicate the dimension name
   */
  handle->name = strdup(name);
  /* Set the dimension comment to NULL unless otherwise
   */
  handle->comments = NULL;  
  switch (class) {
  case MI_DIMCLASS_SPATIAL:
    handle->class  = MI_DIMCLASS_SPATIAL;
    if (strcmp(name, "xspace") == 0) {
      handle->direction_cosines[MI2_X] = 1.0;
      handle->direction_cosines[MI2_Y] = 0.0;
      handle->direction_cosines[MI2_Z] = 0.0;
      handle->comments = strdup("X increases from patient left to right");
    }
    else if (strcmp(name, "yspace") == 0) {
      handle->direction_cosines[MI2_X] = 0.0;
      handle->direction_cosines[MI2_Y] = 1.0;
      handle->direction_cosines[MI2_Z] = 0.0;
      handle->comments = strdup("Y increases from patient posterior to anterior");
    }
    else if (strcmp(name, "zspace") == 0) {
      handle->direction_cosines[MI2_X] = 0.0;
      handle->direction_cosines[MI2_Y] = 0.0;
      handle->direction_cosines[MI2_Z] = 1.0;
      handle->comments = strdup("Z increases from patient inferior to superior");
    }
    else {
      handle->direction_cosines[MI2_X] = 1.0;
      handle->direction_cosines[MI2_Y] = 0.0;
      handle->direction_cosines[MI2_Z] = 0.0;
      handle->comments = NULL;
    }
    break;
  case MI_DIMCLASS_TIME:
    handle->class  = MI_DIMCLASS_TIME;
    break;
  case MI_DIMCLASS_SFREQUENCY:
    handle->class  = MI_DIMCLASS_SFREQUENCY;
    if (strcmp(name, "xfrequency") == 0) {
      handle->direction_cosines[MI2_X] = 1.0;
      handle->direction_cosines[MI2_Y] = 0.0;
      handle->direction_cosines[MI2_Z] = 0.0;
    }
    else if (strcmp(name, "yfrequency") == 0) {
      handle->direction_cosines[MI2_X] = 0.0;
      handle->direction_cosines[MI2_Y] = 1.0;
      handle->direction_cosines[MI2_Z] = 0.0;
    }
    else if (strcmp(name, "zfrequency") == 0) {
      handle->direction_cosines[MI2_X] = 0.0;
      handle->direction_cosines[MI2_Y] = 0.0;
      handle->direction_cosines[MI2_Z] = 1.0;
    }
    else {
      handle->direction_cosines[MI2_X] = 1.0;
      handle->direction_cosines[MI2_Y] = 0.0;
      handle->direction_cosines[MI2_Z] = 0.0;
    }
    break;
  case MI_DIMCLASS_TFREQUENCY:
    handle->class  = MI_DIMCLASS_TFREQUENCY;
    break;
  case MI_DIMCLASS_USER:
    handle->class  = MI_DIMCLASS_USER;
    break;
  case MI_DIMCLASS_RECORD:
    handle->class  = MI_DIMCLASS_RECORD;
    break;
  case MI_DIMCLASS_ANY:
  default:
    return (MI_ERROR);
  }

  handle->offsets = NULL;
  handle->attr = attr;
  if (attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED) {
    handle->widths = (double *) malloc(length *sizeof(double));
    for (i=0; i< length; i++) {
      
      handle->widths[i] = 1.0;
    }
  }
  else {
    handle->widths = NULL;
  }
  handle->start = 0.0;
  handle->step = 1.0;
  handle->width = 1.0;

  handle->flipping_order = MI_FILE_ORDER;
  if (class != MI_DIMCLASS_SPATIAL && class != MI_DIMCLASS_SFREQUENCY ) {
    handle->direction_cosines[MI2_X] = 1.0;
    handle->direction_cosines[MI2_Y] = 0.0;
    handle->direction_cosines[MI2_Z] = 0.0;
  }
  handle->length = length;
  handle->units = strdup("mm");
  /* volume_handle is the only NULL value once the dimension is created.
   */
  handle->volume_handle = NULL;

  *new_dim_ptr = handle;
  
  return (MI_NOERROR);

}

/*! Delete the dimension definition.
 * Note: The original document stated that a dimension has to be
 * associated with a given volume before it can be deleted. This
 * feature was erased from the document and not considered here.
 * \ingroup mi2Dim
 */
int 
mifree_dimension_handle(midimhandle_t dim_ptr)
{
  
  if (dim_ptr == NULL) {
    return (MI_ERROR);
  }
  if (dim_ptr->name != NULL) {
      free(dim_ptr->name);
  }
  if (dim_ptr->offsets != NULL) {
    free(dim_ptr->offsets);
  }
  if (dim_ptr->units != NULL) {
      free(dim_ptr->units);
  }
  if (dim_ptr->widths !=NULL) {
    free(dim_ptr->widths);
  }
  free(dim_ptr);
  
  return (MI_NOERROR);
}

/** Retrieve the list of dimensions defined in a MINC volume, 
 *  with the same class \a class and attribute \a attr.
 * \retval The number of dimensions returned.
 * \retval MI_ERROR on failure.
 * \ingroup mi2Dim
 */
int 
miget_volume_dimensions(mihandle_t volume, midimclass_t class, midimattr_t attr,
			miorder_t order, int array_length, 
			midimhandle_t dimensions[])
{
  // THE PARAMETER "miorder_t order" WAS NOT CONSIDERED WHEW WRITING
  // THIS FUNCTION. MUST FIGURE OUT WHAT TO DO WITH IT
  
  hsize_t number_of_dims; 
  int i=0, max_dims;
  int num_ret_dims = 0;
  
  if (volume == NULL) {
    return (MI_ERROR);
  }
 
  number_of_dims = volume->number_of_dims;

  if (array_length > number_of_dims) {
    max_dims = number_of_dims;
  }
  else {
    max_dims = array_length;
  }
  
  /* Go through each dimension separately and figure out
     which one has a matching class and attirbute.
   */
  for (i=0; i < max_dims; i++) {
      midimhandle_t hdim = volume->dim_handles[i];
      if (class == MI_DIMCLASS_ANY || class == hdim->class) {
          if (hdim->attr == attr || attr ==  MI_DIMATTR_ALL) {
              dimensions[num_ret_dims++] = hdim;
          }
      }
  }
  return (num_ret_dims);
}

/*! Set apparent dimension order, based on an array of dimensions.  You
 * may also set the dimension order by the name of the dimension, see
 * miset_apparent_dimension_order_by_name().
 *
 * \ingroup mi2Dim
 */

int 
miset_apparent_dimension_order(mihandle_t volume, int array_length, 
			       midimhandle_t dimensions[])
{
  int diff;
  int i=0, j=0, k=0;

  if (volume == NULL || array_length <= 0 || 
      array_length != volume->number_of_dims) {
    return (MI_ERROR);
  }
   /* If array_length was more than the number of dimensions
     the rest of the given dimensions will be ignored.
   */ 
  diff = volume->number_of_dims - array_length;
  if (diff < 0) {
    diff = 0;
  }
  /* Allocated space for dimensions indices, if not already done.
   */
  if (volume->dim_indices == NULL){
    volume->dim_indices = (int *)malloc(volume->number_of_dims*sizeof(int));
    memset(volume->dim_indices, -1, sizeof(volume->number_of_dims));
  }
  for (i=0; i < volume->number_of_dims; i++) {
    for (j=0; j < array_length; j++) {
      if (volume->dim_handles[i] == dimensions[j]) {
	volume->dim_indices[j+diff] = i;
	break;
      }
      if (j == (array_length-1)) {
	volume->dim_indices[k++] = i;
      }
    }
  }
  return (MI_NOERROR);
}

/*! Set apparent dimension order by name.
 * \ingroup mi2Dim
 */

int 
miset_apparent_dimension_order_by_name(mihandle_t volume, int array_length, 
				       char **names)
{
  int diff;
  int i=0, j=0, k=0;

  if (volume == NULL) {
    return (MI_ERROR);
  }

  if (names == NULL || array_length <= 0) {
      /* Reset the dimension ordering */
      if (volume->dim_indices != NULL) {
          free(volume->dim_indices);
          volume->dim_indices = NULL;
      }
      return (MI_NOERROR);
  }

  /* Note that all dimension names must be different or an error occurs.
   */
  for (i = 0; i < array_length; i++) {
    for (j = i + 1; j < array_length; j++) {
      if (strcmp(names[i], names[j]) == 0) {
	return (MI_ERROR);
      }
    }
  }
  /* If array_length was more than the number of dimensions
     the rest of the given dimensions will be ignored.
   */ 
  diff = volume->number_of_dims - array_length;
  if (diff < 0) {
      diff = 0;
  }
  /* Allocated space for dimensions indices, if not already done.
   */
  if (volume->dim_indices == NULL) {
      volume->dim_indices = (int *)malloc(volume->number_of_dims*sizeof(int));
      for (i = 0; i < volume->number_of_dims; i++) {
          volume->dim_indices[i] = -1;
      }
  }
  for (i = 0; i < volume->number_of_dims; i++) {
      for (j = 0; j < array_length; j++) {
          if (!strcmp(volume->dim_handles[i]->name, names[j])) {
              volume->dim_indices[i+diff] = j;
              break;
          }
      }
      if (j == (array_length-1)) {
          volume->dim_indices[k++] = i;
      }
  }
  return (MI_NOERROR);
}

/*! Set the record flag and add a record dimension to the volume
    dimensions so the volume would appear to have n+1 dimensions
 * \ingroup mi2Dim
 */

int 
miset_apparent_record_dimension_flag(mihandle_t volume, int record_flag)
{
  midimhandle_t handle;  
 
  if (volume == NULL) {
    return (MI_ERROR);
  }
  /* Allocate space for the dimension
   */
  handle = (midimhandle_t)malloc(sizeof(struct midimension));
  if (handle == NULL) {
    return (MI_ERROR);
  }
  handle->class = MI_DIMCLASS_RECORD;
  handle->volume_handle = volume;

  volume->dim_handles[volume->number_of_dims] = handle;
  /* Add one to the number of dimensions so the volume
     will appear to have (n+1) dimensions
   */
  volume->number_of_dims++;
  
  record_flag = 1;

  return (MI_NOERROR);
}

/*! Get the apparent order of voxels.
 * \ingroup mi2Dim
 */

int 
miget_dimension_apparent_voxel_order(midimhandle_t dimension, 
                                     miflipping_t *file_order,
				     miflipping_t *sign)
{
  if (dimension == NULL) {
    return (MI_ERROR);
  }
  switch (dimension->flipping_order) {
  case MI_FILE_ORDER:
    *file_order = MI_FILE_ORDER;
    if (dimension->step > 0) {
      *sign = MI_POSITIVE;
    }
    else {
      *sign = MI_NEGATIVE;
    }
    break;
  case MI_COUNTER_FILE_ORDER:
    *file_order = MI_COUNTER_FILE_ORDER;
    if (dimension->step > 0) {
      *sign = MI_NEGATIVE;
    }
    else {
      *sign = MI_POSITIVE;
    }
    break;
  case MI_POSITIVE:
    *sign = MI_POSITIVE;
    if (dimension->step > 0) {
      *file_order = MI_FILE_ORDER;
    }
    else {
      *file_order = MI_COUNTER_FILE_ORDER; 
    }
    break;  
  case MI_NEGATIVE:
    *sign = MI_NEGATIVE;
    if (dimension->step > 0) {
      *file_order = MI_COUNTER_FILE_ORDER;
    }
    else {
      *file_order = MI_FILE_ORDER; 
    }
    break;  
  default:
    return (MI_ERROR);
  }
  return (MI_NOERROR);
}

/*! Set the apparent order of voxels.
 * \ingroup mi2Dim
 */

int 
miset_dimension_apparent_voxel_order(midimhandle_t dimension, 
                                     miflipping_t flipping_order)
{
  if (dimension == NULL) {
    return (MI_ERROR);
  }
  switch (flipping_order) {
  case MI_FILE_ORDER:
    dimension->flipping_order  = MI_FILE_ORDER;
    break;
  case MI_COUNTER_FILE_ORDER:
    dimension->flipping_order  = MI_COUNTER_FILE_ORDER;
    break;
  case MI_POSITIVE:
    dimension->flipping_order  = MI_POSITIVE;
    break;
  case MI_NEGATIVE:
    dimension->flipping_order  = MI_NEGATIVE;
    break;
  default:
    return (MI_ERROR);
  }
  
  return (MI_NOERROR);
}

/*! Get the class of a MINC dimension.
 * \ingroup mi2Dim
 */

int 
miget_dimension_class(midimhandle_t dimension, midimclass_t *class)
{
  if (dimension == NULL) {
    return (MI_ERROR);
  }

  switch (dimension->class) {
  case MI_DIMCLASS_ANY:
    *class = MI_DIMCLASS_ANY;
    break;
  case MI_DIMCLASS_SPATIAL:
    *class = MI_DIMCLASS_SPATIAL;
    break;
  case MI_DIMCLASS_TIME:
    *class = MI_DIMCLASS_TIME;
    break;
  case MI_DIMCLASS_SFREQUENCY:
    *class = MI_DIMCLASS_SFREQUENCY;
    break;
  case MI_DIMCLASS_TFREQUENCY:
    *class = MI_DIMCLASS_TFREQUENCY;
    break;
  case MI_DIMCLASS_USER:
    *class = MI_DIMCLASS_USER;
    break;
  case MI_DIMCLASS_RECORD:
    *class = MI_DIMCLASS_RECORD;
    break;
  default:
    return (MI_ERROR);
  }
  
  return (MI_NOERROR);
}

/*! Set the class of a MINC dimension.
 * \ingroup mi2Dim
 */

int 
miset_dimension_class(midimhandle_t dimension, midimclass_t class)
{
   if (dimension == NULL) {
    return (MI_ERROR);
  }
  switch (class) {
  case MI_DIMCLASS_ANY:
    dimension->class = MI_DIMCLASS_ANY;
    break;
  case MI_DIMCLASS_SPATIAL:
    dimension->class = MI_DIMCLASS_SPATIAL;
    break;
  case MI_DIMCLASS_TIME:
    dimension->class = MI_DIMCLASS_TIME;
    break;
  case MI_DIMCLASS_SFREQUENCY:
    dimension->class = MI_DIMCLASS_SFREQUENCY;
    break;
  case MI_DIMCLASS_TFREQUENCY:
    dimension->class = MI_DIMCLASS_TFREQUENCY;
    break;
  case MI_DIMCLASS_USER:
    dimension->class = MI_DIMCLASS_USER;
    break;
  case MI_DIMCLASS_RECORD:
    dimension->class = MI_DIMCLASS_RECORD;
    break;
  default:
    return (MI_ERROR);
  } 
  
  return (MI_NOERROR);
}

/*! Get the direction cosine vector of a given SPATIAL dimension.
 * \ingroup mi2Dim
 */

int 
miget_dimension_cosines(midimhandle_t dimension, double direction_cosines[3])
{
  if (dimension == NULL || (dimension->class != MI_DIMCLASS_SPATIAL &&
                            dimension->class != MI_DIMCLASS_SFREQUENCY)){
    return (MI_ERROR);
  }
  
  direction_cosines[0] = dimension->direction_cosines[0];
  direction_cosines[1] = dimension->direction_cosines[1];
  direction_cosines[2] = dimension->direction_cosines[2];

  return (MI_NOERROR);
}

/*! Set the direction cosine vector for a given SPATIAL dimension.
 * \ingroup mi2Dim
 */

int 
miset_dimension_cosines(midimhandle_t dimension, 
                        const double direction_cosines[3])
{
  
  if (dimension == NULL || dimension->class != MI_DIMCLASS_SPATIAL) {
    return (MI_ERROR);
  }
  
  dimension->direction_cosines[0] = direction_cosines[0];
  dimension->direction_cosines[1] = direction_cosines[1];
  dimension->direction_cosines[2] = direction_cosines[2];

  return (MI_NOERROR);
}

/*! Get the comments attribute for a given dimension.  The string pointer
 * returned in \a *comments_ptr must be freed by the caller.
 *
 * \ingroup mi2Dim
 */

int
miget_dimension_description(midimhandle_t dimension, char **comments_ptr)
{

  if (dimension == NULL) {
    return (MI_ERROR);
  }
 
  *comments_ptr = strdup(dimension->comments);
  
  return (MI_NOERROR); 
}

/*! Set the comments attribute for a given dimension.
 * \ingroup mi2Dim
 */

int
miset_dimension_description(midimhandle_t dimension, const char *comments)
{

 if (dimension == NULL || comments == NULL) {
    return (MI_ERROR);
  }
 
  if ((strlen(comments) + 1) <= MI2_CHAR_LENGTH) {
    dimension->comments = strdup(comments);
  }
  else {
    return (MI_ERROR);
  }
    
  return (MI_NOERROR); 
}


/*! Get the identifier (name) of a MINC dimension.
 * \ingroup mi2Dim
 */

int 
miget_dimension_name(midimhandle_t dimension, char **name_ptr)
{
  if (dimension == NULL) {
    return (MI_ERROR);
  }

  *name_ptr = strdup(dimension->name);

  return (MI_NOERROR);
}

/*! Set the identifier (name) of a given MINC dimension.
 * \ingroup mi2Dim
 */

int 
miset_dimension_name(midimhandle_t dimension, const char *name)
{
    if (dimension == NULL || name == NULL) {
        return (MI_ERROR);
    }

    if ((strlen(name) + 1) > MI2_CHAR_LENGTH) {
        return (MI_ERROR);
    }

    /* Free the existing dimension name.
     */
    if (dimension->name != NULL) {
        free(dimension->name);
    }
 
    dimension->name = strdup(name);

    return (MI_NOERROR);
}

/*! Get the untransformed world coordinates of points along a MINC dimension.
 * \ingroup mi2Dim
 */

int 
miget_dimension_offsets(midimhandle_t dimension, unsigned long array_length, 
			unsigned long start_position, double offsets[])
{
    unsigned long end_position;
    unsigned long i, j;

    if (dimension == NULL || start_position > dimension->length ) {
        return (MI_ERROR);
    }
    if ((start_position + array_length) > dimension->length) {
        end_position = dimension->length;
    }
    else {
        end_position = start_position + array_length;
    }

    if (dimension->offsets == NULL) {
        for (i = start_position, j = 0; i < end_position ; i++, j++) {
            /* For regularly sampled dimensions, the step value
             * is added to each value to get the next value.
             */
            offsets[j] = dimension->start + (i * dimension->step);
        }
    }
    else {
        for (i = start_position, j = 0; i < end_position ; i++, j++) {
            offsets[j] = dimension->offsets[i];
        }
    }
    return (MI_NOERROR);
}

/*! Set the absolute world coordinates of points along a MINC dimension.
 * \ingroup mi2Dim
 */

int 
miset_dimension_offsets(midimhandle_t dimension, 
                        unsigned long array_length, 
			unsigned long start_position, 
                        const double offsets[])
{
    unsigned long end_position;
    unsigned long i, j;

    /* Check to see whether the dimension is regularly sampled.
     */
    if (dimension == NULL || 
        (dimension->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED) == 0 || 
        start_position > dimension->length ) {
        return (MI_ERROR);
    }
    if ((start_position + array_length) > dimension->length) {
        end_position = dimension->length;
    }
    else {
        end_position = start_position + array_length;
    }
  
    /* Allocate space for the offsets if not already done.
     */
    if (dimension->offsets == NULL) {
        dimension->offsets = 
            (double *) malloc(dimension->length * sizeof(double));
    }
    
    for (i = start_position, j = 0; i < end_position; i++, j++) {
        dimension->offsets[i] = offsets[j] ;
    }
    return (MI_NOERROR);
}

/*! Get the sampling flag for a MINC dimension.  
 * If the flag is TRUE, the
 * dimension is irregularly sampled. If the flag is FALSE (the default),
 * sampling points occur regularly along the axis.
 * \ingroup mi2Dim
 */

int 
miget_dimension_sampling_flag(midimhandle_t dimension, BOOLEAN *sampling_flag)
{
  if (dimension == NULL) {
    return (MI_ERROR);
  }

  *sampling_flag = (dimension->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED) != 0;

  return (MI_NOERROR);
}

/*! Set the sampling flag for a MINC dimension.
 * If the flag is TRUE, the
 * dimension is irregularly sampled. If the flag is FALSE (the default),
 * sampling points occur regularly along the axis.
 * \ingroup mi2Dim
 */

int 
miset_dimension_sampling_flag(midimhandle_t dimension, BOOLEAN sampling_flag)
{
    if (dimension == NULL) {
        return (MI_ERROR);
    }
    if (sampling_flag) {
        dimension->attr |= MI_DIMATTR_NOT_REGULARLY_SAMPLED;
    }
    else {
        dimension->attr &= ~MI_DIMATTR_NOT_REGULARLY_SAMPLED;
    }
    return (MI_NOERROR);
}

/*! Get the sampling interval (step) for a single dimension.
 * \ingroup mi2Dim
 */

int 
miget_dimension_separation(midimhandle_t dimension, 
                           mivoxel_order_t voxel_order, 
			   double *separation_ptr)
{
    if (dimension == NULL) {
        return (MI_ERROR);
    }
    if (voxel_order == MI_ORDER_FILE) {
        *separation_ptr = dimension->step;
    }
    else {
        if (dimension->flipping_order == MI_COUNTER_FILE_ORDER) {
            *separation_ptr = -dimension->step;
        }
        else if (dimension->flipping_order == MI_POSITIVE) {
            if (dimension->step > 0) {
                *separation_ptr = dimension->step;
            }
            else {
                *separation_ptr = -dimension->step;
            }
        }
        else if (dimension->flipping_order == MI_NEGATIVE) {
            if (dimension->step < 0) {
                *separation_ptr = dimension->step;
            }
            else {
                *separation_ptr = -dimension->step;
            }
        }
        else {
            *separation_ptr = dimension->step;
        }
    }
    return (MI_NOERROR);
}

/*! Set the sampling interval (step) for a single dimension.
 * \ingroup mi2Dim
 */

int 
miset_dimension_separation(midimhandle_t dimension, double separation)
{
  /* file-order of voxels is assumed.
   */
  if (dimension == NULL) {
    return (MI_ERROR);
  }
  
  dimension->step = separation;
  /* If not explicitly set, the width will be assumed to be equal to the
     dimension's step size.
  */
  //dimension->width = separation;

  return (MI_NOERROR);
}

/*! Get the sampling interval (STEP) for a list of dimensions.
 * \ingroup mi2Dim
 */

int 
miget_dimension_separations(const midimhandle_t dimensions[], 
                            mivoxel_order_t voxel_order,
			    int array_length, 
                            double separations[])
{
    int i;

    for (i=0; i< array_length; i++) {
        miget_dimension_separation(dimensions[i], voxel_order, 
                                   &separations[i]);
    }
    return (MI_NOERROR);
}

/*! Set the sampling interval (STEP) for a list of dimensions.
 * \ingroup mi2Dim
 */

int 
miset_dimension_separations(const midimhandle_t dimensions[], 
                            int array_length, 
			    const double separations[])
{
    int i;
    for (i=0; i< array_length; i++) {
        miset_dimension_separation(dimensions[i], separations[i]);
    }
    return (MI_NOERROR);
}

/*! Get the length of a MINC dimension.
 * \ingroup mi2Dim
 */

int 
miget_dimension_size(midimhandle_t dimension, unsigned long *size_ptr)
{
    if (dimension == NULL) {
        return (MI_ERROR);
    }
    *size_ptr = dimension->length;
    return (MI_NOERROR);
}

/*! Set the length of a MINC dimension if not associated with a 
 *  volume.
 * \ingroup mi2Dim
 */

int 
miset_dimension_size(midimhandle_t dimension, unsigned long size)
{
    /* Check whether the dimension is associated with a volume.
     */
    if (dimension == NULL || dimension->volume_handle != NULL) {
        return (MI_ERROR);
    }
    dimension->length = size;
    return (MI_NOERROR);
}

/*! Retrieve the length of all dimensions in dimensions array.
 * \ingroup mi2Dim
 */

int 
miget_dimension_sizes(const midimhandle_t dimensions[], int array_length,
		      unsigned long sizes[])
{
    int i;

    for ( i = 0; i < array_length; i++) {
        miget_dimension_size(dimensions[i], &sizes[i]);
    }
    
    return (MI_NOERROR);
}

/*! Get the start value of a MINC dimension.
 * \ingroup mi2Dim
 */

int 
miget_dimension_start(midimhandle_t dimension, mivoxel_order_t voxel_order,
		      double *start_ptr)
{
  /* If voxel_order is set to apparent file order (i.e., 1)
     start = start + step * (n-1)
   */
  if (dimension == NULL) {
    return (MI_ERROR);
  }
  if (voxel_order == 0) {
    *start_ptr = dimension->start;
  }
  else {
    
    *start_ptr = dimension->start + (dimension->step * (dimension->length-1));
  }
  return (MI_NOERROR);
}

/*! Set the origin of a MINC dimension.
 * \ingroup mi2Dim
 */

int 
miset_dimension_start(midimhandle_t dimension, double start)
{
  if (dimension == NULL) {
    return (MI_ERROR);
  }
  dimension->start = start;
  return (MI_NOERROR);
}

/*! Get the start values for MINC dimensions in dimensions array.
 * \ingroup mi2Dim
 */

int 
miget_dimension_starts(const midimhandle_t dimensions[], 
                       mivoxel_order_t voxel_order,
		       int array_length, double starts[])
{ 
    int i;

    for (i=0; i < array_length; i++) {
        miget_dimension_start(dimensions[i], voxel_order, &starts[i]);
    }
    return (MI_NOERROR);
}

/*! Set the start values for MINC dimensions in dimensions array.
 * \ingroup mi2Dim
 */

int 
miset_dimension_starts(const midimhandle_t dimensions[], 
                       int array_length, 
		       const double starts[])
{
    int i;
 
    for (i=0; i<array_length; i++) {
        miset_dimension_start(dimensions[i], starts[i]);
    }
    return (MI_NOERROR);
}

/*! Get the unit string for a MINC dimension.  The caller must free the
 * string returned by this function.
 * \ingroup mi2Dim
 */

int 
miget_dimension_units(midimhandle_t dimension, char **units_ptr)
{
    if (dimension == NULL || units_ptr == NULL) {
        return (MI_ERROR);
    }
  
    *units_ptr = strdup(dimension->units);
  
    return (MI_NOERROR);
}

/*! Set the unit string for a MINC dimension.
 * \ingroup mi2Dim
 */

int 
miset_dimension_units(midimhandle_t dimension, const char *units)
{
    if (dimension == NULL || units == NULL) {
        return (MI_ERROR);
    }
  
    if (strlen(units) + 1 > MI2_CHAR_LENGTH) {
        return (MI_ERROR);
    }

    dimension->units = strdup(units);
    return (MI_NOERROR);
}

/*! Get A single full-width half-maximum value from a 
    regularly sampled dimension.
 * \ingroup mi2Dim
 */

int 
miget_dimension_width(midimhandle_t dimension, double *width_ptr)
{
  
  if (dimension == NULL || 
      (dimension->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED) != 0) {
    return (MI_ERROR);
  }
  *width_ptr = dimension->width;
  return (MI_NOERROR);
}

/*! Set the A single full-width half-maximum value for a 
 * regularly sampled dimension.
 * \ingroup mi2Dim
 */

int 
miset_dimension_width(midimhandle_t dimension, double width)
{
  if (dimension == NULL || 
      (dimension->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED) != 0) {
    return (MI_ERROR);
  }
  /* Check to make sure width value is positive.
   */
  if (width < 0) {
    dimension->width = -width;
  }
  else {
    dimension->width = width;
  }
  return (MI_NOERROR);
}

/*! Get the full-width half-maximum value for points along an
 *     irregularly sampled dimension.
 * \ingroup mi2Dim
 */

int 
miget_dimension_widths(midimhandle_t dimension, 
                       mivoxel_order_t voxel_order,
		       unsigned long array_length, 
                       unsigned long start_position,
		       double widths[])
{
  unsigned long  diff;
  int i, j = 0;

  if (dimension == NULL || start_position > dimension->length) {
    return (MI_ERROR);
  }
  
  if ((start_position + array_length) > dimension->length) {
    diff = dimension->length;
  }
  else {
    diff = array_length;
  }
  /* Allocate space for the widths array
   */
  widths = (double *) malloc(diff*sizeof(double));
  /* Check to see whether the dimension is regularly sampled.
   */
  if (start_position == 0) {
    diff--;
  }
  if (dimension->widths == NULL) {
    for (i=start_position; i <= diff; i++) {
      widths[j] = dimension->width;
      j++;
    }
  }
  else { 
    /* If the apparent order is requested, the widths are returned
       REVERSE (flip) order.
     */
    if (voxel_order == 0) {
      for (i=start_position; i <= diff; i++) {
	widths[j] = dimension->widths[i];
	j++;
      }
    }
    else {
      for (i=diff; i >= start_position; i--) {
	widths[j] = dimension->widths[i];
	j++;
      }
    }
      
  }

  return (MI_NOERROR);
}

/*! Set the full-width half-maximum value for points along an
 * irregularly sampled dimension.
 * \ingroup mi2Dim
 */

int 
miset_dimension_widths(midimhandle_t dimension, 
                       unsigned long array_length,
		       unsigned long start_position, 
                       const double widths[])
{
  unsigned long  diff;
  int i, j=0;
  /* Check to see whether the dimension is regularly sampled.
   */
  if (dimension == NULL || 
      (dimension->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED) == 0 || 
      start_position > dimension->length) {
    return (MI_ERROR);
  }

  if ((start_position + array_length) > dimension->length) {
    diff = dimension->length;
  }
  else {
    diff = array_length;
  }
  /* Allocate space for widths array if not already done
   */
  if (dimension->widths == NULL) {
    dimension->widths = (double *) malloc(dimension->length*sizeof(double));
  }
  if (start_position == 0) {
    diff--;
  }
  for (i=start_position; i <= diff; i++) {
    if (widths[i] < 0) {
      dimension->widths[i] = -1 * widths[j];
    }
    else {
      dimension->widths[i] = widths[j];
    }
    j++;
  }
  return (MI_NOERROR);
}