changeset 1089:b37a3ef7098a

check_in_all
author david <david>
date Wed, 13 Aug 1997 13:21:28 +0000
parents b609c92cd668
children 4dd5e5ebf714
files volume_io/Geometry/transforms.c volume_io/Include/volume_io/alloc.h volume_io/Include/volume_io/basic.h volume_io/Include/volume_io/system_dependent.h volume_io/Include/volume_io/volume.h volume_io/Prog_utils/files.c volume_io/Volumes/get_hyperslab.c volume_io/Volumes/input_free.c volume_io/Volumes/input_mnc.c volume_io/Volumes/output_mnc.c volume_io/Volumes/output_volume.c volume_io/Volumes/volumes.c
diffstat 12 files changed, 978 insertions(+), 393 deletions(-) [+]
line wrap: on
line diff
--- a/volume_io/Geometry/transforms.c
+++ b/volume_io/Geometry/transforms.c
@@ -15,7 +15,7 @@
 #include  <internal_volume_io.h>
 
 #ifndef lint
-static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Geometry/transforms.c,v 1.18 1996-05-17 19:36:08 david Exp $";
+static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Geometry/transforms.c,v 1.19 1997-08-13 13:21:28 david Exp $";
 #endif
 
 /* ----------------------------- MNI Header -----------------------------------
@@ -142,6 +142,29 @@
 }
 
 /* ----------------------------- MNI Header -----------------------------------
+@NAME       : get_transform_origin_real
+@INPUT      : transform
+@OUTPUT     : origin
+@RETURNS    : 
+@DESCRIPTION: Passes back the origin of the transform, i.e., where the 
+              point (0,0,0) would be transformed to.
+@METHOD     : 
+@GLOBALS    : 
+@CALLS      : 
+@CREATED    : May 20, 1997            David MacDonald
+@MODIFIED   : 
+---------------------------------------------------------------------------- */
+
+public  void  get_transform_origin_real(
+    Transform   *transform,
+    Real        origin[] )
+{
+    origin[X] = Transform_elem(*transform,0,3);
+    origin[Y] = Transform_elem(*transform,1,3);
+    origin[Z] = Transform_elem(*transform,2,3);
+}
+
+/* ----------------------------- MNI Header -----------------------------------
 @NAME       : get_transform_x_axis
 @INPUT      : transform
 @OUTPUT     : x_axis
@@ -166,6 +189,29 @@
 }
 
 /* ----------------------------- MNI Header -----------------------------------
+@NAME       : get_transform_x_axis_real
+@INPUT      : transform
+@OUTPUT     : x_axis
+@RETURNS    : 
+@DESCRIPTION: Passes back the x axis of the transform, i.e., the vector
+              to which the vector (1,0,0) would be transformed.
+@METHOD     : 
+@GLOBALS    : 
+@CALLS      : 
+@CREATED    : May 20, 1997            David MacDonald
+@MODIFIED   : 
+---------------------------------------------------------------------------- */
+
+public  void  get_transform_x_axis_real(
+    Transform   *transform,
+    Real        x_axis[] )
+{
+    x_axis[X] = Transform_elem(*transform,0,0);
+    x_axis[Y] = Transform_elem(*transform,1,0);
+    x_axis[Z] = Transform_elem(*transform,2,0);
+}
+
+/* ----------------------------- MNI Header -----------------------------------
 @NAME       : set_transform_x_axis
 @INPUT      : x_axis
 @OUTPUT     : transform
@@ -189,6 +235,29 @@
 }
 
 /* ----------------------------- MNI Header -----------------------------------
+@NAME       : set_transform_x_axis_real
+@INPUT      : x_axis
+@OUTPUT     : transform
+@RETURNS    : 
+@DESCRIPTION: Sets the x axis of the transform, i.e., the vector
+              to which the vector (1,0,0) would be transformed.
+@METHOD     : 
+@GLOBALS    : 
+@CALLS      : 
+@CREATED    : May 20, 1997            David MacDonald
+@MODIFIED   : 
+---------------------------------------------------------------------------- */
+
+public  void  set_transform_x_axis_real(
+    Transform   *transform,
+    Real        x_axis[] )
+{
+    Transform_elem(*transform,0,0) = (Transform_elem_type) x_axis[X];
+    Transform_elem(*transform,1,0) = (Transform_elem_type) x_axis[Y];
+    Transform_elem(*transform,2,0) = (Transform_elem_type) x_axis[Z];
+}
+
+/* ----------------------------- MNI Header -----------------------------------
 @NAME       : get_transform_y_axis
 @INPUT      : transform
 @OUTPUT     : y_axis
@@ -213,6 +282,29 @@
 }
 
 /* ----------------------------- MNI Header -----------------------------------
+@NAME       : get_transform_y_axis_real
+@INPUT      : transform
+@OUTPUT     : y_axis
+@RETURNS    : 
+@DESCRIPTION: Passes back the y axis of the transform, i.e., the vector
+              to which the vector (0,1,0) would be transformed.
+@METHOD     : 
+@GLOBALS    : 
+@CALLS      : 
+@CREATED    : May 20, 1997            David MacDonald
+@MODIFIED   : 
+---------------------------------------------------------------------------- */
+
+public  void  get_transform_y_axis_real(
+    Transform   *transform,
+    Real        y_axis[] )
+{
+    y_axis[X] = Transform_elem(*transform,0,1);
+    y_axis[Y] = Transform_elem(*transform,1,1);
+    y_axis[Z] = Transform_elem(*transform,2,1);
+}
+
+/* ----------------------------- MNI Header -----------------------------------
 @NAME       : set_transform_y_axis
 @INPUT      : y_axis
 @OUTPUT     : transform
@@ -236,6 +328,29 @@
 }
 
 /* ----------------------------- MNI Header -----------------------------------
+@NAME       : set_transform_y_axis_real
+@INPUT      : y_axis
+@OUTPUT     : transform
+@RETURNS    : 
+@DESCRIPTION: Sets the y axis of the transform, i.e., the vector
+              to which the vector (0,1,0) would be transformed.
+@METHOD     : 
+@GLOBALS    : 
+@CALLS      : 
+@CREATED    : May 20, 1997            David MacDonald
+@MODIFIED   : 
+---------------------------------------------------------------------------- */
+
+public  void  set_transform_y_axis_real(
+    Transform   *transform,
+    Real        y_axis[] )
+{
+    Transform_elem(*transform,0,1) = (Transform_elem_type) y_axis[X];
+    Transform_elem(*transform,1,1) = (Transform_elem_type) y_axis[Y];
+    Transform_elem(*transform,2,1) = (Transform_elem_type) y_axis[Z];
+}
+
+/* ----------------------------- MNI Header -----------------------------------
 @NAME       : get_transform_z_axis
 @INPUT      : transform
 @OUTPUT     : z_axis
@@ -260,6 +375,29 @@
 }
 
 /* ----------------------------- MNI Header -----------------------------------
+@NAME       : get_transform_z_axis_real
+@INPUT      : transform
+@OUTPUT     : z_axis
+@RETURNS    : 
+@DESCRIPTION: Passes back the z axis of the transform, i.e., the vector
+              to which the vector (0,0,1) would be transformed.
+@METHOD     : 
+@GLOBALS    : 
+@CALLS      : 
+@CREATED    : May 20, 1997            David MacDonald
+@MODIFIED   : 
+---------------------------------------------------------------------------- */
+
+public  void  get_transform_z_axis_real(
+    Transform   *transform,
+    Real        z_axis[] )
+{
+    z_axis[X] = Transform_elem(*transform,0,2);
+    z_axis[Y] = Transform_elem(*transform,1,2);
+    z_axis[Z] = Transform_elem(*transform,2,2);
+}
+
+/* ----------------------------- MNI Header -----------------------------------
 @NAME       : set_transform_z_axis
 @INPUT      : z_axis
 @OUTPUT     : transform
@@ -283,6 +421,29 @@
 }
 
 /* ----------------------------- MNI Header -----------------------------------
+@NAME       : set_transform_z_axis_real
+@INPUT      : z_axis
+@OUTPUT     : transform
+@RETURNS    : 
+@DESCRIPTION: Sets the z axis of the transform, i.e., the vector
+              to which the vector (0,0,1) would be transformed.
+@METHOD     : 
+@GLOBALS    : 
+@CALLS      : 
+@CREATED    : May 20, 1997            David MacDonald
+@MODIFIED   : 
+---------------------------------------------------------------------------- */
+
+public  void  set_transform_z_axis_real(
+    Transform   *transform,
+    Real        z_axis[] )
+{
+    Transform_elem(*transform,0,2) = (Transform_elem_type) z_axis[X];
+    Transform_elem(*transform,1,2) = (Transform_elem_type) z_axis[Y];
+    Transform_elem(*transform,2,2) = (Transform_elem_type) z_axis[Z];
+}
+
+/* ----------------------------- MNI Header -----------------------------------
 @NAME       : make_change_to_bases_transform
 @INPUT      : origin
               x_axis
--- a/volume_io/Include/volume_io/alloc.h
+++ b/volume_io/Include/volume_io/alloc.h
@@ -29,7 +29,7 @@
 ---------------------------------------------------------------------------- */
 
 #ifndef lint
-static char alloc_rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Include/volume_io/alloc.h,v 1.14 1996-11-15 16:09:43 david Exp $";
+static char alloc_rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Include/volume_io/alloc.h,v 1.15 1997-08-13 13:21:30 david Exp $";
 #endif
 
 #include  <basic.h>
@@ -275,7 +275,7 @@
 ---------------------------------------------------------------------------- */
 
 #define  ALLOC5D( ptr, n1, n2, n3, n4, n5 )                                   \
