View Javadoc

1   package org.lcsim.util.swim;
2   
3   import hep.physics.vec.BasicHep3Vector;
4   import hep.physics.vec.Hep3Vector;
5   import hep.physics.vec.VecOp;
6   
7   import org.lcsim.spacegeom.CartesianPoint;
8   import org.lcsim.spacegeom.CartesianVector;
9   import org.lcsim.spacegeom.SpacePoint;
10  import org.lcsim.spacegeom.SpaceVector;
11  
12  /**
13   * A straight line
14   * @author tonyj
15   * @version $Id: Line.java,v 1.12 2007/08/16 21:46:45 jstrube Exp $
16   */
17  public class Line implements Trajectory
18  {
19     private double sinPhi;
20     private double cosPhi;
21     private double sinLambda;
22     private double cosLambda;
23     private Hep3Vector origin;
24     
25     /**
26       * Defines a line in space. The direction of the line is defined
27       * by the angles phi and lambda, such that the Cartesian unit vector is
28       * (cos(lambda)*cos(phi), cos(lambda)*sin(phi), sin(lambda)).
29       * @param origin A point on the line
30       */
31     public Line(Hep3Vector origin, double phi, double lambda)
32     {
33        this.origin = origin;
34        sinPhi = Math.sin(phi);
35        cosPhi = Math.cos(phi);
36        sinLambda = Math.sin(lambda);
37        cosLambda = Math.cos(lambda);
38     }
39  
40     /**
41       * Gets a point after traveling distance alpha from the trajectory's
42       * origin point along the trajectory
43       */
44     public SpacePoint getPointAtDistance(double alpha)
45     {
46        double z = origin.z() + alpha*sinLambda;
47        double dr = alpha*cosLambda;
48        double x = origin.x() + dr*cosPhi;
49        double y = origin.y() + dr*sinPhi;
50        return new CartesianPoint(x,y,z);
51     }
52  
53    /**
54      * Calculates the distance at which the trajectory first reaches radius R.
55      * Returns Double.NaN if the trajectory does not intercept the cylinder.
56      * In principle there could be multiple solutions, so this method should
57      * always return the minimum <b>positive</b> solution.
58      */
59     public double getDistanceToInfiniteCylinder(double r)
60     {
61         if (cosLambda == 0.0) {
62             // This is a special case where the line is parallel to the z-axis.
63             // There can be no well-defined answer in this case
64             return Double.NaN;
65         }
66         // Find the intercepts on the cylinder (Cartesian):
67         double[] distances = this.findInterceptsOnCylinder(r);
68         // Which is the best?
69         double bestDistance = -1.0;
70         if (distances != null)
71         {
72             for (int i=0; i<distances.length; i++) {
73                 if (distances[i] >= 0.0) {
74                     // Potentially valid -- is it the best so far?
75                     if (bestDistance<0.0 || distances[i]<bestDistance) {
76                         // Yes!
77                         bestDistance = distances[i];
78                     }
79                 }
80             }
81         }
82         if (bestDistance < 0.0) {
83             // There was no valid (positive) solution
84             return Double.NaN;
85         }
86         return bestDistance;
87     }
88  
89    /** 
90      * Calculate the distance along the trajectory to reach a given Z plane.
91      * Note distance may be negative.
92      */
93     public double getDistanceToZPlane(double z)
94     {
95        return (z-origin.z())/sinLambda;
96     }
97     
98     /**
99      * Obtain the point of closest approach (POCA)
100     * of the line to a point. The return value is the distance
101     * parameter s, such that getPointAtDistance(s) is the POCA.
102     * @param point Point in space to swim to
103     * @return The length parameter s
104     */
105    public double getDistanceToPoint(Hep3Vector point) {
106        return findPOCAToPoint(point);
107    }
108 
109    /**
110     * An internal utility routine. This finds all intercepts of the
111     * line on a infinite cylinder of radius r. The cylinder's axis
112     * is the same as the z-axis. The return value is an array of
113     * signed distances from the origin point to the intercept point.
114     * If there are no solutions, the return value is null.
115     */
116    protected double[] findInterceptsOnCylinder(double r)
117    {   
118        if (cosLambda == 0.0) { 
119            throw new AssertionError("Don't call this private routine with a line that's parallel to the z-axis!");
120        }
121 
122        // Treat this first as a problem in the XY plane, then think about Z later.
123        // Track is described as
124        //    position(t) = (x0, y0) + t(vx, vy)
125        // i.e.
126        //    x = x0 + t(vx)
127        //    y = y0 + t(vy) = y0 + ((x-x0)/vx)(vy)
128        //                   = [y0 - x0.vy/vx] + [x.vy/vx]
129        //                   = c + m.x
130        // Circle is described as
131        //    x*x + y*y = r*r
132        // Solve for x and y:
133        //    x*x + (c+mx)*(c+mx) = r*r
134        //    x*x + c*c + 2*c*m*x + m*m*x*x = r*r
135        //    (m*m + 1).x^2 + (2*c*m).x + (c*c - r*r) = 0
136        // Thus, solving the quadratic,
137        //    x = [ -(2*c*m) +- sqrt( (2*c*m)^2 - 4*(m*m+1)*(c*c-r*r) ) ] / [2*(m*m+1)]
138 
139        double x0 = origin.x();
140        double y0 = origin.y();
141        double vx = cosLambda*cosPhi;
142        double vy = cosLambda*sinPhi;
143 
144        double c = y0 - (x0*vy)/vx;
145        double m = vy/vx;
146        
147        if (vx == 0.0) {
148            // This is a special case where the line is perpendicular to the x-axis.
149            // Handle this by flipping the x and y coordinates.
150            x0 = origin.y();
151            y0 = origin.x();
152            vx = cosLambda*sinPhi;
153            vy = cosLambda*cosPhi;
154            c  = y0 - (x0*vy)/vx;
155            m  = vy/vx;
156        }
157 
158        // Solve the quadratic...
159        double termA = (m*m + 1.0);
160        double termB = (2.0*c*m);
161        double termC = (c*c - r*r);       
162        double sqrtTerm = termB*termB - 4.0*termA*termC;
163 
164        double[] xSolutions = null; // This will hold the co-ordinates of the solutions
165 
166        if (sqrtTerm>0.0) {
167            // Two solutions.
168            xSolutions = new double[2];
169            xSolutions[0] = ( -termB + Math.sqrt(sqrtTerm) ) / (2.0 * termA);
170            xSolutions[1] = ( -termB - Math.sqrt(sqrtTerm) ) / (2.0 * termA);
171        } else if (sqrtTerm==0.0) {
172            // One solution
173            xSolutions = new double[1];
174            xSolutions[0] = ( -termB ) / (2.0 * termA);
175        } else if (sqrtTerm < 0.0) {
176            // No solutions
177        }
178 
179        double[] distances = null; // The array of signed distances we'll return
180        if (xSolutions == null) {
181            // No solutions -- distances stays null
182        } else {
183            // Unit vector is (cosLambda*cosPhi, cosLambda*sinPhi, sinLambda), so
184            // we can calculate the distances easily...
185            distances = new double[xSolutions.length];
186            for (int i=0; i<distances.length; i++) {
187                // Calculate in a way that remains correct even if we flipped x and y
188                // co-ordinates:
189                distances[i] = (xSolutions[i] - x0) / vx;
190            }
191        }
192        
193        return distances;
194    }
195 
196     /**
197      * An internal utility routine to find the distance of closest
198      * approach (DOCA) of the line to a point. This is a simple 2D
199      * vector calculation:
200      *   * Let the displacement vector from the origin to point be d.
201      *   * Decompose d into a component parallel to the line (d_parallel)
202      *     and a component perpendicular to the line (d_perp):
203      *      d = d_parallel + d_perp
204      *   * Take the dot product with the unit vector v along the line to
205      *     obtain and use the parallel/perpendicular properties to obtain:
206      *        |d.v| = |d_parallel|
207      *   * Hence the DOCA, |d_perp|, is given by
208      *        |d_perp|^2 = |d|^2 - |d_parallel|^2
209      *                   = |d|^2 - |d.v|^2
210      */
211     protected double findDOCAToPoint(Hep3Vector point) 
212     {
213 	// The first line is kind of ugly.
214 	Hep3Vector displacement = new BasicHep3Vector(point.x() - origin.x(), point.y() - origin.y(), point.z() - origin.z());
215 	Hep3Vector lineDirection = this.getUnitVector();
216 	double dotProduct = VecOp.dot(displacement, lineDirection);
217 	double doca = Math.sqrt(displacement.magnitudeSquared() - dotProduct*dotProduct);
218 	return doca;
219     }
220 
221     /**
222      * An internal utility routine to obtain the point of closest approach
223      * (POCA) of the line to a point. The return value is the distance
224      * parameter s, such that getPointAtDistance(s) is the POCA.
225      * The vector calculation is as follows:
226      *   Let the origin point be x and the unit vector along the line be v.
227      *   Let the point we're trying to find the POCA to be p.
228      *   Suppose the POCA is at
229      *     x' = x + sv
230      *   such that the scalar distance parameter we want is s.
231      *   Then the displacement vector from the POCA to p is
232      *     d = x' - p
233      *   But x' is the POCA, so d is perpendicular to v:
234      *     0 = d.v
235      *       = (x' - p).v
236      *       = (x + sv - p).v
237      *   So
238      *     (x-p).v + sv.v = 0
239      *   But v is a unit vector, so
240      *     s = (p-x).v
241      */
242     protected double findPOCAToPoint(Hep3Vector point) 
243     {
244 	// Find (p-x)
245 	Hep3Vector originToPoint = new BasicHep3Vector(point.x()-origin.x(), point.y() - origin.y(), point.z() - origin.z());
246 	Hep3Vector lineDirection = this.getUnitVector();
247 	double dotProduct = VecOp.dot(originToPoint, lineDirection);
248 	return dotProduct;
249     }
250 
251     /**
252      * Internal utility routine to return the unit vector of the
253      * line's direction.
254      */
255     protected Hep3Vector getUnitVector()
256     {
257 	Hep3Vector lineDirection = new BasicHep3Vector(cosLambda*cosPhi, cosLambda*sinPhi, sinLambda);
258 	return lineDirection;
259     }
260 
261     /**
262      * Return the direction of the line. Marked Deprecated since this should
263      * really be replaced by a general method for the Trajectory interface,
264      * perhaps getDirection(double alpha) for distance parameter alpha.
265      */
266     @Deprecated public Hep3Vector getDirection()
267     {
268         return this.getUnitVector();
269     }
270 
271     /**
272      * Routine to find where two lines meet.
273      * This will probably be replaced by a more general vertexing solution
274      * at some point, but for now it works.
275      */
276     @Deprecated static public double[] getPOCAOfLines(Line line1, Line line2)
277     {
278 	double[] output = line1.findTrackToTrackPOCA(line2);
279 	return output;
280     }
281 
282     /**
283      * Internal utility routine to find where two lines meet.
284      * This will probably be replaced by a more general vertexing solution
285      * at some point, but for now it works.
286      */
287     @Deprecated private double[] findTrackToTrackPOCA(Line otherLine)
288     {
289 	Line line1 = this;
290 	Line line2 = otherLine;
291 
292 	// Unit vectors of the two lines:
293 	Hep3Vector v1 = line1.getUnitVector();
294 	Hep3Vector v2 = line2.getUnitVector();
295 	// Origin points on the two lines:
296 	Hep3Vector x1 = line1.origin;
297 	Hep3Vector x2 = line2.origin;
298 
299         // Find the common perpendicular:
300 	Hep3Vector perp = VecOp.cross(v1, v2);
301 	if (perp.magnitude() == 0.0) {
302 	    // Lines are parallel!
303             return null;
304 	} else {
305 	    // Let the origin point along the lines be x1 and x2.
306 	    // Let the unit vectors along the lines be v1 and v2.
307             // Suppose that the points of closest approach are at:
308             //    x1' = x1 + a(v1)
309             //    x2' = x2 + b(v2)
310             // Construct a vector p1 which is perpendicular to perp and to v1:
311             //    p1 = v1 x perp = v1 x (v1 x v2) = v1(v1.v2) - v2(v1.v1)
312             //    p2 = v2 x perp = v2 x (v1 x v2) = v1(v2.v2) - v2(v1.v2)
313             // Then v1.p1 = (v1.v1)(v1.v2) - (v1.v2)(v1.v1) = 0
314             //      v1.p2 = (v1.v1)(v2.v2) - (v1.v2)(v1.v2) = L
315             //      v2.p1 = (v2.v1)(v1.v2) - (v2.v2)(v1.v1) = -L
316             //      v2.p2 = (v2.v1)(v2.v2) - (v2.v2)(v1.v2) = 0
317             // Thus,
318             //    (x2'-x1').p1 = (x2 + b(v2) - x1 -a(v1)).p1
319             //                 = x2.p1 + b(v2.p1) - x1.p1 - a(v1.p1)
320             //                 = x2.p1 - bL - x1.p1
321             //  and similarly,
322             //    (x2'-x1').p2 = x2.p2 -x1.p2 - aL
323             //  But p1 and p2 are perpendicular to perp, and (x2'-x1') is parallel
324             //  to perp for x1' and x2' the closest points. So
325             //    (x2'-x1').p1 = x2.p1 - bL - x1.p1 = 0
326             //    (x2'-x1').p2 = x2.p2 - x1.p2 - aL = 0
327             //  Hence,
328             //    -bL = -x2.p1 + x1.p1 => b = (x2-x1).p1 / L
329             //    -aL = -x2.p2 + x1.p2 => a = (x2-x1).p2 / L
330             //  and so the POCA is at
331             //    (x1' + x2')/2 = (x1 + a(v1) + x2 + b(v2)) / 2
332             //                  = (x1 + [(x2-x1).p2/L]v1 + x2 + [(x2-x1).p1/L]v2) / 2
333 
334 	    // p1 = v1 x perp  (and similarly p2)
335 	    Hep3Vector p1 = VecOp.cross(v1, perp);
336 	    Hep3Vector p2 = VecOp.cross(v2, perp);
337 
338 	    // L = (v1.v1)(v2.v2) - (v1.v2)(v1.v2) = 1 - (v1.v2)^2
339 	    double L = 1.0 - (VecOp.dot(v1,v2) * VecOp.dot(v1,v2));
340 
341 	    // a = (x2-x1).p2 / L
342 	    // b = (x2-x1).p1 / L
343 	    double a = VecOp.dot(VecOp.sub(x2,x1), p2) / L;
344 	    double b = VecOp.dot(VecOp.sub(x2,x1), p1) / L;
345 
346 	    double[] output = new double[2];
347 	    output[0] = a;
348 	    output[1] = b;
349 	    return output;
350 	}
351     }
352 
353    /**
354      * Defines a line in space. The line is defined by a point and a
355      * direction vector. The magnitude of the direction vector is ignored.
356      * @param origin A point on the line
357      * @param dir The signed direction of the line
358      */
359     public Line(Hep3Vector origin, Hep3Vector dir)
360     {
361 	// The easy bit: 
362 	this.origin = origin;
363 
364 	// The hard bit: find the direction as (cosPhi, sinPhi, cosLambda, sinLambda)
365         double normalization = dir.magnitude();
366         Hep3Vector dirNormalized = hep.physics.vec.VecOp.unit(dir);
367 
368 	// cosPhi, sinPhi, sinLambda are fairly easy.
369 	// It helps that the signed unit vector is (cos(lambda)*cos(phi), cos(lambda)*sin(phi), sin(lambda)).
370         double phi = hep.physics.vec.VecOp.phi(dirNormalized);
371 	cosPhi = Math.cos(phi);
372 	sinPhi = Math.sin(phi);
373 	sinLambda = dirNormalized.z();
374 
375 	// cosLambda is harder. First find the absolute value:
376         double cosLambdaSquared = (dirNormalized.x()*dirNormalized.x() + dirNormalized.y()*dirNormalized.y());
377 	if (cosLambdaSquared == 0.0) {
378 	    // Easy case
379             cosLambda = 0.0;
380 	} else {
381 	    // Need to tease it apart using either X or Y. Choose the one with the bigger lever arm...
382 	    boolean useX = (Math.abs(dir.x()) > Math.abs(dir.y()));
383 
384 	    // We will use one of the relations:
385 	    //    vx = cosLambda * cosPhi
386 	    //    vy = cosLambda * sinPhi
387 	    // where the unit direction vector is (vx, vy, vz)
388             if (useX) {
389                 cosLambda = dirNormalized.x() / cosPhi;
390             } else {
391                 cosLambda = dirNormalized.y() / sinPhi;
392             }
393         }
394     }
395     
396     
397     /**
398      * Calculates the <em>unit vector</em> of the momentum at a certain distance from the origin.
399      * Since the momentum is constant for a straight line,
400      * @param alpha is ignored
401      * @return The unit direction of the line, since there is now geometric way to obtain the momentum along a straight line.
402      */
403     public SpaceVector getUnitTangentAtLength(double alpha) {
404         SpaceVector lineDirection = new CartesianVector(cosLambda * cosPhi, cosLambda * sinPhi, sinLambda);
405         return lineDirection;
406     }
407 }