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 
12 #include <dx/dx.h>
13 #include <math.h>
14 
15 #if defined(HAVE_STRING_H)
16 #include <string.h>
17 #endif
18 
19 #define ZOFFSET         1.0
20 #define SENSITIVITY	4.0
21 
22 typedef struct
23 {
24     float x, y;
25 } Point2D;
26 
27 typedef struct
28 {
29     int		depth;
30     int		pickPath[10240];
31     Array	picks;
32     Array	paths;
33     Array       weights;
34 } PickBuf;
35 
36 typedef struct
37 {
38     Point	xyz;
39     int		index;
40     int		pathlen;
41     int		poke;
42 } Pick;
43 
44 typedef struct
45 {
46     int 	segment;
47     int		nweights;
48 } PickWts;
49 
50 #define BOX_HIT    1
51 #define BOX_MISS   0
52 #define BOX_ERROR -1
53 
54 static Error  GetObjectAndCameraFromCache(char *, Object *, Camera *);
55 static Field  GetPicksFromCache(char *, Object *);
56 static Error  PutPicksIntoCache(char *, Object *, Field);
57 
58 static Error  PickObject(Object object, PickBuf *pick, int, Point2D *,
59 					Matrix *stack, int, float, float,
60 					float, float, Camera);
61 
62 static Error  PickField(Field f, PickBuf *p, int, Point2D,
63 					Matrix *s, int, float, float,
64 					float, float);
65 static int    PickBox(Field f, Point2D, Matrix *s, int, float);
66 static Point *TransformArray(Array src, Matrix *m);
67 static int    TriangleHit(Point **, Point2D *, Point *, int, float *);
68 
69 static Error  PickTriangles(Field, PickBuf *, int, Point2D,
70 					Matrix *, int, float, float,
71 					float, float);
72 static Error  PickQuads(Field, PickBuf *, int, Point2D,
73 					Matrix *, int, float, float,
74 					float, float);
75 static Error  Elt2D_Perspective(PickBuf *, int, Point2D, Point *,
76 					int *, int, int, int,
77 					float, int *, float,
78 					float, float);
79 static Error  Elt2D_NoPerspective(PickBuf *, int, Point2D, Point *,
80 					int *, int, int, int,
81 					float, int *, float,
82 					float, float);
83 
84 static Error  PickLines(Field, PickBuf *, int, Point2D,
85 					Matrix *, int, float, float,
86 					float, float);
87 static Error  Elt1D_Perspective(PickBuf *, int, Point2D, Point *,
88 					int *, int, int, float, float,
89 					float, float);
90 static Error  Elt1D_NoPerspective(PickBuf *, int, Point2D, Point *,
91 					int *, int, int, float, float,
92 					float, float);
93 
94 static Error  PickPoints(Field, PickBuf *, int, Point2D,
95 					Matrix *, int, float, float,
96 					float, float);
97 static Error  Elt0D_Perspective(PickBuf *, int, Point2D, Point *,
98 					int, float, float,
99 					float, float);
100 static Error  Elt0D_NoPerspective(PickBuf *, int, Point2D, Point *,
101 					int, float, float,
102 					float, float);
103 
104 static Error  PickFLE(Field, PickBuf *, int, Point2D,
105 					Matrix *, int, float, float,
106 					float, float);
107 static Error  FLE_NoPerspective(PickBuf *, int, Point2D, Point *, int,
108 					int *, int, int *, int, int *, int,
109 					float, float,
110 					float, float);
111 static Error  FLE_Perspective(PickBuf *, int, Point2D, Point *, int,
112 					int *, int, int *, int, int *, int,
113 					float, float,
114 					float, float);
115 
116 static Error  PickPolylines(Field, PickBuf *, int, Point2D,
117 					Matrix *, int, float, float,
118 					float, float);
119 
120 static Error  Polyline_Perspective(PickBuf *, int, Point2D, Point *,
121 					int *, int *, int, int, int, float,
122 					float, float, float);
123 
124 static Error  Polyline_NoPerspective(PickBuf *, int, Point2D, Point *,
125 					int *, int *, int, int, int, float,
126 					float, float, float);
127 
128 static Error  AddPick(PickBuf *, int, Point, int, int, float *);
129 static Field  PickBufToField(PickBuf *, Matrix);
130 static Field  GetFirstHits(Field);
131 static Error  PickInterpolate(Object, Field, int);
132 
133 extern Error _dxfLoadStereoModes();
134 extern Error _dxfInitializeStereoCameraMode(void *, Object);
135 
136 #define CACHE_PICKS	1
137 #define NO_CACHE_PICKS	0
138 
139 #define FIRST_HIT_ONLY  1
140 #define ALL_HITS	0
141 
142 #define PICK_TAG_ARG    0
143 #define IMAGE_TAG_ARG   1
144 #define LIST_ARG 	2
145 #define STEP_ARG 	3
146 #define FIRST_ARG 	4
147 #define PERSISTENCE_ARG	5
148 #define INTERPOLATE_ARG 6
149 #define OBJECT_ARG      7
150 #define CAMERA_ARG	8
151 
152 #define NO_INTERP	0
153 #define CLOSEST_VERTEX  1
154 #define INTERPOLATE     2
155 
156 /*
157  * Used only by StereoPick
158  */
159 #define STEREO_ARG      9
160 #define WHERE_ARG       10
161 
162 static Array getXY(Array);
163 
164 Error
m_Pick(Object * in,Object * out)165 m_Pick(Object *in, Object *out)
166 {
167     Object   cacheObject = NULL, object = NULL;
168     Array    list = NULL;
169     Camera   camera = NULL;
170     PickBuf  pick;
171     int      nPicks, nDim, i;
172     Matrix   initialMatrix, cameraMatrix, invCameraMatrix;
173     Point2D  *xy, *xyPicks = NULL;
174     int	     persp;
175     float    nearPlane;
176     float    width;
177     float    xCenter, yCenter;
178     int      xRes, yRes;
179     Field    result = NULL, field = NULL;
180     float    sensitivity;
181     char     *ptag, *itag;
182     int      persistence;
183     int	     first;
184     int      interpolate = NO_INTERP;
185 
186     pick.picks     = NULL;
187     pick.paths     = NULL;
188     pick.weights   = NULL;
189     pick.depth     = 0;
190 
191     out[0] = NULL;
192 
193     if (in[CAMERA_ARG])
194     {
195 	if (in[OBJECT_ARG] == NULL)
196 	{
197 	    DXSetError(ERROR_MISSING_DATA, "#10000", "object");
198 	    goto error;
199 	}
200 	object = in[OBJECT_ARG];
201 
202 	if (in[CAMERA_ARG] == NULL)
203 	{
204 	    DXSetError(ERROR_MISSING_DATA, "#10000", "camera");
205 	    goto error;
206 	}
207 	camera = (Camera)in[CAMERA_ARG];
208 
209 	if (in[FIRST_ARG])
210 	{
211 	    if (! DXExtractInteger(in[FIRST_ARG], &first))
212 	    {
213 		DXSetError(ERROR_BAD_PARAMETER, "first must be integer");
214 		goto error;
215 	    }
216 
217 	    if (first != FIRST_HIT_ONLY && first != ALL_HITS)
218 	    {
219 		DXSetError(ERROR_BAD_PARAMETER, "first must be 0 or 1");
220 		goto error;
221 	    }
222 	}
223 	else
224 	    first = FIRST_HIT_ONLY;
225 
226 	if (in[INTERPOLATE_ARG])
227 	{
228 	    if (! DXExtractInteger(in[INTERPOLATE_ARG], &interpolate))
229 	    {
230 		DXSetError(ERROR_BAD_PARAMETER, "interpolate must be integer");
231 		goto error;
232 	    }
233 
234 	    if (interpolate != NO_INTERP &&
235 		interpolate != CLOSEST_VERTEX &&
236 		interpolate != INTERPOLATE)
237 	    {
238 		DXSetError(ERROR_BAD_PARAMETER, "interpolate must be %d %d or %d",
239 			NO_INTERP, CLOSEST_VERTEX, INTERPOLATE);
240 		goto error;
241 	    }
242 	}
243 
244 	list = (Array)in[LIST_ARG];
245 	if (list)
246 	{
247 	    list = getXY(list);
248 	    if (!list && DXGetError() != ERROR_NONE)
249 		goto error;
250 	}
251 
252 	if (object && camera && list)
253 	{
254 	    DXGetArrayInfo(list, &nPicks, NULL, NULL, NULL, &nDim);
255 	    if (nDim != 2)
256 	    {
257 		DXSetError(ERROR_DATA_INVALID, "pick list must be 2-D");
258 		goto error;
259 	    }
260 
261 	    xyPicks = (Point2D *)DXAllocate(nPicks*sizeof(Point2D));
262 	    if (! xyPicks)
263 		goto error;
264 
265 	    if (! DXExtractParameter((Object)list, TYPE_FLOAT,
266 					    2, nPicks, (Pointer)xyPicks))
267 		goto error;
268 
269 	    cameraMatrix = DXGetCameraMatrix(camera);
270 	    cameraMatrix = DXConcatenate(cameraMatrix,
271 				DXTranslate(DXPt(0.0,0.0,ZOFFSET)));
272 	    invCameraMatrix = DXInvert(cameraMatrix);
273 
274 	    DXGetCameraResolution(camera, &xRes, &yRes);
275 
276 	    persp = 0;
277 	    if (! DXGetOrthographic(camera, &width, NULL))
278 	    {
279 		DXGetPerspective(camera, &width, NULL);
280 		persp = 1;
281 		nearPlane = -0.001;
282 	    }
283 	    else
284 	    {
285 		nearPlane = DXD_MAX_FLOAT;
286 	    }
287 
288 	    xCenter = xRes / 2.0;
289 	    yCenter = yRes / 2.0;
290 
291 	    sensitivity = SENSITIVITY;
292 
293 	    initialMatrix = cameraMatrix;
294 
295 
296 	    xy = xyPicks;
297 	    for (i = 0; i < nPicks; i++, xy ++)
298 	    {
299 		xy->x = xy->x - xCenter;
300 		xy->y = (yRes-xy->y) - yCenter;
301 
302 		if (! PickObject(object, &pick, i, xy, &initialMatrix, persp,
303 			nearPlane, sensitivity, -DXD_MAX_FLOAT, DXD_MAX_FLOAT,
304 			camera))
305 		     goto error;
306 	    }
307 
308 	    field = PickBufToField(&pick, invCameraMatrix);
309 	    if (! field)
310 		goto error;
311 
312 
313 	    if (! DXSetAttribute((Object)field, "pick object", object))
314 		goto error;
315 
316 	}
317 
318 	if (field)
319 	{
320 	    object = DXGetAttribute((Object)field, "pick object");
321 	    if (! object)
322 	    {
323 		DXSetError(ERROR_MISSING_DATA,
324 		     "unable to access pick object attribute");
325 		goto error;
326 	    }
327 
328 	    if (first == FIRST_HIT_ONLY)
329 		result = GetFirstHits(field);
330 	    else
331 	    {
332 		result = (Field)DXCopy((Object)field, COPY_STRUCTURE);
333 		DXSetAttribute((Object)result, "pick object", NULL);
334 	    }
335 
336 	    if (! result)
337 		goto error;
338 
339 	    if (interpolate && !DXEmptyField(result))
340 	    {
341 		Array array;
342 		int   n, nn, i;
343 
344 		if (! PickInterpolate(object, result, interpolate))
345 		    goto error;
346 
347 		/*
348 		 * error check to verify that all picks got all data
349 		 */
350 
351 		array = (Array)DXGetComponentValue(result, "positions");
352 		DXGetArrayInfo(array, &n, NULL, NULL, NULL, NULL);
353 
354 		for (i = 0;
355 		     NULL != (array =
356 			    (Array)DXGetEnumeratedComponentValue(result, i, NULL));
357 		     i++)
358 		{
359 		    Object attr = DXGetAttribute((Object)array, "dep");
360 		    if (! attr)
361 			continue;
362 
363 		    if (! strcmp("positions", DXGetString((String)attr)))
364 		    {
365 			DXGetArrayInfo(array, &nn, NULL, NULL, NULL, NULL);
366 			if (nn != n)
367 			{
368 			    DXSetError(ERROR_DATA_INVALID,
369 			       "component mismatch in pick object");
370 			    goto error;
371 			}
372 		    }
373 		}
374 	    }
375 
376 	    DXDeleteComponent(result, "pick weights");
377 
378 	    DXDelete(DXReference((Object)field));
379 	}
380 	else
381 	{
382 	    result = DXNewField();
383 	    if (! result)
384 		goto error;
385 	}
386     }
387     else
388     {
389 	if (in[PICK_TAG_ARG] == NULL)
390 	{
391 	    DXSetError(ERROR_MISSING_DATA, "#10000", "pickTag");
392 	    goto error;
393 	}
394 
395 	if (! DXExtractString(in[PICK_TAG_ARG], &ptag))
396 	{
397 	    DXSetError(ERROR_DATA_INVALID, "pickTag must be STRING");
398 	    goto error;
399 	}
400 
401 	if (in[IMAGE_TAG_ARG] == NULL)
402 	{
403 	    if (in[LIST_ARG] != NULL)
404 	    {
405 		DXSetError(ERROR_MISSING_DATA, "#10000", "imageTag");
406 		goto error;
407 	    }
408 	}
409 	else
410 	{
411 	    if (! DXExtractString(in[IMAGE_TAG_ARG], &itag))
412 	    {
413 		DXSetError(ERROR_DATA_INVALID, "imageTag must be STRING");
414 		goto error;
415 	    }
416 
417 	    if (! GetObjectAndCameraFromCache(itag, &cacheObject, &camera))
418 		goto error;
419 	}
420 
421 	if (in[PERSISTENCE_ARG])
422 	{
423 	    if (! DXExtractInteger(in[PERSISTENCE_ARG], &persistence))
424 	    {
425 		DXSetError(ERROR_BAD_PARAMETER, "persistence must be integer");
426 		goto error;
427 	    }
428 
429 	    if (persistence != CACHE_PICKS && persistence != NO_CACHE_PICKS)
430 	    {
431 		DXSetError(ERROR_BAD_PARAMETER, "persistence must be 0 or 1");
432 		goto error;
433 	    }
434 	}
435 	else
436 	    persistence = CACHE_PICKS;
437 
438 	if (in[FIRST_ARG])
439 	{
440 	    if (! DXExtractInteger(in[FIRST_ARG], &first))
441 	    {
442 		DXSetError(ERROR_BAD_PARAMETER, "first must be integer");
443 		goto error;
444 	    }
445 
446 	    if (first != FIRST_HIT_ONLY && first != ALL_HITS)
447 	    {
448 		DXSetError(ERROR_BAD_PARAMETER, "first must be 0 or 1");
449 		goto error;
450 	    }
451 	}
452 	else
453 	    first = FIRST_HIT_ONLY;
454 
455 	if (in[INTERPOLATE_ARG])
456 	{
457 	    if (! DXExtractInteger(in[INTERPOLATE_ARG], &interpolate))
458 	    {
459 		DXSetError(ERROR_BAD_PARAMETER, "interpolate must be integer");
460 		goto error;
461 	    }
462 
463 	    if (interpolate != NO_INTERP &&
464 		interpolate != CLOSEST_VERTEX &&
465 		interpolate != INTERPOLATE)
466 	    {
467 		DXSetError(ERROR_BAD_PARAMETER, "interpolate must be %d %d or %d",
468 			NO_INTERP, CLOSEST_VERTEX, INTERPOLATE);
469 		goto error;
470 	    }
471 	}
472 
473 	object = in[OBJECT_ARG];
474 	if (! object)
475 	    object = cacheObject;
476 
477 	list = (Array)in[LIST_ARG];
478 
479 	if (object && camera && list)
480 	{
481 	    if (DXGetObjectClass((Object)list) != CLASS_ARRAY)
482 	    {
483 		DXSetError(ERROR_DATA_INVALID, "pick list must be class ARRAY");
484 		goto error;
485 	    }
486 
487 	    DXGetArrayInfo(list, &nPicks, NULL, NULL, NULL, &nDim);
488 	    if (nDim != 2)
489 	    {
490 		DXSetError(ERROR_DATA_INVALID, "pick list must be 2-D");
491 		goto error;
492 	    }
493 
494 	    xyPicks = (Point2D *)DXAllocate(nPicks*sizeof(Point2D));
495 	    if (! xyPicks)
496 		goto error;
497 
498 	    if (! DXExtractParameter((Object)list, TYPE_FLOAT,
499 					    2, nPicks, (Pointer)xyPicks))
500 		goto error;
501 
502 	    cameraMatrix = DXGetCameraMatrix(camera);
503 	    cameraMatrix = DXConcatenate(cameraMatrix,
504 				DXTranslate(DXPt(0.0,0.0,ZOFFSET)));
505 	    invCameraMatrix = DXInvert(cameraMatrix);
506 
507 	    DXGetCameraResolution(camera, &xRes, &yRes);
508 
509 	    persp = 0;
510 	    if (! DXGetOrthographic(camera, &width, NULL))
511 	    {
512 		DXGetPerspective(camera, &width, NULL);
513 		persp = 1;
514 		nearPlane = -0.001;
515 	    }
516 	    else
517 	    {
518 		nearPlane = DXD_MAX_FLOAT;
519 	    }
520 
521 	    xCenter = xRes / 2.0;
522 	    yCenter = yRes / 2.0;
523 
524 	    sensitivity = SENSITIVITY;
525 
526 	    initialMatrix = cameraMatrix;
527 
528 	    xy = xyPicks;
529 	    for (i = 0; i < nPicks; i++, xy ++)
530 	    {
531 		xy->x = xy->x - xCenter;
532 		xy->y = xy->y - yCenter;
533 
534 		if (! PickObject(object, &pick, i, xy, &initialMatrix, persp,
535 			nearPlane, sensitivity, -DXD_MAX_FLOAT, DXD_MAX_FLOAT,
536 			camera))
537 		     goto error;
538 	    }
539 
540 	    field = PickBufToField(&pick, invCameraMatrix);
541 	    if (! field)
542 		goto error;
543 
544 
545 	    if (! DXSetAttribute((Object)field, "pick object", object))
546 		goto error;
547 
548 	    if (! PutPicksIntoCache(ptag, in, field))
549 		goto error;
550 
551 	}
552 	else if (persistence == CACHE_PICKS)
553 	{
554 	    field = GetPicksFromCache(ptag, in);
555 	}
556 
557 	if (field)
558 	{
559 	    object = DXGetAttribute((Object)field, "pick object");
560 	    if (! object)
561 	    {
562 		DXSetError(ERROR_MISSING_DATA,
563 		     "unable to access pick object attribute");
564 		goto error;
565 	    }
566 
567 	    if (first == FIRST_HIT_ONLY)
568 		result = GetFirstHits(field);
569 	    else
570 	    {
571 		result = (Field)DXCopy((Object)field, COPY_STRUCTURE);
572 		DXSetAttribute((Object)result, "pick object", NULL);
573 	    }
574 
575 	    if (! result)
576 		goto error;
577 
578 	    if (interpolate && !DXEmptyField(result))
579 	    {
580 		Array array;
581 		int   n, nn, i;
582 
583 		if (! PickInterpolate(object, result, interpolate))
584 		    goto error;
585 
586 		/*
587 		 * error check to verify that all picks got all data
588 		 */
589 
590 		array = (Array)DXGetComponentValue(result, "positions");
591 		DXGetArrayInfo(array, &n, NULL, NULL, NULL, NULL);
592 
593 		for (i = 0;
594 		     NULL != (array =
595 			    (Array)DXGetEnumeratedComponentValue(result, i, NULL));
596 		     i++)
597 		{
598 		    Object attr = DXGetAttribute((Object)array, "dep");
599 		    if (! attr)
600 			continue;
601 
602 		    if (! strcmp("positions", DXGetString((String)attr)))
603 		    {
604 			DXGetArrayInfo(array, &nn, NULL, NULL, NULL, NULL);
605 			if (nn != n)
606 			{
607 			    DXSetError(ERROR_DATA_INVALID,
608 			       "component mismatch in pick object");
609 			    goto error;
610 			}
611 		    }
612 		}
613 	    }
614 
615 	    DXDeleteComponent(result, "pick weights");
616 
617 	    DXDelete(DXReference((Object)field));
618 	}
619 	else
620 	{
621 	    result = DXNewField();
622 	    if (! result)
623 		goto error;
624 	}
625 
626     }
627 
628     if (camera != (Camera)in[CAMERA_ARG])
629         DXDelete((Object)camera);
630 
631     if (list && (Object)list != in[LIST_ARG])
632 	DXDelete((Object)list);
633 
634     if (! result)
635 	out[0] = (Object)DXNewField();
636     else
637 	out[0] = (Object)result;
638 
639     DXDelete((Object)cacheObject);
640     DXDelete((Object)pick.picks);
641     DXDelete((Object)pick.paths);
642     DXDelete((Object)pick.weights);
643 
644     DXFree((Pointer)xyPicks);
645 
646     return OK;
647 
648 error:
649     if (list && (Object)list != in[LIST_ARG])
650 	DXDelete((Object)list);
651 
652     if (camera != (Camera)in[CAMERA_ARG])
653         DXDelete((Object)camera);
654 
655     DXDelete((Object)cacheObject);
656     DXDelete((Object)camera);
657 
658     if (field)
659 	DXDelete(DXReference((Object)field));
660 
661     DXDelete((Object)result);
662     DXDelete((Object)pick.picks);
663     DXDelete((Object)pick.paths);
664     DXDelete((Object)pick.weights);
665     DXFree((Pointer)xyPicks);
666 
667     out[0] = NULL;
668 
669     return ERROR;
670 }
671 
672 extern Error _dxfGetStereoCameras(void *, Camera, Camera *, Camera *);
673 extern void *_dxfGetStereoWindowInfo(char *, void *, void *);
674 
m_StereoPick(Object * in,Object * out)675 Error m_StereoPick(Object *in, Object *out)
676 {
677     Camera camera, lcamera, rcamera;
678     char *where;
679     void *globals;
680 
681     out[0] = NULL;
682 
683     if (! in[LIST_ARG])
684         return OK;
685 
686     in[LIST_ARG] = (Object)getXY((Array)in[LIST_ARG]);
687     if (! in[LIST_ARG])
688         return OK;
689 
690     if (! in[STEREO_ARG])
691     {
692         DXSetError(ERROR_BAD_PARAMETER, "stereo arg must be given");
693         return ERROR;
694     }
695 
696     if (! in[WHERE_ARG] || ! DXExtractString(in[WHERE_ARG], &where))
697     {
698         DXSetError(ERROR_BAD_PARAMETER, "where must be given");
699         return ERROR;
700     }
701 
702     if (! _dxfLoadStereoModes())
703     {
704         DXSetError(ERROR_BAD_PARAMETER, "error loading stereo code");
705         return ERROR;
706     }
707 
708     globals = _dxfGetStereoWindowInfo(where, NULL, NULL);
709     if (! globals)
710     {
711         DXSetError(ERROR_BAD_PARAMETER, "error accessing stereo window info");
712         return ERROR;
713     }
714 
715     if (! in[CAMERA_ARG])
716     {
717         DXSetError(ERROR_BAD_PARAMETER, "camera arg must be given");
718         return ERROR;
719     }
720 
721     camera = (Camera)in[CAMERA_ARG];
722 
723     _dxfLoadStereoModes();
724 
725     if (! _dxfInitializeStereoCameraMode(globals, in[STEREO_ARG]))
726     {
727         DXSetError(ERROR_BAD_PARAMETER, "error initting stereo mode");
728         return ERROR;
729     }
730 
731     if (! _dxfGetStereoCameras(globals, camera, &lcamera, &rcamera))
732     {
733         DXSetError(ERROR_BAD_PARAMETER, "error getting stereo cameras");
734         return ERROR;
735     }
736 
737     return m_Pick(in, out);
738 }
739 
740 static Field
GetPicksFromCache(char * tag,Object * in)741 GetPicksFromCache(char *tag, Object *in)
742 {
743     Object obj;
744     char *buf = (char *)DXAllocate(strlen(tag) + strlen(".picks") + 1);
745     if (! buf)
746 	return ERROR;
747 
748     sprintf(buf, "%s.picks", tag);
749 
750     obj = DXGetCacheEntry(buf, 0, 1, in[OBJECT_ARG]);
751 
752     DXFree((Pointer)buf);
753     return (Field)obj;
754 }
755 
756 static Error
PutPicksIntoCache(char * tag,Object * in,Field f)757 PutPicksIntoCache(char *tag, Object *in, Field f)
758 {
759     Error e;
760     char *buf = (char *)DXAllocate(strlen(tag) + strlen(".picks") + 1);
761     if (! buf)
762 	return ERROR;
763 
764     sprintf(buf, "%s.picks", tag);
765 
766     e = DXSetCacheEntry((Object)f, CACHE_PERMANENT, buf, 0, 1,
767 						    in[OBJECT_ARG]);
768 
769     DXFree((Pointer)buf);
770     return e;
771 }
772 
773 static Error
AddPick(PickBuf * p,int poke,Point xyz,int nwts,int seg,float * wts)774 AddPick(PickBuf *p, int poke, Point xyz, int nwts, int seg, float *wts)
775 {
776     int nPicks, nPathElts, depth = p->depth + 2, wtLnth;
777     Pick pick;
778     PickWts pickWts;
779 
780     if (p->picks == NULL)
781     {
782 	p->picks   = DXNewArray(TYPE_UBYTE, CATEGORY_REAL, 1, sizeof(Pick));
783 	p->paths   = DXNewArray(TYPE_INT, CATEGORY_REAL, 0);
784 	p->weights = DXNewArray(TYPE_UBYTE, CATEGORY_REAL, 0);
785 	if (! p->picks || ! p->paths || ! p->weights)
786 	    return ERROR;
787 
788 	DXReference((Object)p->picks);
789 	DXReference((Object)p->paths);
790 	DXReference((Object)p->weights);
791     }
792 
793     DXGetArrayInfo(p->picks, &nPicks, NULL, NULL, NULL, NULL);
794     DXGetArrayInfo(p->paths, &nPathElts, NULL, NULL, NULL, NULL);
795 
796     pick.xyz     = xyz;
797     pick.index   = nPathElts;
798     pick.poke    = poke;
799     pick.pathlen = depth;
800 
801     if (! DXAddArrayData(p->picks, nPicks, 1, (Pointer)&pick))
802 	return ERROR;
803 
804     if (! DXAddArrayData(p->paths, nPathElts, depth, (Pointer)p->pickPath))
805 	return ERROR;
806 
807     pickWts.nweights = nwts;
808     pickWts.segment  = seg;
809 
810     DXGetArrayInfo(p->weights, &wtLnth, NULL, NULL, NULL, NULL);
811     if (! DXAddArrayData(p->weights, wtLnth, sizeof(pickWts), (Pointer)&pickWts))
812 	return ERROR;
813 
814     if (nwts)
815     {
816 	wtLnth += sizeof(pickWts);
817 	if (! DXAddArrayData(p->weights, wtLnth, nwts*sizeof(float), (Pointer)wts))
818 	    return ERROR;
819     }
820 
821     return OK;
822 }
823 
824 #define TYPE      Pick
825 #define LT(a,b)   ((a)->poke < (b)->poke || \
826 			(((a)->poke == (b)->poke) && (a)->xyz.z > (b)->xyz.z))
827 #define GT(a,b)   ((a)->poke > (b)->poke || \
828 			(((a)->poke == (b)->poke) && (a)->xyz.z < (b)->xyz.z))
829 #define QUICKSORT picksort
830 
831 #include "../libdx/qsort.c"
832 
833 static Field
PickBufToField(PickBuf * p,Matrix m)834 PickBufToField(PickBuf *p, Matrix m)
835 {
836     Array positions = NULL;
837     Array pokes     = NULL;
838     Array indices   = NULL;
839     Array paths     = NULL;
840     Field field     = NULL;
841     int   nPicks, nPokes, nPathElts;
842     Pick  *picks;
843     Point *posPtr;
844     int   *pokPtr;
845     int   *idxPtr;
846     int   *pathSrc, *pathDst;
847     int   i, j, lastPoke;
848 
849     if (p->picks == NULL)
850         return DXNewField();
851 
852     DXGetArrayInfo(p->picks, &nPicks,    NULL, NULL, NULL, NULL);
853     DXGetArrayInfo(p->paths, &nPathElts, NULL, NULL, NULL, NULL);
854 
855     picks = (Pick *)DXGetArrayData(p->picks);
856 
857     picksort(picks, nPicks);
858 
859     /*
860      * Count the pokes
861      */
862     nPokes = 1;
863     i = picks[0].poke;
864     for (j = 1; j < nPicks; j++)
865 	if (i != picks[j].poke)
866 	{
867 	    nPokes ++;
868 	    i = picks[j].poke;
869 	}
870 
871     positions = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, 3);
872     pokes     = DXNewArray(TYPE_INT,   CATEGORY_REAL, 0);
873     indices   = DXNewArray(TYPE_INT,   CATEGORY_REAL, 0);
874     paths     = DXNewArray(TYPE_INT,   CATEGORY_REAL, 0);
875 
876     if (! positions || ! pokes || ! indices || ! paths)
877 	return NULL;
878 
879     if (! DXAddArrayData(positions, 0, nPicks,    NULL) ||
880 	! DXAddArrayData(pokes,     0, nPokes,    NULL) ||
881 	! DXAddArrayData(indices,   0, nPicks,    NULL) ||
882 	! DXAddArrayData(paths,     0, nPathElts, NULL))
883 	return NULL;
884 
885     posPtr = (Point *)DXGetArrayData(positions);
886     pokPtr = (int   *)DXGetArrayData(pokes);
887     idxPtr = (int   *)DXGetArrayData(indices);
888 
889     pathSrc = (int   *)DXGetArrayData(p->paths);
890     pathDst = (int   *)DXGetArrayData(paths);
891 
892     lastPoke = -1;
893     for (i = j = 0; i < nPicks; i++, picks ++)
894     {
895 	*posPtr++ = DXApply(picks->xyz, m);
896 	*idxPtr++ = j;
897 
898 	memcpy(pathDst, pathSrc+picks->index, picks->pathlen*sizeof(int));
899 	pathDst += picks->pathlen;
900 	j += picks->pathlen;
901 
902 	if (picks->poke != lastPoke)
903 	{
904 	    *pokPtr++ = i;
905 	    lastPoke = picks->poke;
906 	}
907     }
908 
909     field = DXNewField();
910     if (! field)
911 	goto error;
912 
913     if ((! DXSetStringAttribute((Object)positions, "dep", "positions"))   ||
914         (! DXSetStringAttribute((Object)pokes,     "ref", "picks"))       ||
915         (! DXSetStringAttribute((Object)indices,   "dep", "positions"))   ||
916         (! DXSetStringAttribute((Object)indices,   "ref", "pick paths")))
917 	goto error;
918 
919     if (! DXSetComponentValue(field, "positions", (Object)positions))
920 	goto error;
921     positions = NULL;
922 
923     if (! DXSetComponentValue(field, "pokes", (Object)pokes))
924 	goto error;
925     pokes = NULL;
926 
927     if (! DXSetComponentValue(field, "picks", (Object)indices))
928 	goto error;
929     indices = NULL;
930 
931     if (! DXSetComponentValue(field, "pick paths", (Object)paths))
932 	goto error;
933     paths = NULL;
934 
935     if (! DXSetComponentValue(field, "pick weights", (Object)p->weights))
936 	goto error;
937 
938     return field;
939 
940 error:
941     DXDelete((Object)positions);
942     DXDelete((Object)pokes);
943     DXDelete((Object)indices);
944     DXDelete((Object)paths);
945     DXDelete((Object)field);
946     return NULL;
947 }
948 
949 static Error
GetObjectAndCameraFromCache(char * tag,Object * object,Camera * camera)950 GetObjectAndCameraFromCache(char *tag, Object *object, Camera *camera)
951 {
952     char *buf;
953 
954     if (object)
955     {
956 	buf = (char *)DXAllocate(strlen(tag) + strlen(".object") + 1);
957 	if (! buf)
958 	    goto error;
959 
960 	sprintf(buf, "%s.object", tag);
961 
962 	*object = DXGetCacheEntry(buf, 0, 0);
963 	DXFree((Pointer)buf);
964     }
965 
966     if (camera)
967     {
968 	buf = (char *)DXAllocate(strlen(tag) + strlen(".camera") + 1);
969 	if (! buf)
970 	    goto error;
971 
972 	sprintf(buf, "%s.camera", tag);
973 
974 	*camera = (Camera)DXGetCacheEntry(buf, 0, 0);
975 	DXFree((Pointer)buf);
976     }
977 
978     return OK;
979 
980 error:
981     return ERROR;
982 }
983 
984 static Error
PickObject(Object object,PickBuf * pick,int poke,Point2D * xy,Matrix * stack,int persp,float nearPlane,float sens,float min,float max,Camera camera)985 PickObject(Object object, PickBuf *pick, int poke, Point2D *xy,
986 	Matrix *stack, int persp, float nearPlane, float sens,
987 	float min, float max, Camera camera)
988 {
989     Object attr = DXGetAttribute(object, "pickable");
990     int    pickable;
991 
992     if (attr)
993     {
994 	if (! DXExtractInteger(attr, &pickable))
995 	{
996 	    DXSetError(ERROR_DATA_INVALID,
997 		"pickable attribute must be an integer");
998 	    return ERROR;
999 	}
1000 
1001 	if (! pickable)
1002 	    return OK;
1003     }
1004 
1005     switch(DXGetObjectClass(object))
1006     {
1007 	case CLASS_XFORM:
1008 	{
1009 	    Object child;
1010 	    Matrix matrix, product;
1011 
1012 	    if (! DXGetXformInfo((Xform)object, &child, &matrix))
1013 		goto error;
1014 
1015 	    product = DXConcatenate(matrix, *stack);
1016 
1017 	    pick->pickPath[pick->depth++] = 0;
1018 
1019 	    if (! PickObject(child, pick, poke, xy,
1020 			&product, persp, nearPlane,
1021 			sens, min, max, camera))
1022 		return ERROR;
1023 
1024 	    pick->depth --;
1025 	    return OK;
1026 	}
1027 
1028 	case CLASS_CLIPPED:
1029 	{
1030 	    Object child;
1031 	    Object clipper;
1032 	    PickBuf clipPick;
1033 
1034 	    clipPick.picks     = NULL;
1035 	    clipPick.paths     = NULL;
1036 	    clipPick.depth     = pick->depth;
1037 
1038 	    if (! DXGetClippedInfo((Clipped)object, &child, &clipper))
1039 		goto error;
1040 
1041 	    pick->pickPath[pick->depth++] = 0;
1042 
1043 	    if (! PickObject(clipper, &clipPick, poke, xy, stack,
1044 				persp, nearPlane, sens, min, max, camera))
1045 		return ERROR;
1046 
1047 	    if (clipPick.picks)
1048 	    {
1049 		int nPicks;
1050 		Pick *picks = (Pick *)DXGetArrayData(clipPick.picks);
1051 
1052 		DXGetArrayInfo(clipPick.picks, &nPicks, NULL, NULL, NULL, NULL);
1053 
1054 		if (nPicks == 1)
1055 		{
1056 		    max = picks->xyz.z;
1057 		}
1058 		else
1059 		{
1060 		    picksort(picks, nPicks);
1061 		    max = picks[0].xyz.z;
1062 		    min = picks[nPicks-1].xyz.z;
1063 		}
1064 
1065 		DXDelete((Object)clipPick.picks);
1066 		DXDelete((Object)clipPick.paths);
1067 	    }
1068 
1069 	    if (! PickObject(child, pick, poke, xy, stack,
1070 				persp, nearPlane, sens, min, max, camera))
1071 		return ERROR;
1072 
1073 	    pick->depth --;
1074 
1075 	    return OK;
1076 	}
1077 
1078 	case CLASS_GROUP:
1079 	{
1080 	    Object child;
1081 	    int i;
1082 
1083 	    pick->depth ++;
1084 
1085 	    for (i = 0;;i++)
1086 	    {
1087 		child = DXGetEnumeratedMember((Group)object, i, NULL);
1088 		if (! child)
1089 		    break;
1090 
1091 		pick->pickPath[pick->depth-1] = i;
1092 
1093 		if (! PickObject(child, pick, poke, xy,
1094 			stack, persp, nearPlane, sens, min, max, camera))
1095 		    return ERROR;
1096 	    }
1097 
1098 	    pick->depth --;
1099 
1100 	    return OK;
1101 	}
1102 
1103 	case CLASS_FIELD:
1104 	{
1105 	    return PickField((Field)object, pick, poke, *xy,
1106 		    stack, persp, nearPlane, sens, min, max);
1107 	}
1108 
1109 	case CLASS_SCREEN:
1110 	{
1111 	    Object child;
1112 	    int fixed, z, width, height;
1113 	    Matrix new = *stack;
1114 
1115 	    if (! DXGetScreenInfo((Screen)object, &child, &fixed, &z))
1116 		goto error;
1117 
1118 	    DXGetCameraResolution(camera, &width, &height);
1119 
1120 	    if (fixed == SCREEN_VIEWPORT)
1121 	    {
1122 		new.b[0] = new.b[0]*width - width/2;
1123 		new.b[1] = new.b[1]*height - height/2;
1124 	    }
1125 	    else if (fixed == SCREEN_PIXEL)
1126 	    {
1127 		new.b[0] -= width/2;
1128 		new.b[1] -= height/2;
1129 	    }
1130 	    else if (fixed == SCREEN_WORLD)
1131 	    {
1132 		Matrix cm;
1133 
1134 		cm = DXGetCameraMatrix(camera);
1135 		new = DXConcatenate(*stack, cm);
1136 
1137 		if (DXGetPerspective(camera, NULL, NULL))
1138 		{
1139 		    new.b[0] = - new.b[0] / new.b[2];
1140 		    new.b[1] = - new.b[1] / new.b[2];
1141 		}
1142 	    }
1143 	    else if (fixed == SCREEN_STATIONARY)
1144 		new.b[0] = new.b[1] = new.b[2] = 0.0;
1145 
1146 	    new.A[0][0] = 1.0; new.A[0][1] = 0.0; new.A[0][2] = 0.0;
1147 	    new.A[1][0] = 0.0; new.A[1][1] = 1.0; new.A[1][2] = 0.0;
1148 	    new.A[2][0] = 0.0; new.A[2][1] = 0.0; new.A[2][2] = 1.0;
1149 
1150 	    pick->pickPath[pick->depth++] = 0;
1151 
1152 	    if (! PickObject(child, pick, poke, xy,
1153 			&new, persp, nearPlane, sens, min, max, camera))
1154 		return ERROR;
1155 
1156 	    pick->depth --;
1157 	    return OK;
1158 	}
1159 
1160 	default:
1161 	{
1162 	    return OK;
1163 	}
1164     }
1165 
1166 error:
1167     return ERROR;
1168 }
1169 
1170 static Error
PickField(Field f,PickBuf * p,int poke,Point2D xy,Matrix * s,int persp,float nearPlane,float sens,float min,float max)1171 PickField(Field f, PickBuf *p, int poke, Point2D xy, Matrix *s,
1172 		    int persp, float nearPlane, float sens, float min, float max)
1173 {
1174     Object c;
1175     char *str;
1176     int  pb;
1177 
1178     if (DXEmptyField(f))
1179 	return OK;
1180 
1181     pb = PickBox(f, xy, s, persp, nearPlane);
1182 
1183     if (pb == BOX_ERROR)
1184 	return ERROR;
1185     else if (pb == BOX_HIT)
1186     {
1187 
1188 	if (NULL != (c = DXGetComponentValue(f, "connections")))
1189 	{
1190 	    if (! DXGetStringAttribute(c, "element type", &str))
1191 	    {
1192 		DXSetError(ERROR_MISSING_DATA, "element type attribute");
1193 		return ERROR;
1194 	    }
1195 
1196 	    if (! strcmp(str, "triangles"))
1197 		return PickTriangles(f, p, poke, xy, s,
1198 				persp, nearPlane, sens, min, max);
1199 	    else if (! strcmp(str, "quads"))
1200 		return PickQuads(f, p, poke, xy, s,
1201 				persp, nearPlane, sens, min, max);
1202 	    else if (! strcmp(str, "lines"))
1203 		return PickLines(f, p, poke, xy, s,
1204 				persp, nearPlane, sens, min, max);
1205 	    else
1206 	    {
1207 		DXSetError(ERROR_DATA_INVALID,
1208 			"unable to pick when element type is %s", str);
1209 		return ERROR;
1210 	    }
1211 	}
1212 	else if (DXGetComponentValue(f, "faces"))
1213 	{
1214 	    return PickFLE(f, p, poke, xy, s, persp, nearPlane, sens, min, max);
1215 	}
1216 	else if (DXGetComponentValue(f, "polylines"))
1217 	{
1218 	    return PickPolylines(f, p, poke, xy, s,
1219 				persp, nearPlane, sens, min, max);
1220 	}
1221 	else
1222 	    return PickPoints(f, p, poke, xy, s, persp, nearPlane, sens, min, max);
1223     }
1224 
1225     return OK;
1226 }
1227 
1228 static Point *
TransformArray(Array src,Matrix * m)1229 TransformArray(Array src, Matrix *m)
1230 {
1231     int		 i;
1232     Point        *dst = NULL;
1233     ArrayHandle  handle = NULL;
1234     int          nPts, nDim;
1235     Pointer      buf = NULL;
1236 
1237     DXGetArrayInfo(src, &nPts, NULL, NULL, NULL, &nDim);
1238 
1239     dst = DXAllocate(nPts*sizeof(Point));
1240     if (! dst)
1241 	goto error;
1242 
1243     handle = DXCreateArrayHandle(src);
1244     if (! handle)
1245 	goto error;
1246 
1247     buf = DXAllocate(DXGetItemSize(src));
1248     if (! buf)
1249 	goto error;
1250 
1251     for (i = 0; i < nPts; i++)
1252     {
1253 	Point d;
1254 	float *s;
1255 
1256 	s = (float *)DXGetArrayEntry(handle, i, buf);
1257 
1258 	if (nDim == 1)
1259 	    d.x = s[0], d.y = d.z = 0.0;
1260 	if (nDim == 2)
1261 	    d.x = s[0], d.y = s[1], d.z = 0.0;
1262 	else
1263 	    d.x = s[0], d.y = s[1], d.z = s[2];
1264 
1265 	dst[i] = DXApply(d, *m);
1266     }
1267 
1268     DXFreeArrayHandle(handle);
1269     DXFree(buf);
1270 
1271     return dst;
1272 
1273 error:
1274     DXFreeArrayHandle(handle);
1275     DXFree(buf);
1276     DXFree((Pointer)dst);
1277 
1278     return NULL;
1279 }
1280 
1281 
1282 static int box_triangles[] =
1283 {
1284     0, 1, 3,
1285     0, 3, 2,
1286     1, 5, 7,
1287     1, 7, 3,
1288     5, 4, 6,
1289     5, 6, 7,
1290     4, 0, 2,
1291     4, 2, 6,
1292     3, 7, 6,
1293     3, 6, 2,
1294     0, 1, 5,
1295     0, 5, 4
1296 };
1297 
1298 static int
PickBox(Field field,Point2D xy,Matrix * s,int persp,float nearPlane)1299 PickBox(Field field, Point2D xy, Matrix *s, int persp, float nearPlane)
1300 {
1301     Point points[8];
1302     int   i, f;
1303 
1304     if (! DXBoundingBox((Object)field, points))
1305 	return ERROR;
1306 
1307     if (s)
1308 	for (i = 0; i < 8; i++)
1309 	    points[i] = DXApply(points[i], *s);
1310 
1311     if (persp)
1312     {
1313 	if (!Elt2D_Perspective(NULL, 0, xy, points, box_triangles,
1314 		8, 12, 3, nearPlane, &f, 0.0, -DXD_MAX_FLOAT, DXD_MAX_FLOAT))
1315 	    goto error;
1316     }
1317     else
1318     {
1319 	if (!Elt2D_NoPerspective(NULL, 0, xy, points, box_triangles,
1320 		8, 12, 3, nearPlane, &f, 0.0, -DXD_MAX_FLOAT, DXD_MAX_FLOAT))
1321 	    goto error;
1322     }
1323 
1324     return f;
1325 
1326 error:
1327     return -1;
1328 }
1329 
1330 static int
TriangleHit(Point ** tri,Point2D * xy,Point * xyz,int persp,float * wts)1331 TriangleHit(Point **tri, Point2D *xy, Point *xyz, int persp, float *wts)
1332 {
1333     int i, side = 0;
1334     float a[3];
1335 
1336     for (i = 0; i < 3; i++)
1337     {
1338 	int j = (i == 2) ? 0 : i+1;
1339 	float x0 = tri[j]->x - tri[i]->x;
1340 	float y0 = tri[j]->y - tri[i]->y;
1341 	float x1 = xy->x - tri[i]->x;
1342 	float y1 = xy->y - tri[i]->y;
1343 	float cross = x0*y1 - x1*y0;
1344 
1345 	if ((cross < 0.0 && side == 1) || (cross > 0 && side == -1))
1346 	    return 0;
1347 
1348 	if (side == 0) {
1349 	    if (cross < 0)
1350 		side = -1;
1351 	    else if (cross > 0)
1352 		side =  1;
1353 	}
1354 
1355 	a[i] = cross;
1356     }
1357 
1358     if (side == 0)
1359 	return 0;
1360 
1361     if (xyz || wts)
1362     {
1363 	float A = 1.0 / (a[0] + a[1] + a[2]);
1364 	float a0 = A*a[0];
1365 	float a1 = A*a[1];
1366 	float a2 = A*a[2];
1367 
1368 	if (xyz)
1369 	{
1370 	    xyz->x = xy->x;
1371 	    xyz->y = xy->y;
1372 	    xyz->z = a0*tri[2]->z + a1*tri[0]->z + a2*tri[1]->z;
1373 
1374 	    if (persp)
1375 	    {
1376 		float z = xyz->z = (ZOFFSET * xyz->z) / (1.0 + xyz->z);
1377 		float depth = ZOFFSET - z;
1378 		xyz->x *= depth;
1379 		xyz->y *= depth;
1380 	    }
1381 	}
1382 
1383 	if (wts)
1384 	{
1385 	    wts[0] = a1;
1386 	    wts[1] = a2;
1387 	    wts[2] = a0;
1388 	}
1389     }
1390 
1391     return 1;
1392 }
1393 
1394 static Error
PickTriangles(Field f,PickBuf * p,int poke,Point2D xy,Matrix * m,int perspective,float nearPlane,float sens,float min,float max)1395 PickTriangles(Field f, PickBuf *p, int poke, Point2D xy,
1396     Matrix *m, int perspective, float nearPlane, float sens, float min, float max)
1397 {
1398     Array   pA;
1399     Array   tA;
1400     Point   *xPoints = NULL;
1401     int     nPts, nTris;
1402     int	    *tris;
1403 
1404     pA = (Array)DXGetComponentValue(f, "positions");
1405     tA = (Array)DXGetComponentValue(f, "connections");
1406     if (! tA || ! pA)
1407     {
1408 	DXSetError(ERROR_MISSING_DATA, "positions or connections");
1409 	goto error;
1410     }
1411 
1412     DXGetArrayInfo(pA, &nPts,  NULL, NULL, NULL, NULL);
1413     DXGetArrayInfo(tA, &nTris, NULL, NULL, NULL, NULL);
1414 
1415     /*
1416      * Transform points
1417      */
1418     xPoints = TransformArray(pA, m);
1419     if (! xPoints)
1420 	return ERROR;
1421 
1422     tris = (int *)DXGetArrayData(tA);
1423 
1424     if (perspective)
1425     {
1426 	if (! Elt2D_Perspective(p, poke, xy, xPoints, tris,
1427 			    nPts, nTris, 3, nearPlane, NULL, sens, min, max))
1428 	    goto error;
1429     }
1430     else
1431     {
1432 	if (! Elt2D_NoPerspective(p, poke, xy, xPoints, tris,
1433 			    nPts, nTris, 3, nearPlane, NULL, sens, min, max))
1434 	    goto error;
1435     }
1436 
1437     DXFree((Pointer)xPoints);
1438     return OK;
1439 
1440 error:
1441     DXFree((Pointer)xPoints);
1442     return ERROR;
1443 }
1444 
1445 static Error
PickQuads(Field f,PickBuf * p,int poke,Point2D xy,Matrix * m,int perspective,float nearPlane,float sens,float min,float max)1446 PickQuads(Field f, PickBuf *p, int poke, Point2D xy,
1447     Matrix *m, int perspective, float nearPlane, float sens, float min, float max)
1448 {
1449     Array   pA;
1450     Array   qA;
1451     Point   *xPoints = NULL;
1452     int     nPts, nQuads;
1453     int	    *quads;
1454 
1455     pA = (Array)DXGetComponentValue(f, "positions");
1456     qA = (Array)DXGetComponentValue(f, "connections");
1457     if (! qA || ! pA)
1458     {
1459 	DXSetError(ERROR_MISSING_DATA, "positions or connections");
1460 	goto error;
1461     }
1462 
1463     DXGetArrayInfo(pA, &nPts,  NULL, NULL, NULL, NULL);
1464     DXGetArrayInfo(qA, &nQuads, NULL, NULL, NULL, NULL);
1465 
1466     /*
1467      * Transform points
1468      */
1469     xPoints = TransformArray(pA, m);
1470     if (! xPoints)
1471 	return ERROR;
1472 
1473     quads = (int *)DXGetArrayData(qA);
1474 
1475     if (perspective)
1476     {
1477 	if (! Elt2D_Perspective(p, poke, xy, xPoints, quads,
1478 			    nPts, nQuads, 4, nearPlane, NULL, sens, min, max))
1479 	    goto error;
1480     }
1481     else
1482     {
1483 	if (! Elt2D_NoPerspective(p, poke, xy, xPoints, quads,
1484 			    nPts, nQuads, 4, nearPlane, NULL, sens, min, max))
1485 	    goto error;
1486     }
1487 
1488     DXFree((Pointer)xPoints);
1489     return OK;
1490 
1491 error:
1492     DXFree((Pointer)xPoints);
1493     return ERROR;
1494 }
1495 
1496 static int triTab[]  = {0, 1, 2};
1497 static int quadTab[] = {0, 1, 3, 2};
1498 
1499 static Error
Elt2D_Perspective(PickBuf * p,int poke,Point2D xy,Point * xPoints,int * elts,int nPts,int nElts,int vPerE,float nearPlane,int * found,float sens,float min,float max)1500 Elt2D_Perspective(PickBuf *p, int poke, Point2D xy, Point *xPoints, int *elts,
1501 	    int nPts, int nElts, int vPerE, float nearPlane, int *found, float sens,
1502 	    float min, float max)
1503 {
1504     int i, j;
1505     float dNear;
1506     Point xyz;
1507     int *table = (vPerE == 3) ? triTab : (vPerE == 4) ? quadTab : NULL;
1508     Point *sPoints = (Point *)DXAllocate(nPts*sizeof(Point));
1509     float *weights = (float *)DXAllocate(vPerE*sizeof(float));
1510     if (! sPoints || ! weights)
1511 	goto error;
1512 
1513     nearPlane += ZOFFSET;
1514     dNear = -1.0 / nearPlane;
1515 
1516     /*
1517      * perform perspective on all unclipped transformed points
1518      */
1519     for (i = 0; i < nPts; i++)
1520     {
1521 	float z = xPoints[i].z;
1522 	if (z < nearPlane)
1523 	{
1524 	    float invDepth = 1.0 / (-z + ZOFFSET);
1525 	    sPoints[i].x = xPoints[i].x * invDepth;
1526 	    sPoints[i].y = xPoints[i].y * invDepth;
1527 	    sPoints[i].z = xPoints[i].z * invDepth;
1528 	}
1529     }
1530 
1531     if (found)
1532 	*found = 0;
1533 
1534     for (i = 0; i < nElts; i++, elts += vPerE)
1535     {
1536 	int     last, next, ngood, nclip;
1537 	Point   *sclipped[12];
1538 	Point   sclippt[12];
1539 	int     orig[12];
1540 	int     lastClipped, nextClipped;
1541 
1542 	if (table)
1543 	    last = elts[table[vPerE-1]];
1544 	else
1545 	    last = elts[vPerE-1];
1546 
1547 	lastClipped = xPoints[last].z >= nearPlane;
1548 
1549 	ngood = 0;
1550 	nclip = 0;
1551 	for (j = 0; j < vPerE; j++)
1552 	{
1553 	    if (table)
1554 		next = elts[table[j]];
1555 	    else
1556 		next = elts[j];
1557 
1558 	    nextClipped = xPoints[next].z >= nearPlane;
1559 
1560 	    if (! lastClipped)
1561 	    {
1562 		sclipped[ngood] = sPoints + last;
1563 		orig[ngood] = last;
1564 		ngood ++;
1565 	    }
1566 
1567 	    if (lastClipped != nextClipped)
1568 	    {
1569 		Point   *lxp = xPoints + last;
1570 		Point   *nxp = xPoints + next;
1571 		float   x, y, d = (nearPlane - lxp->z) / (nxp->z - lxp->z);
1572 
1573 		x = lxp->x + d*(nxp->x - lxp->x);
1574 		y = lxp->y + d*(nxp->y - lxp->y);
1575 		/* z = nearPlane; */
1576 
1577 		sclippt[nclip].x = dNear * x;
1578 		sclippt[nclip].y = dNear * y;
1579 		sclippt[nclip].y = dNear * y;
1580 
1581 		sclipped[ngood] = sclippt + nclip;
1582 		orig[ngood] = -1;
1583 
1584 		nclip++;
1585 		ngood++;
1586 	    }
1587 
1588 	    last = next;
1589 	    lastClipped = nextClipped;
1590 	}
1591 
1592 	/*
1593 	 * If a fragment survived clipping...
1594 	 */
1595 
1596 	for (j = 2; j < ngood; j++)
1597 	{
1598 	    Point   *sc[3];
1599 	    int     si[3];
1600 
1601 	    sc[0] = sclipped[si[0] =   0];
1602 	    sc[1] = sclipped[si[1] = j-1];
1603 	    sc[2] = sclipped[si[2] =   j];
1604 
1605 	    if (TriangleHit(sc, &xy, &xyz, 1, NULL))
1606 	    {
1607 		int ni=0, k;
1608 		float nearPlane = DXD_MAX_FLOAT;
1609 
1610 		if (found)
1611 		    *found = 1;
1612 
1613 		if (! p)
1614 		    goto done;
1615 
1616 		if (xyz.z > min && xyz.z < max)
1617 		{
1618 		    Point  *tpts[3];
1619 
1620 		    for (k = 0; k < 3; k++)
1621 			if (orig[si[k]] != -1)
1622 			{
1623 			    float dx = sPoints[orig[si[k]]].x - xy.x;
1624 			    float dy = sPoints[orig[si[k]]].y - xy.y;
1625 			    float n = sqrt(dx*dx + dy*dy);
1626 			    if (n < nearPlane)
1627 			    {
1628 				ni = orig[si[k]];
1629 				nearPlane = n;
1630 			    }
1631 			}
1632 
1633 		    /*
1634 		     * find an *original* triangle containing the pick
1635 		     * point.
1636 		     */
1637 
1638 		    if (table)
1639 		    {
1640 			tpts[0] = sPoints + elts[table[0]];
1641 			tpts[2] = sPoints + elts[table[1]];
1642 		    }
1643 		    else
1644 		    {
1645 			tpts[0] = sPoints + elts[0];
1646 			tpts[2] = sPoints + elts[1];
1647 		    }
1648 
1649 		    for (k = 2; k < vPerE; k++)
1650 		    {
1651 			float wts[3];
1652 
1653 			tpts[1] = tpts[2];
1654 			if (table)
1655 			    tpts[2] = sPoints + elts[table[k]];
1656 			else
1657 			    tpts[2] = sPoints + elts[k];
1658 
1659 			if (TriangleHit(tpts, &xy, NULL, 1, wts))
1660 			{
1661 			    int l;
1662 
1663 			    for (l = 0; l < vPerE; l++)
1664 				weights[l] = 0;
1665 
1666 			    if (table)
1667 			    {
1668 				weights[table[0]]   = wts[0];
1669 				weights[table[k-1]] = wts[1];
1670 				weights[table[k]]   = wts[2];
1671 			    }
1672 			    else
1673 			    {
1674 				weights[0]   = wts[0];
1675 				weights[k-1] = wts[1];
1676 				weights[k]   = wts[2];
1677 			    }
1678 
1679 			    break;
1680 			}
1681 		    }
1682 
1683 		    if (k == vPerE)
1684 		    {
1685 			DXSetError(ERROR_INTERNAL,
1686 				"found in clipped but not in original");
1687 			goto error;
1688 		    }
1689 
1690 		    p->pickPath[p->depth+0] = i;
1691 		    p->pickPath[p->depth+1] = ni;
1692 		    if (! AddPick(p, poke, xyz, vPerE, -1, weights))
1693 			goto error;
1694 		}
1695 
1696 		break;
1697 	    }
1698 	}
1699     }
1700 
1701 done:
1702     DXFree((Pointer)sPoints);
1703     DXFree((Pointer)weights);
1704     return OK;
1705 
1706 error:
1707     DXFree((Pointer)sPoints);
1708     DXFree((Pointer)weights);
1709     return ERROR;
1710 }
1711 
1712 static Error
Elt2D_NoPerspective(PickBuf * p,int poke,Point2D xy,Point * xPoints,int * elts,int nPts,int nElts,int vPerE,float nearPlane,int * found,float sens,float min,float max)1713 Elt2D_NoPerspective(PickBuf *p, int poke, Point2D xy, Point *xPoints, int *elts,
1714 	    int nPts, int nElts, int vPerE, float nearPlane, int *found, float sens,
1715 	    float min, float max)
1716 {
1717     Point xyz;
1718     int i, j, k, ni=0;
1719     int *table = (vPerE == 3) ? triTab : (vPerE == 4) ? quadTab : NULL;
1720     float nearest;
1721     int indices[3];
1722     Point *pts[3];
1723     float wts[3], *weights = (float *)DXAllocate(vPerE*sizeof(float));
1724 
1725     if (! weights) goto error;
1726 
1727     if (found)
1728 	*found = 0;
1729 
1730     for (i = 0; i < nElts; i++, elts += vPerE)
1731     {
1732 	if (table)
1733 	{
1734 	    indices[0] = elts[table[0]];
1735 	    indices[2] = elts[table[1]];
1736 	}
1737 	else
1738 	{
1739 	    indices[0] = elts[0];
1740 	    indices[2] = elts[1];
1741 	}
1742 
1743 	pts[0] = xPoints + indices[0];
1744 	pts[2] = xPoints + indices[2];
1745 
1746 	for (j = 2; j < vPerE; j++)
1747 	{
1748 	    indices[1] = indices[2];
1749 	    pts[1]     = pts[2];
1750 
1751 	    if (table)
1752 		indices[2] = elts[table[j]];
1753 	    else
1754 		indices[2] = elts[j];
1755 
1756 	    pts[2] = xPoints + indices[2];
1757 
1758 	    if (TriangleHit(pts, &xy, &xyz, 0, wts))
1759 	    {
1760 		if (!p)
1761 		{
1762 		    if (found)
1763 			*found = 1;
1764 		    goto done;
1765 		}
1766 
1767 		if (xyz.z >= nearPlane)
1768 		    continue;
1769 
1770 		if (found)
1771 		    *found = 1;
1772 
1773 		for (k = 0; k < vPerE; k++)
1774 		    weights[k] = 0;
1775 
1776 		if (table)
1777 		{
1778 		    weights[table[0]]   = wts[0];
1779 		    weights[table[j-1]] = wts[1];
1780 		    weights[table[j]]   = wts[2];
1781 		}
1782 		else
1783 		{
1784 		    weights[0]   = wts[0];
1785 		    weights[j-1] = wts[1];
1786 		    weights[j]   = wts[2];
1787 		}
1788 
1789 		for (k = 0, nearest = DXD_MAX_FLOAT; k < 3; k++)
1790 		{
1791 		    if (pts[k]->z < nearPlane)
1792 		    {
1793 			float dx = pts[k]->x - xy.x;
1794 			float dy = pts[k]->y - xy.y;
1795 			float n = sqrt(dx*dx + dy*dy);
1796 
1797 			if (n < nearest)
1798 			{
1799 			    ni = indices[k];
1800 			    nearest = n;
1801 			}
1802 		    }
1803 		}
1804 
1805 		p->pickPath[p->depth+0] =  i;
1806 		p->pickPath[p->depth+1] = ni;
1807 		if (! AddPick(p, poke, xyz, vPerE, -1, weights))
1808 		    goto error;
1809 
1810 		break;
1811 	    }
1812 	}
1813     }
1814 
1815 done:
1816     DXFree((Pointer)weights);
1817     return OK;
1818 
1819 error:
1820     DXFree((Pointer)weights);
1821     return ERROR;
1822 }
1823 
1824 static Error
PickLines(Field f,PickBuf * p,int poke,Point2D xy,Matrix * m,int perspective,float nearPlane,float sens,float min,float max)1825 PickLines(Field f, PickBuf *p, int poke, Point2D xy, Matrix *m,
1826 	int perspective, float nearPlane, float sens, float min, float max)
1827 {
1828     Array   pA;
1829     Array   lA;
1830     Point   *xPoints = NULL;
1831     int     nPts, nLines;
1832     int	    *lines;
1833 
1834     pA = (Array)DXGetComponentValue(f, "positions");
1835     lA = (Array)DXGetComponentValue(f, "connections");
1836     if (! lA || ! pA)
1837     {
1838 	DXSetError(ERROR_MISSING_DATA, "positions or connections");
1839 	goto error;
1840     }
1841 
1842     DXGetArrayInfo(pA, &nPts,  NULL, NULL, NULL, NULL);
1843     DXGetArrayInfo(lA, &nLines, NULL, NULL, NULL, NULL);
1844 
1845     /*
1846      * Transform points
1847      */
1848     xPoints = TransformArray(pA, m);
1849     if (! xPoints)
1850 	return ERROR;
1851 
1852     lines = (int *)DXGetArrayData(lA);
1853 
1854     if (perspective)
1855     {
1856 	if (! Elt1D_Perspective(p, poke, xy, xPoints, lines, nPts,
1857 			    nLines, nearPlane, sens, min, max))
1858 	    goto error;
1859     }
1860     else
1861     {
1862 	if (! Elt1D_NoPerspective(p, poke, xy, xPoints, lines, nPts,
1863 			    nLines, nearPlane, sens, min, max))
1864 	    goto error;
1865     }
1866 
1867     DXFree((Pointer)xPoints);
1868     return OK;
1869 
1870 error:
1871     DXFree((Pointer)xPoints);
1872     return ERROR;
1873 }
1874 
1875 
1876 static Error
Elt1D_Perspective(PickBuf * pick,int poke,Point2D xy,Point * xPoints,int * elts,int nPts,int nElts,float nearPlane,float sens,float min,float max)1877 Elt1D_Perspective(PickBuf *pick, int poke, Point2D xy, Point *xPoints,
1878     int *elts, int nPts, int nElts, float nearPlane, float sens,
1879     float min, float max)
1880 {
1881     int i;
1882     Point *sPoints;
1883 
1884     sPoints = (Point *)DXAllocate(nPts*sizeof(Point));
1885     if (! sPoints)
1886 	goto error;
1887 
1888     nearPlane += ZOFFSET;
1889 
1890     /*
1891      * perform perspective on all unclipped transformed points
1892      */
1893     for (i = 0; i < nPts; i++)
1894     {
1895 	float z = xPoints[i].z;
1896 	if (z < nearPlane)
1897 	{
1898 	    float invDepth = 1.0 / (-z + ZOFFSET);
1899 	    sPoints[i].x = xPoints[i].x * invDepth;
1900 	    sPoints[i].y = xPoints[i].y * invDepth;
1901 	    sPoints[i].z = xPoints[i].z * invDepth;
1902 	}
1903     }
1904 
1905     for (i = 0; i < nElts; i++, elts += 2)
1906     {
1907 	int     p, q;
1908 	Point   *px, *qx;
1909 	Point   *ps, *qs, *sclipped[2];
1910 	Point   sclippt;
1911 	float   x, y, z, a, b, c, D, dx, dy, depth;
1912 	Point   xyz;
1913 	int	orig[2];
1914 
1915 	p = elts[0];
1916 	q = elts[1];
1917 
1918 	px = xPoints + p;
1919 	qx = xPoints + q;
1920 
1921 	ps = sPoints + p;
1922 	qs = sPoints + q;
1923 
1924 	if (px->z > nearPlane && qx->z > nearPlane)
1925 	    continue;
1926 
1927 	if (px->z <= nearPlane && qx->z <= nearPlane)
1928 	{
1929 	    /*xclipped[0] = px;*/
1930 	    /*xclipped[1] = qx;*/
1931 
1932 	    sclipped[0] = ps;
1933 	    sclipped[1] = qs;
1934 
1935 	    orig[0] = p;
1936 	    orig[1] = q;
1937 	}
1938 	else if (px->z > nearPlane && qx->z <= nearPlane)
1939 	{
1940 	    float d = (nearPlane - qx->z) / (px->z - qx->z);
1941 
1942 	    /*xclippt.x = qx->x + d*(px->x - qx->x);*/
1943 	    /*xclippt.y = qx->y + d*(px->y - qx->y);*/
1944 	    /*xclippt.z = nearPlane;*/
1945 	    /*xclipped[0] = &xclippt;*/
1946 	    /*xclipped[1] = qx;*/
1947 
1948 	    sclippt.x = qs->x + d*(ps->x - qs->x);
1949 	    sclippt.y = qs->y + d*(ps->y - qs->y);
1950 	    sclippt.z = nearPlane;
1951 	    sclipped[0] = &sclippt;
1952 	    sclipped[1] = qs;
1953 
1954 	    orig[0] = -1;
1955 	    orig[1] = q;
1956 	}
1957 	else
1958 	{
1959 	    float d = (nearPlane - qx->z) / (px->z - qx->z);
1960 
1961 	    /*xclippt.x = qx->x + d*(px->x - qx->x);*/
1962 	    /*xclippt.y = qx->y + d*(px->y - qx->y);*/
1963 	    /*xclippt.z = nearPlane;*/
1964 	    /*xclipped[0] = &xclippt;*/
1965 	    /*xclipped[1] = px;*/
1966 
1967 	    sclippt.x = qs->x + d*(ps->x - qs->x);
1968 	    sclippt.y = qs->y + d*(ps->y - qs->y);
1969 	    sclippt.z = nearPlane;
1970 	    sclipped[0] = &sclippt;
1971 	    sclipped[1] = ps;
1972 
1973 	    orig[0] = -1;
1974 	    orig[1] = p;
1975 	}
1976 
1977 	dx = sclipped[1]->x - sclipped[0]->x;
1978 	dy = sclipped[1]->y - sclipped[0]->y;
1979 
1980 	if (dx == 0 && dy == 0)
1981 	    continue;
1982 
1983 	D = 1.0 / sqrt(dx*dx + dy*dy);
1984 
1985 	a = dy * D;
1986 	b = -dx * D;
1987 	c = -(a*sclipped[0]->x + b*sclipped[0]->y);
1988 
1989 	D = a*xy.x + b*xy.y + c;
1990 
1991 	if (-sens < D && D < sens)
1992 	{
1993 	    /*
1994 	     * step along line normal by -D to get closest point on line
1995 	     */
1996 	    x = xy.x - D*a;
1997 	    y = xy.y - D*b;
1998 
1999 	    if ((((sclipped[0]->x-sens) <= x && x <= (sclipped[1]->x+sens)) ||
2000 		 ((sclipped[0]->x+sens) >= x && x >= (sclipped[1]->x-sens))) &&
2001 		(((sclipped[0]->y-sens) <= y && y <= (sclipped[1]->y+sens)) ||
2002 		 ((sclipped[0]->y+sens) >= y && y >= (sclipped[1]->y-sens))))
2003 	    {
2004 		float nearPlane = DXD_MAX_FLOAT;
2005 		int   k, ni=0;
2006 
2007 		if (fabs(dx) > fabs(dy))
2008 		    D = (x - sclipped[0]->x)/(sclipped[1]->x - sclipped[0]->x);
2009 		else
2010 		    D = (y - sclipped[0]->y)/(sclipped[1]->y - sclipped[0]->y);
2011 
2012 		/*
2013 		 * interpolate on screen
2014 		 */
2015 		x = sclipped[0]->x + D*(sclipped[1]->x - sclipped[0]->x);
2016 		y = sclipped[0]->y + D*(sclipped[1]->y - sclipped[0]->y);
2017 		z = sclipped[0]->z + D*(sclipped[1]->z - sclipped[0]->z);
2018 
2019 		/*
2020 		 * Perspective transformation is zp = z / depth, where
2021 		 * depth = -z + d.  To regain original z, z = d(zp) / 1 + zp.
2022 		 */
2023 		xyz.z = (ZOFFSET * z) / (1.0 + z);
2024 
2025 		if (xyz.z > min && xyz.z < max)
2026 		{
2027 		    float weights[2];
2028 
2029 		    /*
2030 		     * now undo perspective on xy by multiplying by depth
2031 		     */
2032 		    depth = -xyz.z + ZOFFSET;
2033 		    xyz.x = x * depth;
2034 		    xyz.y = y * depth;
2035 
2036 		    for (k = 0; k < 2; k++)
2037 			if (orig[k] != -1)
2038 			{
2039 			    float dx = sPoints[orig[k]].x - xy.x;
2040 			    float dy = sPoints[orig[k]].y - xy.y;
2041 			    float n = sqrt(dx*dx + dy*dy);
2042 			    if (n < nearPlane)
2043 			    {
2044 				ni = orig[k];
2045 				nearPlane = n;
2046 			    }
2047 			}
2048 
2049 		    if (fabs(dx) > fabs(dy))
2050 			D = (x - sPoints[p].x)/(sPoints[q].x - sPoints[p].x);
2051 		    else
2052 			D = (y - sPoints[p].y)/(sPoints[q].y - sPoints[p].y);
2053 
2054 		    weights[0] = 1.0 - D;
2055 		    weights[1] = D;
2056 
2057 		    pick->pickPath[pick->depth+0] = i;
2058 		    pick->pickPath[pick->depth+1] = ni;
2059 		    if (! AddPick(pick, poke, xyz, 2, -1, weights))
2060 			goto error;
2061 		}
2062 	    }
2063 
2064 	}
2065     }
2066 
2067     DXFree((Pointer)sPoints);
2068     return OK;
2069 
2070 error:
2071     DXFree((Pointer)sPoints);
2072     return ERROR;
2073 }
2074 
2075 static Error
Elt1D_NoPerspective(PickBuf * pick,int poke,Point2D xy,Point * xPoints,int * elts,int nPts,int nElts,float nearPlane,float sens,float min,float max)2076 Elt1D_NoPerspective(PickBuf *pick, int poke, Point2D xy, Point *xPoints,
2077 		    int *elts, int nPts, int nElts, float nearPlane, float sens,
2078 		    float min, float max)
2079 {
2080     int i;
2081     int orig[2];
2082 
2083     nearPlane += ZOFFSET;
2084 
2085     for (i = 0; i < nElts; i++, elts += 2)
2086     {
2087 	int     p, q;
2088 	Point   *px, *qx, *xclipped[2];
2089 	Point   xclippt;
2090 	float   x, y, a, b, c, D, dx, dy;
2091 	Point   xyz;
2092 
2093 	p = elts[0];
2094 	q = elts[1];
2095 
2096 	px = xPoints + p;
2097 	qx = xPoints + q;
2098 
2099 	if (px->z > nearPlane && qx->z > nearPlane)
2100 	    continue;
2101 
2102 	if (px->z <= nearPlane && qx->z <= nearPlane)
2103 	{
2104 	    xclipped[0] = px;
2105 	    xclipped[1] = qx;
2106 
2107 	    orig[0] = p;
2108 	    orig[1] = q;
2109 	}
2110 	else if (px->z > nearPlane && qx->z <= nearPlane)
2111 	{
2112 	    float d = (nearPlane - qx->z) / (px->z - qx->z);
2113 
2114 	    xclippt.x = qx->x + d*(px->x - qx->x);
2115 	    xclippt.y = qx->y + d*(px->y - qx->y);
2116 	    xclippt.z = nearPlane;
2117 	    xclipped[0] = &xclippt;
2118 	    xclipped[1] = qx;
2119 
2120 	    orig[0] = -1;
2121 	    orig[1] = q;
2122 	}
2123 	else
2124 	{
2125 	    float d = (nearPlane - qx->z) / (px->z - qx->z);
2126 
2127 	    xclippt.x = qx->x + d*(px->x - qx->x);
2128 	    xclippt.y = qx->y + d*(px->y - qx->y);
2129 	    xclippt.z = nearPlane;
2130 	    xclipped[0] = &xclippt;
2131 	    xclipped[1] = px;
2132 
2133 	    orig[0] = -1;
2134 	    orig[1] = p;
2135 	}
2136 
2137 	dx = xclipped[1]->x - xclipped[0]->x;
2138 	dy = xclipped[1]->y - xclipped[0]->y;
2139 
2140 	if (dx == 0 && dy == 0)
2141 	    continue;
2142 
2143 	D = 1.0 / sqrt(dx*dx + dy*dy);
2144 
2145 	a = dy * D;
2146 	b = -dx * D;
2147 	c = -(a*xclipped[0]->x + b*xclipped[0]->y);
2148 
2149 	D = a*xy.x + b*xy.y + c;
2150 
2151 	if (-sens < D && D < sens)
2152 	{
2153 
2154 	    /*
2155 	     * step along line normal by -D to get closest point on line
2156 	     */
2157 	    x = xy.x - D*a;
2158 	    y = xy.y - D*b;
2159 
2160 	    if ((((xclipped[0]->x-sens) <= x && x <= (xclipped[1]->x+sens)) ||
2161 		 ((xclipped[0]->x+sens) >= x && x >= (xclipped[1]->x-sens))) &&
2162 		(((xclipped[0]->y-sens) <= y && y <= (xclipped[1]->y+sens)) ||
2163 		 ((xclipped[0]->y+sens) >= y && y >= (xclipped[1]->y-sens))))
2164 	    {
2165 		float nearPlane = DXD_MAX_FLOAT;
2166 		int   k, ni=0;
2167 
2168 		if (fabs(dx) > fabs(dy))
2169 		    D = (x - xclipped[0]->x)/(xclipped[1]->x - xclipped[0]->x);
2170 		else
2171 		    D = (y - xclipped[0]->y)/(xclipped[1]->y - xclipped[0]->y);
2172 
2173 		/*
2174 		 * interpolate on screen
2175 		 */
2176 		xyz.x = x;
2177 		xyz.y = y;
2178 		xyz.z = xclipped[0]->z + D*(xclipped[1]->z - xclipped[0]->z);
2179 
2180 		if (xyz.z > min && xyz.z < max)
2181 		{
2182 		    float weights[2];
2183 
2184 		    for (k = 0; k < 2; k++)
2185 			if (orig[k] != -1)
2186 			{
2187 			    float dx = xPoints[orig[k]].x - xy.x;
2188 			    float dy = xPoints[orig[k]].y - xy.y;
2189 			    float n = sqrt(dx*dx + dy*dy);
2190 			    if (n < nearPlane)
2191 			    {
2192 				ni = orig[k];
2193 				nearPlane = n;
2194 			    }
2195 			}
2196 
2197 		    if (fabs(dx) > fabs(dy))
2198 			D = (x - xPoints[p].x)/(xPoints[q].x - xPoints[p].x);
2199 		    else
2200 			D = (y - xPoints[p].y)/(xPoints[q].y - xPoints[p].y);
2201 
2202 		    weights[0] = 1.0 - D;
2203 		    weights[1] = D;
2204 
2205 		    pick->pickPath[pick->depth+0] = i;
2206 		    pick->pickPath[pick->depth+1] = ni;
2207 		    if (! AddPick(pick, poke, xyz, 2, -1, weights))
2208 			goto error;
2209 		}
2210 	    }
2211 	}
2212 
2213     }
2214 
2215     return OK;
2216 
2217 error:
2218     return ERROR;
2219 }
2220 
2221 static Error
PickPolylines(Field f,PickBuf * p,int poke,Point2D xy,Matrix * m,int perspective,float nearPlane,float sens,float min,float max)2222 PickPolylines(Field f, PickBuf *p, int poke, Point2D xy, Matrix *m,
2223 	int perspective, float nearPlane, float sens, float min, float max)
2224 {
2225     Array   pA;
2226     Array   plA, eA;
2227     Point   *xPoints = NULL;
2228     int     nPts, nPolylines, nEdges;
2229     int	    *polylines, *edges;
2230 
2231     pA  = (Array)DXGetComponentValue(f, "positions");
2232     plA = (Array)DXGetComponentValue(f, "polylines");
2233     eA  = (Array)DXGetComponentValue(f, "edges");
2234     if (! plA || ! pA || ! eA)
2235     {
2236 	DXSetError(ERROR_MISSING_DATA, "positions, polylines or edges");
2237 	goto error;
2238     }
2239 
2240     DXGetArrayInfo(pA,  &nPts,      NULL, NULL, NULL, NULL);
2241     DXGetArrayInfo(plA, &nPolylines, NULL, NULL, NULL, NULL);
2242     DXGetArrayInfo(eA,  &nEdges,     NULL, NULL, NULL, NULL);
2243 
2244     /*
2245      * Transform points
2246      */
2247     xPoints = TransformArray(pA, m);
2248     if (! xPoints)
2249 	return ERROR;
2250 
2251     polylines = (int *)DXGetArrayData(plA);
2252     edges     = (int *)DXGetArrayData(eA);
2253 
2254     if (perspective)
2255     {
2256 	if (! Polyline_Perspective(p, poke, xy, xPoints, polylines, edges,
2257 			nPts, nPolylines, nEdges, nearPlane, sens, min, max))
2258 	    goto error;
2259     }
2260     else
2261     {
2262 	if (! Polyline_NoPerspective(p, poke, xy, xPoints, polylines, edges,
2263 			nPts, nPolylines, nEdges, nearPlane, sens, min, max))
2264 	    goto error;
2265     }
2266 
2267     DXFree((Pointer)xPoints);
2268     return OK;
2269 
2270 error:
2271     DXFree((Pointer)xPoints);
2272     return ERROR;
2273 }
2274 
2275 static Error
Polyline_Perspective(PickBuf * pick,int poke,Point2D xy,Point * xPoints,int * plines,int * edges,int nPts,int nPl,int nE,float nearPlane,float sens,float min,float max)2276 Polyline_Perspective(PickBuf *pick, int poke, Point2D xy, Point *xPoints,
2277     int *plines, int *edges, int nPts, int nPl, int nE, float nearPlane,
2278     float sens, float min, float max)
2279 {
2280     int pline;
2281     int i;
2282     Point *sPoints;
2283 
2284     sPoints = (Point *)DXAllocate(nPts*sizeof(Point));
2285     if (! sPoints)
2286 	goto error;
2287 
2288     nearPlane += ZOFFSET;
2289 
2290     /*
2291      * perform perspective on all unclipped transformed points
2292      */
2293     for (i = 0; i < nPts; i++)
2294     {
2295 	float z = xPoints[i].z;
2296 	if (z < nearPlane)
2297 	{
2298 	    float invDepth = 1.0 / (-z + ZOFFSET);
2299 	    sPoints[i].x = xPoints[i].x * invDepth;
2300 	    sPoints[i].y = xPoints[i].y * invDepth;
2301 	    sPoints[i].z = xPoints[i].z * invDepth;
2302 	}
2303     }
2304 
2305     for (pline = 0; pline < nPl; pline++)
2306     {
2307 	int estart = plines[pline];
2308 	int eend   = (pline == nPl-1) ? nE : plines[pline+1];
2309 	int e, eIndex;
2310 
2311 	for (e = estart, eIndex = 0; e < eend; e++, eIndex++)
2312 	{
2313 	    int     p, q;
2314 	    Point   *px, *qx;
2315 	    Point   *ps, *qs, *sclipped[2];
2316 	    Point   sclippt;
2317 	    float   x, y, z, a, b, c, D, dx, dy, depth;
2318 	    Point   xyz;
2319 	    int     orig[2];
2320 
2321 	    p = edges[e];
2322 	    q = edges[(e == eend-1) ? estart : e+1];
2323 
2324 	    px = xPoints + p;
2325 	    qx = xPoints + q;
2326 
2327 	    ps = sPoints + p;
2328 	    qs = sPoints + q;
2329 
2330 	    if (px->z > nearPlane && qx->z > nearPlane)
2331 		continue;
2332 
2333 	    if (px->z <= nearPlane && qx->z <= nearPlane)
2334 	    {
2335 		/*xclipped[0] = px;*/
2336 		/*xclipped[1] = qx;*/
2337 
2338 		sclipped[0] = ps;
2339 		sclipped[1] = qs;
2340 
2341 		orig[0] = p;
2342 		orig[1] = q;
2343 	    }
2344 	    else if (px->z > nearPlane && qx->z <= nearPlane)
2345 	    {
2346 		float d = (nearPlane - qx->z) / (px->z - qx->z);
2347 
2348 		/*xclippt.x = qx->x + d*(px->x - qx->x);*/
2349 		/*xclippt.y = qx->y + d*(px->y - qx->y);*/
2350 		/*xclippt.z = nearPlane;*/
2351 		/*xclipped[0] = &xclippt;*/
2352 		/*xclipped[1] = qx;*/
2353 
2354 		sclippt.x = qs->x + d*(ps->x - qs->x);
2355 		sclippt.y = qs->y + d*(ps->y - qs->y);
2356 		sclippt.z = nearPlane;
2357 		sclipped[0] = &sclippt;
2358 		sclipped[1] = qs;
2359 
2360 		orig[0] = -1;
2361 		orig[1] = q;
2362 	    }
2363 	    else
2364 	    {
2365 		float d = (nearPlane - qx->z) / (px->z - qx->z);
2366 
2367 		/*xclippt.x = qx->x + d*(px->x - qx->x);*/
2368 		/*xclippt.y = qx->y + d*(px->y - qx->y);*/
2369 		/*xclippt.z = nearPlane;*/
2370 		/*xclipped[0] = &xclippt;*/
2371 		/*xclipped[1] = px;*/
2372 
2373 		sclippt.x = qs->x + d*(ps->x - qs->x);
2374 		sclippt.y = qs->y + d*(ps->y - qs->y);
2375 		sclippt.z = nearPlane;
2376 		sclipped[0] = &sclippt;
2377 		sclipped[1] = ps;
2378 
2379 		orig[0] = -1;
2380 		orig[1] = p;
2381 	    }
2382 
2383 	    dx = sclipped[1]->x - sclipped[0]->x;
2384 	    dy = sclipped[1]->y - sclipped[0]->y;
2385 
2386 	    if (dx == 0 && dy == 0)
2387 		continue;
2388 
2389 	    D = 1.0 / sqrt(dx*dx + dy*dy);
2390 
2391 	    a = dy * D;
2392 	    b = -dx * D;
2393 	    c = -(a*sclipped[0]->x + b*sclipped[0]->y);
2394 
2395 	    D = a*xy.x + b*xy.y + c;
2396 
2397 	    if (-sens < D && D < sens)
2398 	    {
2399 		/*
2400 		 * step along line normal by -D to get closest point on line
2401 		 */
2402 		x = xy.x - D*a;
2403 		y = xy.y - D*b;
2404 
2405 		if ((((sclipped[0]->x-sens) <= x && x <= (sclipped[1]->x+sens)) ||
2406 		     ((sclipped[0]->x+sens) >= x && x >= (sclipped[1]->x-sens))) &&
2407 		    (((sclipped[0]->y-sens) <= y && y <= (sclipped[1]->y+sens)) ||
2408 		     ((sclipped[0]->y+sens) >= y && y >= (sclipped[1]->y-sens))))
2409 		{
2410 		    float nearPlane = DXD_MAX_FLOAT;
2411 		    int   k, ni=0;
2412 
2413 		    if (fabs(dx) > fabs(dy))
2414 			D = (x - sclipped[0]->x)/(sclipped[1]->x - sclipped[0]->x);
2415 		    else
2416 			D = (y - sclipped[0]->y)/(sclipped[1]->y - sclipped[0]->y);
2417 
2418 		    /*
2419 		     * interpolate on screen
2420 		     */
2421 		    x = sclipped[0]->x + D*(sclipped[1]->x - sclipped[0]->x);
2422 		    y = sclipped[0]->y + D*(sclipped[1]->y - sclipped[0]->y);
2423 		    z = sclipped[0]->z + D*(sclipped[1]->z - sclipped[0]->z);
2424 
2425 		    /*
2426 		     * Perspective transformation is zp = z / depth, where
2427 		     * depth = -z + d.  To regain original z, z = d(zp) / 1 + zp.
2428 		     */
2429 		    xyz.z = (ZOFFSET * z) / (1.0 + z);
2430 
2431 		    if (xyz.z > min && xyz.z < max)
2432 		    {
2433 			float weights[2];
2434 
2435 			/*
2436 			 * now undo perspective on xy by multiplying by depth
2437 			 */
2438 			depth = -xyz.z + ZOFFSET;
2439 			xyz.x = x * depth;
2440 			xyz.y = y * depth;
2441 
2442 			for (k = 0; k < 2; k++)
2443 			    if (orig[k] != -1)
2444 			    {
2445 				float dx = sPoints[orig[k]].x - xy.x;
2446 				float dy = sPoints[orig[k]].y - xy.y;
2447 				float n = sqrt(dx*dx + dy*dy);
2448 				if (n < nearPlane)
2449 				{
2450 				    ni = orig[k];
2451 				    nearPlane = n;
2452 				}
2453 			    }
2454 
2455 			if (fabs(dx) > fabs(dy))
2456 			    D = (x - sPoints[p].x)/(sPoints[q].x - sPoints[p].x);
2457 			else
2458 			    D = (y - sPoints[p].y)/(sPoints[q].y - sPoints[p].y);
2459 
2460 			weights[0] = 1.0 - D;
2461 			weights[1] = D;
2462 
2463 			pick->pickPath[pick->depth+0] = pline;
2464 			pick->pickPath[pick->depth+1] = ni;
2465 			if (! AddPick(pick, poke, xyz, 2, eIndex, weights))
2466 			    goto error;
2467 		    }
2468 		}
2469 	    }
2470 	}
2471     }
2472 
2473     DXFree((Pointer)sPoints);
2474     return OK;
2475 
2476 error:
2477     DXFree((Pointer)sPoints);
2478     return ERROR;
2479 }
2480 
2481 static Error
Polyline_NoPerspective(PickBuf * pick,int poke,Point2D xy,Point * xPoints,int * plines,int * edges,int nPts,int nPl,int nE,float nearPlane,float sens,float min,float max)2482 Polyline_NoPerspective(PickBuf *pick, int poke, Point2D xy, Point *xPoints,
2483     int *plines, int *edges, int nPts, int nPl, int nE, float nearPlane,
2484     float sens, float min, float max)
2485 {
2486     int pline;
2487     int orig[2];
2488 
2489     nearPlane += ZOFFSET;
2490 
2491     for (pline = 0; pline < nPl; pline++)
2492     {
2493 	int estart = plines[pline];
2494 	int eend   = (pline == nPl-1) ? nE : plines[pline+1];
2495 	int e, eIndex;
2496 
2497 	for (e = estart, eIndex = 0; e < eend; e++, eIndex++)
2498 	{
2499 	    int     p, q;
2500 	    Point   *px, *qx, *xclipped[2];
2501 	    Point   xclippt;
2502 	    float   x, y, a, b, c, D, dx, dy;
2503 	    Point   xyz;
2504 
2505 	    p = edges[e];
2506 	    q = edges[(e == eend-1) ? estart : e+1];
2507 
2508 	    px = xPoints + p;
2509 	    qx = xPoints + q;
2510 
2511 	    if (px->z > nearPlane && qx->z > nearPlane)
2512 		continue;
2513 
2514 	    if (px->z <= nearPlane && qx->z <= nearPlane)
2515 	    {
2516 		xclipped[0] = px;
2517 		xclipped[1] = qx;
2518 
2519 		orig[0] = p;
2520 		orig[1] = q;
2521 	    }
2522 	    else if (px->z > nearPlane && qx->z <= nearPlane)
2523 	    {
2524 		float d = (nearPlane - qx->z) / (px->z - qx->z);
2525 
2526 		xclippt.x = qx->x + d*(px->x - qx->x);
2527 		xclippt.y = qx->y + d*(px->y - qx->y);
2528 		xclippt.z = nearPlane;
2529 		xclipped[0] = &xclippt;
2530 		xclipped[1] = qx;
2531 
2532 		orig[0] = -1;
2533 		orig[1] = q;
2534 	    }
2535 	    else
2536 	    {
2537 		float d = (nearPlane - qx->z) / (px->z - qx->z);
2538 
2539 		xclippt.x = qx->x + d*(px->x - qx->x);
2540 		xclippt.y = qx->y + d*(px->y - qx->y);
2541 		xclippt.z = nearPlane;
2542 		xclipped[0] = &xclippt;
2543 		xclipped[1] = px;
2544 
2545 		orig[0] = -1;
2546 		orig[1] = p;
2547 	    }
2548 
2549 	    dx = xclipped[1]->x - xclipped[0]->x;
2550 	    dy = xclipped[1]->y - xclipped[0]->y;
2551 
2552 	    if (dx == 0 && dy == 0)
2553 		continue;
2554 
2555 	    D = 1.0 / sqrt(dx*dx + dy*dy);
2556 
2557 	    a = dy * D;
2558 	    b = -dx * D;
2559 	    c = -(a*xclipped[0]->x + b*xclipped[0]->y);
2560 
2561 	    D = a*xy.x + b*xy.y + c;
2562 
2563 	    if (-sens < D && D < sens)
2564 	    {
2565 
2566 		/*
2567 		 * step along line normal by -D to get closest point on line
2568 		 */
2569 		x = xy.x - D*a;
2570 		y = xy.y - D*b;
2571 
2572 		if ((((xclipped[0]->x-sens) <= x && x <= (xclipped[1]->x+sens)) ||
2573 		     ((xclipped[0]->x+sens) >= x && x >= (xclipped[1]->x-sens))) &&
2574 		    (((xclipped[0]->y-sens) <= y && y <= (xclipped[1]->y+sens)) ||
2575 		     ((xclipped[0]->y+sens) >= y && y >= (xclipped[1]->y-sens))))
2576 		{
2577 		    float nearPlane = DXD_MAX_FLOAT;
2578 		    int   k, ni=0;
2579 
2580 		    if (fabs(dx) > fabs(dy))
2581 			D = (x - xclipped[0]->x)/(xclipped[1]->x - xclipped[0]->x);
2582 		    else
2583 			D = (y - xclipped[0]->y)/(xclipped[1]->y - xclipped[0]->y);
2584 
2585 		    /*
2586 		     * interpolate on screen
2587 		     */
2588 		    xyz.x = x;
2589 		    xyz.y = y;
2590 		    xyz.z = xclipped[0]->z + D*(xclipped[1]->z - xclipped[0]->z);
2591 
2592 		    if (xyz.z > min && xyz.z < max)
2593 		    {
2594 			float weights[2];
2595 
2596 			for (k = 0; k < 2; k++)
2597 			    if (orig[k] != -1)
2598 			    {
2599 				float dx = xPoints[orig[k]].x - xy.x;
2600 				float dy = xPoints[orig[k]].y - xy.y;
2601 				float n = sqrt(dx*dx + dy*dy);
2602 				if (n < nearPlane)
2603 				{
2604 				    ni = orig[k];
2605 				    nearPlane = n;
2606 				}
2607 			    }
2608 
2609 			if (fabs(dx) > fabs(dy))
2610 			    D = (x - xPoints[p].x)/(xPoints[q].x - xPoints[p].x);
2611 			else
2612 			    D = (y - xPoints[p].y)/(xPoints[q].y - xPoints[p].y);
2613 
2614 			weights[0] = 1.0 - D;
2615 			weights[1] = D;
2616 
2617 			pick->pickPath[pick->depth+0] = pline;
2618 			pick->pickPath[pick->depth+1] = ni;
2619 			if (! AddPick(pick, poke, xyz, 2, eIndex, weights))
2620 			    goto error;
2621 		    }
2622 		}
2623 	    }
2624 	}
2625 
2626     }
2627 
2628     return OK;
2629 
2630 error:
2631     return ERROR;
2632 }
2633 
2634 
2635 static Error
PickPoints(Field f,PickBuf * p,int poke,Point2D xy,Matrix * m,int perspective,float nearPlane,float sens,float min,float max)2636 PickPoints(Field f, PickBuf *p, int poke, Point2D xy, Matrix *m,
2637 	    int perspective, float nearPlane, float sens, float min, float max)
2638 {
2639     Array   pA;
2640     Point   *xPoints = NULL;
2641     int     nPts;
2642 
2643     pA = (Array)DXGetComponentValue(f, "positions");
2644     if (! pA)
2645     {
2646 	DXSetError(ERROR_MISSING_DATA, "positions");
2647 	goto error;
2648     }
2649 
2650     DXGetArrayInfo(pA, &nPts,  NULL, NULL, NULL, NULL);
2651 
2652     /*
2653      * Transform points
2654      */
2655     xPoints = TransformArray(pA, m);
2656     if (! xPoints)
2657 	return ERROR;
2658 
2659     if (perspective)
2660     {
2661 	if (! Elt0D_Perspective(p, poke, xy, xPoints,
2662 				nPts, nearPlane, sens, min, max))
2663 	    goto error;
2664     }
2665     else
2666     {
2667 	if (! Elt0D_NoPerspective(p, poke, xy, xPoints,
2668 				nPts, nearPlane, sens, min, max))
2669 	    goto error;
2670     }
2671 
2672     DXFree((Pointer)xPoints);
2673     return OK;
2674 
2675 error:
2676     DXFree((Pointer)xPoints);
2677     return ERROR;
2678 }
2679 
2680 static Error
Elt0D_Perspective(PickBuf * pick,int poke,Point2D xy,Point * xPoints,int nPts,float nearPlane,float sens,float min,float max)2681 Elt0D_Perspective(PickBuf *pick, int poke, Point2D xy, Point *xPoints,
2682 			int nPts, float nearPlane, float sens, float min, float max)
2683 {
2684     int i;
2685     Point2D *sPoints;
2686 
2687     sPoints = (Point2D *)DXAllocate(nPts*sizeof(Point2D));
2688     if (! sPoints)
2689 	goto error;
2690 
2691     nearPlane += ZOFFSET;
2692 
2693     /*
2694      * perform perspective on all unclipped transformed points
2695      */
2696     for (i = 0; i < nPts; i++)
2697     {
2698 	float z = xPoints[i].z;
2699 	if (z < nearPlane)
2700 	{
2701 	    float invDepth = 1.0 / (-z + ZOFFSET);
2702 	    sPoints[i].x = xPoints[i].x * invDepth;
2703 	    sPoints[i].y = xPoints[i].y * invDepth;
2704 	}
2705     }
2706 
2707     for (i = 0; i < nPts; i++)
2708     {
2709 	if (xPoints[i].z < nearPlane &&
2710 	    fabs(sPoints[i].x - xy.x) < sens &&
2711 	    fabs(sPoints[i].y - xy.y) < sens)
2712 	{
2713 	    if (xPoints[i].z > min && xPoints[i].z < max)
2714 	    {
2715 		pick->pickPath[pick->depth+0] = i;
2716 		pick->pickPath[pick->depth+1] = i;
2717 		if (! AddPick(pick, poke, xPoints[i], 0, -1, NULL))
2718 		    goto error;
2719 	    }
2720 	}
2721     }
2722 
2723     DXFree((Pointer)sPoints);
2724     return OK;
2725 
2726 error:
2727     DXFree((Pointer)sPoints);
2728     return ERROR;
2729 }
2730 
2731 static Error
Elt0D_NoPerspective(PickBuf * pick,int poke,Point2D xy,Point * xPoints,int nPts,float nearPlane,float sens,float min,float max)2732 Elt0D_NoPerspective(PickBuf *pick, int poke, Point2D xy, Point *xPoints,
2733 			int nPts, float nearPlane, float sens, float min, float max)
2734 {
2735     int i;
2736 
2737     nearPlane += ZOFFSET;
2738 
2739     for (i = 0; i < nPts; i++)
2740     {
2741 	if (xPoints[i].z < nearPlane &&
2742 	    fabs(xPoints[i].x - xy.x) < sens &&
2743 	    fabs(xPoints[i].y - xy.y) < sens)
2744 	{
2745 	    if (xPoints[i].z > min && xPoints[i].z < max)
2746 	    {
2747 		pick->pickPath[pick->depth+0] = i;
2748 		pick->pickPath[pick->depth+1] = i;
2749 		if (! AddPick(pick, poke, xPoints[i], 0, -1, NULL))
2750 		    goto error;
2751 	    }
2752 	}
2753     }
2754 
2755     return OK;
2756 
2757 error:
2758     return ERROR;
2759 }
2760 
2761 static Error
PickFLE(Field f,PickBuf * p,int poke,Point2D xy,Matrix * m,int perspective,float nearPlane,float sens,float min,float max)2762 PickFLE(Field f, PickBuf *p, int poke, Point2D xy, Matrix *m,
2763 	int perspective, float nearPlane, float sens, float min, float max)
2764 {
2765     Array   pA;
2766     Array   fA, lA, eA;
2767     Point   *xPoints = NULL;
2768     int     nPts, nFaces, nLoops, nEdges;
2769     int     *faces, *loops, *edges;
2770 
2771     pA = (Array)DXGetComponentValue(f, "positions");
2772     fA = (Array)DXGetComponentValue(f, "faces");
2773     lA = (Array)DXGetComponentValue(f, "loops");
2774     eA = (Array)DXGetComponentValue(f, "edges");
2775     if (! lA || ! pA)
2776     {
2777 	DXSetError(ERROR_MISSING_DATA, "positions, faces, loops or edges");
2778 	goto error;
2779     }
2780 
2781     DXGetArrayInfo(pA, &nPts,  NULL, NULL, NULL, NULL);
2782     DXGetArrayInfo(fA, &nFaces, NULL, NULL, NULL, NULL);
2783     DXGetArrayInfo(lA, &nLoops, NULL, NULL, NULL, NULL);
2784     DXGetArrayInfo(eA, &nEdges, NULL, NULL, NULL, NULL);
2785 
2786     /*
2787      * Transform points
2788      */
2789     xPoints = TransformArray(pA, m);
2790     if (! xPoints)
2791 	return ERROR;
2792 
2793     faces = (int *)DXGetArrayData(fA);
2794     loops = (int *)DXGetArrayData(lA);
2795     edges = (int *)DXGetArrayData(eA);
2796 
2797     if (perspective)
2798     {
2799 	if (! FLE_Perspective(p, poke, xy, xPoints, nPts,
2800 		faces, nFaces, loops, nLoops, edges, nEdges, nearPlane, sens,
2801 		min, max))
2802 	    goto error;
2803     }
2804     else
2805     {
2806 	if (! FLE_NoPerspective(p, poke, xy, xPoints, nPts,
2807 		faces, nFaces, loops, nLoops, edges, nEdges, nearPlane, sens,
2808 		min, max))
2809 	    goto error;
2810     }
2811 
2812     DXFree((Pointer)xPoints);
2813     return OK;
2814 
2815 error:
2816     DXFree((Pointer)xPoints);
2817     return ERROR;
2818 }
2819 
2820 static Error
FLE_NoPerspective(PickBuf * pick,int poke,Point2D xy,Point * xPoints,int nPts,int * faces,int nFaces,int * loops,int nLoops,int * edges,int nEdges,float nearPlane,float sens,float min,float max)2821 FLE_NoPerspective(PickBuf *pick, int poke, Point2D xy, Point *xPoints,
2822        int nPts, int *faces, int nFaces, int *loops, int nLoops, int *edges,
2823        int nEdges, float nearPlane, float sens, float min, float max)
2824 {
2825     int f, found;
2826     int l, fl, ll;
2827     int e, fe, le;
2828 
2829     for (f = 0; f < nFaces; f++)
2830     {
2831 	int count = 0;
2832 
2833 	fl = faces[f];
2834 	ll = (f == nFaces-1) ? nLoops - 1 : faces[f+1] - 1;
2835 
2836 	for (l = fl, found = 0; l <= ll && !found; l++)
2837 	{
2838 	    int start, end;
2839 
2840 	    fe = loops[l];
2841 	    le = (l == nLoops-1) ? nEdges - 1 : loops[l+1] - 1;
2842 
2843 	    end = edges[le];
2844 	    for (e = fe; e <= le && !found; e++)
2845 	    {
2846 		float x0, x1;
2847 		float y0, y1;
2848 
2849 		start = end;
2850 		end = edges[e];
2851 
2852 		x0 = xPoints[start].x;
2853 		y0 = xPoints[start].y;
2854 
2855 		x1 = xPoints[end].x;
2856 		y1 = xPoints[end].y;
2857 
2858 		if (y0 == y1)
2859 		    continue;
2860 
2861 		if (y0 > y1)
2862 		{
2863 		    float tt;
2864 		    tt = x0;
2865 		    x0 = x1;
2866 		    x1 = tt;
2867 		    tt = y0;
2868 		    y0 = y1;
2869 		    y1 = tt;
2870 		}
2871 
2872 		/*
2873 		 * If the edge's y-range (closed at the top) spans
2874 		 * the xy y value, we may have a hit.
2875 		 */
2876 		if (xy.y > y0 && xy.y <= y1)
2877 		{
2878 		    /*
2879 		     * If both edge endpoints are to the right of
2880 		     * the xy x point, we have a hit.  Otherwise, if
2881 		     * the edge x-range spans the xy point, we get
2882 		     * the intersection of the edge with the y = xy.y
2883 		     * line.  If this intersection is to the right of
2884 		     * the xy point, we have a hit.
2885 		     */
2886 		    if (x0 > xy.x && x1 >= xy.x)
2887 		    {
2888 			count ++;
2889 		    }
2890 		    else if ((x0 < xy.x && x1 >= xy.x) ||
2891 			     (x0 > xy.x && x1 <= xy.x))
2892 		    {
2893 			float x, d = (xy.y - y0) / (y1 - y0);
2894 			x = x0 + d*(x1 - x0);
2895 			if (x == xy.x)
2896 			    found = 1;
2897 
2898 			else if (x > xy.x)
2899 			    count ++;
2900 		    }
2901 		}
2902 	    }
2903 	}
2904 
2905 	/*
2906 	 * If count is odd, then xy is inside the FLE.  Determine the xyz point
2907 	 * and add it to the pick buf.  Find the point by computing a planar
2908 	 * equation and evaluating it for Z at the xy point
2909 	 */
2910 	if (found || count & 0x1)
2911 	{
2912 	    Vector v0, v1, vn, normal;
2913 	    float l0, l0i=0;
2914 	    float l1=0, l1i=0;
2915 	    float l, a, b, c, d;
2916 	    int p, q, r, k;
2917 	    Point xyz;
2918 	    float nearPlane = DXD_MAX_FLOAT;
2919 	    int ni=0;
2920 
2921 	    /*
2922 	     * Compute a planar equation from the first loop in the face
2923 	     */
2924 
2925 	    fe = loops[fl];
2926 	    le = (fl == nLoops-1) ? nEdges - 1 : loops[fl+1] - 1;
2927 
2928 	    normal = DXPt(0.0,0.0,0.0);
2929 
2930 	    q = edges[le];
2931 	    r = edges[fe];
2932 	    for (e = fe; e <= le; e++)
2933 	    {
2934 		p = q;
2935 		q = r;
2936 		r = (e == le) ? edges[fe] : edges[e+1];
2937 
2938 		if (e == fe)
2939 		{
2940 		    v0 = DXSub(xPoints[q], xPoints[p]);
2941 		    l0 = DXLength(v0);
2942 		    if (l0 != 0.0)
2943 			l0i = 1.0 / l0;
2944 		}
2945 		else
2946 		{
2947 		    v0  = v1;
2948 		    l0  = l1;
2949 		    l0i = l1i;
2950 		}
2951 
2952 		v1 = DXSub(xPoints[r], xPoints[q]);
2953 		l1 = DXLength(v1);
2954 
2955 		if (l1 == 0.0 || l0 == 0.0)
2956 		    continue;
2957 
2958 		l1i = 1.0 / l1;
2959 
2960 		vn = DXMul(DXCross(v0, v1), l0i*l1i);
2961 
2962 		l = DXLength(vn);
2963 		if (l > 0.0001)
2964 		    normal = DXAdd(DXMul(vn, 1.0/l), normal);
2965 	    }
2966 
2967 	    l = DXLength(normal);
2968 	    if (l == 0.0)
2969 	    {
2970 	        DXSetError(ERROR_INTERNAL, "hit a zero area FLE?");
2971 		return ERROR;
2972 	    }
2973 
2974 	    l = 1.0 / l;
2975 	    a = normal.x * l;
2976 	    b = normal.y * l;
2977 	    c = normal.z * l;
2978 
2979 	    if (c == 0.0)
2980 	    {
2981 		DXSetError(ERROR_INTERNAL, "hit an edge-on FLE?");
2982 		return ERROR;
2983 	    }
2984 
2985 	    d = -(a*xPoints[q].x + b*xPoints[q].y + c*xPoints[q].z);
2986 
2987 	    xyz.x = xy.x;
2988 	    xyz.y = xy.y;
2989 	    xyz.z = -(a*xy.x + b*xy.y + d) / c;
2990 
2991 	    for (k = fl; k <= ll; k++)
2992 	    {
2993 		fe = loops[k];
2994 		le = (k == nLoops-1) ? nEdges - 1 : loops[k+1] - 1;
2995 
2996 		for (e = fe; e <= le && !found; e++)
2997 		{
2998 		    float dx = xPoints[edges[e]].x - xy.x;
2999 		    float dy = xPoints[edges[e]].y - xy.y;
3000 		    float n = sqrt(dx*dx + dy*dy);
3001 		    if (n < nearPlane)
3002 		    {
3003 			ni = edges[e];
3004 			nearPlane = n;
3005 		    }
3006 		}
3007 	    }
3008 
3009 	    if (xyz.z > min && xyz.z < max)
3010 	    {
3011 		pick->pickPath[pick->depth+0] = f;
3012 		pick->pickPath[pick->depth+1] = ni;
3013 		if (! AddPick(pick, poke, xyz, 0, -1, NULL))
3014 		    goto error;
3015 	    }
3016 	}
3017     }
3018 
3019     return OK;
3020 
3021 error:
3022     return ERROR;
3023 }
3024 
3025 #define MAX_CLIPPTS 100
3026 
3027 static Error
FLE_Perspective(PickBuf * pick,int poke,Point2D xy,Point * xPoints,int nPts,int * faces,int nFaces,int * loops,int nLoops,int * edges,int nEdges,float nearPlane,float sens,float min,float max)3028 FLE_Perspective(PickBuf *pick, int poke, Point2D xy, Point *xPoints,
3029        int nPts, int *faces, int nFaces, int *loops, int nLoops, int *edges,
3030        int nEdges, float nearPlane, float sens, float min, float max)
3031 {
3032     int i, f, found;
3033     int l, fl, ll;
3034     int e, fe, le;
3035     float dNear, z, depth;
3036     int vKnt=0, cKnt, vLen = 100;
3037     Point cPoints[MAX_CLIPPTS];
3038     Point **cLoop   = (Point **)DXAllocate(vLen*sizeof(Pointer));
3039     Point *sPoints  = (Point *)DXAllocate(nPts*sizeof(Point));
3040     if (! sPoints || ! cLoop)
3041 	goto error;
3042 
3043     nearPlane += ZOFFSET;
3044     dNear = -1.0 / nearPlane;
3045 
3046     /*
3047      * perform perspective on all unclipped transformed points
3048      */
3049     for (i = 0; i < nPts; i++)
3050     {
3051 	float z = xPoints[i].z;
3052 	if (z < nearPlane)
3053 	{
3054 	    float invDepth = 1.0 / (-z + ZOFFSET);
3055 	    sPoints[i].x = xPoints[i].x * invDepth;
3056 	    sPoints[i].y = xPoints[i].y * invDepth;
3057 	    sPoints[i].z = xPoints[i].z * invDepth;
3058 	}
3059     }
3060 
3061     for (f = 0; f < nFaces; f++)
3062     {
3063 	int count = 0;
3064 
3065 	fl = faces[f];
3066 	ll = (f == nFaces-1) ? nLoops - 1 : faces[f+1] - 1;
3067 
3068 	for (l = fl, found = 0; l <= ll && !found; l++)
3069 	{
3070 	    Point *start, *end;
3071 
3072 	    fe = loops[l];
3073 	    le = (l == nLoops-1) ? nEdges - 1 : loops[l+1] - 1;
3074 
3075 	    vKnt = cKnt = 0;
3076 	    for (e = fe; e <= le; e++)
3077 	    {
3078 		int tv, nv;
3079 		int ne = (e == le) ? fe : e + 1;
3080 		tv = edges[e];
3081 		nv = edges[ne];
3082 
3083 		if (xPoints[tv].z <= nearPlane)
3084 		{
3085 		    if (vKnt == vLen)
3086 		    {
3087 			cLoop = (Point **)DXReAllocate(cLoop,
3088 						2*vLen*sizeof(Pointer));
3089 			if (cLoop == NULL)
3090 			    goto error;
3091 			vLen *= 2;
3092 		    }
3093 
3094 		    cLoop[vKnt++] = sPoints + tv;
3095 		}
3096 
3097 		if ((xPoints[tv].z < nearPlane && xPoints[nv].z > nearPlane) ||
3098 		    (xPoints[tv].z > nearPlane && xPoints[nv].z < nearPlane))
3099 		{
3100 		    float d = (nearPlane - sPoints[tv].z) /
3101 				    (sPoints[nv].z - sPoints[tv].z);
3102 
3103 		    if (cKnt == MAX_CLIPPTS)
3104 		    {
3105 			DXSetError(ERROR_DATA_INVALID, "too many clip points");
3106 			goto error;
3107 		    }
3108 
3109 		    cPoints[cKnt].x = dNear*(xPoints[tv].x + d*(xPoints[nv].x-xPoints[tv].x));
3110 		    cPoints[cKnt].y = dNear*(xPoints[tv].y + d*(xPoints[nv].y-xPoints[tv].y));
3111 		    cPoints[cKnt].z = dNear*(xPoints[tv].z + d*(xPoints[nv].z-xPoints[tv].z));
3112 
3113 		    cLoop[vKnt++] = cPoints + cKnt++;
3114 		}
3115 	    }
3116 
3117 	    if (vKnt < 3)
3118 		continue;
3119 
3120 	    end = cLoop[vKnt-1];
3121 	    for (e = 0; e < vKnt && !found; e++)
3122 	    {
3123 		float x0, x1;
3124 		float y0, y1;
3125 
3126 		start = end;
3127 		end   = cLoop[e];
3128 
3129 		x0 = start->x;
3130 		y0 = start->y;
3131 
3132 		x1 = end->x;
3133 		y1 = end->y;
3134 
3135 		if (y0 == y1)
3136 		    continue;
3137 
3138 		if (y0 > y1)
3139 		{
3140 		    float tt;
3141 		    tt = x0;
3142 		    x0 = x1;
3143 		    x1 = tt;
3144 		    tt = y0;
3145 		    y0 = y1;
3146 		    y1 = tt;
3147 		}
3148 
3149 		/*
3150 		 * If the edge's y-range (closed at the top) spans
3151 		 * the xy y value, we may have a hit.
3152 		 */
3153 		if (xy.y > y0 && xy.y <= y1)
3154 		{
3155 		    /*
3156 		     * If both edge endpoints are to the right of
3157 		     * the xy x point, we have a hit.  Otherwise, if
3158 		     * the edge x-range spans the xy point, we get
3159 		     * the intersection of the edge with the y = xy.y
3160 		     * line.  If this intersection is to the right of
3161 		     * the xy point, we have a hit.
3162 		     */
3163 		    if (x0 > xy.x && x1 >= xy.x)
3164 		    {
3165 			count ++;
3166 		    }
3167 		    else if ((x0 < xy.x && x1 >= xy.x) ||
3168 			     (x0 > xy.x && x1 <= xy.x))
3169 		    {
3170 			float x, d = (xy.y - y0) / (y1 - y0);
3171 			x = x0 + d*(x1 - x0);
3172 			if (x == xy.x)
3173 			    found = 1;
3174 
3175 			else if (x > xy.x)
3176 			    count ++;
3177 		    }
3178 		}
3179 	    }
3180 	}
3181 
3182 	/*
3183 	 * If count is odd, then xy is inside the FLE.  Determine the xyz point
3184 	 * and add it to the pick buf.  Find the point by computing a planar
3185 	 * equation and evaluating it for Z at the xy point
3186 	 */
3187 	if (found || count & 0x1)
3188 	{
3189 	    Vector v0, v1, vn, normal;
3190 	    float l0, l0i=0;
3191 	    float l1=0, l1i=0;
3192 	    float l, a, b, c, d;
3193 	    Point *p, *q, *r;
3194 	    Point xyz;
3195 	    int k, ni=0;
3196 
3197 	    /*
3198 	     * Compute a planar equation
3199 	     */
3200 
3201 	    normal = DXPt(0.0,0.0,0.0);
3202 
3203 	    q = cLoop[vKnt-1];
3204 	    r = cLoop[0];
3205 	    for (e = 0; e < vKnt; e++)
3206 	    {
3207 		p = q;
3208 		q = r;
3209 		r = (e == vKnt-1) ? cLoop[0] : cLoop[e+1];
3210 
3211 		if (e == 0)
3212 		{
3213 		    v0 = DXSub(*q, *p);
3214 		    l0 = DXLength(v0);
3215 		    if (l0 != 0.0)
3216 			l0i = 1.0 / l0;
3217 		}
3218 		else
3219 		{
3220 		    v0  = v1;
3221 		    l0  = l1;
3222 		    l0i = l1i;
3223 		}
3224 
3225 		v1 = DXSub(*r, *q);
3226 		l1 = DXLength(v1);
3227 
3228 		if (l1 == 0.0 || l0 == 0.0)
3229 		    continue;
3230 
3231 		l1i = 1.0 / l1;
3232 
3233 		vn = DXMul(DXCross(v0, v1), l0i*l1i);
3234 
3235 		l = DXLength(vn);
3236 		if (l > 0.0001)
3237 		    normal = DXAdd(DXMul(vn, 1.0/l), normal);
3238 	    }
3239 
3240 	    l = DXLength(normal);
3241 	    if (l == 0.0)
3242 	    {
3243 	        DXSetError(ERROR_INTERNAL, "hit a zero area FLE?");
3244 		return ERROR;
3245 	    }
3246 
3247 	    l = 1.0 / l;
3248 	    a = normal.x * l;
3249 	    b = normal.y * l;
3250 	    c = normal.z * l;
3251 
3252 	    if (c == 0.0)
3253 	    {
3254 		DXSetError(ERROR_INTERNAL, "hit an edge-on FLE?");
3255 		return ERROR;
3256 	    }
3257 
3258 	    d = -(a*q->x + b*q->y + c*q->z);
3259 
3260 	    xyz.x = xy.x;
3261 	    xyz.y = xy.y;
3262 	    xyz.z = -(a*xy.x + b*xy.y + d) / c;
3263 
3264 	    z = xyz.z = (ZOFFSET * xyz.z) / (1.0 + xyz.z);
3265 	    depth = ZOFFSET - z;
3266 	    xyz.x *= depth;
3267 	    xyz.y *= depth;
3268 
3269 
3270 	    for (k = fl; k <= ll; k++)
3271 	    {
3272 		fe = loops[k];
3273 		le = (k == nLoops-1) ? nEdges - 1 : loops[k+1] - 1;
3274 
3275 		for (e = fe; e <= le && !found; e++)
3276 		    if (xPoints[edges[e]].z < nearPlane)
3277 		    {
3278 			float dx = sPoints[edges[e]].x - xy.x;
3279 			float dy = sPoints[edges[e]].y - xy.y;
3280 			float n = sqrt(dx*dx + dy*dy);
3281 			if (n < nearPlane)
3282 			{
3283 			    ni = edges[e];
3284 			    nearPlane = n;
3285 			}
3286 		    }
3287 	    }
3288 
3289 	    if (xyz.z > min && xyz.z < max)
3290 	    {
3291 		pick->pickPath[pick->depth+0] = f;
3292 		pick->pickPath[pick->depth+1] = ni;
3293 		if (! AddPick(pick, poke, xyz, 0, -1, NULL))
3294 		    goto error;
3295 	    }
3296 	}
3297     }
3298 
3299     DXFree((Pointer)cLoop);
3300     DXFree((Pointer)sPoints);
3301     return OK;
3302 
3303 error:
3304     DXFree((Pointer)cLoop);
3305     DXFree((Pointer)sPoints);
3306     return ERROR;
3307 }
3308 
3309 static Field
GetFirstHits(Field field)3310 GetFirstHits(Field field)
3311 {
3312     int     i;
3313     Field   newField = NULL;
3314     Array   inPointsA, inPicksA, inPokesA, inPathsA, inWeightsA;
3315     Array   outPointsA = NULL, outPicksA = NULL,
3316 	    outPokesA = NULL, outPathsA = NULL,
3317 	    outWeightsA = NULL;
3318     float   *inPoints, *outPoints;
3319     int     *inPicks, *outPicks;
3320     int     *inPokes, *outPokes;
3321     int     *inPaths, *outPaths;
3322     ubyte   *inWeights, *outWeights;
3323     PickWts *wts;
3324     int     wtSz;
3325     int     pathSz;
3326     int     nPokes, nPicks, nPaths;
3327 
3328     if (DXEmptyField(field))
3329 	return DXEndField(DXNewField());
3330 
3331     inPointsA  = (Array)DXGetComponentValue(field, "positions");
3332     inPicksA   = (Array)DXGetComponentValue(field, "picks");
3333     inPokesA   = (Array)DXGetComponentValue(field, "pokes");
3334     inPathsA   = (Array)DXGetComponentValue(field, "pick paths");
3335     inWeightsA = (Array)DXGetComponentValue(field, "pick weights");
3336 
3337     if (! inPointsA || ! inPicksA || ! inPokesA || ! inPathsA)
3338     {
3339 	DXSetError(ERROR_MISSING_DATA, "pick result");
3340 	goto error;
3341     }
3342 
3343     DXGetArrayInfo(inPokesA,  &nPokes, NULL, NULL, NULL, NULL);
3344     DXGetArrayInfo(inPathsA,  &nPaths, NULL, NULL, NULL, NULL);
3345     DXGetArrayInfo(inPicksA,  &nPicks, NULL, NULL, NULL, NULL);
3346 
3347     inPicks   = (int   *)DXGetArrayData(inPicksA);
3348     inPokes   = (int   *)DXGetArrayData(inPokesA);
3349     inPaths   = (int   *)DXGetArrayData(inPathsA);
3350     inPoints  = (float *)DXGetArrayData(inPointsA);
3351     inWeights = (ubyte *)DXGetArrayData(inWeightsA);
3352 
3353     pathSz = 0; wtSz = 0; wts = (PickWts *)inWeights;
3354     for (i = 0; i < nPokes; i++)
3355     {
3356 	int poke = inPokes[i];
3357 	int pick = inPicks[poke];
3358 	int np  = ((i == nPokes-1) ? nPicks  : inPokes[i+1]) - poke;
3359 	int psz = ((poke == nPicks-1) ? nPaths : inPicks[poke+1]) - pick;
3360 	int k;
3361 
3362 	pathSz += psz;
3363 
3364 	for (k = 0; k < np; k++)
3365 	{
3366 	    int n = wts->nweights;
3367 	    int wsz = sizeof(PickWts) + n*sizeof(float);
3368 
3369 	    if (k == 0)
3370 		wtSz += wsz;
3371 
3372 	    wts = (PickWts *)(((ubyte *)wts) + wsz);
3373 	}
3374     }
3375 
3376     outPointsA  = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, 3);
3377     outPicksA   = DXNewArray(TYPE_INT,   CATEGORY_REAL, 0);
3378     outPokesA   = DXNewArray(TYPE_INT,   CATEGORY_REAL, 0);
3379     outPathsA   = DXNewArray(TYPE_INT,   CATEGORY_REAL, 0);
3380     outWeightsA = DXNewArray(TYPE_UBYTE, CATEGORY_REAL, 0);
3381 
3382     if (!outPointsA || !outPicksA || !outPokesA || !outPathsA || !outWeightsA)
3383 	goto error;
3384 
3385     if (! DXAddArrayData(outPointsA,  0, nPokes, NULL) ||
3386         ! DXAddArrayData(outPokesA,   0, nPokes, NULL) ||
3387         ! DXAddArrayData(outPicksA,   0, nPokes, NULL) ||
3388         ! DXAddArrayData(outPathsA,   0, pathSz, NULL) ||
3389         ! DXAddArrayData(outWeightsA, 0,   wtSz, NULL))
3390 	goto error;
3391 
3392     outPicks    = (int   *)DXGetArrayData(outPicksA);
3393     outPokes    = (int   *)DXGetArrayData(outPokesA);
3394     outPaths    = (int   *)DXGetArrayData(outPathsA);
3395     outPoints   = (float *)DXGetArrayData(outPointsA);
3396     outWeights  = (ubyte *)DXGetArrayData(outWeightsA);
3397 
3398     pathSz = 0; wtSz = 0; wts = (PickWts *)inWeights;
3399     for (i = 0; i < nPokes; i++)
3400     {
3401 	int poke = inPokes[i];
3402 	int pick = inPicks[poke];
3403 	int np  = ((i == nPokes-1) ? nPicks  : inPokes[i+1]) - poke;
3404 	int psz = ((poke == nPicks-1) ? nPaths : inPicks[poke+1]) - pick;
3405 	int k;
3406 
3407 	memcpy((char *)outPoints, (char *)(inPoints+3*poke), 3*sizeof(float));
3408 	outPoints += 3;
3409 
3410 	outPokes[i] = i;
3411 	outPicks[i] = pathSz;
3412 
3413 	memcpy((char *)outPaths, (char *)(inPaths+pick), psz*sizeof(int));
3414 	outPaths += psz;
3415 	pathSz   += psz;
3416 
3417 	for (k = 0; k < np; k++)
3418 	{
3419 	    int n = wts->nweights;
3420 	    int wsz = sizeof(PickWts) + n*sizeof(float);
3421 
3422 	    if (k == 0)
3423 	    {
3424 		memcpy((char *)outWeights, (char *)wts, wsz);
3425 		outWeights += wsz;
3426 	    }
3427 
3428 	    wts = (PickWts *)(((ubyte *)wts) + wsz);
3429 	}
3430     }
3431 
3432     newField = DXNewField();
3433     if (! newField)
3434 	goto error;
3435 
3436     if ((! DXSetStringAttribute((Object)outPointsA,  "dep", "positions"))   ||
3437         (! DXSetStringAttribute((Object)outPokesA,   "ref", "picks"))       ||
3438         (! DXSetStringAttribute((Object)outPicksA,   "dep", "positions"))   ||
3439         (! DXSetStringAttribute((Object)outPicksA,   "ref", "pick paths")))
3440 	goto error;
3441 
3442     if (! DXSetComponentValue(newField, "positions", (Object)outPointsA))
3443 	goto error;
3444     outPointsA = NULL;
3445 
3446     if (! DXSetComponentValue(newField, "pokes", (Object)outPokesA))
3447 	goto error;
3448     outPokesA = NULL;
3449 
3450     if (! DXSetComponentValue(newField, "picks", (Object)outPicksA))
3451 	goto error;
3452     outPicksA = NULL;
3453 
3454     if (! DXSetComponentValue(newField, "pick paths", (Object)outPathsA))
3455 	goto error;
3456     outPathsA = NULL;
3457 
3458     if (! DXSetComponentValue(newField, "pick weights", (Object)outWeightsA))
3459 	goto error;
3460     outPathsA = NULL;
3461 
3462     return newField;
3463 
3464 error:
3465     DXDelete((Object)outWeightsA);
3466     DXDelete((Object)outPointsA);
3467     DXDelete((Object)outPicksA);
3468     DXDelete((Object)outPokesA);
3469     DXDelete((Object)outPathsA);
3470     DXDelete((Object)newField);
3471     return NULL;
3472 }
3473 
3474 static Error PickTraverse(Object, Matrix *, Point,
3475 			int *, int, int, int, Field, int, int, float *);
3476 
3477 static Error
PickInterpolate(Object object,Field picks,int flag)3478 PickInterpolate(Object object, Field picks, int flag)
3479 {
3480     Array array, wArray = NULL;
3481     PickWts *wPtr;
3482     Point *point;
3483     int npokes, npicks;
3484     int i, j;
3485 
3486     if (DXEmptyField(picks))
3487 	return OK;
3488 
3489     array = (Array)DXGetComponentValue(picks, "positions");
3490     point = (Point *)DXGetArrayData(array);
3491 
3492     wArray = (Array)DXGetComponentValue(picks, "pick weights");
3493     if (! wArray)
3494     {
3495 	DXSetError(ERROR_MISSING_DATA, "pick weights");
3496 	goto error;
3497     }
3498 
3499     DXReference((Object)wArray);
3500     DXDeleteComponent(picks, "pick weights");
3501 
3502     wPtr = (PickWts *)DXGetArrayData(wArray);
3503 
3504     if (! DXQueryPokeCount(picks, &npokes))
3505 	goto error;
3506 
3507     for (i = 0; i < npokes; i++)
3508     {
3509 
3510 	if (! DXQueryPickCount(picks, i, &npicks))
3511 	    goto error;
3512 
3513 	for (j = 0; j < npicks; j++, point++)
3514 	{
3515 	    int *path, elt, vert, length;
3516 	    float *wts = (float *)(wPtr + 1);
3517 
3518 	    if (! DXQueryPickPath(picks, i, j, &length, &path, &elt, &vert))
3519 		goto error;
3520 
3521 	    if (! PickTraverse(object, NULL, *point, path, length, elt,
3522 				vert, picks, flag, wPtr->segment, wts))
3523 		goto error;
3524 
3525 	    wPtr = (PickWts *)(((char *)wPtr)+sizeof(PickWts)+wPtr->nweights*sizeof(float));
3526 	}
3527     }
3528 
3529     DXDelete((Object)wArray);
3530     return OK;
3531 
3532 error:
3533     DXDelete((Object)wArray);
3534     return ERROR;
3535 }
3536 
3537 #define INTERPOLATE_PICK(type, rnd)					  \
3538 {									  \
3539     int i, j;								  \
3540     type *_dst = (type *)dstD;						  \
3541 									  \
3542     for (i = 0; i < nItems; i++)					  \
3543 	buf[i] = 0.0;							  \
3544     									  \
3545     for (i = 0; i < vPerE; i++)						  \
3546     {									  \
3547 	type *_src = ((type *)DXGetArrayData(srcA)) + element[i]*nItems;  \
3548 	for (j = 0; j < nItems; j++)					  \
3549 	    buf[j] += wts[i]*_src[j];					  \
3550     }									  \
3551     									  \
3552     for (i = 0; i < nItems; i++)					  \
3553 	_dst[i] = (type)(buf[i] + rnd);					  \
3554 }
3555 
3556 static Error
PickTraverse(Object object,Matrix * stack,Point xyz,int * path,int length,int eid,int vid,Field picks,int flag,int seg,float * wts)3557 PickTraverse(Object object, Matrix *stack, Point xyz,
3558     int *path, int length, int eid, int vid,
3559     Field picks, int flag, int seg, float *wts)
3560 {
3561     switch(DXGetObjectClass(object))
3562     {
3563 	case CLASS_FIELD:
3564 	{
3565 	    Field    f = (Field)object;
3566 	    Type     t;
3567 	    Category c;
3568 	    int      r, s[32];
3569 	    Array    srcA, dstA;
3570 	    Pointer  *srcD, *dstD;
3571 	    int      itemSize;
3572 	    int      dstL;
3573 	    int	     srcI;
3574 	    char     *name;
3575 	    int      index;
3576 	    char     *dstName;
3577 	    Object   attr;
3578 	    Array    eltsA, edgesA;
3579 	    int      vPerE;
3580 	    int	     *element=NULL;
3581 	    int      lineseg[2];
3582 
3583 	    if (length != 0)
3584 	    {
3585 		DXSetError(ERROR_INTERNAL,
3586 			"path continues past a field object");
3587 		goto error;
3588 	    }
3589 
3590 	    if (flag == INTERPOLATE)
3591 	    {
3592 		eltsA = (Array)DXGetComponentValue(f, "connections");
3593 		if (eltsA)
3594 		{
3595 		    DXGetArrayInfo(eltsA, NULL, NULL, NULL, NULL, &vPerE);
3596 		    element = ((int *)DXGetArrayData(eltsA)) + eid*vPerE;
3597 		}
3598 		else
3599 		{
3600 		    eltsA = (Array)DXGetComponentValue(f, "polylines");
3601 		    if (eltsA)
3602 		    {
3603 			int estart, eend, *polylines, *edges;
3604 			int nP, nE;
3605 
3606 			edgesA = (Array)DXGetComponentValue(f, "edges");
3607 
3608 			polylines = (int *)DXGetArrayData(eltsA);
3609 			edges     = (int *)DXGetArrayData(edgesA);
3610 
3611 			DXGetArrayInfo(eltsA,  &nP, NULL, NULL, NULL, NULL);
3612 			DXGetArrayInfo(edgesA, &nE, NULL, NULL, NULL, NULL);
3613 
3614 			estart = polylines[eid];
3615 			eend   = (eid == nP-1) ? nE : polylines[eid+1];
3616 
3617 			lineseg[0] = edges[estart + seg];
3618 			if (estart + seg + 1 == eend)
3619 			    lineseg[1] = edges[estart];
3620 			else
3621 			    lineseg[1] = edges[estart + seg + 1];
3622 
3623 			element = lineseg;
3624 			vPerE = 2;
3625 		    }
3626 		    else
3627 		    {
3628 			flag = CLOSEST_VERTEX;
3629 		    }
3630 		}
3631 	    }
3632 
3633 	    for (index = 0;
3634 	         NULL != (srcA =
3635 			(Array)DXGetEnumeratedComponentValue(f, index, &name));
3636 		 index ++)
3637 	    {
3638 		char *dependency;
3639 
3640 		attr = DXGetAttribute((Object)srcA, "dep");
3641 		if (! attr)
3642 		    continue;
3643 
3644 		dependency = DXGetString((String)attr);
3645 
3646 		if (! strcmp(name, "positions"))
3647 		    dstName = "closest vertex";
3648 		else
3649 		    dstName = name;
3650 
3651 		DXGetArrayInfo(srcA, NULL, &t, &c, &r, s);
3652 
3653 		dstA = (Array)DXGetComponentValue(picks, dstName);
3654 		if (! dstA)
3655 		{
3656 
3657 		    dstA = DXNewArrayV(t, c, r, s);
3658 		    if (! dstA)
3659 			goto error;
3660 
3661 		    if (! DXSetAttribute((Object)dstA, "dep",
3662 					(Object)DXNewString("positions")))
3663 			goto error;
3664 
3665 		    if (! DXSetComponentValue(picks, dstName, (Object)dstA))
3666 			goto error;
3667 		}
3668 		else
3669 		    if (! DXTypeCheckV(dstA, t, c, r, s))
3670 		    {
3671 			DXSetError(ERROR_DATA_INVALID,
3672 			    "mismatched data components");
3673 			goto error;
3674 		    }
3675 
3676 		DXGetArrayInfo(dstA, &dstL, NULL, NULL, NULL, NULL);
3677 
3678 		if (! DXAddArrayData(dstA, dstL, 1, NULL))
3679 		    goto error;
3680 
3681 		itemSize = DXGetItemSize(dstA);
3682 
3683 		dstD = (Pointer)((char *)DXGetArrayData(dstA)
3684 							+ dstL*itemSize);
3685 
3686 		if (flag == CLOSEST_VERTEX ||
3687 		    !strcmp(dependency, "connections") ||
3688 		    !strcmp(dependency, "faces") ||
3689 		    !strcmp(dependency, "polylines") ||
3690 		    !strcmp(dstName, "closest vertex"))
3691 		{
3692 		    if (! strcmp(dependency, "connections") ||
3693 			! strcmp(dependency, "polylines") ||
3694 			! strcmp(dependency, "faces"))
3695 			srcI = eid;
3696 		    else
3697 			srcI = vid;
3698 
3699 		    srcD = (Pointer)((char *)DXGetArrayData(srcA)
3700 						    + srcI*itemSize);
3701 
3702 		    memcpy(dstD, srcD, itemSize);
3703 		}
3704 		else if (flag == INTERPOLATE)
3705 		{
3706 		    int nItems = DXGetItemSize(dstA) / DXTypeSize(t);
3707 		    float *buf = (float *)DXAllocate(nItems*sizeof(float));
3708 		    if (! buf)
3709 			goto error;
3710 
3711 		    switch(t)
3712 		    {
3713 			case TYPE_DOUBLE: INTERPOLATE_PICK(double,0.0); break;
3714 			case TYPE_FLOAT:  INTERPOLATE_PICK(float,0.0);  break;
3715 			case TYPE_INT:    INTERPOLATE_PICK(int,0.5);    break;
3716 			case TYPE_UINT:   INTERPOLATE_PICK(uint,0.5);   break;
3717 			case TYPE_SHORT:  INTERPOLATE_PICK(short,0.5);  break;
3718 			case TYPE_USHORT: INTERPOLATE_PICK(ushort,0.5); break;
3719 			case TYPE_BYTE:   INTERPOLATE_PICK(byte,0.5);   break;
3720 			case TYPE_UBYTE:  INTERPOLATE_PICK(ubyte,0.5);  break;
3721 		    	default: break;
3722 		    }
3723 
3724 		    DXFree((Pointer)buf);
3725 		}
3726 	    }
3727 
3728 
3729 	    return OK;
3730 	}
3731 
3732 	case CLASS_GROUP:
3733 	{
3734 	    Group g = (Group)object;
3735 
3736 	    object = DXGetEnumeratedMember(g, *path, NULL);
3737 	    if (! object)
3738 	    {
3739 		DXSetError(ERROR_DATA_INVALID,
3740 			"pick path references non-existent group member");
3741 		goto error;
3742 	    }
3743 
3744 	    return PickTraverse(object, stack, xyz, path+1, length-1,
3745 				eid, vid, picks, flag, seg, wts);
3746 	}
3747 
3748 	case CLASS_XFORM:
3749 	{
3750 	    Matrix m;
3751 	    Xform x = (Xform)object;
3752 
3753 	    if (*path != 0)
3754 	    {
3755 		DXSetError(ERROR_DATA_INVALID,
3756 			"pick path references invalid xform child");
3757 		goto error;
3758 	    }
3759 
3760 	    DXGetXformInfo(x, &object, &m);
3761 	    if (stack)
3762 	    {
3763 		Matrix p = DXConcatenate(m, *stack);
3764 		return PickTraverse(object, &p, xyz, path+1, length-1,
3765 				    eid, vid, picks, flag, seg, wts);
3766 	    }
3767 	    else
3768 		return PickTraverse(object, &m, xyz, path+1, length-1,
3769 				    eid, vid, picks, flag, seg, wts);
3770 	}
3771 
3772 	case CLASS_CLIPPED:
3773 	{
3774 	    Clipped c = (Clipped)object;
3775 
3776 	    if (*path != 0)
3777 	    {
3778 		DXSetError(ERROR_DATA_INVALID,
3779 			"pick path references invalid Clipped child");
3780 		goto error;
3781 	    }
3782 
3783 	    DXGetClippedInfo(c, &object, NULL);
3784 
3785 	    return PickTraverse(object, stack, xyz, path+1, length-1,
3786 					eid, vid, picks, flag, seg, wts);
3787 	}
3788 
3789 	case CLASS_SCREEN:
3790 	{
3791 	    Screen s = (Screen)object;
3792 
3793 	    if (*path != 0)
3794 	    {
3795 		DXSetError(ERROR_DATA_INVALID,
3796 			"pick path references invalid Screen child");
3797 		goto error;
3798 	    }
3799 
3800 	    DXGetScreenInfo(s, &object, NULL, NULL);
3801 
3802 	    return PickTraverse(object, stack, xyz, path+1, length-1,
3803 					eid, vid, picks, flag, seg, wts);
3804 	}
3805 
3806 	default:
3807 	{
3808 	    DXSetError(ERROR_BAD_CLASS,
3809 		"invalid object encountered in traversal");
3810 	    goto error;
3811 	}
3812     }
3813 
3814 error:
3815     return ERROR;
3816 }
3817 
3818 
3819 #if 0
3820 static Error
3821 GetAAMatrix(Object o, Matrix *r)
3822 {
3823     Matrix p, m;
3824 
3825     if (DXGetObjectClass(o) != CLASS_XFORM)
3826 	goto error;
3827 
3828     DXGetXformInfo((Xform)o, &o, &p);
3829 
3830     if (DXGetObjectClass(o) != CLASS_XFORM)
3831 	goto error;
3832 
3833     DXGetXformInfo((Xform)o, &o, &m);
3834 
3835     p = DXConcatenate(m, p);
3836 
3837     if (DXGetObjectClass(o) != CLASS_GROUP ||
3838 	DXGetGroupClass((Group)o) != CLASS_GROUP)
3839 	goto error;
3840 
3841     o = DXGetEnumeratedMember((Group)o, 1, NULL);
3842 
3843     if (DXGetObjectClass(o) != CLASS_XFORM)
3844 	goto error;
3845 
3846     DXGetXformInfo((Xform)o, &o, &m);
3847 
3848     *r = DXConcatenate(m, p);
3849 
3850     return OK;
3851 
3852 error:
3853     DXSetError(ERROR_DATA_INVALID,
3854 	"unrecognized object in AutoAxes object header");
3855     return ERROR;
3856 }
3857 #endif
3858 
3859 static Array
getXY(Array in)3860 getXY(Array in)
3861 {
3862     int i, nIn, nOut, *dataOut;
3863     DXEvents dataIn;
3864     Type t;
3865     Category c;
3866     int rank, nDim;
3867     Array out = NULL;
3868 
3869     if (DXGetObjectClass((Object)in) != CLASS_ARRAY)
3870     {
3871 	DXSetError(ERROR_DATA_INVALID, "pick list must be class ARRAY");
3872 	return NULL;
3873     }
3874 
3875     DXGetArrayInfo(in, &nIn, &t, &c, &rank, &nDim);
3876 
3877     if (t != TYPE_INT || c != CATEGORY_REAL || rank != 1)
3878     {
3879 	DXSetError(ERROR_DATA_INVALID,
3880 		"pick list array of real integer vectors");
3881 	return NULL;
3882     }
3883 
3884     if (nDim == 2)
3885 	return in;
3886 
3887     dataIn = (DXEvents)DXGetArrayData(in);
3888     for (i = nOut = 0; i < nIn; i++)
3889     {
3890 	int b = WHICH_BUTTON(&dataIn[i]);
3891 	if (b != NO_BUTTON && dataIn[i].mouse.state == BUTTON_DOWN)
3892 	    nOut++;
3893     }
3894 
3895     if (nOut == 0)
3896 	return NULL;
3897 
3898     out = DXNewArray(TYPE_INT, CATEGORY_REAL, 1, 2);
3899     if (! out)
3900 	return NULL;
3901 
3902     if (! DXAddArrayData(out, 0, nOut, NULL))
3903     {
3904 	DXDelete((Object)out);
3905 	return NULL;
3906     }
3907 
3908     dataIn = (DXEvents)DXGetArrayData(in);
3909     dataOut = (int *)DXGetArrayData(out);
3910     for (i = 0; i < nIn; i++)
3911     {
3912 	int b = WHICH_BUTTON(&dataIn[i]);
3913 	if (b != NO_BUTTON && dataIn[i].mouse.state == BUTTON_DOWN)
3914 	{
3915 	    dataOut[0] = dataIn[i].mouse.x;
3916 	    dataOut[1] = dataIn[i].mouse.y;
3917 	    dataOut += 2;
3918 	}
3919     }
3920 
3921     return out;
3922 }
3923 
3924