-     ASSIGN(ptr) = alloc_memory_5d( (size_t) (n1), (size_t) (n2),             \
+     ASSIGN_PTR(ptr) = alloc_memory_5d( (size_t) (n1), (size_t) (n2),         \
                           (size_t) (n3), (size_t) (n4), (size_t) (n5),        \
                           sizeof(*****(ptr)) _ALLOC_SOURCE_LINE )
 
--- a/volume_io/Include/volume_io/basic.h
+++ b/volume_io/Include/volume_io/basic.h
@@ -29,7 +29,7 @@
 ---------------------------------------------------------------------------- */
 
 #ifndef lint
-static char basic_rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Include/volume_io/basic.h,v 1.29 1997-03-23 21:11:29 david Exp $";
+static char basic_rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Include/volume_io/basic.h,v 1.30 1997-08-13 13:21:30 david Exp $";
 #endif
 
 #include  <stdlib.h>
@@ -89,14 +89,14 @@
 #endif
 #define  MAX( x, y )  ( ((x) >= (y)) ? (x) : (y) )
 
-#define  MAX3( x, y, z )  MAX( x, MAX(y,z) )
+#define  MAX3( x, y, z )  ( ((x) >= (y)) ? MAX( x, z ) : MAX( y, z ) )
 
 #ifdef   MIN
 #undef   MIN
 #endif
 #define  MIN( x, y )  ( ((x) <= (y)) ? (x) : (y) )
 
-#define  MIN3( x, y, z )  MIN( x, MIN(y,z) )
+#define  MIN3( x, y, z )  ( ((x) <= (y)) ? MIN( x, z ) : MIN( y, z ) )
 
 /* --------- gets the address of a 2-d array element in a 1-d array ----- */
 
--- a/volume_io/Include/volume_io/system_dependent.h
+++ b/volume_io/Include/volume_io/system_dependent.h
@@ -14,6 +14,13 @@
               express or implied warranty.
 ---------------------------------------------------------------------------- */
 
+#ifdef sun
+#ifndef __GNUC__
+#define  NO_DBL_MAX             /*--- sun os does not define DBL_MAX */
+#endif
+#define  NO_STRERROR            /*--- sun os does not define strerror */
+#endif
+
 #ifdef NO_DBL_MAX
 #ifndef DBL_MAX
 #include <values.h>
--- a/volume_io/Include/volume_io/volume.h
+++ b/volume_io/Include/volume_io/volume.h
@@ -16,7 +16,7 @@
 ---------------------------------------------------------------------------- */
 
 #ifndef lint
-static char volume_rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Include/volume_io/volume.h,v 1.48 1996-12-09 20:20:23 david Exp $";
+static char volume_rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Include/volume_io/volume.h,v 1.49 1997-08-13 13:21:29 david Exp $";
 #endif
 
 /* ----------------------------- MNI Header -----------------------------------
@@ -42,6 +42,8 @@
 {
     Real     global_image_range[2];
     STRING   dimension_names[MAX_DIMENSIONS];
+    BOOLEAN  use_starts_set;
+    BOOLEAN  use_volume_starts_and_steps;
 } minc_output_options;
 
 #include  <volume_cache.h>
@@ -75,11 +77,10 @@
     Real                    real_value_translation;
 
     Real                    separations[MAX_DIMENSIONS];
-    Real                    translation_voxel[MAX_DIMENSIONS];
+    Real                    starts[MAX_DIMENSIONS];
     Real                    direction_cosines[MAX_DIMENSIONS][N_DIMENSIONS];
 
-    Real                    world_space_for_translation_voxel[N_DIMENSIONS];
-
+    BOOLEAN                 voxel_to_world_transform_uptodate;
     General_transform       voxel_to_world_transform;
 
     STRING                  coordinate_system_name;
--- a/volume_io/Prog_utils/files.c
+++ b/volume_io/Prog_utils/files.c
@@ -19,7 +19,7 @@
 #include  <errno.h>
 
 #ifndef lint
-static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Prog_utils/files.c,v 1.35 1997-06-29 19:00:21 david Exp $";
+static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Prog_utils/files.c,v 1.36 1997-08-13 13:21:31 david Exp $";
 #endif
 
 private  BOOLEAN  has_no_extension( STRING );
--- a/volume_io/Volumes/get_hyperslab.c
+++ b/volume_io/Volumes/get_hyperslab.c
@@ -15,7 +15,7 @@
 #include  <internal_volume_io.h>
 
 #ifndef lint
-static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Volumes/get_hyperslab.c,v 1.5 1996-11-15 16:09:48 david Exp $";
+static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Volumes/get_hyperslab.c,v 1.6 1997-08-13 13:21:35 david Exp $";
 #endif
 
 public  void  convert_voxels_to_values(
@@ -198,42 +198,32 @@
     }
 }
 
-private  Real  *unsigned_byte_to_real = NULL;
-private  Real  *signed_byte_to_real = NULL;
-private  Real  *unsigned_short_to_real = NULL;
-private  Real  *signed_short_to_real = NULL;
+private  Real  *int_to_real_conversion = NULL;
 
-private  void  check_real_conversion_lookup(
-    Data_types  data_type )
+private  void  check_real_conversion_lookup( void )
 {
-    Real   min_value, max_value, **ptr;
+    Real   min_value1, max_value1, min_value2, max_value2;
     long   i, long_min, long_max;
 
-    switch( data_type )
-    {
-    case  UNSIGNED_BYTE:   ptr = &unsigned_byte_to_real;  break;
-    case  SIGNED_BYTE:     ptr = &signed_byte_to_real;  break;
-    case  UNSIGNED_SHORT:  ptr = &unsigned_short_to_real;  break;
-    case  SIGNED_SHORT:    ptr = &signed_short_to_real;  break;
-    default:               return;
-    }
-
-    if( *ptr != NULL )
+    if( int_to_real_conversion != NULL )
         return;
 
-    get_type_range( data_type, &min_value, &max_value );
-    long_min = (long) min_value;
-    long_max = (long) max_value;
+    get_type_range( UNSIGNED_SHORT, &min_value1, &max_value1 );
+    get_type_range( SIGNED_SHORT, &min_value2, &max_value2 );
 
-    ALLOC( *ptr, long_max - long_min + 1 );
+    long_min = (long) MIN( min_value1, min_value2 );
+    long_max = (long) MAX( max_value1, max_value2 );
+
+    ALLOC( int_to_real_conversion, long_max - long_min + 1 );
 #ifndef  NO_DEBUG_ALLOC
-    (void) unrecord_ptr_alloc_check( *ptr, __FILE__, __LINE__ );
+    (void) unrecord_ptr_alloc_check( int_to_real_conversion,
+                                     __FILE__, __LINE__ );
 #endif
 
-    (*ptr) -= long_min;
+    int_to_real_conversion -= long_min;
 
     for_inclusive( i, long_min, long_max )
-        (*ptr)[i] = (Real) i;
+        int_to_real_conversion[i] = (Real) i;
 }
 
 public  void  get_voxel_values_5d(
@@ -270,7 +260,7 @@
     step2 -= n3 * step3;
     step3 -= n4 * step4;
 
-    check_real_conversion_lookup( data_type );
+    check_real_conversion_lookup();
 
     switch( data_type )
     {
@@ -286,7 +276,7 @@
                     {
                         for_less( i4, 0, n4 )
                         {
-                            *values = unsigned_byte_to_real[
+                            *values = int_to_real_conversion[
                                                 (long) *unsigned_byte_ptr];
                             ++values;
                             unsigned_byte_ptr += step4;
@@ -313,7 +303,7 @@
                     {
                         for_less( i4, 0, n4 )
                         {
-                            *values = signed_byte_to_real[
+                            *values = int_to_real_conversion[
                                                 (long) *signed_byte_ptr];
                             ++values;
                             signed_byte_ptr += step4;
@@ -340,7 +330,7 @@
                     {
                         for_less( i4, 0, n4 )
                         {
-                            *values = unsigned_short_to_real[
+                            *values = int_to_real_conversion[
                                                (long) *unsigned_short_ptr];
                             ++values;
                             unsigned_short_ptr += step4;
@@ -367,7 +357,7 @@
                     {
                         for_less( i4, 0, n4 )
                         {
-                            *values = signed_short_to_real[
+                            *values = int_to_real_conversion[
                                                 (long) *signed_short_ptr];
                             ++values;
                             signed_short_ptr += step4;
@@ -518,7 +508,7 @@
     step0 -= n1 * step1;
     step1 -= n2 * step2;
     step2 -= n3 * step3;
-    check_real_conversion_lookup( data_type );
+    check_real_conversion_lookup();
 
     switch( data_type )
     {
@@ -532,7 +522,7 @@
                 {
                     for_less( i3, 0, n3 )
                     {
-                        *values = unsigned_byte_to_real[
+                        *values = int_to_real_conversion[
                                                 (long) *unsigned_byte_ptr];
                         ++values;
                         unsigned_byte_ptr += step3;
@@ -555,7 +545,7 @@
                 {
                     for_less( i3, 0, n3 )
                     {
-                        *values = signed_byte_to_real[(long) *signed_byte_ptr];
+                        *values = int_to_real_conversion[(long) *signed_byte_ptr];
                         ++values;
                         signed_byte_ptr += step3;
                     }
@@ -577,7 +567,7 @@
                 {
                     for_less( i3, 0, n3 )
                     {
-                        *values = unsigned_short_to_real[
+                        *values = int_to_real_conversion[
                                                 (long) *unsigned_short_ptr];
                         ++values;
                         unsigned_short_ptr += step3;
@@ -600,7 +590,7 @@
                 {
                     for_less( i3, 0, n3 )
                     {
-                        *values = signed_short_to_real[
+                        *values = int_to_real_conversion[
                                                 (long) *signed_short_ptr];
                         ++values;
                         signed_short_ptr += step3;
@@ -730,7 +720,7 @@
     step2 = steps[2];
     step0 -= n1 * step1;
     step1 -= n2 * step2;
-    check_real_conversion_lookup( data_type );
+    check_real_conversion_lookup();
 
     switch( data_type )
     {
@@ -742,7 +732,7 @@
             {
                 for_less( i2, 0, n2 )
                 {
-                    *values = unsigned_byte_to_real[(long) *unsigned_byte_ptr];
+                    *values = int_to_real_conversion[(long) *unsigned_byte_ptr];
                     ++values;
                     unsigned_byte_ptr += step2;
                 }
@@ -760,7 +750,7 @@
             {
                 for_less( i2, 0, n2 )
                 {
-                    *values = signed_byte_to_real[(long) *signed_byte_ptr];
+                    *values = int_to_real_conversion[(long) *signed_byte_ptr];
                     ++values;
                     signed_byte_ptr += step2;
                 }
@@ -778,7 +768,7 @@
             {
                 for_less( i2, 0, n2 )
                 {
-                    *values = unsigned_short_to_real[(long) *unsigned_short_ptr];
+                    *values = int_to_real_conversion[(long) *unsigned_short_ptr];
                     ++values;
                     unsigned_short_ptr += step2;
                 }
@@ -796,7 +786,7 @@
             {
                 for_less( i2, 0, n2 )
                 {
-                    *values = signed_short_to_real[(long) *signed_short_ptr];
+                    *values = int_to_real_conversion[(long) *signed_short_ptr];
                     ++values;
                     signed_short_ptr += step2;
                 }
@@ -904,7 +894,7 @@
     step0 = steps[0];
     step1 = steps[1];
     step0 -= n1 * step1;
-    check_real_conversion_lookup( data_type );
+    check_real_conversion_lookup();
 
     switch( data_type )
     {
@@ -914,7 +904,7 @@
         {
             for_less( i1, 0, n1 )
             {
-                *values = unsigned_byte_to_real[(long) *unsigned_byte_ptr];
+                *values = int_to_real_conversion[(long) *unsigned_byte_ptr];
                 ++values;
                 unsigned_byte_ptr += step1;
             }
@@ -928,7 +918,7 @@
         {
             for_less( i1, 0, n1 )
             {
-                *values = signed_byte_to_real[(long) *signed_byte_ptr];
+                *values = int_to_real_conversion[(long) *signed_byte_ptr];
                 ++values;
                 signed_byte_ptr += step1;
             }
@@ -942,7 +932,7 @@
         {
             for_less( i1, 0, n1 )
             {
-                *values = unsigned_short_to_real[(long) *unsigned_short_ptr];
+                *values = int_to_real_conversion[(long) *unsigned_short_ptr];
                 ++values;
                 unsigned_short_ptr += step1;
             }
@@ -956,7 +946,7 @@
         {
             for_less( i1, 0, n1 )
             {
-                *values = signed_short_to_real[(long) *signed_short_ptr];
+                *values = int_to_real_conversion[(long) *signed_short_ptr];
                 ++values;
                 signed_short_ptr += step1;
             }
@@ -1039,7 +1029,7 @@
     float            *float_ptr;
     double           *double_ptr;
 
-    check_real_conversion_lookup( data_type );
+    check_real_conversion_lookup();
 
     switch( data_type )
     {
@@ -1047,7 +1037,7 @@
         ASSIGN_PTR(unsigned_byte_ptr) = void_ptr;
         for_less( i0, 0, n0 )
         {
-            *values = unsigned_byte_to_real[(long) *unsigned_byte_ptr];
+            *values = int_to_real_conversion[(long) *unsigned_byte_ptr];
             ++values;
             unsigned_byte_ptr += step0;
         }
@@ -1057,7 +1047,7 @@
         ASSIGN_PTR(signed_byte_ptr) = void_ptr;
         for_less( i0, 0, n0 )
         {
-            *values = signed_byte_to_real[(long) *signed_byte_ptr];
+            *values = int_to_real_conversion[(long) *signed_byte_ptr];
             ++values;
             signed_byte_ptr += step0;
         }
@@ -1067,7 +1057,7 @@
         ASSIGN_PTR(unsigned_short_ptr) = void_ptr;
         for_less( i0, 0, n0 )
         {
-            *values = unsigned_short_to_real[(long) *unsigned_short_ptr];
+            *values = int_to_real_conversion[(long) *unsigned_short_ptr];
             ++values;
             unsigned_short_ptr += step0;
         }
@@ -1077,7 +1067,7 @@
         ASSIGN_PTR(signed_short_ptr) = void_ptr;
         for_less( i0, 0, n0 )
         {
-            *values = signed_short_to_real[(long) *signed_short_ptr];
+            *values = int_to_real_conversion[(long) *signed_short_ptr];
             ++values;
             signed_short_ptr += step0;
         }
@@ -1442,3 +1432,40 @@
     get_voxel_values( volume, void_ptr, 1 - dim, &steps[dim], &counts[dim],
                       values );
 }
+
+public  void  get_volume_voxel_hyperslab(
+    Volume   volume,
+    int      v0,
+    int      v1,
+    int      v2,
+    int      v3,
+    int      v4,
+    int      n0,
+    int      n1,
+    int      n2,
+    int      n3,
+    int      n4,
+    Real     voxels[] )
+{
+    switch( get_volume_n_dimensions(volume) )
+    {
+    case 1:
+        get_volume_voxel_hyperslab_1d( volume, v0, n0, voxels );
+        break;
+    case 2:
+        get_volume_voxel_hyperslab_2d( volume, v0, v1, n0, n1, voxels );
+        break;
+    case 3:
+        get_volume_voxel_hyperslab_3d( volume, v0, v1, v2, n0, n1, n2, voxels );
+        break;
+    case 4:
+        get_volume_voxel_hyperslab_4d( volume, v0, v1, v2, v3,
+                                       n0, n1, n2, n3, voxels );
+        break;
+    case 5:
+        get_volume_voxel_hyperslab_5d( volume, v0, v1, v2, v3, v4,
+                                       n0, n1, n2, n3, n4, voxels );
+        break;
+    }
+}
+
--- a/volume_io/Volumes/input_free.c
+++ b/volume_io/Volumes/input_free.c
@@ -16,7 +16,7 @@
 #include  <minc.h>
 
 #ifndef lint
-static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Volumes/input_free.c,v 1.27 1996-05-17 19:36:20 david Exp $";
+static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Volumes/input_free.c,v 1.28 1997-08-13 13:21:32 david Exp $";
 #endif
 
 #define  DEFAULT_SUFFIX  "fre"
@@ -37,7 +37,7 @@
               in volume_input, then the file is converted to bytes on input.
               This function assumes that volume->filename has been assigned.
 @CREATED    :                      David MacDonald
-@MODIFIED   : 
+@MODIFIED   : May  22, 1997   D. MacDonald  - now uses volume starts
 ---------------------------------------------------------------------------- */
 
 public  Status  initialize_free_format_input(
@@ -57,7 +57,6 @@
     char           ch;
     Real           file_separations[MAX_DIMENSIONS];
     Real           volume_separations[MAX_DIMENSIONS];
-    Real           origin_voxel[MAX_DIMENSIONS];
     Real           trans[N_DIMENSIONS];
     FILE           *file;
     BOOLEAN        axis_valid;
@@ -261,11 +260,8 @@
             }
         }
 
