Back to home page

EIC code displayed by LXR

 
 

    


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

0001 #pragma once
0002 /**
0003 SMesh.h : holds tri,vtx,nrm NP either from original G4VSolid conversion or concatenation
0004 ==========================================================================================
0005 
0006 NB SMesh.h is used in two situtions
0007 
0008 1. original SMesh converted from G4VSolid via U4Mesh created NPFold, with
0009    formation of normals using smooth or flat techniques
0010 
0011 2. concatenated SMesh with possibly thousands of SMesh joined together,
0012    normals are joined together from the inputs
0013 
0014 ::
0015 
0016     ~/o/sysrap/tests/SMesh_test.sh
0017     ~/o/sysrap/tests/SScene_test.sh
0018     ~/o/u4/tests/U4TreeCreateTest.sh
0019 
0020 **/
0021 
0022 #include <ostream>
0023 #include <glm/glm.hpp>
0024 #include <glm/gtx/string_cast.hpp>
0025 
0026 #include "stra.h"
0027 #include "s_bb.h"
0028 #include "NPFold.h"
0029 
0030 struct SMesh
0031 {
0032     static SMesh* Concatenate(std::vector<const SMesh*>& submesh, int ridx );
0033 
0034     static constexpr const bool DUMP = false ;
0035     static constexpr const int LIMIT = 50 ;
0036     static constexpr const char* NAME = "SMesh" ;
0037     static constexpr const char* VTX_SPEC = "3,GL_FLOAT,GL_FALSE,12,0,false" ;  // 12=3*sizeof(float)
0038     static constexpr const char* NRM_SPEC = "3,GL_FLOAT,GL_FALSE,12,0,false" ;
0039     static constexpr const char* MATROW_SPEC = "4,GL_FLOAT,GL_FALSE,64,0,false" ; // 64=4*4*sizeof(float)
0040 
0041     static constexpr const bool  NRM_SMOOTH = true ;
0042     // 3:vec3, 12:byte_stride 0:byte_offet
0043 
0044     const char* name    ;            // metadata : loaddir or manually set name
0045     const char* loaddir ;
0046     glm::tmat4x4<double> tr0 = {} ;  // informational for debug only, as gets applied by init
0047     int nidx = -1 ;
0048 
0049     std::vector<std::string> names ; // used to hold subnames in concat SMesh
0050     int   lvid = -1 ;      // set by Import from NPFold metadata for originals
0051 
0052     const NP* tri ;
0053     const NP* vtx ;
0054     const NP* nrm ;
0055 
0056     glm::tvec3<float> mn = {} ;
0057     glm::tvec3<float> mx = {} ;
0058     glm::tvec4<float> ce = {} ;
0059 
0060     struct zmax_functor {
0061         float operator()(const SMesh& mesh) const { return mesh.mx.z ; }
0062     };
0063 
0064     struct extent_functor {
0065         float operator()(const SMesh& mesh) const { return mesh.ce.w ; }
0066     };
0067 
0068 
0069     static SMesh* Load(const char* dir, const char* rel );
0070     static SMesh* Load(const char* dir );
0071     static SMesh* LoadTransformed(const char* dir, const char* rel,  const glm::tmat4x4<double>* tr );
0072     static SMesh* LoadTransformed(const char* dir,                   const glm::tmat4x4<double>* tr );
0073 
0074     static SMesh* Import(const NPFold* fold, const glm::tmat4x4<double>* tr=nullptr, int nidx=-1 );
0075     static SMesh* MakeCopy( const SMesh* src );
0076     SMesh* copy() const ;
0077 
0078     static bool IsConcat( const NPFold* fold );
0079     void import_(         const NPFold* fold, const glm::tmat4x4<double>* tr );
0080     void import_concat(   const NPFold* fold, const glm::tmat4x4<double>* tr );
0081     void import_original( const NPFold* fold, const glm::tmat4x4<double>* tr );
0082 
0083     NPFold* serialize() const ;
0084     void save(const char* dir) const ;
0085 
0086     SMesh();
0087 
0088     const float* get_mn() const ;
0089     const float* get_mx() const ;
0090     const float* get_ce() const ;
0091 
0092     void set_tri( const NP* _tri );
0093     int indices_num() const ;
0094     int indices_offset() const ;
0095     static const char* FormName(int ridx);
0096     void set_name(int ridx);
0097 
0098     void set_vtx( const NP* wvtx, const glm::tmat4x4<double>* tr, std::ostream* out  );
0099     void set_vtx_range();
0100 
0101     template<typename T>
0102     static T Extent( const glm::tvec3<T>& low, const glm::tvec3<T>& high );
0103 
0104     template<typename T>
0105     static glm::tvec4<T> CenterExtent( const glm::tvec3<T>& low, const glm::tvec3<T>& high );
0106 
0107     template<typename T>
0108     static void FindCenterExtent(const NP* vtx, glm::tvec3<T>& mn, glm::tvec3<T>& mx, glm::tvec4<T>& ce );
0109 
0110     template<typename T>
0111     static std::string Desc2D(const NP* a, int limit=200, const char* label=nullptr) ;
0112 
0113     template<typename T, typename S>
0114     static std::string Desc2D(const NP* a, const NP* b, int limit=200, const char* label=nullptr);
0115 
0116     std::string descTransform() const ;
0117 
0118     std::string brief() const ;
0119     std::string desc() const ;
0120     std::string descTri() const ;
0121     std::string descVtx() const ;
0122     std::string descTriVtx() const ;
0123     std::string descVtxNrm() const ;
0124     std::string descName() const ;
0125     std::string descShape() const ;
0126     std::string descRange() const ;
0127     std::string descRangeNumPy() const ;
0128 
0129 
0130     static std::string Desc2D_Ref_2D_int_float(const NP* a, const NP* b,  int limit, const char* label);
0131 
0132 
0133 
0134     template<typename T> static void SmoothNormals(
0135                std::vector<glm::tvec3<T>>& nrm,
0136          const std::vector<glm::tvec3<T>>& vtx,
0137          const std::vector<glm::tvec3<int>>& tri,
0138          std::ostream* out );
0139 
0140     template<typename T> static void FlatNormals(
0141                std::vector<glm::tvec3<T>>& nrm,
0142          const std::vector<glm::tvec3<T>>& vtx,
0143          const std::vector<glm::tvec3<int>>& tri,
0144          std::ostream* out );
0145 
0146 
0147     static NP* MakeNormals( const NP* a_vtx, const NP* a_tri, bool smooth, std::ostream* out );
0148 };
0149 
0150 /**
0151 SMesh::Concatenate
0152 --------------------
0153 
0154 Canonically invoked from::
0155 
0156    SScene::initFromTree_Remainder
0157    SScene::initFromTree_Factor_
0158 
0159 **/
0160 
0161 inline SMesh* SMesh::Concatenate(std::vector<const SMesh*>& submesh, int ridx )
0162 {
0163     SMesh* com = new SMesh ;
0164     com->set_name(ridx);
0165 
0166     std::vector<const NP*> subtri ;
0167     std::vector<const NP*> subvtx ;
0168     std::vector<const NP*> subnrm ;
0169 
0170     int tot_vtx = 0 ;
0171     for(int i=0 ; i < int(submesh.size()) ; i++)
0172     {
0173         const SMesh* sub = submesh[i] ;
0174         com->names.push_back(sub->name ? sub->name : "-" );
0175 
0176         const NP* _tri = sub->tri ;
0177         const NP* _vtx = sub->vtx ;
0178         const NP* _nrm = sub->nrm ;
0179 
0180         int num_vtx = _vtx->num_items() ;
0181         [[maybe_unused]] int num_nrm = _nrm->num_items() ;
0182         assert( num_vtx == num_nrm );
0183 
0184         subtri.push_back(NP::Incremented(_tri, tot_vtx)) ;
0185         subvtx.push_back(_vtx) ;
0186         subnrm.push_back(_nrm) ;
0187 
0188         tot_vtx += num_vtx ;
0189     }
0190     com->tri = NP::Concatenate(subtri) ;
0191     com->vtx = NP::Concatenate(subvtx) ;
0192     com->nrm = NP::Concatenate(subnrm) ;
0193     com->set_vtx_range();
0194 
0195     return com ;
0196 }
0197 
0198 inline SMesh* SMesh::Load(const char* dir, const char* rel )
0199 {
0200     if(DUMP) std::cout << "SMesh::Load dir " << ( dir ? dir : "-" ) << " rel " << ( rel ? rel : "-" )  << "\n" ;
0201     NPFold* fold = NPFold::Load(dir, rel) ;
0202     return Import(fold, nullptr);
0203 }
0204 inline SMesh* SMesh::Load(const char* dir)
0205 {
0206     if(DUMP) std::cout << "SMesh::Load dir " << ( dir ? dir : "-" ) << "\n" ;
0207     NPFold* fold = NPFold::Load(dir)  ;
0208     return Import(fold, nullptr);
0209 }
0210 
0211 inline SMesh* SMesh::LoadTransformed(const char* dir, const char* rel, const glm::tmat4x4<double>* tr)
0212 {
0213     NPFold* fold = NPFold::Load(dir, rel) ;
0214     return Import(fold, tr);
0215 }
0216 inline SMesh* SMesh::LoadTransformed(const char* dir, const glm::tmat4x4<double>* tr)
0217 {
0218     NPFold* fold = NPFold::Load(dir) ;
0219     return Import(fold, tr);
0220 }
0221 
0222 
0223 inline SMesh* SMesh::Import(const NPFold* fold, const glm::tmat4x4<double>* tr, int nidx)
0224 {
0225     //std::cout << "SMesh::Import fold.loaddir " << ( fold->loaddir ? fold->loaddir : "-" ) << "\n" ;
0226 
0227     SMesh* mesh = new SMesh ;
0228     mesh->import_(fold, tr);
0229     mesh->nidx = nidx ;
0230     mesh->loaddir = fold->loaddir ? strdup(fold->loaddir) : nullptr ;
0231     return mesh ;
0232 }
0233 
0234 inline SMesh* SMesh::MakeCopy( const SMesh* src ) // static
0235 {
0236     SMesh* dst = new SMesh ;
0237 
0238     dst->name = src->name ? strdup(src->name) : nullptr ;
0239     dst->tr0  = src->tr0 ;
0240     dst->nidx = src->nidx ;
0241     dst->loaddir = src->loaddir ? strdup(src->loaddir) : nullptr ;
0242     dst->names = src->names ;
0243     dst->lvid = src->lvid ;
0244 
0245     dst->tri = src->tri->copy() ;
0246     dst->vtx = src->vtx->copy() ;
0247     dst->nrm = src->nrm->copy() ;
0248 
0249     dst->mn = src->mn ;
0250     dst->mx = src->mx ;
0251     dst->ce = src->ce ;
0252 
0253     return dst ;
0254 }
0255 
0256 inline SMesh* SMesh::copy() const
0257 {
0258     return MakeCopy(this);
0259 }
0260 
0261 
0262 
0263 
0264 /**
0265 SMesh::IsConcat
0266 ----------------
0267 
0268 Concat distinguished by having float vertices (not double like originals)
0269 and having normals (unlike originals).
0270 
0271 WIP : SUSPECT THIS IS RETURNING TRUE FOR ORIGINALS
0272 
0273 **/
0274 
0275 inline bool SMesh::IsConcat( const NPFold* fold ) // static
0276 {
0277     const NP* vertices = fold->get("vtx") ;
0278     const NP* normals = fold->get("nrm") ;
0279     return vertices && vertices->ebyte == 4 && normals ;
0280 }
0281 
0282 inline void SMesh::import_(const NPFold* fold, const glm::tmat4x4<double>* tr )
0283 {
0284     lvid = fold->get_meta<int>("lvid", -1);
0285     bool is_concat = IsConcat( fold );
0286     if(DUMP) std::cout << "SMesh::import lvid " << lvid << " is_concat " << is_concat << "\n" ;
0287 
0288     if( is_concat )
0289     {
0290         import_concat( fold, tr  ) ;
0291     }
0292     else
0293     {
0294         import_original( fold, tr );
0295     }
0296 
0297 
0298 }
0299 
0300 inline void SMesh::import_concat(const NPFold* fold, const glm::tmat4x4<double>* tr )
0301 {
0302     assert( tr == nullptr );
0303 
0304     const NP* triangles = fold->get("tri");
0305     const NP* vertices = fold->get("vtx") ;
0306     const NP* normals = fold->get("nrm") ;
0307 
0308     tri = triangles ;
0309     vtx = vertices ;
0310     nrm = normals ;
0311 
0312     assert( tri );
0313     assert( vtx );
0314     assert( nrm );
0315 
0316     set_vtx_range();
0317 }
0318 
0319 inline void SMesh::import_original(const NPFold* fold, const glm::tmat4x4<double>* tr )
0320 {
0321     name = fold->loaddir ? strdup(fold->loaddir) : nullptr ;
0322 
0323     const NP* triangles = fold->get("tri");
0324     const NP* vertices = fold->get("vtx") ; // copy ?
0325     const NP* normals = fold->get("nrm") ;
0326 
0327     bool valid_import_original = lvid > -1 && triangles != nullptr && vertices != nullptr && normals == nullptr ;
0328     if(!valid_import_original) std::cerr
0329         << "SMesh::import_original\n"
0330         << " FATAL : FAILED IMPORT \n"
0331         << " valid_import_original " << ( valid_import_original ? "YES" : "NO ") << "\n"
0332         << " triangles " << ( triangles ? "YES" : "NO " ) << "\n"
0333         << " vertices " << ( vertices ? "YES" : "NO " ) << "\n"
0334         << " normals " << ( normals ? "YES" : "NO " ) << " (not execting normals in originals)\n"
0335         << " name " << ( name ? name : "-" ) << "\n"
0336         << " lvid " << lvid
0337         << "\n"
0338         ;
0339 
0340     assert(valid_import_original);
0341     assert( normals == nullptr ); // not expecting normals in originals currently
0342 
0343     bool dump = false ;
0344     std::stringstream ss ;
0345     std::ostream* out = dump ? &ss : nullptr ;
0346 
0347     if(out) *out << "[SMesh::import_original" << std::endl ;
0348 
0349     set_tri( triangles );
0350     set_vtx( vertices, tr, out );
0351     set_vtx_range();
0352 
0353     if(out) *out << "]SMesh::import_original" << std::endl ;
0354     if(dump) std::cout << ss.str() ;
0355 }
0356 
0357 
0358 inline NPFold* SMesh::serialize() const
0359 {
0360     NPFold* fold = new NPFold ;
0361     fold->add("tri", tri);
0362     fold->add("vtx", vtx);
0363     fold->add("nrm", nrm);
0364     fold->names = names ;
0365     fold->set_meta<int>("lvid", lvid) ;
0366     return fold ;
0367 }
0368 inline void SMesh::save(const char* dir) const
0369 {
0370     NPFold* fold = serialize();
0371     fold->save(dir);
0372 }
0373 
0374 
0375 inline SMesh::SMesh()
0376     :
0377     name(nullptr),
0378     tr0(1.),
0379     tri(nullptr),
0380     vtx(nullptr),
0381     nrm(nullptr),
0382     mn(0.f),
0383     mx(0.f),
0384     ce(0.f)
0385 {
0386 }
0387 
0388 inline const float* SMesh::get_mn() const { return glm::value_ptr(mn); }
0389 inline const float* SMesh::get_mx() const { return glm::value_ptr(mx); }
0390 inline const float* SMesh::get_ce() const { return glm::value_ptr(ce); }
0391 
0392 
0393 /**
0394 SMesh::set_tri
0395 ---------------------
0396 
0397 Removed face which was passenger only from U4Mesh for debugging
0398 
0399 **/
0400 
0401 inline void SMesh::set_tri( const NP* _tri )
0402 {
0403     tri = _tri ;
0404     assert( tri->uifc == 'i' );
0405     assert( tri->ebyte == 4 );
0406 }
0407 
0408 inline int SMesh::indices_num() const
0409 {
0410     if(tri == nullptr) return 0 ;
0411     assert( tri->shape.size() == 2 );
0412     return tri->num_values() ;
0413 }
0414 inline int SMesh::indices_offset() const
0415 {
0416     return 0 ;
0417 }
0418 
0419 inline const char* SMesh::FormName(int ridx) // static
0420 {
0421     std::stringstream ss ;
0422     ss << ridx ;
0423     std::string str = ss.str();
0424     return strdup( str.c_str() );
0425 }
0426 inline void SMesh::set_name( int ridx )
0427 {
0428     name = FormName(ridx);
0429 }
0430 
0431 
0432 
0433 inline void SMesh::set_vtx( const NP* _vtx, const glm::tmat4x4<double>* tr,  std::ostream* out  )
0434 {
0435     assert( tri );
0436 
0437     assert( _vtx->uifc == 'f' );
0438     assert( _vtx->ebyte == 8  );
0439 
0440     assert( _vtx->shape.size() == 2 );
0441     assert( _vtx->shape[0] > 2  );   // need at least 3 vtx to make a face
0442     assert( _vtx->shape[1] == 3 );
0443 
0444     if(tr) memcpy( glm::value_ptr(tr0), glm::value_ptr(*tr), sizeof(tr0) );  // informational only
0445     NP* wvtx = stra<double>::MakeTransformedArray( _vtx, tr );
0446     NP* wnrm = MakeNormals( wvtx, tri, NRM_SMOOTH, out );  // uses doubles
0447 
0448     vtx = NP::MakeNarrowIfWide(wvtx);
0449     nrm = NP::MakeNarrowIfWide(wnrm);
0450 }
0451 
0452 inline void SMesh::set_vtx_range()
0453 {
0454     FindCenterExtent(vtx, mn, mx, ce);
0455 }
0456 
0457 template<typename T>
0458 inline std::string SMesh::Desc2D(const NP* a, int limit, const char* label)
0459 {
0460     std::stringstream ss ;
0461     if(label) ss << "[ " << label << std::endl ;
0462     if(a == nullptr)
0463     {
0464         ss << " (null) " << std::endl ;
0465     }
0466     else
0467     {
0468         const T* vv = a->cvalues<T>();
0469         int ni = a->shape[0] ;
0470         int nj = a->shape[1] ;
0471 
0472         for(int i=0 ; i < ni ; i++)
0473         {
0474             bool emit = i < limit || i > ni - limit ;
0475             if(!emit) continue ;
0476 
0477             ss << std::setw(3) << i << " : " ;
0478             for(int j=0 ; j < nj ; j++)
0479             {
0480                 int idx = i*nj + j ;
0481                 if( a->uifc == 'i' || a->uifc == 'u' )
0482                 {
0483                     ss << std::setw(3) << vv[idx] << " " ;
0484                 }
0485                 else
0486                 {
0487                     ss << std::setw(7) << std::fixed << std::setprecision(2) << vv[idx] << " " ;
0488                 }
0489             }
0490             ss << std::endl ;
0491         }
0492     }
0493     if(label) ss << "] " << label << std::endl ;
0494     std::string str = ss.str() ;
0495     return str ;
0496 }
0497 
0498 
0499 
0500 template<typename T, typename S>
0501 inline std::string SMesh::Desc2D(const NP* a, const NP* b, int limit, const char* label)
0502 {
0503 
0504     std::stringstream ss ;
0505     if(label) ss << "[" << label << std::endl ;
0506 
0507     if( a == nullptr || b == nullptr )
0508     {
0509         ss << " missing a or b " << std::endl ;
0510     }
0511     else
0512     {
0513 
0514         const T* a_vv = a->cvalues<T>();
0515         int a_ni = a->shape[0] ;
0516         int a_nj = a->shape[1] ;
0517 
0518         const S* b_vv = b->cvalues<T>();
0519         [[maybe_unused]] int b_ni = b->shape[0] ;
0520         int b_nj = b->shape[1] ;
0521 
0522         assert( a_ni == b_ni );
0523         int ni = a_ni ;
0524 
0525         for(int i=0 ; i < ni ; i++)
0526         {
0527             bool emit = i < limit || i > (ni - limit) ;
0528             if(!emit) continue ;
0529 
0530             ss << std::setw(3) << i << " : " ;
0531 
0532             for(int j=0 ; j < a_nj ; j++)
0533             {
0534                 int idx = i*a_nj + j ;
0535                 if( a->uifc == 'i' || a->uifc == 'u' )
0536                 {
0537                     ss << std::setw(3) << a_vv[idx] << " " ;
0538                 }
0539                 else
0540                 {
0541                     ss << std::setw(7) << std::fixed << std::setprecision(2) << a_vv[idx] << " " ;
0542                 }
0543             }
0544 
0545             for(int j=0 ; j < b_nj ; j++)
0546             {
0547                 int idx = i*b_nj + j ;
0548                 if( b->uifc == 'i' || b->uifc == 'u' )
0549                 {
0550                     ss << std::setw(3) << b_vv[idx] << " " ;
0551                 }
0552                 else
0553                 {
0554                     ss << std::setw(7) << std::fixed << std::setprecision(2) << b_vv[idx] << " " ;
0555                 }
0556             }
0557             ss << std::endl ;
0558 
0559         }
0560     }
0561     if(label) ss << "]" << label << std::endl ;
0562     std::string str = ss.str() ;
0563     return str ;
0564 }
0565 
0566 inline std::string SMesh::descTransform() const
0567 {
0568     return stra<double>::Desc(tr0);
0569 }
0570 
0571 inline std::string SMesh::brief() const
0572 {
0573     std::stringstream ss ;
0574     ss << "SMesh::brief " ;
0575     //ss << ( loaddir ? loaddir : "-" ) ;
0576     ss << " tri " << tri->sstr() ;
0577     ss << " vtx " << vtx->sstr() ;
0578     ss << " nrm " << nrm->sstr() ;
0579 
0580     std::string str = ss.str() ;
0581     return str ;
0582 }
0583 
0584 
0585 inline std::string SMesh::descTri() const
0586 {
0587     return Desc2D<int>(tri,LIMIT, "SMesh::descTri") ;
0588 }
0589 inline std::string SMesh::descVtx() const
0590 {
0591     return Desc2D<float>(vtx,LIMIT,"SMesh::descVtx") ;
0592 }
0593 
0594 inline std::string SMesh::descTriVtx() const
0595 {
0596     return Desc2D_Ref_2D_int_float(tri,vtx,LIMIT/3,"SMesh::descTriVtx") ;
0597 }
0598 inline std::string SMesh::descVtxNrm() const
0599 {
0600     return Desc2D<float,float>(vtx,nrm,LIMIT,"SMesh::descVtxNrm") ;
0601 }
0602 
0603 
0604 
0605 inline std::string SMesh::Desc2D_Ref_2D_int_float(const NP* a, const NP* b,  int limit, const char* label)  // static
0606 {
0607     std::stringstream ss ;
0608     if(label) ss << "[ " << label << std::endl ;
0609     if( a == nullptr || b == nullptr )
0610     {
0611         ss << " a or b missing " << std::endl ;
0612     }
0613     else
0614     {
0615         const int* a_vv = a->cvalues<int>();
0616         int a_ni = a->shape[0] ;
0617         int a_nj = a->shape[1] ;
0618 
0619         const float* b_vv = b->cvalues<float>();
0620         [[maybe_unused]] int b_ni = b->shape[0] ;
0621         int b_nj = b->shape[1] ;
0622 
0623         for(int i=0 ; i < a_ni ; i++)
0624         {
0625             bool emit = i < limit || i > (a_ni - limit) ;
0626             if(!emit) continue ;
0627 
0628             for(int j=0 ; j < a_nj ; j++)
0629             {
0630                 int a_idx = i*a_nj + j ;
0631                 int v = a_vv[a_idx] ;
0632                 assert( v < b_ni );
0633 
0634                 ss << std::setw(3) << v << " : " ;
0635                 for(int bj=0 ; bj < b_nj ; bj++)
0636                 {
0637                     int b_idx =  v*b_nj + bj ;
0638                     ss << std::setw(7) << std::fixed << std::setprecision(2) << b_vv[b_idx] << " " ;
0639                 }
0640                 ss << std::endl ;
0641 
0642             }
0643             ss << std::endl ;
0644         }
0645     }
0646     if(label) ss << "] " << label << std::endl ;
0647     std::string str = ss.str() ;
0648     return str ;
0649 }
0650 
0651 inline std::string SMesh::desc() const
0652 {
0653     std::stringstream ss ;
0654     ss
0655        << "[SMesh::desc"
0656        << std::endl
0657        << descName()
0658        << descShape()
0659        << std::endl
0660        << descRange()
0661        << std::endl
0662        << descTri()
0663        << std::endl
0664        << descVtx()
0665        << std::endl
0666        << descTriVtx()
0667        << std::endl
0668        << descVtxNrm()
0669        << std::endl
0670        << "]SMesh::desc"
0671        << std::endl
0672        ;
0673 
0674     std::string str = ss.str();
0675     return str ;
0676 }
0677 
0678 inline std::string SMesh::descName() const
0679 {
0680     static const char * SPACER = "ridx    0 pidx    36 " ;
0681 
0682     std::stringstream ss ;
0683     ss
0684        << " lvid " << std::setw(3) << lvid
0685        << std::setw(strlen(SPACER)) << " "
0686        ;
0687     std::string str = ss.str();
0688     return str ;
0689 }
0690 
0691 inline std::string SMesh::descShape() const
0692 {
0693     std::stringstream ss ;
0694     ss
0695        << "SMesh::descShape" << std::endl
0696        << " tri "  << ( tri ? tri->sstr() : "-" )
0697        << " vtx "  << ( vtx ? vtx->sstr() : "-" )
0698        << " indices_num " << indices_num()
0699        << " indices_offset " << indices_offset()
0700        ;
0701     std::string str = ss.str();
0702     return str ;
0703 }
0704 
0705 inline std::string SMesh::descRange() const
0706 {
0707     std::array<float,6> bb = {mn[0],mn[1],mn[2],mx[0],mx[1],mx[2]} ;
0708 
0709     const float* _bb = bb.data();
0710     const float* _ce = glm::value_ptr(ce);
0711 
0712     std::stringstream ss ;
0713     ss << "SMesh::descRange  " ;
0714     ss << " ce " << s_bb::Desc_<float,4>( _ce ) ;
0715     ss << " bb " << s_bb::Desc_<float,6>( _bb) ;
0716     ss <<  descName() ;
0717     std::string str = ss.str();
0718     return str ;
0719 }
0720 
0721 inline std::string SMesh::descRangeNumPy() const
0722 {
0723     std::array<float,6> bb = {mn[0],mn[1],mn[2],mx[0],mx[1],mx[2]} ;
0724     const float* _ce = glm::value_ptr(ce);
0725     const float* _bb = bb.data();
0726 
0727     std::stringstream ss ;
0728     ss << s_bb::DescNumPy_<float,4>( _ce, "ce", false ) ;
0729     ss << s_bb::DescNumPy_<float,6>( _bb, "bb", false ) ;
0730     ss << " # SMesh::descRangeNumPy " <<  descName() ;
0731     std::string str = ss.str();
0732     return str ;
0733 }
0734 
0735 
0736 
0737 
0738 
0739 
0740 
0741 template<typename T>
0742 inline T SMesh::Extent( const glm::tvec3<T>& low, const glm::tvec3<T>& high ) // static
0743 {
0744     glm::tvec3<T> dim(high.x - low.x, high.y - low.y, high.z - low.z );
0745     T _extent(0) ;
0746     _extent = std::max( dim.x , _extent );
0747     _extent = std::max( dim.y , _extent );
0748     _extent = std::max( dim.z , _extent );
0749     _extent = _extent / T(2) ;
0750     return _extent ;
0751 }
0752 
0753 
0754 template<typename T>
0755 inline glm::tvec4<T> SMesh::CenterExtent( const glm::tvec3<T>& low, const glm::tvec3<T>& high ) // static
0756 {
0757     glm::tvec3<T> center = (low + high)/T(2) ;
0758     glm::tvec4<T> _ce ;
0759     _ce.x = center.x ;
0760     _ce.y = center.y ;
0761     _ce.z = center.z ;
0762     _ce.w = Extent<T>( low, high );
0763     return _ce ;
0764 }
0765 
0766 
0767 template<typename T>
0768 inline void SMesh::FindCenterExtent(const NP* vtx, glm::tvec3<T>& mn, glm::tvec3<T>& mx, glm::tvec4<T>& ce )
0769 {
0770     assert( vtx->ebyte == sizeof(T) );
0771     int item_stride = 1 ;
0772     int item_offset = 0 ;
0773     const_cast<NP*>(vtx)->minmax2D_reshaped<3,T>(&mn.x, &mx.x, item_stride, item_offset );
0774     ce = CenterExtent<T>( mn, mx );
0775 }
0776 
0777 
0778 /**
0779 SMesh::SmoothNormals
0780 ---------------------
0781 
0782 * https://computergraphics.stackexchange.com/questions/4031/programmatically-generating-vertex-normals
0783 
0784 The smoothing of normals is actually a
0785 cunning technique described by Inigo Quilezles (of SDF fame)
0786 
0787 * https://iquilezles.org/articles/normals/
0788 
0789 Essentially are combining non-normalized cross products
0790 from each face into the vertex normals... so the effect
0791 is to do a weighted average of the normals from all faces
0792 adjacent to the vertex with a weighting according to tri area.
0793 
0794 
0795 Triangle CW/CCW winding order ?
0796 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0797 
0798            C
0799            v2
0800            /.
0801           /  .
0802          /    .
0803        v0 ---- v1
0804        A        B
0805 
0806 glm::cross(v1-v0, v2-v0) is out the page (
0807 glm::cross(AB, AC)
0808 
0809 http://www.fromatogra.com/math/6-triangles
0810 
0811     To know whether a given triangle A, B, C has a clockwise (cw) or counter
0812     clockwise (ccw) winding order, you can look at the sign of the cross product of
0813     AB and AC.
0814 
0815     HUH: cross product is a vector, need to pick some direction... to dot product
0816     the normal with and compare signs of that
0817 
0818     How cw and ccw are mapped to a positive or negative cross product
0819     depends on how your cross product is defined and whether your y axis points up
0820     or down. Remember, the cross product of two vectors is the scaled sine of the
0821     angle between the vectors. When you use a mathematical coordinate system where
0822     y is up, ccw is positive and cw is negative. However in most graphical systems
0823     where y is down, cw is positive and ccw is negative.
0824 
0825 **/
0826 
0827 
0828 template<typename T>
0829 inline void SMesh::SmoothNormals(
0830     std::vector<glm::tvec3<T>>& nrm,
0831     const std::vector<glm::tvec3<T>>& vtx,
0832     const std::vector<glm::tvec3<int>>& tri,
0833     std::ostream* out  ) // static
0834 {
0835     int num_vtx = vtx.size();
0836     int num_tri = tri.size();
0837 
0838     typedef glm::tvec3<T>      R3 ;
0839     typedef glm::tvec3<int>    I3 ;
0840 
0841     nrm.resize(num_vtx);
0842     for(int i=0 ; i < num_vtx ; i++) nrm[i] = R3{}  ;
0843 
0844     for(int i=0 ; i < num_tri ; i++)
0845     {
0846         const I3& t = tri[i] ;
0847         if(out) *out << " tri[" << i << "] " << glm::to_string(t) << std::endl ;
0848 
0849         bool x_expected =  t.x > -1 && t.x < num_vtx ;
0850         bool y_expected =  t.y > -1 && t.y < num_vtx ;
0851         bool z_expected =  t.z > -1 && t.z < num_vtx ;
0852 
0853         bool expected = x_expected && y_expected && z_expected ;
0854 
0855         if(!expected ) std::cout
0856             << "SMesh::SmoothNormals"
0857             << " FATAL NOT expected "
0858             << " i [" << i << "] "
0859             << " t [" << glm::to_string(t) << "]"
0860             << " num_vtx " << num_vtx
0861             << " num_tri " << num_tri
0862             << std::endl
0863             ;
0864 
0865         assert( x_expected );
0866         assert( y_expected );
0867         assert( z_expected );
0868 
0869         const R3& v0 = vtx[t.x] ;
0870         const R3& v1 = vtx[t.y] ;
0871         const R3& v2 = vtx[t.z] ;
0872 
0873         R3 n = glm::cross(v1-v0, v2-v0) ;
0874 
0875         nrm[t.x] += n ;
0876         nrm[t.y] += n ;
0877         nrm[t.z] += n ;
0878     }
0879     for(int i=0 ; i < num_vtx ; i++) nrm[i] = glm::normalize( nrm[i] );
0880 }
0881 
0882 
0883 /**
0884 SMesh::FlatNormals
0885 -------------------
0886 
0887 Note the overwriting of the normal, last face wins
0888 
0889 **/
0890 
0891 template<typename T>
0892 inline void SMesh::FlatNormals(
0893     std::vector<glm::tvec3<T>>& nrm,
0894     const std::vector<glm::tvec3<T>>& vtx,
0895     const std::vector<glm::tvec3<int>>& tri,
0896     std::ostream* out ) // static
0897 {
0898     int num_vtx = vtx.size();
0899     int num_tri = tri.size();
0900 
0901     typedef glm::tvec3<T>      R3 ;
0902     typedef glm::tvec3<int>    I3 ;
0903 
0904     nrm.resize(num_vtx);
0905 
0906     for(int i=0 ; i < num_tri ; i++)
0907     {
0908         const I3& t = tri[i] ;
0909         assert( t.x > -1 && t.x < num_vtx );
0910         assert( t.y > -1 && t.y < num_vtx );
0911         assert( t.z > -1 && t.z < num_vtx );
0912 
0913         const R3& v0 = vtx[t.x] ;
0914         const R3& v1 = vtx[t.y] ;
0915         const R3& v2 = vtx[t.z] ;
0916 
0917         R3 n = glm::cross(v1-v0, v2-v0) ;
0918 
0919         nrm[t.x] = n ;
0920         nrm[t.y] = n ;
0921         nrm[t.z] = n ;
0922     }
0923 }
0924 
0925 
0926 
0927 /**
0928 SMesh::MakeNormals
0929 ---------------------
0930 
0931 **/
0932 
0933 inline NP* SMesh::MakeNormals( const NP* a_vtx, const NP* a_tri, bool smooth, std::ostream* out ) // static
0934 {
0935     int num_vtx = a_vtx ? a_vtx->shape[0] : 0 ;
0936     int num_tri = a_tri ? a_tri->shape[0] : 0 ;
0937 
0938     if(out) *out
0939         << "[ SMesh::MakeNormals "
0940         << " num_vtx " << num_vtx
0941         << " num_tri " << num_tri
0942         << std::endl
0943         ;
0944 
0945     typedef glm::tvec3<double> D3 ;
0946     typedef glm::tvec3<float>  F3 ;
0947     typedef glm::tvec3<int>    I3 ;
0948 
0949     assert( sizeof(D3) == sizeof(double)*3 );
0950     assert( sizeof(F3) == sizeof(float)*3 );
0951     assert( sizeof(I3) == sizeof(int)*3 );
0952 
0953     std::vector<I3> tri(num_tri) ;
0954     assert( NP::INT(sizeof(I3)*tri.size()) == a_tri->arr_bytes() );
0955     memcpy( tri.data(), a_tri->bytes(), a_tri->arr_bytes() );
0956 
0957     NP* a_nrm = nullptr ;
0958     if( a_vtx->ebyte == 8 )
0959     {
0960         std::vector<D3> vtx(num_vtx) ;
0961         std::vector<D3> nrm(num_vtx, {0,0,0}) ;
0962         assert( NP::INT(sizeof(D3)*vtx.size()) == a_vtx->arr_bytes() );
0963         memcpy( vtx.data(), a_vtx->bytes(), a_vtx->arr_bytes() );
0964 
0965         if(smooth)
0966         {
0967             SmoothNormals<double>( nrm, vtx, tri, out );
0968         }
0969         else
0970         {
0971             FlatNormals<double>( nrm, vtx, tri, out );
0972         }
0973 
0974         a_nrm = NP::Make<double>( num_vtx, 3 );
0975         memcpy( a_nrm->bytes(), nrm.data(), a_nrm->arr_bytes() );
0976     }
0977     else if( a_vtx->ebyte == 4 )
0978     {
0979         std::vector<F3> vtx(num_vtx) ;
0980         std::vector<F3> nrm(num_vtx, {0.f,0.f,0.f}) ;
0981         assert( NP::INT(sizeof(F3)*vtx.size()) == a_vtx->arr_bytes() );
0982         memcpy( vtx.data(), a_vtx->bytes(), a_vtx->arr_bytes() );
0983 
0984         if(smooth)
0985         {
0986             SmoothNormals<float>( nrm, vtx, tri, out );
0987         }
0988         else
0989         {
0990             FlatNormals<float>( nrm, vtx, tri, out );
0991         }
0992 
0993         a_nrm = NP::Make<float>( num_vtx, 3 );
0994         memcpy( a_nrm->bytes(), nrm.data(), a_nrm->arr_bytes() );
0995     }
0996 
0997     if(out) *out
0998         << "] SMesh::MakeNormals "
0999         << " a_vtx "  << ( a_vtx ? a_vtx->sstr() : "-" )
1000         << " a_tri "  << ( a_tri ? a_tri->sstr() : "-" )
1001         << " a_nrm "  << ( a_nrm ? a_nrm->sstr() : "-" )
1002         << " num_vtx " << num_vtx
1003         << " num_tri " << num_tri
1004         << std::endl
1005         ;
1006 
1007     return a_nrm ;
1008 }
1009 
1010