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 #include <dxconfig.h>
10 
11 #if defined(HAVE_STRING_H)
12 #include <string.h>
13 #endif
14 
15 /* SimplifySurface
16 
17    is a module that approximates a triangulated surface and resamples data
18 
19    this file contains the wrapper routines that extract DX objects,
20    call the simplification routine, and build a new object with the
21    result.
22 
23    It starts with the output of the "module builder" dx -builder.
24 
25    However, since the module changes the positions and connections,
26    it is not possible to use COPY_STRUCTURE
27 
28    the input objects are recursively decomposed in the routine "traverse"
29 
30    and a new object with the same structure is gradually built from scratch.
31 
32  */
33 
34 /*********************************************************************/
35 /*
36 
37   Author: Andre GUEZIEC, March/April 1997
38 
39  */
40 /*********************************************************************/
41 
42 
43 #include <dx/dx.h>
44 #include "simplesurf.h"
45 
46 static Error traverse(Object *, Object *);
47 static Error doLeaf(Object *, Object *);
48 
49 /*
50  * Declare the interface routine.
51  */
52 
53 static int
54 SimplifySurface_worker(
55     Field in_field,
56     int p_knt, int p_dim, float *p_positions,
57     int c_knt, int c_nv, int *c_connections,
58     float *old_positional_error,
59     int data_dimension,
60     float *original_surface_data,
61     float *max_error,
62     float *max_data_error,
63     int *preserve_volume,
64     int *simplify_boundary,
65     int *preserve_boundary_length,
66     int *preserve_data,
67     int *statistics,
68     Field *simplified_surface);
69 
70 
71 /*+--------------------------------------------------------------------------+
72   |                                                                          |
73   |   m_SimplifySurface                                                      |
74   |                                                                          |
75   +--------------------------------------------------------------------------+*/
76 
77 
78 Error
m_SimplifySurface(Object * in,Object * out)79 m_SimplifySurface(Object *in, Object *out)
80 {
81   int i;
82 
83   Object new_in[8];
84 
85   /* initialize all new inputs to NULL */
86 
87   for (i=0;i<8;i++)
88 
89     new_in[i] = NULL;
90 
91   /*
92    * Initialize all outputs to NULL
93    */
94   out[0] = NULL;
95 
96   /*
97    * Error checks: required inputs are verified.
98    */
99 
100   /* Parameter "original_surface" is required. */
101   if (in[0] == NULL)
102   {
103     DXSetError(ERROR_MISSING_DATA, "\"original_surface\" must be specified");
104     return ERROR;
105   }
106 
107 
108   /* cull the invalid data:
109 
110    I have taken inspiration from "regrid.m":
111 
112    I create a new object
113    when calling DXInvalidateConnections and subsequently
114    use this new object both as input and output argument of
115    obj = DXInvalidateUnreferencedPositions(obj)
116    obj = DXCull(obj)
117    otherwise I get an error at DXGetObjectClass of the result of
118    DXCull*/
119 
120   new_in[0] = DXInvalidateConnections(in[0]);
121   if (!DXInvalidateUnreferencedPositions(new_in[0])) goto error;
122   if (!DXCull(new_in[0]))  goto error;
123 
124   /* other inputs are values*/
125 
126   for (i=1;i<8;i++)
127 
128     new_in[i] = in[i];
129 
130   if (!traverse(new_in, out))
131     {
132       goto error;
133     }
134 
135   if (new_in[0] != in[0] && new_in[0]!=NULL) DXDelete(new_in[0]);
136 
137   return OK;
138 
139 error:
140 
141   if (new_in[0] != in[0] && new_in[0]!=NULL) DXDelete(new_in[0]);
142 
143   DXDelete(out[0]);
144   out[0] = NULL;
145 
146   return ERROR;
147 }
148 
149 
150 /*+--------------------------------------------------------------------------+
151   |                                                                          |
152   |   traverse                                                               |
153   |                                                                          |
154   +--------------------------------------------------------------------------+*/
155 
156 /*
157    traverse() explores recursively the object hierarchy, runs each object through
158    the SimplifySurface routine (doLeaf()) and rebuilds a new hierarchy that is
159    a copy of the previous hierarchy except that there are fewer
160    positions and connections.
161 
162    Apparently, DXCopy cannot be used for this purpose, even using the DX_COPY_STRUCTURE
163    parameter, because the positions and connections could not be changed when using
164    DXCopy()
165 */
166 
traverse(Object * in,Object * out)167 static Error traverse(Object *in, Object *out)
168 {
169 
170   switch(DXGetObjectClass(in[0]))
171     {
172     case CLASS_PRIVATE:
173     case CLASS_LIGHT:
174     case CLASS_CAMERA:
175     case CLASS_FIELD:
176     case CLASS_ARRAY:
177     case CLASS_STRING:
178 
179       /*
180        * If we have made it to the leaf level, call the leaf handler.
181        */
182 
183       if (!doLeaf(in, out))
184 	return ERROR;
185 
186       return OK;
187 
188     case CLASS_GROUP:/* if we encounter a group or a series, we need to recreate
189                         a group with the same members.
190                         A series is a particular type of group
191 	                The difference between a series and a group is
192                         each series member has a floating point value attached to it */
193       {
194 	int
195           is_series = 0,
196 	  i,
197 	  inputknt,
198 	  memknt;
199 
200 	float
201 	  position;
202 
203 	Group
204 	  new_group; /* create a new group to avoid type
205 			problems associated with series groups */
206 
207 	Object new_in[8], new_out[1];
208 
209 	DXGetMemberCount((Group)in[0], &memknt);
210 
211 	is_series = (DXGetGroupClass((Group)in[0]) == CLASS_SERIES);
212 
213 	if (is_series)
214 	  new_group = (Group) DXNewSeries();
215 	else
216 	  new_group = DXNewGroup();
217 
218 	/* other inputs are values: pass references to the other input objects other
219            than the first object when recursively calling SimplifySurface */
220 
221 	for (inputknt=1;inputknt<8;inputknt++)
222 
223 	  new_in[inputknt] = in[inputknt];
224 
225 	/*
226 	 * Create new in and out lists for each child
227 	 * of the first input.
228 	 */
229         for (i = 0; i < memknt; i++)
230 	  {
231 
232 	    /*
233 	     * For all inputs that are Values, pass them to
234 	     * child object list.  For all that are Field/Group, get
235 	     * the appropriate decendent and place it into the
236 	     * child input object list.
237 	     */
238 
239 
240 	    /* input "original_surface" is Field/Group */
241 	    if (in[0])
242 	      {
243 		/* in the particular case when the group is a series,
244 		   we retrieve the floating point number associated with the
245 		   group member and we will later pass on this floating point value to
246 		   the new series member
247 		   */
248 
249 		if (is_series)
250 		  new_in[0] = DXGetSeriesMember((Series)in[0], i, &position);
251 
252 		else
253 		  new_in[0] = DXGetEnumeratedMember((Group)in[0], i, NULL);
254 
255 	      }
256 	    else
257 	      new_in[0] = NULL;
258 
259             /* in new_in, the first object is a reference to the ith group member,
260                           the next objects are references to the parameters of
261                           SimplifySurface */
262 
263 	    /*
264 	     * For all outputs that are Values, pass them to
265 	     * child object list.  For all that are Field/Group,  get
266 	     * the appropriate decendent and place it into the
267 	     * child output object list.  Note that none should
268 	     * be NULL (unlike inputs, which can default).
269 	     */
270 
271 	    /* output "simplified_surface" is Field/Group */
272 
273 	    /* in fact, new_out will not be used in SimplifySurface,
274 	       but it will be overwritten with the "simplified" field */
275 
276 	    new_out[0] = DXGetEnumeratedMember(new_group, i, NULL);
277 
278 	    if (! traverse(new_in, new_out))
279 	      return ERROR;
280 
281 	    /*
282 	     * Now for each output that is not a Value, replace
283 	     * the updated child into the object in the parent.
284 	     */
285 
286 	    /* output "simplified_surface" is Field/Group */
287 
288 	    if (is_series)
289 	      DXSetSeriesMember((Series)new_group, i, (double) position, new_out[0]);
290 
291 	    else
292 	      DXSetEnumeratedMember(new_group, i, new_out[0]);
293 
294 	  }
295 
296 	/* set the output to the group that was newly created */
297 	out[0] = (Object) new_group;
298 
299 	return OK;
300       }
301 
302     /* this next class is the class of rigid transformations. Transformations are
303        applied at rendering time, and are not used for changing the object positions
304        in the visual program before rendering */
305 
306     case CLASS_XFORM:
307       {
308 	int inputknt;
309 
310 	Object new_in[8], new_out[1];
311 
312 	Matrix mat[1];
313 	/*
314 	 * Create new in and out lists for the decendent of the
315 	 * first input.  For inputs and outputs that are Values
316 	 * copy them into the new in and out lists.  Otherwise
317 	 * get the corresponding decendents.
318 	 */
319 
320 
321 	/* input "original_surface" is Field/Group */
322 	if (in[0])
323 	  DXGetXformInfo((Xform)in[0], &new_in[0], &mat[0]);
324 	else
325 	  new_in[0] = NULL;
326 
327 	/* we also store the parameters of the transformation in the Matrix mat.
328 	   We will have to call DXNewXform later to create an copy of the transformation
329            used in the new field. Unless we prefer the new field to simply point
330            to the old transformation rather than have its own copy of the transformation.
331 
332            If the new object does *not* have its own copy, I wonder what happens
333            if one object gets transformed and not the other.
334 
335                                      Input
336                                      |   |
337                              Translate   SimplifySurface
338                                  |               |
339                                Image1          Image2
340 
341               Are we getting the same image 1 and 2, or does 2 use the old transformation
342               and 1 the old. In other words, does Translate create a new transformation
343               (adds a translation, that is later composed with all other transformations
344                in a combined transformation matrix) or modifies the transformation matrix, or
345                creates a new matrix and unreferences the old matrix.
346 
347                Since "simplified" is a new object with its own downstream visual program
348                it seems safer to copy the transform rather than refer to it.
349         */
350 
351 	for (inputknt=1;inputknt<8;inputknt++)
352 
353 	  new_in[inputknt] = in[inputknt];
354 
355 	new_out[0] = NULL;
356 
357 	if (! traverse(new_in, new_out))
358 	  return ERROR;
359 
360 	/*
361 	 * Now for each output that is not a Value replace
362 	 * the updated child into the object in the parent.
363 	 */
364 
365 	{
366 	  Xform transform = DXNewXform((Object) new_out[0],  mat[0]);
367 	  out[0]   = (Object)transform;
368 	}
369 
370 	return OK;
371       }
372 
373     case CLASS_CLIPPED:
374       {
375 	int inputknt;
376 
377 	Object new_in[8], new_out[1], clipping[1];
378 
379 	if (in[0])
380 	  DXGetClippedInfo((Clipped)in[0], &new_in[0], &clipping[0]);
381 	else
382 	  new_in[0] = NULL;
383 
384 	for (inputknt=1;inputknt<8;inputknt++)
385 
386 	  new_in[inputknt] = in[inputknt];
387 
388 	new_out[0] = NULL;
389 
390 	if (! traverse(new_in, new_out))
391 	  return ERROR;
392 
393 	out[0] = (Object) DXNewClipped((Object) new_out[0],
394 				       (Object) clipping[0]);
395 
396 	return OK;
397       }
398 
399     case CLASS_SCREEN:
400       {
401 	Object new_in[8], new_out[1];
402 
403 	int position, z, inputknt;
404 
405 	if (in[0])
406 	  DXGetScreenInfo((Screen)in[0], &new_in[0], &position, &z);
407 	else
408 	  new_in[0] = NULL;
409 
410 	for (inputknt=1;inputknt<8;inputknt++)
411 
412 	  new_in[inputknt] = in[inputknt];
413 
414 	new_out[0] = NULL;
415 
416 	if (! traverse(new_in, new_out))
417 	  return ERROR;
418 
419 
420 	out[0] = (Object)DXNewScreen((Object) new_out[0], position,  z);
421 	return OK;
422       }
423 
424     default:
425       {
426 	DXSetError(ERROR_BAD_CLASS, "encountered in object traversal");
427 	return ERROR;
428       }
429     }
430 }
431 
432 /*+--------------------------------------------------------------------------+
433   |                                                                          |
434   |   doLeaf                                                                 |
435   |                                                                          |
436   +--------------------------------------------------------------------------+*/
437 
doLeaf(Object * in,Object * out)438 static int doLeaf(Object *in, Object *out)
439 {
440 
441   int result=0, the_input;
442   Array array, data_array = NULL;
443   Field field;
444   Pointer *in_data[8];
445   int in_knt[8];
446   Type      type;
447   Category  category;
448   int       rank, shape, data_dimension = 0;
449   Object    attr;
450   Object    element_type_attr;
451   char      the_message[256];
452 
453   static char *in_name[8] = {"\"original_surface\"",  "\"max_error\"",
454                               "\"max_data_error\"",    "\"preserve_volume\"",
455                               "\"simplify_boundary\"", "\"preserve_boundary_length\"",
456                               "\"preserve_data\"",     "\"statistics\""};
457   /*
458    * Irregular positions info
459    */
460 
461   int p_knt, p_dim;
462   float *p_positions;
463 
464   /*
465    * Irregular connections info
466    */
467 
468   int c_knt, c_nv;
469   float *c_connections;
470 
471 
472   /* positional error info:
473      the positional error provides the width of the error volume for each vertex
474      of the surface */
475 
476   float *old_positional_error = NULL;
477 
478   /*
479    Process the first input,"original surface"
480 
481    positions and/or connections are required, so the first input must
482    be a field.
483    */
484 
485   if (DXGetObjectClass(in[0]) != CLASS_FIELD)
486   {
487     /* do not process the input but return it copied */
488     out[0] = DXCopy(in[0], COPY_HEADER);
489 
490     return OK;
491   }
492   else {
493     field = (Field)in[0];
494 
495     if (DXEmptyField(field))
496       return OK;
497 
498     array = (Array)DXGetComponentValue(field, "positions");
499     if (! array)
500     {
501       DXSetError(ERROR_BAD_CLASS,
502 		 "\"original_surface\" contains no positions component");
503       goto error;
504     }
505 
506     /*
507      * The user requested irregular positions.  So we
508      * get the count, the dimensionality and a pointer to the
509      * explicitly enumerated positions.  If the positions
510      * are in fact regular, this will expand them.
511      */
512     DXGetArrayInfo(array, &p_knt, NULL, NULL, NULL, &p_dim);
513 
514     if (p_dim != 3) {
515       DXSetError(ERROR_DATA_INVALID, "positions must be three-dimensional");
516       goto error;
517     }
518 
519 
520     p_positions = (float *)DXGetArrayData(array);
521     if (! p_positions)
522       goto error;
523 
524     array = (Array)DXGetComponentValue(field, "connections");
525     if (! array)
526     {
527       DXSetError(ERROR_BAD_CLASS,
528 		 "\"original_surface\" contains no connections component");
529       goto error;
530     }
531 
532     /*
533      * Check that the field's element type matches that requested
534      */
535     element_type_attr = DXGetAttribute((Object)array, "element type");
536     if (! element_type_attr)
537     {
538         DXSetError(ERROR_DATA_INVALID,
539             "input \"original_surface\" has no element type attribute");
540         goto error;
541     }
542 
543     if (DXGetObjectClass(element_type_attr) != CLASS_STRING)
544     {
545         DXSetError(ERROR_DATA_INVALID,
546 	  "input \"original_surface\" element type attribute is not a string");
547         goto error;
548     }
549 
550     /* we accept triangles as well as quads, that we convert directly to triangles */
551 
552     if ((strcmp(DXGetString((String)element_type_attr), "triangles")) &&
553         (strcmp(DXGetString((String)element_type_attr), "quads")))
554     {
555       /* then we cannot process the data.
556 	 it can be a field associated with a string object for instance
557          the solution is to copy the object */
558 
559       out[0] = DXCopy(in[0], COPY_HEADER);
560 
561       /* we copy the header only so that the copy can be deleted if we want */
562 
563 
564       return OK;
565 
566 
567     }
568 
569     /*
570      * The user requested irregular connections.  So we
571      * get the count, the dimensionality and a pointer to the
572      * explicitly enumerated elements.  If the positions
573      * are in fact regular, this will expand them.
574      */
575     DXGetArrayInfo(array, &c_knt, NULL, NULL, NULL, &c_nv);
576 
577     c_connections = (float *)DXGetArrayData(array);
578     if (! c_connections)
579       goto error;
580 
581   }
582   /*
583    * If the input argument is not NULL then we get the
584    * data array: either the object itself, if its an
585    * array, or the data component if the argument is a field
586    */
587   if (! in[0])
588   {
589     array = NULL;
590     in_data[0] = NULL;
591     in_knt[0] = 0;
592   }
593   else { if (DXGetObjectClass(in[0]) == CLASS_ARRAY)
594     {
595       array = (Array)in[0];
596     }
597     else if (DXGetObjectClass(in[0]) == CLASS_STRING)
598     {
599       in_data[0] = (Pointer)DXGetString((String)in[0]);
600       in_knt[0] = 1;
601     }
602     else { if (DXGetObjectClass(in[0]) != CLASS_FIELD)
603       {
604         DXSetError(ERROR_BAD_CLASS, "\"original_surface\" should be a field");
605         goto error;
606       }
607 
608       array = (Array)DXGetComponentValue((Field)in[0], "data");
609       if (! array)
610       {
611         in_data[0] = NULL;
612         in_knt[0]  = 0;
613 	goto GET_POSITIONAL_ERROR;
614       }
615 
616       else if (DXGetObjectClass((Object)array) != CLASS_ARRAY)
617       {
618           in_data[0] = NULL;
619 	  in_knt [0] = 0;
620 	  goto GET_POSITIONAL_ERROR;
621       }
622     }
623 
624     /*
625      * get the dependency of the data component
626      */
627     attr = DXGetAttribute((Object)array, "dep");
628     if (! attr)
629     {
630       DXSetError(ERROR_MISSING_DATA,
631 		 "data component of \"original_surface\" has no dependency");
632       goto error;
633     }
634 
635     if (DXGetObjectClass(attr) != CLASS_STRING)
636     {
637       DXSetError(ERROR_BAD_CLASS,
638 	      "dependency attribute of data component of \"original_surface\"");
639       goto error;
640     }
641 
642   /*
643    * The dependency of this arg should be positions
644    */
645     if (strcmp("positions", DXGetString((String)attr)))
646     {
647       in_data[0] = NULL;
648     }
649 
650 
651     /* At this point, we only want to deal with data that is
652 	scalar and dependent on position */
653 
654     /* any type gets promoted to floats */
655 
656     else if (DXGetObjectClass(in[0]) != CLASS_STRING)    {
657 
658       /* I am putting NULL into the "shape" slot since I don't know what the rank is
659 	 beforehand. If the rank is not zero or one, I am not interested
660 	 in this data, and I won't try to obtain the shape information */
661 
662       DXGetArrayInfo(array, &in_knt[0], &type, &category, &rank, NULL);
663 
664       if (category != CATEGORY_REAL || (rank > 1))
665 	{
666 
667 
668 	  in_data[0] = NULL;
669 
670 	  /* then don't use this data for simplification, but still
671 	     resample the data afterwards */
672 
673 	  in_knt[0]  = 0;
674 	  goto GET_POSITIONAL_ERROR;
675 	}
676 
677       /* if the rank is zero then the data dimension is one */
678 
679       if (rank == 0) data_dimension = 1;
680 
681       /* otherwise the rank is one and I am fetching the data dimension now */
682 
683       else DXGetArrayInfo(array, &in_knt[0], &type, &category, &rank, &data_dimension);
684 
685       /* we are not interested in this data for simplification if the dimension is
686 	 larger than three. This is for memory management purposes.
687 	 the algorithm would work all the same, only a bit slower. */
688 
689       if (data_dimension > 3)
690 	{
691 	  in_data[0] = NULL;
692 
693 	  in_knt[0]  = 0;
694 
695 	  goto GET_POSITIONAL_ERROR;
696 	}
697 
698       else if (DXQueryConstantArray(array, NULL, NULL))
699 	{
700 	  /* do not use the data of a constant array for simplification purposes */
701 	  in_data[0] = NULL;
702 	  in_knt[0]  = 0;
703 
704 	  goto GET_POSITIONAL_ERROR;
705 	}
706 
707       else if (type == TYPE_FLOAT)
708 	/* then get the data directly. No need to convert it to floats since it is already converted */
709 	in_data[0] = DXGetArrayData(array);
710 
711       else
712 	{
713 	  /* otherwise take that data and promote the array to floats */
714 
715 	  /* I spent an *entire* day debugging to realize that
716 	     DXArrayConvert() sets an error code, and screws up the rest of
717 	     the module if array is of TYPE_FLOAT. Apparently, it is not
718 	     clever enough to realize this.
719 
720 	     This is why I separately test if (type == TYPE_FLOAT)
721 	     and in that case, avoid to call DXArrayConvert() */
722 
723 	  data_array = DXArrayConvert(array, TYPE_FLOAT, CATEGORY_REAL, rank);
724 
725 	  /* again, if this fails, we can't use the data */
726 
727 	  if (!data_array)
728 	    {
729 	      in_data[0] = NULL;
730 
731 	      in_knt[0]  = 0;
732 
733 	      goto GET_POSITIONAL_ERROR;
734 	    }
735 
736 	  else in_data[0] = DXGetArrayData(data_array);
737 	}
738 
739     }
740 
741 GET_POSITIONAL_ERROR:
742 
743     /* we try to extract the "positional error" component from the input field
744        if it exists, in which case we extract the *old_positional_error data
745        and pass it on to the SimplifySurface driver */
746 
747     array = (Array)DXGetComponentValue(field, "positional error");
748 
749     if (array)
750       {
751 	/* verify that it is dependent on positions */
752 	attr = DXGetAttribute((Object)array, "dep");
753 
754 	if (!strcmp("positions", DXGetString((String)attr)))
755 	  {
756 	    int knt;
757 
758 	    /* verify that the array info makes sense */
759 	     DXGetArrayInfo(array, &knt, &type, &category, &rank, NULL);
760 
761 	     if (type == TYPE_FLOAT && category == CATEGORY_REAL && rank < 2 && knt == p_knt)
762 
763 	       old_positional_error = (float *) DXGetArrayData(array);
764 
765 	  }
766       }
767 
768   }
769 
770   /*
771    Then, process Input 1: Input 1 specifies the maximum simplification error.
772    In case Input 1 is not specified, we compute a data dependent default value
773    */
774 
775   if (! in[1])
776   {
777     array = NULL;
778     in_data[1] = NULL;
779     in_knt [1] = 0;
780   }
781 
782   else
783   {
784     if (DXGetObjectClass(in[1]) == CLASS_ARRAY)
785     {
786       array = (Array)in[1];
787     }
788     else if (DXGetObjectClass(in[1]) == CLASS_STRING)
789     {
790       in_data[1] = (Pointer)DXGetString((String)in[1]);
791       in_knt [1] = 1;
792     }
793     else
794     {
795       if (DXGetObjectClass(in[1]) != CLASS_FIELD)
796       {
797         DXSetError(ERROR_BAD_CLASS, "\"max_error\" should be a field");
798         goto error;
799       }
800 
801       array = (Array)DXGetComponentValue((Field)in[1], "data");
802       if (! array)
803       {
804         DXSetError(ERROR_MISSING_DATA, "\"max_error\" has no data component");
805         goto error;
806       }
807 
808       if (DXGetObjectClass((Object)array) != CLASS_ARRAY)
809       {
810         DXSetError(ERROR_BAD_CLASS,
811 		   "data component of \"max_error\" should be an array");
812         goto error;
813       }
814     }
815 
816 
817     if (DXGetObjectClass(in[1]) != CLASS_STRING)    {
818        DXGetArrayInfo(array, &in_knt[1], &type, &category, &rank, &shape);
819        if (type != TYPE_FLOAT || category != CATEGORY_REAL ||
820              !((rank == 0) || ((rank == 1)&&(shape == 1))))
821        {
822          DXSetError(ERROR_DATA_INVALID, "input \"max_error\"");
823          goto error;
824        }
825 
826        in_data[1] = DXGetArrayData(array);
827        if (! in_data[1])
828           goto error;
829 
830     }
831   }
832 
833   /*
834    Then, process Input 2: Input 2 specifies the maximum simplification error
835    on the data. Input 2 is only relevant if the data attached to the surface
836    (Input 0) will be preserved during simplification.
837    */
838 
839   if (! in[2])
840   {
841     /* the maximum error on the data was not provided,
842        so we will later use a default value */
843 
844     array = NULL;
845     in_data[2] = NULL;
846     in_knt[2]  = 0;
847 
848   }
849   else
850   {
851     if (DXGetObjectClass(in[2]) == CLASS_ARRAY)
852     {
853       array = (Array)in[2];
854     }
855     else if (DXGetObjectClass(in[2]) == CLASS_STRING)
856     {
857       in_data[2] = (Pointer)DXGetString((String)in[2]);
858       in_knt[2] = 1;
859     }
860     else
861     {
862       if (DXGetObjectClass(in[2]) != CLASS_FIELD)
863       {
864         DXSetError(ERROR_BAD_CLASS, "\"max_data_error\" should be a field");
865         goto error;
866       }
867 
868       array = (Array)DXGetComponentValue((Field)in[2], "data");
869       if (! array)
870       {
871         DXSetError(ERROR_MISSING_DATA, "\"max_data_error\" has no data component");
872         goto error;
873       }
874 
875       if (DXGetObjectClass((Object)array) != CLASS_ARRAY)
876       {
877         DXSetError(ERROR_BAD_CLASS, "data component of \"max_data_error\" should be an array");
878         goto error;
879       }
880     }
881 
882 
883     if (DXGetObjectClass(in[2]) != CLASS_STRING)    {
884        DXGetArrayInfo(array, &in_knt[2], &type, &category, &rank, &shape);
885        if (type != TYPE_FLOAT || category != CATEGORY_REAL ||
886              !((rank == 0) || ((rank == 1) && (shape == 1))))
887        {
888          DXSetError(ERROR_DATA_INVALID, "input \"max_data_error\"");
889          goto error;
890        }
891 
892        in_data[2] = DXGetArrayData(array);
893        if (! in_data[2])
894           goto error;
895 
896     }
897   }
898 
899   /*
900    Process Input 3, which is a flag specifying whether the object volume should
901    be preserved.
902 
903    Input 3,4,5,6,7 are all flags. To reduce the size of this file,
904    I treat them in a loop. Originally, I have
905    kept the structure provided by the Module Builder, that consists of
906    rewriting similar code for each input (except for the input name);
907    */
908 
909  for(the_input=3;the_input<8;the_input++) {
910 
911   if (! in[the_input])
912   {
913     array = NULL;
914     /* the input value was not specified. We'll pick default values later */
915     in_data[the_input] = NULL;
916     in_knt[the_input]  = 0;
917   }
918   else
919   {
920     if (DXGetObjectClass(in[the_input]) == CLASS_ARRAY)
921     {
922       array = (Array)in[the_input];
923     }
924     else if (DXGetObjectClass(in[the_input]) == CLASS_STRING)
925     {
926       in_data[the_input] = (Pointer)DXGetString((String)in[the_input]);
927       in_knt[the_input] = 1;
928     }
929     else
930     {
931       if (DXGetObjectClass(in[the_input]) != CLASS_FIELD)
932       {
933         sprintf(the_message," %s should be a field", in_name[the_input]);
934         DXSetError(ERROR_BAD_CLASS, the_message);
935         goto error;
936       }
937 
938       array = (Array)DXGetComponentValue((Field)in[the_input], "data");
939       if (! array)
940       {
941 
942         sprintf(the_message," %s has no data component", in_name[the_input]);
943         DXSetError(ERROR_MISSING_DATA, the_message);
944         goto error;
945       }
946 
947       if (DXGetObjectClass((Object)array) != CLASS_ARRAY)
948       {
949         sprintf(the_message,"data component of %s should be an array", in_name[the_input]);
950         DXSetError(ERROR_BAD_CLASS, the_message);
951         goto error;
952       }
953     }
954 
955 
956     if (DXGetObjectClass(in[the_input]) != CLASS_STRING)    {
957        DXGetArrayInfo(array, &in_knt[the_input], &type, &category, &rank, &shape);
958 
959        if (type != TYPE_INT || category != CATEGORY_REAL || !((rank == 0) || ((rank == 1) && (shape == 1))))
960 	 {
961 	   sprintf(the_message, "input %s", in_name[the_input]);
962            DXSetError(ERROR_DATA_INVALID, the_message);
963            goto error;
964 	 }
965 
966        in_data[the_input] = DXGetArrayData(array);
967        if (! in_data[the_input])
968           goto error;
969 
970     }
971   }
972 }
973   /*
974    Process Input 4, which is a flag specifying whether the surface boundary should
975    be simplified ----> done within the loop
976    */
977 
978 
979 /* START_WORKER */
980 
981   {
982 
983     float
984         max_error,
985         max_error_data;
986 
987     /* default values for the options "volume", "boundary" "length" and "data" */
988 
989     int
990         preserve_volume          = 1,
991         simplify_boundary        = 0,
992         preserve_boundary_length = 1,
993         preserve_data            = 1,
994         statistics               = 0;
995 
996     Field f = DXNewField(); /* create a new field to contain the simplified surface */
997 
998     if (!f) goto error;
999 
1000     /* verify that the input parameters have all been specified.
1001        if this is not the case, use the defaults */
1002 
1003     if (in_data[1] == NULL) /* this is the \"maximum_simplification_error\" parameter */
1004       {
1005 	 max_error =  _dxfFloatDataBoundingBoxDiagonal(
1006 						       (float *)p_positions,
1007 						       p_knt, 3);
1008 
1009 	/* use one percent of the bounding box diagonal */
1010 
1011 	max_error /= 100.;
1012 
1013 	DXMessage ( "SimplifySurface: 'max_error' defaults to 1 percent of bbox diagonal: %g .", max_error);
1014 
1015 	in_knt [1]  = 1  ;
1016 	in_data[1]  = (Pointer) &max_error;
1017       }
1018 
1019      /* process in priority the "preserve_data" parameter
1020 	to determine whether the user decided to ignore the data
1021 	component before computing a default max_error_data parameter */
1022 
1023      if (in_data[6] == NULL)
1024        {
1025 	 in_knt [6] = 1;
1026 	 in_data[6] = (Pointer) &preserve_data;
1027        }
1028 
1029 
1030 
1031     if (in_data[2] == NULL)
1032       {
1033 	 /* the maximum error on the data was not provided,
1034 	    so we are using a default value */
1035 
1036 	if (in_data[0] ) /* if the data is an array of floats */
1037          {
1038           /* did we set the "preserve_data" flag to zero, meaning
1039 	     that we want to ignore the data ? */
1040            int *preserve_data_flag = (int *)in_data[6];
1041 
1042            if (preserve_data_flag[0] == 1)
1043 
1044 	  /* then, in that case, the bounding box diagonal for the data still should
1045 	     be larger than zero, otherwise, the data does not need preserving */
1046 
1047 	  if ((max_error_data = _dxfFloatDataBoundingBoxDiagonal((float *) in_data[0],
1048 								 in_knt[0], data_dimension)) > 0.0)
1049 	    {
1050 	      /* by default, we allow ten percent error on the data bounding box diagonal
1051 		 more than on the positions */
1052 
1053 	      max_error_data /= 10.;
1054 
1055 	      DXMessage (
1056 		    "SimplifySurface: 'max_error_data' defaults to 10 percent of data bbox diagonal: %g .",
1057 		    max_error_data);
1058 
1059 	      in_knt [2] = 1;
1060 
1061 	      in_data[2] = (Pointer) &max_error_data;
1062 	    }
1063          }
1064       }
1065 
1066      if (in_data[3] == NULL) /* parameter instructing DX whether to preserve volume
1067                                 or not preserve volume */
1068        {
1069 	 in_knt [3] = 1;
1070 
1071 	 in_data[3] = (Pointer) &preserve_volume;
1072 
1073        }
1074 
1075      if (in_data[4] == NULL) /* parameter instructing DX whether to simplify the boundary
1076                                 edges in addition to the interior edges */
1077        {
1078 	 in_knt [4] = 1;
1079 	 in_data[4] = (Pointer) &simplify_boundary;
1080        }
1081 
1082      if (in_data[5] == NULL) /* in case the boundary edges are simplified, parameter specifying
1083                                 whether the length of the boundary should be preserved */
1084        {
1085 	 in_knt [5] = 1;
1086 	 in_data[5] = (Pointer) &preserve_boundary_length;
1087        }
1088 
1089      if (in_data[7] == NULL) /* instructs SimplifySurface to provide limited statistics
1090                                 on the simplification: original and final numbers of
1091                                 vertices and triangles, and percentage of original
1092                                 vertices and triangles. */
1093        {
1094 	 in_knt [7] = 1;
1095 	 in_data[7] = (Pointer) &statistics;
1096        }
1097 
1098 
1099 
1100 
1101     /*
1102      Call the worker routine. Check the error code.
1103      */
1104 
1105     result = SimplifySurface_worker(
1106 				      (Field) in[0],
1107 				      p_knt, p_dim, (float *) p_positions,
1108 				      c_knt, c_nv,  (int *)   c_connections,
1109 				      old_positional_error,
1110 				      data_dimension,
1111 				      (float *)in_data[0],
1112 				      (float *)in_data[1],
1113 				      (float *)in_data[2],
1114 				      (int *)in_data[3],
1115 				      (int *)in_data[4],
1116 				      (int *)in_data[5],
1117 				      (int *)in_data[6],
1118 				      (int *)in_data[7],
1119 				      &f
1120 				      );
1121    if (! result)
1122     {
1123        if (DXGetError()==ERROR_NONE)
1124           DXSetError(ERROR_INTERNAL, "error return from user routine");
1125        goto error;
1126     }
1127 
1128     /* creates boxes and neighbors */
1129     DXEndField(f);
1130 
1131     out[0] = (Object) f;
1132   }
1133 
1134 
1135 
1136   /*
1137    * In either event: success or failure of the simplification, clean up allocated memory
1138    */
1139 
1140   if (data_array) DXFree((Pointer)data_array);
1141 
1142   return result;
1143 
1144 error:
1145 
1146   if (data_array) DXFree((Pointer)data_array);
1147 
1148   return 0;
1149 }
1150 
1151 /*+--------------------------------------------------------------------------+
1152   |                                                                          |
1153   |   SimplifySurface_worker                                                 |
1154   |                                                                          |
1155   +--------------------------------------------------------------------------+*/
1156 
1157 
1158 static int
SimplifySurface_worker(Field in_field,int p_knt,int p_dim,float * p_positions,int c_knt,int c_nv,int * c_connections,float * old_positional_error,int data_dimension,float * original_surface_data,float * max_error,float * max_data_error,int * preserve_volume,int * simplify_boundary,int * preserve_boundary_length,int * preserve_data,int * statistics,Field * simplified_surface)1159 SimplifySurface_worker(
1160     Field in_field,
1161     int p_knt, int p_dim, float *p_positions,
1162     int c_knt, int c_nv, int *c_connections,
1163     float *old_positional_error,
1164     int data_dimension,
1165     float *original_surface_data,
1166     float *max_error,
1167     float *max_data_error,
1168     int *preserve_volume,
1169     int *simplify_boundary,
1170     int *preserve_boundary_length,
1171     int *preserve_data,
1172     int *statistics,
1173     Field *simplified_surface)
1174 {
1175   /*
1176    * The arguments to this routine are:
1177    *
1178    *  p_knt:           total count of input positions
1179    *  p_dim:           dimensionality of input positions
1180    *  p_positions:     pointer to positions list
1181    *  c_knt:           total count of input connections elements
1182    *  c_nv:            number of vertices per element
1183    *  c_connections:   pointer to connections list
1184 
1185 
1186    old_positional_error: the values of the component "positional error"
1187    if any are available
1188 
1189    */
1190 
1191 
1192 
1193 /*+--------------------------------------------------------------------------+
1194   |                                                                          |
1195   |   _dxfSimplifySurfaceDriver                                              |
1196   |                                                                          |
1197   +--------------------------------------------------------------------------+*/
1198 
1199 /* call the driver routine that simplifies the surface */
1200 
1201 {
1202 
1203   Array pos = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, 3);
1204 
1205   if (!pos)
1206     goto error;
1207 
1208   else
1209     {
1210       int
1211         nT                               = c_knt,
1212 	new_nV                           = 0,
1213 	new_nT                           = 0,
1214 	old_nV_after_manifold_conversion = 0,
1215 	old_nT_after_manifold_conversion = 0,
1216         *connections                     = (int *) c_connections,
1217 	*new_connections                 = NULL,
1218 	*vertex_parents                  = NULL,
1219 	*face_parents                    = NULL,
1220 	*vertex_lut                      = NULL,
1221 	*face_lut                        = NULL;
1222 
1223       float
1224 	*new_positions        = NULL,
1225 	*vertex_data          = NULL,
1226 	vertex_data_max_error = 0,
1227 	*new_vertex_data      = NULL,
1228 	*new_positional_error = NULL,
1229 	*face_normals         = NULL,
1230 	*old_face_areas       = NULL,
1231 	*new_face_areas       = NULL;
1232 
1233       static char *element_type[5] = {"","","", "triangles", "quads"};
1234 
1235       /* since there isn't always data attached to the vertices,
1236 	 original_surface_data may be NULL
1237 	 and accessing original_surface_data[0] would cause a segmentation fault
1238 	 this is why I created the intermediate pointers "vertex_data"
1239 	 and "vertex_data_max_error"
1240        */
1241 
1242       if (original_surface_data /* if there is data at all */
1243           &&
1244           max_data_error        /* and if we have a valid error bound*/
1245 	  &&
1246 	  /* also, the user may explicitely want to ignore the data: */
1247 	  preserve_data[0])
1248 	{
1249 
1250 	  vertex_data             = (float *) original_surface_data;
1251 	  vertex_data_max_error   =           max_data_error[0];
1252 
1253 	}
1254 	/* if simple statistics are requested, we print the number of vertices and triangles
1255 	   before and after simplification, as well as the percentage of the original number
1256            of triangles that they represent */
1257 
1258 	if (statistics[0])
1259 	   DXMessage ( "SimplifySurface: 'original_surface' has %d vertices and %d %s.", p_knt, c_knt,
1260 		       element_type[c_nv]);
1261 
1262       /* since we don't know beforehand how many new positions there
1263 	 will be nor how many new connections,
1264 
1265 	 _dxfSimplifySurfaceDriver()
1266 	 uses DXAllocate() to allocate new_positions, new_vertex_data,
1267 	 new_connections, vertex_parents, face_parents, vertex_lut,
1268 	 new_positional_error, face_normals, old_face_areas, new_face_areas.
1269 
1270 	 Accordingly, I run DXFree() on each array after completion of the routine */
1271 
1272       /* convert the quads to triangles */
1273 
1274       if ((c_nv == 4) && !(_dxfQuads2Triangles(&nT, &connections, &face_lut)))
1275         goto failed_creating_surface;
1276 
1277       if (_dxfSimplifySurfaceDriver(p_knt, p_positions,
1278 				    nT, connections,
1279 				    data_dimension,
1280 				    vertex_data,
1281 				    max_error[0],
1282 				    vertex_data_max_error,
1283 				    &old_nV_after_manifold_conversion,
1284 				    &old_nT_after_manifold_conversion,
1285 				    &new_nV, &new_positions, &new_vertex_data,
1286 				    &new_nT, &new_connections,
1287 				    &vertex_parents, &face_parents, &vertex_lut, &face_lut,
1288 				    old_positional_error,
1289 				    &new_positional_error, &face_normals,
1290 				    &old_face_areas, &new_face_areas,
1291 				    preserve_volume[0],
1292 				    simplify_boundary[0],
1293 				    preserve_boundary_length[0]))
1294 
1295 	{
1296 	  Array con  = DXNewArray(TYPE_INT, CATEGORY_REAL, 1, 3);
1297 
1298 	  if (!con)
1299 	    goto failed_creating_surface;
1300 
1301 	  else
1302 	    {
1303 	      Array pos_error = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, 1);
1304 
1305 	      if (!pos_error)
1306 		goto failed_creating_surface;
1307 
1308 	      else
1309 		{
1310 		  Array nor  = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, 3);
1311 
1312 		  if (!nor)
1313 		    goto failed_creating_surface;
1314 		  else
1315 		    {
1316 		     /* provide simple statistics, if explicitely requested by the user */
1317 
1318 	             if (statistics[0])
1319                         {
1320 	                  DXMessage ( "SimplifySurface: 'simplified' has %d vertices (%2.1f percent of original)...",
1321                                        new_nV, 100. * (double)new_nV/(double)p_knt);
1322 	                  DXMessage ( "SimplifySurface: ...and %d triangles (%2.1f percent of original).",
1323                                        new_nT, 100. * (double)new_nT/(double)c_knt);
1324                         }
1325 
1326                       /* copy the new positions and new connections to the newly created
1327                          arrays */
1328 		      DXAddArrayData(pos,  0, new_nV, new_positions);
1329 		      DXFree((Pointer)new_positions);
1330 
1331 		      DXAddArrayData(con,  0, new_nT, new_connections);
1332 
1333                       /* "positional error" is a component created by SimplifySurface() */
1334 		      DXAddArrayData(pos_error, 0, new_nV, new_positional_error);
1335 		      DXFree((Pointer)new_positional_error);
1336 
1337                       /* convert the triangle normals that were computed by the
1338                          SimplifySurfaceDriver() routine to vertex normals,
1339                          using averaging weighted with triangle areas */
1340 
1341 		      _dxfVertexNormalsfromTriangleNormals(nor, new_nV, new_nT, new_connections,
1342 							   face_normals, new_face_areas);
1343 
1344 		      DXFree((Pointer)new_connections);
1345 
1346 		      DXFree((Pointer)new_face_areas);
1347 
1348 		      DXFree((Pointer)face_normals);
1349 
1350                       /* set the positions, connections and other components in the
1351                          new field "*simplified_surface */
1352 
1353 		      DXSetComponentValue(*simplified_surface, "positions", (Object)pos);
1354 		      DXSetComponentValue(*simplified_surface, "positional error", (Object)pos_error);
1355 		      DXSetComponentValue(*simplified_surface, "normals", (Object)nor);
1356 
1357 		      DXSetConnections(*simplified_surface, "triangles", con);
1358 
1359 
1360                       /* define the dependencies and other relations between field
1361                          components */
1362 
1363 		      /* set positions as dependent on positions */
1364 
1365 		      if (!DXSetComponentAttribute(
1366 						   *simplified_surface, "positions",
1367 						   "dep",
1368 						   (Object)DXNewString("positions")))
1369 			goto failed_creating_surface;
1370 
1371 		      /* set positional error as dependent on positions */
1372 
1373 		      if (!DXSetComponentAttribute(
1374 						   *simplified_surface, "positional error",
1375 						   "dep",
1376 						   (Object)DXNewString("positions")))
1377 			goto failed_creating_surface;
1378 
1379 		      /* set normals as dependent on positions */
1380 
1381 		      if (!DXSetComponentAttribute(
1382 						   *simplified_surface, "normals",
1383 						   "dep",
1384 						   (Object)DXNewString("positions")))
1385 			goto failed_creating_surface;
1386 
1387 		      /* create the data component only if it was carried along the simplification */
1388 
1389 		      if (new_vertex_data)
1390 			{
1391 			  Array data = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, data_dimension);
1392 
1393 			  if (!data)
1394 			    goto failed_creating_surface;
1395 
1396 			  DXAddArrayData(data, 0, new_nV, new_vertex_data);
1397 			  DXFree((Pointer)new_vertex_data);
1398 			  DXSetComponentValue(*simplified_surface, "data", (Object)data);
1399 
1400 			  /* set data as dependent on positions */
1401 
1402 			  if (!DXSetComponentAttribute(
1403 						       *simplified_surface, "data",
1404 						       "dep",
1405 						       (Object)DXNewString("positions")))
1406 			    goto failed_creating_surface;
1407 			}
1408 
1409 		      /* resample the components dep on positions and dep on connections
1410 			 that can be resampled. By "resampling", I mean using
1411                          the old values (vectors) attached to the original surface vertices
1412                          and triangles and the vertex_parents and face_parents arrays,
1413                          that indicate to which old elements the new elements correspond
1414                          to compute new values (vectors) attached to the new vertices and
1415                          triangles */
1416 
1417 		      _dxfResampleComponentValuesAfterSimplification(in_field, simplified_surface,
1418 						    old_nV_after_manifold_conversion,
1419 						    old_nT_after_manifold_conversion, new_nV, new_nT,
1420 						    vertex_parents, face_parents, vertex_lut, face_lut,
1421 						    old_face_areas);
1422 
1423 
1424 		      /* copy the surface attributes such as "name"
1425 			 or "fuzz" or "Tube diameter", things like that */
1426 		      /*                      destination,      source */
1427 		      if (!DXCopyAttributes((Object)*simplified_surface,
1428 					    (Object)in_field))
1429 			goto error;
1430 
1431 		      DXFree((Pointer)vertex_parents);
1432 
1433 		      DXFree((Pointer)face_parents);
1434 
1435 		      DXFree((Pointer)vertex_lut);
1436 
1437 		      DXFree((Pointer)face_lut);
1438 
1439 		      DXFree((Pointer)old_face_areas);
1440 
1441                       if (connections && connections != c_connections)
1442                         DXFree((Pointer)connections);
1443 		    }
1444 		}
1445 	    }
1446 	}
1447 
1448       else
1449 	{
1450 
1451 	failed_creating_surface:
1452 
1453           if (connections && connections != c_connections) DXFree((Pointer)connections);
1454 	  if (new_positions       )                        DXFree((Pointer)new_positions);
1455 	  if (new_connections     )                        DXFree((Pointer)new_connections);
1456 	  if (new_positional_error)                        DXFree((Pointer)new_positional_error);
1457 	  if (face_normals        )                        DXFree((Pointer)face_normals);
1458 	  if (vertex_parents      )                        DXFree((Pointer)vertex_parents);
1459 	  if (face_parents        )                        DXFree((Pointer)face_parents);
1460 	  if (vertex_lut          )                        DXFree((Pointer)vertex_lut);
1461 	  if (face_lut            )                        DXFree((Pointer)face_lut);
1462 	  if (old_face_areas      )                        DXFree((Pointer)old_face_areas);
1463 	  if (new_face_areas      )                        DXFree((Pointer)new_face_areas);
1464 	  if (new_vertex_data     )                        DXFree((Pointer)new_vertex_data);
1465 
1466 	  goto error;
1467 	}
1468     }
1469 }
1470 
1471 
1472   /*
1473    * successful completion
1474    */
1475    return 1;
1476 
1477   /*
1478    * unsuccessful completion
1479    */
1480 error:
1481 
1482 
1483    return 0;
1484 
1485 }
1486 
1487 /*+--------------------------------------------------------------------------+
1488   |                                                                          |
1489   |   _dxfResampleComponentValuesAfterSimplification                         |
1490   |                                                                          |
1491   +--------------------------------------------------------------------------+*/
1492 
_dxfResampleComponentValuesAfterSimplification(Field original_surface,Field * simplified_surface,int old_nV_after_conversion,int old_nT_after_conversion,int new_nV,int new_nT,int * vertex_parents,int * face_parents,int * vertex_lut,int * face_lut,float * old_face_areas)1493 int _dxfResampleComponentValuesAfterSimplification(Field original_surface,
1494 						   Field *simplified_surface,
1495 						   int old_nV_after_conversion,
1496 						   int old_nT_after_conversion,
1497 						   int new_nV,
1498 						   int new_nT,
1499 						   int *vertex_parents, int *face_parents,
1500 						   int *vertex_lut, int *face_lut, float *old_face_areas)
1501 {
1502   /* get each enumerated component after each other, and create a
1503      resampled version of it in the simplified surface whenever
1504      applicable, except for the positions, connections, and data, if
1505      there is already a data component in the simplified surface */
1506 
1507   register int num_component = 0;
1508 
1509   char *component_name = NULL;
1510 
1511   Array
1512     array     = NULL,
1513     new_array = NULL;
1514 
1515   while ((array = (Array)DXGetEnumeratedComponentValue(original_surface, num_component++, &component_name)))
1516     {
1517 
1518       Object attr = DXGetAttribute((Object)array, "dep");
1519 
1520       /* reject "positions", "connections",  "normals", "neighbors", "positional error", components
1521 	"invalid positions", "invalid_connections" because the surface was DXCulled so
1522 	they are irrelevant.
1523 	are there other components that I should reject?
1524 
1525 	yes! "box" */
1526 
1527        if (strcmp("positions", component_name) != 0           && strcmp("connections", component_name) != 0 &&
1528 	   strcmp("normals", component_name) != 0             && strcmp("neighbors", component_name) != 0 &&
1529 	   strcmp("invalid positions", component_name)   != 0 &&
1530 	   strcmp("invalid connections", component_name) != 0 &&
1531 	   strcmp("positional error", component_name)    != 0 &&
1532 	   strcmp("box", component_name)    != 0 )
1533 	 {
1534 
1535 	   /* reject "data" component if already present in the
1536 	      "*simplified_surface" field */
1537 
1538 	   if (strcmp("data", component_name) != 0 || /* or if it *is* the data component
1539 							 I dont want to see it in the simplified surface
1540 							 already */
1541 	       DXGetComponentValue(*simplified_surface, "data") == NULL)
1542 
1543 	     /* there must be an attribute */
1544 
1545 	     if (attr && DXGetObjectClass(attr) == CLASS_STRING)
1546 	       {
1547 
1548 		 /* it must be either dependent on positions...
1549 		  but not of type string*/
1550 
1551 		 if (!strcmp("positions", DXGetString((String)attr)))
1552 		   {
1553 		     /* in which case we resample according to rule 1
1554 			and create the same attribute for the "*simplified_surface" Field */
1555 
1556 		     new_array = _dxfResampleComponentDepPosAfterSimplification(array,
1557 										      old_nV_after_conversion,
1558 										      new_nV,
1559 										      vertex_parents,
1560 										      vertex_lut);
1561 
1562 
1563 		     if (new_array)
1564 		       {
1565 			 DXSetComponentValue(*simplified_surface, component_name, (Object)new_array);
1566 
1567 			 /* set component as dependent on positions */
1568 
1569 			 if (!DXSetComponentAttribute(*simplified_surface, component_name,
1570 						      "dep",
1571 						      (Object)DXNewString("positions")))
1572 			   goto error;
1573 		       }
1574 
1575 		   }
1576 
1577 		 /* or on connections... */
1578 
1579 		 else if (!strcmp("connections", DXGetString((String)attr)))
1580 		   {
1581 		     /* dep on connections component, resample according to rule 2
1582 			and create the same attribute for the "*simplified_surface" Field */
1583 
1584 		     new_array = _dxfResampleComponentDepConAfterSimplification(array,
1585 										      old_nT_after_conversion,
1586 										      new_nT,
1587 										      face_parents,
1588 										      face_lut,
1589 										      old_face_areas);
1590 
1591 
1592 		     if (new_array)
1593 		       {
1594 			 DXSetComponentValue(*simplified_surface, component_name, (Object)new_array);
1595 
1596 			 /* set component as dependent on positions */
1597 
1598 			 if (!DXSetComponentAttribute(*simplified_surface, component_name,
1599 						      "dep",
1600 						      (Object)DXNewString("connections")))
1601 			   goto error;
1602 		       }
1603 		   }
1604 	       }
1605 	 }
1606 
1607        /* DXFree((Pointer)component_name);*/
1608     }
1609 
1610   return 1;
1611 
1612 error:
1613 
1614   if (new_array) DXDelete ((Object) new_array);
1615 
1616   return 0;
1617 }
1618 
1619 /*+--------------------------------------------------------------------------+
1620   |                                                                          |
1621   |   _dxfResampleComponentDepPosAfterSimplification                         |
1622   |                                                                          |
1623   +--------------------------------------------------------------------------+*/
1624 
_dxfResampleComponentDepPosAfterSimplification(Array array,int old_nV_after_conversion,int new_nV,int * vertex_parents,int * vertex_lut)1625 Array _dxfResampleComponentDepPosAfterSimplification(Array array,
1626 						     int old_nV_after_conversion,
1627 						     int new_nV, int *vertex_parents,
1628 						     int *vertex_lut)
1629 {
1630   /* returns NULL in case the resampling is not possible */
1631 
1632   Array  new_array = NULL;
1633 
1634 
1635   Pointer new_array_data = NULL;
1636 
1637   Type type;
1638   Category category;
1639 
1640   int
1641     *vertex_weights = NULL,
1642     rank,
1643     shape[3],
1644     size,
1645     tensor_size = 1,
1646     old_nV,
1647     i;
1648 
1649   float *vertex_data_averages = NULL;
1650 
1651   DXGetArrayInfo(array, &old_nV, &type, &category, &rank, NULL);
1652 
1653   /* I do not average strings (of course) or higher order tensors  */
1654 
1655   if (type == TYPE_STRING || rank > 3) goto error;
1656 
1657   /* now that I am sure that the rank is 3 or less I can call the
1658      same routine with 3 allocated slots for shape */
1659 
1660   DXGetArrayInfo(array, &old_nV, &type, &category, &rank, shape);
1661 
1662 
1663   /* particular treatment if the array is a constant array */
1664 
1665   if (DXQueryConstantArray(array, &old_nV, NULL))
1666     {
1667       if (rank == 0)
1668 	new_array = (Array) DXNewConstantArray(new_nV, DXGetConstantArrayData(array), type, category, rank, 1);
1669 
1670       else
1671 	new_array = (Array) DXNewConstantArrayV(new_nV, DXGetConstantArrayData(array), type, category, rank, shape);
1672 
1673       if (!new_array) goto error;
1674     }
1675 
1676   else
1677 
1678     {
1679       /* the array was not a constant array: we need to resample */
1680 
1681       if (rank == 0)
1682 	new_array = DXNewArray(type, category, rank, 1);
1683       else
1684 	new_array = DXNewArrayV(type, category, rank, shape);
1685 
1686       if (!new_array) goto error;
1687 
1688       size = DXTypeSize(type);
1689 
1690       /* multiply the element size by the element shape */
1691 
1692       for(i=0;i<rank;i++)
1693 	tensor_size *= shape[i];
1694 
1695       new_array_data = DXAllocateZero(size * tensor_size * new_nV);
1696 
1697       /* DXAllocate sets an error code if the allocation actually fails
1698 	 but does not free anything if the error code is set;
1699 	 this is why we need to free */
1700 
1701       if (!new_array_data) goto error;
1702 
1703       vertex_weights = (int *) DXAllocateZero(new_nV * sizeof (int));
1704 
1705       if (!vertex_weights) goto error;
1706 
1707       vertex_data_averages = (float *) DXAllocateZero(new_nV * tensor_size * sizeof (float));
1708 
1709       if (!vertex_data_averages) goto error;
1710 
1711       switch(type)
1712 	{
1713 	case TYPE_BYTE:
1714 	  _dxfRESAMPLE_DEP_POS_AFTER_SIMPLIFICATION(byte); break;
1715 
1716 	case TYPE_UBYTE:
1717 	  _dxfRESAMPLE_DEP_POS_AFTER_SIMPLIFICATION(ubyte); break;
1718 
1719 	case TYPE_DOUBLE:
1720 	  _dxfRESAMPLE_DEP_POS_AFTER_SIMPLIFICATION(double); break;
1721 
1722 	case TYPE_FLOAT:
1723 	  _dxfRESAMPLE_DEP_POS_AFTER_SIMPLIFICATION(float); break;
1724 
1725 	case TYPE_INT:
1726 	  _dxfRESAMPLE_DEP_POS_AFTER_SIMPLIFICATION(int); break;
1727 
1728 	case TYPE_UINT:
1729 	  _dxfRESAMPLE_DEP_POS_AFTER_SIMPLIFICATION(uint); break;
1730 
1731 	case TYPE_SHORT:
1732 	  _dxfRESAMPLE_DEP_POS_AFTER_SIMPLIFICATION(short); break;
1733 
1734 	case TYPE_USHORT:
1735 	  _dxfRESAMPLE_DEP_POS_AFTER_SIMPLIFICATION(ushort); break;
1736 
1737 	default:
1738 	  goto error; break;
1739 
1740 	}
1741 
1742       DXAddArrayData(new_array, 0, new_nV, new_array_data);
1743       DXFree((Pointer) new_array_data);
1744       DXFree((Pointer) vertex_weights);
1745       DXFree((Pointer) vertex_data_averages);
1746     }
1747 
1748   return new_array;
1749 
1750 error:
1751 
1752   if (new_array)            DXDelete ((Object) new_array);
1753   if (new_array_data)       DXFree(new_array_data);
1754   if (vertex_weights)       DXFree(vertex_weights);
1755   if (vertex_data_averages) DXFree(vertex_data_averages);
1756 
1757  return (Array) NULL;
1758 }
1759 
1760 /*+--------------------------------------------------------------------------+
1761   |                                                                          |
1762   |   _dxfResampleComponentDepConAfterSimplification                         |
1763   |                                                                          |
1764   +--------------------------------------------------------------------------+*/
1765 
_dxfResampleComponentDepConAfterSimplification(Array array,int old_nT_after_conversion,int new_nT,int * face_parents,int * face_lut,float * old_face_areas)1766 Array _dxfResampleComponentDepConAfterSimplification(Array array,
1767 						     int old_nT_after_conversion,
1768 						     int new_nT, int *face_parents, int *face_lut,
1769 						     float *old_face_areas)
1770 {
1771   /* returns NULL in case an error occured and the resampling will not be done */
1772 
1773   Array   new_array = NULL;
1774 
1775   Pointer new_array_data = NULL;
1776 
1777   Type type;
1778   Category category;
1779 
1780   int
1781     rank,
1782     shape[3],
1783     size,
1784     tensor_size = 1,
1785     old_nT,
1786     i;
1787 
1788   float
1789     *face_data_weights  = NULL,
1790     *face_data_averages = NULL;
1791 
1792   DXGetArrayInfo(array, &old_nT, &type, &category, &rank, NULL);
1793 
1794   /* I do not average strings (of course) or higher order tensors  */
1795 
1796   if (type == TYPE_STRING || rank > 3) goto error;
1797 
1798   DXGetArrayInfo(array, &old_nT, &type, &category, &rank, shape);
1799 
1800 
1801   /* particular treatment if the array is a constant array */
1802 
1803   if (DXQueryConstantArray(array, &old_nT, NULL))
1804     {
1805       if (rank == 0)
1806 	new_array = (Array) DXNewConstantArray(new_nT, DXGetConstantArrayData(array), type, category, rank, 1);
1807 
1808       else
1809 	new_array = (Array) DXNewConstantArrayV(new_nT, DXGetConstantArrayData(array), type, category, rank, shape);
1810 
1811       if (!new_array) goto error;
1812     }
1813 
1814   else
1815 
1816     {
1817       /* the array was not a constant array: we need to resample */
1818 
1819       if (rank == 0)
1820 	new_array = DXNewArray(type, category, rank, 1);
1821       else
1822 	new_array = DXNewArrayV(type, category, rank, shape);
1823 
1824       if (!new_array) goto error;
1825 
1826       size = DXTypeSize(type);
1827 
1828       /* multiply the element size by the element shape */
1829 
1830       for(i=0;i<rank;i++)
1831 	tensor_size *= shape[i];
1832 
1833       new_array_data = DXAllocateZero(size * tensor_size * new_nT);
1834 
1835       if (!new_array_data) goto error;
1836 
1837       face_data_averages = (float *) DXAllocateZero(new_nT * tensor_size * sizeof (float));
1838 
1839       if (!face_data_averages) goto error;
1840 
1841       face_data_weights = (float *) DXAllocateZero(new_nT * tensor_size * sizeof (float));
1842 
1843       if (!face_data_weights) goto error;
1844 
1845       switch(type)
1846 	{
1847 	case TYPE_BYTE:
1848 	  _dxfRESAMPLE_DEP_CON_AFTER_SIMPLIFICATION(byte); break;
1849 
1850 	case TYPE_UBYTE:
1851 	  _dxfRESAMPLE_DEP_CON_AFTER_SIMPLIFICATION(ubyte); break;
1852 
1853 	case TYPE_DOUBLE:
1854 	  _dxfRESAMPLE_DEP_CON_AFTER_SIMPLIFICATION(double); break;
1855 
1856 	case TYPE_FLOAT:
1857 	  _dxfRESAMPLE_DEP_CON_AFTER_SIMPLIFICATION(float); break;
1858 
1859 	case TYPE_INT:
1860 	  _dxfRESAMPLE_DEP_CON_AFTER_SIMPLIFICATION(int); break;
1861 
1862 	case TYPE_UINT:
1863 	  _dxfRESAMPLE_DEP_CON_AFTER_SIMPLIFICATION(uint); break;
1864 
1865 	case TYPE_SHORT:
1866 	  _dxfRESAMPLE_DEP_CON_AFTER_SIMPLIFICATION(short); break;
1867 
1868 	case TYPE_USHORT:
1869 	  _dxfRESAMPLE_DEP_CON_AFTER_SIMPLIFICATION(ushort); break;
1870 
1871 	default:
1872 	  goto error; break;
1873 
1874 	}
1875 
1876       DXAddArrayData(new_array, 0, new_nT, new_array_data);
1877       DXFree((Pointer) new_array_data);
1878       DXFree((Pointer) face_data_averages);
1879       DXFree((Pointer) face_data_weights);
1880     }
1881 
1882   return new_array;
1883 
1884 error:
1885 
1886   if (new_array)            DXDelete ((Object) new_array);
1887 
1888   if (new_array_data)       DXFree(new_array_data);
1889   if (face_data_averages)   DXFree(face_data_averages);
1890   if (face_data_weights)    DXFree(face_data_weights);
1891 
1892  return (Array) NULL;
1893 
1894 
1895 }
1896 
1897 
1898 /*+--------------------------------------------------------------------------+
1899   |                                                                          |
1900   |   _dxfVertexNormalsfromTriangleNormals                                   |
1901   |                                                                          |
1902   +--------------------------------------------------------------------------+*/
1903 
_dxfVertexNormalsfromTriangleNormals(Array nor,int nV,int nT,int * triangles,float * face_normals,float * face_areas)1904 int _dxfVertexNormalsfromTriangleNormals(Array nor, int nV, int nT, int *triangles,
1905 					 float *face_normals, float *face_areas)
1906 
1907      /* this routine uses readily available face areas and face normals to compute
1908 	vertex normals */
1909 
1910 {
1911   float *vertex_normals = DXAllocateZero(nV * 3 * sizeof (float));
1912 
1913   if (!vertex_normals) goto error;
1914 
1915   else
1916     {
1917       /* actually compute the normals by averaging the face normals weighted
1918 	 with the face areas */
1919 
1920       register int i,j,k, the_vertex;
1921 
1922       register float *the_vertex_normal;
1923 
1924       for(i=0;i<nT;i++,face_areas++,face_normals+=3)
1925 
1926 	for(j=0;j<3;j++,triangles++)
1927 	  {
1928 	    the_vertex = triangles[0];
1929 
1930 	    /* add to the vertex normal of the_vertex the contribution of
1931 	       the current normal weighted by the current area */
1932 
1933 	    the_vertex_normal = vertex_normals + 3 * the_vertex;
1934 
1935 	    for (k=0;k<3;k++)
1936 	      the_vertex_normal[k] += face_areas[0] * face_normals[k];
1937 
1938 	  }
1939 
1940       /* normalize the vertex normals */
1941 
1942 
1943       the_vertex_normal = vertex_normals;
1944 
1945       for(i=0;i<nV;i++,the_vertex_normal+=3)
1946 	{
1947 	  double norm =
1948 	    the_vertex_normal[0]*the_vertex_normal[0]+
1949 	    the_vertex_normal[1]*the_vertex_normal[1]+
1950 	    the_vertex_normal[2]*the_vertex_normal[2];
1951 
1952 	  if (norm > 0.0)
1953 	    {
1954 	      norm = sqrt(norm);
1955 	      for (j=0;j<3;j++)
1956 		the_vertex_normal[j] /= norm;
1957 	    }
1958 	}
1959 
1960 
1961 
1962       DXAddArrayData(nor, 0, nV, vertex_normals);
1963 
1964       DXFree((Pointer)vertex_normals);
1965 
1966     }
1967 
1968 
1969 
1970   return 1;
1971 
1972 error:
1973 
1974   return 0;
1975 
1976 }
1977 
1978 /*+--------------------------------------------------------------------------+
1979   |                                                                          |
1980   |   _dxfFloatDataBoundingBoxDiagonal                                       |
1981   |                                                                          |
1982   +--------------------------------------------------------------------------+*/
1983 
_dxfFloatDataBoundingBoxDiagonal(float * data,int n_data,int data_dim)1984 float _dxfFloatDataBoundingBoxDiagonal(float *data, int n_data, int data_dim)
1985 {
1986   float diagonal = 0.0;
1987 
1988   float
1989     *cur_data = data,
1990     *bbox     = NULL;
1991 
1992   register int i,j;
1993 
1994   bbox = (float *) DXAllocateZero (2 * data_dim * sizeof (float));
1995 
1996   if (!bbox) goto error;
1997 
1998   /* initialize the bounding box to the first array values */
1999 
2000   /* the bbox is organized as follows
2001 
2002      bbox[0]               MININUM
2003      bbox[1]                DATA
2004                             VALUES
2005 			    FOR
2006 			    EACH
2007      bbox[data_dim-1]       COMPONENT
2008 
2009 
2010      bbox[data_dim]        MAXIMUM
2011      bbox[data_dim+1]       DATA
2012                             VALUES
2013 			    FOR
2014 			    EACH
2015      bbox[2*data_dim-1]     COMPONENT
2016      */
2017 
2018   for(j=0;j<data_dim;j++)
2019     {
2020       bbox[j] = bbox[j+data_dim] = data[j];
2021     }
2022 
2023   /* update the bounding box if data values are smaller than the minimum or larger than the maximum */
2024 
2025   cur_data = data + data_dim;
2026 
2027   for (i=1;i<n_data;i++,cur_data+=data_dim)
2028 
2029     for(j=0;j<data_dim;j++)
2030       {
2031 	if (cur_data[j] < bbox[j]) bbox[j] = cur_data[j];
2032 
2033 	else if (cur_data[j] > bbox[j+data_dim]) bbox[j+data_dim] = cur_data[j];
2034       }
2035 
2036   /* once the bounding box is complete, compute its diameter. If the dimension of the data is
2037      one, we just compute max-min and thus avoid the computation of squares and of a square root */
2038 
2039   if (data_dim == 1) diagonal = bbox[1] - bbox[0];
2040 
2041   else
2042     {
2043       double
2044 	one_coordinate,
2045 	double_diagonal = 0.0;
2046 
2047       for (j=0;j<data_dim;j++)
2048 	{
2049 	  one_coordinate   = bbox[j+data_dim] - bbox[j];
2050 	  double_diagonal += one_coordinate * one_coordinate;
2051 	}
2052 
2053       if (double_diagonal>0.0) double_diagonal = sqrt(double_diagonal);
2054 
2055       diagonal = (float) double_diagonal;
2056     }
2057 
2058   DXFree((Pointer)bbox);
2059   return diagonal;
2060 
2061 error:
2062 
2063   DXFree((Pointer)bbox);
2064 
2065   return 0.0;
2066 }
2067 
2068 /*+--------------------------------------------------------------------------+
2069   |                                                                          |
2070   |   _dxfQuads2Triangles                                                    |
2071   |                                                                          |
2072   +--------------------------------------------------------------------------+*/
2073 
_dxfQuads2Triangles(int * nQ,int ** connections,int ** face_lut)2074 int _dxfQuads2Triangles(int *nQ, int **connections, int **face_lut)
2075 {
2076 
2077   /* create a new array for connections, as well as look-up table */
2078 
2079   int
2080     i,
2081     new_nT = 2 * *nQ,
2082     *quads = *connections,
2083     *new_t = NULL,
2084     *t,
2085     *lut;
2086 
2087   new_t = (int *) DXAllocateZero(new_nT * 3 * sizeof (int));
2088 
2089   if (!new_t) goto error;
2090 
2091   *face_lut = (int *) DXAllocateZero(new_nT * sizeof (int));
2092 
2093   if (!(*face_lut)) goto error;
2094 
2095   /* the triangles are created according to the following simple rule:
2096 
2097          (v0,v1,v2,v3)        ---->  (v0,v2,v1) + (v1,v2,v3) see page 3-6 of the user's guide
2098 
2099            ___                                 v1 _v3
2100        v1 |   | v3                    v1|\       \ |
2101           |   |                ---->    |_\   +   \|
2102        v0 |___| v2                    v0   v2      v2
2103 
2104        */
2105 
2106   t   = new_t;
2107 
2108   lut = *face_lut;
2109 
2110   for(i=0;i< *nQ;i++,quads+=4,lut+=2)
2111     {
2112       /* write the first triangle (v0,v2,v1) */
2113       t[0] = quads[0];
2114       t[1] = quads[2];
2115       t[2] = quads[1];
2116 
2117       /* set the triangle pointer to point to the next triangle */
2118       t+=3;
2119 
2120       /* write the second triangle (v1,v2,v3) */
2121       t[0] = quads[1];
2122       t[1] = quads[2];
2123       t[2] = quads[3];
2124 
2125       t+=3;
2126 
2127       /* both triangles have a face look up table entry of i, which is the index
2128          of the original quad to which they correspond */
2129 
2130       lut[0] = i;
2131       lut[1] = i;
2132     }
2133 
2134 
2135   /* "export" the new connections array outside this routine; They will
2136      be freed after the simplification is completed */
2137 
2138   *connections = new_t;
2139 
2140   /* the number of triangles after conversion of "quads" to triangles is: */
2141 
2142   *nQ = new_nT;
2143 
2144   return 1;
2145 
2146 error:
2147   if (new_t)      DXFree((Pointer)new_t);
2148   if (*face_lut) {DXFree((Pointer)(*face_lut)); *face_lut = NULL;}
2149   return 0;
2150 }
2151 
2152 /*****************************************************************************/
2153