-        for_less( axis, 0, N_DIMENSIONS )
-            origin_voxel[axis] = 0.0;
-
         set_volume_separations( volume, volume_separations );
-        set_volume_translation( volume, origin_voxel, trans );
+        set_volume_starts( volume, trans );
     }
 
     set_volume_type( volume, desired_data_type, FALSE, 0.0, 0.0 );
--- a/volume_io/Volumes/input_mnc.c
+++ b/volume_io/Volumes/input_mnc.c
@@ -16,7 +16,7 @@
 #include  <minc.h>
 
 #ifndef lint
-static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Volumes/input_mnc.c,v 1.57 1997-04-17 17:25:47 david Exp $";
+static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Volumes/input_mnc.c,v 1.58 1997-08-13 13:21:33 david Exp $";
 #endif
 
 #define  INVALID_AXIS   -1
@@ -92,6 +92,9 @@
 @CALLS      : 
 @CREATED    : 1993            David MacDonald
 @MODIFIED   : Nov. 15, 1996   D. MacDonald - added handling of space type
+@MODIFIED   : May  20, 1997   D. MacDonald - removed float arithmetic in
+                                             transformations and made volumes
+                                             store starts/steps/dircos
 ---------------------------------------------------------------------------- */
 
 public  Minc_file  initialize_minc_input_from_minc_id(
@@ -117,16 +120,13 @@
     int                 sizes[MAX_VAR_DIMS];
     double              file_separations[MAX_VAR_DIMS];
     Real                volume_separations[MI_NUM_SPACE_DIMS];
+    Real                volume_starts[MI_NUM_SPACE_DIMS];
     Real                default_voxel_min, default_voxel_max;
-    Real                world_space[N_DIMENSIONS], voxel_zero;
+    Real                voxel_zero;
     double              start_position[MAX_VAR_DIMS];
     double              dir_cosines[MAX_VAR_DIMS][MI_NUM_SPACE_DIMS];
     double              tmp_cosines[MI_NUM_SPACE_DIMS];
     BOOLEAN             spatial_dim_flags[MAX_VAR_DIMS];
-    Vector              offset;
-    Point               origin;
-    Real                zero_voxel[MAX_DIMENSIONS];
-    Vector              spatial_axis;
     double              real_min, real_max;
     int                 d, dimvar, which_valid_axis, axis;
     int                 spatial_axis_indices[MAX_VAR_DIMS];
@@ -356,67 +356,30 @@
             sizes[file->to_volume_index[d]] = (int) file->sizes_in_file[d];
             volume_separations[file->to_volume_index[d]] =
                                           file_separations[d];
+
+            if( file->to_volume_index[d] != INVALID_AXIS )
+            {
+                volume_starts[file->to_volume_index[d]] = start_position[d];
+                set_volume_direction_unit_cosine( volume,
+                                file->to_volume_index[d], dir_cosines[d] );
+            }
         }
     }
 
+    /* --- create the world transform stored in the volume */
+
+    set_volume_separations( volume, volume_separations );
+    set_volume_starts( volume, volume_starts );
+
     if( space_type_consensus )
         set_volume_space_type( volume, prev_space_type );
 
     /* --- create the file world transform */
 
-    fill_Point( origin, 0.0, 0.0, 0.0 );
-
-    for_less( d, 0, MAX_DIMENSIONS )
-        zero_voxel[d] = 0.0;
-
-    for_less( d, 0, N_DIMENSIONS )
-    {
-        axis = file->spatial_axes[d];
-        if( axis != INVALID_AXIS )
-        {
-            fill_Vector( spatial_axis,
-                         dir_cosines[axis][0],
-                         dir_cosines[axis][1],
-                         dir_cosines[axis][2] );
-            NORMALIZE_VECTOR( spatial_axis, spatial_axis );
-            
-            SCALE_VECTOR( offset, spatial_axis, start_position[axis] );
-            ADD_POINT_VECTOR( origin, origin, offset );
-        }
-    }
-
-    world_space[X] = (Real) Point_x(origin);
-    world_space[Y] = (Real) Point_y(origin);
-    world_space[Z] = (Real) Point_z(origin);
-
     compute_world_transform( file->spatial_axes, file_separations,
-                             zero_voxel, world_space, dir_cosines,
+                             dir_cosines, start_position,
                              &file->voxel_to_world_transform );
 
