File indexing completed on 2026-09-28 08:28:30
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
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 }
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 }