View Javadoc

1   package org.lcsim.detector;
2   
3   import hep.physics.vec.BasicHep3Vector;
4   import hep.physics.vec.Hep3Vector;
5   
6   import java.util.Collection;
7   
8   import org.lcsim.detector.solids.Inside;
9   
10  /**
11   * Navigates from a top or "world" volume
12   * into daughter volumes using String
13   * paths of Physical Volume names.  The
14   * returned information is a unique
15   * stack of PhysicalVolume objects, called
16   * an IPhysicalVolumePath.
17   *  
18   * String paths are a series of PhysicalVolume names.
19   * 
20   * "/a/b/c"
21   * 
22   * Paths do not include an explicit name of the top
23   * volume.  The top volume can be addressed with
24   * a single slash, "/", which the getPath method will
25   * interpret to mean the top volume of this navigator.
26   * 
27   * The LogicalVolume class enforces unique naming of 
28   * PhysicalVolume objects within its own daughter collection.
29   * This allows unique addressing of children from a
30   * given PhysicalVolume node.
31   * 
32   * Locates the deepest daughter volume containing a given 
33   * global point within the top volume.
34   * 
35   * Computes the combined transform of IPhysicalVolumePaths.
36   *
37   * In theory, the top PhysicalVolume need not be the world
38   * volume.  It is referred to as the "top" volume to avoid 
39   * confusion.  
40   *
41   * @author Jeremy McCormick <jeremym@slac.stanford.edu>
42   *
43   */
44  public class PhysicalVolumeNavigator 
45  implements IPhysicalVolumeNavigator
46  {
47  	String name;
48  	
49  	/**
50  	 * Find the full geometry path to the PhysicalVolume containing 
51       * the global point @param globalPoint, relative to the world volume.
52  	 * 
53  	 * @param globalPoint Point in top geometry system.
54  	 * @param level Depth to descend.  -1 means to bottom.
55       * @return An IPhysicalVolumePath containing the unique path to the containing sub-volume,
56       *         or null if the @param globalPoint is outside the world volume.
57  	 */
58  	public IPhysicalVolumePath getPath(Hep3Vector globalPoint, int level) 
59  	{                
60          // Path that will be returned to user.
61          // Empty path means globalPoint is outside
62          // the world volume.
63  		IPhysicalVolumePath path = new PhysicalVolumePath();
64          
65          // Get the top volume from this navigator.
66  		IPhysicalVolume world = getTopPhysicalVolume();
67          
68          // Start by looking in the world volume.
69  		ILogicalVolume lvCurr = world.getLogicalVolume();
70  
71  		// First time, see if point is inside the world.
72  		if (lvCurr.getSolid().inside(globalPoint) == Inside.INSIDE)
73  		{
74              // Add world to path.
75  			path.add(world);
76  		}		
77          // The point is outside the world volume!
78  		else {            
79  			System.err.println("!!! Point " + globalPoint.v() + " is outside the top volume <"+world.getName()+">. !!!");
80  			
81  			// Return an empty path.
82  			return path;
83  		}
84  
85  		// Current depth.
86  		int depth=0;
87  
88  		// The current local point that is computed
89          // by applying the containing daughter's transform
90          // successively as the search is performed.
91          Hep3Vector localPoint = new BasicHep3Vector(globalPoint.x(),globalPoint.y(),globalPoint.z());
92          
93          // Combined transform of path to current daughter.
94          // If a daughter is found to contain the point,
95          // its transform is applied to this.
96          ITransform3D combinedTransform = new Transform3D();
97          
98          // Go into the geometry tree as long as there are 
99          // daughter volumes in the current LogicalVolume.
100 		while(lvCurr.getNumberOfDaughters() != 0)
101 		{			
102             // No daughter found yet.
103 			boolean inDau=false;
104 			
105 			// Loop over the daughters.
106 			for (IPhysicalVolume dau : lvCurr.getDaughters())
107 			{												
108                 // Transform the local point from parent
109                 // into the daughter's coordinate system.
110                 Hep3Vector checkLocalPoint =
111                     dau.getTransform().inverse().transformed(localPoint);
112                                 						
113                 // Check if the point is inside this daughter's solid.
114                 if (dau.getLogicalVolume().getSolid().inside(checkLocalPoint) == Inside.INSIDE)
115 				{                    
116                     // Found a containing daughter.
117 					inDau=true;
118 					
119 					// Add this daughter to the returned path.
120 					path.add(dau);
121 
122 					// Traverse into the daughter.
123 					lvCurr = dau.getLogicalVolume();
124 
125 					// Increment the current depth.
126 					++depth;
127                     
128                     // Add the daughter's transform to the combined transform.
129                     combinedTransform.multiplyBy(dau.getTransform());
130                     
131                     // Set the current point to the daughter's local point.
132                     localPoint = checkLocalPoint;
133 					
134 					// Stop looking at this volume's daughters.
135 					break;
136 				}			                
137 			}
138 
139 			// If depth is past selected level or
140             // no daughter was found, stop looking
141             // and quit.  Current path will be returned.
142 			if ( level != -1 && depth >= level || !inDau)
143 			{
144 				break;
145 			}			
146 		}
147 
148 		return path;
149 	}
150 
151 	/**
152 	 * Get the IPhysicalVolumePath to the deepest PhysicalVolume
153 	 * in the tree containing the global point @param globalPoint,
154 	 * relative to the top volume.
155 	 */
156 	public IPhysicalVolumePath getPath(Hep3Vector globalPoint) 
157 	{
158 		return getPath(globalPoint,-1);
159 	}
160 
161 	/**
162 	 * Get the combined transform from a String path.
163 	 * @param path A valid path into the geometry tree.
164 	 */
165 	public Transform3D getTransform(String path) 
166 	{
167 		return getTransform(getPath(path));
168 	}
169 
170 	/**
171 	 * Compute the combined global to local transform from a path of PhysicalVolumes.
172 	 * 
173 	 * @param path The PhysicalVolumePath containing the unique geometry node.
174 	 */
175 	public Transform3D getTransform(IPhysicalVolumePath path) 
176 	{
177 		Transform3D theTransform = new Transform3D();
178 		for (IPhysicalVolume physvol : path)
179 		{
180 			theTransform.multiplyBy(physvol.getTransform());
181 		}
182 		return theTransform;
183 	}
184 
185 	/**
186 	 * Utility method for dealing with strings
187 	 * containing slash-delimited volume names.
188 	 * Returns an array containing the name
189 	 * components.  The top volume name is not
190 	 * included.  Single leading and trailing
191 	 * slashes are discarded to avoid empty
192 	 * array entries when splitting the path.
193 	 *
194 	 * @param path
195 	 * @return
196 	 */
197 	private static String[] splitPath(String path)
198 	{
199 		if (path.equals("/"))
200 		{
201 			return new String[] {};
202 		}
203 		
204 		// Eat the first slash.
205 		if (path.startsWith("/"))
206 		{
207 			path = path.substring(1);
208 		}
209 		
210 		// Eat the last slash.
211 		if (path.endsWith("/"))
212 		{
213 			path = path.substring(0,(path.length()-1));
214 		}
215 		
216 		//System.out.println("path after eating slashes: "+path);
217 
218 		// Split on remaining slashes.
219 		return path.split("/");
220 	}	
221 	
222 	/**
223 	 * 
224 	 * This is the primary method for navigating 
225 	 * using PhysicalVolume names.  It looks for
226 	 * a stack of volumes in the geometry tree with 
227 	 * names matching those in @param path.
228 	 * The match must be exact.  Failure
229 	 * to find a match throws a RuntimeException.
230 	 * The top volume name is not included
231 	 * in the search, but it is added to 
232 	 * the path so that the caller still has
233 	 * access to the complete geometric tree.
234 	 * 
235 	 * @param path
236 	 * @return The path associated with this array of volume names.
237 	 */
238 	public IPhysicalVolumePath getPath(String[] path)
239 	{
240 		// The path to be returned to user.
241 		IPhysicalVolumePath physvols = new PhysicalVolumePath();
242 
243 		// The top physical volume.
244 		IPhysicalVolume pv = getTopPhysicalVolume();
245 		
246 		// Always add the top volume.
247 		physvols.add(pv);
248 		
249 		// Loop over the names in the path. 
250 		for (String name : path)		
251 		{									
252 			PhysicalVolumeContainer pvSearch = 
253 				pv.getLogicalVolume().getDaughters().findByName(name);
254 			
255 			if (pvSearch.size() > 1)
256 			{
257 				throw new RuntimeException("Got multiple matches on name <"+name+"> in mom <"+pv.getName() + ">.");
258 			}
259 			else if (pvSearch.size() == 0)
260 			{
261 				throw new RuntimeException("Path component <"+name+"> was not found!");
262 			}
263 			else {				
264 				IPhysicalVolume pvFound = pvSearch.get(0);
265 				//System.out.println("found match = " + pvFound.getName());
266 				physvols.add(pvFound);
267 				pv = pvSearch.get(0);
268 			}
269 		}
270 		return physvols;
271 	}		
272 
273 	/**
274 	 * Get the IPhysicalVolumePath from a String @param path argument.
275 	 */
276 	public IPhysicalVolumePath getPath(String path) 
277 	{			
278 		return getPath(splitPath(path));
279 	}
280 
281 	/**
282 	 * Get the top volume of this navigator.
283 	 * @return The top volume's PhysicalVolume.
284 	 */
285 	public IPhysicalVolume getTopPhysicalVolume() 
286 	{				
287 		return pvTop;
288 	}
289 
290 	/**
291 	 * Set the top volume of this navigator.
292 	 * @param physvol A PhysicalVolume that cannot be null.
293 	 */
294 	public void setTopPhysicalVolume(IPhysicalVolume physvol) 
295 	{
296 		if (physvol == null)
297 		{
298 			throw new IllegalArgumentException("Top volume points to null!");
299 		}
300 		pvTop = physvol;		
301 	}
302 	
303 	/**
304 	 * Sets the top volume of this navigator 
305 	 * from the top node of an IPhysicalVolumePath.
306 	 * 
307 	 * @param path
308 	 */
309 	public void setTopPhysicalVolume(IPhysicalVolumePath path) 
310 	{
311 		if (path == null)
312 		{
313 			throw new IllegalArgumentException("The path cannot be null!");
314 		}
315 		
316 		if (path.isEmpty())
317 		{
318 			throw new IllegalArgumentException("The path is empty!");
319 		}
320 		
321 		setTopPhysicalVolume(path.getTopVolume());		
322 	}
323 		
324 	private IPhysicalVolume pvTop;
325 
326     public PhysicalVolumeNavigator(String name, IPhysicalVolume pvTop)
327     {
328         this.name = name;        
329         setTopPhysicalVolume(pvTop);        
330         PhysicalVolumeNavigatorStore.getInstance().add(this);
331     }
332     
333     public PhysicalVolumeNavigator(String name, IPhysicalVolumePath path)
334     {
335         this.name = name;
336         
337         if ( path == null )
338         {
339             throw new IllegalArgumentException("The path is null!");
340         }
341         
342         setTopPhysicalVolume(path);
343         
344         PhysicalVolumeNavigatorStore.getInstance().add(this);
345     }       
346     	
347 	public void traversePreOrder(
348 			IPhysicalVolumeVisitor visitor)
349 	{
350 		if ( visitor == null )
351 		{
352 			throw new IllegalArgumentException("The Visitor is null!");
353 		}
354 		
355 		traversePreOrder(getTopPhysicalVolume(), visitor);
356 	}
357 	
358 	/**
359 	 * Visit the PhysicalVolume recursively using preorder,
360 	 * calling the given IPhysicalVolumeVisitor's visit method
361 	 * for each node.
362 	 * 
363 	 * @param physicalVolume
364 	 * @param visitor
365 	 */
366 	protected void traversePreOrder(
367 			IPhysicalVolume physicalVolume, 
368 			IPhysicalVolumeVisitor visitor)
369 	{
370 		// Visit this node.
371 		visitor.visit(physicalVolume);
372 		
373 		// Recursively traverse the daughters.
374 		if ( physicalVolume.getLogicalVolume().getNumberOfDaughters() > 0 )
375 		{
376 			for ( IPhysicalVolume child : physicalVolume.getLogicalVolume().getDaughters())
377 			{
378 				traversePreOrder(child, visitor);
379 			}
380 		}		
381 	}
382 		
383 	/**
384 	 * Visit each PhysicalVolume recursively using postorder,
385 	 * calling the given IPhysicalVolumeVisitor's visit method
386 	 * for each node.
387 	 * 
388 	 * @param physicalVolume
389 	 * @param visitor
390 	 */
391 	protected void traversePostOrder(IPhysicalVolume physicalVolume, IPhysicalVolumeVisitor visitor)
392 	{		
393 		// Recursively traverse the daughters.
394 		if ( physicalVolume.getLogicalVolume().getNumberOfDaughters() > 0 )
395 		{
396 			for ( IPhysicalVolume child : physicalVolume.getLogicalVolume().getDaughters())
397 			{
398 				traversePostOrder(child, visitor);
399 			}
400 		}
401 		
402 		// Visit this node.
403 		visitor.visit(physicalVolume);		
404 	}	
405 	
406 	/**
407 	 * Visit the top volume recursively using postorder,
408 	 * calling the given IPhysicalVolumeVisitor's visit method
409 	 * for each node.
410 	 * 
411 	 * @param visitor A visitor to process the nodes.
412 	 */
413 	
414 	public void traversePostOrder(IPhysicalVolumeVisitor visitor)
415 	{
416 		traversePostOrder(getTopPhysicalVolume(), visitor);
417 	}	
418 	
419 	public String getName()
420 	{
421 		return name;
422 	}
423 	
424 	/**
425 	 * Utility method for getting String paths to leaf volumes.
426 	 * @param paths List of paths passed in by caller.
427 	 * @param node The top node to traverse.
428 	 * @param path The recursive path String.
429 	 */
430 	public static void getLeafPaths(Collection<String> paths, IPhysicalVolume node, String path)
431     {
432         if (node == null)
433         {
434             return;
435         }
436         if (node.getLogicalVolume().getDaughters().size() == 0)
437         {
438             paths.add(path + "/" + node.getName());
439             return;
440         }
441         
442         path += "/" + node.getName();
443         for (IPhysicalVolume dau : node.getLogicalVolume().getDaughters())
444         {
445             getLeafPaths(paths, dau, path);
446         }
447     }
448 }