1 /***********************************************************************/
2 /* Open Visualization Data Explorer                                    */
3 /* (C) Copyright IBM Corp. 1989,1999                                   */
4 /* ALL RIGHTS RESERVED                                                 */
5 /* This code licensed under the                                        */
6 /*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
7 /***********************************************************************/
8 /*********************************************************************/
9 /* (c) COPYRIGHT International Business Machines Corp.  1995         */
10 /*                      ALL RIGHTS RESERVED                          */
11 /*********************************************************************/
12 
13 #include <dxconfig.h>
14 
15 #if defined(HAVE_STRING_H)
16 #include <string.h>
17 #endif
18 
19 #include <dx/dx.h>
20 
21 
22 /* "how" as a number doesn't seem very easy to use, but
23  * i don't have a great alternate suggestion.
24  */
25 typedef struct taskarg {
26     Object o;                /* data to work on */
27     int how;                 /* 0 to 7, arbitrary reorientations */
28     int partitioned;         /* if set, f is part of a partitioned field */
29     int maxextent[2];        /* total mesh size for partitioned images */
30     int count[2];            /* x and y counts */
31     int grid;                /* what original grid was like */
32     int altergrid;           /* how the grid needs to be altered */
33     int alterdata;           /* ditto for data; see INV_xxx flags below */
34     int dotodata;            /* the actual transform to the data - depends on
35                                 interaction between altergrid & alterdata */
36 } Rtaskarg;
37 
38 static Error DoImage (Pointer);
39 static Error ReorientImage (Rtaskarg *t);
40 
41 static Error setextents(Object, int *);
42 static Error dogrid (Rtaskarg *tp);
43 static Error dodata (Rtaskarg *tp);
44 
45 #if 0   /* set this if you want to run serial instead of parallel */
46 #define SERIAL 1
47 #endif
48 
m_Reorient(Object * in,Object * out)49 Error m_Reorient(Object *in, Object *out)
50 {
51 
52     Class c;
53     Rtaskarg *t;
54 
55 
56     if (!in[0]) {
57 	DXSetError(ERROR_BAD_PARAMETER, "#10000", "image");
58 	return ERROR;
59     }
60 
61     c = DXGetObjectClass(in[0]);
62     if (c != CLASS_GROUP && c != CLASS_FIELD) {
63 	DXSetError(ERROR_BAD_PARAMETER, "#10190", "image");
64 	return ERROR;
65     }
66 
67     /* initial values all 0 */
68     t = (Rtaskarg *)DXAllocateZero(sizeof(Rtaskarg));
69     if (!t)
70 	return ERROR;
71 
72     /* default is to ensure that the image is oriented in the manner
73      * the Display() module prefers.
74      */
75     if (in[1] && (!DXExtractInteger(in[1], &t->how) || t->how < 0 || t->how > 7)) {
76 	DXSetError(ERROR_BAD_PARAMETER, "#10040", "how", 0, 7);
77 	goto error;
78     }
79 
80 
81     t->o = (Object)DXCopy(in[0], COPY_STRUCTURE);
82     if (!t->o)
83 	goto error;
84 
85 #if !SERIAL
86     if (!(DXCreateTaskGroup()))
87 	goto error;
88 #endif
89 
90     if (!ReorientImage(t))
91 	goto error;
92 
93 #if !SERIAL
94     if (!(DXExecuteTaskGroup()))
95 	goto error;
96 #endif
97 
98     /* successful return */
99     out[0] = t->o;
100     return OK;
101 
102   error:
103     DXDelete(t->o);
104     DXFree((Pointer)t);
105     return ERROR;
106 }
107 
108 
109 
110 static Error
ReorientImage(Rtaskarg * t)111 ReorientImage(Rtaskarg *t)
112 {
113     Object subo;
114     Class class;
115     int i;
116     Rtaskarg nt;
117 
118     memcpy((char *)&nt, (char *)t, sizeof(Rtaskarg));
119 
120     class = DXGetObjectClass(t->o);
121     if (class == CLASS_GROUP)
122 	class = DXGetGroupClass((Group)t->o);
123 
124     switch (class) {
125       case CLASS_COMPOSITEFIELD:
126 	/* put this in a subroutine */
127 
128 	if (setextents(t->o, (int *)&nt.maxextent) == ERROR)
129 	    goto error;
130 
131 	nt.partitioned = 1;
132 	for (i=0; (subo = DXGetEnumeratedMember((Group)t->o, i, NULL)); i++) {
133 	    nt.o = subo;
134 	    if (!ReorientImage(&nt))
135 		goto error;
136 	}
137 	break;
138 
139       case CLASS_GROUP:
140 	for (i=0; (subo = DXGetEnumeratedMember((Group)t->o, i, NULL)); i++) {
141 	    nt.o = subo;
142 	    if (!ReorientImage(&nt))
143 		goto error;
144 	}
145 	break;
146 
147       case CLASS_FIELD:
148 	nt.o = t->o;
149 #if !SERIAL
150 	if (!DXAddTask(DoImage, (Pointer)&nt, sizeof(nt), 1.0))
151 	    goto error;
152 #else
153 	if (!DoImage(&nt))
154 	    goto error;
155 
156 #endif
157 	break;
158 
159       default:
160 	/* set an error here?  bad object type? */
161 	break;
162 
163     }
164 
165     return OK;
166 
167   error:
168     return ERROR;
169 
170 }
171 
setextents(Object o,int * e)172 static Error setextents(Object o, int *e)
173 {
174     Object subo;
175     Array a;
176     int i, dim;
177     int mo[2];
178     int counts[2];
179 
180     /* gag.  i'm going to have to look for partitioned images here
181      * and treat them special.  oh boy.
182      */
183     for (i=0; (subo = DXGetEnumeratedMember((Group)o, i, NULL)); i++) {
184 
185 	if (DXGetObjectClass(subo) != CLASS_FIELD) {
186 	    DXSetError(ERROR_DATA_INVALID,
187 		       "member of partitioned field is not a field");
188 	    return ERROR;
189 	}
190 
191 	a = (Array)DXGetComponentValue((Field)subo, "connections");
192 	if (!a) {
193 	    DXSetError(ERROR_DATA_INVALID,
194 		       "field has no `connections' component");
195 	    return ERROR;
196 	}
197 	if (DXGetObjectClass((Object)a) != CLASS_ARRAY) {
198 	    DXSetError(ERROR_DATA_INVALID,
199 		       "connections component is not an Array object");
200 	    return ERROR;
201 	}
202 
203 	if ((DXQueryGridConnections(a, &dim, NULL) == ERROR) ||
204 	    (dim != 2)) {
205 	    DXSetError(ERROR_DATA_INVALID,
206 		       "field must have regular 2d connections");
207 	    return ERROR;
208 	}
209 	DXQueryGridConnections(a, &dim, counts);
210 
211 	if (!DXGetMeshOffsets((MeshArray)a, mo))
212 	    return ERROR;
213 
214 	if (mo[0] + counts[0] > e[0])
215 	    e[0] = mo[0] + counts[0];
216 	if (mo[1] + counts[1] > e[1])
217 	    e[1] = mo[1] + counts[1];
218     }
219 
220     return OK;
221 }
222 
223 
224 /* two issues:  what to do with the data, and what to do
225  * with the origins & deltas which define the grid.
226  */
227 
228 /* settings for the "alter" flags below */
229 #define INV_NOTHING    0x00   /* already correct order */
230 #define INV_TRANSPOSE  0x01   /* transpose x and y data */
231 #define INV_ROWS       0x02   /* invert the data inside each row */
232 #define INV_COLS       0x04   /* same w/ cols */
233 #define INV_EXCHANGE   0x08   /* exchange the x and y axis */
234 
235 /* settings for the grid flag */
236 #define Y_FASTEST      0x01   /* y variables are contig in memory */
237 #define NEGATIVE_X     0x02   /* deltas from positions array */
238 #define NEGATIVE_Y     0x04   /* ditto */
239 
DoImage(Pointer ptr)240 static Error DoImage (Pointer ptr)
241 {
242     Rtaskarg *tp = (Rtaskarg *)ptr;
243 
244     if (dogrid(tp) == ERROR)
245 	return ERROR;
246 
247     return OK;
248 }
249 
250 
dogrid(Rtaskarg * tp)251 static Error dogrid (Rtaskarg *tp)
252 {
253     Field field;
254     Array positions, connections;
255     Array new_positions=NULL;
256     Array new_connections=NULL;
257 
258     int yfastest = 0;
259     int negative_x = 0;
260     int negative_y = 0;
261 
262     int dim, counts[2], itemp;
263     int meshextents[2];
264     float origin[2], deltas[4], ftemp;
265     int otherdim;
266 
267 
268     field = (Field)tp->o;
269 
270     /* get partition info here because i'll have to deal
271      * with fixing up the mesh offsets as well.
272      */
273     connections = (Array) DXGetComponentValue(field, "connections");
274     if (!connections) {
275 	DXSetError(ERROR_MISSING_DATA, "field has no `connections' component");
276 	return ERROR;
277     }
278 
279     if ((!DXQueryGridConnections(connections, &dim, NULL))
280 	|| (dim != 2)) {
281 	DXSetError(ERROR_DATA_INVALID, "connections must be a 2D regular grid");
282 	goto error;
283     }
284 
285     if (!DXGetMeshOffsets((MeshArray)connections, (int *)meshextents))
286 	return ERROR;
287 
288     /* now we have the start of this grid in meshextents and the extents
289      * of all the partitioned fields combined in tp->maxextents
290      */
291 
292 #if 0
293     DXMessage("old extents: %d %d, %d %d", meshextents[0], meshextents[1],
294 	      tp->maxextent[0], tp->maxextent[1]);
295 #endif
296 
297 
298     /* verify the positions are regular and 2d
299      */
300     positions = (Array) DXGetComponentValue(field, "positions");
301     if (!positions) {
302 	DXSetError(ERROR_MISSING_DATA, "field has no `positions' component");
303 	return ERROR;
304     }
305 
306     if ((!DXQueryGridPositions(positions, &dim, counts, origin, deltas))
307 	|| dim != 2) {
308 	DXSetError(ERROR_DATA_INVALID, "positions must be a 2D regular grid");
309 	goto error;
310     }
311 
312     /* decide what to do with the grid */
313 
314     /* figure out what the orientation of the input image was; we want
315      * the output deltas to be positive with x varies fastest.  figure
316      * out what has to happen to the data to make that happen, and
317      * then figure out what additional thing has to happen to the
318      * data to change the orientation as the user requested.
319      */
320     if (deltas[3] != 0) {
321 	yfastest++;
322 	tp->grid |= Y_FASTEST;
323 	tp->altergrid |= INV_EXCHANGE | INV_TRANSPOSE;
324     } else if (deltas[2] != 0) {
325 	yfastest = 0;
326     } else {
327 	DXSetError(ERROR_DATA_INVALID, "image must have an orthogonal grid");
328 	goto error;
329     }
330 
331     if (deltas[0] < 0 || deltas[2] < 0) {
332 	negative_x++;
333 	tp->grid |= NEGATIVE_X;
334 	tp->altergrid |= INV_COLS;
335     }
336     if (deltas[1] < 0 || deltas[3] < 0) {
337 	negative_y++;
338 	tp->grid |= NEGATIVE_Y;
339 	tp->altergrid |= INV_ROWS;
340     }
341 
342     tp->count[0] = counts[0];
343     tp->count[1] = counts[1];
344 
345 
346     /* above this line is all setup; below this line the field is changed */
347 
348 
349     /* first do the data, then do the grid.
350      * this is because depending on what "how" is, this subroutine
351      * might change the "altergrid" setting.
352      */
353     if (dodata(tp) == ERROR)
354 	goto error;
355 
356 
357     /* only do the stuff below if there is something to do */
358     if (tp->grid == 0 && tp->altergrid == 0 && tp->dotodata == 0) {
359 
360 	if (!DXEndField(field))
361 	    return ERROR;
362 
363 	return OK;
364     }
365 
366 
367     /* hack because i can't get the logic behind this right.
368      */
369     otherdim = 0;
370 /*    DXMessage("grid = %d, how now = %d\n", tp->altergrid, tp->how); */
371     switch (tp->altergrid) {
372       case 0:
373 	if (tp->how == 4 || tp->how == 6)
374 	    otherdim++;
375 	break;
376       case 1:
377 	if (tp->how == 1 || tp->how == 3)
378 	    otherdim++;
379 	break;
380       case 2:
381 	if (tp->how == 0 || tp->how == 2)
382 	    otherdim++;
383 	break;
384       case 3:
385 	if (tp->how == 5 || tp->how == 7)
386 	    otherdim++;
387 	break;
388       case 4:
389 	if (tp->how == 0 || tp->how == 2)
390 	    otherdim++;
391 	break;
392       case 5:
393 	if (tp->how == 5 || tp->how == 7)
394 	    otherdim++;
395 	break;
396       case 6:
397 	if (tp->how == 4 || tp->how == 6)
398 	    otherdim++;
399 	break;
400       case 7:
401 	if (tp->how == 1 || tp->how == 3)
402 	    otherdim++;
403 	break;
404     }
405 
406     /* now fix the counts and deltas to match the order the
407      * Display module is most comfortable with.
408      */
409 
410     if (tp->altergrid & INV_COLS) {
411 	origin[0] += deltas[0] * (counts[0]-1) + deltas[2] * (counts[1]-1);
412 	if (deltas[0] != 0.0)
413 	    deltas[0] = -deltas[0];
414 	if (deltas[2] != 0.0)
415 	    deltas[2] = -deltas[2];
416     }
417     if (tp->dotodata & INV_COLS) {
418 	if (otherdim)
419 	    meshextents[1] = tp->maxextent[1] - counts[1] - meshextents[1];
420 	else
421 	    meshextents[0] = tp->maxextent[0] - counts[0] - meshextents[0];
422     }
423     if (tp->altergrid & INV_ROWS) {
424 	origin[1] += deltas[1] * (counts[0]-1) + deltas[3] * (counts[1]-1);
425 	if (deltas[1] != 0.0)
426 	    deltas[1] = -deltas[1];
427 	if (deltas[3] != 0.0)
428 	    deltas[3] = -deltas[3];
429     }
430     if (tp->dotodata & INV_ROWS) {
431 	if (otherdim)
432 	    meshextents[0] = tp->maxextent[0] - counts[0] - meshextents[0];
433 	else
434 	    meshextents[1] = tp->maxextent[1] - counts[1] - meshextents[1];
435     }
436     if (tp->altergrid & INV_TRANSPOSE) {
437 	ftemp = deltas[2];
438 	deltas[2] = deltas[0];
439 	deltas[0] = ftemp;
440 
441 	ftemp = deltas[3];
442 	deltas[3] = deltas[1];
443 	deltas[1] = ftemp;
444     }
445     if (tp->altergrid & INV_EXCHANGE) {
446 	itemp = counts[0];
447 	counts[0] = counts[1];
448 	counts[1] = itemp;
449     }
450     if (tp->dotodata & INV_TRANSPOSE) {
451 	itemp = meshextents[0];
452 	meshextents[0] = meshextents[1];
453 	meshextents[1] = itemp;
454     }
455 
456 
457 #if 0
458     DXMessage("new extents: %d %d", meshextents[0], meshextents[1]);
459 #endif
460 
461 
462     /* redo the positions component */
463     new_positions = DXMakeGridPositionsV(2, counts, origin, deltas);
464     if (!new_positions)
465 	goto error;
466 
467     /* redo the connections component */
468     new_connections = DXMakeGridConnectionsV(2, counts);
469     if (!new_connections)
470 	goto error;
471 
472 
473     /* and for partitioned data, fix the mesh extents */
474     if (tp->partitioned) {
475 	if (!DXSetMeshOffsets((MeshArray)new_connections, (int *)meshextents))
476 	    return ERROR;
477     }
478 
479 
480     /* place in field */
481     if (!DXSetComponentValue(field, "positions", (Object)new_positions))
482 	goto error;
483     if (!DXSetComponentValue(field, "connections", (Object)new_connections))
484 	goto error;
485 
486     if (!DXChangedComponentValues(field, "positions"))
487 	goto error;
488     if (!DXChangedComponentValues(field, "connections"))
489 	goto error;
490 
491     if (!DXEndField(field))
492 	goto error;
493 
494     return OK;
495 
496   error:
497     DXDelete((Object)new_positions);
498     DXDelete((Object)new_connections);
499     return ERROR;
500 }
501 
502 
503 #define MAXSHAPE 64
504 
dodata(Rtaskarg * tp)505 static Error dodata (Rtaskarg *tp)
506 {
507     Field field;
508     Array a, new_a = NULL;
509 
510     int yfastest;
511     int loopcount[2];
512     int cnum;
513     int invert;
514 
515     Pointer fromptr, toptr;
516     int size;
517     ubyte *from1, *to1;
518     int *from4, *to4;
519     typedef struct {
520 	ubyte _u[3];
521     } threebytes;
522     threebytes *from3, *to3;
523     typedef struct {
524 	int _i[3];
525     } twelvebytes;
526     twelvebytes *from12, *to12;
527     int i, ii, j, jj, k;
528 
529     char *name, *dattr, *rattr;
530     int nodeps, norefs;
531     int items;
532     Type type;
533     Category cat;
534     int rank, shape[MAXSHAPE];
535 
536     int didwork;
537 
538 
539 
540     field = (Field)tp->o;
541 
542     yfastest = tp->grid & Y_FASTEST;
543     /*negative_x = tp->grid & NEGATIVE_X;*/
544     /*negative_y = tp->grid & NEGATIVE_Y;*/
545 
546 #if 1    /* in again */   /*xxx  out for now */
547 
548 #if 0
549     DXMessage("yfastest = %d, neg_x = %d, neg_y = %d",
550 	      yfastest, negative_x, negative_y);
551 #endif
552 
553     /* take care of all the possibilities; inverted axis and
554      * different data orientations.  alter tp->how to make it match
555      * what happens with the x-fastest, positive x & y deltas.
556      */
557     if (tp->how != 0) {
558 	invert  = tp->grid &  Y_FASTEST ? 1 : 0;
559 	invert += tp->grid & NEGATIVE_X ? 1 : 0;
560 	invert += tp->grid & NEGATIVE_Y ? 1 : 0;
561 	switch (invert) {
562 	  case 2:
563 	    switch (tp->how) {
564 	      case 4:
565 		tp->how = 7;
566 		break;
567 	      case 5:
568 		tp->how = 4;
569 		break;
570 	      case 6:
571 		tp->how = 5;
572 		break;
573 	      case 7:
574 		tp->how = 6;
575 		break;
576 	    }
577 	    break;
578 	  case 1:
579 	  case 3:
580 	    switch (tp->how) {
581 	      case 1:
582 		tp->how = 3;
583 		break;
584 	      case 3:
585 		tp->how = 1;
586 		break;
587 	      case 5:
588 		tp->how = 6;
589 		break;
590 	      case 6:
591 		tp->how = 5;
592 		break;
593 	      case 4:
594 		tp->how = 7;
595 		break;
596 	      case 7:
597 		tp->how = 4;
598 		break;
599 	    }
600 	    break;
601 	}
602     }
603 
604     /* now that we know something about how the input image is
605      * oriented, see how the user wants it to go out.
606      */
607     switch (tp->how) {
608       case 0:
609 	/* just make the image in the order the Display module
610          * is happiest handling.
611 	 */
612 	break;
613 
614       case 4:
615 	tp->alterdata |= (yfastest ? INV_ROWS : INV_COLS);
616 	break;
617 
618       case 6:
619 	tp->alterdata |= (yfastest ? INV_COLS : INV_ROWS);
620 	break;
621 
622       case 2:
623 	tp->alterdata |= INV_ROWS;
624 	tp->alterdata |= INV_COLS;
625 	break;
626 
627       case 7:
628 	tp->altergrid ^= INV_EXCHANGE;
629 	tp->alterdata |= INV_TRANSPOSE;
630 	break;
631 
632       case 3:
633 	tp->altergrid ^= INV_EXCHANGE;
634 	tp->alterdata |= INV_TRANSPOSE;
635 	tp->alterdata |= (yfastest ? INV_ROWS : INV_COLS);
636 	break;
637 
638       case 1:
639 	tp->altergrid ^= INV_EXCHANGE;
640 	tp->alterdata |= INV_TRANSPOSE;
641 	tp->alterdata |= (yfastest ? INV_COLS : INV_ROWS);
642 	break;
643 
644       case 5:
645 	tp->altergrid ^= INV_EXCHANGE;
646 	tp->alterdata |= INV_TRANSPOSE;
647 	tp->alterdata |= INV_ROWS;
648 	tp->alterdata |= INV_COLS;
649 	break;
650     }
651 
652 
653     /* now compare what happens to the grid vs data.  if we're
654      * moving the grid we can leave the data alone; if we are
655      * not moving the grid we have to move the data.
656      */
657     if ((tp->altergrid & INV_TRANSPOSE) ^ (tp->alterdata & INV_TRANSPOSE))
658 	tp->dotodata |= INV_TRANSPOSE;
659     if ((tp->altergrid & INV_ROWS) ^ (tp->alterdata & INV_ROWS))
660 	tp->dotodata |= INV_ROWS;
661     if ((tp->altergrid & INV_COLS) ^ (tp->alterdata & INV_COLS))
662 	tp->dotodata |= INV_COLS;
663 
664 
665 #else
666 
667 
668 
669     switch (tp->how) {
670       case 0:
671 	/* just make the image in the order the Display module
672          * is happiest handling.
673 	 */
674 	break;
675 
676       case 4:
677 	tp->alterdata |= INV_COLS;
678 	break;
679 
680       case 6:
681 	tp->alterdata |= INV_ROWS;
682 	break;
683 
684       case 2:
685 	tp->alterdata |= INV_ROWS | INV_COLS;
686 	break;
687 
688 #define FLIP(what) ((tp->altergrid & (what)) ^ (what))
689 
690       case 7:
691 #if 0
692 	tp->altergrid ^= INV_EXCHANGE;
693 	tp->alterdata |= FLIP (INV_TRANSPOSE);
694 #endif
695 	tp->alterdata |= INV_EXCHANGE | INV_TRANSPOSE | INV_EXCHANGE;
696 	break;
697 
698       case 3:
699 #if 0
700 	tp->altergrid ^= INV_EXCHANGE;
701 	tp->alterdata |= FLIP (INV_TRANSPOSE | INV_COLS);
702 #endif
703 	tp->alterdata |= INV_EXCHANGE | INV_TRANSPOSE | INV_EXCHANGE | INV_COLS;
704 	break;
705 
706       case 1:
707 #if 0
708 	tp->altergrid ^= INV_EXCHANGE;
709 	tp->alterdata |= FLIP (INV_TRANSPOSE | INV_ROWS);
710 #endif
711 	tp->alterdata |= INV_EXCHANGE | INV_TRANSPOSE | INV_EXCHANGE | INV_ROWS;
712 	break;
713 
714       case 5:
715 #if 0
716 	tp->altergrid ^= INV_EXCHANGE;
717 	tp->alterdata |= FLIP (INV_TRANSPOSE | INV_ROWS | INV_COLS);
718 #endif
719 	tp->alterdata |= INV_EXCHANGE | INV_TRANSPOSE | INV_EXCHANGE | INV_ROWS | INV_COLS;
720 	break;
721     }
722 
723 
724 #if 0
725     DXMessage("before grid = 0x%04x, data = 0x%04x",
726 	      tp->altergrid, tp->alterdata);
727 #endif
728 
729 #define TOGGLE(what, what1, bits)  (what ^= (what1 & bits))
730 
731     TOGGLE(tp->altergrid, tp->alterdata, INV_EXCHANGE);
732     tp->alterdata &= ~INV_EXCHANGE;
733     TOGGLE(tp->alterdata, tp->altergrid, INV_TRANSPOSE);
734     TOGGLE(tp->alterdata, tp->altergrid, INV_ROWS);
735     TOGGLE(tp->alterdata, tp->altergrid, INV_COLS);
736 
737 #if 0
738     DXMessage("after grid = 0x%04x, data = 0x%04x",
739 	      tp->altergrid, tp->alterdata);
740 #endif
741 
742 
743 #endif
744 
745 #if 0
746     DXMessage("altergrid = %d, alterdata = %d, dotodata = %d",
747 	      tp->altergrid, tp->alterdata, tp->dotodata);
748 #endif
749 
750     /* if we don't have to change the data in the components, we're done */
751     if (tp->dotodata == INV_NOTHING)
752 	return OK;
753 
754 
755     for (cnum = 0;
756 	 (a = (Array)DXGetEnumeratedComponentValue(field, cnum, &name)); cnum++) {
757 
758 	if (!strcmp(name, "positions") || !strcmp(name, "connections"))
759 	    continue;
760 
761 	/* if no deps and no refs, just pass this component through untouched.
762 	 */
763 	nodeps = 0;
764 	dattr = NULL;
765 	if (!DXGetStringAttribute((Object)a, "dep", &dattr) || !dattr)
766 	    nodeps++;
767 
768 	norefs = 0;
769 	rattr = NULL;
770 	if (!DXGetStringAttribute((Object)a, "ref", &rattr) || !rattr)
771 	    norefs++;
772 
773 	if (nodeps && norefs)
774 	    continue;
775 
776 	if (dattr && strcmp(dattr, "positions") && strcmp(dattr, "connections"))
777 	    continue;
778 
779 	if (nodeps && !norefs) {
780 	    /* this is not dep anything but is ref something.  to do this
781 	     * right, i need to reorder the refs the same way i reorder
782 	     * the target.  i don't have the code to do this written, but
783 	     * i've got it in slab/slice and other places.
784 	     */
785 	    continue;
786 	}
787 
788 
789 	/* ok, we got a live one */
790 
791 	if (!strcmp(dattr, "positions")) {
792 	    loopcount[0] = tp->count[0];
793 	    loopcount[1] = tp->count[1];
794 	} else {
795 	    loopcount[0] = tp->count[0] - 1;
796 	    loopcount[1] = tp->count[1] - 1;
797 	}
798 
799 
800 	size = DXGetItemSize(a);
801 	fromptr = DXGetArrayData(a);
802 	if (!fromptr)
803 	    goto error;
804 
805 	if (!DXGetArrayInfo(a, &items, &type, &cat, &rank, shape))
806 	    goto error;
807 
808 	new_a = DXNewArrayV(type, cat, rank, shape);
809 	if (!new_a)
810 	    goto error;
811 
812 	if (!DXAddArrayData(new_a, 0, items, NULL))
813 	    goto error;
814 
815 	toptr = DXGetArrayData(new_a);
816 	if (!toptr)
817 	    goto error;
818 
819 #define MOVEDATA1(op, np) \
820  \
821      if (tp->dotodata == INV_TRANSPOSE) { \
822 	 for (i=0; i<loopcount[1]; i++) \
823 	     for (j=0; j<loopcount[0]; j++) \
824 		 np[i*loopcount[0] + j] = op[j*loopcount[1] + i]; \
825     } \
826 \
827     if (tp->dotodata == INV_ROWS) { \
828 	for (i=0; i<loopcount[1]; i++) \
829 	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
830 		np[j*loopcount[1] + i] = op[jj*loopcount[1] + i]; \
831     } \
832 \
833     if (tp->dotodata == INV_COLS) { \
834 	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
835 	    for (j=0; j<loopcount[0]; j++) \
836 		np[j*loopcount[1] + i] = op[j*loopcount[1] + ii]; \
837     } \
838 \
839     if (tp->dotodata == (INV_COLS|INV_ROWS)) { \
840 	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
841 	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
842 		np[j*loopcount[1] + i] = op[jj*loopcount[1] + ii]; \
843     } \
844 \
845     if (tp->dotodata == (INV_TRANSPOSE|INV_COLS)) { \
846 	for (i=0; i<loopcount[1]; i++) \
847 	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
848 		np[i*loopcount[0] + j] = op[jj*loopcount[1] + i]; \
849     } \
850 \
851     if (tp->dotodata == (INV_TRANSPOSE|INV_ROWS)) { \
852 	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
853 	    for (j=0; j<loopcount[0]; j++) \
854 		np[i*loopcount[0] + j] = op[j*loopcount[1] + ii]; \
855     } \
856 \
857     if (tp->dotodata == (INV_TRANSPOSE|INV_COLS|INV_ROWS)) { \
858 	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
859 	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
860 		np[i*loopcount[0] + j] = op[jj*loopcount[1] + ii]; \
861     }
862 
863 
864 #define MOVEDATA(op, np, size) \
865  \
866     if (tp->dotodata == INV_TRANSPOSE) { \
867 	for (i=0; i<loopcount[1]; i++) \
868 	    for (j=0; j<loopcount[0]; j++) \
869 		for (k=0; k<size; k++) \
870 		    np[(i*loopcount[0] + j)*size + k] = \
871 			op[(j*loopcount[1] + i)*size + k]; \
872     } \
873 \
874     if (tp->dotodata == INV_ROWS) { \
875 	for (i=0; i<loopcount[1]; i++) \
876 	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
877 		for (k=0; k<size; k++) \
878 		    np[(j*loopcount[1] + i)*size + k] = \
879 			op[(jj*loopcount[1] + i)*size + k]; \
880     } \
881 \
882     if (tp->dotodata == INV_COLS) { \
883 	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
884 	    for (j=0; j<loopcount[0]; j++) \
885 		for (k=0; k<size; k++) \
886 		    np[(j*loopcount[1] + i)*size + k] = \
887 			op[(j*loopcount[1] + ii)*size + k]; \
888     } \
889 \
890     if (tp->dotodata == (INV_COLS|INV_ROWS)) { \
891 	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
892 	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
893 		for (k=0; k<size; k++) \
894 		    np[(j*loopcount[1] + i)*size + k] = \
895 			op[(jj*loopcount[1] + ii)*size + k]; \
896     } \
897 \
898     if (tp->dotodata == (INV_TRANSPOSE|INV_COLS)) { \
899 	for (i=0; i<loopcount[1]; i++) \
900 	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
901 		for (k=0; k<size; k++) \
902 		    np[(i*loopcount[0] + j)*size + k] = \
903 			op[(jj*loopcount[1] + i)*size + k]; \
904     } \
905 \
906     if (tp->dotodata == (INV_TRANSPOSE|INV_ROWS)) { \
907 	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
908 	    for (j=0; j<loopcount[0]; j++) \
909 		for (k=0; k<size; k++) \
910 		    np[(i*loopcount[0] + j)*size + k] = \
911 			op[(j*loopcount[1] + ii)*size + k]; \
912     } \
913 \
914     if (tp->dotodata == (INV_TRANSPOSE|INV_COLS|INV_ROWS)) { \
915 	for (i=0, ii=loopcount[1]-1; i<loopcount[1]; i++, --ii) \
916 	    for (j=0, jj=loopcount[0]-1; j<loopcount[0]; j++, --jj) \
917 		for (k=0; k<size; k++) \
918 		    np[(i*loopcount[0] + j)*size + k] = \
919 			op[(jj*loopcount[1] + ii)*size + k]; \
920     }
921 
922 
923 	if (size == 4) {
924 	    from4 = (int *)fromptr;
925 	    to4 = (int *)toptr;
926 	    MOVEDATA1(from4, to4);
927 	}
928 
929 	else if (size == 12) {
930 	    from12 = (twelvebytes *)fromptr;
931 	    to12 = (twelvebytes *)toptr;
932 	    MOVEDATA1(from12, to12);
933 	}
934 
935 	else if (size == 3) {
936 	    from3 = (threebytes *)fromptr;
937 	    to3 = (threebytes *)toptr;
938 	    MOVEDATA1(from3, to3);
939 	}
940 
941 	else {
942 	    from1 = (ubyte *)fromptr;
943 	    to1 = (ubyte *)toptr;
944 	    MOVEDATA(from1, to1, size);
945 	}
946 
947 
948 	if (!DXSetComponentValue(field, name, (Object)new_a))
949 	    goto error;
950 
951 	new_a = NULL;
952 
953     }
954 
955     /* we are changing positions & connections - make sure things
956      * which are derived from them are deleted.
957      */
958     DXChangedComponentValues(field, "positions");
959     DXChangedComponentValues(field, "connections");
960 
961 
962 
963     /* do this as a second pass so the enumeration count doesn't get mangled.
964      */
965   again:
966     didwork = 0;
967     for (i=0; (a = (Array)DXGetEnumeratedComponentValue(field, i, &name)); i++) {
968 
969 	if (!strcmp(name, "positions") || !strcmp(name, "connections"))
970 	    continue;
971 
972 	/* if no deps and no refs, just pass this component through untouched.
973 	 */
974 	nodeps = 0;
975 	dattr = NULL;
976 	if (!DXGetStringAttribute((Object)a, "dep", &dattr) || !dattr)
977 	    nodeps++;
978 
979 	norefs = 0;
980 	rattr = NULL;
981 	if (!DXGetStringAttribute((Object)a, "ref", &rattr) || !rattr)
982 	    norefs++;
983 
984 	if (nodeps && norefs)
985 	    continue;
986 
987 	if (dattr && strcmp(dattr, "positions") && strcmp(dattr, "connections"))
988 	    continue;
989 
990 
991 	/* the list of things which aren't going to be valid at the end
992          * after moving the positions & connections, so nuke them now rather
993 	 * than passing them through and having them be wrong.
994 	 */
995 	if ((rattr && !strcmp(rattr, "positions"))
996 	    || (rattr && !strcmp(rattr, "connections"))) {
997 	    DXSetComponentValue(field, name, NULL);  /* screws enum count? */
998 	    DXChangedComponentValues(field, name);
999 	    didwork++;
1000 	    goto again;
1001 	}
1002 
1003     }
1004 
1005     /* done */
1006     if (!DXEndField(field))
1007 	return ERROR;
1008 
1009     return OK;
1010 
1011   error:
1012     DXDelete((Object)new_a);
1013     return ERROR;
1014 }
1015 
1016 
1017 
1018 
1019 
1020 
1021