-    /* --- create the world transform stored in the volume */
-
-    fill_Point( origin, 0.0, 0.0, 0.0 );
-
-    for_less( d, 0, file->n_file_dimensions )
-    {
-        if( file->to_volume_index[d] != INVALID_AXIS )
-        {
-            set_volume_direction_cosine( volume,
-                                         file->to_volume_index[d],
-                                         dir_cosines[d] );
-        }
-    }
-
-    general_transform_point( &file->voxel_to_world_transform,
-                             0.0, 0.0, 0.0,
-                             &world_space[X], &world_space[Y], &world_space[Z]);
-
-    for_less( d, 0, N_DIMENSIONS )
-        zero_voxel[d] = 0.0;
-
-    set_volume_translation( volume, zero_voxel, world_space );
-    set_volume_separations( volume, volume_separations );
-
     /* --- decide on type conversion */
 
     if( file->converting_to_colour )
@@ -1114,8 +1077,11 @@
 public  BOOLEAN  advance_input_volume(
     Minc_file   file )
 {
-    int   ind, c, axis;
-    Real  voxel[MAX_DIMENSIONS], world_space[N_DIMENSIONS];
+    int                 ind, c, axis;
+    Real                voxel[MAX_DIMENSIONS], world_space[N_DIMENSIONS];
+    Real                vol_world_space[N_DIMENSIONS];
+    Transform           offset;
+    General_transform   offset_transform, new_transform;
 
     ind = file->n_file_dimensions-1;
 
@@ -1139,6 +1105,8 @@
         for_less( ind, 0, get_volume_n_dimensions( file->volume ) )
             file->indices[file->valid_file_axes[ind]] = 0;
 
+        /*--- update the volume's voxel-to-world transform */
+
         for_less( c, 0, N_DIMENSIONS )
         {
             axis = file->spatial_axes[c];
@@ -1155,8 +1123,21 @@
 
         for_less( c, 0, get_volume_n_dimensions(file->volume) )
             voxel[c] = 0.0;
+        
+        convert_voxel_to_world( file->volume, voxel,
+                                &vol_world_space[X], &vol_world_space[Y],
+                                &vol_world_space[Z]);
 
-        set_volume_translation( file->volume, voxel, world_space );
+        make_identity_transform( &offset );
+        for_less( c, 0, N_DIMENSIONS )
+            Transform_elem(offset,c,3) = world_space[c] - vol_world_space[c];
+        create_linear_transform( &offset_transform, &offset );
+        concat_general_transforms( get_voxel_to_world_transform(file->volume),
+                                   &offset_transform, &new_transform );
+        set_voxel_to_world_transform( file->volume, &new_transform );
+        delete_general_transform( &offset_transform );
+
+        /*--- update the volume if it is cached */
 
         if( file->volume->is_cached_volume )
             set_cache_volume_file_offset( &file->volume->cache, file->volume,
--- a/volume_io/Volumes/output_mnc.c
+++ b/volume_io/Volumes/output_mnc.c
@@ -16,7 +16,7 @@
 #include  <minc.h>
 
 #ifndef lint
-static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Volumes/output_mnc.c,v 1.49 1997-04-17 17:26:02 david Exp $";
+static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Volumes/output_mnc.c,v 1.50 1997-08-13 13:21:34 david Exp $";
 #endif
 
 #define  INVALID_AXIS   -1
@@ -84,103 +84,124 @@
 @CALLS      : 
 @CREATED    : Oct. 24, 1995    David MacDonald
 @MODIFIED   : Nov. 15, 1996    D. MacDonald  - added handling of space type
+@MODIFIED   : May. 20, 1997    D. MacDonald  - removed float arithmetic
+@MODIFIED   : May  22, 1997   D. MacDonald - added use_volume_starts_and_steps
 ---------------------------------------------------------------------------- */
 
 private  Status  output_world_transform(
     Minc_file              file,
     STRING                 space_type,
-    General_transform      *voxel_to_world_transform )
+    General_transform      *voxel_to_world_transform,
+    BOOLEAN                use_volume_starts_and_steps_flag )
 {
-    double              separation[MAX_VAR_DIMS];
+    double              step[MAX_VAR_DIMS];
     Real                start[MAX_VAR_DIMS];
-    double              dir_cosines[MAX_VAR_DIMS][MI_NUM_SPACE_DIMS];
-    int                 i, j, d, axis;
-    Point               origin;
-    Vector              axes[MAX_DIMENSIONS];
-    Transform           transform, t, inverse;
+    double              dir_cosines[MAX_DIMENSIONS][N_DIMENSIONS];
+    int                 dim, axis, spatial_axes[N_DIMENSIONS];
 
-    if( voxel_to_world_transform == (General_transform *) NULL )
-    {
-        make_identity_transform( &transform );
-    }
-    else if( get_transform_type( voxel_to_world_transform ) == LINEAR )
+    /*--- set all starts/steps/dir_cosines to default */
+
+    for_less( dim, 0, file->n_file_dimensions )
     {
-        transform = *(get_linear_transform_ptr( voxel_to_world_transform ));
-    }
-    else
-    {
-        print_error( "Cannot output non-linear transforms.  Using identity.\n");
-        make_identity_transform( &transform );
+        start[dim] = 0.0;
+        step[dim] = 1.0;
+        dir_cosines[dim][X] = 0.0;
+        dir_cosines[dim][Y] = 0.0;
+        dir_cosines[dim][Z] = 0.0;
     }
 
-    get_transform_origin( &transform, &origin );
-    get_transform_x_axis( &transform, &axes[X] );
-    get_transform_y_axis( &transform, &axes[Y] );
-    get_transform_z_axis( &transform, &axes[Z] );
+    /*--- if must use the volume's starts and steps */
 
-    separation[X] = MAGNITUDE( axes[X] );
-    separation[Y] = MAGNITUDE( axes[Y] );
-    separation[Z] = MAGNITUDE( axes[Z] );
-    SCALE_VECTOR( axes[X], axes[X], 1.0 / separation[X] );
-    SCALE_VECTOR( axes[Y], axes[Y], 1.0 / separation[Y] );
-    SCALE_VECTOR( axes[Z], axes[Z], 1.0 / separation[Z] );
+    if( use_volume_starts_and_steps_flag )
+    {
+        for_less( dim, 0, file->n_file_dimensions )
+        {
+            if( convert_dim_name_to_spatial_axis( file->dim_names[dim], &axis ))
+            {
+                if( file->to_volume_index[dim] == INVALID_AXIS )
+                    dir_cosines[dim][axis] = 1.0;    /*--- default */
+                else
+                {
+                    start[dim] =
+                          file->volume->starts[file->to_volume_index[dim]];
+                    step[dim] =
+                          file->volume->separations[file->to_volume_index[dim]];
+                    dir_cosines[dim][X] = file->volume->direction_cosines
+                                       [file->to_volume_index[dim]][X];
+                    dir_cosines[dim][Y] = file->volume->direction_cosines
+                                       [file->to_volume_index[dim]][Y];
+                    dir_cosines[dim][Z] = file->volume->direction_cosines
+                                       [file->to_volume_index[dim]][Z];
+                }
+            }
+        }
+    }
+    else  /*--- convert the linear transform to starts/steps/dir cosines */
+    {
+        if( voxel_to_world_transform == NULL ||
+            get_transform_type( voxel_to_world_transform ) != LINEAR )
+        {
+            print_error(
+             "Cannot output null or non-linear transforms.  Using identity.\n");
 
-    for_less( i, 0, 3 )
-    {
-        if( Vector_coord(axes[i],i) < 0.0f )
+            for_less( dim, 0, file->n_file_dimensions )
+            {
+                if( convert_dim_name_to_spatial_axis( file->dim_names[dim],
+                                                      &axis ))
+                    dir_cosines[dim][axis] = 1.0;
+            }
+        }
+        else
         {
-            SCALE_VECTOR( axes[i], axes[i], -1.0 );
-            separation[i] *= -1.0;
+            spatial_axes[0] = INVALID_AXIS;
+            spatial_axes[1] = INVALID_AXIS;
+            spatial_axes[2] = INVALID_AXIS;
+
+            for_less( dim, 0, file->n_file_dimensions )
+            {
+                if( convert_dim_name_to_spatial_axis( file->dim_names[dim],
+                                                      &axis ))
+                    spatial_axes[axis] = dim;
+            }
+
+            convert_transform_to_starts_and_steps( voxel_to_world_transform,
+                                                   file->n_file_dimensions,
+                                                   NULL, spatial_axes,
+                                                   start, step,
+                                                   dir_cosines );
         }
     }
 
-    for_less( i, 0, 3 )
+    for_less( dim, 0, file->n_file_dimensions )
     {
-        for_less( j, 0, 3 )
-            dir_cosines[i][j] = (Real) Vector_coord(axes[i],j);
-    }
-
-    make_identity_transform( &t );
-    set_transform_x_axis( &t, &axes[X] );
-    set_transform_y_axis( &t, &axes[Y] );
-    set_transform_z_axis( &t, &axes[Z] );
-
-    (void) compute_transform_inverse( &t, &inverse );
+        if( convert_dim_name_to_spatial_axis( file->dim_names[dim], &axis ) )
+        {
+            file->dim_ids[dim] = micreate_std_variable( file->cdfid,
+                                      file->dim_names[dim], NC_DOUBLE, 0, NULL);
 
-    transform_point( &inverse,
-                     (Real) Point_x(origin), (Real) Point_y(origin),
-                     (Real) Point_z(origin),
-                     &start[X], &start[Y], &start[Z] );
-
-    for_less( d, 0, file->n_file_dimensions )
-    {
-        if( convert_dim_name_to_spatial_axis( file->dim_names[d], &axis ) )
-        {
-            file->dim_ids[d] = micreate_std_variable( file->cdfid,
-                                      file->dim_names[d], NC_DOUBLE, 0, NULL);
-
-            if( file->dim_ids[d] < 0 )
+            if( file->dim_ids[dim] < 0 )
                 return( ERROR );
 
-            (void) miattputdbl( file->cdfid, file->dim_ids[d], MIstep,
-                                separation[axis]);
-            (void) miattputdbl( file->cdfid, file->dim_ids[d], MIstart,
-                                start[axis]);
-            if( !is_default_direction_cosine( axis, dir_cosines[axis] ) )
+            (void) miattputdbl( file->cdfid, file->dim_ids[dim], MIstep,
+                                step[dim]);
+            (void) miattputdbl( file->cdfid, file->dim_ids[dim], MIstart,
+                                start[dim]);
+            if( !is_default_direction_cosine( axis, dir_cosines[dim] ) )
             {
-                (void) ncattput( file->cdfid, file->dim_ids[d],
+                (void) ncattput( file->cdfid, file->dim_ids[dim],
                                  MIdirection_cosines,
-                                 NC_DOUBLE, N_DIMENSIONS, dir_cosines[axis]);
+                                 NC_DOUBLE, N_DIMENSIONS, dir_cosines[dim]);
             }
-            (void) miattputstr( file->cdfid, file->dim_ids[d], MIunits, UNITS );
+            (void) miattputstr( file->cdfid, file->dim_ids[dim], MIunits,
+                                UNITS );
             if( !equal_strings( space_type, MI_UNKNOWN_SPACE ) )
             {
-                (void) miattputstr( file->cdfid, file->dim_ids[d], MIspacetype,
-                                    space_type );
+                (void) miattputstr( file->cdfid, file->dim_ids[dim],
+                                    MIspacetype, space_type );
             }
         }
         else
-            file->dim_ids[d] = -1;
+            file->dim_ids[dim] = -1;
     }
 
     return( OK );
@@ -375,7 +396,8 @@
     }
 
     if( output_world_transform( file, volume_to_attach->coordinate_system_name,
-                                voxel_to_world_transform ) != OK )
+                                voxel_to_world_transform,
+                                options->use_volume_starts_and_steps ) != OK )
     {
         FREE( file );
         return( NULL );
@@ -1556,7 +1578,7 @@
 @GLOBALS    : 
 @CALLS      : 
 @CREATED    : 1993            David MacDonald
-@MODIFIED   : 
+@MODIFIED   : May  22, 1997   D. MacDonald - added use_volume_starts_and_steps
 ---------------------------------------------------------------------------- */
 
 public  void  set_default_minc_output_options(
@@ -1569,6 +1591,9 @@
 
     options->global_image_range[0] = 0.0;
     options->global_image_range[1] = -1.0;
+
+    options->use_volume_starts_and_steps = FALSE;
+    options->use_starts_set = FALSE;
 }
 
 /* ----------------------------- MNI Header -----------------------------------
@@ -1680,3 +1705,28 @@
     options->global_image_range[0] = real_min;
     options->global_image_range[1] = real_max;
 }
+
+/* ----------------------------- MNI Header -----------------------------------
+@NAME       : set_minc_output_use_volume_starts_and_steps_flag
+@INPUT      : options
+              flag
+@OUTPUT     : 
+@RETURNS    : 
+@DESCRIPTION: Tells MINC output to use the exact starts and steps stored in
+              the volume, not the voxel-to-world-transform.  This avoids
+              round-off errors in converting to transform on input, then
+              from transform on output.
+@METHOD     : 
+@GLOBALS    : 
+@CALLS      :  
+@CREATED    : May. 22, 1997    David MacDonald
+@MODIFIED   : 
+---------------------------------------------------------------------------- */
+
+public  void  set_minc_output_use_volume_starts_and_steps_flag(
+    minc_output_options  *options,
+    BOOLEAN              flag )
+{
+    options->use_volume_starts_and_steps = flag;
+    options->use_starts_set = TRUE;
+}
--- a/volume_io/Volumes/output_volume.c
+++ b/volume_io/Volumes/output_volume.c
@@ -15,7 +15,7 @@
 #include  <internal_volume_io.h>
 
 #ifndef lint
-static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Volumes/output_volume.c,v 1.19 1995-11-17 20:25:43 david Exp $";
+static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Volumes/output_volume.c,v 1.20 1997-08-13 13:21:34 david Exp $";
 #endif
 
 /* ----------------------------- MNI Header -----------------------------------
@@ -292,7 +292,8 @@
 @GLOBALS    : 
 @CALLS      : 
 @CREATED    : 1993            David MacDonald
-@MODIFIED   : 
+@MODIFIED   : May  22, 1997   D. MacDonald - now sets
+                                            use_volume_starts_and_steps flag
 ---------------------------------------------------------------------------- */
 
 public  Status  output_modified_volume(
@@ -321,7 +322,7 @@
 
     n_dims = get_volume_n_dimensions(volume);
 
-    if( options == (minc_output_options *) NULL )
+    if( options == NULL )
         set_default_minc_output_options( &used_options );
     else
         used_options = *options;
@@ -333,6 +334,17 @@
         set_minc_output_real_range( &used_options, real_min, real_max );
     }
 
+    /*--- if the user has not explicitly set the use_volume_starts_and_steps
+          flag, let's set it if the transform is linear, to output the
+          same starts as was input, and avoid round-off error */
+
+    if( !used_options.use_starts_set &&
+        !used_options.use_volume_starts_and_steps &&
+        get_transform_type(get_voxel_to_world_transform(volume)) == LINEAR )
+    {
+        set_minc_output_use_volume_starts_and_steps_flag( &used_options, TRUE );
+    }
+
     minc_file = initialize_minc_output( filename,
                                         n_dims, dim_names, sizes,
                                         file_nc_data_type, file_signed_flag,
@@ -340,7 +352,7 @@
                                         get_voxel_to_world_transform(volume),
                                         volume, &used_options );
 
-    if( minc_file == (Minc_file) NULL )
+    if( minc_file == NULL )
         return( ERROR );
 
     status = copy_volume_auxiliary_and_history( minc_file, filename,
--- a/volume_io/Volumes/volumes.c
+++ b/volume_io/Volumes/volumes.c
@@ -17,7 +17,7 @@
 #include  <float.h>
 
 #ifndef lint
-static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Volumes/volumes.c,v 1.65 1996-12-09 20:20:27 david Exp $";
+static char rcsid[] = "$Header: /private-cvsroot/minc/volume_io/Volumes/volumes.c,v 1.66 1997-08-13 13:21:33 david Exp $";
 #endif
 
 STRING   XYZ_dimension_names[] = { MIxspace, MIyspace, MIzspace };
@@ -136,6 +136,7 @@
 @CALLS      : 
 @CREATED    : June, 1993       David MacDonald
 @MODIFIED   : Nov. 15, 1996    D. MacDonald    - handles space type
+@MODIFIED   : May  22, 1996    D. MacDonald    - now stores starts/steps
 ---------------------------------------------------------------------------- */
 
 public   Volume   create_volume(
@@ -177,14 +178,11 @@
     volume->real_value_translation = 0.0;
 
     for_less( i, 0, N_DIMENSIONS )
-    {
-        volume->world_space_for_translation_voxel[i] = 0.0;
         volume->spatial_axes[i] = -1;
-    }
 
     for_less( i, 0, n_dimensions )
     {
-        volume->translation_voxel[i] = 0.0;
+        volume->starts[i] = 0.0;
         volume->separations[i] = 1.0;
         volume->direction_cosines[i][X] = 0.0;
         volume->direction_cosines[i][Y] = 0.0;
@@ -213,6 +211,7 @@
 
     make_identity_transform( &identity );
     create_linear_transform( &volume->voxel_to_world_transform, &identity );
+    volume->voxel_to_world_transform_uptodate = TRUE;
 
     volume->coordinate_system_name = create_string( MI_UNKNOWN_SPACE );
 
@@ -571,76 +570,122 @@
 
     get_volume_sizes( volume, sizes );
 
-    for_less( i, 0, get_multidim_n_dimensions( &volume->array ) )
+    for_less( i, 0, get_volume_n_dimensions( volume ) )
         n *= (unsigned int) sizes[i];
 
     return( n );
 }
 
 /* ----------------------------- MNI Header -----------------------------------
-@NAME       : set_voxel_to_world_transform
+@NAME       : assign_voxel_to_world_transform
 @INPUT      : volume
               transform
 @OUTPUT     : 
 @RETURNS    : 
-@DESCRIPTION: Sets the volume's transformation from voxel to world coords.
+@DESCRIPTION: Updates the volume's transformation from voxel to world coords.
 @METHOD     : 
 @GLOBALS    : 
 @CALLS      : 
-@CREATED    : 1993            David MacDonald
+@CREATED    : May  20, 1997    D. MacDonald - created from
+                                          set_voxel_to_world_transform
 @MODIFIED   : 
 ---------------------------------------------------------------------------- */
 
-public  void  set_voxel_to_world_transform(
+private  void  assign_voxel_to_world_transform(
     Volume             volume,
     General_transform  *transform )
 {
-    Real        sign;
-    int         c, axis;
-    Vector      axes[N_DIMENSIONS];
-    Transform   *linear_transform;
-
     delete_general_transform( &volume->voxel_to_world_transform );
 
     volume->voxel_to_world_transform = *transform;
-
-    if( get_transform_type( transform ) == LINEAR )
-    {
-        linear_transform = get_linear_transform_ptr( transform );
-        get_transform_x_axis( linear_transform, &axes[X] );
-        get_transform_y_axis( linear_transform, &axes[Y] );
-        get_transform_z_axis( linear_transform, &axes[Z] );
+}
 
-        for_less( c, 0, N_DIMENSIONS )
-        {
-            axis = volume->spatial_axes[c];
-            if( volume->separations[axis] < 0.0 )
-                sign = -1.0;
-            else
-                sign = 1.0;
-            if( axis >= 0 )
-                volume->separations[axis] = sign * MAGNITUDE( axes[c] );
-        }
-    }
+/* ----------------------------- MNI Header -----------------------------------
+@NAME       : dot_vectors
+@INPUT      : n
+              v1
+              v2
+@OUTPUT     :
+@RETURNS    : Dot product
+@DESCRIPTION: Computes the dot product of 2 n-dimensional vectors.
+              This function should be moved to some vector routines.
+@METHOD     :
+@GLOBALS    :
+@CALLS      :
+@CREATED    :         1993    David MacDonald
+@MODIFIED   :
+---------------------------------------------------------------------------- */
+
+private  Real   dot_vectors(
+    int    n,
+    Real   v1[],
+    Real   v2[] )
+{
+    int   i;
+    Real  d;
+
+    d = 0.0;
+    for_less( i, 0, n )
+        d += v1[i] * v2[i];
+
+    return( d );
 }
 
 /* ----------------------------- MNI Header -----------------------------------
-@NAME       : get_voxel_to_world_transform
-@INPUT      : 
-@OUTPUT     : 
-@RETURNS    : transform
-@DESCRIPTION: Returns a pointer to the voxel to world transform of the volume.
-@METHOD     : 
-@GLOBALS    : 
-@CALLS      : 
-@CREATED    : 1993            David MacDonald
-@MODIFIED   : 
+@NAME       : cross_3D_vector
+@INPUT      : v1
+              v2
+@OUTPUT     : cross
+@RETURNS    : 
+@DESCRIPTION: Computes the cross product of 2 n-dimensional vectors.
+              This function should be moved to some vector routines.
+@METHOD     :
+@GLOBALS    :
+@CALLS      :
+@CREATED    :         1993    David MacDonald
+@MODIFIED   :
 ---------------------------------------------------------------------------- */
 
-public  General_transform  *get_voxel_to_world_transform(
-    Volume   volume )
+private  void   cross_3D_vector(
+    Real   v1[],
+    Real   v2[],
+    Real   cross[] )
 {
-    return( &volume->voxel_to_world_transform );
+    cross[0] = v1[1] * v2[2] - v1[2] * v2[1];
+    cross[1] = v1[2] * v2[0] - v1[0] * v2[2];
+    cross[2] = v1[0] * v2[1] - v1[1] * v2[0];
+}
+
+/* ----------------------------- MNI Header -----------------------------------
+@NAME       : normalize_vector
+@INPUT      : n
+              v1
+@OUTPUT     : v1_normalized
+@RETURNS    : 
+@DESCRIPTION: Normalizes the length of v1 to 1, placing result in
+              v1_normalized
+@METHOD     :
+@GLOBALS    :
+@CALLS      :
+@CREATED    : May  22, 1997   D. MacDonald
+@MODIFIED   :
+---------------------------------------------------------------------------- */
+
+private  void   normalize_vector(
+    Real   v1[],
+    Real   v1_normalized[] )
+{
+    int    d;
+    Real   mag;
+
+    mag = dot_vectors( N_DIMENSIONS, v1, v1 );
+    if( mag <= 0.0 )
+        mag = 1.0;
+
+    mag = sqrt( mag );
+
+    for_less( d, 0, N_DIMENSIONS )
+        v1_normalized[d] = v1[d] / mag;
 }
 
 /* ----------------------------- MNI Header -----------------------------------
@@ -660,24 +705,26 @@
 @GLOBALS    : 
 @CALLS      : 
 @CREATED    : 1993            David MacDonald
-@MODIFIED   : 
+@MODIFIED   : May  22, 1997   D. MacDonald - now uses starts/steps
 ---------------------------------------------------------------------------- */
 
 public  void  compute_world_transform(
     int                 spatial_axes[N_DIMENSIONS],
     Real                separations[],
-    Real                translation_voxel[],
-    Real                world_space_for_translation_voxel[N_DIMENSIONS],
     Real                direction_cosines[][N_DIMENSIONS],
+    Real                starts[],
     General_transform   *world_transform )
 {
     Transform                transform;
     Real                     separations_3D[N_DIMENSIONS];
-    Vector                   directions[N_DIMENSIONS];
-    Point                    point;
-    Real                     x_trans, y_trans, z_trans;
-    Real                     voxel[N_DIMENSIONS];
-    int                      c, axis, n_axes, axis_list[N_DIMENSIONS];
+    Real                     directions[N_DIMENSIONS][N_DIMENSIONS];
+    Real                     starts_3D[N_DIMENSIONS];
+    Real                     normal[N_DIMENSIONS];
+    int                      dim, c, a1, a2, axis, n_axes;
+    int                      axis_list[N_DIMENSIONS];
+
+    /*--- find how many direction cosines are specified, and set the
+          3d separations and starts */
 
     n_axes = 0;
 
@@ -687,19 +734,17 @@
         if( axis >= 0 )
         {
             separations_3D[c] = separations[axis];
-            voxel[c] = translation_voxel[axis];
-            fill_Vector( directions[c],
-                         direction_cosines[axis][X],
-                         direction_cosines[axis][Y],
-                         direction_cosines[axis][Z]);
-
+            starts_3D[c] = starts[axis];
+            directions[c][X] = direction_cosines[axis][X];
+            directions[c][Y] = direction_cosines[axis][Y];
+            directions[c][Z] = direction_cosines[axis][Z];
             axis_list[n_axes] = c;
             ++n_axes;
         }
         else
         {
             separations_3D[c] = 1.0;
-            voxel[c] = 0.0;
+            starts_3D[c] = 0.0;
         }
     }
 
@@ -709,55 +754,81 @@
         return;
     }
 
+    /*--- convert 1 or 2 axes to 3 axes */
+
     if( n_axes == 1 )
     {
-        create_two_orthogonal_vectors( &directions[axis_list[0]],
-                            &directions[(axis_list[0]+1) % N_DIMENSIONS],
-                            &directions[(axis_list[0]+2) % N_DIMENSIONS] );
+        a1 = (axis_list[0] + 1) % N_DIMENSIONS;
+        a2 = (axis_list[0] + 2) % N_DIMENSIONS;
+
+        /*--- create an orthogonal vector */
+
+        directions[a1][X] = directions[axis_list[0]][Y] +
+                            directions[axis_list[0]][Z];
+        directions[a1][Y] = -directions[axis_list[0]][X] -
+                            directions[axis_list[0]][Z];
+        directions[a1][Z] = directions[axis_list[0]][Y] -
+                            directions[axis_list[0]][X];
+
+        cross_3D_vector( directions[axis_list[0]], directions[a1],
+                         directions[a2] );
+        normalize_vector( directions[a1], directions[a1] );
+        normalize_vector( directions[a2], directions[a2] );
     }
     else if( n_axes == 2 )
     {
-        axis = N_DIMENSIONS - axis_list[0] - axis_list[1];
+        a2 = N_DIMENSIONS - axis_list[0] - axis_list[1];
 
-        CROSS_VECTORS( directions[axis],
-                       directions[(axis+1) % N_DIMENSIONS],
-                       directions[(axis+2) % N_DIMENSIONS] );
+        cross_3D_vector( directions[axis_list[0]], directions[axis_list[1]],
+               directions[a2] );
+
+        normalize_vector( directions[a2], directions[a2] );
     }
 
-    if( EQUAL_VECTORS( directions[0], directions[1] ) ||
-        EQUAL_VECTORS( directions[1], directions[2] ) ||
-        EQUAL_VECTORS( directions[2], directions[0] ) )
+    /*--- check to make sure that 3 axes are not a singular system */
+
+    for_less( dim, 0, N_DIMENSIONS )
+    {
+        cross_3D_vector( directions[dim], directions[(dim+1)%N_DIMENSIONS],
+                         normal );
+        if( normal[0] == 0.0 && normal[1] == 0.0 && normal[2] == 0.0 )
+            break;
+    }
+
+    if( dim < N_DIMENSIONS )
     {
-        fill_Vector( directions[0], 1.0, 0.0, 0.0 );
-        fill_Vector( directions[1], 0.0, 1.0, 0.0 );
-        fill_Vector( directions[2], 0.0, 0.0, 1.0 );
+        directions[0][0] = 1.0;
+        directions[0][1] = 0.0;
+        directions[0][2] = 0.0;
+        directions[1][0] = 0.0;
+        directions[1][1] = 1.0;
+        directions[1][2] = 0.0;
+        directions[2][0] = 0.0;
+        directions[2][1] = 0.0;
+        directions[2][2] = 1.0;
     }
 
+    /*--- make the linear transformation */
+
+    make_identity_transform( &transform );
+
     for_less( c, 0, N_DIMENSIONS )
     {
-        NORMALIZE_VECTOR( directions[c], directions[c] );
-        SCALE_VECTOR( directions[c], directions[c], separations_3D[c] );
-    }
-
-    fill_Point( point, 0.0, 0.0, 0.0 );
-
-    make_change_to_bases_transform( &point,
-              &directions[X], &directions[Y], &directions[Z], &transform );
+        for_less( dim, 0, N_DIMENSIONS )
+        {
+            Transform_elem(transform,dim,c) = directions[c][dim] *
+                                              separations_3D[c];
 
-    transform_point( &transform, voxel[X], voxel[Y], voxel[Z],
-                     &x_trans, &y_trans, &z_trans );
-
-    fill_Point( point, world_space_for_translation_voxel[X] - x_trans,
-                       world_space_for_translation_voxel[Y] - y_trans,
-                       world_space_for_translation_voxel[Z] - z_trans );
-
-    set_transform_origin( &transform, &point );
+            Transform_elem(transform,dim,3) += directions[c][dim] *
+                                               starts_3D[c];
+        }
+    }
 
     create_linear_transform( world_transform, &transform );
 }
 
 /* ----------------------------- MNI Header -----------------------------------
-@NAME       : recompute_world_transform
+@NAME       : check_recompute_world_transform
 @INPUT      : volume
 @OUTPUT     : 
 @RETURNS    : 
@@ -767,24 +838,277 @@
 @GLOBALS    : 
 @CALLS      : 
 @CREATED    : 1993            David MacDonald
-@MODIFIED   : 
+@MODIFIED   : May  20, 1997   D. MacDonald - now checks update flag
 ---------------------------------------------------------------------------- */
 
-private  void  recompute_world_transform(
+private  void  check_recompute_world_transform(
     Volume  volume )
 {
     General_transform        world_transform;
-    Real                     separations[MAX_DIMENSIONS];
+
+    if( !volume->voxel_to_world_transform_uptodate )
+    {
+        volume->voxel_to_world_transform_uptodate = TRUE;
+
+        compute_world_transform( volume->spatial_axes,
+                                 volume->separations,
+                                 volume->direction_cosines,
+                                 volume->starts,
+                                 &world_transform );
+
+        assign_voxel_to_world_transform( volume, &world_transform );
+    }
+}
+
+/* ----------------------------- MNI Header -----------------------------------
+@NAME       : convert_transform_origin_to_starts
+@INPUT      : origin
+              n_volume_dimensions
+              spatial_axes
+              dir_cosines
+@OUTPUT     : starts
+@RETURNS    : 
+@DESCRIPTION: Converts a transform origin into starts (multiples of the
+              dir_cosines).  dir_cosines need not be mutually orthogonal
+@METHOD     : 
+@GLOBALS    : 
+@CALLS      :  
+@CREATED    : May. 22, 1997    David MacDonald
+@MODIFIED   : 
+---------------------------------------------------------------------------- */
+
+private  void  convert_transform_origin_to_starts(
+    Real               origin[],
+    int                n_volume_dimensions,
+    int                spatial_axes[],
+    Real               dir_cosines[][N_DIMENSIONS],
+    Real               starts[] )
+{
+    int         axis, dim, which[N_DIMENSIONS], n_axes, i, j;
+    Real        o_dot_c, c_dot_c;
+    Real        x_dot_x, x_dot_y, x_dot_v, y_dot_y, y_dot_v, bottom;
+    Real        **matrix, solution[N_DIMENSIONS];
+
+    for_less( dim, 0, n_volume_dimensions )
+        starts[dim] = 0.0;
+
+    /*--- get the list of valid axes (which) */
 
-    get_volume_separations( volume, separations );
+    n_axes = 0;
+    for_less( dim, 0, N_DIMENSIONS )
+    {
+        axis = spatial_axes[dim];
+        if( axis >= 0 )
+        {
+            which[n_axes] = axis;
+            ++n_axes;
+        }
+    }
+
+    /*--- get the starts: computed differently for 1, 2, or 3 axes */
+
+    if( n_axes == 1 )
+    {
+        o_dot_c = dot_vectors( N_DIMENSIONS, origin, dir_cosines[which[0]] );
+        c_dot_c = dot_vectors( N_DIMENSIONS, dir_cosines[which[0]],
+                                     dir_cosines[which[0]] );
+
+        if( c_dot_c != 0.0 )
+            starts[which[0]] = o_dot_c / c_dot_c;
+    }
+    else if( n_axes == 2 )
+    {
+        x_dot_x = dot_vectors( N_DIMENSIONS, dir_cosines[which[0]],
+                                     dir_cosines[which[0]] );
+        x_dot_v = dot_vectors( N_DIMENSIONS, dir_cosines[which[0]], origin );
+        x_dot_y = dot_vectors( N_DIMENSIONS, dir_cosines[which[0]],
+                                     dir_cosines[which[1]] );
+        y_dot_y = dot_vectors( N_DIMENSIONS, dir_cosines[which[1]],
+                                     dir_cosines[which[1]] );
+        y_dot_v = dot_vectors( N_DIMENSIONS, dir_cosines[which[1]], origin );
+
+        bottom = x_dot_x * y_dot_y - x_dot_y * x_dot_y;
+
+        if( bottom != 0.0 )
+        {
+            starts[which[0]] = (x_dot_v * y_dot_y - x_dot_y * y_dot_v) / bottom;
+            starts[which[1]] = (y_dot_v * x_dot_x - x_dot_y * x_dot_v) / bottom;
+        }
+    }
+    else if( n_axes == 3 )
+    {
+        /*--- this is the usual case, solve the equations to find what
+              starts give the desired origin */
+
+        ALLOC2D( matrix, N_DIMENSIONS, N_DIMENSIONS );
+
+        for_less( i, 0, N_DIMENSIONS )
+        for_less( j, 0, N_DIMENSIONS )
+        {
+            matrix[i][j] = dir_cosines[which[j]][i];
+        }
+
+        if( solve_linear_system( N_DIMENSIONS, matrix, origin, solution ) )
+        {
+            starts[which[0]] = solution[0];
+            starts[which[1]] = solution[1];
+            starts[which[2]] = solution[2];
+        }
+
+        FREE2D( matrix );
+    }
+    else
+    {
+        print_error(
+          "Invalid number of axes in convert_transform_origin_to_starts\n");
+    }
+}
 
-    compute_world_transform( volume->spatial_axes, separations,
-                             volume->translation_voxel,
-                             volume->world_space_for_translation_voxel,
-                             volume->direction_cosines,
-                             &world_transform );
+/* ----------------------------- MNI Header -----------------------------------
+@NAME       : convert_transform_to_starts_and_steps
+@INPUT      : transform
+              separation_signs
+@OUTPUT     : starts
+              steps
+              dir_cosines
+              spatial_axes
+@RETURNS    : 
+@DESCRIPTION: Converts a linear transform to a set of 3 starts, 3 steps,
+              and 3 direction cosines.  The separation signs determine
+              the desired signs of each of the separations.
+@METHOD     : 
+@GLOBALS    : 
+@CALLS      :  
+@CREATED    : May. 20, 1997    David MacDonald
+@MODIFIED   : 
+---------------------------------------------------------------------------- */
+
+public  void  convert_transform_to_starts_and_steps(
+    General_transform  *transform,
+    int                n_volume_dimensions,
+    Real               step_signs[],
+    int                spatial_axes[],
+    Real               starts[],
+    Real               steps[],
+    Real               dir_cosines[][N_DIMENSIONS] )
+{
+    Real        sign, mag;
+    int         axis, dim;
+    Real        axes[N_DIMENSIONS][N_DIMENSIONS];
+    Real        origin[N_DIMENSIONS];
+    Transform   *linear_transform;
+
+    if( get_transform_type( transform ) != LINEAR )
+    {
+        print_error( "convert_transform_to_starts_and_steps(): non-linear transform found.\n" );
+        return;
+    }
+
+    linear_transform = get_linear_transform_ptr( transform );
+
+    get_transform_origin_real( linear_transform, origin );
+    get_transform_x_axis_real( linear_transform, &axes[X][0] );
+    get_transform_y_axis_real( linear_transform, &axes[Y][0] );
+    get_transform_z_axis_real( linear_transform, &axes[Z][0] );
+
+    /*--- assign default steps */
+
+    for_less( dim, 0, n_volume_dimensions )
+        steps[dim] = 1.0;
+
+    /*--- assign the steps and dir_cosines for the spatial axes */
+
+    for_less( dim, 0, N_DIMENSIONS )
+    {
+        axis = spatial_axes[dim];
+        if( axis >= 0 )
+        {
+            mag = dot_vectors( N_DIMENSIONS, axes[dim], axes[dim] );
+
+            if( mag <= 0.0 )
+                mag = 1.0;
+            mag = sqrt( mag );
 
-    set_voxel_to_world_transform( volume, &world_transform );
+            if( step_signs == NULL )
+            {
+                if( axes[dim][dim] < 0.0 )
+                    sign = -1.0;
+                else
+                    sign = 1.0;
+            }
+            else  /*--- make the sign of steps match the step_signs passed in */
+            {
+                if( step_signs[axis] < 0.0 )
+                    sign = -1.0;
+                else
+                    sign = 1.0;
+            }
+
+            steps[axis] = sign * mag;
+            dir_cosines[axis][X] = axes[dim][X] / steps[axis];
+            dir_cosines[axis][Y] = axes[dim][Y] / steps[axis];
+            dir_cosines[axis][Z] = axes[dim][Z] / steps[axis];
+        }
+    }
+
+    /*--- finally, get the starts */
+
+    convert_transform_origin_to_starts( origin, n_volume_dimensions,
+                                        spatial_axes, dir_cosines, starts );
+}
+
+/* ----------------------------- MNI Header -----------------------------------
+@NAME       : set_voxel_to_world_transform
+@INPUT      : volume
+              transform
+@OUTPUT     : 
+@RETURNS    : 
+@DESCRIPTION: Sets the volume's transformation from voxel to world coords.
+@METHOD     : 
+@GLOBALS    : 
+@CALLS      : 
+@CREATED    : 1993            David MacDonald
+@MODIFIED   : May  22, 1997   D. MacDonald - recomputes the starts/steps
+---------------------------------------------------------------------------- */
+
+public  void  set_voxel_to_world_transform(
+    Volume             volume,
+    General_transform  *transform )
+{
+    assign_voxel_to_world_transform( volume, transform );
+    volume->voxel_to_world_transform_uptodate = TRUE;
+
+    if( get_transform_type( transform ) == LINEAR )
+    {
+        convert_transform_to_starts_and_steps( transform,
+                                               get_volume_n_dimensions(volume),
+                                               volume->separations,
+                                               volume->spatial_axes,
+                                               volume->starts,
+                                               volume->separations,
+                                               volume->direction_cosines );
+    }
+}
+
+/* ----------------------------- MNI Header -----------------------------------
+@NAME       : get_voxel_to_world_transform
+@INPUT      : 
+@OUTPUT     : 
+@RETURNS    : transform
+@DESCRIPTION: Returns a pointer to the voxel to world transform of the volume.
+@METHOD     : 
+@GLOBALS    : 
+@CALLS      : 
+@CREATED    : 1993            David MacDonald
+@MODIFIED   : May  22, 1997   D. MacDonald - now delays recomputing transform
+---------------------------------------------------------------------------- */
+
+public  General_transform  *get_voxel_to_world_transform(
+    Volume   volume )
+{
+    check_recompute_world_transform( volume );
+
+    return( &volume->voxel_to_world_transform );
 }
 
 /* ----------------------------- MNI Header -----------------------------------
@@ -917,7 +1241,7 @@
 {
     int   i;
 
-    for_less( i, 0, get_multidim_n_dimensions( &volume->array ) )
+    for_less( i, 0, get_volume_n_dimensions( volume ) )
         separations[i] = volume->separations[i];
 }
 
@@ -932,7 +1256,7 @@
 @GLOBALS    : 
 @CALLS      : 
 @CREATED    : 1993            David MacDonald
-@MODIFIED   : 
+@MODIFIED   : May  22, 1997   D. MacDonald - now delays recomputing transform
 ---------------------------------------------------------------------------- */
 
 public  void  set_volume_separations(
@@ -941,74 +1265,108 @@
 {
     int   i;
 
-    for_less( i, 0, get_multidim_n_dimensions( &volume->array ) )
+    for_less( i, 0, get_volume_n_dimensions( volume ) )
         volume->separations[i] = separations[i];
 
-    recompute_world_transform( volume );
+    volume->voxel_to_world_transform_uptodate = FALSE;
 }
 
 /* ----------------------------- MNI Header -----------------------------------
-@NAME       : set_volume_translation
+@NAME       : set_volume_starts
 @INPUT      : volume
-              voxel
-              world_space_voxel_maps_to
+              starts[]
 @OUTPUT     : 
 @RETURNS    : 
 @DESCRIPTION: Sets the translation portion of the voxel to world transform,
-              by specifying a point in voxel space (voxel), and a point
-              in world space (world_space_voxel_maps_to).
+              by specifying the start vector, as specified by the MINC format.
 @METHOD     : 
 @GLOBALS    : 
 @CALLS      : 
-@CREATED    : 1993            David MacDonald
-@MODIFIED   : 
+@CREATED    : May  20, 1997   David MacDonald
+@MODIFIED   : May  22, 1997   D. MacDonald - now delays recomputing transform
 ---------------------------------------------------------------------------- */
 
-public  void  set_volume_translation(
+public  void  set_volume_starts(
     Volume  volume,
-    Real    voxel[],
-    Real    world_space_voxel_maps_to[] )
+    Real    starts[] )
 {
     int  c;
 
-    for_less( c, 0, get_multidim_n_dimensions( &volume->array ) )
-        volume->translation_voxel[c] = voxel[c];
+    for_less( c, 0, get_volume_n_dimensions( volume ) )
+        volume->starts[c] = starts[c];
+
+    volume->voxel_to_world_transform_uptodate = FALSE;
+}
 
-    for_less( c, 0, N_DIMENSIONS )
-        volume->world_space_for_translation_voxel[c] =
-                               world_space_voxel_maps_to[c];
+/* ----------------------------- MNI Header -----------------------------------
+@NAME       : get_volume_starts
+@INPUT      : volume
+@OUTPUT     : starts
+@RETURNS    : 
+@DESCRIPTION: Passes back the start vector of the volume.
+@METHOD     : 
+@GLOBALS    : 
+@CALLS      : 
+@CREATED    : May  20, 1997   David MacDonald
+@MODIFIED   : 
+---------------------------------------------------------------------------- */
 
-    recompute_world_transform( volume );
+public  void  get_volume_starts(
+    Volume  volume,
+    Real    starts[] )
+{
+    int  c;
+
+    for_less( c, 0, get_volume_n_dimensions( volume ) )
+        starts[c] = volume->starts[c];
 }
 
 /* ----------------------------- MNI Header -----------------------------------
-@NAME       : get_volume_translation
+@NAME       : set_volume_direction_unit_cosine
 @INPUT      : volume
-@OUTPUT     : voxel
-              world_space_voxel_maps_to
+              axis
+              dir
+@OUTPUT     : 
 @RETURNS    : 
-@DESCRIPTION: Passes back the translation portion of the voxel to world
-              transform of the volume.
+@DESCRIPTION: Sets the direction cosine for one axis, assumed to be unit
+              length.
 @METHOD     : 
 @GLOBALS    : 
 @CALLS      : 
-@CREATED    : 1993            David MacDonald
-@MODIFIED   : 
+@CREATED    : May  20, 1997     David MacDonald
 ---------------------------------------------------------------------------- */
 
-public  void  get_volume_translation(
-    Volume  volume,
-    Real    voxel[],
-    Real    world_space_voxel_maps_to[] )
+public  void  set_volume_direction_unit_cosine(
+    Volume   volume,
+    int      axis,
+    Real     dir[] )
 {
-    int  c;
+    int    dim;
+
+    if( axis < 0 || axis >= get_volume_n_dimensions(volume) )
+    {
+        print_error(
+         "set_volume_direction_cosine:  cannot set dir cosine for axis %d\n",
+          axis );
+        return;
+    }
 
-    for_less( c, 0, get_multidim_n_dimensions( &volume->array ) )
-        voxel[c] = volume->translation_voxel[c];
+    /*--- check if this is a spatial axis */
+
+    for_less( dim, 0, N_DIMENSIONS )
+    {
+        if( volume->spatial_axes[dim] == axis )
+            break;
+    }
 
-    for_less( c, 0, N_DIMENSIONS )
-        world_space_voxel_maps_to[c] =
-                 volume->world_space_for_translation_voxel[c];
+    if( dim == N_DIMENSIONS )   /* this is not a spatial axis, ignore the dir */
+        return;
+
+    volume->direction_cosines[axis][X] = dir[X];
+    volume->direction_cosines[axis][Y] = dir[Y];
+    volume->direction_cosines[axis][Z] = dir[Z];
+
+    volume->voxel_to_world_transform_uptodate = FALSE;
 }
 
 /* ----------------------------- MNI Header -----------------------------------
@@ -1023,7 +1381,8 @@
 @GLOBALS    : 
 @CALLS      : 
 @CREATED    : 1993            David MacDonald
-@MODIFIED   : 
+@MODIFIED   : May  20, 1997   D. MacDonald    - split into
+                                            set_volume_direction_unit_cosine
 ---------------------------------------------------------------------------- */
 
 public  void  set_volume_direction_cosine(
@@ -1031,29 +1390,7 @@
     int      axis,
     Real     dir[] )
 {
-    int    d;
-    Real   len;
-
-    if( axis < 0 || axis >= get_volume_n_dimensions(volume) )
-    {
-        print_error(
-         "set_volume_direction_cosine:  cannot set dir cosine for axis %d\n",
-          axis );
-        return;
-    }
-
-    for_less( d, 0, N_DIMENSIONS )
-    {
-        if( volume->spatial_axes[d] == axis )
-            break;
-    }
-
-    if( d == N_DIMENSIONS )   /* this is not a spatial axis, ignore the dir */
-        return;
-
-    volume->direction_cosines[axis][X] = dir[X];
-    volume->direction_cosines[axis][Y] = dir[Y];
-    volume->direction_cosines[axis][Z] = dir[Z];
+    Real   len, unit_vector[N_DIMENSIONS];
 
     len = dir[X] * dir[X] + dir[Y] * dir[Y] + dir[Z] * dir[Z];
 
@@ -1063,15 +1400,16 @@
         return;
     }
 
-    if( len != 1.0 )
-    {
-        len = sqrt( len );
-        volume->direction_cosines[axis][X] /= len;
-        volume->direction_cosines[axis][Y] /= len;
-        volume->direction_cosines[axis][Z] /= len;
-    }
+    if( len <= 0.0 )
+        len = 1.0;
+
+    len = sqrt( len );
 
-    recompute_world_transform( volume );
+    unit_vector[X] = dir[X] / len;
+    unit_vector[Y] = dir[Y] / len;
+    unit_vector[Z] = dir[Z] / len;
+
+    set_volume_direction_unit_cosine( volume, axis, unit_vector );
 }
 
 /* ----------------------------- MNI Header -----------------------------------
@@ -1111,11 +1449,17 @@
     }
 
     if( d == N_DIMENSIONS )   /* this is not a spatial axis, ignore the dir */
-        return;
-
-    dir[X] = volume->direction_cosines[axis][X];
-    dir[Y] = volume->direction_cosines[axis][Y];
-    dir[Z] = volume->direction_cosines[axis][Z];
+    {
+        dir[X] = 0.0;
+        dir[Y] = 0.0;
+        dir[Z] = 0.0;
+    }
+    else
+    {
+        dir[X] = volume->direction_cosines[axis][X];
+        dir[Y] = volume->direction_cosines[axis][Y];
+        dir[Z] = volume->direction_cosines[axis][Z];
+    }
 }
 
 /* ----------------------------- MNI Header -----------------------------------
@@ -1198,7 +1542,7 @@
               Note that centre of first voxel corresponds to (0.0,0.0,0.0) in
               voxel coordinates.
 @CREATED    : Mar   1993           David MacDonald
-@MODIFIED   : 
+@MODIFIED   : May  22, 1997   D. MacDonald - checks to recompute transform
 ---------------------------------------------------------------------------- */
 
 public  void  convert_voxel_to_world(
@@ -1210,6 +1554,8 @@
 {
     Real   xyz[N_DIMENSIONS];
 
+    check_recompute_world_transform( volume );
+
     reorder_voxel_to_xyz( volume, voxel, xyz );
 
     /* apply linear transform */
@@ -1275,7 +1621,7 @@
               vector is a normal vector (ie. a derivative), so transforms by
               transpose of inverse transform.
 @CREATED    : Mar   1993           David MacDonald
-@MODIFIED   :
+@MODIFIED   : May  22, 1997   D. MacDonald - checks to recompute transform
 ---------------------------------------------------------------------------- */
 
 public  void  convert_voxel_normal_vector_to_world(
@@ -1288,7 +1634,7 @@
     Real        xyz[N_DIMENSIONS];
     Transform   *inverse;
 
-    reorder_voxel_to_xyz( volume, voxel_vector, xyz );
+    check_recompute_world_transform( volume );
 
     if( get_transform_type( &volume->voxel_to_world_transform ) != LINEAR )
         handle_internal_error( "Cannot get normal vector of nonlinear xforms.");
@@ -1298,6 +1644,8 @@
 
     /* transform vector by transpose of inverse transformation */
 
+    reorder_voxel_to_xyz( volume, voxel_vector, xyz );
+
     *x_world = Transform_elem(*inverse,0,0) * xyz[X] +
                Transform_elem(*inverse,1,0) * xyz[Y] +
                Transform_elem(*inverse,2,0) * xyz[Z];
@@ -1386,7 +1734,7 @@
 @RETURNS    : 
 @DESCRIPTION: Converts from world coordinates to voxel coordinates.
 @CREATED    : Mar   1993           David MacDonald
-@MODIFIED   : 
+@MODIFIED   : May  22, 1997   D. MacDonald - checks to recompute transform
 ---------------------------------------------------------------------------- */
 
 public  void  convert_world_to_voxel(
@@ -1398,7 +1746,7 @@
 {
     Real   xyz[N_DIMENSIONS];
 
-    /* apply linear transform */
+    check_recompute_world_transform( volume );
 
     general_inverse_transform_point( &volume->voxel_to_world_transform,
                                      x_world, y_world, z_world,
@@ -1738,11 +2086,9 @@
 {
     int                c, sizes[MAX_DIMENSIONS];
     Real               separations[MAX_DIMENSIONS];
+    Real               starts[MAX_DIMENSIONS];
+    Real               dir_cosine[N_DIMENSIONS];
     Volume             copy;
-    General_transform  transform;
-
-    get_volume_sizes( volume, sizes );
-    get_volume_separations( volume, separations );
 
     if( nc_data_type == NC_UNSPECIFIED )
     {
@@ -1754,7 +2100,6 @@
     copy = create_volume( get_volume_n_dimensions(volume),
                           volume->dimension_names, nc_data_type, signed_flag,
                           voxel_min, voxel_max );
-    set_volume_sizes( copy, sizes );
 
     for_less( c, 0, N_DIMENSIONS )
         copy->spatial_axes[c] = volume->spatial_axes[c];
@@ -1763,15 +2108,20 @@
                            get_volume_real_min(volume),
                            get_volume_real_max(volume) );
 
+    get_volume_sizes( volume, sizes );
+    set_volume_sizes( copy, sizes );
+
+    get_volume_separations( volume, separations );
     set_volume_separations( copy, separations );
 
+    get_volume_starts( volume, starts );
+    set_volume_starts( copy, starts );
+
     for_less( c, 0, get_volume_n_dimensions(volume) )
-        set_volume_direction_cosine( copy, c, volume->direction_cosines[c] );
-
-    copy_general_transform( get_voxel_to_world_transform(volume),
-                            &transform );
-
-    set_voxel_to_world_transform( copy, &transform );
+    {
+        get_volume_direction_cosine( volume, c, dir_cosine );
+        set_volume_direction_unit_cosine( copy, c, dir_cosine );
+    }
 
     set_volume_space_type( copy, volume->coordinate_system_name );