/***********************************************************************/
/* Open Visualization Data Explorer                                    */
/* (C) Copyright IBM Corp. 1989,1999                                   */
/* ALL RIGHTS RESERVED                                                 */
/* This code licensed under the                                        */
/*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
/***********************************************************************/
/*********************************************************************/
/* (c) COPYRIGHT International Business Machines Corp.  1995         */ 
/*                      ALL RIGHTS RESERVED                          */
/*********************************************************************/

#include <dxconfig.h>



#include <dx/dx.h>


/* "how" as a number doesn't seem very easy to use, but
 * i don't have a great alternate suggestion.
 */
typedef struct taskarg {
    Object o;                /* data to work on */
    int how;                 /* 0 to 7, arbitrary reorientations */
    int partitioned;         /* if set, f is part of a partitioned field */
    int maxextent[2];        /* total mesh size for partitioned images */
    int count[2];            /* x and y counts */
    int grid;                /* what original grid was like */
    int altergrid;           /* how the grid needs to be altered */
    int alterdata;           /* ditto for data; see INV_xxx flags below */
    int dotodata;            /* the actual transform to the data - depends on
                                interaction between altergrid & alterdata */
} Rtaskarg;

static Error DoImage (Pointer);
static Error ReorientImage (Rtaskarg *t);

static Error setextents(Object, int *);
static Error dogrid (Rtaskarg *tp);
static Error dodata (Rtaskarg *tp);

#if 0   /* set this if you want to run serial instead of parallel */
#define SERIAL 1
#endif

Error m_Reorient(Object *in, Object *out)
{
    
    Class c;
    Rtaskarg *t;


    if (!in[0]) {
	DXSetError(ERROR_BAD_PARAMETER, "#10000", "image");
	return ERROR;
    }

    c = DXGetObjectClass(in[0]);
    if (c != CLASS_GROUP && c != CLASS_FIELD) {
	DXSetError(ERROR_BAD_PARAMETER, "#10190", "image");
	return ERROR;
    }

    /* initial values all 0 */
    t = (Rtaskarg *)DXAllocateZero(sizeof(Rtaskarg));
    if (!t)
	return ERROR;

    /* default is to ensure that the image is oriented in the manner
     * the Display() module prefers.
     */
    if (in[1] && (!DXExtractInteger(in[1], &t->how) || t->how < 0 || t->how > 7)) {
	DXSetError(ERROR_BAD_PARAMETER, "#10040", "how", 0, 7);
	goto error;
    }


    t->o = (Object)DXCopy(in[0], COPY_STRUCTURE);
    if (!t->o)
	goto error;

#if !SERIAL    
    if (!(DXCreateTaskGroup()))
	goto error;
#endif

    if (!ReorientImage(t))
	goto error;

#if !SERIAL
    if (!(DXExecuteTaskGroup()))
	goto error;
#endif    
    
    /* successful return */
    out[0] = t->o;
    return OK;
    
  error:
    DXDelete(t->o);
    DXFree((Pointer)t);
    return ERROR;
}



extern Error ReorientImage(Rtaskarg *t)
{
    Object subo;
    Class class;
    int i;
    Rtaskarg nt;
    
    memcpy((char *)&nt, (char *)t, sizeof(Rtaskarg));

    class = DXGetObjectClass(t->o);
    if (class == CLASS_GROUP)
	class = DXGetGroupClass((Group)t->o);
    
    switch (class) {
      case CLASS_COMPOSITEFIELD:
	/* put this in a subroutine */

	if (setextents(t->o, (int *)&nt.maxextent) == ERROR)
	    goto error;
	
	nt.partitioned = 1;
	for (i=0; subo = DXGetEnumeratedMember((Group)t->o, i, NULL); i++) {
	    nt.o = subo;
	    if (!ReorientImage(&nt))
		goto error;
	}
	break;
	
      case CLASS_GROUP:
	for (i=0; subo = DXGetEnumeratedMember((Group)t->o, i, NULL); i++) {
	    nt.o = subo;
	    if (!ReorientImage(&nt))
		goto error;
	}
	break;
	
      case CLASS_FIELD:
	nt.o = t->o;
#if !SERIAL
	if (!DXAddTask(DoImage, (Pointer)&nt, sizeof(nt), 1.0))
	    goto error;
#else
	if (!DoImage(&nt))
	    goto error;
	
#endif
	break;

      default:
	/* set an error here?  bad object type? */
	break;
	
    }
    
    return OK;

  error:
    return ERROR;
    
}

