Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:48:57

0001 
0002 #include <csignal>
0003 #include "scuda.h"
0004 #include "squad.h"
0005 #include "stran.h"
0006 #include "OpticksCSG.h"
0007 
0008 #include "SSim.hh"
0009 #include "stree.h"
0010 #include "snd.hh"
0011 #include "s_bb.h"
0012 
0013 #include "ssys.h"
0014 #include "SLOG.hh"
0015 
0016 #include "CSGNode.h"
0017 #include "CSGFoundry.h"
0018 #include "CSGImport.h"
0019 
0020 const plog::Severity CSGImport::LEVEL = SLOG::EnvLevel("CSGImport", "DEBUG" );
0021 
0022 const int CSGImport::LVID = ssys::getenvint("LVID", -1);
0023 const int CSGImport::NDID = ssys::getenvint("NDID", -1);
0024 
0025 
0026 CSGImport::CSGImport( CSGFoundry* fd_ )
0027     :
0028     fd(fd_),
0029     st(nullptr)
0030 {
0031     LOG_IF(fatal, fd == nullptr) << " fd(CSGFoundry) required " ;
0032     assert( fd ) ;
0033 }
0034 
0035 
0036 
0037 /**
0038 CSGImport::import : populate CSGFoundry using geometry info from stree.h
0039 --------------------------------------------------------------------------
0040 
0041 Former equivalent from hybrid old+new morass wass CSG_GGeo_Convert
0042 
0043 **/
0044 
0045 void CSGImport::import()
0046 {
0047     LOG(LEVEL) << "[" ;
0048 
0049     st = fd->sim ? fd->sim->tree : nullptr ;
0050     LOG_IF(fatal, st == nullptr) << " fd.sim(SSim) fd.st(stree) required " ;
0051     assert(st);
0052 
0053 
0054     importNames();
0055     importSolid();
0056     importInst();
0057 
0058     LOG(LEVEL) << "]" ;
0059 }
0060 
0061 
0062 void CSGImport::importNames()
0063 {
0064     st->get_mmlabel( fd->mmlabel);    // populate fd->mmlabel from the stree
0065     st->get_meshname(fd->meshname);   // populate fd->meshname from the stree
0066 }
0067 
0068 
0069 /**
0070 CSGImport::importSolid : "Solid" means compound Solid
0071 ----------------------------------------------------------------
0072 
0073 Following prior CSG_GGeo_Convert::convertAllSolid
0074 
0075 CSG_GGeo_Convert::convertSolid
0076    shows that need to start from top level of the compound solids
0077    to fit in with the inflexible, having to declare everything ahead,
0078    way of populating CSGFoundry
0079 
0080 **/
0081 
0082 void CSGImport::importSolid()
0083 {
0084     int num_ridx = st->get_num_ridx() ;
0085     for(int ridx=0 ; ridx < num_ridx ; ridx++)
0086     {
0087         char ridx_type = st->get_ridx_type(ridx) ;
0088         switch(ridx_type)
0089         {
0090             case 'R': importSolidGlobal( ridx, ridx_type ) ; break ;   // remainder
0091             case 'T': importSolidGlobal( ridx, ridx_type ) ; break ;   // triangulate
0092             case 'F': importSolidFactor( ridx, ridx_type ) ; break ;   // factor
0093         }
0094     }
0095 }
0096 
0097 /**
0098 CSGImport::importSolidRemainder_OLD : non-instanced global volumes
0099 -------------------------------------------------------------------
0100 
0101 cf::
0102 
0103     CSG_GGeo_Convert::convertSolid
0104     CSG_GGeo_Convert::convertPrim
0105 
0106 
0107 AABB from each CSGPrim is combined using s_bb::IncludeAABB
0108 
0109 Q: Is that assuming common frame for all CSGPrim in the CSGSolid ?
0110 A: That assumption is true for prims of the remainder solid, but might not be for
0111    the prim of other solid.
0112 
0113 **/
0114 CSGSolid* CSGImport::importSolidRemainder_OLD(int ridx, const char* rlabel)
0115 {
0116     assert( ridx == 0 );
0117     int num_rem = st->rem.size() ;
0118 
0119     LOG(LEVEL)
0120         << " ridx " << ridx
0121         << " rlabel " << rlabel
0122         << " num_rem " << num_rem
0123         ;
0124 
0125     std::array<float,6> bb = {} ;
0126     CSGSolid* so = fd->addSolid(num_rem, rlabel);
0127 
0128     for(int i=0 ; i < num_rem ; i++)
0129     {
0130         int primIdx = i ;  // primIdx within the CSGSolid
0131         const snode& node = st->rem[primIdx] ;
0132         CSGPrim* pr = importPrim( primIdx, node ) ;
0133         assert( pr );
0134         s_bb::IncludeAABB( bb.data(), pr->AABB() );
0135     }
0136     s_bb::CenterExtent( &(so->center_extent.x), bb.data() );
0137     return so ;
0138 }
0139 
0140 
0141 /**
0142 CSGImport::importSolidGlobal
0143 -----------------------------
0144 
0145 Generalizes the old CSGImport::importSolidRemainder to source the
0146 stree level snode from either *rem* or *tri* depending on the
0147 ridx_type returned by stree::get_ridx_type
0148 
0149 **/
0150 
0151 CSGSolid* CSGImport::importSolidGlobal(int ridx, char ridx_type )
0152 {
0153     assert( ridx_type == 'R' || ridx_type == 'T' );  // remainder or triangulate
0154 
0155     std::string _rlabel = CSGSolid::MakeLabel(ridx_type,ridx) ;
0156     const char* rlabel = _rlabel.c_str();
0157 
0158 
0159     const std::vector<snode>* src = st->get_node_vector(ridx_type) ;
0160     assert( src );
0161 
0162     int num_src = src->size() ;
0163 
0164     LOG(LEVEL)
0165         << " ridx " << ridx
0166         << " ridx_type " << ridx_type
0167         << " rlabel " << rlabel
0168         << " num_src " << num_src
0169         ;
0170 
0171     std::array<float,6> bb = {} ;
0172     CSGSolid* so = fd->addSolid(num_src, rlabel);
0173     so->setIntent(ridx_type);
0174 
0175     for(int i=0 ; i < num_src ; i++)
0176     {
0177         int primIdx = i ;  // primIdx within the CSGSolid
0178         const snode& node = (*src)[primIdx] ;
0179         CSGPrim* pr = importPrim( primIdx, node ) ;
0180         assert( pr );
0181         s_bb::IncludeAABB( bb.data(), pr->AABB() );
0182     }
0183     s_bb::CenterExtent( &(so->center_extent.x), bb.data() );
0184     return so ;
0185 }
0186 
0187 
0188 
0189 /**
0190 CSGImport::importSolidFactor
0191 -----------------------------
0192 
0193 Note the simple way the bbox of each prim are combined to
0194 give the center_extent of the solid. This implies that
0195 the bbox of all the prim are from the same frame, which
0196 should be the frame of the outer prim of the instance.
0197 Otherwise the center extent would be incorrect, unless
0198 there is no relative transform differences between the
0199 prims of the compound solid.
0200 
0201 TODO: confirm consistent frame for the prim bbox
0202 
0203 **/
0204 
0205 CSGSolid* CSGImport::importSolidFactor(int ridx, char ridx_type )
0206 {
0207     assert( ridx > 0 );
0208     assert( ridx_type == 'F' );
0209 
0210     std::string _rlabel = CSGSolid::MakeLabel(ridx_type,ridx) ;
0211     const char* rlabel = _rlabel.c_str();
0212 
0213 
0214     int  num_rem = st->get_num_remainder() ;
0215     assert( num_rem == 1 ) ;  // YEP: always one
0216 
0217     int num_factor = st->factor.size() ;
0218     assert( ridx - num_rem < num_factor );
0219 
0220     const sfactor& sf = st->factor[ridx-num_rem] ;
0221     int subtree = sf.subtree ;  // number of prim within the compound solid
0222 
0223     CSGSolid* so = fd->addSolid(subtree, rlabel);
0224     so->setIntent(ridx_type);
0225 
0226     int q_repeat_index = ridx ;
0227     int q_repeat_ordinal = 0 ;   // just first repeat
0228 
0229     std::vector<snode> nodes ;
0230     st->get_repeat_node(nodes, q_repeat_index, q_repeat_ordinal) ;
0231 
0232     LOG(LEVEL)
0233         << " ridx " << ridx
0234         << " ridx_type " << ridx_type
0235         << " num_rem " << num_rem
0236         << " rlabel " << rlabel
0237         << " num_factor " << num_factor
0238         << " nodes.size " << nodes.size()
0239         << " subtree " << subtree
0240         ;
0241 
0242     assert( subtree == int(nodes.size()) );
0243 
0244     std::array<float,6> bb = {} ;
0245 
0246     for(int i=0 ; i < subtree ; i++)
0247     {
0248         int primIdx = i ;  // primIdx within the CSGSolid
0249         const snode& node = nodes[primIdx] ;   // structural node
0250 
0251         CSGPrim* pr = importPrim( primIdx, node );
0252         assert( pr );
0253         pr->setRepeatIdx(ridx);
0254 
0255         s_bb::IncludeAABB( bb.data(), pr->AABB() );
0256     }
0257     s_bb::CenterExtent( &(so->center_extent.x), bb.data() );
0258 
0259     return so ;
0260 }
0261 
0262 
0263 
0264 
0265 /**
0266 CSGImport::importPrim
0267 ----------------------
0268 
0269 Converting *snd/scsg* n-ary tree with compounds (eg multiunion and polycone)
0270 into the CSGNode serialized binary tree with list node constituents appended using
0271 subNum/subOffset referencing.
0272 
0273 * Despite the input *snd* tree being an n-ary tree (able to hold polycone and multiunion compounds)
0274   it must be traversed as a binary tree by regarding the compound nodes as effectively leaf node "primitives"
0275   in order to generate the indices into the complete binary tree serialization in level order
0276 
0277 
0278 WIP:LISTNODE
0279 
0280 * from binary tree point of view the listnode is just a leaf "prim"
0281 * for a listnode only prim : its just one binary tree node with lots of child
0282 * need smth similar to CSGMaker::makeList
0283 
0284 1. get the binary tree nodes into complete binary tree vector (excluding the subs of any listnode)
0285 2. count total subs of any listnodes TODO: move down into sn.h
0286 3. addPrim to foundry with space for binary nodes and all subs
0287 
0288 
0289 
0290 **/
0291 
0292 
0293 CSGPrim* CSGImport::importPrim(int primIdx, const snode& node )
0294 {
0295     int lvid = node.lvid ;
0296     const char* name = fd->getMeshName(lvid)  ;
0297     bool strip = true ;
0298     std::string soname = st->get_lvid_soname(lvid, strip);
0299 
0300 
0301     const sn* rt = sn::GetLVRoot(lvid);
0302     assert(rt);
0303     int idx_rc = rt->check_idx("CSGImport::importPrim.check_idx");
0304 
0305 
0306     // 1. get the binary tree nodes into complete binary tree vector (excluding the subs of any listnode)
0307 
0308     std::vector<const sn*> nds ;
0309     sn::GetLVNodesComplete(nds, lvid);   // many nullptr in unbalanced deep complete binary trees
0310     int bn = nds.size();                 // binary nodes
0311 
0312     // 2. count total subs for any listnodes of this lvid
0313 
0314     std::vector<const sn*> lns ;
0315     sn::GetLVListnodes( lns, lvid );
0316     int num_sub_total = sn::GetChildTotal( lns );
0317 
0318     int ln = lns.size();
0319     bool ln_expect = ln == 0 || ln == 1 ;
0320 
0321 
0322     bool dump_LVID = node.lvid == LVID || ln > 0 || idx_rc > 0 ;
0323     if(dump_LVID) std::cout
0324         << "[CSGImport::importPrim.dump_LVID:" << dump_LVID
0325         << " node.lvid " << node.lvid
0326         << " idx_rc " << idx_rc
0327         << " LVID " << LVID
0328         << " name " << ( name ? name : "-" )
0329         << " soname " << soname
0330         << " primIdx " << primIdx
0331         << " bn " << bn << "(binary nodes)"
0332         << " ln(subset of bn) " << ln
0333         << " ln_expect " << ( ln_expect ? "YES" : "NO " )
0334         << " num_sub_total " << num_sub_total
0335         << "\n"
0336         << "[rt.render\n"
0337         << rt->render()
0338         << "]rt.render\n"
0339         ;
0340 
0341     if(dump_LVID && ln > 0 ) std::cout
0342         << ".CSGImport::importPrim dumping as ln > 0 : solid contains listnode"
0343         << std::endl
0344         ;
0345 
0346     assert( ln_expect ); // simplify initial impl
0347 
0348 
0349     // 3. addPrim to foundry with space for binary nodes and all subs
0350 
0351     CSGPrim* pr = fd->addPrim( bn + num_sub_total );
0352 
0353     pr->setMeshIdx(lvid);
0354     pr->setPrimIdx(primIdx);  // primIdx within the CSGSolid
0355 
0356     std::stringstream ss ;
0357     std::ostream* out = dump_LVID ? &ss : nullptr  ;
0358 
0359     std::array<float,6> bb = {} ;
0360 
0361     CSGNode* root = nullptr ;
0362 
0363     // for any listnode in the binary tree, collect referenced n-ary subs
0364     std::vector<const sn*> subs ;
0365 
0366     int sub_offset = 0 ;
0367     sub_offset += bn ;
0368 
0369 
0370     // HMM: would be simpler for listnode to contribute to the prim bb
0371     // if could add the listnode subs
0372     // immediately into their offset place after the binary tree
0373     // rather than collecting and adding later
0374     // BUT out of order node adding is not currently possible with CSGFoundry::addNode
0375 
0376     for(int i=0 ; i < bn ; i++)
0377     {
0378         int partIdx = i ;
0379         const sn* nd = nds[partIdx];
0380 
0381         CSGNode* n = nullptr ;
0382         if(nd && nd->is_listnode())
0383         {
0384             n = importListnode(pr->nodeOffset(), partIdx, node, nd ) ;
0385 
0386             int num_sub = nd->child.size() ;
0387             for(int j=0 ; j < num_sub ; j++)
0388             {
0389                 const sn* c = nd->child[j];
0390                 subs.push_back(c);
0391             }
0392             n->setSubNum(num_sub);
0393             n->setSubOffset(sub_offset);
0394             sub_offset += num_sub ;
0395         }
0396         else
0397         {
0398             n = importNode(pr->nodeOffset(), partIdx, node, nd ) ;
0399         }
0400         assert(n);
0401         if(root == nullptr) root = n ;   // first node becomes root
0402 
0403         if(!n->is_complemented_primitive()) s_bb::IncludeAABB( bb.data(), n->AABB(), out );
0404     }
0405 
0406     assert( sub_offset == bn + num_sub_total );
0407     assert( int(subs.size()) == num_sub_total );
0408 
0409 
0410 
0411     for( int i=0 ; i < num_sub_total ; i++ )
0412     {
0413         const sn* nd = subs[i];
0414         CSGNode* n = importNode(pr->nodeOffset(), i, node, nd );
0415         assert( n );
0416         if(!n->is_complemented_primitive()) s_bb::IncludeAABB( bb.data(), n->AABB(), out );
0417     }
0418 
0419 
0420     pr->setAABB( bb.data() );
0421 
0422     assert( root );
0423 
0424     // IsCompound : > CSG_ZERO, < CSG_LEAF
0425     //
0426     // Q: Is this actually needed by anything ?
0427     // A: YES, for example its how intersect_tree gets numNode,
0428     //    without the subNum would get no intersects onto booleans
0429     //
0430     if(CSG::IsCompound(root->typecode()) && !CSG::IsList(root->typecode()))
0431     {
0432         assert( bn > 0 );
0433         root->setSubNum( bn );
0434         root->setSubOffset( 0 );
0435     }
0436 
0437 
0438     LOG_IF(info, dump_LVID ) <<  ss.str() ;
0439     LOG(LEVEL)
0440         << " primIdx " << std::setw(4) << primIdx
0441         << " lvid "    << std::setw(3) << lvid
0442         << " binaryNodes(bn) "  << std::setw(3) << bn
0443         << " : "
0444         << name
0445         ;
0446 
0447     if(dump_LVID) std::cout
0448         << "]CSGImport::importPrim.dump_LVID:" << dump_LVID
0449         << " node.lvid " << node.lvid
0450         << " LVID " << LVID
0451         << " name " << ( name ? name : "-" )
0452         << " soname " << soname
0453          << std::endl
0454         ;
0455 
0456 
0457     return pr ;
0458 }
0459 
0460 
0461 
0462 
0463 
0464 
0465 
0466 
0467 
0468 /**
0469 CSGImport::importNode (cf CSG_GGeo_Convert::convertNode)
0470 ----------------------------------------------------------
0471 
0472 Note similarity with the old CSG_GGeo_Convert::convertNode
0473 
0474 An assert constrains the *sn* CSG constituent to be from the shape *lvid*
0475 that is associated with the structural *snode*.
0476 
0477 (snode)node
0478     structural node "parent", corresponding to Geant4 PV/LV
0479 (sn)nd
0480     constituent CSG nd, corresponding to Geant4 G4VSolid
0481 
0482 nodeIdx
0483     local 0-based index over the CSGNode that comprise the CSGPrim
0484 
0485 (snode)node.index
0486     absolute structural node index with large values
0487 
0488 (sn)nd.index
0489     csg level index
0490 
0491 Lack of complement in snd.hh and inflexibility motivated the move to sn.h
0492 
0493 
0494 
0495 **TODO: handling nodes where external bbox expected**
0496 
0497 ::
0498 
0499     if( expect_external_bbox )
0500     {
0501         assert(aabb);
0502         n->setAABB_Narrow( aabb );
0503     }
0504 
0505 transform handling
0506 ~~~~~~~~~~~~~~~~~~~~
0507 
0508 stree::get_combined_tran_and_aabb
0509    computes combined structural(snode) and CSG tree node(sn) transform
0510    and inplace applies that transform to th
0511 
0512 
0513 
0514 **CSG Leaf/Tree Frame AABB ?**
0515 
0516 The stree::get_combined_tran_and_aabb expects the sn.h AABB
0517 to be leaf frame (not CSG tree frame needed by sn::uncoincide).
0518 
0519 **Leaf CSGNode transforms**
0520 
0521 For the instanced with node.repeat_index > 0,
0522 transforms are within the instance frame.
0523 
0524 For global remainder with node.repeat_index == 0, it will be the absolute transform
0525 combining the CSG node transforms with the structural node transforms all the way down
0526 from root.
0527 
0528 
0529 **/
0530 
0531 CSGNode* CSGImport::importNode(int nodeOffset, int partIdx, const snode& node, const sn* nd)
0532 {
0533     if(nd) assert( node.lvid == nd->lvid );
0534 
0535     int  typecode = nd ? nd->typecode : CSG_ZERO ;
0536     bool leaf = CSG::IsLeaf(typecode) ;
0537 
0538     bool external_bbox_is_expected = CSG::ExpectExternalBBox(typecode);
0539     // CSG_CONVEXPOLYHEDRON, CSG_CONTIGUOUS, CSG_DISCONTIGUOUS, CSG_OVERLAP
0540 
0541     bool expect = external_bbox_is_expected == false ;
0542     LOG_IF(fatal, !expect)
0543         << " NOT EXPECTING LEAF WITH EXTERNAL BBOX EXPECTED "
0544         << " for node of type " << CSG::Name(typecode)
0545         << " nd.lvid " << ( nd ? nd->lvid : -1 )
0546         ;
0547     assert(expect);
0548     if(!expect) std::raise(SIGINT);
0549 
0550     std::array<double,6> bb ;
0551     double* aabb = leaf ? bb.data() : nullptr ;
0552 
0553     std::ostream* out = nullptr ;
0554     stree::VTR* t_stack = nullptr ;
0555 
0556     const Tran<double>* tv = leaf ? st->get_combined_tran_and_aabb( aabb, node, nd, out, t_stack ) : nullptr ;
0557     unsigned tranIdx = tv ?  1 + fd->addTran(tv) : 0 ;   // 1-based index referencing foundry transforms
0558 
0559     CSGNode* n = fd->addNode();
0560     n->setTypecode(typecode);
0561     n->setBoundary(node.boundary);
0562     n->setComplement( nd ? nd->complement : false );
0563     n->setTransform(tranIdx);
0564     n->setParam_Narrow( nd ? nd->getPA_data() : nullptr );
0565     n->setAABB_Narrow(aabb ? aabb : nullptr  );
0566 
0567     return n ;
0568 }
0569 
0570 CSGNode* CSGImport::importListnode(int nodeOffset, int partIdx, const snode& node, const sn* nd)
0571 {
0572     if(nd) assert( node.lvid == nd->lvid );
0573     assert( nd->is_listnode() );
0574     int typecode = nd->typecode ;
0575 
0576     CSGNode* n = fd->addNode();
0577     n->setTypecode(typecode);
0578     n->setBoundary(node.boundary);
0579 
0580     return n ;
0581 }
0582 
0583 
0584 
0585 
0586 /**
0587 CSGImport::importInst
0588 ---------------------------
0589 
0590 Invoked from CSGImport::import
0591 
0592 The CSGFoundry calls should parallel CSG_GGeo_Convert::addInstances
0593 the source is the stree instead of GGeo/GMergedMesh etc..
0594 
0595 **/
0596 
0597 void CSGImport::importInst()
0598 {
0599     fd->addInstanceVector( st->inst_f4 );
0600 }
0601 
0602 
0603