Problem 983

Summary: G4TessellatedSolid::CalculateExtent() implementation is incomplete
Product: Geant4 Reporter: Stan Seibert <volsung>
Component: geometry/solidsAssignee: PRTruscott
Status: RESOLVED FIXED    
Severity: normal CC: flei, Gabriele.Cosmo
Priority: P4    
Version: 9.0   
Hardware: All   
OS: All   

Description Stan Seibert 2007-11-01 02:19:34 CET
The implementation of CalculateExtent() in the G4TessellatedSolid is incomplete.  The method checks if the affine transform is pure translation, and if so, it computes the extent of the solid correctly.  However, if there is a rotation component to the affine transform, it branches to an empty else block and always returns false.  This causes very confusing and erratic tracking behavior when geometry optimization is turned on (the default), since G4SmartVoxelHeader::BuildNodes() assumes a working CalculateExtent() method.  Rotated G4TessellatedSolid and G4ExtrudedSolid objects will disappear or be clipped in strange ways when viewed with the raytracer.

Here is one possible implementation for the method which is slower for pure translations, but is sufficiently general to handle all affine transformations:

G4bool G4TessellatedSolid::CalculateExtent(const EAxis pAxis,
         const G4VoxelLimits& pVoxelLimit, const G4AffineTransform& pTransform,
               G4double& pMin, G4double& pMax) const
{
    G4ThreeVectorList transVertexList(vertexList);

    // Put solid into transformed frame
    for (size_t i=0; i<vertexList.size(); i++)
      pTransform.ApplyPointTransform(transVertexList[i]);

    // Find min and max extent in each dimension
    G4ThreeVector minExtent(kInfinity, kInfinity, kInfinity);
    G4ThreeVector maxExtent(-kInfinity, -kInfinity, -kInfinity);
    for (size_t i=0; i<transVertexList.size(); i++)
    {
      for (int axis=G4ThreeVector::X; axis < G4ThreeVector::SIZE; axis++)
      {
        G4double coordinate = transVertexList[i][axis];
        if (coordinate < minExtent[axis])
          minExtent[axis] = coordinate;
        if (coordinate > maxExtent[axis])
          maxExtent[axis] = coordinate;
      }
    }
    
    // Check for containment and clamp to voxel boundaries
    for (int axis=G4ThreeVector::X; axis < G4ThreeVector::SIZE; axis++)
    {
      EAxis geomAxis; // G4 geom classes use different index type
      switch(axis)
      {
        case G4ThreeVector::X: geomAxis = kXAxis; break;
        case G4ThreeVector::Y: geomAxis = kYAxis; break;
        case G4ThreeVector::Z: geomAxis = kZAxis; break;
      }
      G4bool isLimited = pVoxelLimit.IsLimited(geomAxis);
      G4double voxelMinExtent = pVoxelLimit.GetMinExtent(geomAxis);
      G4double voxelMaxExtent = pVoxelLimit.GetMaxExtent(geomAxis);
      
      if (isLimited)
      {
        if ( minExtent[axis] > voxelMaxExtent+kCarTolerance ||
             maxExtent[axis] < voxelMinExtent-kCarTolerance    ) return false ;
        else
        {
          if (minExtent[axis] < voxelMinExtent)
          {
            minExtent[axis] = voxelMinExtent ;
          }
          if (maxExtent[axis] > voxelMaxExtent)
          {
            maxExtent[axis] = voxelMaxExtent;
          }
        }
      }
    }
    
    // Convert pAxis into G4ThreeVector index
    int clhepAxis;
    switch(pAxis)
    {
      case kXAxis: clhepAxis = G4ThreeVector::X; break;
      case kYAxis: clhepAxis = G4ThreeVector::Y; break;
      case kZAxis: clhepAxis = G4ThreeVector::Z; break;
      default: break;
    } 
    
    pMin = minExtent[clhepAxis] - kCarTolerance;
    pMax = maxExtent[clhepAxis] + kCarTolerance;
    
    return true;  
}
Comment 1 Gabriele Cosmo 2007-11-15 14:38:32 CET
Thanks for the report and the proposed solution.
The fix has been introduced in the internal tag "geom-specific-V09-00-08"
and will be available from the next release or patch.