Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-28 08:28:30

0001 /**
0002  * @file TessellatedSolidTest.cc
0003  * @brief Verifies host-side tessellated-solid conversion and detection.
0004  *
0005  * The test constructs a closed, offset tetrahedron at runtime. It verifies
0006  * entity-type and tag dispatch, then checks that conversion produces a box
0007  * placeholder with the expected dimensions, local bounds, and translation.
0008  *
0009  * The test places the tetrahedron with a non-identity rotation and verifies
0010  * that displaced-solid and multi-union transforms preserve it. Recursive
0011  * detection is checked when the tetrahedron is used directly or inside
0012  * Boolean, displaced, and multi-union solids, while confirming that
0013  * an ordinary box is not detected as tessellated.
0014  *
0015  * The test does not exercise triangulated GPU intersection.
0016  */
0017 
0018 #include <cmath>
0019 #include <cstdlib>
0020 #include <iostream>
0021 #include <string>
0022 
0023 #include "G4Box.hh"
0024 #include "G4DisplacedSolid.hh"
0025 #include "G4MultiUnion.hh"
0026 #include "G4RotationMatrix.hh"
0027 #include "G4TessellatedSolid.hh"
0028 #include "G4Transform3D.hh"
0029 #include "G4TriangularFacet.hh"
0030 #include "G4UnionSolid.hh"
0031 
0032 #include "U4Solid.h"
0033 
0034 #include "s_csg.h"
0035 
0036 namespace
0037 {
0038 bool close(double actual, double expected, double tolerance = 1.e-9)
0039 {
0040     return std::fabs(actual - expected) <= tolerance * (1. + std::fabs(expected));
0041 }
0042 
0043 bool close(const glm::tmat4x4<double>& actual, const glm::tmat4x4<double>& expected)
0044 {
0045     for (int column = 0; column < 4; ++column)
0046     {
0047         for (int row = 0; row < 4; ++row)
0048         {
0049             if (!close(actual[column][row], expected[column][row]))
0050                 return false;
0051         }
0052     }
0053     return true;
0054 }
0055 
0056 bool addFacet(G4TessellatedSolid& solid, const G4ThreeVector& a, const G4ThreeVector& b, const G4ThreeVector& c)
0057 {
0058     return solid.AddFacet(new G4TriangularFacet(a, b, c, ABSOLUTE));
0059 }
0060 } // namespace
0061 
0062 int main()
0063 {
0064     const G4ThreeVector p0(10., 20., 30.);
0065     const G4ThreeVector p1(14., 20., 30.);
0066     const G4ThreeVector p2(10., 26., 30.);
0067     const G4ThreeVector p3(10., 20., 38.);
0068 
0069     G4TessellatedSolid solid("OffsetTetrahedron");
0070 
0071     bool facetsAdded =
0072         addFacet(solid, p0, p2, p1) &&
0073         addFacet(solid, p0, p1, p3) &&
0074         addFacet(solid, p0, p3, p2) &&
0075         addFacet(solid, p1, p2, p3);
0076     if (!facetsAdded)
0077     {
0078         std::cerr << "failed to construct tessellated test geometry" << std::endl;
0079         return EXIT_FAILURE;
0080     }
0081     solid.SetSolidClosed(true);
0082 
0083     if (U4Solid::Type(solid.GetEntityType().c_str()) != _G4TessellatedSolid ||
0084         std::string(U4Solid::Tag(_G4TessellatedSolid)) != "Tes")
0085     {
0086         std::cerr << "G4TessellatedSolid type or tag dispatch failed" << std::endl;
0087         return EXIT_FAILURE;
0088     }
0089 
0090     G4Box            box("Box", 1., 1., 1.);
0091     G4UnionSolid     boolean("BooleanWithTessellated", &box, &solid);
0092     G4DisplacedSolid displaced("DisplacedTessellated", &solid, G4Transform3D());
0093     G4MultiUnion     multiUnion("MultiUnionWithTessellated");
0094     multiUnion.AddNode(box, G4Transform3D());
0095     multiUnion.AddNode(solid, G4Transform3D());
0096 
0097     bool containment =
0098         U4Solid::ContainsTessellated(&solid) &&
0099         U4Solid::ContainsTessellated(&boolean) &&
0100         U4Solid::ContainsTessellated(&displaced) &&
0101         U4Solid::ContainsTessellated(&multiUnion) &&
0102         !U4Solid::ContainsTessellated(&box);
0103 
0104     if (!containment)
0105     {
0106         std::cerr << "recursive tessellated-solid detection failed" << std::endl;
0107         return EXIT_FAILURE;
0108     }
0109 
0110     s_csg csg;
0111     sn*   root = U4Solid::Convert(&solid, 0, 0, 0);
0112     if (root == nullptr || root->typecode != CSG_BOX3)
0113     {
0114         std::cerr << "G4TessellatedSolid did not convert to a box placeholder" << std::endl;
0115         return EXIT_FAILURE;
0116     }
0117 
0118     const double*        param = root->getParam();
0119     const double*        aabb = root->getAABB();
0120     glm::tmat4x4<double> transform(1.);
0121     glm::tmat4x4<double> inverse(1.);
0122     root->getNodeTransformProduct(transform, inverse, false, nullptr, nullptr);
0123 
0124     bool dimensions =
0125         param != nullptr && close(param[0], 4.) && close(param[1], 6.) && close(param[2], 8.);
0126     bool localBounds =
0127         aabb != nullptr &&
0128         close(aabb[0], -2.) && close(aabb[1], -3.) && close(aabb[2], -4.) &&
0129         close(aabb[3], 2.) && close(aabb[4], 3.) && close(aabb[5], 4.);
0130     bool translation =
0131         close(transform[3][0], 12.) && close(transform[3][1], 23.) && close(transform[3][2], 34.);
0132 
0133     delete root;
0134 
0135     if (!dimensions || !localBounds || !translation)
0136     {
0137         std::cerr << "box placeholder does not preserve the tessellated-solid extent" << std::endl;
0138         return EXIT_FAILURE;
0139     }
0140 
0141     G4RotationMatrix rotation;
0142     rotation.rotateZ(0.5 * std::acos(-1.));
0143     G4Transform3D placement(rotation, G4ThreeVector(100., 200., 300.));
0144 
0145     G4DisplacedSolid rotatedDisplaced("RotatedDisplacedTessellated", &solid, placement);
0146     G4MultiUnion     rotatedMultiUnion("RotatedMultiUnionTessellated");
0147     rotatedMultiUnion.AddNode(solid, placement);
0148 
0149     sn* displacedRoot = U4Solid::Convert(&rotatedDisplaced, 1, 0, 0);
0150     sn* multiUnionRoot = U4Solid::Convert(&rotatedMultiUnion, 2, 0, 0);
0151 
0152     if (displacedRoot == nullptr || multiUnionRoot == nullptr || multiUnionRoot->num_child() != 1)
0153     {
0154         std::cerr << "failed to convert rotated tessellated test geometry" << std::endl;
0155         delete displacedRoot;
0156         delete multiUnionRoot;
0157         return EXIT_FAILURE;
0158     }
0159 
0160     glm::tmat4x4<double> displacedPlacement(1.);
0161     glm::tmat4x4<double> multiUnionPlacement(1.);
0162     U4Transform::GetDispTransform(displacedPlacement, &rotatedDisplaced);
0163     U4Transform::GetMultiUnionItemTransform(multiUnionPlacement, &rotatedMultiUnion, 0);
0164 
0165     glm::tmat4x4<double> displacedTransform(1.);
0166     glm::tmat4x4<double> displacedInverse(1.);
0167     displacedRoot->getNodeTransformProduct(
0168         displacedTransform, displacedInverse, false, nullptr, nullptr);
0169 
0170     glm::tmat4x4<double> multiUnionTransform(1.);
0171     glm::tmat4x4<double> multiUnionInverse(1.);
0172     multiUnionRoot->get_child(0)->getNodeTransformProduct(
0173         multiUnionTransform, multiUnionInverse, false, nullptr, nullptr);
0174 
0175     glm::tmat4x4<double> expectedDisplaced = displacedPlacement * transform;
0176     glm::tmat4x4<double> expectedMultiUnion = multiUnionPlacement * transform;
0177     bool                 displacedPlacementPreserved =
0178         close(displacedTransform, expectedDisplaced) &&
0179         close(displacedInverse, glm::inverse(expectedDisplaced));
0180     bool multiUnionPlacementPreserved =
0181         close(multiUnionTransform, expectedMultiUnion) &&
0182         close(multiUnionInverse, glm::inverse(expectedMultiUnion));
0183 
0184     delete displacedRoot;
0185     delete multiUnionRoot;
0186 
0187     if (!displacedPlacementPreserved || !multiUnionPlacementPreserved)
0188     {
0189         std::cerr << "rotated enclosing placement was composed in the wrong order" << std::endl;
0190         return EXIT_FAILURE;
0191     }
0192 
0193     std::cout << "G4TessellatedSolid conversion preserved extent and rotated placements" << std::endl;
0194     return EXIT_SUCCESS;
0195 }