static Error setextents(Object o, int *e)
{
    Object subo;
    Array a;
    int i, dim;
    int mo[2];
    int counts[2];

    /* gag.  i'm going to have to look for partitioned images here
     * and treat them special.  oh boy.
     */
    for (i=0; subo = DXGetEnumeratedMember((Group)o, i, NULL); i++) {

	if (DXGetObjectClass(subo) != CLASS_FIELD) {
	    DXSetError(ERROR_INVALID_DATA, 
		       "member of partitioned field is not a field");
	    return ERROR;
	}

	a = (Array)DXGetComponentValue((Field)subo, "connections");
	if (!a) {
	    DXSetError(ERROR_INVALID_DATA, 
		       "field has no `connections' component");
	    return ERROR;
	}
	if (DXGetObjectClass((Object)a) != CLASS_ARRAY) {
	    DXSetError(ERROR_INVALID_DATA, 
		       "connections component is not an Array object");
	    return ERROR;
	}
     
	if ((DXQueryGridConnections(a, &dim, NULL) == ERROR) ||
	    (dim != 2)) {
	    DXSetError(ERROR_INVALID_DATA, 
		       "field must have regular 2d connections");
	    return ERROR;
	}
	DXQueryGridConnections(a, &dim, counts);

	if (!DXGetMeshOffsets((MeshArray)a, mo))
	    return ERROR;
	
	if (mo[0] + counts[0] > e[0])
	    e[0] = mo[0] + counts[0];
	if (mo[1] + counts[1] > e[1])
	    e[1] = mo[1] + counts[1];
    }

    return OK;
}


/* two issues:  what to do with the data, and what to do
 * with the origins & deltas which define the grid.
 */

/* settings for the "alter" flags below */
#define INV_NOTHING    0x00   /* already correct order */
#define INV_TRANSPOSE  0x01   /* transpose x and y data */                
#define INV_ROWS       0x02   /* invert the data inside each row */  
#define INV_COLS       0x04   /* same w/ cols */                     
#define INV_EXCHANGE   0x08   /* exchange the x and y axis */

/* settings for the grid flag */
#define Y_FASTEST      0x01   /* y variables are contig in memory */
#define NEGATIVE_X     0x02   /* deltas from positions array */
#define NEGATIVE_Y     0x04   /* ditto */

static Error DoImage (Pointer ptr)
{
    Rtaskarg *tp = (Rtaskarg *)ptr;

    if (dogrid(tp) == ERROR)
	return ERROR;

    return OK;
}


