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