Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:49:41

0001 #pragma once
0002 /**
0003 sn.h : minimal pointer based transient binary tree node
0004 ========================================================
0005 
0006 Motivation
0007 -----------
0008 
0009 In order to duplicate at CSG/CSGNode level the old workflow geometry
0010 (that goes thru GGeo/NNode) it is necessary to perform binary tree
0011 manipulations equivalent to those done by npy/NTreeBuilder::UnionTree
0012 in order to handle shapes such as G4Polycone.
0013 
0014 However the old array based *snd/scsg* node approach with integer index
0015 addressing lacks the capability to easily delete nodes making it unsuitable
0016 for tree manipulations such as pruning and rearrangement that are needed
0017 in order to flexibly create complete binary trees with any number of leaf nodes.
0018 
0019 Hence the *sn* nodes are developed. Initially sn.h was used as transient
0020 template for binary trees that are subsequently solidified into *snd* trees.
0021 But have now moved all snd functionality over to sn. So can directly use
0022 only sn and the old "WITH-SND" is removed.
0023 
0024 sn ctor/dtor register/de-register from s_pool<sn,_sn>
0025 -------------------------------------------------------
0026 
0027 In order to convert active *sn* pointers into indices
0028 on persisting have explictly avoided leaking ANY *sn* by
0029 taking care to ALWAYS delete appropriately.
0030 This means that can use the *sn* ctor/dtor to add/erase update
0031 an std::map of active *sn* pointers keyed on a creation index.
0032 This map allows the active *sn* pointers to be converted into
0033 a contiguous set of indices to facilitate serialization.
0034 
0035 Possible Future
0036 -----------------
0037 
0038 CSG_CONTIGUOUS could keep n-ary CSG trees all the way to the GPU
0039 
0040 **/
0041 
0042 #include <map>
0043 #include <set>
0044 #include <vector>
0045 #include <sstream>
0046 #include <iomanip>
0047 #include <cassert>
0048 #include <csignal>
0049 #include <cstdint>
0050 #include <functional>
0051 #include <algorithm>
0052 
0053 #include "ssys.h"
0054 #include "OpticksCSG.h"
0055 #include "scanvas.h"
0056 #include "s_pa.h"
0057 #include "s_bb.h"
0058 #include "s_tv.h"
0059 #include "s_pool.h"
0060 
0061 //#include "s_csg.h" // DONT DO THAT : CIRCULAR
0062 #include "st.h"      // complete binary tree math
0063 #include "stra.h"    // glm transform utilities
0064 #include "sgeomtools.h"
0065 
0066 #include "NPFold.h"
0067 
0068 struct _sn
0069 {
0070     int  typecode ;     // 0
0071     int  complement ;   // 1
0072     int  lvid ;         // 2
0073     int  xform ;        // 3
0074     int  param ;        // 4
0075     int  aabb ;         // 5
0076     int  parent ;       // 6
0077 
0078 #ifdef WITH_CHILD
0079     int  sibdex ;       // 7     0-based sibling index
0080     int  num_child ;    // 8
0081     int  first_child ;  // 9
0082     int  next_sibling ; // 10
0083     int  index ;        // 11
0084     int  depth ;        // 12
0085     int  note  ;        // 13
0086     int  coincide ;     // 14
0087     char label[16] ;    // 15,16,17,18
0088     static constexpr const char* ITEM = "19" ;
0089 #else
0090     int  left ;         // 7
0091     int  right ;        // 8
0092     int  index ;        // 9
0093     int  depth ;        // 10
0094     int  note  ;        // 11
0095     int  coincide ;     // 12
0096     char label[16] ;    // 13,14,15,16
0097     static constexpr const char* ITEM = "17" ;
0098 #endif
0099 
0100     std::string desc() const ;
0101     bool is_root() const ;
0102 };
0103 
0104 inline std::string _sn::desc() const
0105 {
0106     std::stringstream ss ;
0107     ss << "_sn::desc "
0108        << " typecode " << std::setw(4) << typecode
0109        << " complement " << std::setw(1) << complement
0110        << " lvid " << std::setw(4) << lvid
0111        << " xform " << std::setw(4) << xform
0112        << " param " << std::setw(4) << param
0113        << " aabb " << std::setw(4) << aabb
0114        << " parent " << std::setw(4) << parent
0115 #ifdef WITH_CHILD
0116        << " sx " << std::setw(4) << sibdex
0117        << " nc " << std::setw(4) << num_child
0118        << " fc " << std::setw(4) << first_child
0119        << " ns " << std::setw(4) << next_sibling
0120 #else
0121        << " left " << std::setw(4) << left
0122        << " right " << std::setw(4) << right
0123 #endif
0124        << " is_root " << ( is_root() ? "YES" : "NO " )
0125        ;
0126     std::string str = ss.str();
0127     return str ;
0128 }
0129 
0130 /**
0131 _sn::is_root
0132 ------------
0133 
0134 Only root expected to have parent -1
0135 
0136 **/
0137 inline bool _sn::is_root() const
0138 {
0139     return parent == -1 ;
0140 }
0141 
0142 #include "SYSRAP_API_EXPORT.hh"
0143 struct SYSRAP_API sn
0144 {
0145     typedef std::array<double,6> BB ;
0146     typedef std::vector<BB> VBB ;
0147     typedef glm::tmat4x4<double> TR ;
0148     typedef std::vector<TR> VTR ;
0149 
0150     static int Check_LEAK(const char* msg, int i=-1);
0151 
0152     // persisted
0153     int   typecode ;
0154     int   complement ;
0155     int   lvid ;
0156     s_tv* xform ;
0157     s_pa* param ;
0158     s_bb* aabb  ;
0159     sn*   parent ;   // NOT owned by this sn
0160 
0161 #ifdef WITH_CHILD
0162     std::vector<sn*> child ;
0163 #else
0164     sn* left ;
0165     sn* right ;
0166 #endif
0167     int depth ;
0168     int note ;
0169     int coincide ;
0170     char label[16] ;
0171 
0172     // internals, not persisted
0173     int pid ;
0174     int subdepth ;
0175 
0176 
0177     typedef s_pool<sn,_sn> POOL ;
0178     static POOL* pool ;
0179     static constexpr const int VERSION = 0 ;
0180     static constexpr const char* NAME = "sn.npy" ;
0181     static constexpr const double zero = 0. ;
0182     static constexpr const double Z_EPSILON = 1e-3 ;
0183     static constexpr const double UNBOUNDED_DEFAULT_EXTENT = 0. ;
0184 
0185     static void SetPOOL( POOL* pool_ );
0186     static int level();
0187     static std::string Desc();
0188     static void PrepareToSerialize();
0189     void prepare_to_serialize();
0190 
0191     // templating allows to work with both "sn*" and "const sn*"
0192     template<typename N> static std::string Desc(N* n);
0193     template<typename N> static std::string DescXform(N* n);
0194     template<typename N> static std::string DescXformFull(N* n);
0195     template<typename N> static std::string DescPrim(N* n);
0196 
0197     template<typename N> static std::string Desc(const std::vector<N*>& nds, bool reverse=false);
0198     template<typename N> static std::string DescXform(const std::vector<N*>& nds, bool reverse=false);
0199     template<typename N> static std::string DescXformFull(const std::vector<N*>& nds, bool reverse=false);
0200     template<typename N> static std::string DescPrim(const std::vector<N*>& nds, bool reverse=false);
0201 
0202     template<typename N> static std::string Brief(const std::vector<N*>& nds, bool reverse=false);
0203 
0204 
0205     static int Index(const sn* n);
0206 
0207     int  idx() const ;  // to match snd.hh
0208     int  index() const ;
0209     int  parent_index() const ;
0210 
0211     bool is_root() const ;
0212 
0213     int  num_child() const ;
0214     sn*  first_child() const ;
0215     sn*  last_child() const ;
0216     sn*  get_child(int ch) const ;
0217     sn*  get_left() const ;
0218     sn*  get_right() const ;
0219 
0220     int  total_siblings() const ;
0221     int  child_index( const sn* ch ) ;
0222     int  sibling_index() const ;
0223     const sn*  get_sibling(int sx) const ; // returns this when sx is sibling_index
0224     const sn*  next_sibling() const ;      // returns nullptr when this is last
0225 
0226     static void Serialize(     _sn& p, const sn* o );
0227     static sn*  Import(  const _sn* p, const std::vector<_sn>& buf );
0228     static sn*  Import_r(const _sn* p, const std::vector<_sn>& buf, int d );
0229 
0230     static constexpr const bool LEAK = false ;
0231 
0232 
0233     sn(int typecode, sn* left, sn* right);
0234 #ifdef WITH_CHILD
0235     void add_child( sn* ch );
0236 #endif
0237 
0238     ~sn();
0239 
0240 
0241     void disown_child(sn* ch) ;
0242     sn* deepcopy() const ;
0243     sn* deepcopy_r(int d) const ;
0244 
0245     sn* deepcopy_excluding_leaf(const sn* l) const ;
0246     sn* deepcopy_excluding_leaf_r(int d, const sn* l) const ;
0247 
0248 
0249     sn* copy() const ;
0250 
0251     static void DeepCopy(std::vector<sn*>& p1, const std::vector<sn*>& p0) ;
0252 
0253     void set_child( int ix, sn* ch, bool copy );
0254     void set_child_leaking_prior( int ix, sn* ch, bool copy );
0255 
0256 
0257     void set_left( sn* left, bool copy );
0258     void set_right( sn* right, bool copy  );
0259 
0260     bool is_primitive() const ;
0261     bool is_complement() const ;
0262     bool is_complement_primitive() const ;
0263     bool is_bileaf() const ;
0264     bool is_operator() const ;
0265     bool is_zero() const ;
0266 
0267     bool is_lrzero() const ;  //  l-zero AND  r-zero
0268     bool is_rzero() const ;   // !l-zero AND  r-zero
0269     bool is_lzero() const ;   //  l-zero AND !r-zero
0270 
0271     int num_node() const ;
0272     int num_node_r(int d) const ;
0273 
0274     int num_notsupported_node() const ;
0275     int num_notsupported_node_r(int d) const ;
0276 
0277 
0278     int num_leaf() const ;
0279     int num_leaf_r(int d) const ;
0280 
0281     int maxdepth() const ;
0282     int maxdepth_r(int d) const ;
0283 
0284     void labeltree();
0285 
0286     int labeltree_maxdepth() ;
0287     int labeltree_maxdepth_r(int d) ;
0288 
0289     void labeltree_subdepth() ;
0290     void labeltree_subdepth_r(int d);
0291 
0292     int checktree() const ;
0293     int checktree_r(char code,  int d ) const ;
0294 
0295     unsigned operators(int minsubdepth) const ;
0296     void operators_v(unsigned& mask, int minsubdepth) const ;
0297     void operators_r(unsigned& mask, int minsubdepth) const ;
0298 
0299     void typecodes(std::set<int>& tcs, int minsubdepth=0 ) const ;
0300     void typecodes_r(std::set<int>& tcs, int minsubdepth ) const ;
0301     std::string desc_typecodes() const ;
0302     int  typecodes_count(const std::vector<int>& tcq, int minsubdepth=0 ) const ;
0303     std::string desc_typecodes_count() const ;
0304 
0305 
0306     bool is_positive_form() const ;
0307     bool is_listnode() const ;
0308     std::string tag() const ;
0309 
0310     void preorder( std::vector<const sn*>& order ) const ;
0311     void inorder(  std::vector<const sn*>& order ) const ;
0312     void postorder(std::vector<const sn*>& order ) const ;
0313 
0314     void preorder_r( std::vector<const sn*>& order, int d ) const ;
0315     void inorder_r(  std::vector<const sn*>& order, int d ) const ;
0316     void postorder_r(std::vector<const sn*>& order, int d ) const ;
0317 
0318     void inorder_(std::vector<sn*>& order ) ;
0319     void inorder_r_(std::vector<sn*>& order, int d );
0320 
0321 
0322 
0323 
0324     std::string desc_order(const std::vector<const sn*>& order ) const ;
0325     std::string desc() const ;
0326     std::string desc_xform() const ;
0327     std::string desc_xform_full() const ;
0328     std::string desc_prim() const ;
0329     std::string desc_prim_all(bool reverse) const ;
0330 
0331     std::string id() const ;
0332     std::string brief() const ;
0333     std::string desc_child() const ;
0334     std::string desc_this() const ;
0335 
0336     std::string desc_r() const ;
0337     void desc_r(int d, std::stringstream& ss) const ;
0338 
0339     std::string detail_r() const ;
0340     void detail_r(int d, std::stringstream& ss) const ;
0341     std::string detail() const ;
0342 
0343 
0344     std::string render() const ;
0345     std::string render_typetag() const ;
0346     std::string render_parent() const ;
0347     std::string rdr() const ;
0348     std::string render(int mode) const ;
0349 
0350     enum { MINIMAL, TYPECODE, DEPTH, SUBDEPTH, TYPETAG, PID, NOTE, PARENT, IDX,  NUM_MODE=9 } ;
0351 
0352     static constexpr const char* MODE_MINIMAL = "MINIMAL" ;
0353     static constexpr const char* MODE_TYPECODE = "TYPECODE" ;
0354     static constexpr const char* MODE_DEPTH = "DEPTH" ;
0355     static constexpr const char* MODE_SUBDEPTH = "SUBDEPTH" ;
0356     static constexpr const char* MODE_TYPETAG = "TYPETAG" ;
0357     static constexpr const char* MODE_PID = "PID" ;
0358     static constexpr const char* MODE_NOTE = "NOTE" ;
0359     static constexpr const char* MODE_PARENT = "PARENT" ;
0360     static constexpr const char* MODE_IDX = "IDX" ;
0361 
0362     static const char* rendermode(int mode);
0363 
0364     void render_r(scanvas* canvas, std::vector<const sn*>& order, int mode, int d) const ;
0365 
0366 
0367     static int BinaryTreeHeight(int num_leaves);
0368     static int BinaryTreeHeight_1(int num_leaves);
0369 
0370     static sn* ZeroTree_r(int elevation, int op);
0371     static sn* ZeroTree(int num_leaves, int op );
0372 
0373     static sn* CommonOperatorTypeTree(   std::vector<int>& leaftypes,  int op );
0374 
0375     void populate_leaftypes(std::vector<int>& leaftypes );
0376     void populate_leaves(   std::vector<sn*>& leaves );
0377 
0378 
0379     void prune();
0380     void prune_r(int d) ;
0381     bool has_dangle() const ;
0382 
0383     void positivize() ;
0384     void positivize_r(bool negate, int d) ;
0385     void flip_complement();
0386     void flip_complement_child();
0387 
0388 
0389 
0390     void zero_label();
0391     void set_label( const char* label_ );
0392     void set_lvid(int lvid_);
0393     void set_lvid_r(int lvid_, int d);
0394     int  check_idx(const char* msg) const ;
0395     int  check_idx_r(int d, const char* msg) const ;
0396 
0397 
0398     void setPA( double x, double y, double z, double w, double z1, double z2 );
0399     const double* getPA_data() const  ;
0400     void    copyPA_data(double* dst) const ;
0401 
0402     void setBB(  double x0, double y0, double z0, double x1, double y1, double z1 );
0403     void setBB(  double x0 );
0404 
0405     const double* getBB_data() const ;
0406     void    copyBB_data(double* dst) const ;
0407 
0408     double  getBB_xmin() const ;
0409     double  getBB_ymin() const ;
0410     double  getBB_zmin() const ;
0411 
0412     double  getBB_xmax() const ;
0413     double  getBB_ymax() const ;
0414     double  getBB_zmax() const ;
0415 
0416     double  getBB_xavg() const ;
0417     double  getBB_yavg() const ;
0418     double  getBB_zavg() const ;
0419 
0420     static double AABB_XMin( const sn* n );
0421     static double AABB_YMin( const sn* n );
0422     static double AABB_ZMin( const sn* n );
0423 
0424     static double AABB_XMax( const sn* n );
0425     static double AABB_YMax( const sn* n );
0426     static double AABB_ZMax( const sn* n );
0427 
0428     static double AABB_XAvg( const sn* n );
0429     static double AABB_YAvg( const sn* n );
0430     static double AABB_ZAvg( const sn* n );
0431 
0432 
0433 
0434 
0435     bool hasXF() const ;
0436     void setXF();  // set identity transform
0437     void setXF(     const glm::tmat4x4<double>& t );
0438     void setXF(     const glm::tmat4x4<double>& t, const glm::tmat4x4<double>& v ) ;
0439     void combineXF( const glm::tmat4x4<double>& t );
0440     void combineXF( const glm::tmat4x4<double>& t, const glm::tmat4x4<double>& v ) ;
0441     std::string descXF() const ;
0442 
0443     static sn* Cylinder(double radius, double z1, double z2) ;
0444     static sn* CutCylinder(
0445         double R,
0446         double dz,
0447         double _pz_nrm_x,
0448         double _pz_nrm_y,
0449         double _pz_nrm_z,
0450         double _nz_nrm_x,
0451         double _nz_nrm_y,
0452         double _nz_nrm_z
0453     );
0454 
0455     static void CutCylinderZRange(
0456         double& zmin,
0457         double& zmax,
0458         double R,
0459         double dz,
0460         double pz_nrm_x,
0461         double pz_nrm_y,
0462         double pz_nrm_z,
0463         double nz_nrm_x,
0464         double nz_nrm_y,
0465         double nz_nrm_z
0466     );
0467 
0468     static sn* Cone(double r1, double z1, double r2, double z2);
0469     static sn* Sphere(double radius);
0470     static sn* ZSphere(double radius, double z1, double z2);
0471     static sn* Box3(double fullside);
0472     static sn* Box3(double fx, double fy, double fz );
0473     static sn* Torus(double rmin, double rmax, double rtor, double startPhi_deg, double deltaPhi_deg );
0474 
0475     static constexpr const char* sn__PhiCut_PACMAN_ALLOWED = "sn__PhiCut_PACMAN_ALLOWED" ;
0476     static sn* PhiCut(double phi0, double phi1);
0477     static sn* HalfSpace(double x, double y, double z, double w);
0478     static sn* Notsupported();
0479 
0480     static sn* Zero(double  x,  double y,  double z,  double w,  double z1, double z2);
0481     static sn* Zero();
0482     static sn* Prim(int typecode) ;
0483     static sn* Create(int typecode, sn* left=nullptr, sn* right=nullptr );
0484     static sn* Boolean( int op, sn* l, sn* r );
0485 
0486     static void ZNudgeExpandEnds(   int lvid, std::vector<sn*>& prims, bool enable);
0487     static void ZNudgeOverlapJoints(int lvid, std::vector<sn*>& prims, bool enable);
0488     static void ZNudgeOverlapJoint(int lvid, int i, sn* lower, sn* upper, bool enable, std::ostream* out  );
0489 
0490     bool can_znudge() const ;
0491     static bool CanZNudgeAll(std::vector<sn*>& prims);
0492 
0493 
0494     enum {
0495             NOTE_INCREASE_ZMAX = 0x1 << 0,
0496             NOTE_DECREASE_ZMIN = 0x1 << 1,
0497             HINT_LISTNODE_PRIM_DISCONTIGUOUS = 0x1 << 2,
0498             HINT_LISTNODE_PRIM_CONTIGUOUS    = 0x1 << 3
0499          };
0500 
0501     static unsigned NameHint(const char* name);
0502     static constexpr const char* _HINT_LISTNODE_PRIM_DISCONTIGUOUS = "HINT_LISTNODE_PRIM_DISCONTIGUOUS" ;
0503     static constexpr const char* _HINT_LISTNODE_PRIM_CONTIGUOUS    = "HINT_LISTNODE_PRIM_CONTIGUOUS" ;
0504     void set_hint_note(unsigned hint);
0505     void set_hint_listnode_prim_discontiguous();
0506     void set_hint_listnode_prim_contiguous();
0507     bool  is_hint_listnode_prim_discontiguous() const ;
0508     bool  is_hint_listnode_prim_contiguous() const ;
0509 
0510     void increase_zmax( double dz ); // expand upwards in +Z direction
0511     void decrease_zmin( double dz ); // expand downwards in -Z direction
0512 
0513     void increase_zmax_( double dz );
0514     void decrease_zmin_( double dz );
0515 
0516     void increase_zmax_cone( double dz );
0517     void decrease_zmin_cone( double dz );
0518 
0519 
0520 
0521 
0522     double zmin() const ;
0523     double zmax() const ;
0524     void set_zmin(double zmin_) ;
0525     void set_zmax(double zmax_) ;
0526 
0527     double    rperp_at_zmax() const ;
0528     void set_rperp_at_zmax(double rperp_) const ;
0529 
0530     double   rperp_at_zmin() const ;
0531     void set_rperp_at_zmin(double rperp_) const ;
0532 
0533 
0534     static double Sphere_RPerp_At_Z(double r, double z);
0535     double rperp_at_zmin_zsphere() const ;
0536     double rperp_at_zmax_zsphere() const ;
0537 
0538 
0539 
0540 
0541     static std::string ZDesc(const std::vector<sn*>& prims);
0542 
0543     void getParam_(double& p0, double& p1, double& p2, double& p3, double& p4, double& p5 ) const ;
0544     const double* getParam() const ;
0545     const double* getAABB() const ;
0546     bool hasAABB() const ;  // not-nullptr and not all zero
0547 
0548 
0549     static sn* Collection( std::vector<sn*>& prims );
0550     static sn* UnionTree(  std::vector<sn*>& prims );
0551     static sn* Contiguous( std::vector<sn*>& prims );
0552     static sn* Discontiguous( std::vector<sn*>& prims );
0553     static sn* Compound(   std::vector<sn*>& prims, int typecode_ );
0554 
0555     static sn* Buggy_CommonOperatorTree( std::vector<sn*>& leaves    , int op );
0556     static sn* BuildCommonTypeTree_Unbalanced( const std::vector<sn*>& leaves, int typecode );
0557 
0558     static void GetLVListnodes( std::vector<const sn*>& lns, int lvid );
0559     static int  GetChildTotal(  const std::vector<const sn*>& nds );
0560 
0561     static void GetLVNodes(  std::vector<sn*>& nds, int lvid );
0562     static void GetLVNodes_( std::vector<const sn*>& nds, int lvid );
0563     void getLVNodes( std::vector<sn*>& nds ) const ;
0564     static bool Includes(const std::vector<sn*>& nds, sn* nd );
0565 
0566     static sn* Get(int idx);
0567     static void FindLVRootNodes( std::vector<sn*>& nds, int lvid );
0568     static sn* GetLVRoot( int lvid );
0569 
0570     std::string rbrief() const ;
0571     void rbrief_r(std::ostream& os, int d) const  ;
0572 
0573     bool has_type(const std::vector<OpticksCSG_t>& types) const ;
0574     template<typename ... Args>
0575     void typenodes_(std::vector<const sn*>& nds, Args ... tcs ) const ;
0576     void typenodes_r_(std::vector<const sn*>& nds, const std::vector<OpticksCSG_t>& types, int d) const ;
0577 
0578     int max_binary_depth() const ;
0579     int max_binary_depth_r(int d) const ;
0580 
0581     uint64_t getLVBinNode() const ;
0582     uint64_t getLVSubNode() const ;
0583     uint64_t getLVNumNode() const ;
0584 
0585     static void GetLVNodesComplete(std::vector<const sn*>& nds, int lvid);
0586     void        getLVNodesComplete(std::vector<const sn*>& nds) const ;
0587     static void GetLVNodesComplete_r(std::vector<const sn*>& nds, const sn* nd, int idx);
0588 
0589     static void SelectListNode(std::vector<const sn*>& lns, const std::vector<const sn*>& nds);
0590 
0591     void ancestors(std::vector<const sn*>& nds) const ;
0592 
0593     void connectedtype_ancestors(std::vector<const sn*>& nds ) const ;
0594     static void ConnectedTypeAncestors(const sn* n, std::vector<const sn*>& nds, int q_typecode);
0595 
0596     void collect_progeny( std::vector<const sn*>& progeny, int exclude_typecode ) const ;
0597     static void CollectProgeny_r( const sn* n, std::vector<const sn*>& progeny, int exclude_typecode );
0598 
0599     void collect_prim( std::vector<const sn*>& prim ) const ;
0600     void collect_prim_r( std::vector<const sn*>& prim, int d ) const ;
0601 
0602     bool has_note(int q_note) const ;
0603     void collect_prim_note(  std::vector<sn*>& prim, int q_note );
0604     void collect_prim_note_r( std::vector<sn*>& prim, int q_note, int d );
0605 
0606     sn* find_joint_to_candidate_listnode( std::vector<sn*>& prim, int q_note ) ;
0607     sn* find_joint_to_candidate_listnode_r( int d,  std::vector<sn*>& prim, int q_note );
0608     bool has_candidate_listnode(int q_note);
0609     bool has_candidate_listnode_discontiguous() ;
0610     bool has_candidate_listnode_contiguous() ;
0611 
0612     static sn* CreateSmallerTreeWithListNode(sn* root0, int q_note);
0613     static sn* CreateSmallerTreeWithListNode_discontiguous(sn* root0);
0614     static sn* CreateSmallerTreeWithListNode_contiguous(   sn* root0);
0615     static int TypeFromNote(int q_note);
0616 
0617     void collect_monogroup( std::vector<const sn*>& monogroup ) const ;
0618 
0619     static bool AreFromSameMonogroup(const sn* a, const sn* b, int op);
0620     static bool AreFromSameUnion(const sn* a, const sn* b);
0621 
0622 
0623 
0624 
0625     static void NodeTransformProduct(
0626         int idx,
0627         glm::tmat4x4<double>& t,
0628         glm::tmat4x4<double>& v,
0629         bool reverse,
0630         std::ostream* out,
0631         VTR* t_stack );
0632 
0633     static std::string DescNodeTransformProduct(
0634         int idx,
0635         glm::tmat4x4<double>& t,
0636         glm::tmat4x4<double>& v,
0637         bool reverse );
0638 
0639     void getNodeTransformProduct(
0640         glm::tmat4x4<double>& t,
0641         glm::tmat4x4<double>& v,
0642         bool reverse,
0643         std::ostream* out,
0644         VTR* t_stack ) const ;
0645 
0646     std::string desc_getNodeTransformProduct(
0647         glm::tmat4x4<double>& t,
0648         glm::tmat4x4<double>& v,
0649         bool reverse) const ;
0650 
0651     double radius_sphere() const ;
0652 
0653     void setAABB_LeafFrame() ;
0654     void setAABB_LeafFrame_All() ;
0655 
0656     void setAABB_TreeFrame_All() ;
0657     //void setAABB(); // CONSIDER CAREFULLY between setAABB_(Tree/Leaf)Frame_All
0658 
0659 
0660     void postconvert(int lvid);
0661 
0662     template<typename N>
0663     static void OrderPrim( std::vector<N*>& prim, std::function<double(N* p)> fn, bool ascending  );
0664 
0665 
0666 
0667 
0668     static void Transform_Leaf2Tree( glm::tvec3<double>& xyz,  const sn* leaf, std::ostream* out );
0669 
0670     void uncoincide() ;
0671     void uncoincide_( bool enable, std::ostream* out);
0672     void uncoincide_zminmax( int i, sn* lower, sn* upper, bool enable, std::ostream* out  ) ;
0673 
0674 
0675 };  // END
0676 
0677 
0678 
0679 inline int sn::Check_LEAK(const char* msg, int i)  // static
0680 {
0681     //const char* spacer = "\n\n" ;
0682     const char* spacer = "" ;
0683 
0684     std::stringstream ss ;
0685     ss << "sn::Check_LEAK[" << std::setw(3) << i << "] " << std::setw(30) << ( msg ? msg : "-" ) << "  " << sn::pool->brief() << spacer << std::endl ;
0686     std::string str = ss.str();
0687     std::cout << str ;
0688 
0689     if(!sn::LEAK)
0690     {
0691         assert( sn::pool->size() == 0 );
0692     }
0693 
0694     if(!s_tv::LEAK)
0695     {
0696         assert( s_tv::pool->size() == 0 );
0697     }
0698 
0699     if(!s_pa::LEAK)
0700     {
0701         assert( s_pa::pool->size() == 0 );
0702     }
0703 
0704     if(!s_bb::LEAK)
0705     {
0706         assert( s_bb::pool->size() == 0 );
0707     }
0708 
0709     return 0 ;
0710 }
0711 
0712 
0713 
0714 
0715 
0716 
0717 
0718 
0719 inline void        sn::SetPOOL( POOL* pool_ ){ pool = pool_ ; }
0720 inline int         sn::level() {  return ssys::getenvint("sn__level",-1) ; } // static
0721 
0722 
0723 inline void sn::PrepareToSerialize()  // static
0724 {
0725     pool->prepare_to_serialize();
0726 }
0727 
0728 inline void sn::prepare_to_serialize()
0729 {
0730     if(!hasXF()) setXF();  // ensure all node have a transform before serialization
0731 }
0732 
0733 
0734 inline std::string sn::Desc(){    return pool ? pool->desc() : "-" ; } // static
0735 
0736 
0737 
0738 
0739 
0740 template<typename N>
0741 inline std::string sn::Desc(N* n) // static
0742 {
0743     return n ? n->desc() : "(null)" ;
0744 }
0745 
0746 template<typename N>
0747 inline std::string sn::DescXform(N* n) // static
0748 {
0749     return n ? n->desc_xform() : "(null)" ;
0750 }
0751 
0752 template<typename N>
0753 inline std::string sn::DescXformFull(N* n) // static
0754 {
0755     return n ? n->desc_xform_full() : "(null)" ;
0756 }
0757 
0758 template<typename N>
0759 inline std::string sn::DescPrim(N* n) // static
0760 {
0761     return n ? n->desc_prim() : "(null)" ;
0762 }
0763 
0764 
0765 template<typename N>
0766 inline std::string sn::Desc(const std::vector<N*>& nds, bool reverse) // static
0767 {
0768     int num_nd = nds.size() ;
0769     std::stringstream ss ;
0770     ss << "sn::Desc num_nd " << num_nd << ( reverse ? " DESC ORDER REVERSED " : "-" ) << std::endl ;
0771     for(int i=0 ; i < num_nd ; i++) ss << Desc(nds[reverse ? num_nd - 1 - i : i]) << std::endl ;
0772     std::string str = ss.str();
0773     return str ;
0774 }
0775 
0776 template<typename N>
0777 inline std::string sn::DescXform(const std::vector<N*>& nds, bool reverse) // static
0778 {
0779     int num_nd = nds.size() ;
0780     std::stringstream ss ;
0781     ss << "sn::DescXform num_nd " << num_nd << ( reverse ? " DESC ORDER REVERSED " : "-" ) << std::endl ;
0782     for(int i=0 ; i < num_nd ; i++) ss << DescXform(nds[reverse ? num_nd - 1 - i : i]) << std::endl ;
0783     std::string str = ss.str();
0784     return str ;
0785 }
0786 
0787 template<typename N>
0788 inline std::string sn::DescXformFull(const std::vector<N*>& nds, bool reverse) // static
0789 {
0790     int num_nd = nds.size() ;
0791     std::stringstream ss ;
0792     ss << "sn::DescXformFull num_nd " << num_nd << ( reverse ? " DESC ORDER REVERSED " : "-" ) << std::endl ;
0793     for(int i=0 ; i < num_nd ; i++) ss << DescXformFull(nds[reverse ? num_nd - 1 - i : i]) << std::endl ;
0794     std::string str = ss.str();
0795     return str ;
0796 }
0797 
0798 
0799 template<typename N>
0800 inline std::string sn::DescPrim(const std::vector<N*>& nds, bool reverse) // static
0801 {
0802     int num_nd = nds.size() ;
0803     std::stringstream ss ;
0804     ss << "sn::DescPrim num_nd " << num_nd << ( reverse ? " DESC ORDER REVERSED " : "-" ) << std::endl ;
0805     for(int i=0 ; i < num_nd ; i++) ss << DescPrim(nds[reverse ? num_nd - 1 - i : i]) << std::endl ;
0806     std::string str = ss.str();
0807     return str ;
0808 }
0809 
0810 
0811 template<typename N>
0812 inline std::string sn::Brief(const std::vector<N*>& nds, bool reverse) // static
0813 {
0814     int num_nd = nds.size() ;
0815     std::stringstream ss ;
0816     ss << "sn::Brief num_nd " << num_nd << ( reverse ? " DESC ORDER REVERSED " : "-" ) << std::endl ;
0817     for(int i=0 ; i < num_nd ; i++) ss << Desc(nds[reverse ? num_nd - 1 - i : i]) << std::endl ;
0818     std::string str = ss.str();
0819     return str ;
0820 }
0821 
0822 
0823 
0824 inline int sn::Index(const sn* n){ return pool ? pool->index(n) : -1 ; } // static
0825 inline int  sn::idx() const { return Index(this); } // to match snd.hh
0826 inline int  sn::index() const { return Index(this); }
0827 inline int  sn::parent_index() const { return parent ? Index(parent) : -2 ; }
0828 inline bool sn::is_root() const { return parent == nullptr ; }
0829 
0830 
0831 
0832 inline int sn::num_child() const
0833 {
0834 #ifdef WITH_CHILD
0835     return int(child.size());
0836 #else
0837     return left && right ? 2 : 0 ;
0838 #endif
0839 }
0840 
0841 inline sn* sn::first_child() const
0842 {
0843 #ifdef WITH_CHILD
0844     return child.size() > 0 ? child[0] : nullptr ;
0845 #else
0846     return left ;
0847 #endif
0848 }
0849 inline sn* sn::last_child() const
0850 {
0851 #ifdef WITH_CHILD
0852     return child.size() > 0 ? child[child.size()-1] : nullptr ;
0853 #else
0854     return right ;
0855 #endif
0856 }
0857 inline sn* sn::get_child(int ch) const
0858 {
0859 #ifdef WITH_CHILD
0860     return ch > -1 && ch < int(child.size()) ? child[ch] : nullptr ;
0861 #else
0862     switch(ch)
0863     {
0864         case 0: return left  ; break ;
0865         case 1: return right ; break ;
0866     }
0867     return nullptr ;
0868 #endif
0869 }
0870 
0871 
0872 inline sn* sn::get_left() const
0873 {
0874     sn* l = nullptr ;
0875 #ifdef WITH_CHILD
0876     assert( child.size() == 2 );
0877     l = child[0] ;
0878 #else
0879     l = left ;
0880 #endif
0881     return l ;
0882 }
0883 
0884 inline sn* sn::get_right() const
0885 {
0886     sn* r = nullptr ;
0887 #ifdef WITH_CHILD
0888     assert( child.size() == 2 );
0889     r = child[1] ;
0890 #else
0891     r = right ;
0892 #endif
0893     return r ;
0894 }
0895 
0896 
0897 
0898 
0899 
0900 
0901 inline int sn::total_siblings() const
0902 {
0903 #ifdef WITH_CHILD
0904     return parent ? int(parent->child.size()) : 1 ;  // root regarded as sole sibling (single child)
0905 #else
0906     if(parent == nullptr) return 1 ;
0907     return ( parent->left && parent->right ) ? 2 : -1 ;
0908 #endif
0909 }
0910 
0911 inline int sn::child_index( const sn* ch )
0912 {
0913 #ifdef WITH_CHILD
0914     size_t idx = std::distance( child.begin(), std::find( child.begin(), child.end(), ch )) ;
0915     return idx < child.size() ? idx : -1 ;
0916 #else
0917     int idx = -1 ;
0918     if(      ch == left )  idx = 0 ;
0919     else if( ch == right ) idx = 1 ;
0920     return idx ;
0921 #endif
0922 }
0923 
0924 inline int sn::sibling_index() const
0925 {
0926     int tot_sib = total_siblings() ;
0927     int sibdex = parent == nullptr ? 0 : parent->child_index(this) ;
0928 
0929     if(level() > 1) std::cout << "sn::sibling_index"
0930               << " tot_sib " << tot_sib
0931               << " sibdex " << sibdex
0932               << std::endl
0933               ;
0934 
0935     assert( sibdex < tot_sib );
0936     return sibdex ;
0937 }
0938 
0939 inline const sn* sn::get_sibling(int sx) const     // NB this return self for appropriate sx
0940 {
0941 #ifdef WITH_CHILD
0942     assert( sx < total_siblings() );
0943     return parent ? parent->child[sx] : this ;
0944 #else
0945     const sn* sib = nullptr ;
0946     switch(sx)
0947     {
0948         case 0: sib = parent ? parent->left  : nullptr ; break ;
0949         case 1: sib = parent ? parent->right : nullptr ; break ;
0950     }
0951     return sib ;
0952 #endif
0953 }
0954 
0955 inline const sn* sn::next_sibling() const
0956 {
0957     int next_sib = 1+sibling_index() ;
0958     int tot_sib = total_siblings() ;
0959 
0960     if(level() > 1) std::cout << "sn::next_sibling"
0961               << " tot_sib " << tot_sib
0962               << " next_sib " << next_sib
0963               << std::endl
0964               ;
0965 
0966     return next_sib < tot_sib  ? get_sibling(next_sib) : nullptr ;
0967 }
0968 
0969 /**
0970 sn::Serialize
0971 --------------
0972 
0973 The Serialize operates by converting pointer members into pool indices
0974 This T::Serialize is invoked from s_pool<T,P>::serialize_
0975 for with paired T and P for all pool objects.
0976 
0977 At first glance this looks like the WITH_CHILD vector of child nodes
0978 is restricted to working with two children, but that is not the case
0979 because the the full vector in the T pool gets represented via next_sibling
0980 links in the P buffer allowing any number of child nodes to be handled.
0981 This functionality is needed for multiunion.
0982 
0983 **/
0984 
0985 inline void sn::Serialize(_sn& n, const sn* x) // static
0986 {
0987     if(level() > 1) std::cout
0988         << "sn::Serialize ["
0989         << std::endl
0990         ;
0991 
0992     assert( pool      && "sn::pool  is required for sn::Serialize" );
0993     assert( s_tv::pool && "s_tv::pool is required for sn::Serialize" );
0994     assert( s_pa::pool && "s_pa::pool is required for sn::Serialize" );
0995     assert( s_bb::pool && "s_bb::pool is required for sn::Serialize" );
0996 
0997     n.typecode = x->typecode ;
0998     n.complement = x->complement ;
0999     n.lvid = x->lvid ;
1000 
1001     n.xform = s_tv::pool->index(x->xform) ;
1002     n.param = s_pa::pool->index(x->param) ;
1003     n.aabb = s_bb::pool->index(x->aabb) ;
1004     n.parent = pool->index(x->parent);
1005 
1006 #ifdef WITH_CHILD
1007     n.sibdex = x->sibling_index();  // 0 for root
1008     n.num_child = x->num_child() ;
1009     n.first_child = pool->index(x->first_child());
1010     n.next_sibling = pool->index(x->next_sibling());
1011 #else
1012     n.left  = pool->index(x->left);
1013     n.right = pool->index(x->right);
1014 #endif
1015 
1016     n.index = pool->index(x) ;
1017     n.depth = x->depth ;
1018     n.note  = x->note  ;
1019     n.coincide  = x->coincide  ;
1020 
1021     assert( sizeof(n.label) == sizeof(x->label) );
1022     strncpy( &n.label[0], x->label, sizeof(n.label) );
1023 
1024 
1025     if(level() > 1) std::cout
1026         << "sn::Serialize ]"
1027         << std::endl
1028         << "(sn)x"
1029         << std::endl
1030         << x->desc()
1031         << std::endl
1032         << "(_sn)n"
1033         << std::endl
1034         << n.desc()
1035         << std::endl
1036         ;
1037 
1038 
1039 }
1040 
1041 /**
1042 sn::Import
1043 -----------
1044 
1045 Used by s_pool<T,P>::import_ in a loop providing
1046 pointers to every entry in the vector buf.
1047 However only root_importable _sn nodes with parent -1
1048 get recursively imported.
1049 
1050 **/
1051 
1052 inline sn* sn::Import( const _sn* p, const std::vector<_sn>& buf ) // static
1053 {
1054     if(level() > 0) std::cout << "sn::Import" << std::endl ;
1055     return p->is_root() ? Import_r(p, buf, 0) : nullptr ;
1056 }
1057 
1058 /**
1059 sn::Import_r
1060 -------------
1061 
1062 Note that because all _sn nodes are available in the buf
1063 issues of ordering of Import are avoided.
1064 
1065 **/
1066 
1067 inline sn* sn::Import_r(const _sn* _n,  const std::vector<_sn>& buf, int d)
1068 {
1069     assert( s_tv::pool && "s_tv::pool is required for sn::Import_r " );
1070     if(level() > 0) std::cout << "sn::Import_r d " << d << " " << ( _n ? _n->desc() : "(null)" ) << std::endl ;
1071     if(_n == nullptr) return nullptr ;
1072 
1073 #ifdef WITH_CHILD
1074     sn* n = Create( _n->typecode , nullptr, nullptr );
1075     n->complement = _n->complement ;
1076     n->lvid = _n->lvid ;
1077     n->xform = s_tv::pool->getbyidx(_n->xform) ;
1078     n->param = s_pa::pool->getbyidx(_n->param) ;
1079     n->aabb =  s_bb::pool->getbyidx(_n->aabb) ;
1080 
1081     const _sn* _child = _n->first_child  > -1 ? &buf[_n->first_child] : nullptr  ;
1082 
1083     while( _child )
1084     {
1085         sn* ch = Import_r( _child, buf, d+1 );
1086         n->add_child(ch);  // push_back and sets *ch->parent* to *n*
1087         _child = _child->next_sibling > -1 ? &buf[_child->next_sibling] : nullptr ;
1088     }
1089 #else
1090     const _sn* _l = _n->left  > -1 ? &buf[_n->left]  : nullptr ;
1091     const _sn* _r = _n->right > -1 ? &buf[_n->right] : nullptr ;
1092     sn* l = Import_r( _l, buf, d+1 );
1093     sn* r = Import_r( _r, buf, d+1 );
1094     sn* n = Create( _n->typecode, l, r );  // sn::sn ctor sets parent of l and r to n
1095     n->complement = _n->complement ;
1096     n->lvid = _n->lvid ;
1097     n->xform = s_tv::pool->getbyidx(_n->xform) ;
1098     n->param = s_pa::pool->getbyidx(_n->param) ;
1099     n->aabb = s_bb::pool->getbyidx(_n->aabb) ;
1100 #endif
1101     return n ;
1102 }
1103 
1104 
1105 
1106 /**
1107 sn::sn ctor
1108 -------------
1109 
1110 note that sn::pid cannot be relied upon for indexing into the pool
1111 as other ctor/dtor can change the pool while this holds on to the old stale pid
1112 
1113 **/
1114 
1115 inline sn::sn(int typecode_, sn* left_, sn* right_)
1116     :
1117     typecode(typecode_),
1118     complement(0),
1119     lvid(-1),
1120     xform(nullptr),
1121     param(nullptr),
1122     aabb(nullptr),
1123     parent(nullptr),
1124 #ifdef WITH_CHILD
1125 #else
1126     left(left_),
1127     right(right_),
1128 #endif
1129     depth(0),
1130     note(0),
1131     coincide(0),
1132     pid(pool ? pool->add(this) : -1),
1133     subdepth(0)
1134 {
1135     if(level() > 1) std::cout << "[ sn::sn " << id() << "\n" ;
1136     zero_label();
1137 
1138 #ifdef WITH_CHILD
1139     if(left_ && right_)
1140     {
1141         add_child(left_);   // sets parent of left_ to this
1142         add_child(right_);  // sets parent of right_ to this
1143     }
1144 #else
1145     if(left && right)
1146     {
1147         left->parent = this ;
1148         right->parent = this ;
1149     }
1150 #endif
1151 
1152     if(level() > 1) std::cout << "] sn::sn " << id() << "\n" ;
1153 }
1154 
1155 #ifdef WITH_CHILD
1156 inline void sn::add_child( sn* ch )
1157 {
1158     assert(ch);
1159     ch->parent = this ;
1160     child.push_back(ch) ;
1161 }
1162 #endif
1163 
1164 
1165 
1166 
1167 
1168 // dtor
1169 inline sn::~sn()
1170 {
1171     if(level() > 1) std::cout << "[ sn::~sn " << id() << "\n" ;
1172 
1173     delete xform ;
1174     delete param ;
1175     delete aabb  ;
1176     // parent is not deleted : as it is regarded as weakly linked (ie not owned by this node)
1177 
1178 
1179 #ifdef WITH_CHILD
1180     for(int i=0 ; i < int(child.size()) ; i++)
1181     {
1182         sn* ch = child[i] ;
1183         delete ch ;
1184     }
1185 #else
1186     delete left ;
1187     delete right ;
1188 #endif
1189 
1190     if(pool) pool->remove(this);
1191 
1192     if(level() > 1) std::cout << "] sn::~sn " << id() << "\n" ;
1193 }
1194 
1195 
1196 
1197 
1198 
1199 
1200 
1201 #ifdef WITH_CHILD
1202 /**
1203 sn::disown_child
1204 ------------------
1205 
1206 * find ch pointer within child vector
1207 * appy std::vector::erase with the iterator
1208 
1209 HUH: contrary to prior note the std::vector::erase
1210 simply removes from the vector, it DOES NOT
1211 call the dtor of the erased pointer
1212 
1213 Prior note was wrong::
1214 
1215     Note that the erase calls the dtor which
1216     will also delete child nodes (recursively)
1217     and removes pool entries.
1218 
1219 **/
1220 inline void sn::disown_child(sn* ch)
1221 {
1222     typedef std::vector<sn*>::iterator IT ;
1223     IT it = std::find(child.begin(), child.end(), ch );
1224     if(it != child.end() ) child.erase(it) ;
1225 }
1226 #endif
1227 
1228 
1229 /**
1230 sn::deepcopy
1231 -------------
1232 
1233 Note that the xform, param and aabb pointers are
1234 now copied by sn::copy which is invoked from sn::deepcopy.
1235 This means that the copied tree is now fully independent
1236 from the source tree. This allows the source tree to be
1237 fully deleted without effecting the copied tree.
1238 
1239 **/
1240 
1241 inline sn* sn::deepcopy() const
1242 {
1243     return deepcopy_r(0);
1244 }
1245 
1246 /**
1247 sn::deepcopy_r
1248 ----------------
1249 
1250 The default copy ctor copies the child vector, but that is a shallow copy
1251 just duplicating pointers into the new child vector.
1252 Hence within the child loop below those shallow copies are disowned
1253 (removed from the child vector) and deep copies are made and added
1254 to the child vector of the copy
1255 
1256 **/
1257 
1258 inline sn* sn::deepcopy_r(int d) const
1259 {
1260     sn* n = copy();
1261 
1262 #ifdef WITH_CHILD
1263     for(int i=0 ; i < int(child.size()) ; i++)
1264     {
1265         sn* ch = child[i] ;
1266         n->disown_child( ch ) ;          // remove shallow copied child from the vector
1267         sn* deep_ch = ch->deepcopy_r(d+1) ;
1268         n->child.push_back( deep_ch );
1269     }
1270 #else
1271     // whether nullptr or not the shallow default copy
1272     // should have copied left and right
1273     assert( n->left == left );
1274     assert( n->right == right );
1275     // but thats just a shallow copy so replace here with deep copies
1276     n->left  = left  ? left->deepcopy_r(d+1) : nullptr ;
1277     n->right = right ? right->deepcopy_r(d+1) : nullptr ;
1278 #endif
1279 
1280     return n ;
1281 }
1282 
1283 
1284 
1285 inline sn* sn::deepcopy_excluding_leaf(const sn* l) const
1286 {
1287     return deepcopy_excluding_leaf_r(0, l );
1288 }
1289 
1290 inline sn* sn::deepcopy_excluding_leaf_r(int d, const sn* l) const
1291 {
1292     if(this == l) return nullptr ;
1293 
1294     sn* n = copy();
1295 
1296 #ifdef WITH_CHILD
1297     for(int i=0 ; i < int(child.size()) ; i++)
1298     {
1299         sn* ch = child[i] ;
1300         n->disown_child( ch ) ;          // remove shallow copied child from the vector
1301         sn* deep_ch = ch->deepcopy_excluding_leaf_r(d+1, l ) ;
1302         n->child.push_back( deep_ch );
1303     }
1304 #else
1305     // whether nullptr or not the shallow default copy
1306     // should have copied left and right
1307     assert( n->left == left );
1308     assert( n->right == right );
1309     // but thats just a shallow copy so replace here with deep copies
1310     n->left  = left  ? left->deepcopy_excluding_leaf_r(d+1) : nullptr ;
1311     n->right = right ? right->deepcopy_excluding_leaf_r(d+1) : nullptr ;
1312 #endif
1313     return n ;
1314 }
1315 
1316 
1317 
1318 
1319 
1320 
1321 
1322 
1323 
1324 
1325 /**
1326 sn::copy
1327 ---------
1328 
1329 default copy ctor just shallow copies the pointers and the pid,
1330 so fix those up here to make the copy truly independant of the
1331 original node
1332 
1333 **/
1334 
1335 inline sn* sn::copy() const
1336 {
1337     sn* n = new sn(*this) ;
1338 
1339     n->pid = pool ? pool->add(n) : -1 ;
1340     n->parent = nullptr ;
1341     n->xform = xform ? xform->copy() : nullptr ;
1342     n->param = param ? param->copy() : nullptr ;
1343     n->aabb  = aabb ? aabb->copy() : nullptr ;
1344 
1345     return n ;
1346 }
1347 
1348 
1349 /**
1350 sn::DeepCopy
1351 -------------
1352 
1353 It makes most sense to use this for copying prim nodes
1354 
1355 **/
1356 
1357 inline void sn::DeepCopy(std::vector<sn*>& p1, const std::vector<sn*>& p0) // static
1358 {
1359     for(unsigned i=0 ; i < p0.size() ; i++) p1.push_back(p0[i]->deepcopy());
1360 }
1361 
1362 
1363 
1364 /**
1365 sn::set_child
1366 ---------------
1367 
1368 When *ch* (the new child to be set) is from within the tree and
1369 not a newly created disconnected node, eg when pruning and moving
1370 around nodes it is necessary to deepcopy that preexisting node
1371 using copy:true
1372 
1373 
1374 copy:false
1375 
1376    * parent of *ch* is set to this
1377    * reference to child[ix] slot is obtained
1378    * old occupier of the child[ix] slot is deleted (for LEAK:false the standard)
1379    * child[ix] slot pointer set to *ch*
1380 
1381 copy:true
1382 
1383    * as above but deepcopy the *ch* first
1384 
1385 
1386 * NB former slot residents are deleted
1387 
1388 **/
1389 
1390 inline void sn::set_child( int ix, sn* ch, bool copy )
1391 {
1392     sn* new_ch = copy ? ch->deepcopy() : ch ;
1393     new_ch->parent = this ;
1394 
1395 #ifdef WITH_CHILD
1396     assert( ix < int(child.size()) );
1397     sn*& target = child[ix] ;
1398     if(!LEAK) delete target ;
1399     target = new_ch ;
1400 #else
1401     sn** target = ix == 0 ? &left : &right ;
1402     if(!LEAK) delete *target ;
1403     *target = new_ch ;
1404 #endif
1405 
1406 }
1407 
1408 
1409 inline void sn::set_child_leaking_prior( int ix, sn* ch, bool copy )
1410 {
1411     sn* new_ch = copy ? ch->deepcopy() : ch ;
1412     new_ch->parent = this ;
1413 
1414 #ifdef WITH_CHILD
1415     assert( ix < int(child.size()) );
1416     sn*& target = child[ix] ;
1417     target = new_ch ;
1418 #else
1419     sn** target = ix == 0 ? &left : &right ;
1420     *target = new_ch ;
1421 #endif
1422 
1423 }
1424 
1425 
1426 
1427 
1428 inline void sn::set_left( sn* ch, bool copy )
1429 {
1430     set_child(0, ch, copy );
1431 }
1432 inline void sn::set_right( sn* ch, bool copy )
1433 {
1434     set_child(1, ch, copy );
1435 }
1436 
1437 
1438 
1439 
1440 
1441 
1442 
1443 
1444 inline bool sn::is_primitive() const
1445 {
1446 #ifdef WITH_CHILD
1447     return child.size() == 0 ;
1448 #else
1449     return left == nullptr && right == nullptr ;
1450 #endif
1451 
1452 }
1453 
1454 inline bool sn::is_complement() const           { return complement == 1 ; }
1455 inline bool sn::is_complement_primitive() const { return is_complement() && is_primitive() ; }
1456 
1457 
1458 inline bool sn::is_bileaf() const
1459 {
1460 #ifdef WITH_CHILD
1461     int num_ch   = int(child.size()) ;
1462     int num_prim = 0 ;
1463     for(int i=0 ; i < num_ch ; i++) if(child[i]->is_primitive()) num_prim += 1 ;
1464     bool all_prim = num_prim == num_ch ;
1465     return !is_primitive() && all_prim ;
1466 #else
1467     return !is_primitive() && left->is_primitive() && right->is_primitive() ;
1468 #endif
1469 }
1470 inline bool sn::is_operator() const
1471 {
1472 #ifdef WITH_CHILD
1473     return child.size() == 2 ;
1474 #else
1475     return left != nullptr && right != nullptr ;
1476 #endif
1477 }
1478 inline bool sn::is_zero() const
1479 {
1480     return typecode == 0 ;
1481 }
1482 inline bool sn::is_lrzero() const
1483 {
1484 #ifdef WITH_CHILD
1485     int num_ch   = int(child.size()) ;
1486     int num_zero = 0 ;
1487     for(int i=0 ; i < num_ch ; i++) if(child[i]->is_zero()) num_zero += 1 ;
1488     bool all_zero = num_zero == num_ch ;
1489     return is_operator() && all_zero ;
1490 #else
1491     return is_operator() && left->is_zero() && right->is_zero() ;
1492 #endif
1493 }
1494 inline bool sn::is_rzero() const
1495 {
1496 #ifdef WITH_CHILD
1497     return is_operator() && !child[0]->is_zero() && child[1]->is_zero() ;
1498 #else
1499     return is_operator() && !left->is_zero() && right->is_zero() ;
1500 #endif
1501 }
1502 inline bool sn::is_lzero() const
1503 {
1504 #ifdef WITH_CHILD
1505     return is_operator() && child[0]->is_zero() && !child[1]->is_zero() ;
1506 #else
1507     return is_operator() && left->is_zero() && !right->is_zero() ;
1508 #endif
1509 }
1510 
1511 
1512 
1513 
1514 
1515 
1516 
1517 inline int sn::num_node() const
1518 {
1519     return num_node_r(0);
1520 }
1521 inline int sn::num_node_r(int d) const
1522 {
1523     int nn = 1 ;   // always at least 1 node,  no exclusion of CSG_ZERO
1524 #ifdef WITH_CHILD
1525     for(int i=0 ; i < int(child.size()) ; i++) nn += child[i]->num_node_r(d+1) ;
1526 #else
1527     nn += left ? left->num_node_r(d+1) : 0 ;
1528     nn += right ? right->num_node_r(d+1) : 0 ;
1529 #endif
1530     return nn ;
1531 }
1532 
1533 
1534 inline int sn::num_notsupported_node() const
1535 {
1536     return num_notsupported_node_r(0);
1537 }
1538 
1539 inline int sn::num_notsupported_node_r(int d) const
1540 {
1541     int nn = typecode == CSG_NOTSUPPORTED ? 1 : 0 ;
1542 #ifdef WITH_CHILD
1543     for(int i=0 ; i < int(child.size()) ; i++) nn += child[i]->num_notsupported_node_r(d+1) ;
1544 #else
1545     nn += left ? left->num_notsupported_node_r(d+1) : 0 ;
1546     nn += right ? right->num_notsupported_node_r(d+1) : 0 ;
1547 #endif
1548     return nn ;
1549 }
1550 
1551 
1552 
1553 
1554 inline int sn::num_leaf() const
1555 {
1556     return num_leaf_r(0);
1557 }
1558 inline int sn::num_leaf_r(int d) const
1559 {
1560     int nl = is_primitive() ? 1 : 0 ;
1561 #ifdef WITH_CHILD
1562     for(int i=0 ; i < int(child.size()) ; i++) nl += child[i]->num_leaf_r(d+1) ;
1563 #else
1564     nl += left ? left->num_leaf_r(d+1) : 0 ;
1565     nl += right ? right->num_leaf_r(d+1) : 0 ;
1566 #endif
1567     return nl ;
1568 }
1569 
1570 
1571 inline int sn::maxdepth() const
1572 {
1573     return maxdepth_r(0);
1574 }
1575 inline int sn::maxdepth_r(int d) const
1576 {
1577 #ifdef WITH_CHILD
1578     if( child.size() == 0 ) return d ;
1579     int mx = 0 ;
1580     for(int i=0 ; i < int(child.size()) ; i++) mx = std::max( mx, child[i]->maxdepth_r(d+1) ) ;
1581     return mx ;
1582 #else
1583     return left && right ? std::max( left->maxdepth_r(d+1), right->maxdepth_r(d+1)) : d ;
1584 #endif
1585 }
1586 
1587 
1588 
1589 inline void sn::labeltree()
1590 {
1591     labeltree_maxdepth();
1592     labeltree_subdepth();
1593 }
1594 
1595 inline int sn::labeltree_maxdepth()
1596 {
1597     return labeltree_maxdepth_r(0);
1598 }
1599 inline int sn::labeltree_maxdepth_r(int d)
1600 {
1601     depth = d ;
1602 
1603     int nc = num_child();
1604     if(nc == 0) return d ;
1605 
1606     int mx = 0 ;
1607     for(int i=0 ; i < nc ; i++)
1608     {
1609         sn* ch = get_child(i) ;
1610         mx = std::max(mx, ch->labeltree_maxdepth_r(d+1) ) ;
1611     }
1612     return mx ;
1613 }
1614 
1615 
1616 
1617 /**
1618 sn::labeltree_subdepth  (based on NTreeBalance::subdepth_r)
1619 ------------------------------------------------------------
1620 
1621 How far down can you go from each node.
1622 
1623 Labels the nodes with the subdepth, which is
1624 the max height of each node treated as a subtree::
1625 
1626 
1627                3
1628 
1629       [1]               2
1630 
1631    [0]    [0]       0          [1]
1632 
1633                            [0]     [0]
1634 
1635 
1636 bileafs are triplets of nodes with subdepths 1,0,0
1637 The above tree has two bileafs, one other leaf and root.
1638 
1639 **/
1640 
1641 inline void sn::labeltree_subdepth()
1642 {
1643     labeltree_subdepth_r(0);
1644 }
1645 inline void sn::labeltree_subdepth_r(int d)
1646 {
1647     subdepth = maxdepth() ;
1648     for(int i=0 ; i < num_child() ; i++)
1649     {
1650         sn* ch = get_child(i) ;
1651         ch->labeltree_subdepth_r(d+1) ;
1652     }
1653 }
1654 
1655 
1656 inline int sn::checktree() const
1657 {
1658     int chk_D = checktree_r('D', 0);
1659     int chk_P = checktree_r('P', 0);
1660     int chk = chk_D + chk_P ;
1661 
1662     if( chk > 0 )
1663     {
1664         if(level()>0) std::cout
1665             << "sn::checktree"
1666             << " chk_D " << chk_D
1667             << " chk_P " << chk_P
1668             << desc()
1669             << std::endl
1670             ;
1671     }
1672     return chk ;
1673 }
1674 
1675 
1676 inline int sn::checktree_r(char code,  int d ) const
1677 {
1678     int chk = 0 ;
1679 
1680     if( code == 'D' ) // check expected depth
1681     {
1682         if(d != depth) chk += 1 ;
1683     }
1684     else if( code == 'P' ) // check for non-roots without parent set
1685     {
1686         if( depth > 0 && parent == nullptr ) chk += 1 ;
1687     }
1688 
1689     for(int i=0 ; i < num_child() ; i++)
1690     {
1691         sn* ch = get_child(i) ;
1692         ch->checktree_r(code, d+1) ;
1693     }
1694 
1695     return chk ;
1696 }
1697 
1698 
1699 
1700 
1701 
1702 
1703 
1704 
1705 
1706 
1707 
1708 
1709 /**
1710 sn::operators (based on NTreeBalance::operators)
1711 ----------------------------------------------------
1712 
1713 Returns mask of CSG operators in the tree restricted to nodes with subdepth >= *minsubdepth*
1714 
1715 **/
1716 
1717 inline unsigned sn::operators(int minsubdepth) const
1718 {
1719    unsigned mask = 0 ;
1720    operators_r(mask, minsubdepth);
1721    return mask ;
1722 }
1723 
1724 
1725 inline void sn::operators_v(unsigned& mask, int minsubdepth) const
1726 {
1727     if( subdepth >= minsubdepth )
1728     {
1729         switch( typecode )
1730         {
1731             case CSG_UNION         : mask |= CSG::Mask(CSG_UNION)        ; break ;
1732             case CSG_INTERSECTION  : mask |= CSG::Mask(CSG_INTERSECTION) ; break ;
1733             case CSG_DIFFERENCE    : mask |= CSG::Mask(CSG_DIFFERENCE)   ; break ;
1734             default                : mask |= 0                           ; break ;
1735         }
1736     }
1737 }
1738 
1739 
1740 inline void sn::operators_r(unsigned& mask, int minsubdepth) const
1741 {
1742 #ifdef WITH_CHILD
1743     if(child.size() >= 2) operators_v(mask, minsubdepth) ;
1744     for(int i=0 ; i < int(child.size()) ; i++) child[i]->operators_r(mask, minsubdepth ) ;
1745 #else
1746     if(left && right )
1747     {
1748         operators_v(mask, minsubdepth );
1749         left->operators_r( mask, minsubdepth );
1750         right->operators_r( mask, minsubdepth );
1751     }
1752 #endif
1753 
1754 }
1755 
1756 
1757 
1758 /**
1759 sn::typecodes
1760 -------------
1761 
1762 Collect distinct typecode into the set for nodes with subdepth >= minsubdepth,
1763 minsubdepth=0 corresponds to entire tree.
1764 
1765 **/
1766 
1767 inline void sn::typecodes(std::set<int>& tcs, int minsubdepth ) const
1768 {
1769     typecodes_r(tcs, minsubdepth);
1770 }
1771 
1772 inline void sn::typecodes_r(std::set<int>& tcs, int minsubdepth ) const
1773 {
1774     if(subdepth >= minsubdepth) tcs.insert(typecode);
1775 #ifdef WITH_CHILD
1776     for(int i=0 ; i < int(child.size()) ; i++) child[i]->typecodes_r(tcs, minsubdepth ) ;
1777 #else
1778     if(left && right )
1779     {
1780         left->typecodes_r( tcs, minsubdepth );
1781         right->typecodes_r( tcs, minsubdepth );
1782     }
1783 #endif
1784 }
1785 
1786 
1787 inline std::string sn::desc_typecodes() const
1788 {
1789     std::stringstream ss ;
1790 
1791     std::set<int> tcs ;
1792     int minsubdepth = 0;
1793     typecodes(tcs, minsubdepth );
1794 
1795     ss << "[sn::desc_typecodes\n" ;
1796     for (int tc : tcs) ss << CSG::Name(tc) << "\n" ;
1797     ss << "]sn::desc_typecodes\n" ;
1798 
1799     std::string str = ss.str();
1800     return str ;
1801 }
1802 
1803 /**
1804 sn::typecodes_count
1805 ---------------------
1806 
1807 Returns the number of typecodes in the tcq query vector that
1808 are present in the set of typecodes of the sn node tree.
1809 
1810 **/
1811 
1812 inline int sn::typecodes_count(const std::vector<int>& tcq, int minsubdepth) const
1813 {
1814     std::set<int> tcs ;
1815     typecodes(tcs, minsubdepth );
1816 
1817     int count = 0;
1818     for (int value : tcq) if (tcs.count(value)) ++count;
1819     return count;
1820 }
1821 
1822 inline std::string sn::desc_typecodes_count() const
1823 {
1824     std::vector<int> tcq = {CSG_TORUS, CSG_NOTSUPPORTED, CSG_CUTCYLINDER } ;
1825 
1826     int minsubdepth = 0;
1827     int count = typecodes_count(tcq, minsubdepth );
1828 
1829     std::stringstream ss ;
1830     ss << "[sn::desc_typecodes_count [" << count << "]\n" ;
1831     for (int tc : tcq) ss << CSG::Name(tc) << "\n" ;
1832     ss << "]sn::desc_typecodes_count [" << count << "]\n" ;
1833 
1834     std::string str = ss.str();
1835     return str ;
1836 }
1837 
1838 
1839 
1840 
1841 
1842 
1843 
1844 
1845 
1846 
1847 inline bool sn::is_positive_form() const
1848 {
1849     unsigned ops = operators(0);  // minsubdepth:0 ie entire tree
1850     return (ops & CSG::Mask(CSG_DIFFERENCE)) == 0 ;
1851 }
1852 
1853 inline bool        sn::is_listnode() const { return CSG::IsList(typecode); }
1854 inline std::string sn::tag() const {         return CSG::Tag(typecode) ;  }
1855 
1856 
1857 
1858 inline void sn::preorder(std::vector<const sn*>& order ) const
1859 {
1860     preorder_r(order, 0);
1861 }
1862 inline void sn::inorder(std::vector<const sn*>& order ) const
1863 {
1864     inorder_r(order, 0);
1865 }
1866 inline void sn::postorder(std::vector<const sn*>& order ) const
1867 {
1868     postorder_r(order, 0);
1869 }
1870 
1871 
1872 inline void sn::preorder_r(std::vector<const sn*>& order, int d ) const
1873 {
1874     order.push_back(this);
1875 #ifdef WITH_CHILD
1876     for(int i=0 ; i < int(child.size()) ; i++) child[i]->preorder_r(order, d+1) ;
1877 #else
1878     if(left) left->preorder_r(order, d+1) ;
1879     if(right) right->preorder_r(order, d+1) ;
1880 #endif
1881 }
1882 
1883 /**
1884 sn::inorder_r
1885 -------------
1886 
1887 **/
1888 
1889 inline void sn::inorder_r(std::vector<const sn*>& order, int d ) const
1890 {
1891 #ifdef WITH_CHILD
1892     int nc = int(child.size()) ;
1893     if( nc > 0 )
1894     {
1895         int split = nc - 1 ;
1896         for(int i=0 ; i < split ; i++) child[i]->inorder_r(order, d+1) ;
1897         order.push_back(this);
1898         for(int i=split ; i < nc ; i++) child[i]->inorder_r(order, d+1) ;
1899     }
1900     else
1901     {
1902         order.push_back(this);
1903     }
1904 #else
1905     if(left) left->inorder_r(order, d+1) ;
1906     order.push_back(this);
1907     if(right) right->inorder_r(order, d+1) ;
1908 #endif
1909 }
1910 inline void sn::postorder_r(std::vector<const sn*>& order, int d ) const
1911 {
1912 #ifdef WITH_CHILD
1913     for(int i=0 ; i < int(child.size()) ; i++) child[i]->postorder_r(order, d+1) ;
1914 #else
1915     if(left) left->postorder_r(order, d+1) ;
1916     if(right) right->postorder_r(order, d+1) ;
1917 #endif
1918     order.push_back(this);
1919 }
1920 
1921 
1922 inline void sn::inorder_(std::vector<sn*>& order )
1923 {
1924     inorder_r_(order, 0);
1925 }
1926 inline void sn::inorder_r_(std::vector<sn*>& order, int d )
1927 {
1928 #ifdef WITH_CHILD
1929     int nc = int(child.size()) ;
1930     if( nc > 0 )
1931     {
1932         int split = nc - 1 ;
1933         for(int i=0 ; i < split ; i++) child[i]->inorder_r_(order, d+1) ;
1934         order.push_back(this);
1935         for(int i=split ; i < nc ; i++) child[i]->inorder_r_(order, d+1) ;
1936     }
1937     else
1938     {
1939         order.push_back(this);
1940     }
1941 #else
1942     if(left) left->inorder_r_(order, d+1) ;
1943     order.push_back(this);
1944     if(right) right->inorder_r_(order, d+1) ;
1945 #endif
1946 }
1947 
1948 
1949 inline std::string sn::desc_order(const std::vector<const sn*>& order ) const
1950 {
1951     std::stringstream ss ;
1952     ss << "sn::desc_order [" ;
1953     for(int i=0 ; i < int(order.size()) ; i++)
1954     {
1955         const sn* n = order[i] ;
1956         ss << n->pid << " " ;
1957     }
1958     ss << "]" ;
1959     std::string str = ss.str();
1960     return str ;
1961 }
1962 
1963 
1964 inline std::string sn::desc() const
1965 {
1966     std::stringstream ss ;
1967     ss << "sn::desc"
1968        << " pid " << std::setw(4) << pid
1969        << " idx " << std::setw(4) << index()
1970        << " typecode " << std::setw(3) << typecode
1971        << " num_node " << std::setw(3) << num_node()
1972        << " num_leaf " << std::setw(3) << num_leaf()
1973        << " maxdepth " << std::setw(2) << maxdepth()
1974        << " is_positive_form " << ( is_positive_form() ? "Y" : "N" )
1975        << " lvid " << std::setw(3) << lvid
1976        << " xform " << ( xform ? "Y" : "N" )
1977        << " tag " << tag()
1978        ;
1979     std::string str = ss.str();
1980     return str ;
1981 }
1982 
1983 inline std::string sn::desc_xform() const
1984 {
1985     std::stringstream ss ;
1986     ss << "sn::desc_xform"
1987        << " pid " << std::setw(4) << pid
1988        << " idx " << std::setw(4) << index()
1989        << " lvid " << std::setw(3) << lvid
1990        << " tag " << tag()
1991        << " xform " << ( xform ? "Y" : "N" )
1992        << " " << ( xform ? xform->desc() : "-" )
1993        ;
1994     std::string str = ss.str();
1995     return str ;
1996 }
1997 
1998 
1999 inline std::string sn::desc_xform_full() const
2000 {
2001     std::stringstream ss ;
2002     ss << "sn::desc_xform_full"
2003        << " pid " << std::setw(4) << pid
2004        << " idx " << std::setw(4) << index()
2005        << " lvid " << std::setw(3) << lvid
2006        << " tag " << tag()
2007        << " xform " << ( xform ? "Y" : "N" )
2008        << " " << ( xform ? xform->desc_full() : "-" )
2009        ;
2010     std::string str = ss.str();
2011     return str ;
2012 }
2013 
2014 
2015 
2016 inline std::string sn::desc_prim() const
2017 {
2018     bool hint_lnprd = is_hint_listnode_prim_discontiguous();
2019     bool hint_lnprc = is_hint_listnode_prim_contiguous();
2020 
2021     std::stringstream ss ;
2022     ss
2023        << " idx " << std::setw(4) << index()
2024        << " lvid " << std::setw(3) << lvid
2025        << " " << tag()
2026        << ( aabb ? aabb->desc() : "-" )
2027        << " " << ( hint_lnprd ? "HINT_LNPRD" : "" )
2028        << " " << ( hint_lnprc ? "HINT_LNPRC" : "" )
2029        ;
2030 
2031     std::string str = ss.str();
2032     return str ;
2033 }
2034 
2035 inline std::string sn::desc_prim_all(bool reverse) const
2036 {
2037     std::vector<const sn*> prim ;
2038     collect_prim(prim);
2039     bool ascending = true ;
2040     OrderPrim<const sn>(prim, sn::AABB_ZMin, ascending );
2041 
2042     return DescPrim(prim, reverse);
2043 }
2044 
2045 
2046 inline std::string sn::id() const
2047 {
2048     std::stringstream ss ;
2049     ss << "sn::id"
2050        << " pid " << pid
2051        << " idx " << idx()
2052        ;
2053     std::string str = ss.str();
2054     return str ;
2055 }
2056 
2057 inline std::string sn::brief() const
2058 {
2059     std::stringstream ss ;
2060     ss << "sn::brief"
2061        << " tc " << std::setw(4) << typecode
2062        << " cm " << std::setw(2) << complement
2063        << " lv " << std::setw(3) << lvid
2064        << " xf " << std::setw(1) << ( xform ? "Y" : "N" )
2065        << " pa " << std::setw(1) << ( param ? "Y" : "N" )
2066        << " bb " << std::setw(1) << ( aabb  ? "Y" : "N" )
2067        << " pt " << std::setw(1) << ( parent ? "Y" : "N" )
2068 #ifdef WITH_CHILD
2069        << " nc " << std::setw(2) << child.size()
2070 #else
2071        << " l  " << std::setw(1) << ( left  ? "Y" : "N" )
2072        << " r  " << std::setw(1) << ( right  ? "Y" : "N" )
2073 #endif
2074        << " dp " << std::setw(2) << depth
2075        << " tg " << tag()
2076        << " bb.desc " << ( aabb ? aabb->desc() : "-" )
2077        ;
2078     std::string str = ss.str();
2079     return str ;
2080 }
2081 
2082 
2083 inline std::string sn::desc_child() const
2084 {
2085     std::stringstream ss ;
2086     ss << "sn::desc_child num " << num_child() << std::endl ;
2087     for( int i=0 ; i < num_child() ; i++)
2088     {
2089         const sn* ch = get_child(i) ;
2090         ss << " i " << std::setw(2) << i << " 0x" << std::hex << uint64_t(ch) << std::dec << std::endl ;
2091     }
2092     std::string str = ss.str();
2093     return str ;
2094 }
2095 
2096 inline std::string sn::desc_this() const
2097 {
2098     std::stringstream ss ;
2099     ss << " 0x" << std::hex << uint64_t(this) << " " << std::dec ;
2100     std::string str = ss.str();
2101     return str ;
2102 }
2103 
2104 
2105 inline std::string sn::desc_r() const
2106 {
2107     std::stringstream ss ;
2108     ss << "sn::desc_r" << std::endl ;
2109     desc_r(0, ss);
2110     std::string str = ss.str();
2111     return str ;
2112 }
2113 inline void sn::desc_r(int d, std::stringstream& ss) const
2114 {
2115     ss << std::setw(3) << d << ":" << desc_this() << desc()  << std::endl ;
2116 #ifdef WITH_CHILD
2117     for(int i=0 ; i < int(child.size()) ; i++) child[i]->desc_r(d+1, ss ) ;
2118 #else
2119     if( left && right )
2120     {
2121         left->desc_r(d+1, ss);
2122         right->desc_r(d+1, ss);
2123     }
2124 #endif
2125 }
2126 
2127 
2128 inline std::string sn::detail_r() const
2129 {
2130     std::stringstream ss ;
2131     ss << "sn::detail_r" << std::endl ;
2132     detail_r(0, ss);
2133     std::string str = ss.str();
2134     return str ;
2135 }
2136 inline void sn::detail_r(int d, std::stringstream& ss) const
2137 {
2138     ss << std::setw(3) << d << ":" << desc_this() << detail()  << std::endl ;
2139     for(int i=0 ; i < int(child.size()) ; i++) child[i]->detail_r(d+1, ss ) ;
2140 }
2141 
2142 inline std::string sn::detail() const
2143 {
2144     std::stringstream ss ;
2145     ss << descXF() ;
2146     std::string str = ss.str();
2147     return str ;
2148 }
2149 
2150 
2151 
2152 
2153 
2154 inline std::string sn::render() const
2155 {
2156     std::stringstream ss ;
2157     for(int mode=0 ; mode < NUM_MODE ; mode++) ss << render(mode) << std::endl ;
2158     std::string str = ss.str();
2159     return str ;
2160 }
2161 
2162 inline std::string sn::render_typetag() const { return render(TYPETAG);  }
2163 inline std::string sn::render_parent() const {  return render(PARENT);  }
2164 
2165 inline std::string sn::rdr() const {            return render_typetag();  }
2166 
2167 inline std::string sn::render(int mode) const
2168 {
2169     int nn = num_node();
2170 
2171     std::vector<const sn*> pre ;
2172     preorder(pre);
2173     assert( int(pre.size()) == nn );
2174 
2175     std::vector<const sn*> in ;
2176     inorder(in);
2177     assert( int(in.size()) == nn );
2178 
2179     std::vector<const sn*> post ;
2180     postorder(post);
2181     assert( int(post.size()) == nn );
2182 
2183 
2184     int width = nn ;
2185     int height = maxdepth();
2186 
2187     int xscale = 3 ;
2188     int yscale = 2 ;
2189 
2190     scanvas canvas( width+1, height+2, xscale, yscale );
2191     render_r(&canvas, in, mode,  0);
2192 
2193     std::stringstream ss ;
2194     ss << std::endl ;
2195     ss << desc() << std::endl ;
2196     ss << "sn::render mode " << mode << " " << rendermode(mode) << std::endl ;
2197     ss << canvas.c << std::endl ;
2198 
2199     if(mode == MINIMAL || mode == PID)
2200     {
2201         ss << "preorder  " << desc_order(pre)  << std::endl ;
2202         ss << "inorder   " << desc_order(in)   << std::endl ;
2203         ss << "postorder " << desc_order(post) << std::endl ;
2204 
2205         unsigned ops = operators(0);
2206         bool pos = is_positive_form() ;
2207 
2208         ss << " ops = operators(0) " << ops << std::endl ;
2209         ss << " CSG::MaskDesc(ops) : " << CSG::MaskDesc(ops) << std::endl ;
2210         ss << " is_positive_form() : " << ( pos ? "YES" : "NO" ) << std::endl ;
2211     }
2212 
2213     std::string str = ss.str();
2214     return str ;
2215 }
2216 
2217 inline const char* sn::rendermode(int mode) // static
2218 {
2219     const char* md = nullptr ;
2220     switch(mode)
2221     {
2222         case MINIMAL:  md = MODE_MINIMAL  ; break ;
2223         case TYPECODE: md = MODE_TYPECODE ; break ;
2224         case DEPTH:    md = MODE_DEPTH    ; break ;
2225         case SUBDEPTH: md = MODE_SUBDEPTH ; break ;
2226         case TYPETAG:  md = MODE_TYPETAG  ; break ;
2227         case PID:      md = MODE_PID      ; break ;
2228         case NOTE:     md = MODE_NOTE     ; break ;
2229         case PARENT:   md = MODE_PARENT   ; break ;
2230         case IDX:      md = MODE_IDX      ; break ;
2231     }
2232     return md ;
2233 }
2234 
2235 inline void sn::render_r(scanvas* canvas, std::vector<const sn*>& order, int mode, int d) const
2236 {
2237     int ordinal = std::distance( order.begin(), std::find(order.begin(), order.end(), this )) ;
2238     assert( ordinal < int(order.size()) );
2239 
2240     int ix = ordinal ;
2241     int iy = d ;
2242     std::string tag = CSG::Tag(typecode, complement == 1);
2243 
2244     switch(mode)
2245     {
2246         case MINIMAL  : canvas->drawch( ix, iy, 0,0, 'o' )            ; break ;
2247         case TYPECODE : canvas->draw(   ix, iy, 0,0,  typecode  )     ; break ;
2248         case DEPTH    : canvas->draw(   ix, iy, 0,0,  depth )         ; break ;
2249         case SUBDEPTH : canvas->draw(   ix, iy, 0,0,  subdepth )      ; break ;
2250         case TYPETAG  : canvas->draw(   ix, iy, 0,0,  tag.c_str())    ; break ;
2251         case PID      : canvas->draw(   ix, iy, 0,0,  pid )           ; break ;
2252         case NOTE     : canvas->draw(   ix, iy, 0,0,  note )          ; break ;
2253         case PARENT   : canvas->draw(   ix, iy, 0,0,  parent_index()) ; break ;
2254         case IDX      : canvas->draw(   ix, iy, 0,0,  idx())          ; break ;
2255     }
2256 
2257 #ifdef WITH_CHILD
2258     for(int i=0 ; i < int(child.size()) ; i++) child[i]->render_r(canvas, order, mode, d+1) ;
2259 #else
2260     if(left)  left->render_r( canvas, order, mode, d+1 );
2261     if(right) right->render_r( canvas, order, mode, d+1 );
2262 #endif
2263 }
2264 
2265 
2266 
2267 /**
2268 sn::BinaryTreeHeight
2269 ---------------------
2270 
2271 Return complete binary tree height sufficient for num_leaves
2272 
2273    height: 0, 1, 2, 3,  4,  5,  6,   7,   8,   9,   10,
2274    tprim : 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
2275 
2276 
2277                           1                                  h=0,  1
2278 
2279             10                        11                     h=1,  2
2280 
2281       100         101          110            111            h=2,  4
2282 
2283    1000 1001  1010  1011   1100   1101     1110  1111        h=3,  8
2284 
2285 
2286 **/
2287 
2288 inline int sn::BinaryTreeHeight(int q_leaves )
2289 {
2290     int h = 0 ;
2291     while( (1 << h) < q_leaves )  h += 1 ;
2292     return h ;
2293 }
2294 
2295 inline int sn::BinaryTreeHeight_1(int q_leaves )
2296 {
2297     int  height = -1 ;
2298     for(int h=0 ; h < 10 ; h++ )
2299     {
2300         int tprim = 1 << h ;
2301         if( tprim >= q_leaves )
2302         {
2303            height = h ;
2304            break ;
2305         }
2306     }
2307     return height ;
2308 }
2309 
2310 
2311 /**
2312 sn::ZeroTree_r
2313 ---------------
2314 
2315 Recursively builds complete binary tree
2316 with all operator nodes with a common *op* typecode
2317 and all leaf nodes are sn::Zero.
2318 
2319 **/
2320 
2321 inline sn* sn::ZeroTree_r( int elevation, int op )  // static
2322 {
2323     sn* l = elevation > 1 ? ZeroTree_r( elevation - 1 , op ) : sn::Zero() ;
2324     sn* r = elevation > 1 ? ZeroTree_r( elevation - 1 , op ) : sn::Zero() ;
2325     sn* lr = sn::Create(op, l, r ) ;
2326     return lr  ;
2327 }
2328 inline sn* sn::ZeroTree( int num_leaves, int op ) // static
2329 {
2330     int height = BinaryTreeHeight(num_leaves) ;
2331     if(level() > 0 ) std::cout << "[sn::ZeroTree num_leaves " << num_leaves << " height " << height << std::endl;
2332     sn* root = ZeroTree_r( height, op );
2333     if(level() > 0) std::cout << "]sn::ZeroTree " << std::endl ;
2334     return root ;
2335 }
2336 
2337 /**
2338 sn::CommonOperatorTypeTree (formerly sn::CommonTree)
2339 ------------------------------------------------------------
2340 
2341 This was implemented while sn was not fully featured.
2342 It was used to provide a "template" tree with typecodes only,
2343 to be used for form snd trees.
2344 
2345 **/
2346 
2347 inline sn* sn::CommonOperatorTypeTree( std::vector<int>& leaftypes, int op ) // static
2348 {
2349     int num_leaves = leaftypes.size();
2350     sn* root = nullptr ;
2351     if( num_leaves == 1 )
2352     {
2353         root = sn::Prim(leaftypes[0]) ;
2354     }
2355     else
2356     {
2357         root = ZeroTree(num_leaves, op );
2358 
2359         if(level() > 0) std::cout << "sn::CommonOperatorTypeTree ZeroTree num_leaves " << num_leaves << std::endl ;
2360         if(level() > 1) std::cout << root->render(5) ;
2361 
2362         root->populate_leaftypes(leaftypes);
2363 
2364         if(level() > 0) std::cout << "sn::CommonOperatorTypeTree populated num_leaves " << num_leaves << std::endl ;
2365         if(level() > 1) std::cout << root->render(5) ;
2366 
2367         root->prune();
2368 
2369         if(level() > 0) std::cout << "sn::CommonOperatorTypeTree pruned num_leaves " << num_leaves << std::endl ;
2370         if(level() > 1) std::cout << root->render(5) ;
2371     }
2372     return root ;
2373 }
2374 
2375 
2376 
2377 
2378 
2379 /**
2380 sn::populate_leaftypes
2381 -------------------------
2382 
2383 Replacing zeros with leaftype nodes (not fully featured ones).
2384 
2385 **/
2386 
2387 inline void sn::populate_leaftypes(std::vector<int>& leaftypes )
2388 {
2389     int num_leaves = leaftypes.size();
2390     int num_leaves_placed = 0 ;
2391 
2392     std::vector<sn*> order ;
2393     inorder_(order) ;
2394 
2395     int num_nodes = order.size();
2396 
2397     if(level() > 0) std::cout
2398         << "sn::populate_leaftypes"
2399         << " num_leaves " << num_leaves
2400         << " num_nodes " << num_nodes
2401         << std::endl
2402         ;
2403 
2404     for(int i=0 ; i < num_nodes ; i++)
2405     {
2406         sn* n = order[i];
2407         if(level() > 1) std::cout
2408             << "sn::populate_leaftypes " << std::setw(3) << i
2409             << " n.desc " << n->desc()
2410             << std::endl
2411             ;
2412     }
2413 
2414     for(int i=0 ; i < num_nodes ; i++)
2415     {
2416         sn* n = order[i];
2417 
2418 #ifdef WITH_CHILD
2419         if(level() > 1) std::cout
2420             << "sn::populate_leaftypes"
2421             << " WITH_CHILD "
2422             << " i " << i
2423             << " n.is_operator " << n->is_operator()
2424             << " n.child.size " << n->child.size()
2425             << " num_leaves_placed " << num_leaves_placed
2426             << std::endl
2427             ;
2428 
2429         if(n->is_operator())
2430         {
2431             assert( n->child.size() == 2 );
2432             for(int j=0 ; j < 2 ; j++)
2433             {
2434                 sn* ch = n->child[j] ;
2435                 if(level() > 1 ) std::cout
2436                     << "sn::populate_leaftypes"
2437                     << " ch.desc " << ch->desc()
2438                     << std::endl
2439                     ;
2440 
2441                 if( ch->is_zero() && num_leaves_placed < num_leaves )
2442                 {
2443                     n->set_child(j, sn::Prim(leaftypes[num_leaves_placed]), false) ;
2444                     num_leaves_placed += 1 ;
2445                 }
2446             }
2447         }
2448 #else
2449         if(n->is_operator())
2450         {
2451             if(n->left->is_zero() && num_leaves_placed < num_leaves)
2452             {
2453                 n->set_left( sn::Prim(leaftypes[num_leaves_placed]), false ) ;
2454                 num_leaves_placed += 1 ;
2455             }
2456             if(n->right->is_zero() && num_leaves_placed < num_leaves)
2457             {
2458                 n->set_right(sn::Prim(leaftypes[num_leaves_placed]), false ) ;
2459                 num_leaves_placed += 1 ;
2460             }
2461         }
2462 #endif
2463     }
2464     assert( num_leaves_placed == num_leaves );
2465 }
2466 
2467 
2468 
2469 
2470 
2471 /**
2472 sn::populate_leaves
2473 ---------------------
2474 
2475 Replacing zeros with fully featured leaf nodes.
2476 
2477 **/
2478 
2479 inline void sn::populate_leaves(std::vector<sn*>& leaves )
2480 {
2481     int num_leaves = leaves.size();
2482     int num_leaves_placed = 0 ;
2483 
2484     std::vector<sn*> order ;
2485     inorder_(order) ;   // these all all nodes of the tree, not just leaves
2486 
2487     int num_nodes = order.size();
2488 
2489     if(level() > 0) std::cout
2490         << "sn::populate_leaves"
2491         << " num_leaves " << num_leaves
2492         << " num_nodes " << num_nodes
2493         << std::endl
2494         ;
2495 
2496     for(int i=0 ; i < num_nodes ; i++)
2497     {
2498         sn* n = order[i];
2499         if(level() > 1) std::cout
2500             << "sn::populate_leaves " << std::setw(3) << i
2501             << " n.desc " << n->desc()
2502             << std::endl
2503             ;
2504     }
2505 
2506     for(int i=0 ; i < num_nodes ; i++)
2507     {
2508         sn* n = order[i];
2509 
2510 #ifdef WITH_CHILD
2511         if(level() > 1) std::cout
2512             << "sn::populate_leaves"
2513             << " WITH_CHILD "
2514             << " i " << i
2515             << " n.is_operator " << n->is_operator()
2516             << " n.child.size " << n->child.size()
2517             << " num_leaves_placed " << num_leaves_placed
2518             << std::endl
2519             ;
2520 
2521         if(n->is_operator())
2522         {
2523             assert( n->child.size() == 2 );
2524             for(int j=0 ; j < 2 ; j++)
2525             {
2526                 sn* ch = n->child[j] ;
2527                 if(level() > 1 ) std::cout
2528                     << "sn::populate_leaves"
2529                     << " ch.desc " << ch->desc()
2530                     << std::endl
2531                     ;
2532 
2533                 if( ch->is_zero() && num_leaves_placed < num_leaves )
2534                 {
2535                     n->set_child(j, leaves[num_leaves_placed], false) ;
2536                     num_leaves_placed += 1 ;
2537                 }
2538             }
2539         }
2540 #else
2541         if(n->is_operator())
2542         {
2543             if(n->left->is_zero() && num_leaves_placed < num_leaves)
2544             {
2545                 n->set_left( leaves[num_leaves_placed], false ) ;
2546                 num_leaves_placed += 1 ;
2547             }
2548             if(n->right->is_zero() && num_leaves_placed < num_leaves)
2549             {
2550                 n->set_right( leaves[num_leaves_placed], false ) ;
2551                 num_leaves_placed += 1 ;
2552             }
2553         }
2554 #endif
2555     }
2556     assert( num_leaves_placed == num_leaves );
2557 }
2558 
2559 
2560 
2561 inline void sn::prune()
2562 {
2563     prune_r(0);
2564 
2565     if(has_dangle())
2566     {
2567         if(level() > -1) std::cout << "sn::prune ERROR root still has dangle " << std::endl ;
2568     }
2569 
2570 }
2571 
2572 /**
2573 sn::prune_r
2574 -------------
2575 
2576 * based on npy/NTreeBuilder
2577 * returning to tree integrity relies on actions of set_left/set_right
2578 
2579 
2580 
2581 
2582 l->is_lrzero::
2583 
2584 
2585       before                   after prune_r
2586 
2587             this                    this
2588             /   \                  /    \
2589            /     \                /      \
2590           l       r              0        r
2591          / \
2592         /   \
2593        0     0
2594 
2595 
2596 l->is_rzero::
2597 
2598 
2599      before                    after prune_r
2600 
2601            this                    this
2602           /    \                  /     \
2603          /      \                /       \
2604         l        r             l=>"ll"    r
2605        / \
2606       /   \
2607      ll    0
2608 
2609 **/
2610 
2611 inline void sn::prune_r(int d)
2612 {
2613     if(!is_operator()) return ;
2614 
2615     sn* l = get_left();
2616     sn* r = get_right();
2617 
2618     l->prune_r(d+1);
2619     r->prune_r(d+1);
2620 
2621     // postorder visit : so both children always visited before their parents
2622 
2623     if( l->is_lrzero() )     // left node is an operator which has both its left and right zero
2624     {
2625         set_left(sn::Zero(), false) ;       // prune : ie replace operator with CSG_ZERO placeholder
2626     }
2627     else if( l->is_rzero() )  // left node is an operator with left non-zero and right zero
2628     {
2629         sn* ll = l->get_left();
2630         set_left( ll, true );   // moving the lonely primitive up to higher elevation
2631     }
2632 
2633     if(r->is_lrzero())        // right node is operator with both its left and right zero
2634     {
2635         set_right(sn::Zero(), false) ;      // prune
2636     }
2637     else if( r->is_rzero() )  // right node is operator with its left non-zero and right zero
2638     {
2639         sn* rl = r->get_left() ;
2640         set_right(rl, true) ;         // moving the lonely primitive up to higher elevation
2641     }
2642 }
2643 
2644 
2645 
2646 /**
2647 sn::has_dangle   (non recursive)
2648 ---------------------------------
2649 
2650 A dangle node is an operator with a one or two placeholder children (aka zeros), eg::
2651 
2652       op           op           op
2653      /  \         /  \         /  \
2654     0    V       V    0       0    0
2655 
2656 **/
2657 
2658 inline bool sn::has_dangle() const  // see NTreeBuilder::rootprune
2659 {
2660 #ifdef WITH_CHILD
2661     int num_zero = 0 ;
2662     for(int i=0 ; i < int(child.size()) ; i++) if(child[i]->is_zero()) num_zero += 1 ;
2663     return num_zero > 0 ;
2664 #else
2665     return is_operator() && ( right->is_zero() || left->is_zero()) ;
2666 #endif
2667 }
2668 
2669 
2670 
2671 
2672 /**
2673 sn::positivize (base on NTreePositive::positivize_r)
2674 --------------------------------------------------------
2675 
2676 * https://smartech.gatech.edu/bitstream/handle/1853/3371/99-04.pdf?sequence=1&isAllowed=y
2677 
2678 * addition: union
2679 * subtraction: difference
2680 * product: intersect
2681 
2682 Tree positivization (which is not the same as normalization)
2683 eliminates subtraction operators by propagating negations down the tree using deMorgan rules.
2684 
2685 Q: What about compound (aka listnodes) ?
2686 A: From the point of view of the binary tree the listnode should be regarded as a primitive,
2687    so if a negation is signalled all the listed prims should be complemented.
2688    For example with hole subtraction the subtraction gets converted to intersect and the
2689    holes are complemented.
2690 
2691 **/
2692 
2693 
2694 inline void sn::positivize()
2695 {
2696     positivize_r(false, 0);
2697 }
2698 inline void sn::positivize_r(bool negate, int d)
2699 {
2700     if(is_primitive() || is_listnode())
2701     {
2702         if(negate)
2703         {
2704             if(is_primitive())
2705             {
2706                 flip_complement();
2707             }
2708             else if(is_listnode())
2709             {
2710                 flip_complement_child();
2711             }
2712         }
2713     }
2714     else
2715     {
2716         bool left_negate = false ;
2717         bool right_negate = false ;
2718 
2719         if(typecode == CSG_INTERSECTION || typecode == CSG_UNION)
2720         {
2721             if(negate)                             // !( A*B ) ->  !A + !B       !(A + B) ->     !A * !B
2722             {
2723                 typecode = CSG::DeMorganSwap(typecode) ;   // UNION->INTERSECTION, INTERSECTION->UNION
2724                 left_negate = true ;
2725                 right_negate = true ;
2726             }
2727             else
2728             {                                      //  A * B ->  A * B         A + B ->  A + B
2729                 left_negate = false ;
2730                 right_negate = false ;
2731             }
2732         }
2733         else if(typecode == CSG_DIFFERENCE)
2734         {
2735             if(negate)                             //  !(A - B) -> !(A*!B) -> !A + B
2736             {
2737                 typecode = CSG_UNION ;
2738                 left_negate = true ;
2739                 right_negate = false  ;
2740             }
2741             else
2742             {
2743                 typecode = CSG_INTERSECTION ;    //    A - B ->  A * !B
2744                 left_negate = false ;
2745                 right_negate = true ;
2746             }
2747         }
2748 
2749 #ifdef WITH_CHILD
2750         assert( child.size() == 2 );
2751         sn* left = child[0] ;
2752         sn* right = child[1] ;
2753 #endif
2754         left->positivize_r(left_negate,  d+1);
2755         right->positivize_r(right_negate, d+1);
2756     }
2757 }
2758 
2759 
2760 inline void sn::flip_complement()
2761 {
2762     switch(complement)
2763     {
2764         case 0: complement = 1 ; break ;
2765         case 1: complement = 0 ; break ;
2766         default: assert(0)     ; break ;
2767     }
2768 }
2769 inline void sn::flip_complement_child()
2770 {
2771     for(int i=0 ; i < num_child() ; i++)
2772     {
2773         sn* ch = get_child(i) ;
2774         ch->flip_complement() ;
2775     }
2776 }
2777 
2778 
2779 inline void sn::zero_label()
2780 {
2781     for(int i=0 ; i < int(sizeof(label)) ; i++) label[i] = '\0' ;
2782 }
2783 
2784 inline void sn::set_label( const char* label_ )
2785 {
2786     strncpy( &label[0], label_, sizeof(label) );
2787 }
2788 
2789 /**
2790 sn::set_lvid
2791 -------------
2792 
2793 Recursively set:
2794 
2795 * lvid
2796 * depth
2797 * parent pointers : ordinarily this is not necessary as it is done in the ctor,
2798   but after deepcopy the parent pointers are scrubbed, so set that here too
2799 
2800 
2801 **/
2802 
2803 inline void sn::set_lvid(int lvid_)
2804 {
2805     set_lvid_r(lvid_, 0);
2806 
2807     int chk = checktree();
2808     if( chk != 0 )
2809     {
2810         if(level() > 0 ) std::cout
2811            << "sn::set_lvid"
2812            << " lvid " << lvid_
2813            << " checktree " << chk
2814            << std::endl
2815            ;
2816     }
2817     assert( chk == 0 );
2818 }
2819 inline void sn::set_lvid_r(int lvid_, int d)
2820 {
2821     lvid = lvid_ ;
2822     depth = d ;
2823 
2824     for(int i=0 ; i < num_child() ; i++)
2825     {
2826         sn* ch = get_child(i) ;
2827         ch->set_lvid_r(lvid_, d+1 );
2828 
2829         if(ch->parent == nullptr)
2830         {
2831             ch->parent = this ; // ordinarily not needed, but deepcopy scrubs parent pointers
2832         }
2833         else
2834         {
2835             assert( ch->parent == this ) ;
2836         }
2837     }
2838 }
2839 
2840 
2841 /**
2842 sn::check_idx
2843 --------------
2844 
2845 Recursive check that all nodes of the tree are
2846 accessible from the pool
2847 
2848 **/
2849 
2850 inline int sn::check_idx(const char* msg) const
2851 {
2852     return check_idx_r(0, msg);
2853 }
2854 inline int sn::check_idx_r(int d, const char* msg) const
2855 {
2856     int idx_ = idx(); // lookup contiguous index of this object within the pool of active nodes
2857     const sn* chk = Get(idx_);
2858 
2859     bool expect = chk == this ;
2860     if(!expect) std::cerr
2861         << "sn::check_idx_r"
2862         << " ERROR Get(idx()) != this  ? "
2863         << " idx_ " << idx_
2864         << " msg " << ( msg ? msg : "-" )
2865         << "\n"
2866         << " this.desc " << desc() << "\n"
2867         << " chk.desc  " << ( chk ? chk->desc() : "-" ) << "\n"
2868         ;
2869 
2870     int rc = expect ? 0 : 1 ;
2871     //assert(expect);
2872 
2873     for(int i=0 ; i < num_child() ; i++) rc += get_child(i)->check_idx_r(d+1, msg);
2874     return rc ;
2875 }
2876 
2877 
2878 
2879 
2880 
2881 inline void sn::setPA( double x0, double y0, double z0, double w0, double x1, double y1 )
2882 {
2883     if( param == nullptr ) param = new s_pa ;
2884     param->x0 = x0 ;
2885     param->y0 = y0 ;
2886     param->z0 = z0 ;
2887     param->w0 = w0 ;
2888     param->x1 = x1 ;
2889     param->y1 = y1 ;
2890 }
2891 
2892 
2893 inline const double* sn::getPA_data() const
2894 {
2895     return param ? param->data() : nullptr ;
2896 }
2897 
2898 inline void sn::copyPA_data(double* dst) const
2899 {
2900     const double* src = getPA_data() ;
2901     for(int i=0 ; i < 6 ; i++) dst[i] = src[i] ;
2902 }
2903 
2904 
2905 
2906 
2907 
2908 inline void sn::setBB( double x0, double y0, double z0, double x1, double y1, double z1 )
2909 {
2910     bool bad_bbox =  x0 >= x1 || y0 >= y1 || z0 >= z1  ;
2911     if(bad_bbox) std::cerr
2912           << "sn::setBB BAD BOUNDING BOX "
2913           << "\n"
2914           << " x0 " << x0
2915           << " x1 " << x1
2916           << "\n"
2917           << " y0 " << y0
2918           << " y1 " << y1
2919           << "\n"
2920           << " z0 " << z0
2921           << " z1 " << z1
2922           << "\n"
2923           ;
2924 
2925     assert(!bad_bbox);
2926 
2927 
2928     if( aabb == nullptr ) aabb = new s_bb ;
2929     aabb->x0 = x0 ;
2930     aabb->y0 = y0 ;
2931     aabb->z0 = z0 ;
2932     aabb->x1 = x1 ;
2933     aabb->y1 = y1 ;
2934     aabb->z1 = z1 ;
2935 }
2936 
2937 inline void sn::setBB( double x0 )
2938 {
2939     if( aabb == nullptr ) aabb = new s_bb ;
2940     aabb->x0 = -x0 ;
2941     aabb->y0 = -x0 ;
2942     aabb->z0 = -x0 ;
2943     aabb->x1 = +x0 ;
2944     aabb->y1 = +x0 ;
2945     aabb->z1 = +x0 ;
2946 }
2947 
2948 inline const double* sn::getBB_data() const
2949 {
2950     return aabb ? aabb->data() : nullptr ;
2951 }
2952 
2953 /**
2954 sn::copyBB_data
2955 ----------------
2956 
2957 Canonical usage is from stree::get_combined_tran_and_aabb
2958 which is canonically called after sn::postconvert has been
2959 run for the CSG tree. The sn::postconvert does::
2960 
2961     positivize
2962     setAABB_TreeFrame_All
2963     uncoincide
2964     setAABB_LeafFrame_All
2965 
2966 So when this sn::copyBB_data is called the AABB are
2967 in leaf frame form.
2968 
2969 **/
2970 
2971 inline void sn::copyBB_data(double* dst) const
2972 {
2973     const double* src = getBB_data() ;
2974     for(int i=0 ; i < 6 ; i++) dst[i] = src[i] ;
2975 }
2976 
2977 
2978 
2979 inline double sn::getBB_xmin() const { return aabb ? aabb->x0 : 0. ; }
2980 inline double sn::getBB_ymin() const { return aabb ? aabb->y0 : 0. ; }
2981 inline double sn::getBB_zmin() const { return aabb ? aabb->z0 : 0. ; }
2982 
2983 inline double sn::getBB_xmax() const { return aabb ? aabb->x1 : 0. ; }
2984 inline double sn::getBB_ymax() const { return aabb ? aabb->y1 : 0. ; }
2985 inline double sn::getBB_zmax() const { return aabb ? aabb->z1 : 0. ; }
2986 
2987 inline double sn::getBB_xavg() const { return ( getBB_xmin() + getBB_xmax() )/2. ; }
2988 inline double sn::getBB_yavg() const { return ( getBB_ymin() + getBB_ymax() )/2. ; }
2989 inline double sn::getBB_zavg() const { return ( getBB_zmin() + getBB_zmax() )/2. ; }
2990 
2991 
2992 inline double sn::AABB_XMin( const sn* n ){ return n ? n->getBB_xmin() : 0. ; }
2993 inline double sn::AABB_YMin( const sn* n ){ return n ? n->getBB_ymin() : 0. ; }
2994 inline double sn::AABB_ZMin( const sn* n ){ return n ? n->getBB_zmin() : 0. ; }
2995 
2996 inline double sn::AABB_XMax( const sn* n ){ return n ? n->getBB_xmax() : 0. ; }
2997 inline double sn::AABB_YMax( const sn* n ){ return n ? n->getBB_ymax() : 0. ; }
2998 inline double sn::AABB_ZMax( const sn* n ){ return n ? n->getBB_zmax() : 0. ; }
2999 
3000 inline double sn::AABB_XAvg( const sn* n ){ return n ? n->getBB_xavg() : 0. ; }
3001 inline double sn::AABB_YAvg( const sn* n ){ return n ? n->getBB_yavg() : 0. ; }
3002 inline double sn::AABB_ZAvg( const sn* n ){ return n ? n->getBB_zavg() : 0. ; }
3003 
3004 
3005 inline bool sn::hasXF() const
3006 {
3007     return xform != nullptr ;
3008 }
3009 inline void sn::setXF()
3010 {
3011     glm::tmat4x4<double> id(1.);
3012     setXF(id,id);
3013 }
3014 inline void sn::setXF( const glm::tmat4x4<double>& t )
3015 {
3016     glm::tmat4x4<double> v = glm::inverse(t) ;
3017     setXF(t, v);
3018 }
3019 inline void sn::combineXF( const glm::tmat4x4<double>& t )
3020 {
3021     glm::tmat4x4<double> v = glm::inverse(t) ;
3022     combineXF(t, v);
3023 }
3024 inline void sn::setXF( const glm::tmat4x4<double>& t, const glm::tmat4x4<double>& v )
3025 {
3026     if( xform == nullptr ) xform = new s_tv ;
3027     xform->t = t ;
3028     xform->v = v ;
3029 }
3030 inline void sn::combineXF( const glm::tmat4x4<double>& t, const glm::tmat4x4<double>& v )
3031 {
3032     if( xform == nullptr )
3033     {
3034         xform = new s_tv ;
3035         xform->t = t ;
3036         xform->v = v ;
3037     }
3038     else
3039     {
3040         glm::tmat4x4<double> tt = xform->t * t ;
3041         glm::tmat4x4<double> vv = v * xform->v ;
3042         xform->t = tt ;
3043         xform->v = vv ;
3044     }
3045 }
3046 
3047 inline std::string sn::descXF() const
3048 {
3049     std::stringstream ss ;
3050     ss << "sn::descXF (s_tv)xform " << ( xform ? xform->desc() : "-" ) ;
3051     std::string str = ss.str();
3052     return str ;
3053 }
3054 
3055 
3056 
3057 inline sn* sn::Cylinder(double radius, double z1, double z2) // static
3058 {
3059     assert( z2 > z1 );
3060     sn* nd = Create(CSG_CYLINDER);
3061     nd->setPA( 0.f, 0.f, 0.f, radius, z1, z2)  ;
3062     nd->setBB( -radius, -radius, z1, +radius, +radius, z2 );
3063     return nd ;
3064 }
3065 
3066 /**
3067 sn::CutCylinder
3068 ----------------
3069 
3070 HMM asis s_pa.h restricts to 6 param so:
3071 
3072 1. require endnormals to have Y component of zero, bringing down to 4 param
3073 2. symmetric +dz -dz height : 1 param
3074 3. just radius (not inner) : 1 param
3075 
3076 **/
3077 
3078 inline sn* sn::CutCylinder(
3079     double R,
3080     double dz,
3081     double _pz_nrm_x,
3082     double _pz_nrm_y,
3083     double _pz_nrm_z,
3084     double _nz_nrm_x,
3085     double _nz_nrm_y,
3086     double _nz_nrm_z ) // static
3087 {
3088     assert( _pz_nrm_z > 0. );  // expect outwards normal away from +DZ top edge
3089     assert( _nz_nrm_z < 0. );  // expect outwards normal away from -DZ bot edge
3090 
3091     assert( _pz_nrm_y == 0. ); // simplifying assumptions for initial impl
3092     assert( _nz_nrm_y == 0. );
3093 
3094     double pz_nrm = std::sqrt( _pz_nrm_x*_pz_nrm_x + _pz_nrm_y*_pz_nrm_y +  _pz_nrm_z*_pz_nrm_z );
3095     double pz_nrm_x = _pz_nrm_x/pz_nrm ;
3096     double pz_nrm_y = _pz_nrm_y/pz_nrm ;
3097     double pz_nrm_z = _pz_nrm_z/pz_nrm ;
3098 
3099     double nz_nrm = std::sqrt( _nz_nrm_x*_nz_nrm_x + _nz_nrm_y*_nz_nrm_y + _nz_nrm_z*_nz_nrm_z );
3100     double nz_nrm_x = _nz_nrm_x/nz_nrm ;
3101     double nz_nrm_y = _nz_nrm_y/nz_nrm ;
3102     double nz_nrm_z = _nz_nrm_z/nz_nrm ;
3103 
3104 
3105     double zmin, zmax ;
3106     CutCylinderZRange(zmin, zmax, R, dz, pz_nrm_x, pz_nrm_y, pz_nrm_z, nz_nrm_x, nz_nrm_y, nz_nrm_z );
3107 
3108 
3109     bool dump = false ;
3110     if(dump) std::cout
3111        << "sn::CutCylinder\n"
3112        << " R " << std::setw(10) << std::fixed << std::setprecision(6) << R << "\n"
3113        << " dz " << std::setw(10) << std::fixed << std::setprecision(6) << dz << "\n"
3114        << " _pz_nrm_x " << std::setw(10) << std::fixed << std::setprecision(6) << _pz_nrm_x << "\n"
3115        << " _pz_nrm_y " << std::setw(10) << std::fixed << std::setprecision(6) << _pz_nrm_y << "\n"
3116        << " _pz_nrm_z " << std::setw(10) << std::fixed << std::setprecision(6) << _pz_nrm_z << "\n"
3117        << "\n"
3118        << " pz_nrm_x " << std::setw(10) << std::fixed << std::setprecision(6) << pz_nrm_x << "\n"
3119        << " pz_nrm_y " << std::setw(10) << std::fixed << std::setprecision(6) << pz_nrm_y << "\n"
3120        << " pz_nrm_z " << std::setw(10) << std::fixed << std::setprecision(6) << pz_nrm_z << "\n"
3121        << "\n"
3122        << " _nz_nrm_x " << std::setw(10) << std::fixed << std::setprecision(6) << _nz_nrm_x << "\n"
3123        << " _nz_nrm_y " << std::setw(10) << std::fixed << std::setprecision(6) << _nz_nrm_y << "\n"
3124        << " _nz_nrm_z " << std::setw(10) << std::fixed << std::setprecision(6) << _nz_nrm_z << "\n"
3125        << "\n"
3126        << " nz_nrm_x " << std::setw(10) << std::fixed << std::setprecision(6) << nz_nrm_x << "\n"
3127        << " nz_nrm_y " << std::setw(10) << std::fixed << std::setprecision(6) << nz_nrm_y << "\n"
3128        << " nz_nrm_z " << std::setw(10) << std::fixed << std::setprecision(6) << nz_nrm_z << "\n"
3129        << "\n"
3130        << " zmax " << std::setw(10) << std::fixed << std::setprecision(6) << zmax << "\n"
3131        << " zmin " << std::setw(10) << std::fixed << std::setprecision(6) << zmin << "\n"
3132        << "\n"
3133        ;
3134 
3135     sn* nd = Create(CSG_CUTCYLINDER);
3136     nd->setPA( R, dz, pz_nrm_x, pz_nrm_z, nz_nrm_x, nz_nrm_z)  ;
3137     nd->setBB( -R, -R, zmin, +R, +R, zmax );
3138 
3139     return nd ;
3140 }
3141 
3142 
3143 /**
3144 sn::CutCylinderZRange
3145 -----------------------
3146 
3147 ::
3148 
3149      (pz_nrm_x,0,pz_nrm_z) "highNormal"
3150        \
3151         \
3152          \     A              +dz+pdz
3153           P    | - - - - - -  +dz
3154      B    |    |
3155      |    |    |
3156      |    |    +
3157      |    C    |
3158      +    |    |
3159      |    |    |
3160      |    |    D
3161      |    N       - - - - -  -dz
3162      C     \                 -dz-ndz
3163             \
3164              \
3165            (nz_nrm_x,0,nz_nrm_z) "lowNormal"
3166 
3167 
3168           |    |
3169           0   radius
3170 
3171 
3172 Vectors PA and PB are perpendicular to the normal and in opposite directions::
3173 
3174     ( pz_nrm_z, 0, -pz_nrm_x)   # PA as pz_nrm_z > 0
3175     (-pz_nrm_z, 0,  pz_nrm_x)   # PB
3176 
3177 Similar triangles "Z"/"X" (equivalent to equating tangents)::
3178 
3179     pdz/R = (-pz_nrm_x)/(pz_nrm_z)
3180     pdz   = R*std::abs(-pz_nrm_x)/(pz_nrm_z)    # pz_nrm_z>0.  R > 0. => pdz > 0.
3181     ## do not known the sign of pz_nrm_x : the cut could be either way, but want maximum z so take std::abs to pick extreme
3182 
3183 Vectors ND and NC are perpendicular to normal and in opposite directions::
3184 
3185     ( nz_nrm_z, 0, -nz_nrm_x )   ##  NC   as nz_nrm_z < 0.
3186     (-nz_nrm_z, 0,  nz_nrm_x )   ##  ND
3187 
3188 Similar triangles "Z"/"X"::
3189 
3190      ndz/R = (nz_nrm_x/-nz_nrm_z)
3191      ndz = R*std::abs(nz_nrm_x)/(-nz_nrm_z)      # nz_nrm_z < 0.  R > 0. => ndz > 0.
3192      ## again do not known the sign of nz_nrm_x : the cut could be either way, but want minimum z so take std::abs to pick extreme
3193 
3194 **/
3195 
3196 inline void sn::CutCylinderZRange(
3197     double& zmin,
3198     double& zmax,
3199     double R,
3200     double dz,
3201     double pz_nrm_x,
3202     double pz_nrm_y,
3203     double pz_nrm_z,
3204     double nz_nrm_x,
3205     double nz_nrm_y,
3206     double nz_nrm_z ) // static
3207 {
3208     double pdz = R*std::abs(-pz_nrm_x)/(pz_nrm_z) ;
3209     assert( pdz > 0. );
3210     zmax = dz + pdz ;
3211 
3212     double ndz = R*std::abs(nz_nrm_x)/(-nz_nrm_z) ;
3213     assert( ndz > 0. );
3214     zmin = -dz -ndz ;
3215 }
3216 
3217 
3218 
3219 inline sn* sn::Cone(double r1, double z1, double r2, double z2)  // static
3220 {
3221     assert( z2 > z1 );
3222     double rmax = fmax(r1, r2) ;
3223     sn* nd = Create(CSG_CONE) ;
3224     nd->setPA( r1, z1, r2, z2, 0., 0. ) ;
3225     nd->setBB( -rmax, -rmax, z1, rmax, rmax, z2 );
3226     return nd ;
3227 }
3228 inline sn* sn::Sphere(double radius)  // static
3229 {
3230     assert( radius > zero );
3231     sn* nd = Create(CSG_SPHERE) ;
3232     nd->setPA( zero, zero, zero, radius, zero, zero );
3233     nd->setBB(  -radius, -radius, -radius,  radius, radius, radius  );
3234     return nd ;
3235 }
3236 inline sn* sn::ZSphere(double radius, double z1, double z2)  // static
3237 {
3238     assert( radius > zero );
3239     assert( z2 > z1 );
3240     sn* nd = Create(CSG_ZSPHERE) ;
3241     nd->setPA( zero, zero, zero, radius, z1, z2 );
3242     nd->setBB(  -radius, -radius, z1,  radius, radius, z2  );
3243     return nd ;
3244 }
3245 inline sn* sn::Box3(double fullside)  // static
3246 {
3247     return Box3(fullside, fullside, fullside);
3248 }
3249 inline sn* sn::Box3(double fx, double fy, double fz )  // static
3250 {
3251     assert( fx > 0. );
3252     assert( fy > 0. );
3253     assert( fz > 0. );
3254 
3255     sn* nd = Create(CSG_BOX3) ;
3256     nd->setPA( fx, fy, fz, 0., 0., 0. );
3257     nd->setBB( -fx*0.5 , -fy*0.5, -fz*0.5, fx*0.5 , fy*0.5, fz*0.5 );
3258     return nd ;
3259 }
3260 
3261 
3262 
3263 
3264 
3265 /**
3266 sn::Torus
3267 ----------
3268 
3269 BB now accounts for phi range using sgeomtools.h based on G4GeomTools
3270 
3271 Square torus for ease of illustration::
3272 
3273     +---------------------------------------+
3274     |                                       |
3275     |                                       |
3276     |                                       |
3277     |       +-----------------------+       |
3278     |       |                       |       |
3279     |       |                       |       |
3280     |       |                       |       |
3281     |       |                       |       |
3282     |   +   |           +           |   +   |
3283     |       |                       |       |
3284     |       |                       |       |
3285     |       |                       |       |
3286     |       |                       |       |
3287     |       +-----------------------+       |
3288     |                                       |
3289     |                                       |
3290     |                                       |
3291     +---------------------------------------+
3292 
3293                         |     rtor      |
3294 
3295                                     |   |   |
3296                                     rmax rmax
3297 
3298                         |     rtor + rmax   |
3299 
3300                         | rtor-rmax |
3301 
3302 **/
3303 
3304 inline sn* sn::Torus(double rmin, double rmax, double rtor, double startPhi_deg, double deltaPhi_deg )
3305 {
3306     double rext = rtor+rmax ;
3307     double rint = rtor-rmax ;
3308 
3309     double startPhi = startPhi_deg/180.*M_PI ;
3310     double deltaPhi = deltaPhi_deg/180.*M_PI ;
3311     double2 pmin ;
3312     double2 pmax ;
3313     sgeomtools::DiskExtent(rint, rext, startPhi, deltaPhi, pmin, pmax );
3314 
3315     sn* nd = Create(CSG_TORUS) ;
3316     nd->setPA( rmin, rmax, rtor, startPhi_deg, deltaPhi_deg, 0.  );
3317     nd->setBB( pmin.x, pmin.y, -rmax, pmax.x, pmax.y, +rmax );
3318 
3319     return nd ;
3320 }
3321 
3322 /**
3323 sn::PhiCut
3324 -----------
3325 
3326       [cosPhi1, sinPhi1]
3327          phi1
3328           |
3329           |
3330           |
3331           +--------- phi0  [cosPhi0, sinPhi0]
3332 
3333 
3334 ::
3335 
3336    cross_product = cosPhi0 * sinPhi1 - cosPhi1 * sinPhi0
3337 
3338    cross_product > 0.
3339         sweep from phi0 to phi1 is in the positive direction
3340         and the interior angle is less than 180 deg making
3341         a wedge shape
3342 
3343    cross_product < 0.
3344         sweep covers more than half the circle. The interior angle is greater than 180 deg.
3345         This creates a "concave" or "reflex" wedge (like a Pac-Man with his mouth open).
3346 
3347 
3348 **/
3349 
3350 inline sn* sn::PhiCut(double phi0, double phi1)  // static
3351 {
3352     sn* nd = Create(CSG_PHICUT) ;
3353     bool expect_phi = phi1 > phi0 ;
3354 
3355     double cos_phi0 = cos(phi0);
3356     double sin_phi0 = sin(phi0);
3357     double cos_phi1 = cos(phi1);
3358     double sin_phi1 = sin(phi1);
3359 
3360     double cross_product = cos_phi0*sin_phi1 - cos_phi1*sin_phi0 ;
3361     bool is_wedge = cross_product > 0. ;
3362     bool is_pacman = cross_product < 0. ;
3363 
3364     int PACMAN_ALLOWED = ssys::getenvint(sn__PhiCut_PACMAN_ALLOWED, 0) ;
3365     bool expect_cross_product = PACMAN_ALLOWED ? true  : is_wedge ;
3366     bool expect = expect_phi && expect_cross_product ;
3367 
3368     if(!expect) std::cerr
3369        << "sn::PhiCut"
3370        << " phi0 " << phi0
3371        << " phi1 " << phi1
3372        << " expect " << ( expect ?  "YES" : "NO " )
3373        << " expect_phi " << ( expect_phi ?  "YES" : "NO " )
3374        << " expect_cross_product " << ( expect_cross_product ? "YES" : "NO " )
3375        << " cross_product " << cross_product
3376        << " is_wedge " << ( is_wedge ? "YES" : "NO " )
3377        << " is_pacman " << ( is_pacman ? "YES" : "NO " )
3378        << " PACMAN_ALLOWED [" <<  sn__PhiCut_PACMAN_ALLOWED << "] " << PACMAN_ALLOWED
3379        << "\n"
3380        ;
3381 
3382     assert( expect_phi );
3383     assert( expect_cross_product );
3384     assert( expect );
3385 
3386     nd->setPA( cos_phi0, sin_phi0, cos_phi1, sin_phi1, zero, zero );
3387     return nd ;
3388 }
3389 
3390 
3391 /**
3392 sn::HalfSpace
3393 --------------
3394 
3395 Define CSG_HALFSPACE with a unit normal *n* and a distance *w* from the origin
3396 in the normal direction. Hence points *p* that are within the plane fulfil::
3397 
3398       p.n = w
3399 
3400                             n
3401                          .
3402            \  *       .
3403             \  *   .
3404              \  .
3405               O  *
3406                \  *
3407                 \  *
3408                  \
3409 
3410 
3411 
3412 See CSG/csg_intersect_leaf_halfspace.h
3413 
3414 **/
3415 
3416 
3417 inline sn* sn::HalfSpace(double x, double y, double z, double w)  // static
3418 {
3419     glm::tvec3<double> _normal(x,y,z);
3420     glm::tvec3<double> normal = glm::normalize(_normal);
3421     double plane_distance = w ;
3422 
3423     sn* nd = Create(CSG_HALFSPACE) ;
3424     nd->setPA( normal.x, normal.y, normal.z,  plane_distance, zero, zero );
3425     return nd ;
3426 }
3427 
3428 
3429 inline sn* sn::Notsupported() // static
3430 {
3431     sn* nd = Create(CSG_NOTSUPPORTED);
3432     return nd ;
3433 }
3434 
3435 
3436 inline sn* sn::Zero(double  x,  double y,  double z,  double w,  double z1, double z2) // static
3437 {
3438     sn* nd = Create(CSG_ZERO);
3439     nd->setPA( x, y, z, w, z1, z2 );
3440     return nd ;
3441 }
3442 inline sn* sn::Zero() // static
3443 {
3444     sn* nd = Create(CSG_ZERO);
3445     return nd ;
3446 }
3447 inline sn* sn::Prim(int typecode_)   // static
3448 {
3449     return new sn(typecode_, nullptr, nullptr) ;
3450 }
3451 inline sn* sn::Create(int typecode_, sn* left_, sn* right_)  // static
3452 {
3453     sn* nd = new sn(typecode_, left_, right_) ;
3454     return nd ;
3455 }
3456 inline sn* sn::Boolean(int typecode_, sn* left_, sn* right_)  // static
3457 {
3458     sn* nd = Create(typecode_, left_, right_);
3459     return nd ;
3460 }
3461 
3462 
3463 
3464 
3465 
3466 
3467 
3468 /**
3469 sn::ZNudgeExpandEnds
3470 ---------------------
3471 
3472 CAUTION: changes geometry, only appropriate
3473 for subtracted constituents eg inners
3474 
3475 This is used from U4Polycone::init_inner
3476 and is probably only applicable to the
3477 very controlled situation of the polycone
3478 with a bunch of cylinders and cones.
3479 
3480 * cf X4Solid::Polycone_Inner_Nudge
3481 
3482 **/
3483 
3484 inline void sn::ZNudgeExpandEnds(int lvid, std::vector<sn*>& prims, bool enable) // static
3485 {
3486     int num_prim = prims.size() ;
3487 
3488     sn* lower = prims[0] ;
3489     sn* upper = prims[prims.size()-1] ;
3490     bool can_znudge_ends = lower->can_znudge() && upper->can_znudge() ;
3491     assert( can_znudge_ends );
3492 
3493     double lower_zmin = lower->zmin() ;
3494     double upper_zmax = upper->zmax() ;
3495     bool z_expect = upper_zmax > lower_zmin  ;
3496 
3497     if(level() > 0) std::cout
3498        << std::endl
3499        << "sn::ZNudgeExpandEnds "
3500        << " lvid " << lvid
3501        << " num_prim " << num_prim
3502        << " enable " << ( enable ? "YES" : "NO " )
3503        << " level " << level()
3504        << " can_znudge_ends " << ( can_znudge_ends ? "YES" : "NO " )
3505        << " lower_zmin " << lower_zmin
3506        << " upper_zmax " << upper_zmax
3507        << " z_expect " << ( z_expect ? "YES" : "NO " )
3508        << std::endl
3509        << ZDesc(prims)
3510        << std::endl
3511        ;
3512 
3513     if(!enable) return ;
3514     assert( z_expect );
3515 
3516     double dz = 1. ;
3517     lower->decrease_zmin(dz);
3518     upper->increase_zmax(dz);
3519 }
3520 
3521 /**
3522 sn::ZNudgeOverlapJoints
3523 -------------------------
3524 **/
3525 
3526 inline void sn::ZNudgeOverlapJoints(int lvid, std::vector<sn*>& prims, bool enable ) // static
3527 {
3528     int num_prim = prims.size() ;
3529     assert( num_prim > 1 && "one prim has no joints" );
3530 
3531     bool dump = level() > 0 ;
3532 
3533     std::stringstream ss ;
3534     std::ostream* out = dump ? &ss : nullptr  ;
3535 
3536     if(out) *out
3537        << std::endl
3538        << "sn::ZNudgeOverlapJoints "
3539        << " lvid " << lvid
3540        << " num_prim " << num_prim
3541        << " enable " << ( enable ? "YES" : "NO " )
3542        << " level " << level()
3543        << std::endl
3544        << ZDesc(prims)
3545        << std::endl
3546        ;
3547 
3548     for(int i=1 ; i < num_prim ; i++)
3549     {
3550         sn* lower = prims[i-1] ;
3551         sn* upper = prims[i] ;
3552         ZNudgeOverlapJoint(lvid, i, lower, upper, enable, out );
3553     }
3554 
3555     if(out)
3556     {
3557         std::string str = ss.str();
3558         std::cout << str ;
3559     }
3560 }
3561 
3562 /**
3563 sn::ZNudgeOverlapJoint
3564 -----------------------
3565 
3566 This is used from U4Polycone nudging.
3567 
3568 It is not so easy to use this with the more general sn::uncoincide nudging
3569 because in this case there are transforms involved, so the zmax/zmin param
3570 may not look coincident but with the transforms they are.
3571 
3572 
3573 
3574 lower_rperp_at_zmax > upper_rperp_at_zmin::
3575 
3576         +-----+
3577         |     |
3578     +---+.....+---+
3579     |   +~~~~~+   |       upper->decrease_zmin
3580     |             |
3581     +-------------+
3582 
3583 !(lower_rperp_at_zmax > upper_rperp_at_zmin)::
3584 
3585     +-------------+
3586     |             |
3587     |   +~~~~~+   |    lower->increase_zmax
3588     +---+-----+---+
3589         |     |
3590         +-----+
3591 
3592 
3593 HMM a cone atop a cylinder where the cone at zmin
3594 starts at the same radius of the cylinder will mean that
3595 there will be a change to the shape for both
3596 uncoinciding by the cylinder expanding up and the cone
3597 expanding down. So there is no way to avoid concidence
3598 on that joint, without changing geometry::
3599 
3600 
3601         +----------------+
3602        /                  \
3603       /                    \
3604      /                      \
3605     ~~~~~~~~~~~~~~~~~~~~~~~~~~
3606     |                        |
3607     |                        |
3608     |                        |
3609     |                        |
3610     |                        |
3611     +------------------------+
3612 
3613 This happens with::
3614 
3615    NNVTMCPPMTsMask_virtual
3616    HamamatsuR12860sMask_virtual
3617 
3618 As they are virtual it doesnt matter for physics in this case :
3619 but that doesnt stop it being an issue.
3620 
3621 **/
3622 
3623 inline void sn::ZNudgeOverlapJoint(int lvid, int i, sn* lower, sn* upper, bool enable, std::ostream* out  ) // static
3624 {
3625     bool can_znudge_ = lower->can_znudge() && upper->can_znudge() ;
3626     if(!can_znudge_) std::raise(SIGINT) ;
3627     assert( can_znudge_ );
3628 
3629     double dz = 1. ;
3630     double lower_zmax = lower->zmax();
3631     double upper_zmin = upper->zmin() ;
3632     bool z_coincident_joint = std::abs( lower_zmax - upper_zmin ) < Z_EPSILON  ;
3633 
3634     double upper_rperp_at_zmin = upper->rperp_at_zmin() ;
3635     double lower_rperp_at_zmax = lower->rperp_at_zmax() ;
3636 
3637     if(out) *out
3638         << "sn::ZNudgeOverlapJoint"
3639         << " ("<< i-1 << "," << i << ") "
3640         << " lower_zmax " << lower_zmax
3641         << " upper_zmin " << upper_zmin
3642         << " z_coincident_joint " << ( z_coincident_joint ? "YES" : "NO " )
3643         << " enable " << ( enable ? "YES" : "NO " )
3644         << " upper_rperp_at_zmin " << upper_rperp_at_zmin
3645         << " lower_rperp_at_zmax " << lower_rperp_at_zmax
3646         << std::endl
3647         ;
3648 
3649     if(!z_coincident_joint) return ;
3650 
3651     if( lower_rperp_at_zmax > upper_rperp_at_zmin )
3652     {
3653         upper->decrease_zmin( dz );
3654         if(out) *out
3655             << "sn::ZNudgeOverlapJoint"
3656             << " lvid " << lvid
3657             << " lower_rperp_at_zmax > upper_rperp_at_zmin : upper->decrease_zmin( dz ) "
3658             << "  : expand upper down into bigger lower "
3659             << std::endl
3660             ;
3661     }
3662     else
3663     {
3664         lower->increase_zmax( dz );
3665         if(out) *out
3666             << "sn::ZNudgeOverlapJoints"
3667             << " lvid " << lvid
3668             << " !(lower_rperp_at_zmax > upper_rperp_at_zmin) : lower->increase_zmax( dz ) "
3669             << "  : expand lower up into bigger upper "
3670             << std::endl
3671             ;
3672     }
3673 }
3674 
3675 
3676 
3677 
3678 /**
3679 sn::can_znudge
3680 ----------------
3681 
3682 Typecode currently must be one of::
3683 
3684    CSG_CYLINDER
3685    CSG_CONE
3686    CSG_DISC
3687    CSG_ZSPHERE
3688 
3689 **/
3690 
3691 inline bool sn::can_znudge() const
3692 {
3693     return param && CSG::CanZNudge(typecode) ;
3694 }
3695 
3696 /**
3697 sn::CanZNudgeAll
3698 -----------------
3699 
3700 Returns true when all prim are ZNudge capable
3701 
3702 **/
3703 
3704 inline bool sn::CanZNudgeAll(std::vector<sn*>& prims)  // static
3705 {
3706     int num_prim = prims.size() ;
3707     int count = 0 ;
3708     for(int i=0 ; i < num_prim ; i++) if(prims[i]->can_znudge()) count += 1 ;
3709     return count == num_prim ;
3710 }
3711 
3712 
3713 inline unsigned sn::NameHint(const char* name) // static
3714 {
3715     unsigned hint = 0 ;
3716     if(     strstr(name, _HINT_LISTNODE_PRIM_DISCONTIGUOUS) != nullptr) hint = HINT_LISTNODE_PRIM_DISCONTIGUOUS ;
3717     else if(strstr(name, _HINT_LISTNODE_PRIM_CONTIGUOUS)    != nullptr) hint = HINT_LISTNODE_PRIM_CONTIGUOUS ;
3718     return hint ;
3719 }
3720 
3721 
3722 /**
3723 sn::set_hint_note
3724 ------------------
3725 
3726 Canonically invoked from U4Solid::init_Tree based on the solid name
3727 
3728 **/
3729 
3730 inline void sn::set_hint_note(unsigned hint)
3731 {
3732     if(hint == 0u) return ;
3733     if(      (hint & HINT_LISTNODE_PRIM_DISCONTIGUOUS) != 0 )  note |= HINT_LISTNODE_PRIM_DISCONTIGUOUS ;
3734     else if( (hint & HINT_LISTNODE_PRIM_CONTIGUOUS)    != 0 )  note |= HINT_LISTNODE_PRIM_CONTIGUOUS    ;
3735 }
3736 
3737 inline void sn::set_hint_listnode_prim_discontiguous(){ set_hint_note( HINT_LISTNODE_PRIM_DISCONTIGUOUS ); }
3738 inline void sn::set_hint_listnode_prim_contiguous(){    set_hint_note( HINT_LISTNODE_PRIM_CONTIGUOUS    ); }
3739 
3740 inline bool sn::is_hint_listnode_prim_discontiguous() const { return ( note & HINT_LISTNODE_PRIM_DISCONTIGUOUS ) != 0 ; }
3741 inline bool sn::is_hint_listnode_prim_contiguous() const {    return ( note & HINT_LISTNODE_PRIM_CONTIGUOUS )    != 0 ; }
3742 
3743 
3744 
3745 
3746 
3747 inline void sn::increase_zmax( double dz )
3748 {
3749     if(typecode == CSG_CONE)
3750     {
3751         increase_zmax_cone( dz );
3752     }
3753     else
3754     {
3755         increase_zmax_( dz );
3756     }
3757     note |= NOTE_INCREASE_ZMAX ;
3758 }
3759 
3760 inline void sn::decrease_zmin( double dz )
3761 {
3762     if(typecode == CSG_CONE)
3763     {
3764         decrease_zmin_cone( dz );
3765     }
3766     else
3767     {
3768         decrease_zmin_( dz );
3769     }
3770     note |= NOTE_DECREASE_ZMIN ;
3771 }
3772 
3773 
3774 
3775 
3776 
3777 /**
3778 sn::increase_zmax_
3779 ------------------
3780 
3781 Expand upwards in +Z direction::
3782 
3783     +~~~~~~~~+  zmax + dz  (dz > 0.)
3784     +--------+  zmax
3785     |        |
3786     |        |
3787     +--------+  zmin
3788 
3789 **/
3790 inline void sn::increase_zmax_( double dz )
3791 {
3792     assert( dz > 0. );
3793     double _zmax = zmax();
3794     double new_zmax = _zmax + dz ;
3795 
3796     std::cout
3797         << "sn::increase_zmax_"
3798         << " lvid " << lvid
3799         << " _zmax "    << std::fixed << std::setw(7) << std::setprecision(2) << _zmax
3800         << " dz "       << std::fixed << std::setw(7) << std::setprecision(2) << dz
3801         << " new_zmax " << std::fixed << std::setw(7) << std::setprecision(2) << new_zmax
3802         << std::endl
3803         ;
3804 
3805     set_zmax(new_zmax);
3806 }
3807 
3808 
3809 
3810 
3811 
3812 /**
3813 sn::decrease_zmin_
3814 --------------------
3815 
3816 Expand downwards in -Z direction::
3817 
3818     +--------+  zmax
3819     |        |
3820     |        |
3821     +--------+  zmin
3822     +~~~~~~~~+  zmin - dz    (dz > 0.)
3823 
3824 **/
3825 inline void sn::decrease_zmin_( double dz )
3826 {
3827     assert( dz > 0. );
3828     double _zmin = zmin();
3829     double new_zmin = _zmin - dz ;
3830 
3831     std::cout
3832         << "sn::decrease_zmin_"
3833         << " lvid " << lvid
3834         << " _zmin "    << std::fixed << std::setw(7) << std::setprecision(2) << _zmin
3835         << " dz "       << std::fixed << std::setw(7) << std::setprecision(2) << dz
3836         << " new_zmin " << std::fixed << std::setw(7) << std::setprecision(2) << new_zmin
3837         << std::endl
3838         ;
3839 
3840     set_zmin(new_zmin);
3841 }
3842 
3843 
3844 /**
3845 sn::increase_zmax_cone
3846 ------------------------
3847 
3848 Impl from ncone::decrease_z1
3849 
3850 This avoids increase_zmax and decrease_zmin
3851 changing angle of the cone by proportionately changing
3852 rperp_at_zmin and rperp_at_zmax
3853 
3854 
3855 
3856                    new_rperp_at_zmax : new_r2
3857                     |
3858                     | rperp_at_zmax  : r2
3859                     | |
3860                     | |
3861               +~~~~~+ |        new_zmax : new_z2
3862              /   .   \|
3863             +----.----+        zmax : z2           z2 > z1  by assertion
3864            /     .     \
3865           /      .      \
3866          /       .       \
3867         +--------.--------+    zmin : z1
3868        /         .        |\
3869       +~~~~~~~~~~.~~~~~~~~|~+  new_zmin : new_z1
3870                           | |
3871                           | |
3872                           rperp_at_zmin : r1
3873                             |
3874                            new_rperp_at_zmin : new_r1
3875 
3876 
3877 Consider increasing zmax by dz (dz > 0) to new_zmax
3878 while keeping the same cone angle::
3879 
3880 
3881        new_z2 - z1       z2 - z1
3882       -------------  =  ----------       ratios of corresponding z and r diffs
3883        new_r2 - r1       r2 - r1         must be equal for fixed cone angle
3884 
3885 
3886       new_r2 - r1     new_z2 - z1
3887       ----------  =  -------------       collect r and z terms on each side
3888        r2  - r1        z2 - z1
3889 
3890        new_r2 =   r1 +  (r2 - r1) ( new_z2 - z1 )/(z2 - z1)
3891 
3892 
3893 Similarly for decreasing zmin by dz (dz > 0) to new_zmin
3894 
3895 
3896      new_r1 - r2          r2 - r1
3897    ---------------  =   ----------
3898      new_z1 - z2          z2 - z1
3899 
3900 
3901 
3902      new_r1 = r2 + ( r2 - r1 )*(new_z1 - z2) / (z2 - z1 )
3903 
3904 
3905 **/
3906 
3907 
3908 
3909 inline void sn::increase_zmax_cone( double dz )
3910 {
3911     double r1 = param->value(0) ;  // aka rperp_at_zmin
3912     double z1 = param->value(1) ;  // aka zmin
3913     double r2 = param->value(2) ;  // aka rperp_at_zmax
3914     double z2 = param->value(3) ;  // aka zmax
3915 
3916     double new_z2 = z2 + dz ;
3917     double new_r2 = r1 +  (r2 - r1)*( new_z2 - z1 )/(z2 - z1)  ;
3918 
3919     std::cout
3920         << "sn::increase_zmax_cone"
3921         << " lvid " << lvid
3922         << " z2 " << z2
3923         << " r2 " << r2
3924         << " dz " << dz
3925         << " new_z2 " << new_z2
3926         << " new_r2 " << new_r2
3927         << std::endl
3928         ;
3929 
3930     set_zmax(new_z2);
3931     set_rperp_at_zmax(new_r2 < 0. ? 0. : new_r2);
3932 }
3933 
3934 
3935 inline void sn::decrease_zmin_cone( double dz )
3936 {
3937     double r1 = param->value(0) ;  // aka rperp_at_zmin
3938     double z1 = param->value(1) ;  // aka zmin
3939     double r2 = param->value(2) ;  // aka rperp_at_zmax
3940     double z2 = param->value(3) ;  // aka zmax
3941 
3942     double new_z1 = z1 - dz ;
3943     double new_r1 = r2 + (r2 - r1)*(new_z1 - z2) /(z2 - z1 ) ;
3944 
3945     std::cout
3946         << "sn::decrease_zmin_cone"
3947         << " lvid " << lvid
3948         << " z1 " << z1
3949         << " r1 " << r1
3950         << " dz " << dz
3951         << " new_z1 " << new_z1
3952         << " new_r1 " << new_r1
3953         << std::endl
3954         ;
3955 
3956     set_zmin(new_z1);
3957     set_rperp_at_zmin(new_r1 < 0. ? 0. : new_r1);
3958 
3959 }
3960 
3961 
3962 
3963 
3964 
3965 
3966 
3967 inline double sn::zmin() const
3968 {
3969     assert( can_znudge() );
3970     double v = 0. ;
3971     switch(typecode)
3972     {
3973         case CSG_CYLINDER: v = param->value(4) ; break ;
3974         case CSG_ZSPHERE:  v = param->value(4) ; break ;
3975         case CSG_CONE:     v = param->value(1) ; break ;
3976     }
3977     return v ;
3978 }
3979 
3980 inline void sn::set_zmin(double zmin_)
3981 {
3982     assert( can_znudge() );
3983     switch(typecode)
3984     {
3985         case CSG_CYLINDER: param->set_value(4, zmin_) ; break ;
3986         case CSG_ZSPHERE:  param->set_value(4, zmin_) ; break ;
3987         case CSG_CONE:     param->set_value(1, zmin_) ; break ;
3988     }
3989 }
3990 
3991 inline double sn::zmax() const
3992 {
3993     assert( can_znudge() );
3994     double v = 0. ;
3995     switch(typecode)
3996     {
3997         case CSG_CYLINDER: v = param->value(5) ; break ;
3998         case CSG_ZSPHERE:  v = param->value(5) ; break ;
3999         case CSG_CONE:     v = param->value(3) ; break ;
4000     }
4001     return v ;
4002 }
4003 inline void sn::set_zmax(double zmax_)
4004 {
4005     assert( can_znudge() );
4006     switch(typecode)
4007     {
4008         case CSG_CYLINDER: param->set_value(5, zmax_) ; break ;
4009         case CSG_ZSPHERE:  param->set_value(5, zmax_) ; break ;
4010         case CSG_CONE:     param->set_value(3, zmax_) ; break ;
4011     }
4012 }
4013 
4014 inline double sn::rperp_at_zmax() const
4015 {
4016     assert( can_znudge() );
4017     double v = 0. ;
4018     switch(typecode)
4019     {
4020         case CSG_CYLINDER: v = param->value(3)         ; break ;
4021         case CSG_ZSPHERE:  v = rperp_at_zmax_zsphere() ; break ;
4022         case CSG_CONE:     v = param->value(2)         ; break ;
4023     }
4024     return v ;
4025 }
4026 
4027 inline void sn::set_rperp_at_zmax(double rperp_) const
4028 {
4029     assert( can_znudge() );
4030     switch(typecode)
4031     {
4032         case CSG_CYLINDER: param->set_value(3, rperp_) ; break ;
4033         case CSG_CONE:     param->set_value(2, rperp_) ; break ;
4034     }
4035 }
4036 
4037 inline double sn::rperp_at_zmin() const
4038 {
4039     assert( can_znudge() );
4040     double v = 0. ;
4041     switch(typecode)
4042     {
4043         case CSG_CYLINDER: v = param->value(3)         ; break ;
4044         case CSG_ZSPHERE:  v = rperp_at_zmin_zsphere() ; break ;
4045         case CSG_CONE:     v = param->value(0)         ; break ;
4046     }
4047     return v ;
4048 }
4049 
4050 
4051 
4052 
4053 
4054 inline void sn::set_rperp_at_zmin(double rperp_) const
4055 {
4056     assert( can_znudge() );
4057     switch(typecode)
4058     {
4059         case CSG_CYLINDER: param->set_value(3, rperp_) ; break ;
4060         case CSG_CONE:     param->set_value(0, rperp_) ; break ;
4061     }
4062 }
4063 
4064 
4065 
4066 
4067 
4068 /**
4069 sn::Sphere_RPerp_At_Z
4070 -----------------------
4071 
4072                 :
4073                 :
4074                 *
4075                 :   *
4076                 :  /:    .
4077                 : r : z
4078                 :/  :       .
4079       +---------+---+-------+
4080                   p
4081 
4082       pp + zz = rr   =>   p = sqrt( rr - zz )
4083 
4084 **/
4085 
4086 inline double sn::Sphere_RPerp_At_Z(double r, double z)  // static
4087 {
4088     assert( std::abs(z) <= r );
4089     return sqrt(r*r - z*z ) ;
4090 }
4091 
4092 /**
4093 sn::rperp_at_zmin_zsphere sn::rperp_at_zmax_zsphere(
4094 ------------------------------------------------------
4095 
4096 ::
4097 
4098            _  _
4099         .          .
4100       /             \
4101      .               .
4102      +---------------+
4103 
4104 
4105 HMM: For ellipsoid need to apply transform to the node local
4106 param before using values at "tree level"
4107 
4108 
4109 **/
4110 inline double sn::rperp_at_zmin_zsphere() const
4111 {
4112     double r = radius_sphere() ;
4113     double z = zmin();
4114     double p = Sphere_RPerp_At_Z( r, z );
4115     return p  ;
4116 }
4117 inline double sn::rperp_at_zmax_zsphere() const
4118 {
4119     double r = radius_sphere() ;
4120     double z = zmax();
4121     double p = Sphere_RPerp_At_Z( r, z );
4122     return p ;
4123 }
4124 
4125 
4126 
4127 /**
4128 sn::ZDesc
4129 -----------
4130 
4131    +----+
4132    |    |
4133    +----+
4134    |    |
4135    +----+
4136    |    |
4137    +----+
4138 
4139 **/
4140 
4141 inline std::string sn::ZDesc(const std::vector<sn*>& prims) // static
4142 {
4143     int num_prim = prims.size() ;
4144     std::stringstream ss ;
4145     ss << "sn::ZDesc" ;
4146     ss << " prims(" ;
4147     for(int i=0 ; i < num_prim ; i++) ss << prims[i]->index() << " " ;
4148     ss << ") " ;
4149     ss << std::endl ;
4150 
4151     for(int i=0 ; i < num_prim ; i++)
4152     {
4153         sn* a = prims[i];
4154         ss << " idx "  << std::setw(3) << a->index()
4155            << " tag "   << std::setw(3) << a->tag()
4156            << " zmin " << std::setw(10) << a->zmin()
4157            << " zmax " << std::setw(10) << a->zmax()
4158            << " rperp_at_zmin " << std::setw(10) << a->rperp_at_zmin()
4159            << " rperp_at_zmax " << std::setw(10) << a->rperp_at_zmax()
4160            << std::endl
4161            ;
4162     }
4163     std::string str = ss.str();
4164     return str ;
4165 }
4166 
4167 
4168 inline void sn::getParam_(double& p0, double& p1, double& p2, double& p3, double& p4, double& p5 ) const
4169 {
4170     const double* d = param ? param->data() : nullptr ;
4171     p0 = d ? d[0] : 0. ;
4172     p1 = d ? d[1] : 0. ;
4173     p2 = d ? d[2] : 0. ;
4174     p3 = d ? d[3] : 0. ;
4175     p4 = d ? d[4] : 0. ;
4176     p5 = d ? d[5] : 0. ;
4177 }
4178 inline const double* sn::getParam() const { return param ? param->data() : nullptr ; }
4179 inline const double* sn::getAABB()  const { return aabb ? aabb->data() : nullptr ; }
4180 
4181 inline bool sn::hasAABB() const   // not-nullptr and not all zero
4182 {
4183     const double* aabb = getAABB();
4184     return aabb != nullptr && !s_bb::AllZero(aabb) ;
4185 }
4186 
4187 
4188 
4189 /**
4190 sn::Collection
4191 -----------------
4192 
4193 Used for example from U4Polycone::init
4194 
4195 +-------------+-------------------+-------------------+
4196 |  VERSION    |  Impl             |  Notes            |
4197 +=============+===================+===================+
4198 |     0       |  sn::UnionTree    | backward looking  |
4199 +-------------+-------------------+-------------------+
4200 |     1       |  sn::Contiguous   | forward looking   |
4201 +-------------+-------------------+-------------------+
4202 
4203 **/
4204 
4205 inline sn* sn::Collection(std::vector<sn*>& prims ) // static
4206 {
4207     sn* n = nullptr ;
4208     switch(VERSION)
4209     {
4210         case 0: n = UnionTree(prims)  ; break ;
4211         case 1: n = Contiguous(prims) ; break ;
4212     }
4213     return n ;
4214 }
4215 
4216 inline sn* sn::UnionTree(std::vector<sn*>& prims )
4217 {
4218     //sn* n = Buggy_CommonOperatorTree( prims, CSG_UNION );
4219     sn* n = BuildCommonTypeTree_Unbalanced(prims, CSG_UNION );
4220     return n ;
4221 }
4222 inline sn* sn::Contiguous(std::vector<sn*>& prims )
4223 {
4224     sn* n = Compound( prims, CSG_CONTIGUOUS );
4225     return n ;
4226 }
4227 inline sn* sn::Discontiguous(std::vector<sn*>& prims )
4228 {
4229     sn* n = Compound( prims, CSG_DISCONTIGUOUS );
4230     return n ;
4231 }
4232 
4233 /**
4234 sn::Compound
4235 ------------
4236 
4237 Note there is no subNum/subOffset here, those are needed when
4238 serializing the n-ary sn tree of nodes into CSGNode presumably.
4239 
4240 **/
4241 
4242 inline sn* sn::Compound(std::vector<sn*>& prims, int typecode_ )
4243 {
4244     assert( typecode_ == CSG_CONTIGUOUS || typecode_ == CSG_DISCONTIGUOUS );
4245 
4246     int num_prim = prims.size();
4247     assert( num_prim > 0 );
4248 
4249     sn* nd = Create( typecode_ );
4250 
4251     for(int i=0 ; i < num_prim ; i++)
4252     {
4253         sn* pr = prims[i] ;
4254 #ifdef WITH_CHILD
4255         nd->add_child(pr) ;
4256 #else
4257         assert(0 && "sn::Compound requires WITH_CHILD " );
4258         assert(num_prim == 2 );
4259         if(i==0) nd->set_left(pr,  false) ;
4260         if(i==1) nd->set_right(pr, false) ;
4261 #endif
4262     }
4263     return nd ;
4264 }
4265 
4266 
4267 
4268 
4269 
4270 
4271 /**
4272 sn::Buggy_CommonOperatorTree
4273 -----------------------------
4274 
4275 This has issues of inadvertent node deletion when
4276 there are for example 3 leaves::
4277 
4278 
4279         U
4280       U   2
4281     0  1
4282 
4283 The populate_leaves and/or prune needs to be cleverer
4284 to make this approach work.
4285 
4286 See sn::BuildCommonTypeTree_Unbalanced below for
4287 alternative without this bug.
4288 
4289 **/
4290 
4291 
4292 inline sn* sn::Buggy_CommonOperatorTree( std::vector<sn*>& leaves, int op ) // static
4293 {
4294     int num_leaves = leaves.size();
4295     sn* root = nullptr ;
4296     if( num_leaves == 1 )
4297     {
4298         root = leaves[0] ;
4299     }
4300     else
4301     {
4302         root = ZeroTree(num_leaves, op );
4303 
4304         if(level() > 0) std::cout
4305             << "sn::CommonOperatorTree after ZeroTree"
4306             << " num_leaves " << num_leaves
4307             << " level " << level()
4308             << std::endl
4309             ;
4310         if(level() > 1) std::cout << root->render(5) ;
4311 
4312         root->populate_leaves(leaves);
4313 
4314         if(level() > 0) std::cout
4315             << "sn::CommonOperatorTree after populate_leaves"
4316             << " num_leaves " << num_leaves
4317             << " level " << level()
4318             << std::endl
4319             ;
4320         if(level() > 1) std::cout << root->render(5) ;
4321 
4322         root->prune();
4323 
4324         if(level() > 0) std::cout
4325             << "sn::CommonOperatorTree after prun"
4326             << " num_leaves " << num_leaves
4327             << " level " << level()
4328             << std::endl
4329             ;
4330         if(level() > 1) std::cout << root->render(5) ;
4331     }
4332     return root ;
4333 }
4334 
4335 
4336 
4337 
4338 
4339 
4340 
4341 /**
4342 sn::BuildCommonTypeTree_Unbalanced
4343 ------------------------------------
4344 
4345 Simple unbalanced tree building from leaves that is now used from sn::UnionTree.
4346 Previously used a more complicated approach sn::Buggy_CommonOperatorTree
4347 
4348 For development of tree manipulations see::
4349 
4350      sysrap/tests/tree_test.cc
4351      sysrap/tests/tree.h
4352 
4353 To build unbalanced, after the first single leaf root,
4354 each additional leaf is accompanied by an operator node
4355 that becomes the new root::
4356 
4357     0
4358 
4359 
4360       U
4361     0   1
4362 
4363           U
4364       U     2
4365     0   1
4366 
4367              U
4368           U     3
4369       U     2
4370     0   1
4371 
4372 
4373 
4374 **/
4375 
4376 
4377 inline sn* sn::BuildCommonTypeTree_Unbalanced( const std::vector<sn*>& leaves, int typecode )  // static
4378 {
4379     int num_leaves = leaves.size() ;
4380     int num_leaves_placed = 0 ;
4381     if(num_leaves == 0) return nullptr ;
4382 
4383     sn* root = leaves[num_leaves_placed] ;
4384     num_leaves_placed += 1 ;
4385 
4386     while( num_leaves_placed < num_leaves )
4387     {
4388         root = Create(typecode, root, leaves[num_leaves_placed]);
4389         num_leaves_placed += 1 ;
4390     }
4391     return root ;
4392 }
4393 
4394 
4395 /**
4396 sn::GetLVListnodes
4397 -------------------
4398 
4399 Q: What about repeated globals, will this yield duplicates ?
4400 A: No, as are searching the sn::POOL which should have only one of each CSG node
4401 
4402 **/
4403 
4404 inline void sn::GetLVListnodes( std::vector<const sn*>& lns, int lvid ) // static
4405 {
4406     std::vector<const sn*> nds ;
4407     GetLVNodes_(nds, lvid );
4408 
4409     int num_nd = nds.size();
4410     for(int i=0 ; i < num_nd ; i++)
4411     {
4412         const sn* n = nds[i];
4413         if(n->is_listnode()) lns.push_back(n) ;
4414     }
4415 }
4416 
4417 inline int sn::GetChildTotal(  const std::vector<const sn*>& nds ) // static
4418 {
4419     int child_total = 0 ;
4420     int num_nd = nds.size();
4421     for(int i=0 ; i < num_nd ; i++)
4422     {
4423         const sn* n = nds[i];
4424         child_total += ( n ? n->num_child() : 0 ) ;
4425     }
4426     return child_total ;
4427 }
4428 
4429 
4430 /**
4431 sn::GetLVNodes
4432 ---------------
4433 
4434 Collect all sn with the provided lvid
4435 
4436 **/
4437 
4438 struct sn_find_lvid
4439 {
4440     int lvid ;
4441     sn_find_lvid(int q_lvid) : lvid(q_lvid) {}
4442     bool operator()(const sn* n){ return lvid == n->lvid ; }
4443 };
4444 
4445 inline void sn::GetLVNodes( std::vector<sn*>& nds, int lvid ) // static
4446 {
4447     sn_find_lvid flv(lvid);
4448     pool->find(nds, flv );
4449 }
4450 
4451 
4452 inline void sn::GetLVNodes_( std::vector<const sn*>& nds, int lvid ) // static
4453 {
4454     sn_find_lvid flv(lvid);
4455     pool->find_(nds, flv );
4456 }
4457 
4458 
4459 
4460 
4461 
4462 
4463 /**
4464 sn::getLVNodes
4465 ---------------
4466 
4467 Collect all sn with the lvid of this node.
4468 The vector is expected to include this node.
4469 
4470 **/
4471 
4472 inline void sn::getLVNodes( std::vector<sn*>& nds ) const
4473 {
4474     GetLVNodes(nds, lvid );
4475     assert( Includes(nds, const_cast<sn*>(this) ) );
4476 }
4477 
4478 inline bool sn::Includes( const std::vector<sn*>& nds, sn* nd ) // static
4479 {
4480     return std::find(nds.begin(), nds.end(), nd ) != nds.end() ;
4481 }
4482 
4483 inline sn* sn::Get(int idx) // static
4484 {
4485     return pool->getbyidx(idx) ;
4486 }
4487 
4488 
4489 /**
4490 sn::GetLVRoot
4491 ---------------
4492 
4493 First sn with the lvid and sn::is_root():true in (s_csg.h)pool
4494 
4495 **/
4496 
4497 struct sn_find_lvid_root
4498 {
4499     int lvid ;
4500     sn_find_lvid_root(int q_lvid) : lvid(q_lvid) {}
4501     bool operator()(const sn* n){ return lvid == n->lvid && n->is_root() ; }
4502 };
4503 
4504 
4505 inline void sn::FindLVRootNodes( std::vector<sn*>& nds, int lvid ) // static
4506 {
4507     sn_find_lvid_root flvr(lvid);
4508     pool->find(nds, flvr );
4509 }
4510 
4511 
4512 inline sn* sn::GetLVRoot( int lvid ) // static
4513 {
4514     std::vector<sn*> nds ;
4515     FindLVRootNodes( nds, lvid );
4516 
4517     int count = nds.size() ;
4518     bool expect = count == 0 || count == 1 ;
4519 
4520     const char* _DUMP = "sn__GetLVRoot_DUMP" ;
4521     bool DUMP = ssys::getenvbool(_DUMP) ;
4522     if(DUMP || !expect) std::cout
4523         << _DUMP << ":" << ( DUMP ? "YES" : "NO " ) << "\n"
4524         << "Desc(nds)\n"
4525         << Desc(nds)
4526         << "\n"
4527         << " nds.size " << count
4528         << " expect " << ( expect ? "YES" : "NO " )
4529         << "\n"
4530         ;
4531 
4532     assert(expect);
4533     return count == 1 ? nds[0] : nullptr ;
4534 }
4535 
4536 
4537 
4538 inline std::string sn::rbrief() const
4539 {
4540     std::stringstream ss ;
4541     ss << "sn::rbrief" << std::endl ;
4542 
4543     rbrief_r(ss, 0) ;
4544     std::string str = ss.str();
4545     return str ;
4546 }
4547 
4548 inline void sn::rbrief_r(std::ostream& os, int d) const
4549 {
4550     os << std::setw(3) << d << " : " << brief() << std::endl ;
4551     for(int i=0 ; i < num_child() ; i++) get_child(i)->rbrief_r(os, d+1) ;
4552 }
4553 
4554 
4555 /**
4556 sn::has_type
4557 ------------
4558 
4559 Returns true when this node has typecode present in the types vector.
4560 
4561 **/
4562 
4563 
4564 inline bool sn::has_type(const std::vector<OpticksCSG_t>& types) const
4565 {
4566     return std::find( types.begin(), types.end(), typecode ) != types.end() ;
4567 }
4568 
4569 /**
4570 sn::typenodes_
4571 -----------------
4572 
4573 Collect sn with typecode provided in the args.
4574 
4575 **/
4576 
4577 template<typename ... Args>
4578 inline void sn::typenodes_(std::vector<const sn*>& nds, Args ... tcs ) const
4579 {
4580     std::vector<OpticksCSG_t> types = {tcs ...};
4581     typenodes_r_(nds, types, 0 );
4582 }
4583 
4584 // NB MUST USE SYSRAP_API TO PLANT THE SYMBOLS IN THE LIB (OR MAKE THEM VISIBLE FROM ELSEWHERE)
4585 template SYSRAP_API void sn::typenodes_(std::vector<const sn*>& nds, OpticksCSG_t ) const  ;
4586 template SYSRAP_API void sn::typenodes_(std::vector<const sn*>& nds, OpticksCSG_t, OpticksCSG_t ) const ;
4587 template SYSRAP_API void sn::typenodes_(std::vector<const sn*>& nds, OpticksCSG_t, OpticksCSG_t, OpticksCSG_t ) const  ;
4588 
4589 /**
4590 sn::typenodes_r_
4591 -------------------
4592 
4593 Recursive traverse CSG tree collecting snd::index when the snd::typecode is in the types vector.
4594 
4595 **/
4596 
4597 inline void sn::typenodes_r_(std::vector<const sn*>& nds, const std::vector<OpticksCSG_t>& types, int d) const
4598 {
4599     if(has_type(types)) nds.push_back(this);
4600     for(int i=0 ; i < num_child() ; i++) get_child(i)->typenodes_r_(nds, types, d+1 ) ;
4601 }
4602 
4603 
4604 
4605 
4606 
4607 /**
4608 sn::max_binary_depth
4609 -----------------------
4610 
4611 Maximum depth of the binary compliant portion of the n-ary tree,
4612 ie with listnodes not recursed and where nodes have either 0 or 2 children.
4613 The listnodes are regarded as leaf node primitives.
4614 
4615 * Despite the *sn* tree being an n-ary tree (able to hold polycone and multiunion compounds)
4616   it must be traversed as a binary tree by regarding the compound nodes as effectively
4617   leaf node "primitives" in order to generate the indices into the complete binary
4618   tree serialization in level order
4619 
4620 * hence the recursion is halted at list nodes
4621 
4622 **/
4623 
4624 inline int sn::max_binary_depth() const
4625 {
4626     return max_binary_depth_r(0) ;
4627 }
4628 inline int sn::max_binary_depth_r(int d) const
4629 {
4630     int mx = d ;
4631     if( is_listnode() == false )
4632     {
4633         int nc = num_child() ;
4634         if( nc > 0 ) assert( nc == 2 ) ;
4635         for(int i=0 ; i < nc ; i++)
4636         {
4637             sn* ch = get_child(i) ;
4638             mx = std::max( mx,  ch->max_binary_depth_r(d + 1) ) ;
4639         }
4640     }
4641     return mx ;
4642 }
4643 
4644 
4645 
4646 
4647 
4648 /**
4649 sn::getLVBinNode
4650 ------------------
4651 
4652 Returns the number of nodes in a complete binary tree
4653 of height corresponding to the max_binary_depth
4654 of this node.
4655 
4656 **/
4657 
4658 inline uint64_t sn::getLVBinNode() const
4659 {
4660     int h = max_binary_depth();
4661     uint64_t n = st::complete_binary_tree_nodes( h );
4662     if(false) std::cout
4663         << "sn::getLVBinNode"
4664         << " h " << h
4665         << " n " << n
4666         << "\n"
4667         ;
4668     return n ;
4669 }
4670 
4671 /**
4672 sn::getLVSubNode
4673 -------------------
4674 
4675 Sum of children of compound nodes found beneath this node.
4676 HMM: this assumes compound nodes only contain leaf nodes
4677 
4678 Notice that the compound nodes themselves are regarded as part of
4679 the binary tree.
4680 
4681 **/
4682 
4683 inline uint64_t sn::getLVSubNode() const
4684 {
4685     int constituents = 0 ;
4686     std::vector<const sn*> subs ;
4687     typenodes_(subs, CSG_CONTIGUOUS, CSG_DISCONTIGUOUS, CSG_OVERLAP );
4688     int nsub = subs.size();
4689     for(int i=0 ; i < nsub ; i++)
4690     {
4691         const sn* nd = subs[i] ;
4692         assert( nd->typecode == CSG_CONTIGUOUS || nd->typecode == CSG_DISCONTIGUOUS );
4693         constituents += nd->num_child() ;
4694     }
4695     return constituents ;
4696 }
4697 
4698 
4699 /**
4700 sn::getLVNumNode
4701 -------------------
4702 
4703 Returns total number of nodes that can contain
4704 a complete binary tree + listnode constituents
4705 serialization of this node.
4706 
4707 **/
4708 
4709 inline uint64_t sn::getLVNumNode() const
4710 {
4711     uint64_t bn = getLVBinNode() ;
4712     uint64_t sn = getLVSubNode() ;
4713     return bn + sn ;
4714 }
4715 
4716 
4717 
4718 
4719 
4720 
4721 /**
4722 sn::GetLVNodesComplete
4723 -------------------------
4724 
4725 As the traversal is constrained to the binary tree portion of the n-ary snd tree
4726 can populate a vector of *snd* pointers in complete binary tree level order indexing
4727 with nullptr left for the zeros.  This is similar to the old NCSG::export_tree_r.
4728 
4729 **/
4730 
4731 inline void sn::GetLVNodesComplete(std::vector<const sn*>& nds, int lvid) // static
4732 {
4733     const sn* root = GetLVRoot(lvid);  // first sn from pool with requested lvid that is_root
4734     assert(root);
4735     root->getLVNodesComplete(nds);
4736 
4737     if(level() > 0 && nds.size() > 8 )
4738     {
4739         std::cout
4740             << "sn::GetLVNodesComplete"
4741             << " lvid " << lvid
4742             << " level " << level()
4743             << std::endl
4744             << root->rbrief()
4745             << std::endl
4746             << root->render(SUBDEPTH)
4747             ;
4748     }
4749 }
4750 
4751 /**
4752 sn::getLVNodesComplete
4753 -------------------------
4754 
4755 For BoxGridMultiUnion10:30_YX  which is grid of 3x3x3=27 multiunions with 7x7x7=343 prim each
4756 getting bn 1 ns 343
4757 
4758 THIS METHOD IS ALL ABOUT THE BINARY TREE
4759 
4760 ACCESSING THE LISTED CHILDREN OF THE LISTNODE NEEDS
4761 TO BE SEPARATE : BUT THE LISTNODE "HEAD" ITSELF
4762 IS PART OF THE BINARY TREE SO SHOULD BE INCLUDED
4763 IN THE RETURNED VECTOR
4764 
4765 **/
4766 
4767 inline void sn::getLVNodesComplete(std::vector<const sn*>& nds) const
4768 {
4769     uint64_t bn = getLVBinNode();
4770     uint64_t ns = getLVSubNode();
4771     uint64_t numParts = bn + ns ;
4772 
4773     //nds.resize(numParts);  // MAYBE THIS SHOULD JUST BE bn ?
4774     nds.resize(bn);
4775 
4776     if(false) std::cout
4777         << "sn::getLVNodesComplete"
4778         << " bn " << bn
4779         << " ns " << ns
4780         << " numParts " << numParts
4781         << "\n"
4782         ;
4783 
4784     // assert( ns == 0 ); // CHECKING : AS IMPL LOOKS LIKE ONLY HANDLES BINARY NODES
4785     // WIP: need to detect listnode at CSGImport::importPrim_ level
4786 
4787     GetLVNodesComplete_r( nds, this, 0 );
4788 }
4789 
4790 
4791 
4792 /**
4793 sn::GetLVNodesComplete_r
4794 -------------------------
4795 
4796 Serializes tree nodes into complete binary tree vector using
4797 0-based binary tree level order layout of nodes in the vector.
4798 
4799 **/
4800 
4801 inline void sn::GetLVNodesComplete_r(std::vector<const sn*>& nds, const sn* nd, int idx)  // static
4802 {
4803     assert( idx < int(nds.size()) );
4804     nds[idx] = nd ;
4805 
4806     int nc = nd->num_child() ;
4807 
4808     if( nc > 0 && nd->is_listnode() == false ) // non-list operator node
4809     {
4810         assert( nc == 2 ) ;
4811         for(int i=0 ; i < nc ; i++)
4812         {
4813             const sn* child = nd->get_child(i) ;
4814 
4815             int cidx = 2*idx + 1 + i ; // 0-based complete binary tree level order indexing
4816 
4817             GetLVNodesComplete_r(nds, child, cidx );
4818         }
4819     }
4820 }
4821 
4822 
4823 
4824 inline void sn::SelectListNode(std::vector<const sn*>& lns, const std::vector<const sn*>& nds) // static
4825 {
4826     for(unsigned i=0 ; i < nds.size() ; i++)
4827     {
4828        const sn* nd = nds[i] ;
4829        if(nd->is_listnode()) lns.push_back(nd);
4830     }
4831 }
4832 
4833 
4834 
4835 
4836 
4837 
4838 /**
4839 sn::ancestors (not including this node)
4840 -----------------------------------------
4841 
4842 Collect by following parent links then reverse
4843 the vector to put into root first order.
4844 
4845 **/
4846 
4847 inline void sn::ancestors(std::vector<const sn*>& nds) const
4848 {
4849     const sn* nd = this ;
4850     while( nd && nd->parent )
4851     {
4852         nds.push_back(nd->parent);
4853         nd = nd->parent ;
4854     }
4855     std::reverse( nds.begin(), nds.end() );
4856 }
4857 
4858 /**
4859 sn::connectedtype_ancestors
4860 -----------------------------
4861 
4862 Follow impl from nnode::collect_connectedtype_ancestors
4863 
4864 Notice this is different from selecting all ancestors and then requiring
4865 a type, because the traversal up the parent links is stopped
4866 once reaching an node of type different to the parent type.
4867 
4868 **/
4869 
4870 inline void sn::connectedtype_ancestors(std::vector<const sn*>& nds ) const
4871 {
4872     if(!parent) return ;   // start from parent to avoid collecting self
4873     ConnectedTypeAncestors( parent, nds, parent->typecode );
4874 }
4875 inline void sn::ConnectedTypeAncestors(const sn* n, std::vector<const sn*>& nds, int q_typecode) // static
4876 {
4877     while(n && n->typecode == q_typecode)
4878     {
4879         nds.push_back(n);
4880         n = n->parent ;
4881     }
4882 }
4883 
4884 
4885 
4886 /**
4887 sn::collect_progeny
4888 ---------------------
4889 
4890 Follow impl from nnode::collect_progeny
4891 
4892 Progeny excludes self, so start from child
4893 
4894 **/
4895 
4896 inline void sn::collect_progeny( std::vector<const sn*>& progeny, int exclude_typecode ) const
4897 {
4898     for(int i=0 ; i < num_child() ; i++)
4899     {
4900         const sn* ch = get_child(i);
4901         CollectProgeny_r(ch, progeny, exclude_typecode );
4902     }
4903 }
4904 inline void sn::CollectProgeny_r( const sn* n, std::vector<const sn*>& progeny, int exclude_typecode ) // static
4905 {
4906     if(n->typecode != exclude_typecode || exclude_typecode == CSG_ZERO)
4907     {
4908         if(std::find(progeny.begin(), progeny.end(), n) == progeny.end()) progeny.push_back(n);
4909     }
4910 
4911     for(int i=0 ; i < n->num_child() ; i++)
4912     {
4913         const sn* ch = n->get_child(i);
4914         CollectProgeny_r(ch, progeny, exclude_typecode );
4915     }
4916 }
4917 
4918 
4919 
4920 /**
4921 sn::collect_prim
4922 -----------------
4923 
4924 Current impl includes listnode subs in the prim vector
4925 as is_primitive judged by num child is true for listnode subs
4926 and false for the listnode itself.
4927 
4928 In a binary tree sense the listnode itself is a leaf encompassing
4929 its subs but in an n-ary tree sense the subs are the leaves and
4930 the listnode is their compound container parent.
4931 
4932 **/
4933 
4934 inline void sn::collect_prim( std::vector<const sn*>& prim ) const
4935 {
4936     collect_prim_r(prim, 0);
4937 }
4938 inline void sn::collect_prim_r( std::vector<const sn*>& prim, int d ) const
4939 {
4940     if(is_primitive()) prim.push_back(this);
4941     for(int i=0 ; i < num_child() ; i++) get_child(i)->collect_prim_r(prim, d+1) ;
4942 }
4943 
4944 
4945 inline bool sn::has_note( int q_note ) const
4946 {
4947     return ( note & q_note ) != 0 ;
4948 }
4949 
4950 /**
4951 sn::collect_prim_note
4952 ----------------------
4953 
4954 Collect prim with sn::note that bitwise includes the q_note bit
4955 Due to tail recursion this descends all the way before unwinding
4956 and collecting any prim, so the order is from the bottom first.
4957 
4958 **/
4959 
4960 inline void sn::collect_prim_note( std::vector<sn*>& prim, int q_note )
4961 {
4962     collect_prim_note_r(prim, q_note, 0);
4963 }
4964 inline void sn::collect_prim_note_r( std::vector<sn*>& prim, int q_note, int d )
4965 {
4966     if(is_primitive() && has_note(q_note)) prim.push_back(this);
4967     for(int i=0 ; i < num_child() ; i++) get_child(i)->collect_prim_note_r(prim, q_note, d+1) ;
4968 }
4969 
4970 
4971 
4972 /**
4973 sn::find_joint_to_candidate_listnode
4974 --------------------------------------
4975 
4976 ::
4977 
4978     sn::desc pid   18 idx   18 typecode   1 num_node  19 num_leaf  10 maxdepth  9 is_positive_form Y lvid   0 tag un
4979     sn::render mode 4 TYPETAG
4980                                                        un
4981 
4982                                                  un       cy
4983 
4984                                            un       cy
4985 
4986                                      un       cy
4987 
4988                                un       cy
4989 
4990                          un       cy
4991 
4992                    un       cy
4993 
4994             [un]      cy
4995 
4996        in       cy
4997 
4998     cy    !cy
4999 
5000 
5001 
5002 
5003 * off the top, where d=0, find all the listnode hinted prim (tail recursion, so the order is from bottom)
5004 * recursive traverse, not descending to leaves
5005 
5006   * look for left and right listnode hinted sub-trees
5007   * where left has no listnode hinted and right has fun : return that joint node
5008 
5009 
5010 Q: HMM does this require having run postconvert ?
5011 
5012 
5013 **/
5014 
5015 
5016 inline sn* sn::find_joint_to_candidate_listnode(std::vector<sn*>& prim, int q_note )
5017 {
5018     sn* j = find_joint_to_candidate_listnode_r(0, prim, q_note );
5019     return j ;
5020 }
5021 
5022 inline sn* sn::find_joint_to_candidate_listnode_r( int d, std::vector<sn*>& prim, int q_note )
5023 {
5024     if( d == 0 )
5025     {
5026         prim.clear();
5027         collect_prim_note(prim, q_note);
5028     }
5029     if(prim.size()==0) return nullptr ;  // none of the prim are hinted as listnode constituents
5030 
5031     sn* joint = nullptr ;
5032     if(num_child() != 2) return nullptr ;  // dont traverse leaves or compounds
5033 
5034     // look left and right
5035     std::vector<sn*> l_prim ;
5036     std::vector<sn*> r_prim ;
5037     get_child(0)->collect_prim_note(l_prim, q_note);
5038     get_child(1)->collect_prim_note(r_prim, q_note);
5039 
5040     if(r_prim.size() == 1 && l_prim.size() == 0 )
5041     {
5042         joint = this ;
5043     }
5044     else
5045     {
5046         for(int i=0 ; i < num_child() ; i++)
5047         {
5048             joint = get_child(i)->find_joint_to_candidate_listnode_r(d+1, prim, q_note ) ;
5049             if(joint) break ;
5050         }
5051     }
5052     return joint ;
5053 }
5054 
5055 inline bool sn::has_candidate_listnode(int q_note)
5056 {
5057     std::vector<sn*> prim ;
5058     sn* j = find_joint_to_candidate_listnode(prim, q_note);
5059     return j != nullptr && prim.size() > 0 ;
5060 }
5061 
5062 inline bool sn::has_candidate_listnode_discontiguous(){ return has_candidate_listnode( HINT_LISTNODE_PRIM_DISCONTIGUOUS ) ; }
5063 inline bool sn::has_candidate_listnode_contiguous(){    return has_candidate_listnode( HINT_LISTNODE_PRIM_CONTIGUOUS    ) ; }
5064 
5065 
5066 
5067 
5068 /**
5069 sn::CreateSmallerTreeWithListNode
5070 -----------------------------------
5071 
5072 Example tree, where the eight nodes with + are hinted as listnode prim::
5073 
5074 
5075                                                       (un)
5076 
5077                                                 (un)      cy+
5078 
5079                                           (un)      cy+
5080 
5081                                     (un)      cy+
5082 
5083                               (un)      cy+
5084 
5085                        (un)       cy+
5086 
5087                   (un)      cy+
5088 
5089             {un}      cy+
5090 
5091        in       cy+
5092 
5093     cy    !cy
5094 
5095 
5096 After shrinkage::
5097 
5098 
5099             {un}
5100 
5101        in       ln[cy+,cy+,cy+,cy+,cy+,cy+,cy+,cy+]
5102 
5103     cy    !cy
5104 
5105 
5106 
5107 Manipulations needed::
5108 
5109 1. find the joint node {un} between the extraneous (un) union nodes and
5110    the hinted prim cy+ to be incorporated into listnode
5111 
5112 2. collect the cy+ hinted prim doing a deepcopy that includes xform, param, aabb
5113 
5114 3. form the "ln" listnode from the deepcopied cy+ hinted prim
5115 
5116 4. set the right node of the deepcopied joint node {un} to the listnode "ln"
5117 
5118 
5119 Note that the created tree including param, xform, aabb is independent
5120 of the original tree due to the use of deepcopy for everything.
5121 This enables the entire old tree root to be deleted.
5122 
5123 Note that deepcopy excludes the parent pointer, but includes xform, param, aabb
5124 
5125 NB : this depends a lot on deepcopy
5126 
5127 **/
5128 
5129 inline sn* sn::CreateSmallerTreeWithListNode(sn* root0, int q_note ) // static
5130 {
5131     std::cerr << "[sn::CreateSmallerTreeWithListNode\n" ;
5132 
5133     std::vector<sn*> prim0 ;  // populated with the hinted listnode prim
5134     sn* j0 = root0->find_joint_to_candidate_listnode(prim0, q_note);
5135     if(j0 == nullptr) return nullptr ;
5136 
5137     std::vector<sn*> prim1 ;
5138     sn::DeepCopy(prim1, prim0);
5139 
5140     sn* ln = sn::Compound( prim1, TypeFromNote(q_note) );
5141 
5142     sn* j1 = j0->deepcopy();
5143 
5144     j1->set_right( ln, false );  // NB this deletes the extraneous RHS just copied by j0->deepcopy
5145     //j1->set_child_leaking_prior(1, ln, false);
5146 
5147 
5148     // ordering may be critical here as nodes get created and deleted by the above
5149 
5150     std::cerr << "]sn::CreateSmallerTreeWithListNode\n" ;
5151     return j1 ;
5152 }
5153 
5154 inline sn* sn::CreateSmallerTreeWithListNode_discontiguous(sn* root0)
5155 {
5156     return CreateSmallerTreeWithListNode( root0, HINT_LISTNODE_PRIM_DISCONTIGUOUS ) ;
5157 }
5158 inline sn* sn::CreateSmallerTreeWithListNode_contiguous(sn* root0)
5159 {
5160     return CreateSmallerTreeWithListNode( root0, HINT_LISTNODE_PRIM_CONTIGUOUS ) ;
5161 }
5162 
5163 inline int sn::TypeFromNote(int q_note) // static
5164 {
5165     int type = CSG_ZERO ;
5166     switch(q_note)
5167     {
5168        case HINT_LISTNODE_PRIM_DISCONTIGUOUS: type = CSG_DISCONTIGUOUS ; break ;
5169        case HINT_LISTNODE_PRIM_CONTIGUOUS:    type = CSG_CONTIGUOUS    ; break ;
5170     }
5171     assert( type != CSG_ZERO ) ;
5172     return type ;
5173 }
5174 
5175 
5176 
5177 
5178 
5179 /**
5180 sn::collect_monogroup
5181 -----------------------
5182 
5183 Follow impl from nnode::collect_monogroup
5184 
5185 
5186 1. follow parent links collecting ancestors until reach ancestor of another CSG type
5187    eg on starting with a primitive of CSG_UNION parent finds
5188    direct lineage ancestors that are also CSG_UNION
5189 
5190 2. for each of those same type ancestors collect
5191    all progeny but exclude the operator nodes to
5192    give just the prims within the same type monogroup
5193 
5194 **/
5195 
5196 inline void sn::collect_monogroup( std::vector<const sn*>& monogroup ) const
5197 {
5198    if(!parent) return ;
5199 
5200    std::vector<const sn*> connectedtype ;
5201    connectedtype_ancestors(connectedtype);
5202    int num_connectedtype = connectedtype.size() ;
5203 
5204    int exclude_typecode = parent->typecode ;
5205 
5206    for(int i=0 ; i < num_connectedtype ; i++)
5207    {
5208        const sn* ca = connectedtype[i];
5209        ca->collect_progeny( monogroup, exclude_typecode );
5210    }
5211 }
5212 
5213 /**
5214 sn::AreFromSameMonogroup
5215 --------------------------
5216 
5217 After nnode::is_same_monogroup
5218 
5219 1. if a or b have no parent or either of their parent type is not *op* returns false
5220 
5221 2. collect monogroup of a
5222 
5223 3. return true if b is found within the monogroup of a
5224 
5225 **/
5226 
5227 
5228 
5229 inline bool sn::AreFromSameMonogroup(const sn* a, const sn* b, int op)  // static
5230 {
5231    if(!a->parent || !b->parent || a->parent->typecode != op || b->parent->typecode != op) return false ;
5232 
5233    std::vector<const sn*> monogroup ;
5234    a->collect_monogroup(monogroup);
5235 
5236    return std::find(monogroup.begin(), monogroup.end(), b ) != monogroup.end() ;
5237 }
5238 
5239 
5240 inline bool sn::AreFromSameUnion(const sn* a, const sn* b) // static
5241 {
5242    return AreFromSameMonogroup(a,b, CSG_UNION );
5243 }
5244 
5245 
5246 
5247 /**
5248 sn::NodeTransformProduct
5249 ---------------------------
5250 
5251 cf nmat4triple::product
5252 
5253 1. finds CSG node ancestors of snd idx
5254 
5255 **/
5256 
5257 inline void sn::NodeTransformProduct(
5258     int idx,
5259     glm::tmat4x4<double>& t,
5260     glm::tmat4x4<double>& v,
5261     bool reverse,
5262     std::ostream* out,
5263     VTR* t_stack)  // static
5264 {
5265     sn* nd = Get(idx);
5266     assert(nd);
5267     nd->getNodeTransformProduct(t,v,reverse,out,t_stack) ;
5268 }
5269 
5270 inline std::string sn::DescNodeTransformProduct(
5271     int idx,
5272     glm::tmat4x4<double>& t,
5273     glm::tmat4x4<double>& v,
5274     bool reverse ) // static
5275 {
5276     std::stringstream ss ;
5277     ss << "sn::DescNodeTransformProduct" << std::endl ;
5278     NodeTransformProduct( idx, t, v, reverse, &ss, nullptr );
5279     std::string str = ss.str();
5280     return str ;
5281 }
5282 
5283 /**
5284 sn::getNodeTransformProduct
5285 -----------------------------
5286 
5287 HMM: does ancestors work with subs of a listnode ?
5288 
5289 Former sn__getNodeTransformProduct_KLUDGE_SKIP_CONTIGUOUS
5290     prevented inclusion of transforms from CSG_CONTIGUOUS (aka listnode)
5291     as observed some issue with extraneous transforms being set on those.
5292 
5293     The cause was found to be a bug in s_pool whereby sn csg nodes
5294     without transforms magically acquired them on being imported.
5295     The erroreous transform being the last one in the s_tv pool.
5296     Cause of this was a -1 meaning not-set being misinterpreted
5297     to mean the one before the last.
5298 
5299 **/
5300 
5301 
5302 
5303 inline void sn::getNodeTransformProduct(
5304     glm::tmat4x4<double>& t,
5305     glm::tmat4x4<double>& v,
5306     bool reverse,
5307     std::ostream* out,
5308     VTR* t_stack ) const
5309 {
5310     if(out) *out << "\nsn::getNodeTransformProduct.HEAD\n" ;
5311 
5312     std::vector<const sn*> nds ;
5313     ancestors(nds);
5314     nds.push_back(this);
5315 
5316     int num_nds = nds.size();
5317 
5318     if(out)
5319     {
5320         for(int i=0 ; i < num_nds ; i++) *out << "sn::getNodeTransformProduct.nd[" << i << "] " << nds[i]->desc() << "\n" ;
5321         *out
5322              << std::endl
5323              << "sn::getNodeTransformProduct"
5324              << " idx " << idx()
5325              << " reverse " << reverse
5326              << " num_nds " << num_nds
5327              << std::endl
5328              ;
5329     }
5330 
5331     glm::tmat4x4<double> tp(1.);
5332     glm::tmat4x4<double> vp(1.);
5333 
5334     for(int i=0 ; i < num_nds ; i++ )
5335     {
5336         int j  = num_nds - 1 - i ;
5337         const sn* ii = nds[reverse ? j : i] ;
5338         const sn* jj = nds[reverse ? i : j] ;
5339 
5340         int   itc = ii->typecode ;
5341         int   jtc = jj->typecode ;
5342 
5343         const s_tv* ixf = ii->xform ;
5344         const s_tv* jxf = jj->xform ;
5345 
5346         if(out)
5347         {
5348             *out
5349                 << " i " << i
5350                 << " j " << j
5351                 << " ii.idx " << ii->idx()
5352                 << " jj.idx " << jj->idx()
5353                 << " ixf " << ( ixf ? "Y" : "N" )
5354                 << " jxf " << ( jxf ? "Y" : "N" )
5355                 << " itc " << itc
5356                 << " jtc " << jtc
5357                 << std::endl
5358                 ;
5359 
5360            if(ixf) *out << stra<double>::Desc( ixf->t, ixf->v, "(ixf.t)", "(ixf.v)" ) << std::endl ;
5361            if(jxf) *out << stra<double>::Desc( jxf->t, jxf->v, "(jxf.t)", "(jxf.v)" ) << std::endl ;
5362         }
5363 
5364         if(t_stack && ixf) t_stack->push_back(ixf->t);
5365         if(ixf) tp *= ixf->t ;
5366         if(jxf) vp *= jxf->v ;  // // inverse-transform product in opposite order
5367 
5368     }
5369     memcpy( glm::value_ptr(t), glm::value_ptr(tp), sizeof(glm::tmat4x4<double>) );
5370     memcpy( glm::value_ptr(v), glm::value_ptr(vp), sizeof(glm::tmat4x4<double>) );
5371 
5372     if(out) *out << stra<double>::Desc( tp, vp , "tp", "vp" ) << std::endl ;
5373     if(out) *out << "\nsn::getNodeTransformProduct.TAIL\n" ;
5374 }
5375 
5376 inline std::string sn::desc_getNodeTransformProduct(
5377     glm::tmat4x4<double>& t,
5378     glm::tmat4x4<double>& v,
5379     bool reverse) const
5380 {
5381     std::stringstream ss ;
5382     ss << "sn::desc_getNodeTransformProduct" << std::endl ;
5383     getNodeTransformProduct( t, v, reverse, &ss, nullptr );
5384     std::string str = ss.str();
5385     return str ;
5386 }
5387 
5388 
5389 
5390 inline double sn::radius_sphere() const
5391 {
5392     double cx, cy, cz, r, z1, z2 ;
5393     getParam_(cx, cy, cz, r, z1, z2 );
5394     assert( cx == 0. && cy == 0. && cz == 0. ) ;
5395     return r ;
5396 }
5397 
5398 
5399 /**
5400 sn::setAABB_LeafFrame
5401 ----------------------
5402 
5403 See sn::postconvert for call context
5404 
5405 Gets parameters and uses to setBB depending on typecode.
5406 No transforms are used.
5407 
5408 Migrated down from CSGNode::setAABBLocal as nudging
5409 needs this done earlier.
5410 
5411 **/
5412 
5413 inline void sn::setAABB_LeafFrame()
5414 {
5415     if(typecode == CSG_SPHERE)
5416     {
5417         double cx, cy, cz, r, a, b ;
5418         getParam_(cx, cy, cz, r, a, b );
5419         assert( cx == 0. && cy == 0. && cz == 0. );
5420         assert( a == 0. && b == 0. );
5421         setBB(  -r, -r, -r,  r, r, r  );
5422     }
5423     else if(typecode == CSG_ZSPHERE)
5424     {
5425         double cx, cy, cz, r, z1, z2 ;
5426         getParam_(cx, cy, cz, r, z1, z2 );
5427         assert( cx == 0. && cy == 0. && cz == 0. ) ;
5428         assert( z1 == zmin());
5429         assert( z2 == zmax());
5430         assert( z2 > z1 );
5431         setBB(  -r, -r, z1,  r, r, z2  );
5432     }
5433     else if( typecode == CSG_CONE )
5434     {
5435         double r1, z1, r2, z2, a, b ;
5436         getParam_(r1, z1, r2, z2, a, b );
5437         assert( a == 0. && b == 0. );
5438         double rmax = fmaxf(r1, r2) ;
5439         setBB( -rmax, -rmax, z1, rmax, rmax, z2 );
5440     }
5441     else if( typecode == CSG_BOX3 )
5442     {
5443         double fx, fy, fz, a, b, c ;
5444         getParam_(fx, fy, fz, a, b, c );
5445         assert( a == 0. && b == 0. && c == 0. );
5446         setBB( -fx*0.5 , -fy*0.5, -fz*0.5, fx*0.5 , fy*0.5, fz*0.5 );
5447     }
5448     else if( typecode == CSG_CYLINDER || typecode == CSG_OLDCYLINDER )
5449     {
5450         double px, py, a, radius, z1, z2 ;
5451         getParam_(px, py, a, radius, z1, z2 ) ;
5452         assert( px == 0. && py == 0. && a == 0. );
5453         setBB( px-radius, py-radius, z1, px+radius, py+radius, z2 );
5454     }
5455     else if( typecode == CSG_CUTCYLINDER  )
5456     {
5457         double R, dz, pz_nrm_x, pz_nrm_z, nz_nrm_x, nz_nrm_z ;
5458         getParam_( R, dz, pz_nrm_x, pz_nrm_z, nz_nrm_x, nz_nrm_z );
5459 
5460         double pz_nrm_y = 0. ;
5461         double nz_nrm_y = 0. ;
5462 
5463         double zmin = 0. ;
5464         double zmax = 0. ;
5465         CutCylinderZRange(zmin, zmax, R, dz, pz_nrm_x, pz_nrm_y, pz_nrm_z, nz_nrm_x, nz_nrm_y, nz_nrm_z );
5466 
5467         setBB( -R, -R, zmin, +R, +R, zmax );
5468     }
5469     else if( typecode == CSG_DISC )
5470     {
5471         double px, py, ir, r, z1, z2 ;
5472         getParam_(px, py, ir, r, z1, z2);
5473         assert( px == 0. && py == 0. && ir == 0. );
5474         setBB( px - r , py - r , z1, px + r, py + r, z2 );
5475     }
5476     else if( typecode == CSG_HYPERBOLOID )
5477     {
5478         double r0, zf, z1, z2, a, b ;
5479         getParam_(r0, zf, z1, z2, a, b ) ;
5480         assert( a == 0. && b == 0. );
5481         assert( z1 < z2 );
5482         const double rr0 = r0*r0 ;
5483         const double z1s = z1/zf ;
5484         const double z2s = z2/zf ;
5485 
5486         const double rr1 = rr0 * ( z1s*z1s + 1. ) ;
5487         const double rr2 = rr0 * ( z2s*z2s + 1. ) ;
5488         const double rmx = sqrtf(fmaxf( rr1, rr2 )) ;
5489 
5490         setBB(  -rmx,  -rmx,  z1,  rmx, rmx, z2 );
5491     }
5492     else if( typecode == CSG_TORUS )
5493     {
5494         double rmin, rmax, rtor, startPhi_deg, deltaPhi_deg, zero ;
5495         getParam_(rmin, rmax, rtor, startPhi_deg, deltaPhi_deg, zero) ;
5496 
5497         double rext = rtor+rmax ;
5498         double rint = rtor-rmax ;
5499         double startPhi = startPhi_deg/180.*M_PI ;
5500         double deltaPhi = deltaPhi_deg/180.*M_PI ;
5501         double2 pmin ;
5502         double2 pmax ;
5503         sgeomtools::DiskExtent(rint, rext, startPhi, deltaPhi, pmin, pmax );
5504 
5505         setBB( pmin.x, pmin.y, -rmax, pmax.x, pmax.y, +rmax );
5506 
5507     }
5508     else if( typecode == CSG_UNION || typecode == CSG_INTERSECTION || typecode == CSG_DIFFERENCE )
5509     {
5510         setBB( 0. );
5511     }
5512     else if( typecode == CSG_CONTIGUOUS || typecode == CSG_DISCONTIGUOUS )
5513     {
5514         // cannot define bbox of list header nodes without combining bbox of all the subs
5515         // so have to defer setting the bbox until all the subs are converted
5516         setBB( 0. );
5517     }
5518     else if( typecode == CSG_NOTSUPPORTED )
5519     {
5520         setBB( 0. );
5521     }
5522     else if( typecode == CSG_ZERO )
5523     {
5524         setBB( UNBOUNDED_DEFAULT_EXTENT );
5525     }
5526     else if( typecode == CSG_PHICUT || typecode == CSG_HALFSPACE )
5527     {
5528         //setBB( UNBOUNDED_DEFAULT_EXTENT );
5529         setBB( 0. );
5530     }
5531     else if( typecode == CSG_THETACUT )
5532     {
5533         setBB( UNBOUNDED_DEFAULT_EXTENT );
5534     }
5535     else if( typecode == CSG_SEGMENT )
5536     {
5537         setBB( 0. );
5538         // segment is a special type of "leaf" where the bbox
5539         // can only be defined in combination with other nodes
5540     }
5541     else
5542     {
5543         std::cout
5544             << "sn::setAABB_LeafFrame : FATAL NOT IMPLEMENTED : "
5545             << " typecode " << typecode
5546             << " CSG::Name(typecode) " << CSG::Name(typecode)
5547             << std::endl
5548             ;
5549         assert(0);
5550         setBB( 0. );
5551     }
5552 }
5553 
5554 
5555 /**
5556 sn::setAABB_LeafFrame_All
5557 ---------------------------
5558 
5559 See sn::postconvert for call context
5560 
5561 1. collects vector of all prim
5562 2. for each prim call setAABB_LeadFrame
5563 
5564 **/
5565 
5566 inline void sn::setAABB_LeafFrame_All()
5567 {
5568     std::vector<const sn*> prim ;
5569     collect_prim(prim);
5570     int num_prim = prim.size() ;
5571     for(int i=0 ; i < num_prim ; i++)
5572     {
5573         const sn* p = prim[i] ;
5574         sn* _p = const_cast<sn*>(p) ;
5575         _p->setAABB_LeafFrame() ;
5576     }
5577 }
5578 
5579 
5580 /*
5581 sn::setAABB_TreeFrame_All formerly sn::setAABB
5582 -----------------------------------------------
5583 
5584 See sn::postconvert for call context
5585 
5586 1. collect vector of all prim
5587 2. setAABB_LeafFrame for each prim using param and typecode
5588 3. gets transform for the node and inplace applies it to the BB
5589 
5590 **/
5591 
5592 inline void sn::setAABB_TreeFrame_All()
5593 {
5594     std::vector<const sn*> prim ;
5595     collect_prim(prim);
5596     int num_prim = prim.size() ;
5597 
5598     for(int i=0 ; i < num_prim ; i++)
5599     {
5600         const sn* p = prim[i] ;
5601         sn* _p = const_cast<sn*>(p) ;
5602         _p->setAABB_LeafFrame() ;
5603 
5604         glm::tmat4x4<double> t(1.) ;
5605         glm::tmat4x4<double> v(1.) ;
5606 
5607         bool reverse = false ;
5608         std::ostream* out = nullptr ;
5609         VTR* t_stack = nullptr ;
5610         p->getNodeTransformProduct(t, v, reverse, out, t_stack );
5611 
5612         const double* pbb = _p->getBB_data() ;
5613         stra<double>::Transform_AABB_Inplace( const_cast<double*>(pbb),  t );
5614     }
5615 }
5616 
5617 
5618 
5619 /**
5620 sn::postconvert
5621 -----------------
5622 
5623 This is called from U4Solid::init_Tree only from the depth zero solid (ie root node of trees)
5624 
5625 Note that uncoincide needs the leaf bbox in tree frame so they can be compared
5626 to look for coincidences and make some parameter nudges.
5627 But subsequent code such as stree::get_combined_tran_and_aabb
5628 expects the bbox to be in leaf frame, hence the bbox are recomputed
5629 from the possibly nudged parameters in leaf frame after the uncoincide.
5630 
5631 HOW TO HANDLE AABB FOR LISTNODE ?
5632 
5633 * each sub has transform
5634 
5635 
5636 **/
5637 
5638 inline void sn::postconvert(int lvid)
5639 {
5640     set_lvid(lvid);
5641 
5642     positivize() ;
5643 
5644     setAABB_TreeFrame_All();
5645 
5646     uncoincide();
5647 
5648     setAABB_LeafFrame_All();
5649 }
5650 
5651 struct sn_compare
5652 {
5653     std::function<double(const sn* p)>& fn ;
5654     bool ascending ;
5655 
5656     sn_compare( std::function<double(const sn* p)>& fn_ , bool ascending_ )
5657         :
5658         fn(fn_),
5659         ascending(ascending_)
5660     {}
5661     bool operator()(const sn* a, const sn* b)
5662     {
5663         bool cmp = fn(a) < fn(b) ;
5664         return ascending ? cmp : !cmp ;
5665     }
5666 };
5667 
5668 /**
5669 sn::OrderPrim
5670 ---------------
5671 
5672 Sorts the vector of prim by comparison of the
5673 results of the argument function on each node.
5674 
5675 **/
5676 
5677 
5678 template<typename N>
5679 inline void sn::OrderPrim( std::vector<N*>& prim, std::function<double(N* p)> fn, bool ascending  ) // static
5680 {
5681     sn_compare cmp(fn, ascending) ;
5682     std::sort( prim.begin(), prim.end(), cmp );
5683 }
5684 
5685 
5686 inline void sn::Transform_Leaf2Tree( glm::tvec3<double>& xyz,  const sn* leaf, std::ostream* out )  // static
5687 {
5688     glm::tvec4<double> pos0 ;
5689     pos0.x = xyz.x ;
5690     pos0.y = xyz.y ;
5691     pos0.z = xyz.z ;
5692     pos0.w = 1. ;
5693 
5694     glm::tmat4x4<double> t(1.) ;
5695     glm::tmat4x4<double> v(1.) ;
5696     bool reverse = false ;
5697     VTR* t_stack = nullptr ;
5698     leaf->getNodeTransformProduct(t, v, reverse, nullptr, t_stack );
5699 
5700     glm::tvec4<double> pos = t * pos0 ;
5701 
5702     xyz.x = pos.x ;
5703     xyz.y = pos.y ;
5704     xyz.z = pos.z ;
5705 
5706     if(out) *out
5707         << "sn::Transform_Leaf2Tree"
5708         << std::endl
5709         << " pos0 " << stra<double>::Desc(pos0)
5710         << std::endl
5711         << " pos  " << stra<double>::Desc(pos)
5712         << std::endl
5713         ;
5714 }
5715 
5716 
5717 inline void sn::uncoincide()
5718 {
5719     int uncoincide_dump_lvid = ssys::getenvint("sn__uncoincide_dump_lvid", 107) ;
5720     bool dump = lvid == std::abs(uncoincide_dump_lvid) ;
5721     bool enable = true ;
5722 
5723     std::stringstream ss ;
5724     if(dump) ss
5725         << "sn::uncoincide"
5726         << " sn__uncoincide_dump_lvid " << uncoincide_dump_lvid
5727         << " lvid " << lvid
5728         << std::endl
5729         ;
5730 
5731     uncoincide_( enable, dump ? &ss : nullptr );
5732 
5733     if( dump )
5734     {
5735         std::string str = ss.str() ;
5736         std::cout << str << std::endl ;
5737     }
5738 }
5739 
5740 /**
5741 sn::uncoincide_
5742 -------------------
5743 
5744 Many box box coincidences are currently not counted from can_znudge:false
5745 
5746 **/
5747 
5748 inline void sn::uncoincide_(bool enable, std::ostream* out)
5749 {
5750     std::vector<const sn*> prim ;
5751     collect_prim(prim);
5752     int num_prim = prim.size() ;
5753 
5754     bool ascending = true ;
5755     OrderPrim<const sn>(prim, AABB_ZAvg, ascending ) ;
5756 
5757     if(out) *out
5758         << "sn::uncoincide_"
5759         << " lvid " << lvid
5760         << " num_prim " << num_prim
5761         << std::endl
5762         ;
5763 
5764     for(int i=1 ; i < num_prim ; i++)
5765     {
5766         const sn* lower = prim[i-1] ;
5767         const sn* upper = prim[i] ;
5768         sn* _lower = const_cast<sn*>( lower );
5769         sn* _upper = const_cast<sn*>( upper );
5770         uncoincide_zminmax( i, _lower, _upper, enable, out );
5771     }
5772 
5773     if(out) *out
5774         << "sn::uncoincide_"
5775         << " lvid " << lvid
5776         << " num_prim " << num_prim
5777         << " coincide " << coincide
5778         << std::endl
5779         ;
5780 }
5781 
5782 /**
5783 sn::uncoincide_zminmax
5784 ------------------------
5785 
5786 TODO : should be using a transformed rperp as this needs to
5787 operate in the frame of the root of the CSG tree
5788 
5789 **/
5790 
5791 inline void sn::uncoincide_zminmax( int i, sn* lower, sn* upper, bool enable, std::ostream* out )
5792 {
5793     bool can_znudge = lower->can_znudge() && upper->can_znudge() ;
5794     bool same_union = AreFromSameUnion(lower, upper) ;
5795     double lower_zmax = lower->getBB_zmax() ;
5796     double upper_zmin = upper->getBB_zmin() ;
5797     bool z_minmax_coincide =  std::abs( lower_zmax - upper_zmin ) < Z_EPSILON ;
5798     bool fixable_coincide = z_minmax_coincide && can_znudge && same_union ;
5799 
5800     if(out && z_minmax_coincide) *out
5801         << "sn::uncoincide_zminmax"
5802         << " lvid " << lvid
5803         << " ("<< i-1 << "," << i << ") "
5804         << " lower_zmax " << lower_zmax
5805         << " upper_zmin " << upper_zmin
5806         << " lower_tag " << lower->tag()
5807         << " upper_tag " << upper->tag()
5808         << " can_znudge " << ( can_znudge ? "YES" : "NO " )
5809         << " same_union " << ( same_union ? "YES" : "NO " )
5810         << " z_minmax_coincide " << ( z_minmax_coincide ? "YES" : "NO " )
5811         << " fixable_coincide " << ( fixable_coincide ? "YES" : "NO " )
5812         << " enable " << ( enable ? "YES" : "NO " )
5813         << std::endl
5814         ;
5815 
5816     if(!fixable_coincide) return ;
5817 
5818     coincide += 1 ;
5819 
5820     double upper_rperp_at_zmin = upper->rperp_at_zmin() ;
5821     double lower_rperp_at_zmax = lower->rperp_at_zmax() ;
5822 
5823     // NB these positions must use coordinates/param in corresponding upper/lower leaf frame
5824     glm::tvec3<double> upper_pos( upper->rperp_at_zmin(), upper->rperp_at_zmin(), upper->zmin() );
5825     glm::tvec3<double> lower_pos( lower->rperp_at_zmax(), lower->rperp_at_zmax(), lower->zmax() );
5826 
5827     // transforming the leaf frame coordinates into tree frame
5828     Transform_Leaf2Tree( upper_pos, upper, nullptr );
5829     Transform_Leaf2Tree( lower_pos, lower, nullptr );
5830 
5831     bool upper_lower_pos_z_equal = std::abs( upper_pos.z - lower_pos.z) < Z_EPSILON ;
5832     bool rperp_equal = std::abs( lower_pos.x - upper_pos.x ) < Z_EPSILON  ;
5833     bool upper_rperp_smaller = lower_pos.x > upper_pos.x ;
5834     bool upper_shape_smaller = upper->typecode == CSG_ZSPHERE && lower->typecode == CSG_CYLINDER ;
5835     bool upper_decrease_zmin = rperp_equal ?  upper_shape_smaller : upper_rperp_smaller ;
5836 
5837     double dz = 1. ;
5838     if( upper_decrease_zmin )  upper->decrease_zmin( dz );
5839     else                       lower->increase_zmax( dz );
5840 
5841 
5842     if(out) *out
5843         << "sn::uncoincide_zminmax"
5844         << " lvid " << lvid
5845         << " ("<< i-1 << "," << i << ") "
5846         << " lower_rperp_at_zmax " << lower_rperp_at_zmax
5847         << " upper_rperp_at_zmin " << upper_rperp_at_zmin
5848         << " (leaf frame) "
5849         << std::endl
5850         << " upper_pos " << stra<double>::Desc(upper_pos) << " (tree frame) "
5851         << std::endl
5852         << " lower_pos " << stra<double>::Desc(lower_pos) << " (tree frame) "
5853         << std::endl
5854         << " rperp_equal " << ( rperp_equal ? "YES" : "NO " )
5855         << " upper_rperp_smaller " << ( upper_rperp_smaller ? "YES" : "NO " )
5856         << " upper_shape_smaller " << ( upper_shape_smaller ? "YES" : "NO " )
5857         << " upper_decrease_zmin " << ( upper_decrease_zmin ? "YES" : "NO " )
5858         << " upper_lower_pos_z_equal " << ( upper_lower_pos_z_equal ? "YES" : "NO " )
5859         << std::endl
5860         << ( upper_decrease_zmin  ?
5861              "  upper->decrease_zmin( dz ) : expand upper down into bigger lower "
5862              :
5863              "  lower->increase_zmax( dz ) : expand lower up into bigger upper "
5864            )
5865         << " coincide " << coincide
5866         << std::endl
5867         ;
5868 
5869    assert( upper_lower_pos_z_equal && "EXPECTED upper_lower_pos_z_equal FOR COINCIDENT " );
5870 
5871 }
5872 
5873 
5874