static Error dogrid (Rtaskarg *tp)
{
    Field field;
    Array positions, connections, a;
    Array new_positions=NULL;
    Array new_connections=NULL;

    int yfastest = 0;
    int negative_x = 0;
    int negative_y = 0;
    int xcounts, ycounts;
    
    int i, dim, counts[2], itemp;
    int meshextents[2];
    float origin[2], deltas[4], ftemp;
    int otherdim;


    field = (Field)tp->o;

    /* get partition info here because i'll have to deal
     * with fixing up the mesh offsets as well.
     */
    connections = (Array) DXGetComponentValue(field, "connections");
    if (!connections) {
	DXSetError(ERROR_MISSING_DATA, "field has no `connections' component");
	return ERROR;
    }

    if ((!DXQueryGridConnections(connections, &dim, NULL)) 
	|| (dim != 2)) {
	DXSetError(ERROR_INVALID_DATA, "connections must be a 2D regular grid");
	goto error;
    }
    
    if (!DXGetMeshOffsets((MeshArray)connections, (int *)meshextents))
	return ERROR;
    
    /* now we have the start of this grid in meshextents and the extents
     * of all the partitioned fields combined in tp->maxextents
     */

#if 0
    DXMessage("old extents: %d %d, %d %d", meshextents[0], meshextents[1], 
	      tp->maxextent[0], tp->maxextent[1]);
#endif

    
    /* verify the positions are regular and 2d 
     */
    positions = (Array) DXGetComponentValue(field, "positions");
    if (!positions) {
	DXSetError(ERROR_MISSING_DATA, "field has no `positions' component");
	return ERROR;
    }
    
    if ((!DXQueryGridPositions(positions, &dim, counts, origin, deltas)) 
	|| dim != 2) {
	DXSetError(ERROR_INVALID_DATA, "positions must be a 2D regular grid");
	goto error;
    }

    /* decide what to do with the grid */

    /* figure out what the orientation of the input image was; we want
     * the output deltas to be positive with x varies fastest.  figure
     * out what has to happen to the data to make that happen, and
     * then figure out what additional thing has to happen to the
     * data to change the orientation as the user requested.
     */
    if (deltas[3] != 0) {
	yfastest++;
	tp->grid |= Y_FASTEST;
	tp->altergrid |= INV_EXCHANGE | INV_TRANSPOSE;
    } else if (deltas[2] != 0) {
	yfastest = 0;
    } else {
	DXSetError(ERROR_INVALID_DATA, "image must have an orthogonal grid");
	goto error;
    }

    if (deltas[0] < 0 || deltas[2] < 0) {
	negative_x++;
	tp->grid |= NEGATIVE_X;
	tp->altergrid |= INV_COLS;
    }
    if (deltas[1] < 0 || deltas[3] < 0) {
	negative_y++;
	tp->grid |= NEGATIVE_Y;
	tp->altergrid |= INV_ROWS;
    }

    tp->count[0] = counts[0];
    tp->count[1] = counts[1];


    /* above this line is all setup; below this line the field is changed */


    /* first do the data, then do the grid.
     * this is because depending on what "how" is, this subroutine
     * might change the "altergrid" setting.
     */
    if (dodata(tp) == ERROR)
	goto error;
    

    /* only do the stuff below if there is something to do */
    if (tp->grid == 0 && tp->altergrid == 0 && tp->dotodata == 0) {

	if (!DXEndField(field))
	    return ERROR;

	return OK;
    }


    /* hack because i can't get the logic behind this right.
     */
    otherdim = 0;
/*    DXMessage("grid = %d, how now = %d\n", tp->altergrid, tp->how); */
    switch (tp->altergrid) {
      case 0:
	if (tp->how == 4 || tp->how == 6)
	    otherdim++;
	break;
      case 1:
	if (tp->how == 1 || tp->how == 3)
	    otherdim++;
	break;
      case 2:
	if (tp->how == 0 || tp->how == 2)
	    otherdim++;
	break;
      case 3:
	if (tp->how == 5 || tp->how == 7)
	    otherdim++;
	break;
      case 4:
	if (tp->how == 0 || tp->how == 2)
	    otherdim++;
	break;
      case 5:
	if (tp->how == 5 || tp->how == 7)
	    otherdim++;
	break;
      case 6:
	if (tp->how == 4 || tp->how == 6)
	    otherdim++;
	break;
      case 7:
	if (tp->how == 1 || tp->how == 3)
	    otherdim++;
	break;
    }

    /* now fix the counts and deltas to match the order the
     * Display module is most comfortable with.
     */
    
    if (tp->altergrid & INV_COLS) {
	origin[0] += deltas[0] * (counts[0]-1) + deltas[2] * (counts[1]-1);
	if (deltas[0] != 0.0)
	    deltas[0] = -deltas[0];
	if (deltas[2] != 0.0)
	    deltas[2] = -deltas[2];
    }
    if (tp->dotodata & INV_COLS) {
	if (otherdim)
	    meshextents[1] = tp->maxextent[1] - counts[1] - meshextents[1];
	else
	    meshextents[0] = tp->maxextent[0] - counts[0] - meshextents[0];
    }
    if (tp->altergrid & INV_ROWS) {
	origin[1] += deltas[1] * (counts[0]-1) + deltas[3] * (counts[1]-1);
	if (deltas[1] != 0.0)
	    deltas[1] = -deltas[1];
	if (deltas[3] != 0.0)
	    deltas[3] = -deltas[3];
    } 
    if (tp->dotodata & INV_ROWS) {
	if (otherdim)
	    meshextents[0] = tp->maxextent[0] - counts[0] - meshextents[0];
	else
	    meshextents[1] = tp->maxextent[1] - counts[1] - meshextents[1];
    }
    if (tp->altergrid & INV_TRANSPOSE) {
	ftemp = deltas[2];
	deltas[2] = deltas[0];
	deltas[0] = ftemp;

	ftemp = deltas[3];
	deltas[3] = deltas[1];
	deltas[1] = ftemp;
    }
    if (tp->altergrid & INV_EXCHANGE) {
	itemp = counts[0];
	counts[0] = counts[1];
	counts[1] = itemp;
    }
    if (tp->dotodata & INV_TRANSPOSE) {
	itemp = meshextents[0];
	meshextents[0] = meshextents[1];
	meshextents[1] = itemp;
    }
    

#if 0
    DXMessage("new extents: %d %d", meshextents[0], meshextents[1]);
#endif


    /* redo the positions component */
    new_positions = DXMakeGridPositionsV(2, counts, origin, deltas);
    if (!new_positions)
	goto error;
    
    /* redo the connections component */
    new_connections = DXMakeGridConnectionsV(2, counts);
    if (!new_connections)
	goto error;

   
    /* and for partitioned data, fix the mesh extents */
    if (tp->partitioned) {
	if (!DXSetMeshOffsets((MeshArray)new_connections, (int *)meshextents))
	    return ERROR;
    }
    
    
    /* place in field */
    if (!DXSetComponentValue(field, "positions", (Object)new_positions))
	goto error;
    if (!DXSetComponentValue(field, "connections", (Object)new_connections))
	goto error;
    
    if (!DXChangedComponentValues(field, "positions"))
	goto error;
    if (!DXChangedComponentValues(field, "connections"))
	goto error;

    if (!DXEndField(field))
	goto error;

    return OK;
    
  error:
    DXDelete((Object)new_positions);
    DXDelete((Object)new_connections);
    return ERROR;
}


