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  * $Header: /src/master/dx/src/exec/dxmods/shade.c,v 1.6 2006/01/03 17:02:25 davidt Exp $
10  */
11 
12 #include <dxconfig.h>
13 
14 #if defined(HAVE_STRING_H)
15 #include <string.h>
16 #endif
17 
18 #include <stdio.h>
19 #include <math.h>
20 #include <dx/dx.h>
21 #include "_normals.h"
22 
23 static Error ShadeField(Field, float, int, float, float);
24 static Error DoNormals(Object, int, char *, int);
25 static Error ShadeIt(Object, int, char *, float, int, float, float, int);
26 static Error CheckNormalsDirection(Object, int);
27 static Error CheckNormalsDirectionField(Field, int);
28 
29 Error
m_Shade(Object * in,Object * out)30   m_Shade(Object *in, Object *out)
31 {
32   int shade, shininess, flipfront;
33   char *how;
34   float specular, ambient, diffuse;
35   Object outo=NULL;
36 
37 
38 
39   if (!in[0]) {
40     DXSetError(ERROR_BAD_PARAMETER, "object must be specified");
41     return ERROR;
42   }
43 
44 
45   /* shade param */
46   if (!in[1]) {
47     shade = 1;
48   }
49   else {
50     if (!DXExtractInteger(in[1], &shade)) {
51       DXSetError(ERROR_BAD_PARAMETER,"shade must be either 0 or 1");
52       goto error;
53     }
54   }
55   if ((shade < 0)||(shade > 1)) {
56     DXSetError(ERROR_BAD_PARAMETER,"shade must be either 0 or 1");
57     goto error;
58   }
59 
60 
61   /* how param */
62   if (!in[2]) {
63     how = "default";
64   }
65   else {
66     if (!DXExtractString(in[2], &how)) {
67       DXSetError(ERROR_BAD_PARAMETER,
68 		 "how must be either `faceted' or `smooth'");
69       goto error;
70     }
71     /* XXX convert to lower case */
72     if (strcmp(how,"faceted")&&(strcmp(how,"smooth"))) {
73       DXSetError(ERROR_BAD_PARAMETER,
74 		 "how must be either `faceted' or `smooth'");
75       goto error;
76     }
77   }
78   if (in[2] && (shade==0)) {
79     DXWarning("how is ignored because shade is equal to 0");
80   }
81 
82 
83   /* specular param */
84   if (!in[3]) {
85     specular = -1.0;
86   }
87   else {
88     if (!DXExtractFloat(in[3], &specular)) {
89       DXSetError(ERROR_BAD_PARAMETER,
90 		 "specular must be a non-negative scalar value");
91       goto error;
92     }
93     if (specular < 0) {
94       DXSetError(ERROR_BAD_PARAMETER,
95 		 "specular must be a non-negative scalar value");
96       goto error;
97     }
98   }
99 
100 
101   /* shininess */
102   if (!in[4]) {
103     shininess = -1;
104   }
105   else {
106     if (!DXExtractInteger(in[4], &shininess)) {
107       DXSetError(ERROR_BAD_PARAMETER,
108 		 "shininess must be a non-negative integer");
109       goto error;
110     }
111     if (shininess < 0) {
112       DXSetError(ERROR_BAD_PARAMETER,
113 		 "shininess must be a non-negative integer");
114       goto error;
115     }
116   }
117 
118 
119   /* diffuse param */
120   if (!in[5]) {
121     diffuse = -1.0;
122   }
123   else {
124     if (!DXExtractFloat(in[5], &diffuse)) {
125       DXSetError(ERROR_BAD_PARAMETER,
126 		 "diffuse must be a non-negative scalar value");
127       goto error;
128     }
129     if (diffuse < 0) {
130       DXSetError(ERROR_BAD_PARAMETER,
131 		 "diffuse must be a non-negative scalar value");
132       goto error;
133     }
134   }
135 
136   /* ambient param */
137   if (!in[6]) {
138     ambient = -1.0;
139   }
140   else {
141     if (!DXExtractFloat(in[6], &ambient)) {
142       DXSetError(ERROR_BAD_PARAMETER,
143 		 "ambient must be a non-negative scalar value");
144       goto error;
145     }
146     if (ambient < 0) {
147       DXSetError(ERROR_BAD_PARAMETER,
148 		 "ambient must be a non-negative scalar value");
149       goto error;
150     }
151   }
152 
153   /* whether or not to flip front and back */
154   flipfront = 0;
155 
156   if (!(outo = DXCopy(in[0], COPY_STRUCTURE)))
157     goto error;
158 
159   /* do the stuff with normals (must be done at the field level) */
160 
161   if (!ShadeIt(outo, shade, how, specular, shininess, diffuse, ambient,
162                flipfront))
163     goto error;
164 
165 
166   out[0] = outo;
167   return OK;
168 
169  error:
170   DXDelete((Object)outo);
171   return ERROR;
172 }
173 
174 
ShadeIt(Object obj,int shade,char * how,float specular,int shininess,float diffuse,float ambient,int flipfront)175 static Error ShadeIt(Object obj, int shade, char *how, float specular,
176                      int shininess, float diffuse, float ambient,
177                      int flipfront)
178 {
179   Object child;
180   int i;
181 
182   switch (DXGetObjectClass(obj)) {
183   case (CLASS_FIELD):
184     /* do the normals stuff here */
185     if (!DoNormals((Object)obj, shade, how, flipfront))
186       goto error;
187     if (!ShadeField((Field)obj, specular, shininess,
188 		    diffuse, ambient))
189       goto error;
190     break;
191   case (CLASS_GROUP):
192     switch (DXGetGroupClass((Group)obj)) {
193     case (CLASS_COMPOSITEFIELD):
194       /* do the normals stuff here, then continue through the
195 	 composite field */
196       if (!DoNormals((Object)obj, shade, how, flipfront))
197 	goto error;
198       for (i=0; (child=DXGetEnumeratedMember((Group)obj,i,NULL));i++){
199 	if (!ShadeField((Field)child, specular, shininess, diffuse,
200 			ambient))
201 	  goto error;
202       }
203       break;
204     default:
205       for (i=0; (child=DXGetEnumeratedMember((Group)obj,i,NULL)); i++) {
206 	if (!ShadeIt(child, shade, how, specular, shininess,
207 		     diffuse, ambient, flipfront))
208 	  goto error;
209       }
210       break;
211     }
212     break;
213   case (CLASS_XFORM):
214     if (!DXGetXformInfo((Xform)obj, &child, NULL))
215       goto error;
216     if (!ShadeIt(child, shade, how, specular, shininess, diffuse,
217 		 ambient, flipfront))
218       goto error;
219     break;
220   case (CLASS_CLIPPED):
221     if (!DXGetClippedInfo((Clipped)obj, &child, NULL))
222       goto error;
223     if (!ShadeIt(child, shade, how, specular, shininess, diffuse,
224 		 ambient, flipfront))
225       goto error;
226     break;
227   case (CLASS_SCREEN):
228     if (!DXGetScreenInfo((Screen)obj, &child, NULL, NULL))
229       goto error;
230     if (!ShadeIt(child, shade, how, specular, shininess, diffuse,
231 		 ambient, flipfront))
232       goto error;
233     break;
234   default:
235     DXSetError(ERROR_DATA_INVALID,
236 	       "object must be a field, group, xform, or clipped object");
237     goto error;
238   }
239 
240   return OK;
241 
242  error:
243   return ERROR;
244 
245 }
246 
ShadeField(Field field,float specular,int shininess,float diffuse,float ambient)247 static Error ShadeField(Field field, float specular,
248                         int shininess, float diffuse, float ambient)
249 {
250   /* set all the rendering attributes */
251   if (specular != -1.0)
252     if (!DXSetFloatAttribute((Object)field, "specular", specular))
253       goto error;
254   if (shininess != -1)
255     if (!DXSetIntegerAttribute((Object)field, "shininess", shininess))
256       goto error;
257   if (diffuse != -1)
258     if (!DXSetFloatAttribute((Object)field, "diffuse", diffuse))
259       goto error;
260   if (ambient != -1)
261     if (!DXSetFloatAttribute((Object)field, "ambient", ambient))
262       goto error;
263 
264   if (!DXEndField(field))
265     goto error;
266   return OK;
267 
268  error:
269   return ERROR;
270 
271 }
272 
DoNormals(Object obj,int shade,char * how,int flipfront)273 static Error DoNormals(Object obj, int shade, char *how, int flipfront)
274 {
275   int i;
276   char *attr;
277   Field first, child;
278   int shadeattribute;
279 
280   /* this is called at the Field or Composite field level */
281 
282 
283   switch (DXGetObjectClass(obj)) {
284   case CLASS_FIELD:
285 
286 
287     /* if we don't want the object shaded... */
288     /* regardless of what how is, make sure either there aren't any normals,
289      * or if there are, that the "no shade" attribute is set */
290     if (shade == 0) {
291       /* if there are normals, we need to disable them */
292       if (DXGetComponentValue((Field)obj, "normals")) {
293         if (!DXSetIntegerAttribute((Object)obj, "shade", 0))
294 	  goto error;
295       }
296     }
297 
298     /* we want the object shaded */
299     else if (shade==1) {
300       /* if there's a shade=0 attribute set, set it to 1 */
301       if (DXGetAttribute((Object)obj,"shade")) {
302          if (!DXGetIntegerAttribute((Object)obj, "shade", &shadeattribute))
303            goto error;
304          if (shadeattribute==0) {
305            /* set it to 1 */
306            if (!DXSetIntegerAttribute((Object)obj, "shade", 1))
307              goto error;
308          }
309          else if (shadeattribute != 1) {
310            DXSetError(ERROR_DATA_INVALID,
311                       "invalid shade attribute, must be 0 or 1");
312            goto error;
313          }
314       }
315       if (!strcmp(how, "default")) {
316 	/* only do something if there aren't any normals. otherwise
317 	 * do nothing */
318 	if (!DXGetComponentValue((Field)obj, "normals")) {
319 
320           /* follow data if present */
321           if (DXGetComponentValue((Field)obj,"data")) {
322             attr = DXGetString((String)DXGetComponentAttribute((Field)obj,
323                                                                "data","dep"));
324             if (!attr) {
325 	      DXSetError(ERROR_MISSING_DATA,
326 			 "missing data dependent attribute");
327 	      goto error;
328             }
329           }
330           else {
331             attr = "positions";
332           }
333 
334           if (!_dxfNormalsObject((Object)obj, attr))
335 	    goto error;
336 	}
337         else {
338           /* check whether the normals are consistent with the connections */
339           if (!CheckNormalsDirection(obj, flipfront))
340              goto error;
341         }
342       }
343       else if (!strcmp(how,"faceted")) {
344 	/* call normals dep connections (make sure this routine does the
345 	 * check whether they are there already) */
346         if (!_dxfNormalsObject((Object)obj,"connections"))
347 	  goto error;
348       }
349       else if (!strcmp(how,"smooth")) {
350 	/* call normals dep positions (make sure this routine does the
351 	 * check whether they are there already) */
352         if (!_dxfNormalsObject((Object)obj,"positions"))
353 	  goto error;
354       }
355       break;
356     case (CLASS_GROUP):
357       /* it's a composite field */
358       if (shade == 0) {
359 	/* if there are normals, we need to disable them */
360 	first = (Field)DXGetEnumeratedMember((Group)obj, 0, NULL);
361 	/* if there are normals, we need to disable them */
362 	if (DXGetComponentValue(first, "normals")) {
363 
364 	  for (i=0; (child=(Field)DXGetEnumeratedMember((Group)obj,i,NULL));i++){
365 	    if (!DXSetIntegerAttribute((Object)child, "shade", 0))
366 	      goto error;
367 	  }
368 	}
369       }
370       /* we want the object shaded */
371       else if (shade==1) {
372 	if (!strcmp(how, "default")) {
373 	  /* only do something if there aren't any normals. otherwise
374 	   * do nothing */
375 	  first = (Field)DXGetEnumeratedMember((Group)obj, 0, NULL);
376 	  if (!DXGetComponentValue(first, "normals")) {
377 
378 	    /* follow data if present */
379 	    if (DXGetComponentValue(first,"data")) {
380 	      attr = DXGetString((String)DXGetComponentAttribute(first,
381 								 "data",
382 								 "dep"));
383 	      if (!attr) {
384 		DXSetError(ERROR_MISSING_DATA,
385 			   "missing data dependent attribute");
386 		goto error;
387 	      }
388 	    }
389 	    else {
390 	      attr = "positions";
391 	    }
392 
393 	    /* call normals on the composite field */
394 	    if (!_dxfNormalsObject(obj, attr))
395 	      goto error;
396 	  }
397           else {
398             /* check whether the normals are consistent with the connections */
399             if (!CheckNormalsDirection(obj, flipfront))
400                goto error;
401           }
402 	}
403 	else if (!strcmp(how,"faceted")) {
404 	  /* call normals dep connections (make sure this routine does the
405 	   * check whether they are there already) */
406           if (!_dxfNormalsObject((Object)obj,"connections"))
407 	    goto error;
408 	}
409 	else if (!strcmp(how,"smooth")) {
410 	  /* call normals dep positions (make sure this routine does the
411 	   * check whether they are there already) */
412           if (!_dxfNormalsObject((Object)obj,"positions"))
413 	    goto error;
414 	}
415       }
416     }
417     break;
418   default:
419     break;
420   }
421 
422   return OK;
423  error:
424   return ERROR;
425 
426 }
427 
428 
429 /* this routine checks the normals against the direction of the
430    connections. If they disagree, it fixes the normals. This can
431    be called on either a field or a composite field. */
CheckNormalsDirection(Object obj,int flipfront)432 static Error CheckNormalsDirection(Object obj, int flipfront)
433 {
434    int i;
435    Object child;
436 
437    switch (DXGetObjectClass(obj)) {
438      case (CLASS_FIELD):
439        if (!CheckNormalsDirectionField((Field)obj, flipfront))
440           goto error;
441        break;
442      case (CLASS_GROUP):
443        if (DXGetGroupClass((Group)obj) != CLASS_COMPOSITEFIELD) {
444 	  DXSetError(ERROR_UNEXPECTED,"unexpected class in CheckNormalsDirection");
445 	  goto error;
446        }
447        for (i=0; (child = DXGetEnumeratedMember((Group)obj,i,NULL));i++){
448           if (!CheckNormalsDirectionField((Field)child, flipfront))
449             goto error;
450        }
451        break;
452      default:
453        DXSetError(ERROR_UNEXPECTED,"unexpected class in CheckNormalsDirection");
454        goto error;
455 
456    }
457 
458    return OK;
459 error:
460    return ERROR;
461 }
462 
CheckNormalsDirectionField(Field obj,int flipfront)463 static Error CheckNormalsDirectionField(Field obj, int flipfront)
464 {
465   Array connections, normals, positions, newnormals=NULL, newconnections=NULL;
466   Point *pos1, *pos2, *pos3, *norm, vec1, vec2, crossprod, newnormal;
467   Point position1, position2, position3;
468   Vector zerovec={0.0, 0.0, 0.0};
469   char *str1, *str2;
470   Object dep, eType;
471   ArrayHandle positionshandle=NULL, normalshandle=NULL, connectionshandle=NULL;
472   int rank, shape[10], *connections_ptr, conn1, conn2, conn3;
473   int numitems, i=0, *cptr1, numcon, scratchtri[3];
474   int scratchquad[4];
475   float dotprod, scratch[3];
476   Triangle newtri, *triptr=NULL;
477   Quadrilateral newquad, *quadptr=NULL;
478 
479   /* get the connections component */
480   connections = (Array)DXGetComponentValue(obj, "connections");
481   if (!connections) {
482      goto done;
483   }
484   if (!DXGetArrayInfo(connections, &numcon, NULL, NULL, NULL, NULL))
485     goto error;
486   /* get the element type attribute */
487   eType = DXGetAttribute((Object)connections, "element type");
488   if (! eType || DXGetObjectClass(eType) != CLASS_STRING) {
489     DXSetError(ERROR_DATA_INVALID,
490 	       "invalid or missing element type attribute");
491     goto error;
492   }
493   str1 = DXGetString((String)eType);
494   /* what about faces XXX ? */
495   if (strcmp(str1, "quads") && strcmp(str1, "triangles"))
496     goto done;
497 
498 
499   connectionshandle = DXCreateArrayHandle(connections);
500   if (!connectionshandle)
501     goto error;
502 
503   /* first do the flipping on the connections. That way we only
504      flip the normals once */
505   if (flipfront) {
506     if (!strcmp(str1,"triangles")) {
507       newconnections = DXNewArray(TYPE_INT, CATEGORY_REAL, 1, 3);
508       if (!newconnections)
509 	goto error;
510       if (!DXAddArrayData(newconnections, 0, numcon, NULL))
511 	goto error;
512       for (i=0; i<numcon; i++) {
513 	if (NULL ==
514 	    (triptr = DXIterateArray(connectionshandle, i,
515 				     triptr, (Pointer)scratchtri)))
516 	  goto error;
517 	newtri.p = triptr->p;
518 	newtri.q = triptr->r;
519 	newtri.r = triptr->q;
520 	if (!DXAddArrayData(newconnections, i, 1, &newtri))
521 	  goto error;
522       }
523     }
524     else { /* quads */
525 	/* else I'll treat it as irregular XXX */
526 	newconnections = DXNewArray(TYPE_INT, CATEGORY_REAL, 1, 4);
527 	if (!newconnections)
528 	  goto error;
529 	if (!DXAddArrayData(newconnections, 0, numcon, NULL))
530 	  goto error;
531 	for (i=0; i<numcon; i++) {
532 	  if (NULL ==
533 	      (quadptr = DXIterateArray(connectionshandle, i,
534 					quadptr, (Pointer)scratchquad)))
535 	    goto error;
536 	  /* XXX this is irregular */
537 	  newquad.p = quadptr->r;
538 	  newquad.q = quadptr->s;
539 	  newquad.r = quadptr->p;
540 	  newquad.s = quadptr->q;
541 	  if (!DXAddArrayData(newconnections, i, 1, &newquad))
542 	    goto error;
543 	}
544     }
545     if (!DXSetComponentValue(obj, "connections", (Object)newconnections))
546       goto error;
547     newconnections = NULL;
548     DXChangedComponentValues(obj,"connections");
549     connections = (Array)DXGetComponentValue(obj, "connections");
550     connectionshandle = DXCreateArrayHandle(connections);
551     if (!connectionshandle)
552        goto error;
553   }
554 
555   /* get the dependency attribute of the normals */
556   normals = (Array)DXGetComponentValue(obj, "normals");
557   if (!normals) {
558     /* I don't think this should happen, but if it does, it's ok */
559     goto done;
560   }
561 
562   dep = DXGetAttribute((Object)normals, "dep");
563   if (! dep || DXGetObjectClass(dep) != CLASS_STRING) {
564     DXSetError(ERROR_DATA_INVALID,
565 	       "invalid or missing normals dep attribute");
566     goto error;
567   }
568   str2 = DXGetString((String)dep);
569   if (strcmp(str2, "positions") && strcmp(str2, "connections")) {
570     DXSetError(ERROR_DATA_INVALID,"unrecognized normals dep attribute");
571     goto error;
572   }
573 
574   positions = (Array)DXGetComponentValue(obj, "positions");
575   if (!positions) {
576     goto done;
577   }
578 
579 
580   positionshandle = DXCreateArrayHandle(positions);
581   if (!positionshandle)
582     goto error;
583   if (!DXGetArrayInfo(positions, NULL, NULL, NULL, &rank, shape))
584     goto error;
585   if (rank != 1) {
586     DXSetError(ERROR_DATA_INVALID,"rank %d positions not supported", rank);
587     goto error;
588   }
589 
590   normalshandle = DXCreateArrayHandle(normals);
591   if (!normalshandle)
592     goto error;
593   if (!DXGetArrayInfo(normals, &numitems, NULL, NULL, NULL, NULL))
594     goto error;
595 
596   if ((shape[0] != 2)&&(shape[0] != 3)) {
597     DXSetError(ERROR_DATA_INVALID,
598 	       "only 2D and 3D positions supported for quads and triangles");
599     goto error;
600   }
601 
602   if (!strcmp(str1, "triangles")) {
603     /* check the first triangle */
604     connections_ptr = (int *)DXGetArrayData(connections);
605     conn1 = connections_ptr[0];
606     conn2 = connections_ptr[1];
607     conn3 = connections_ptr[2];
608 
609     if (NULL == (pos1 = DXGetArrayEntry(positionshandle, conn1, scratch)))
610       goto error;
611     if (shape[0]==2)
612       position1 = DXPt(pos1->x, pos1->y, 0.0);
613     else
614       position1 = DXPt(pos1->x, pos1->y, pos1->z);
615 
616     if (NULL == (pos2 = DXGetArrayEntry(positionshandle, conn2, scratch)))
617       goto error;
618     if (shape[0]==2)
619       position2 = DXPt(pos2->x, pos2->y, 0.0);
620     else
621       position2 = DXPt(pos2->x, pos2->y, pos2->z);
622 
623     if (NULL == (pos3 = DXGetArrayEntry(positionshandle, conn3, scratch)))
624       goto error;
625     if (shape[0]==2)
626       position3 = DXPt(pos3->x, pos3->y, 0.0);
627     else
628       position3 = DXPt(pos3->x, pos3->y, pos3->z);
629 
630     vec1 = DXSub(position2, position1);
631     vec2 = DXSub(position3, position2);
632     crossprod = DXCross(vec1, vec2);
633 
634     /* now grab an appropriate normal */
635     if (!strcmp(str2,"positions")) {
636       if (NULL ==
637 	  (norm = DXGetArrayEntry(normalshandle, conn1, scratch)))
638 	goto error;
639     }
640     else {
641       if (NULL ==
642 	  (norm = DXGetArrayEntry(normalshandle, 0, (Pointer)scratch)))
643 	goto error;
644     }
645 
646     /* check the dot product of the normal with crossprod. Should be + */
647     dotprod = DXDot(crossprod, *norm);
648     if (dotprod > 0)
649       goto done;
650     else {
651       /* flip the normals */
652       switch (DXGetArrayClass(normals)) {
653       case (CLASS_CONSTANTARRAY):
654 	if (NULL ==
655 	    (norm = DXIterateArray(normalshandle, i,
656 				   norm, (Pointer)scratch)))
657 	  goto error;
658 	newnormal = DXSub(zerovec, *norm);
659 	newnormals = (Array)DXNewConstantArray(numitems, &newnormal, TYPE_FLOAT,
660 					CATEGORY_REAL, 1, 3);
661 	break;
662       default:
663 	newnormals = DXNewArray(TYPE_FLOAT,CATEGORY_REAL, 1, 3);
664 	if (!DXAddArrayData(newnormals, 0, numitems, NULL))
665 	  goto error;
666 	for (i=0; i<numitems; i++) {
667 	  if (NULL ==
668               (norm = DXIterateArray(normalshandle, i,
669                                      norm, (Pointer)scratch)))
670 	    goto error;
671 	  newnormal = DXSub(zerovec, *norm);
672 	  if (!DXAddArrayData(newnormals, i, 1, &newnormal))
673 	    goto error;
674 	}
675 	break;
676       }
677       if (!DXSetComponentValue(obj, "normals", (Object)newnormals))
678 	goto error;
679       newnormals = NULL;
680       DXChangedComponentValues(obj,"normals");
681     }
682   }
683   else {
684     /* check the first quad */
685 
686     if (NULL==(cptr1 = DXGetArrayEntry(connectionshandle, 0, scratchquad)))
687       goto error;
688     if (NULL == (pos1 = DXGetArrayEntry(positionshandle, cptr1[0], scratch)))
689       goto error;
690     if (shape[0]==2)
691       position1 = DXPt(pos1->x, pos1->y, 0.0);
692     else
693       position1 = DXPt(pos1->x, pos1->y, pos1->z);
694 
695     if (NULL == (pos2 = DXGetArrayEntry(positionshandle, cptr1[1], scratch)))
696       goto error;
697     if (shape[0]==2)
698       position2 = DXPt(pos2->x, pos2->y, 0.0);
699     else
700       position2 = DXPt(pos2->x, pos2->y, pos2->z);
701 
702     if (NULL == (pos3 = DXGetArrayEntry(positionshandle, cptr1[2], scratch)))
703       goto error;
704     if (shape[0]==2)
705       position3 = DXPt(pos3->x, pos3->y, 0.0);
706     else
707       position3 = DXPt(pos3->x, pos3->y, pos3->z);
708 
709 
710     vec1 = DXSub(position3, position1);
711     vec2 = DXSub(position2, position1);
712     crossprod = DXCross(vec1, vec2);
713 
714     /* now grab an appropriate normal */
715     if (!strcmp(str2,"positions")) {
716       if (NULL ==
717 	  (norm = DXGetArrayEntry(normalshandle, *cptr1, scratch)))
718 	goto error;
719     }
720     else {
721       if (NULL ==
722 	  (norm = DXGetArrayEntry(normalshandle, 0, (Pointer)scratch)))
723 	goto error;
724     }
725 
726     /* check the dot product of the normal with crossprod. Should be + */
727     dotprod = DXDot(crossprod, *norm);
728     if (dotprod > 0)
729       goto done;
730     else {
731       /* flip the normals */
732       switch (DXGetArrayClass(normals)) {
733       case (CLASS_CONSTANTARRAY):
734 	if (NULL ==
735 	    (norm = DXIterateArray(normalshandle, i,
736 				   norm, (Pointer)scratch)))
737 	  goto error;
738 	newnormal = DXSub(zerovec, *norm);
739 	newnormals = (Array)DXNewConstantArray(numitems, &newnormal, TYPE_FLOAT,
740 					CATEGORY_REAL, 1, 3);
741 	break;
742       default:
743 	newnormals = DXNewArray(TYPE_FLOAT,CATEGORY_REAL, 1, 3);
744 	if (!DXAddArrayData(newnormals, 0, numitems, NULL))
745 	  goto error;
746 	for (i=0; i<numitems; i++) {
747 	  if (NULL ==
748               (norm = DXIterateArray(normalshandle, i,
749                                      norm, (Pointer)scratch)))
750 	    goto error;
751 	  newnormal = DXSub(zerovec, *norm);
752 	  if (!DXAddArrayData(newnormals, i, 1, &newnormal))
753 	    goto error;
754 	}
755 	break;
756       }
757       if (!DXSetComponentValue(obj, "normals", (Object)newnormals))
758 	goto error;
759       newnormals = NULL;
760       DXChangedComponentValues(obj,"normals");
761     }
762   }
763 
764 
765  done:
766   DXFreeArrayHandle(positionshandle);
767   DXFreeArrayHandle(normalshandle);
768   DXFreeArrayHandle(connectionshandle);
769   return OK;
770  error:
771   DXFreeArrayHandle(positionshandle);
772   DXFreeArrayHandle(normalshandle);
773   DXFreeArrayHandle(connectionshandle);
774   DXDelete((Object)newnormals);
775   DXDelete((Object)newconnections);
776   return ERROR;
777 
778 }
779