View Javadoc

1   package org.lcsim.detector.solids;
2   
3   import hep.physics.matrix.BasicMatrix;
4   import hep.physics.vec.Hep3Vector;
5   import hep.physics.vec.VecOp;
6   
7   import java.util.List;
8   
9   /**
10   *
11   * @author tknelson
12   */
13  public class GeomOp3D
14  {
15      
16      public static final double DISTANCE_TOLERANCE = 1E-9;
17      public static final double ANGULAR_TOLERANCE = 1E-9;
18      
19      /**
20       * Creates a new instance of GeomOp3D
21       */
22      public GeomOp3D()
23      {
24      }
25      
26      // equivalence tests (objects are same within tolerances)
27      //=======================================================
28      public static boolean equals(Point3D point1, Point3D point2)
29      {
30          return intersects(point1, point2);
31      }
32      
33      public static boolean equals(Plane3D plane1, Plane3D plane2)
34      {
35  //        System.out.println("Equals returns: " + ( parallel(plane1,plane2) &&
36  //                VecOp.dot(plane1.getNormal(),plane2.getNormal()) > 0 &&
37  //                Math.abs(plane1.getDistance() - plane2.getDistance()) < DISTANCE_TOLERANCE));
38          
39          return ( parallel(plane1,plane2) &&
40                  VecOp.dot(plane1.getNormal(),plane2.getNormal()) > 0 &&
41                  Math.abs(plane1.getDistance() - plane2.getDistance()) < DISTANCE_TOLERANCE);
42      }
43      
44      // intersection tests
45      //===================
46      
47      // Point3D to X
48      //-----------
49      
50      /**
51       * point - point intersection test (are same point)
52       *
53       * @param   point1
54       * @param   point2
55       * @return  true if points are closer than DISTANCE_TOLERANCE
56       */
57      public static boolean intersects(Point3D point1, Point3D point2)
58      {
59          return (distanceBetween(point1,point2) < DISTANCE_TOLERANCE);
60      }
61      
62      /**
63       * point - line intersection test (point lies along line)
64       *
65       * @param   point
66       * @param   line
67       * @return  true if point is closer to line than DISTANCE_TOLERANCE
68       */
69      public static boolean intersects(Point3D point, Line3D line)
70      {
71          return (distanceBetween(point,line) < DISTANCE_TOLERANCE );
72      }
73      
74      /**
75       * point - linesegment intersection test (point lies along linesegment)
76       *
77       * @param   point
78       * @param   linesegment
79       * @return  true if point is closer to linesegment than DISTANCE_TOLERANCE
80       */
81      public static boolean intersects(Point3D point, LineSegment3D linesegment)
82      {
83          if ( intersects(point,linesegment.getLine()) )
84          {
85              double length_at_point = VecOp.dot( VecOp.sub(point,linesegment.getStartPoint()), linesegment.getDirection() );
86              return ( (length_at_point + DISTANCE_TOLERANCE) > 0 && (length_at_point - DISTANCE_TOLERANCE < linesegment.getLength()) );
87          }
88          else
89          {
90              return false;
91          }
92      }
93      
94      /**
95       * point - plane intersection test (point lies on plane)
96       *
97       * @param   point
98       * @param   plane
99       * @return  true if point is closer to plane than DISTANCE_TOLERANCE
100      */
101     public static boolean intersects(Point3D point, Plane3D plane)
102     {
103         return ( Math.abs(distanceBetween(point,plane)) < DISTANCE_TOLERANCE);
104     }
105     
106     /**
107      * point - polygon intersection test (point lies on polygon)
108      *
109      * @param   point
110      * @param   polygon
111      * @return  true if point is closer to polygon than DISTANCE_TOLERANCE
112      */
113     // (this is better optimized than using distanceBetween)
114     public static boolean intersects(Point3D point, Polygon3D polygon)
115     {
116         List<Point3D> vertices = polygon.getClosedVertices();
117         
118         double angle_sum = 0.0;
119         for (int ivertex = 0; ivertex < vertices.size()-1; ivertex++)
120         {
121             Hep3Vector v1_vec = VecOp.sub(vertices.get(ivertex),point);
122             Hep3Vector v2_vec = VecOp.sub(vertices.get(ivertex+1),point);
123             
124             double v1_mag = v1_vec.magnitude();
125             double v2_mag = v2_vec.magnitude();
126             
127             if (v1_mag < DISTANCE_TOLERANCE || v2_mag < DISTANCE_TOLERANCE)
128             {
129                 return true;
130             }
131             else
132             {
133                 angle_sum += Math.acos(VecOp.dot(v1_vec,v2_vec)/(v1_mag*v2_mag));
134             }
135         }
136         
137         if ( Math.abs(angle_sum - 2*Math.PI) < polygon.nSides()*ANGULAR_TOLERANCE && intersects(point,polygon.getPlane()))
138         {
139             return true;
140         }
141         else
142         {
143             return false;
144         }
145     }
146     
147     //Line3D to X
148     //-----------
149     
150     /**
151      * line - line intersection test
152      *
153      * @param   line1
154      * @param   line2
155      * @return  true if lines pass closer than DISTANCE_TOLERANCE
156      */
157     public static boolean intersects(Line3D line1, Line3D line2)
158     {
159         return (distanceBetween(line1, line2) < DISTANCE_TOLERANCE);
160     }
161     
162     /**
163      * line - linesegment intersection test
164      *
165      * @param   line
166      * @param   linesegment
167      * @return  true if line passes closer to linesegment than DISTANCE_TOLERANCE
168      */
169     public static boolean intersects(Line3D line, LineSegment3D linesegment)
170     {
171         return (distanceBetween(line, linesegment) < DISTANCE_TOLERANCE);
172     }
173     
174     /**
175      * line - plane intersection test
176      *
177      * @param   line
178      * @param   plane
179      * @return  true if line passes closer to plane than DISTANCE_TOLERANCE
180      */
181     public static boolean intersects(Line3D line, Plane3D plane)
182     {
183         return ( Math.abs(distanceBetween(line, plane)) < DISTANCE_TOLERANCE);
184     }
185     
186     /**
187      * line - polygon intersection test
188      *
189      * @param   line
190      * @param   polygon
191      * @return  true if line passes closer to polygon than DISTANCE_TOLERANCE
192      */
193     public static boolean intersects(Line3D line, Polygon3D polygon)
194     {
195         if (parallel(line,polygon.getPlane()))
196         {
197             if (intersects(line,polygon.getPlane())) // line is in plane
198             {
199                 for (LineSegment3D edge : polygon.getEdges())
200                 {
201                     if (intersects(line,edge)) return true;
202                 }
203                 return false;
204             }
205             else
206             {
207                 return false;
208             }
209         }
210         else
211         {
212             Point3D plane_intersection = intersection(line,polygon.getPlane());
213             return intersects(plane_intersection,polygon);
214         }
215     }
216     
217     
218     // Line segment to X
219     //------------------
220     
221     /**
222      * linesegment - linesegment intersection test
223      *
224      * @param   linesegment1
225      * @param   linesegment2
226      * @return  true if linesegments pass closer than DISTANCE_TOLERANCE
227      */
228     public static boolean intersects(LineSegment3D linesegment1, LineSegment3D linesegment2)
229     {
230         return (distanceBetween(linesegment1, linesegment2) < DISTANCE_TOLERANCE);
231     }
232     
233     /**
234      * linesegment - plane intersection test
235      *
236      * @param   linesegment
237      * @param   plane
238      * @return  true if linesegment passes closer to plane than DISTANCE_TOLERANCE
239      */
240     public static boolean intersects(LineSegment3D linesegment, Plane3D plane)
241     {
242         double start_distance = distanceBetween(linesegment.getStartPoint(),plane);
243         double end_distance = distanceBetween(linesegment.getEndPoint(),plane);
244         if (Math.abs(start_distance) < DISTANCE_TOLERANCE || Math.abs(end_distance) < DISTANCE_TOLERANCE)
245         {
246             return true;
247         }
248         else if (start_distance * end_distance < 0)
249         {
250             return true;
251         }
252         else
253         {
254             return false;
255         }
256     }
257     
258     /**
259      * linesegment - polygon intersection test
260      *
261      * @param   linesegment
262      * @param   plane
263      * @return  true if linesegment passes closer to polygon than DISTANCE_TOLERANCE
264      */
265     public static boolean intersects(LineSegment3D linesegment, Polygon3D polygon) // IS IMPLEMENTATION IN DISTANCEBETWEEN LINESEGMENT,POLYGON BETTER?
266     {
267         Line3D line = linesegment.getLine();
268         Plane3D plane = polygon.getPlane();
269         
270         if (parallel(line,plane))
271         {
272             if (intersects(line,plane)) // line is in plane
273             {
274                 for (LineSegment3D edge : polygon.getEdges())
275                 {
276                     if (intersects(linesegment,edge)) return true;
277                 }
278                 return (intersects(linesegment.getStartPoint(),polygon) || intersects(linesegment.getEndPoint(),polygon));
279             }
280             else
281             {
282                 return false;
283             }
284         }
285         else
286         {
287             Point3D line_plane_intersection = intersection(line,plane);
288             return (intersects(line_plane_intersection,polygon) && intersects(linesegment,plane));
289         }
290     }
291     
292     // Plane to X
293     //-----------
294     
295     /**
296      * plane - plane intersection test
297      *
298      * @param   plane1
299      * @param   plane2
300      * @return  true if planes pass closer than DISTANCE_TOLERANCE
301      */
302     public static boolean intersects(Plane3D plane1, Plane3D plane2)
303     {
304         return (Math.abs(distanceBetween(plane1,plane2)) < DISTANCE_TOLERANCE);
305     }
306     
307     /**
308      * plane - polygon intersection test
309      *
310      * @param   plane
311      * @param   polygon
312      * @return  true if plane passes closer to polygon than DISTANCE_TOLERANCE
313      */
314     public static boolean intersects(Plane3D plane, Polygon3D polygon)
315     {
316         boolean pos = false;
317         boolean neg = false;
318         for (Point3D vertex : polygon.getVertices())
319         {
320             if ( VecOp.dot(vertex,polygon.getNormal()) - plane.getDistance() + DISTANCE_TOLERANCE > 0 ) pos = true;
321             if ( VecOp.dot(vertex,polygon.getNormal()) - plane.getDistance() - DISTANCE_TOLERANCE < 0 ) neg = true;
322             if (pos && neg) return true;
323         }
324         return false;
325     }
326     
327     // Polygon to X
328     //-------------
329     
330     /**
331      * polygon - polygon intersection test
332      *
333      * @param   polygon1
334      * @param   polygon2
335      * @return  true if polygons pass closer than DISTANCE_TOLERANCE
336      */
337     public static boolean intersects(Polygon3D polygon1, Polygon3D polygon2)
338     {
339         for (LineSegment3D edge : polygon1.getEdges())
340         {
341             if (intersects(edge,polygon2)) return true;
342         }
343         return false;
344     }
345     
346     // Nearest distance between objects
347     //=================================
348     
349     // Point3D to X
350     //--------------
351     
352     /**
353      * point - point distance
354      *
355      * @param   point1
356      * @param   point2
357      * @return  distance between points
358      */
359     public static double distanceBetween(Point3D point1, Point3D point2)
360     {
361         return VecOp.sub(point2,point1).magnitude();
362     }
363     
364     /**
365      * point - line distance
366      *
367      * @param   point
368      * @param   line
369      * @return  closest distance between point and line
370      */
371     public static double distanceBetween(Point3D point, Line3D line)
372     {
373         return VecOp.cross(line.getDirection(),VecOp.sub(point,line.getStartPoint())).magnitude();
374     }
375     
376     /**
377      * point - linesegment distance
378      *
379      * @param   point
380      * @param   linesegment
381      * @return  closest distance between point and linesegment
382      */
383     public static double distanceBetween(Point3D point, LineSegment3D linesegment)
384     {
385         Hep3Vector diff = VecOp.sub(point,linesegment.getStartPoint());
386         double dot = VecOp.dot(diff,linesegment.getDirection());
387         if (dot <= 0) return distanceBetween(point,linesegment.getStartPoint());
388         else if (dot >= linesegment.getLength()) return distanceBetween(point,linesegment.getEndPoint());
389         else return distanceBetween(point,linesegment.getLine());
390     }
391     
392     /**
393      * point - plane distance
394      *
395      * @param   point
396      * @param   line
397      * @return  closest distance between point and plane (signed in direciton of normal to plane)
398      */
399     public static double distanceBetween(Point3D point, Plane3D plane)
400     {
401         return VecOp.dot(point,plane.getNormal())-plane.getDistance();
402     }
403     
404     /**
405      * point - polygon distance
406      *
407      * @param   point
408      * @param   polygon
409      * @return  closest distance between point and polygon
410      */
411     public static double distanceBetween(Point3D point, Polygon3D polygon)  // FIXME (there is a bug here ... getting negative values)
412     {
413         double distance = Double.POSITIVE_INFINITY;
414         
415         double point_plane_distance = distanceBetween(point,polygon.getPlane());
416         Point3D projected_point = new Point3D(VecOp.sub(point,VecOp.mult(point_plane_distance,polygon.getPlane().getNormal())));
417         
418         if (intersects(projected_point,polygon))
419         {
420             distance = Math.min(distance,point_plane_distance);
421         }
422         else
423         {
424             for (LineSegment3D edge : polygon.getEdges())
425             {
426                 distance = Math.min(distanceBetween(point,edge),distance);
427             }
428         }
429         return distance;
430     }
431     
432     // Line3D to X
433     //----------
434     
435     /**
436      * line - line distance
437      *
438      * @param   line1
439      * @param   line2
440      * @return  closest distance between lines
441      */
442     public static double distanceBetween(Line3D line1, Line3D line2)
443     {
444         Hep3Vector start_diff = VecOp.sub(line2.getStartPoint(),line1.getStartPoint());
445         if (parallel(line1,line2))
446         {
447             return VecOp.cross(start_diff,line1.getDirection()).magnitude();
448         }
449         else
450         {
451             Hep3Vector normal = VecOp.cross(line1.getDirection(),line2.getDirection());
452             double denominator = normal.magnitude();
453             return Math.abs(VecOp.dot(normal,start_diff)/denominator);
454         }
455     }
456     
457     /**
458      * line - linesegment distance
459      *
460      * @param   line
461      * @param   linesegment
462      * @return  closest distance between line and linesegment
463      */
464     public static double distanceBetween(Line3D line, LineSegment3D linesegment)
465     {
466         Hep3Vector start_diff = VecOp.sub(linesegment.getStartPoint(),line.getStartPoint());
467         if (parallel(line,linesegment.getLine()))
468         {
469 //            System.out.println("Lines are parallel ");
470             return VecOp.cross(start_diff,line.getDirection()).magnitude();
471         }
472         else
473         {
474             double[] pca = linesPCA(line,linesegment);                         // FIXME (check usage here) (fixed?)
475             
476 //            System.out.println("pca returns: "+ pca[0]+", "+pca[1]);
477             
478             pca[1] = Math.max(0.0,pca[1]);
479             pca[1] = Math.min(linesegment.getLength(),pca[1]);
480             
481 //            System.out.println("closest point on line is: "+line.getEndPoint(pca[0]));
482 //            System.out.println("closest point on line segment is: "+linesegment.getLine().getEndPoint(pca[1]));
483             
484             return distanceBetween(line.getEndPoint(pca[0]),linesegment.getLine().getEndPoint(pca[1]));
485         }
486     }
487     
488     /**
489      * line - plane distance
490      *
491      * @param   line
492      * @param   plane
493      * @return  closest distance between line and plane (signed in direction of normal to plane)
494      */
495     public static double distanceBetween(Line3D line, Plane3D plane)
496     {
497         if (Math.abs(VecOp.dot(line.getDirection(),plane.getNormal())) > DISTANCE_TOLERANCE) return 0;
498         else return distanceBetween(line.getStartPoint(),plane);
499     }
500     
501     /**
502      * line - polygon distance
503      *
504      * @param   line
505      * @param   polygon
506      * @return  closest distance between line and polygon
507      */
508     public static double distanceBetween(Line3D line, Polygon3D polygon)
509     {
510         double distance = Double.POSITIVE_INFINITY;
511         
512         for (LineSegment3D edge : polygon.getEdges())
513         {
514             distance = Math.min(distance,distanceBetween(line,edge));
515         }
516         
517         Point3D plane_intersection = intersection(line,polygon.getPlane());
518         if (intersects(plane_intersection,polygon))
519         {
520             distance = 0;
521         }
522         return distance;
523     }
524     
525     
526     // LineSegment3D to X
527     //-----------------
528     
529     /**
530      * linesegment - linesegment distance
531      *
532      * @param   linesegment1
533      * @param   linesegment2
534      * @return  closest distance between linesegments
535      */
536     public static double distanceBetween(LineSegment3D linesegment1, LineSegment3D linesegment2)
537     {
538         Hep3Vector start_diff = VecOp.sub(linesegment2.getStartPoint(),linesegment1.getStartPoint());
539         if (parallel(linesegment1.getLine(),linesegment2.getLine()))
540         {
541             Hep3Vector end_diff = VecOp.sub(linesegment2.getEndPoint(),linesegment1.getEndPoint());
542             
543             double start_projection = VecOp.dot(start_diff,linesegment1.getDirection());
544             double end_projection = VecOp.dot(end_diff,linesegment1.getDirection());
545             if (start_projection < 0 && end_projection < 0)
546             {
547                 if (start_projection < end_projection)
548                 {
549                     return distanceBetween(linesegment1.getStartPoint(),linesegment2.getEndPoint());
550                 }
551                 else
552                 {
553                     return distanceBetween(linesegment1.getStartPoint(),linesegment2.getStartPoint());
554                 }
555             }
556             else if (start_projection > linesegment1.getLength() && end_projection > linesegment1.getLength())
557             {
558                 if (start_projection > end_projection)
559                 {
560                     return distanceBetween(linesegment1.getEndPoint(),linesegment2.getEndPoint());
561                 }
562                 else
563                 {
564                     return distanceBetween(linesegment1.getEndPoint(),linesegment2.getEndPoint());
565                 }
566             }
567             else
568             {
569                 return VecOp.cross(start_diff,linesegment1.getDirection()).magnitude();
570             }
571         }
572         else
573         {
574             double[] pca = linesPCA(linesegment1,linesegment2);                         // FIXME (check usage here) (fixed?)
575             
576             pca[0] = Math.max(0.0,pca[0]);
577             pca[0] = Math.min(linesegment1.getLength(),pca[0]);
578             
579             pca[1] = Math.max(0.0,pca[1]);
580             pca[1] = Math.min(linesegment2.getLength(),pca[1]);
581             
582             return distanceBetween(linesegment1.getEndPoint(pca[0]),linesegment2.getEndPoint(pca[1]));
583         }
584         
585     }
586     
587     /**
588      * linesegment - plane distance
589      *
590      * @param   linesegment
591      * @param   plane
592      * @return  closest distance between linesegment and plane (signed in direction of normal to plane)
593      */
594     public static double distanceBetween(LineSegment3D linesegment, Plane3D plane)
595     {
596         if (intersects(linesegment,plane))
597         {
598             return 0;
599         }
600         else
601         {
602             return Math.min(distanceBetween(linesegment.getStartPoint(),plane),distanceBetween(linesegment.getEndPoint(),plane));
603         }
604     }
605     
606     /**
607      * linesegment - polygon distance
608      *
609      * @param   linesegment
610      * @param   polygon
611      * @return  closest distance between linesegment and polygon
612      */
613     public static double distanceBetween(LineSegment3D linesegment, Polygon3D polygon)
614     {
615         Line3D line = linesegment.getLine();
616         Plane3D plane = polygon.getPlane();
617         
618         // if not parallel check for line segment crossing polygon interior
619         if (!parallel(line,plane))
620         {
621             if (intersects(intersection(line,plane),polygon) && intersects(linesegment,polygon.getPlane())) return 0;
622         }
623         
624         double distance = Double.POSITIVE_INFINITY;
625         // line segments to edges of polygon
626         for (LineSegment3D edge : polygon.getEdges())
627         {
628             distance = Math.min(distance,distanceBetween(linesegment,edge));
629         }
630         
631         // line segment endpoints to interior of polygon
632         for (Point3D endpoint : linesegment.getPoints())
633         {
634             LineSegment3D closest_approach = lineBetween(endpoint,plane);
635             if (intersects(closest_approach.getEndPoint(),polygon))
636             {
637                 distance = Math.min(distance,closest_approach.getLength());
638             }
639         }
640         
641         return distance;
642     }
643     
644     
645     // Plane3D to X
646     //-----------
647     
648     /**
649      * plane - plane distance
650      *
651      * @param   plane1
652      * @param   plane2
653      * @return  closest distance between planes
654      */
655     public static double distanceBetween(Plane3D plane1, Plane3D plane2)  // FIXME!!!!!!!!!!!!!!
656     {
657         if ( parallel(plane1,plane2) )
658         {
659             return plane2.getDistance()-VecOp.dot(plane1.getNormal(),plane2.getNormal())*plane1.getDistance();
660         }
661         else
662         {
663             return 0;
664         }
665     }
666     
667     /**
668      * plane - polygon distance
669      *
670      * @param   plane
671      * @param   polygon
672      * @return  closest distance between plane and polygon (signed in direction of normal to plane)
673      */
674     public static double distanceBetween(Plane3D plane, Polygon3D polygon)
675     {
676         if (intersects(plane,polygon)) return 0;
677         
678         double abs_distance = Double.POSITIVE_INFINITY;
679         double distance = Double.POSITIVE_INFINITY;
680         
681         for (Point3D point : polygon.getVertices())
682         {
683             double vertex_distance = distanceBetween(point,plane);
684             if (Math.abs(vertex_distance) < abs_distance)
685             {
686                 abs_distance = Math.abs(vertex_distance);
687                 distance = vertex_distance;
688                 if (abs_distance < DISTANCE_TOLERANCE) return distance;
689             }
690         }
691         return distance;
692     }
693     
694     // Polygon3D to X
695     //-------------
696     
697     /**
698      * polygon - polygon distance
699      *
700      * @param   polygon1
701      * @param   polygon2
702      * @return  closest distance between polygons
703      */
704     // (this may be far from optimal)
705     public static double distanceBetween(Polygon3D polygon1, Polygon3D polygon2)
706     {
707         double distance = Double.POSITIVE_INFINITY;
708         for (LineSegment3D edge : polygon1.getEdges())
709         {
710             Math.min(distance,distanceBetween(edge,polygon2));
711             if (distance < DISTANCE_TOLERANCE) return 0;
712         }
713         for (LineSegment3D edge : polygon2.getEdges())
714         {
715             Math.min(distance,distanceBetween(edge,polygon1));
716             if (distance < DISTANCE_TOLERANCE) return 0;
717         }
718         return distance;
719     }
720     
721     // Intersections
722     //==============
723     
724     // Line3D - X
725     //------------
726     
727     // Polygon3D to X
728     //-------------
729     
730     /**
731      * line - plane intersection (must test for for parallelism)
732      *
733      * @param   line
734      * @param   plane
735      * @return  point of intersection between line and plane
736      */
737     public static Point3D intersection(Line3D line, Plane3D plane)
738     {
739         double distance_to_plane = -distanceBetween(line.getStartPoint(),plane);
740         double slope_to_plane = VecOp.dot(line.getDirection(),plane.getNormal());
741         if (slope_to_plane == 0)
742         {
743             throw new RuntimeException("Line is parallel to plane!  Please check first with parallel(Line line,Plane plane)");
744         }
745         else
746         {
747             return line.getEndPoint(distance_to_plane/slope_to_plane);
748         }
749     }
750     
751     // Plane3D - X
752     //----------
753     
754     /**
755      * plane - plane intersection (must test for for parallelism)
756      *
757      * @param   plane1
758      * @param   plane2
759      * @return  line of intersection between two planes
760      */
761     public static Line3D intersection(Plane3D plane1, Plane3D plane2)
762     {
763         Hep3Vector direction = VecOp.cross(plane1.getNormal(),plane2.getNormal());
764         
765         if (direction.magnitudeSquared() == 0)
766         {
767             throw new RuntimeException("Planes are parallel!  Please check first with parallel(Plane plane1,Plane plane2)");
768         }
769         
770         double dot_normals = VecOp.dot(plane1.getNormal(),plane2.getNormal());
771         
772         double determinant = 1 - Math.pow(dot_normals,2);
773         
774         double c1 = (plane1.getDistance()-plane2.getDistance()*dot_normals)/determinant;
775         double c2 = (plane2.getDistance()-plane1.getDistance()*dot_normals)/determinant;
776         
777         Point3D point = new Point3D( VecOp.add(VecOp.mult(c1,plane1.getNormal()), VecOp.mult(c2,plane2.getNormal()) ) );
778         
779         return new Line3D(point,direction);
780         
781     }
782     
783     
784     
785     // Line3D segments of closest approach
786     //==================================
787     
788     // Point3D to X
789     //-----------
790     
791     /**
792      * point - point closest approach (linesegment between two points)
793      *
794      * @param   point1
795      * @param   point2
796      * @return  linesegment between point1 and point2
797      */
798     public static LineSegment3D lineBetween(Point3D point1, Point3D point2)
799     {
800         return new LineSegment3D(point1, point2);
801     }
802     
803     /**
804      * point - line closest approach
805      *
806      * @param   point
807      * @param   line
808      * @return  shortest linesegment from point to line
809      */
810     public static LineSegment3D lineBetween(Point3D point, Line3D line)
811     {
812         Point3D closest_point = line.getEndPoint(VecOp.dot(VecOp.sub(point,line.getStartPoint()),line.getDirection()));
813         return new LineSegment3D(point,closest_point);
814     }
815     
816     /**
817      * point - linesegment closest approach
818      *
819      * @param   point
820      * @param   linesegment
821      * @return  shortest linesegment from point to linesegment
822      */
823     public static LineSegment3D lineBetween(Point3D point, LineSegment3D linesegment)
824     {
825         Hep3Vector diff = VecOp.sub(point,linesegment.getStartPoint());
826         double dot = VecOp.dot(diff,linesegment.getDirection());
827         if (dot <= 0) return new LineSegment3D(point,linesegment.getStartPoint());
828         else if (dot >= linesegment.getLength()) return new LineSegment3D(point,linesegment.getEndPoint());
829         else return new LineSegment3D(point,linesegment.getEndPoint(dot));
830     }
831     
832     /**
833      * point - plane closest approach
834      *
835      * @param   point
836      * @param   plane
837      * @return  shortest linesegment from point to plane
838      */
839     public static LineSegment3D lineBetween(Point3D point, Plane3D plane)
840     {
841         double distance = VecOp.dot(point,plane.getNormal())-plane.getDistance();
842         Point3D closest_point = new Point3D( VecOp.sub(point,VecOp.mult(distance,plane.getNormal())) );
843         return new LineSegment3D(point,closest_point);
844     }
845     
846     /**
847      * point - polygon closest approach
848      *
849      * @param   point
850      * @param   polygon
851      * @return  shortest linesegment from point to polygon
852      */
853     public static LineSegment3D lineBetween(Point3D point, Polygon3D polygon)
854     {
855         double point_plane_distance = distanceBetween(point,polygon.getPlane());
856         Point3D projected_point = new Point3D(VecOp.sub(point,VecOp.mult(point_plane_distance,polygon.getPlane().getNormal())));
857         
858         if (intersects(projected_point,polygon))
859         {
860             return new LineSegment3D(point,projected_point);
861         }
862         else
863         {
864             double distance = Double.POSITIVE_INFINITY;
865             LineSegment3D closest_edge = new LineSegment3D();
866             for (LineSegment3D edge : polygon.getEdges())
867             {
868                 if (distanceBetween(point,edge) < distance)
869                 {
870                     distance = distanceBetween(point,edge);
871                     closest_edge = edge;
872                 }
873             }
874             return lineBetween(point,closest_edge);
875         }
876     }
877     
878     // Line3D to X
879     //----------
880     
881     /**
882      * line - line closest approach (must check first for parallelism)
883      *
884      * @param   line1
885      * @param   line2
886      * @return  shortest linesegment from line1 to line2
887      */
888     public static LineSegment3D lineBetween(Line3D line1, Line3D line2)
889     {
890         if (parallel(line1,line2))
891         {
892             throw new RuntimeException("Lines are parallel!  Please check first with parallel(Line line1,Line line2)");
893         }
894         else
895         {
896             double pca[] = linesPCA(line1,line2);                         // FIXME (check usage here) (OK?)
897             return new LineSegment3D(line1.getEndPoint(pca[0]),line2.getEndPoint(pca[1]));
898         }
899     }
900     
901     /**
902      * line - linesegment closest approach (must check first for parallelism)
903      *
904      * @param   line
905      * @param   linesegment
906      * @return  shortest linesegment from line to linesegment
907      */
908     public static LineSegment3D lineBetween(Line3D line, LineSegment3D linesegment)
909     {
910         LineSegment3D line_between;
911         
912         if (parallel(line,linesegment.getLine()))
913         {
914             throw new RuntimeException("Line and line segment are parallel!  Please check first with parallel(Line line,LineSegment linesegment)");
915         }
916         else
917         {
918             double[] pca = linesPCA(line,linesegment);                         // FIXME (check usage here) (fixed?)
919             
920             if (pca[1]<0.0)
921             {
922                 line_between = lineBetween(linesegment.getStartPoint(),line);
923                 line_between.reverse();
924             }
925             else if (pca[1]>linesegment.getLength())
926             {
927                 line_between = lineBetween(linesegment.getEndPoint(),line);
928                 line_between.reverse();
929             }
930             else
931             {
932                 line_between = new LineSegment3D(line.getEndPoint(pca[0]),linesegment.getEndPoint(pca[1]));
933             }
934            
935             return line_between;            
936         }
937     }
938     
939     /**
940      * line - polygon closest approach (must check for for intersection)
941      *
942      * @param   line
943      * @param   polygon
944      * @return  shortest linesegment from line to polygon
945      */
946     public static LineSegment3D lineBetween(Line3D line, Polygon3D polygon)
947     {
948         Point3D plane_intersection = intersection(line,polygon.getPlane());
949         if (intersects(plane_intersection,polygon))
950         {
951             throw new RuntimeException("Line intersects polygon  Please check first with intersects(Line line, Polygon polygon)");
952         }
953         else
954         {
955             double distance = Double.POSITIVE_INFINITY;
956             LineSegment3D closest_edge = new LineSegment3D();
957             for (LineSegment3D edge : polygon.getEdges())
958             {
959                 if (distanceBetween(line,edge) < distance)
960                 {
961                     distance = distanceBetween(line,edge);
962                     closest_edge = edge;
963                 }
964             }
965             return lineBetween(line,closest_edge);
966         }
967     }
968     
969     // LineSegment3D to X
970     //-----------------
971     
972     /**
973      * linesegment - linesegment closest approach
974      *
975      * @param   linesegment1
976      * @param   linesegment2
977      * @return  shortest linesegment from linesegment1 to linesegment2
978      */
979     public static LineSegment3D lineBetween(LineSegment3D linesegment1, LineSegment3D linesegment2)
980     {
981         Hep3Vector start_diff = VecOp.sub(linesegment2.getStartPoint(),linesegment1.getStartPoint());
982         if (parallel(linesegment1.getLine(),linesegment2.getLine()))
983         {
984             Hep3Vector end_diff = VecOp.sub(linesegment2.getEndPoint(),linesegment1.getEndPoint());
985             
986             double start_projection = VecOp.dot(start_diff,linesegment1.getDirection());
987             double end_projection = VecOp.dot(end_diff,linesegment1.getDirection());
988             if (start_projection < 0 && end_projection < 0)
989             {
990                 if (start_projection < end_projection)
991                 {
992                     return new LineSegment3D(linesegment1.getStartPoint(),linesegment2.getEndPoint());
993                 }
994                 else
995                 {
996                     return new LineSegment3D(linesegment1.getStartPoint(),linesegment2.getStartPoint());
997                 }
998             }
999             else if (start_projection > linesegment1.getLength() && end_projection > linesegment1.getLength())
1000             {
1001                 if (start_projection > end_projection)
1002                 {
1003                     return new LineSegment3D(linesegment1.getEndPoint(),linesegment2.getEndPoint());
1004                 }
1005                 else
1006                 {
1007                     return new LineSegment3D(linesegment1.getEndPoint(),linesegment2.getEndPoint());
1008                 }
1009             }
1010             else
1011             {
1012                 double startpoint1 = Math.max(0,start_projection);
1013                 double endpoint1 = Math.min(linesegment1.getLength(),end_projection);
1014                 Point3D origin = linesegment1.getEndPoint((startpoint1+endpoint1)/2);
1015                 
1016                 double startpoint2 = (start_projection-startpoint1)/2;
1017                 double endpoint2 = (end_projection-endpoint1)/2;
1018                 Point3D destination = linesegment2.getEndPoint((startpoint2+endpoint2)/2);
1019                 
1020                 return new LineSegment3D(origin,destination);
1021             }
1022         }
1023         else
1024         {
1025             double[] pca = linesPCA(linesegment1,linesegment2);                         // FIXME (check usage here) (fixed?)
1026             pca[0] = Math.max(0.0,pca[0]);
1027             pca[0] = Math.min(linesegment1.getLength(),pca[0]);
1028             
1029             pca[1] = Math.max(0.0,pca[1]);
1030             pca[1] = Math.min(linesegment2.getLength(),pca[1]);
1031             
1032             return new LineSegment3D(linesegment1.getEndPoint(pca[0]),linesegment2.getEndPoint(pca[1]));
1033         }
1034         
1035     }
1036     
1037     /**
1038      * linesegment - plane closest approach (must check first for intersection)
1039      *
1040      * @param   linesegment
1041      * @param   plane
1042      * @return  shortest linesegment from linesegment to plane
1043      */
1044     public static LineSegment3D lineBetween(LineSegment3D linesegment, Plane3D plane)
1045     {
1046         if (intersects(linesegment,plane))
1047         {
1048             throw new RuntimeException("Line segment intersects plane.  Please check first with intersects(LineSegment linesegment, Plane plane)");
1049         }
1050         else if (parallel(linesegment,plane))
1051         {
1052             return lineBetween(linesegment.getEndPoint(linesegment.getLength()/2),plane);
1053         }
1054         else
1055         {
1056             if (distanceBetween(linesegment.getStartPoint(),plane) < distanceBetween(linesegment.getEndPoint(),plane))
1057             {
1058                 return lineBetween(linesegment.getStartPoint(),plane);
1059             }
1060             else
1061             {
1062                 return lineBetween(linesegment.getEndPoint(),plane);
1063             }
1064         }
1065     }
1066     
1067     /**
1068      * linesegment - polygon closest approach (must check first for intersection)
1069      *
1070      * @param   linesegment
1071      * @param   polygon
1072      * @return  shortest linesegment from linesegment to polygon
1073      */
1074     public static LineSegment3D lineBetween(LineSegment3D linesegment, Polygon3D polygon)
1075     {
1076         Line3D line = linesegment.getLine();
1077         Plane3D plane = polygon.getPlane();
1078         
1079         // if not parallel check for intersection of segment with polygon interior
1080         if (!parallel(line,plane))
1081         {
1082             if (intersects(intersection(line,plane),polygon) && intersects(linesegment,polygon.getPlane()))
1083             {
1084                 throw new RuntimeException("Line segment intersects polygon.  Please check first with intersects(LineSegment linesegment, Polygon polygon)");
1085             }
1086         }
1087         
1088         // line segments to edges of polygon
1089         double edge_distance = Double.POSITIVE_INFINITY;
1090         LineSegment3D closest_edge = new LineSegment3D();
1091         for (LineSegment3D edge : polygon.getEdges())
1092         {
1093             if (distanceBetween(linesegment,edge) < edge_distance)
1094             {
1095                 edge_distance = distanceBetween(linesegment,edge);
1096                 closest_edge = edge;
1097             }
1098         }
1099         
1100         double endpoint_distance = edge_distance;
1101         LineSegment3D closest_planar_approach = new LineSegment3D();
1102         for (Point3D endpoint : linesegment.getPoints())
1103         {
1104             LineSegment3D closest_approach = lineBetween(endpoint,plane);
1105             if (closest_approach.getLength() < endpoint_distance && intersects(closest_approach.getEndPoint(),polygon))
1106             {
1107                 endpoint_distance = closest_approach.getLength();
1108                 closest_planar_approach = closest_approach;
1109             }
1110         }
1111         
1112         if (endpoint_distance < edge_distance)
1113         {
1114             return closest_planar_approach;
1115         }
1116         else
1117         {
1118             return lineBetween(linesegment,closest_edge);
1119         }
1120     }
1121     
1122     // Plane3D to X
1123     //-----------
1124     
1125     /**
1126      * plane - polygon closest approach (must check first for intersection)
1127      *
1128      * @param   plane
1129      * @param   polygon
1130      * @return  shortest linesegment from plane to polygon
1131      */
1132     public static LineSegment3D lineBetween(Plane3D plane, Polygon3D polygon)
1133     {
1134         if (intersects(plane,polygon))
1135         {
1136             throw new RuntimeException("Plane intersects polygon.  Please check first with intersects(Plane plane, Polygon polygon)");
1137         }
1138         
1139         double distance = Double.POSITIVE_INFINITY;
1140         LineSegment3D closest_edge = new LineSegment3D();
1141         for (LineSegment3D edge : polygon.getEdges())
1142         {
1143             if (distanceBetween(edge,plane) < distance)
1144             {
1145                 distance = distanceBetween(edge,plane);
1146                 closest_edge = edge;
1147             }
1148         }
1149         return lineBetween(closest_edge,plane);
1150     }
1151     
1152     
1153     // Polygon3D to X
1154     //-------------
1155     
1156     /**
1157      * polygon - polygon closest approach
1158      *
1159      * @param   polygon1
1160      * @param   polygon2
1161      * @return  shortest linesegment from polygon1 to polygon2
1162      */
1163     // does not deliver optimal solution for parallel polygons that are adjacent
1164     public static LineSegment3D lineBetween(Polygon3D polygon1, Polygon3D polygon2)
1165     {
1166         double distance = Double.POSITIVE_INFINITY;
1167         LineSegment3D closest_edge = new LineSegment3D();
1168         
1169         Plane3D plane = polygon2.getPlane();
1170         for (LineSegment3D edge : polygon1.getEdges())
1171         {
1172             if (distanceBetween(edge,plane) < distance)
1173             {
1174                 distance = distanceBetween(edge,plane);
1175                 closest_edge = edge;
1176             }
1177         }
1178         if (distance < DISTANCE_TOLERANCE)
1179         {
1180             throw new RuntimeException("Polygons intersect.  Please check first with intersects(Polygon polygon1, Polygon polygon2)");
1181         }
1182         
1183         plane = polygon1.getPlane();
1184         for (LineSegment3D edge : polygon2.getEdges())
1185         {
1186             if (distanceBetween(edge,plane) < distance)
1187             {
1188                 distance = distanceBetween(edge,plane);
1189                 closest_edge = edge;
1190             }
1191         }
1192         if (distance < DISTANCE_TOLERANCE)
1193         {
1194             throw new RuntimeException("Polygons intersect.  Please check first with intersects(Polygon polygon1, Polygon polygon2)");
1195         }
1196         
1197         return lineBetween(closest_edge,plane);
1198         
1199     }
1200     
1201     
1202     // Coplanarity and collinearity
1203     //=============================
1204     
1205     /**
1206      * coplanarity test for a list of points
1207      *
1208      * @param   points
1209      * @return  true if points are coplanar within DISTANCE_TOLERANCE
1210      */
1211     public static boolean coplanar(List<Point3D> points)
1212     {
1213         if (points.size()<4) return true;
1214         if (collinear(points)) return true;
1215         
1216         Hep3Vector v1 = VecOp.sub(points.get(1),points.get(0));
1217         Hep3Vector v2 = VecOp.sub(points.get(2),points.get(1));
1218         
1219         // normal and distance
1220         Hep3Vector normal = VecOp.unit(VecOp.cross(v1,v2));
1221         
1222         for (int ipoint = 3; ipoint < points.size(); ipoint++)
1223         {
1224             Hep3Vector vtest = VecOp.sub(points.get(ipoint),points.get(ipoint-1));
1225             if (VecOp.dot(vtest,normal) > DISTANCE_TOLERANCE) return false;
1226         }
1227         
1228         return true;
1229     }
1230     
1231     /**
1232      * coplanarity test for a line and plane
1233      *
1234      * @param   line
1235      * @param   plane
1236      * @return  true if line is in plane within DISTANCE_TOLERANCE
1237      */
1238     public static boolean coplanar(Line3D line, Plane3D plane)
1239     {
1240         return (parallel(line,plane) && intersects(line.getStartPoint(),plane));
1241     }
1242     
1243     /**
1244      * collinearity test for a list of points
1245      *
1246      * @param   points
1247      * @return  true if points are collinear within DISTANCE_TOLERANCE
1248      */
1249     public static boolean collinear(List<Point3D> points)
1250     {
1251         if (points.size()<3) return true;
1252         
1253         Hep3Vector direction = VecOp.unit(VecOp.sub(points.get(1),points.get(0)));
1254         
1255         for (int ipoint = 2; ipoint < points.size(); ipoint++)
1256         {
1257             Hep3Vector vtest = VecOp.sub(points.get(ipoint),points.get(ipoint-1));
1258             if (VecOp.cross(vtest,direction).magnitude() > DISTANCE_TOLERANCE) return false;
1259         }
1260         
1261         return true;
1262     }
1263     
1264     
1265     /**
1266      * test for parallel lines
1267      *
1268      * @param   line1
1269      * @param   line2
1270      * @return  true if lines are parallel within ANGULAR_TOLERANCE
1271      */
1272     public static boolean parallel(Line3D line1, Line3D line2)
1273     {
1274         return VecOp.cross(line1.getDirection(),line2.getDirection()).magnitude() < ANGULAR_TOLERANCE;
1275     }
1276     
1277     /**
1278      * test whether line is parallel to plane
1279      *
1280      * @param   line
1281      * @param   plane
1282      * @return  true if line is parallel to plane within ANGULAR_TOLERANCE
1283      */
1284     public static boolean parallel(Line3D line, Plane3D plane)
1285     {
1286         return Math.abs(VecOp.dot(line.getDirection(),plane.getNormal())) < ANGULAR_TOLERANCE;
1287     }
1288     
1289     /**
1290      * test for parallel planes
1291      *
1292      * @param   plane1
1293      * @param   plane2
1294      * @return  true if planes are parallel within ANGULAR_TOLERANCE
1295      */
1296     public static boolean parallel(Plane3D plane1, Plane3D plane2)
1297     {
1298         return VecOp.cross(plane1.getNormal(),plane2.getNormal()).magnitude() < ANGULAR_TOLERANCE;
1299     }
1300     
1301     /**
1302      * test for plane normal to a vector
1303      *
1304      * @param   plane
1305      * @param   unit_vector (unchecked)
1306      * @return  true if plane normal to a unit vector within ANGULAR_TOLERANCE
1307      */
1308     public static boolean isNormal(Hep3Vector unit_vector, Plane3D plane)
1309     {
1310         if ( VecOp.cross(unit_vector,plane.getNormal()).magnitude() < ANGULAR_TOLERANCE )
1311         {
1312             return VecOp.dot(unit_vector,plane.getNormal()) > 0;
1313         }
1314         else return false;
1315     }
1316     
1317 //========================================
1318 // Private methods
1319 //========================================
1320     
1321     // Finds points of closest approach fora pair of lines
1322     // returns length parameter along each line of PCA
1323     private static double[] linesPCA(Line3D line1, Line3D line2)
1324     {
1325         Hep3Vector s1 = VecOp.sub(line2.getStartPoint(),line1.getStartPoint());
1326         Hep3Vector s2 = line2.getDirection();
1327         Hep3Vector s3 = VecOp.cross(line1.getDirection(),line2.getDirection());
1328         double[][] s_elements = {s1.v(),s2.v(),s3.v()};
1329         BasicMatrix s = new BasicMatrix(s_elements);
1330         double s_det = s.det();
1331         
1332         Hep3Vector t1 = s1;
1333         Hep3Vector t2 = line1.getDirection();
1334         Hep3Vector t3 = s3;
1335         double[][] t_elements = {t1.v(),t2.v(),t3.v()};
1336         BasicMatrix t = new BasicMatrix(t_elements); // check orientation... may need to be transposed
1337         t.transpose();
1338         double t_det = t.det();
1339         
1340         double denominator = t3.magnitudeSquared();
1341         if (denominator == 0)
1342         {
1343             throw new RuntimeException("Lines must be checked for parallelism before calling linesPCA!!");
1344         }
1345         
1346         double s_pca = s_det/denominator;
1347         double t_pca = t_det/denominator;
1348         
1349         return new double[]{s_pca,t_pca};
1350     }
1351     
1352     
1353 }