#define MAXSHAPE 64

static Error dodata (Rtaskarg *tp)
{
    Field field;
    Array a, new_a = NULL;

    int yfastest;
    int negative_x;
    int negative_y;
    int loopcount[2];
    int cnum;
    int invert;

    Pointer fromptr, toptr;
    int size;
    ubyte *from1, *to1;
    int *from4, *to4;
    typedef struct {
	ubyte _u[3];
    } threebytes;
    threebytes *from3, *to3;
    typedef struct {
	int _i[3];
    } twelvebytes;
    twelvebytes *from12, *to12;
    int i, ii, j, jj, k;
    
    char *name, *dattr, *rattr;
    int nodeps, norefs;
    int items;
    Type type;
    Category cat;
    int rank, shape[MAXSHAPE];

    int didwork;


    
    field = (Field)tp->o;

    yfastest = tp->grid & Y_FASTEST;
    negative_x = tp->grid & NEGATIVE_X;
    negative_y = tp->grid & NEGATIVE_Y;

#if 1    /* in again */   /*xxx  out for now */

#if 0
    DXMessage("yfastest = %d, neg_x = %d, neg_y = %d",
	      yfastest, negative_x, negative_y);
#endif

    /* take care of all the possibilities; inverted axis and
     * different data orientations.  alter tp->how to make it match
     * what happens with the x-fastest, positive x & y deltas.
     */
    if (tp->how != 0) {
	invert  = tp->grid &  Y_FASTEST ? 1 : 0;
	invert += tp->grid & NEGATIVE_X ? 1 : 0;
	invert += tp->grid & NEGATIVE_Y ? 1 : 0;
	switch (invert) {
	  case 2:
	    switch (tp->how) {
	      case 4:
		tp->how = 7;
		break;
	      case 5:
		tp->how = 4;
		break;
	      case 6:
		tp->how = 5;
		break;
	      case 7:
		tp->how = 6;
		break;
	    }
	    break;
	  case 1:
	  case 3:
	    switch (tp->how) {
	      case 1:
		tp->how = 3;
		break;
	      case 3:
		tp->how = 1;
		break;
	      case 5:
		tp->how = 6;
		break;
	      case 6:
		tp->how = 5;
		break;
	      case 4:
		tp->how = 7;
		break;
	      case 7:
		tp->how = 4;
		break;
	    }
	    break;
	}
    }

    /* now that we know something about how the input image is
     * oriented, see how the user wants it to go out.  
     */
    switch (tp->how) {
      case 0:
	/* just make the image in the order the Display module
         * is happiest handling.
	 */
	break;

      case 4:
	tp->alterdata |= (yfastest ? INV_ROWS : INV_COLS);
	break;

      case 6:
	tp->alterdata |= (yfastest ? INV_COLS : INV_ROWS);
	break;

      case 2:
	tp->alterdata |= INV_ROWS;
	tp->alterdata |= INV_COLS;
	break;

      case 7:
	tp->altergrid ^= INV_EXCHANGE;
	tp->alterdata |= INV_TRANSPOSE;
	break;

      case 3:
	tp->altergrid ^= INV_EXCHANGE;
	tp->alterdata |= INV_TRANSPOSE;
	tp->alterdata |= (yfastest ? INV_ROWS : INV_COLS);
	break;

      case 1:
	tp->altergrid ^= INV_EXCHANGE;
	tp->alterdata |= INV_TRANSPOSE;
	tp->alterdata |= (yfastest ? INV_COLS : INV_ROWS);
	break;

      case 5:
	tp->altergrid ^= INV_EXCHANGE;
	tp->alterdata |= INV_TRANSPOSE;
	tp->alterdata |= INV_ROWS;
	tp->alterdata |= INV_COLS;
	break;
    }


    /* now compare what happens to the grid vs data.  if we're
     * moving the grid we can leave the data alone; if we are
     * not moving the grid we have to move the data.
     */
    if ((tp->altergrid & INV_TRANSPOSE) ^ (tp->alterdata & INV_TRANSPOSE))
	tp->dotodata |= INV_TRANSPOSE;
    if ((tp->altergrid & INV_ROWS) ^ (tp->alterdata & INV_ROWS))
	tp->dotodata |= INV_ROWS;
    if ((tp->altergrid & INV_COLS) ^ (tp->alterdata & INV_COLS))
	tp->dotodata |= INV_COLS;
    
    
#else



    switch (tp->how) {
      case 0:
	/* just make the image in the order the Display module
         * is happiest handling.
	 */
	break;

      case 4:
	tp->alterdata |= INV_COLS;
	break;

      case 6:
	tp->alterdata |= INV_ROWS;
	break;

      case 2:
	tp->alterdata |= INV_ROWS | INV_COLS;
	break;

#define FLIP(what) ((tp->altergrid & (what)) ^ (what))

      case 7:
#if 0
	tp->altergrid ^= INV_EXCHANGE;
	tp->alterdata |= FLIP (INV_TRANSPOSE);
#endif
	tp->alterdata |= INV_EXCHANGE | INV_TRANSPOSE | INV_EXCHANGE;
	break;

      case 3:
#if 0
	tp->altergrid ^= INV_EXCHANGE;
	tp->alterdata |= FLIP (INV_TRANSPOSE | INV_COLS);
#endif
	tp->alterdata |= INV_EXCHANGE | INV_TRANSPOSE | INV_EXCHANGE | INV_COLS;
	break;

      case 1:
#if 0
	tp->altergrid ^= INV_EXCHANGE;
	tp->alterdata |= FLIP (INV_TRANSPOSE | INV_ROWS);
#endif
	tp->alterdata |= INV_EXCHANGE | INV_TRANSPOSE | INV_EXCHANGE | INV_ROWS;
	break;

      case 5:
#if 0
	tp->altergrid ^= INV_EXCHANGE;
	tp->alterdata |= FLIP (INV_TRANSPOSE | INV_ROWS | INV_COLS);
#endif
	tp->alterdata |= INV_EXCHANGE | INV_TRANSPOSE | INV_EXCHANGE | INV_ROWS | INV_COLS;
	break;
    }


#if 0
    DXMessage("before grid = 0x%04x, data = 0x%04x", 
	      tp->altergrid, tp->alterdata);
#endif

#define TOGGLE(what, what1, bits)  (what ^= (what1 & bits))

    TOGGLE(tp->altergrid, tp->alterdata, INV_EXCHANGE);
    tp->alterdata &= ~INV_EXCHANGE;
    TOGGLE(tp->alterdata, tp->altergrid, INV_TRANSPOSE);
    TOGGLE(tp->alterdata, tp->altergrid, INV_ROWS);
    TOGGLE(tp->alterdata, tp->altergrid, INV_COLS);

#if 0
    DXMessage("after grid = 0x%04x, data = 0x%04x", 
	      tp->altergrid, tp->alterdata);
#endif


#endif 

#if 0
    DXMessage("altergrid = %d, alterdata = %d, dotodata = %d",
	      tp->altergrid, tp->alterdata, tp->dotodata);
#endif
    
    /* if we don't have to change the data in the components, we're done */
    if (tp->dotodata == INV_NOTHING) 
	return OK;


    for (cnum = 0; 
	 a = (Array)DXGetEnumeratedComponentValue(field, cnum, &name); cnum++) {

	if (!strcmp(name, "positions") || !strcmp(name, "connections"))
	    continue;

	/* if no deps and no refs, just pass this component through untouched.
	 */
	nodeps = 0;
	dattr = NULL;
	if (!DXGetStringAttribute((Object)a, "dep", &dattr) || !dattr)
	    nodeps++;
	
	norefs = 0;
	rattr = NULL;
	if (!DXGetStringAttribute((Object)a, "ref", &rattr) || !rattr)
	    norefs++;

	if (nodeps && norefs)
	    continue;
	
	if (dattr && strcmp(dattr, "positions") && strcmp(dattr, "connections"))
	    continue;

	if (nodeps && !norefs) {
	    /* this is not dep anything but is ref something.  to do this
	     * right, i need to reorder the refs the same way i reorder
	     * the target.  i don't have the code to do this written, but
	     * i've got it in slab/slice and other places.
	     */
	    continue;
	}

	
	/* ok, we got a live one */

	if (!strcmp(dattr, "positions")) {
	    loopcount[0] = tp->count[0];
	    loopcount[1] = tp->count[1];
	} else {
	    loopcount[0] = tp->count[0] - 1;
	    loopcount[1] = tp->count[1] - 1;
	}
	    

	size = DXGetItemSize(a);
	fromptr = DXGetArrayData(a);
	if (!fromptr)
	    goto error;

	if (!DXGetArrayInfo(a, &items, &type, &cat, &rank, shape))
	    goto error;

	new_a = DXNewArrayV(type, cat, rank, shape);
	if (!new_a)
	    goto error;

	if (!DXAddArrayData(new_a, 0, items, NULL))
	    goto error;

	toptr = DXGetArrayData(new_a);
	if (!toptr)
	    goto error;

#define MOVEDATA1(op, np) \
 \
     if (tp->dotodata == INV_TRANSPOSE) { \
	 for (i=0; i<loopcount[1]; i++) \
	     for (j=0; j<loopcount[0]; j++) \
		 np[i*loopcount[0] + j] = op[j*loopcount[1] + i]; \
    } \
\
    if (tp->dotodata == INV_ROWS) { \
	for (i=0; i<loopcount[1]; i++) \
	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
		np[j*loopcount[1] + i] = op[jj*loopcount[1] + i]; \
    } \
\
    if (tp->dotodata == INV_COLS) { \
	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
	    for (j=0; j<loopcount[0]; j++) \
		np[j*loopcount[1] + i] = op[j*loopcount[1] + ii]; \
    } \
\
    if (tp->dotodata == (INV_COLS|INV_ROWS)) { \
	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
		np[j*loopcount[1] + i] = op[jj*loopcount[1] + ii]; \
    } \
\
    if (tp->dotodata == (INV_TRANSPOSE|INV_COLS)) { \
	for (i=0; i<loopcount[1]; i++) \
	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
		np[i*loopcount[0] + j] = op[jj*loopcount[1] + i]; \
    } \
\
    if (tp->dotodata == (INV_TRANSPOSE|INV_ROWS)) { \
	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
	    for (j=0; j<loopcount[0]; j++) \
		np[i*loopcount[0] + j] = op[j*loopcount[1] + ii]; \
    } \
\
    if (tp->dotodata == (INV_TRANSPOSE|INV_COLS|INV_ROWS)) { \
	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
		np[i*loopcount[0] + j] = op[jj*loopcount[1] + ii]; \
    }

	
#define MOVEDATA(op, np, size) \
 \
    if (tp->dotodata == INV_TRANSPOSE) { \
	for (i=0; i<loopcount[1]; i++) \
	    for (j=0; j<loopcount[0]; j++) \
		for (k=0; k<size; k++) \
		    np[(i*loopcount[0] + j)*size + k] = \
			op[(j*loopcount[1] + i)*size + k]; \
    } \
\
    if (tp->dotodata == INV_ROWS) { \
	for (i=0; i<loopcount[1]; i++) \
	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
		for (k=0; k<size; k++) \
		    np[(j*loopcount[1] + i)*size + k] = \
			op[(jj*loopcount[1] + i)*size + k]; \
    } \
\
    if (tp->dotodata == INV_COLS) { \
	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
	    for (j=0; j<loopcount[0]; j++) \
		for (k=0; k<size; k++) \
		    np[(j*loopcount[1] + i)*size + k] = \
			op[(j*loopcount[1] + ii)*size + k]; \
    } \
\
    if (tp->dotodata == (INV_COLS|INV_ROWS)) { \
	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
		for (k=0; k<size; k++) \
		    np[(j*loopcount[1] + i)*size + k] = \
			op[(jj*loopcount[1] + ii)*size + k]; \
    } \
\
    if (tp->dotodata == (INV_TRANSPOSE|INV_COLS)) { \
	for (i=0; i<loopcount[1]; i++) \
	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
		for (k=0; k<size; k++) \
		    np[(i*loopcount[0] + j)*size + k] = \
			op[(jj*loopcount[1] + i)*size + k]; \
    } \
\
    if (tp->dotodata == (INV_TRANSPOSE|INV_ROWS)) { \
	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
	    for (j=0; j<loopcount[0]; j++) \
		for (k=0; k<size; k++) \
		    np[(i*loopcount[0] + j)*size + k] = \
			op[(j*loopcount[1] + ii)*size + k]; \
    } \
\
    if (tp->dotodata == (INV_TRANSPOSE|INV_COLS|INV_ROWS)) { \
	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
		for (k=0; k<size; k++) \
		    np[(i*loopcount[0] + j)*size + k] = \
			op[(jj*loopcount[1] + ii)*size + k]; \
    }


	if (size == 4) {
	    from4 = (int *)fromptr;
	    to4 = (int *)toptr;
	    MOVEDATA1(from4, to4);
	}
	
	else if (size == 12) {
	    from12 = (twelvebytes *)fromptr;
	    to12 = (twelvebytes *)toptr;
	    MOVEDATA1(from12, to12);
	}
	
	else if (size == 3) {
	    from3 = (threebytes *)fromptr;
	    to3 = (threebytes *)toptr;
	    MOVEDATA1(from3, to3);
	}
	
	else {
	    from1 = (ubyte *)fromptr;
	    to1 = (ubyte *)toptr;
	    MOVEDATA(from1, to1, size);
	}


	if (!DXSetComponentValue(field, name, (Object)new_a))
	    goto error;
	
	new_a = NULL;

    }

    /* we are changing positions & connections - make sure things
     * which are derived from them are deleted.
     */
    DXChangedComponentValues(field, "positions");
    DXChangedComponentValues(field, "connections");



    /* do this as a second pass so the enumeration count doesn't get mangled.
     */
  again:
    didwork = 0;
    for (i=0; a = (Array)DXGetEnumeratedComponentValue(field, i, &name); i++) {
	
	if (!strcmp(name, "positions") || !strcmp(name, "connections"))
	    continue;
	
	/* if no deps and no refs, just pass this component through untouched.
	 */
	nodeps = 0;
	dattr = NULL;
	if (!DXGetStringAttribute((Object)a, "dep", &dattr) || !dattr)
	    nodeps++;
	
	norefs = 0;
	rattr = NULL;
	if (!DXGetStringAttribute((Object)a, "ref", &rattr) || !rattr)
	    norefs++;
	
	if (nodeps && norefs)
	    continue;
	
	if (dattr && strcmp(dattr, "positions") && strcmp(dattr, "connections"))
	    continue;

	
	/* the list of things which aren't going to be valid at the end
         * after moving the positions & connections, so nuke them now rather
	 * than passing them through and having them be wrong.
	 */
	if ((rattr && !strcmp(rattr, "positions"))
	    || (rattr && !strcmp(rattr, "connections"))) {
	    DXSetComponentValue(field, name, NULL);  /* screws enum count? */
	    DXChangedComponentValues(field, name);
	    didwork++;
	    goto again;
	}
	
    }   
    
    /* done */
    if (!DXEndField(field))
	return ERROR;
    
    return OK;
    
  error:
    DXDelete((Object)new_a);
    return ERROR;
}






