diff --git a/GeoModelCore/GeoModelKernel/GeoModelKernel/GeoPolyhedronGeneric.h b/GeoModelCore/GeoModelKernel/GeoModelKernel/GeoPolyhedronGeneric.h new file mode 100644 index 0000000000000000000000000000000000000000..f367de4e8bf2bdb5e1df85dedb828c1cf8b0abfa --- /dev/null +++ b/GeoModelCore/GeoModelKernel/GeoModelKernel/GeoPolyhedronGeneric.h @@ -0,0 +1,147 @@ +/* + Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef GEOMODELKERNEL_GEOPOLYHEDRONGENERIC_H +#define GEOMODELKERNEL_GEOPOLYHEDRONGENERIC_H + +/** + * @class: GeoPolyhedronGeneric + * + * @brief This shape represents a generic polyhedron, described by an array of vertices + * and faces defined by a set of ordered vertices + */ + +#include <vector> +#include "GeoModelKernel/GeoShape.h" + +class GeoPolyhedronGeneric : public GeoShape { + public: + // Default constructor + GeoPolyhedronGeneric(){}; + + // Constructor + GeoPolyhedronGeneric(const GeoShape*); + + // Copy constructor + GeoPolyhedronGeneric(const GeoPolyhedronGeneric& gpg); + + // Returns the volume of the shape, for mass inventory + virtual double volume() const; + + // Returns the bonding box of the shape + virtual void extent (double& xmin, double& ymin, double& zmin, + double& xmax, double& ymax, double& zmax) const; + + // Returns true if the shape contains the point, false otherwise. + virtual bool contains (double x, double y, double z) const; + + // Returns true if the shape contains the point, false otherwise. + virtual bool contains (GeoTrf::Vector3D point, double precision = 1.e-6, bool debugMode = false) const; + + // Returns the shape type, as a string + virtual const std::string& type() const { + return getClassType(); + } + + // Returns the shape type, as a coded integer + virtual ShapeType typeID() const{ + return getClassTypeID(); + } + + // For type identification + static const std::string& getClassType() { + return s_classType; + } + + // For type identification + static ShapeType getClassTypeID() { + return s_classTypeID; + } + + // Executes a GeoShapeAction + virtual void exec(GeoShapeAction *action) const; + + // Define vertices + void addVertices(std::vector<GeoTrf::Vector3D> vertices); + + // Add another facet to the polyhedron. A minimum of four + // faces are required to create a valid solid. + void addFace(std::vector<size_t> face); + + // normals can be uploaded or calculated + void addNormal(GeoTrf::Vector3D n); + + // finalize GPG + bool construct(bool debugMode = false); + + // Returns the number of vertices of the polyhedron + unsigned int getNVertices() const { return m_Vertices.size(); } + + // Returns the number of (boundary) faces of the polyhedron + unsigned int getNFaces() const { return m_Faces.size(); } + + // check of the hermeticity of the polyhedron (all edge need to be mirrored), plus at least four faces required, false otherwise + bool isValid ( bool debugMode = false) const; + + // Returns coordinates of the specified vertex + GeoTrf::Vector3D getVertex(unsigned int i) const { + return m_Vertices.at(i); + } + + // Returns definition of the specified face + std::vector<size_t> getFace(unsigned int i) const { + return m_Faces.at(i); + } + + // return normal ( pointing outwards ) + GeoTrf::Vector3D faceNormal(unsigned int i) const; + + // true for point laying on the face + bool insideFace(unsigned int i, GeoTrf::Vector3D point, double precision = 1.e-6, bool debugMode = false) const; + + // method to intersect ( extra-polyhedron) edge with face + std::vector< std::pair< double, std::pair<size_t,size_t> > > edgeFaceIntersect( int i, GeoTrf::Vector3D eInit, GeoTrf::Vector3D edge ) const; + + GeoTrf::Vector3D getRefPoint(int face = -1) const; + + int getOrigin() const; + void setOrigin( int ); + + void cleanAfterBooleanOperation(); + + // Returns true + virtual bool isPolyhedron () const { + return true; + } + + + virtual ~GeoPolyhedronGeneric() = default; +protected: + + private: + static const std::string s_classType; + static const ShapeType s_classTypeID; + + std::vector<GeoTrf::Vector3D> m_Vertices{}; + std::vector< std::vector<size_t> > m_Faces{}; + GeoTrf::Vector3D m_cog{GeoTrf::Vector3D(0.,0.,0.)}; + double m_span{0.}; + std::vector<GeoTrf::Vector3D> m_face_cog; + std::vector<double> m_face_span; + mutable std::vector<GeoTrf::Vector3D> m_Normals{}; + + bool intersectLines(GeoTrf::Vector3D A, GeoTrf::Vector3D a, GeoTrf::Vector3D B, GeoTrf::Vector3D b, double& da, double& db, double& dx) const; + + // fill reference span for volume and face : run automatically for transcript from GeoShape + void fillSpans(); + + // retrieve cached span value + double getRefSpan(int face = -1) const; + + // for debugging purposes + int m_origin{0}; + +}; + +#endif diff --git a/GeoModelCore/GeoModelKernel/GeoModelKernel/GeoPolyhedronManipulator.h b/GeoModelCore/GeoModelKernel/GeoModelKernel/GeoPolyhedronManipulator.h new file mode 100644 index 0000000000000000000000000000000000000000..c0b1fe90fcf5c84fd07f86e5f1e1361421fa3332 --- /dev/null +++ b/GeoModelCore/GeoModelKernel/GeoModelKernel/GeoPolyhedronManipulator.h @@ -0,0 +1,211 @@ +/* + Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef GEOMODELKERNEL_GEOPOLYHEDRONMANIPULATOR_H +#define GEOMODELKERNEL_GEOPOLYHEDRONMANIPULATOR_H + +/** + * @class GeoPolyhedronManipulator + * + * @brief This helper class collects methods for analytical and numerical processing of boolean shapes involving polyhedra. + * + */ + +#include "GeoModelKernel/GeoShape.h" +#include <GeoModelKernel/GeoDefinitions.h> +#include "GeoModelKernel/GeoShapeShift.h" +#include "GeoModelKernel/GeoPolyhedronGeneric.h" +#include <string> + +class GeoShapeIntersection; +class GeoShapeUnion; +class GeoShapeSubtraction; +class GeoPolyhedronGeneric; + +struct ShiftedGeoShape { + const GeoShape* shape{nullptr}; + const GeoPolyhedronGeneric* phed{nullptr}; + GeoTrf::Transform3D transform; + double volume{-1}; // -1 not calculated/not analytical + // constructor + ShiftedGeoShape(const GeoShape* inputShape) { + shape = inputShape; + transform = GeoTrf::Transform3D::Identity(); + // resolve (nested) shift transform + while ( shape->type()=="Shift" ) { + const GeoShapeShift* shift = dynamic_cast<const GeoShapeShift*> (shape); + transform = shift->getX()*transform; + shape = shift->getOp(); + } + if (!(shape->type()=="Union" || shape->type()=="Subtraction" || shape->type()=="Intersection") ) { + volume = shape->volume(); + phed = new GeoPolyhedronGeneric(shape); + if (!phed->isValid()) phed=nullptr; // temporary / to deal with non-polyhedrized shapes + } + } +}; + +struct NPL_vertex{ + int index; bool shared{false}; int overlapping_face{-1}; bool used{false}; int onEdge{-1}; bool onEdgeDummy{false}; + int shared_adjacent{-1}; bool oriented{false}; // int alternative_face; + bool onEdgeProtected{false}; // vertex with protected line connection + std::vector<int> active_lines; + std::pair<int,int> faces; // oriented : (incoming adjacent, outgoing hit face : edge->edge, edge->line, line->edge) + std::pair<int,int> faces_shared_loop; // non-oriented : line->line + NPL_vertex() { overlapping_face = -1; used = false; shared_adjacent = 0; onEdgeDummy = false; oriented = false;} +}; + +struct NPL_edge{ + std::pair<int,int> endpoints{std::make_pair<int>(-1,-1)}; + std::pair<int,int> endpoint_faces{std::make_pair<int>(-1,-1)}; + std::pair<int,int> endpoint_hitvx{std::make_pair<int>(-1,-1)}; + int adjacent_face{-1}; int overlap{-1}; int generating_edge{-1}; + bool used{false}; bool shared{false}; int symmetrized{-1}; + bool protectedLine{false}; int dummy_overlap{-1}; + // defoult constructor + NPL_edge(){} + // constructor + NPL_edge(std::pair<int,int> ep, int adjacent){ endpoints = ep; adjacent_face = adjacent; } +}; + +struct NPL_ordered{ + std::vector<size_t> vtx; + std::vector<int> edges; + int top_overlap{-1}; bool shared{false}; bool oriented{false}; + void reorder(size_t a) { + while ( vtx.front()!= a) { + vtx.push_back(vtx.front()); + vtx.erase(vtx.begin()); + edges.push_back(edges.front()); + edges.erase(edges.begin()); + } + } +}; + +struct NPH_plane{ + int index_ph; + std::vector<size_t> vertices; + std::vector<size_t> edges; + std::vector<NPL_vertex> hit_vertices; + std::vector<std::pair< int, std::vector< NPL_vertex > > > active_planes; + std::vector<NPL_vertex> vlog; + std::vector<NPL_vertex> vlog_ori; + //std::vector<NPL_ordered> shared_subface; + std::vector<NPL_ordered> subfaces; + std::vector<NPL_edge> plane_edges; + GeoTrf::Vector3D normal; + std::vector<int> overlapping; + NPH_plane(int index) { index_ph = index;} +}; + +struct NPH_vertex{ + int index{-1}; + int index_ph{-1}; + bool shared{false}; + int onDoublEdge{-1}; int hitDoublEdge{-1}; + std::pair<int,int> hit_edge_edge; + std::vector<std::pair<int,int> > edges; + NPH_vertex() {hit_edge_edge = std::pair<int,int>(-1,-1);} +}; + +struct NPH_vtx{ // vertex created by intersection of edge with face or edge or vertex of another polyhedron + int index; + int edge; // -1 for original vertices, generating edge otherwise + double distance; // along generating edge + int hitPlane; // index of intersected plane + int hitEdge; // index of intersected edge + int hitVertex; // index of intersected vertex + std::pair<int,int> faces; + NPH_vtx(int vtx_index) { index = vtx_index; hitPlane = -1; hitEdge = -1; hitVertex = -1; } +}; + +struct NPH_edge{ + int index_ph; int edge_inverse; + std::pair<int,int> endpoints; + std::pair<int,int> endpoint_faces; + std::pair<int,int> faces; + std::vector<int> inPlane; + std::vector<int> doubleEdge; + std::vector<NPH_vtx> evtx; // vector of vertices along edge, index+relative distance ( within (0,1) interval ) + // defoult constructor + NPH_edge() { endpoints = std::make_pair<int>(-1,-1); faces = std::make_pair<int>(-1,-1); endpoint_faces = std::make_pair<int>(-1,-1); edge_inverse = -1; } + // constructor + NPH_edge(std::pair<int,int> ep){ endpoints = ep; faces = std::make_pair<int>(-1,-1); endpoint_faces = std::make_pair<int>(-1,-1); edge_inverse = -1; } +}; + +struct NPH_line{ + std::pair<int,int> faces; + std::pair<int,int> endpoints; + std::vector<int> evtx; // vector of vertices along edge, index+relative distance ( within (0,1) interval ) + // constructor + NPH_line(std::pair<int,int> ef){ faces = ef; } +}; + +class GeoPolyhedronManipulator +{ + public: + + GeoPolyhedronGeneric* intersection( const GeoPolyhedronGeneric* phA, const GeoPolyhedronGeneric* phB, bool debugMode); + GeoPolyhedronGeneric* subtraction( const GeoPolyhedronGeneric* phA, const GeoPolyhedronGeneric* phB, bool debugMode); + GeoPolyhedronGeneric* uni( const GeoPolyhedronGeneric* phA, const GeoPolyhedronGeneric* phB, bool debugMode); + GeoPolyhedronGeneric* resolveBoolean(const GeoPolyhedronGeneric* phA, const GeoPolyhedronGeneric* phB, int modeBoolean, bool debugMode); + + private: + + // mutable unsigned int m_counterNumerical = 0; + double m_tolerance = 1.e-5; + // cache input polyhedra + const GeoPolyhedronGeneric* m_phA{nullptr}; + const GeoPolyhedronGeneric* m_phB{nullptr}; + + // method to find (distance to) the intersection of a (polyhedron) edge with (another) polyhedron face plane + // arguments : edge: (1) starting point, (2) endpoint-starting point, plane: (3) point , (4) normal + double edgeFaceIntersection( GeoTrf::Vector3D eInit, GeoTrf::Vector3D edge, GeoTrf::Vector3D facePoint, GeoTrf::Vector3D faceNormal ) const; + + // auxiliary method : input edges + std::vector<NPH_edge> collectEdges(std::vector<NPH_plane>& planes, std::vector<NPH_vertex>& vlog, bool debugMode = false ) const; + + // auxiliary method : intersections + void collectLines( std::vector<NPH_plane>& planes, std::vector<GeoTrf::Vector3D>& vtx, std::vector<NPH_vertex>& vlog, + std::vector<NPH_edge>& edges, bool debugMode ) const; + + std::vector<NPL_edge> evaluateVertexLogic(int plane_index, std::vector<NPH_vertex>& vlog, std::vector<NPH_edge> edges, + std::vector<NPH_plane>& planes, std::vector<GeoTrf::Vector3D> vtx) const; + + bool insideFace(unsigned int plane_index, GeoTrf::Vector3D point, double precision = 1.e-5, bool debugMode = false) const { + unsigned int nPA = m_phA->getNFaces(); + return ( plane_index < m_phA->getNFaces() ? m_phA->insideFace(plane_index,point, precision, debugMode) : + m_phB->insideFace(plane_index-nPA, point, precision, debugMode) ) ; + } + + void intersectFace(unsigned int in_edge, std::vector<NPH_edge>& edges, unsigned int plane_index, std::vector<NPH_plane>& planes, std::vector<GeoTrf::Vector3D>& vtx, std::vector<NPH_vertex>& vlog) const; + + bool intersectLines(GeoTrf::Vector3D A, GeoTrf::Vector3D a, GeoTrf::Vector3D B, GeoTrf::Vector3D b, double& da, double& db, double& d) const; + + bool inPlane(NPH_vertex vlog, std::vector<NPH_edge> edges, int ip) const; + + NPL_vertex filterLog(NPH_vertex vlog, std::vector<NPH_edge> edges, int ip, std::vector<NPH_plane> planes) const; + void filterLogPlane(NPL_vertex& plv, NPH_vertex& vlog, std::vector<NPH_edge> edges, int ip, std::vector<NPH_plane> planes) const; + + bool matchPlanes( int , int, std::vector<NPH_plane> planes ) const; + + std::vector<int> findHitPlane( NPH_vertex vlog, int ph_index, std::vector<NPH_edge> edges, std::vector<NPH_plane> planes) const; + + std::pair<int,int> hitDoubleEdge(int vx, int plane_index, int ph_index, std::vector<NPH_vertex> vlog, std::vector<NPH_edge> edges) const; + + void setDoubleEdge(int ie,int ia, std::vector<NPH_edge>& edges) const; + + NPL_edge symmetrize( NPL_edge line ) const; + + void saveHitOnEdge(NPH_vtx nvtx, std::vector<NPH_edge>& edges) const; + + NPL_edge createLine(int iv1, int iv2, int adj, NPH_plane plane) const; + + NPL_ordered mergeLoops( NPL_ordered loop1, NPL_ordered loop2, std::vector< GeoTrf::Vector3D > vtx); + + GeoTrf::Vector3D areaVec( std::vector<size_t> loop, std::vector< GeoTrf::Vector3D > vtx) const; + +}; + +#endif diff --git a/GeoModelCore/GeoModelKernel/src/GeoPolyhedronGeneric.cxx b/GeoModelCore/GeoModelKernel/src/GeoPolyhedronGeneric.cxx new file mode 100755 index 0000000000000000000000000000000000000000..28e9b1825ca4e8bf3b5ed0dd510b8c1d460d0141 --- /dev/null +++ b/GeoModelCore/GeoModelKernel/src/GeoPolyhedronGeneric.cxx @@ -0,0 +1,648 @@ +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#include "GeoModelKernel/GeoPolyhedronGeneric.h" +#include "GeoModelKernel/GeoShapeAction.h" +#include "GeoModelKernel/GeoBox.h" +#include "GeoModelKernel/GeoTrd.h" +#include "GeoModelKernel/GeoTube.h" +#include "GeoModelKernel/GeoSimplePolygonBrep.h" +#include <cmath> +#include <stdexcept> +#include <iostream> +#include <map> + + +const std::string GeoPolyhedronGeneric::s_classType = "PolyhedronGeneric"; +const ShapeType GeoPolyhedronGeneric::s_classTypeID = 0x25; + +GeoPolyhedronGeneric::GeoPolyhedronGeneric(const GeoShape* shape) { + + std::vector<GeoTrf::Vector3D> vtx; + + if (shape->type() == "Box") { + const GeoBox* box = dynamic_cast<const GeoBox*> (shape); + double hx = box->getXHalfLength(); + double hy = box->getYHalfLength(); + double hz = box->getZHalfLength(); + + vtx.push_back(GeoTrf::Vector3D(-hx,-hy, hz)); + vtx.push_back(GeoTrf::Vector3D(-hx,+hy, hz)); + vtx.push_back(GeoTrf::Vector3D(+hx,+hy, hz)); + vtx.push_back(GeoTrf::Vector3D(+hx,-hy, hz)); + vtx.push_back(GeoTrf::Vector3D(+hx,-hy,-hz)); + vtx.push_back(GeoTrf::Vector3D(+hx,+hy,-hz)); + vtx.push_back(GeoTrf::Vector3D(-hx,+hy,-hz)); + vtx.push_back(GeoTrf::Vector3D(-hx,-hy,-hz)); + + m_origin = 1; + } + + if (shape->type() == "Trd") { + const GeoTrd* trd = dynamic_cast<const GeoTrd*> (shape); + double hx1 = trd->getXHalfLength1(); + double hx2 = trd->getXHalfLength2(); + double hy1 = trd->getYHalfLength1(); + double hy2 = trd->getYHalfLength2(); + double hz = trd->getZHalfLength(); + + vtx.push_back(GeoTrf::Vector3D(-hx2,-hy2, hz)); + vtx.push_back(GeoTrf::Vector3D(-hx2,+hy2, hz)); + vtx.push_back(GeoTrf::Vector3D(+hx2,+hy2, hz)); + vtx.push_back(GeoTrf::Vector3D(+hx2,-hy2, hz)); + vtx.push_back(GeoTrf::Vector3D(+hx1,-hy1,-hz)); + vtx.push_back(GeoTrf::Vector3D(+hx1,+hy1,-hz)); + vtx.push_back(GeoTrf::Vector3D(-hx1,+hy1,-hz)); + vtx.push_back(GeoTrf::Vector3D(-hx1,-hy1,-hz)); + + m_origin = 2; + } + + if (shape->type() == "Box" || shape->type() == "Trd") { + + addVertices(vtx); + std::vector<size_t> f0 = {0,1,2,3}; + addFace(f0); + std::vector<size_t> f1 = {4,5,6,7}; + addFace(f1); + std::vector<size_t> f2 = {0,7,6,1}; + addFace(f2); + std::vector<size_t> f3 = {3,4,7,0}; + addFace(f3); + std::vector<size_t> f4 = {2,5,4,3}; + addFace(f4); + std::vector<size_t> f5 = {1,6,5,2}; + addFace(f5); + + for (unsigned int i=0; i<getNFaces(); i++) + m_Normals.push_back( + ((m_Vertices[m_Faces[i][0]]-m_Vertices[m_Faces[i][1]]).cross(m_Vertices[m_Faces[i][2]]-m_Vertices[m_Faces[i][1]])).normalized() ); + } + + if (shape->type() == "SimplePolygonBrep") { + + const GeoSimplePolygonBrep* spb = dynamic_cast<const GeoSimplePolygonBrep*> (shape); + double hz = spb->getDZ(); + unsigned int nvtx = spb->getNVertices(); + + for (unsigned int i=0; i < nvtx; i++) vtx.push_back(GeoTrf::Vector3D(spb->getXVertex(i),spb->getYVertex(i),hz)); + for (unsigned int i=0; i < nvtx; i++) vtx.push_back(GeoTrf::Vector3D(spb->getXVertex(i),spb->getYVertex(i),-hz)); + + // consistent coding requires points to be ordered clockwise + double clockwise = 0.; + for (unsigned int iv = 0; iv < nvtx; iv++) { + unsigned int iv1 = iv < nvtx-1 ? iv + 1 : iv + 1 - nvtx; + unsigned int iv2 = iv < nvtx-2 ? iv + 2 : iv + 2 - nvtx; + double pn = ( (vtx[iv]-vtx[iv1]).cross(vtx[iv2]-vtx[iv1]).normalized() ).z(); + if ( std::abs(pn) > 1.e-3 ) clockwise += pn; + } + + addVertices(vtx); + + std::vector<size_t> f0; std::vector<size_t> f1; + if (clockwise>0) { + for (unsigned int i=0; i < nvtx; i++) f0.push_back(i); + for (unsigned int i=2*nvtx-1; i >= nvtx; i--) f1.push_back(i); + } else { + for (int i=nvtx-1; i >= 0; i--) f0.push_back(i); + for (int i=nvtx; i < 2*nvtx; i++) f1.push_back(i); + } + addFace(f0); addNormal(GeoTrf::Vector3D(0.,0.,1.)); + addFace(f1); addNormal(GeoTrf::Vector3D(0.,0.,-1.)); + + for (unsigned int i=0; i<nvtx; i++) { + std::vector<size_t> f; f.clear(); + if (clockwise>0) { + f.push_back( i == nvtx-1 ? 0 : i+1); f.push_back(i); f.push_back(nvtx+i); f.push_back( i == nvtx-1 ? nvtx : nvtx+i+1); + } else { + f.push_back( i == nvtx-1 ? nvtx : nvtx+i+1); f.push_back(nvtx+i); f.push_back(i); f.push_back( i == nvtx-1 ? 0 : i+1); + } + addFace(f); addNormal( (m_Vertices[f[0]]-m_Vertices[f[1]]).cross(m_Vertices[f[2]]-m_Vertices[f[1]]).normalized() ); + } + + fillSpans(); + + // double-check normals and consistent ordering of faces + for (unsigned int ip=2; ip<getNFaces(); ip++) { + if (m_Normals[ip].norm()!=1) { + for (unsigned int iv=1; iv<m_Faces[ip].size()-2; iv++) { + // recalculate normal using other vertices + GeoTrf::Vector3D tn = ((m_Vertices[m_Faces[ip][iv]]-m_Vertices[m_Faces[ip][iv+1]]).cross(m_Vertices[m_Faces[ip][iv+2]]-m_Vertices[m_Faces[ip][iv+1]])).normalized(); + if (tn.norm()>0) { m_Normals[ip] = tn; break; } + } + } + } + + if (!isValid() ) std::cout << " GeoPolyhedronGeneric constructor from SimplePolygonBrep not valid" << std::endl; + + m_origin = 3; + } + + if (shape->type() == "Tube") { // polyhedrize : TODO : make the precision tunable + + const GeoTube* tube = dynamic_cast<const GeoTube*> (shape); + double hz = tube->getZHalfLength(); + double rmin = tube->getRMin(); + double rmax = tube->getRMax(); + + int nsplit = 16; + + // build outer shell + double ff = acos(-1.)/nsplit; + double rx = rmax * sqrt( ff / sin(ff) / cos(ff) ); + for (unsigned int i=0; i < nsplit; i++) vtx.push_back(GeoTrf::Vector3D(rx*cos(-i*2*ff),rx*sin(-i*2*ff),hz)); + for (unsigned int i=0; i < nsplit; i++) vtx.push_back(GeoTrf::Vector3D(rx*cos(-i*2*ff),rx*sin(-i*2*ff),-hz)); + + std::vector<size_t> f0; std::vector<size_t> f1; + + for (unsigned int i=0; i < nsplit; i++) f0.push_back(i); + for (unsigned int i=2*nsplit-1; i >= nsplit; i--) f1.push_back(i); + + if (rmin>0) { + + f0.push_back(0); f1.push_back(2*nsplit-1); // needed for combined loop + + double rn = rmin * sqrt( ff / sin(ff) / cos(ff) ); + for (unsigned int i=0; i < nsplit; i++) vtx.push_back(GeoTrf::Vector3D(rn*cos(-i*2*ff),rn*sin(-i*2*ff),-hz)); + for (unsigned int i=0; i < nsplit; i++) vtx.push_back(GeoTrf::Vector3D(rn*cos(-i*2*ff),rn*sin(-i*2*ff),hz)); + + for (unsigned int i=2*nsplit; i < 3*nsplit; i++) f1.push_back(i); + for (unsigned int i=4*nsplit-1; i >= 3*nsplit; i--) f0.push_back(i); + + f0.push_back(4*nsplit-1); f1.push_back(2*nsplit); // needed for combined loop + } + + addFace(f0); addNormal(GeoTrf::Vector3D(0.,0.,1.)); + addFace(f1); addNormal(GeoTrf::Vector3D(0.,0.,-1.)); + + addVertices(vtx); + + for (unsigned int i=0; i<nsplit; i++) { + std::vector<size_t> f; f.clear(); + f.push_back( i == nsplit-1 ? 0 : i+1); f.push_back(i); f.push_back(nsplit+i); f.push_back( i == nsplit-1 ? nsplit : nsplit+i+1); + addFace(f); addNormal( (m_Vertices[f[0]]-m_Vertices[f[1]]).cross(m_Vertices[f[2]]-m_Vertices[f[1]]).normalized() ); + } + + if (rmin>0) { + std::vector<size_t> f; f.clear(); + f.push_back(3*nsplit); f.push_back(0); f.push_back(nsplit); f.push_back(2*nsplit); + addFace(f); addNormal( (m_Vertices[f[0]]-m_Vertices[f[1]]).cross(m_Vertices[f[2]]-m_Vertices[f[1]]).normalized() ); + + for (unsigned int i=0; i<nsplit; i++) { + f.clear(); + f.push_back( i == 3*nsplit-1 ? 2*nsplit : 2*nsplit+i+1); f.push_back(2*nsplit+i); f.push_back(3*nsplit+i); f.push_back( i == 3*nsplit-1 ? 3*nsplit : 3*nsplit+i+1); + addFace(f); addNormal( (m_Vertices[f[0]]-m_Vertices[f[1]]).cross(m_Vertices[f[2]]-m_Vertices[f[1]]).normalized() ); + } + + f.clear(); + f.push_back(2*nsplit); f.push_back(nsplit); f.push_back(0); f.push_back(3*nsplit); + addFace(f); addNormal( (m_Vertices[f[0]]-m_Vertices[f[1]]).cross(m_Vertices[f[2]]-m_Vertices[f[1]]).normalized() ); + } + + fillSpans(); + + std::cout << "tube->polyhedron:rmin,rmax,hz:"<<rmin <<","<<rmax<<","<<hz<<":valid:" << this->isValid() <<":volume:" << this->volume() << std::endl; + + m_origin = 4; + + } + + if (m_face_span.size()<m_Faces.size()) fillSpans(); + +} + +GeoPolyhedronGeneric::GeoPolyhedronGeneric(const GeoPolyhedronGeneric& gpg) { + + m_Vertices = gpg.m_Vertices; + m_Faces = gpg.m_Faces; + m_Normals = gpg.m_Normals; + m_origin = gpg.m_origin; // only used for debugging of initial conversion from GeoShape + + if (isValid()) fillSpans(); + +} + +double GeoPolyhedronGeneric::volume () const { + if (!isValid()) + throw std::runtime_error ("Volume requested for incomplete polyhedron"); + // + GeoTrf::Vector3D refVtx = m_Vertices[0]; // reference point + double vol = 0.; + std::vector<GeoTrf::Vector3D> vtx; + + for (unsigned int i=0; i< getNFaces(); i++) { + + // retrieve vertices + std::vector<size_t> faceDef = getFace(i); + vtx.clear(); + for (auto vi : faceDef ) vtx.push_back(m_Vertices[vi]); + // normal + GeoTrf::Vector3D normal = faceNormal(i); + + // area + int n = vtx.size(); + GeoTrf::Vector3D area = vtx[0].cross(vtx[n - 1]); + for (int k = 1; k < n; ++k) area += vtx[k].cross(vtx[k-1]); + vol += 0.5 * area.norm() * (vtx[1]-refVtx).dot(normal) / 3.; + } + + return std::abs(vol); +} + +void GeoPolyhedronGeneric::extent (double& xmin, double& ymin, double& zmin, + double& xmax, double& ymax, double& zmax) const +{ + if (!isValid()) + throw std::runtime_error ("Extent requested for incomplete polyhedron"); + xmin = xmax = m_Vertices[0].x(); + ymin = ymax = m_Vertices[0].y(); + zmin = zmax = m_Vertices[0].z(); + for (size_t k = 1; k < m_Vertices.size(); ++k) + { + double x = m_Vertices[k].x(); + double y = m_Vertices[k].y(); + double z = m_Vertices[k].z(); + xmin = std::min(xmin, x); + xmax = std::max(xmax, x); + ymin = std::min(ymin, y); + ymax = std::max(ymax, y); + zmin = std::min(zmin, z); + zmax = std::max(zmax, z); + } +} + +bool GeoPolyhedronGeneric::contains (double x, double y, double z) const +{ + return this->contains( GeoTrf::Vector3D(x,y,z) ); +} + +bool GeoPolyhedronGeneric::contains ( GeoTrf::Vector3D point, double precision, bool debugMode ) const +{ + // TODO : make the method independent from the direction of normals ( so that it can be used for their validation ) + if (debugMode) std::cout <<"test inside for point:" << point.x()<<"," <<point.y()<<"," << point.z() + <<":distance from ref:"<< ( point - getRefPoint() ).norm() <<" compare with span:" << getRefSpan() << std::endl; + + // compare with object extent + if ( ( point - getRefPoint() ).norm() > getRefSpan() ) return false; + + // algorithm : count intersection with faces along probe line : point+(0,0,1)*t + // odd/even number of intersections : inside/outside + + bool ineg = false; bool ipos = false; + GeoTrf::Vector3D probeDir(0.,0.,1.); + std::vector<GeoTrf::Vector3D> intersections; // keep trace of intersections to intervene when an edge is hit + + for (auto vi : m_Vertices) if ( (vi - point).norm() < precision) return true; + + if (debugMode) for (auto vi : m_Vertices) std::cout <<"input vertices:" << vi.x() <<"," << vi.y() <<"," << vi.z() << std::endl; + + double h,pn,dist; + for (size_t i=0; i< getNFaces() ; i++) { + std::vector<size_t> face= getFace(i); + if (face.size() < 3) continue; + // check if point lays within the face + GeoTrf::Vector3D n = this->faceNormal(i); + + h = ( m_Vertices[m_Faces[i][0]] - point).dot(n); // nearest distance point - plane + + if ( std::abs(h) < precision ) { + if ( insideFace(i, point, precision) ) { + if (debugMode) std::cout << "point at face:"<< i << std::endl; + return true; + } + } + + pn = probeDir.dot(n); // dot product normal*probe direction + //if (debugMode) std::cout <<" face normal:" << i <<":" << n.x() <<"," << n.y()<<"," << n.z() << std::endl; + if ( std::abs(pn) > 1.e-9 ) { + dist = h / pn; + GeoTrf::Vector3D ix = point + dist*probeDir ; + + if ( insideFace(i, GeoTrf::Vector3D( ix ), precision, debugMode ) ) { + //if (debugMode) + // std::cout << "plane intersection:" << (point+dist*probeDir).x() <<"," <<(point+dist*probeDir).y() <<"," <<(point+dist*probeDir).z() + // <<": at distance:" << dist<<":valid:" << validIntersection << std::endl; + if ( std::abs(dist) < precision ) { + if (debugMode) std::cout << "point at face:"<< i << std::endl; + return true; + } + bool edgeHit = false; + for ( auto px : intersections ) if ((ix - px).norm() < precision ) edgeHit = true; + if (!edgeHit) { + if (dist > precision) ipos ^= true; + else if (dist < -precision) ineg ^= true; + if (debugMode) std::cout << "valid intersection with face:" << i << ":at distance:" << dist <<":pos:" << ipos <<":neg:" << ineg << std::endl; + intersections.push_back(ix); + } + } + } + } + + return ipos && ineg; // TODO check if not compatible +} + +void GeoPolyhedronGeneric::addVertices(std::vector<GeoTrf::Vector3D> vertices) +{ + m_Vertices = vertices; +} + +void GeoPolyhedronGeneric::addFace(std::vector<size_t> faceVtx) +{ + m_Faces.push_back(faceVtx); +} + +void GeoPolyhedronGeneric::addNormal(GeoTrf::Vector3D n) +{ + m_Normals.push_back(n); +} + + +GeoTrf::Vector3D GeoPolyhedronGeneric::faceNormal(unsigned int i) const +{ + + if (!m_Normals.size() ) { // recalculate normals + for (unsigned int i=0; i<getNFaces(); i++) { + m_Normals.push_back( + ( (m_Vertices[m_Faces[i][0]]-m_Vertices[m_Faces[i][1]]).cross(m_Vertices[m_Faces[i][2]]-m_Vertices[m_Faces[i][1]])).normalized() ) ; + // check orientation + for (unsigned int iv=0; iv<m_Faces[i].size()-2; iv++) { + GeoTrf::Vector3D ref = 0.5*(m_Vertices[m_Faces[i][iv]] + 0.5*(m_Vertices[m_Faces[i][iv+1]]+m_Vertices[m_Faces[i][iv+2]])); + if (insideFace(i,ref)) { + bool cn = contains( ref+1.e-3*m_Normals.back()); + if ( cn != contains( ref-1.e-3*m_Normals.back()) ) { // valid test point + if (cn) m_Normals.back() = -1.* m_Normals.back(); + break; + } + } + } + } + } + + return m_Normals[i]; +} + +bool GeoPolyhedronGeneric::insideFace(unsigned int i, GeoTrf::Vector3D point, double precision, bool debugMode) const +{ + std::vector<GeoTrf::Vector3D> vtx; + std::vector<size_t> face= getFace(i); + for (auto vi : face ) vtx.push_back(m_Vertices[vi]); + + if ( std::abs(( point - vtx[0]).dot(faceNormal(i) ) ) > precision ) { + if (debugMode) std::cout << "insideFace called for point not on plane" << std::endl; + return false; + } + + GeoTrf::Vector3D probe = getRefPoint(i) - point ; + + // check span + if ( probe.norm() > getRefSpan() ) { + if (debugMode) std::cout << "insideFace called for point outside span" << std::endl; + return false; + } + + // vertex + for (auto vx : vtx) if ( (vx - point).norm()< precision ) { + if (debugMode) std::cout << "insideFace vertex hit" << std::endl; + return true; + } + + // save valid intersections in order to handle case when probe hits a vertex ( double intersection ) + std::vector<GeoTrf::Vector3D> valid_intersections; + + if ( probe.norm() < precision) return true; // on edge + + bool in = false; + double d,dist,t; + + if (debugMode) { + std::cout << "insideFace:" << i <<":test point:" << point.x() <<"," << point.y() <<"," << point.z() << std::endl; + std::cout << "insideFace:probe:" << probe.x() <<"," << probe.y() <<"," << probe.z() <<":norm:" << probe.norm()<< std::endl; + for ( auto iv : vtx ) std::cout << "face vertices:" << iv.x() <<"," << iv.y() <<"," << iv.z() << std::endl; + } + + for (unsigned int iv = 0; iv < vtx.size(); iv++) { + + GeoTrf::Vector3D edge = (iv<vtx.size()-1) ? vtx[iv+1] - vtx[iv] : vtx[0] - vtx[iv]; + double ee = edge.norm(); + if (debugMode) std::cout << "insideFace:edge:" << edge.x() <<"," << edge.y() <<"," << edge.z() <<":norm:" << ee<< std::endl; + + // dist and t in units (probe, edge) + bool lineIntersect = intersectLines( point, probe, vtx[iv], edge, dist, t, d); + if (!lineIntersect && std::abs(d) > precision ) continue; + + if (!lineIntersect) { // point on edge line + double td = ( point - vtx[iv] ).dot(edge)/ee/ee; + if (td*ee > -precision && (td-1.)*ee < precision) return true; // does not count as intersection otherwise + } + if (!lineIntersect) continue; + // + if (debugMode) std::cout << "insideFace:intersecting line:" <<iv <<":d:"<< d << ":dist:" << dist <<":t:" << t << std::endl; + if ( std::abs(d)< 1.e-3 && t*ee >- precision && (t-1.)*ee < precision ) { // TODO calculate error on d to remove hardcoded tolerance + // + if (fabs(dist*probe.norm()) < precision ) return true; + // check if not found already + GeoTrf::Vector3D xn = vtx[iv]+t*edge; + bool known = false; for ( auto x : valid_intersections ) if ( (xn -x).norm() < precision ) known = true; + if (!known) { + in ^= dist>0; + valid_intersections.push_back(xn); + if (debugMode) std::cout <<"insideFace:collecting valid intersection:" << xn.x() <<"," << xn.y() <<"," << xn.z()<< ":insideFace current:" << in << std::endl; + } + if ( (xn-point).norm() < precision ) return true; // point on edge + } + + } + + if (debugMode) std::cout << "insideFace returns:" << in << std::endl; + + return in; + +} + +void GeoPolyhedronGeneric::exec(GeoShapeAction *action) const +{ + action->handleShape(this); +} + + +bool GeoPolyhedronGeneric::intersectLines(GeoTrf::Vector3D A, GeoTrf::Vector3D a, GeoTrf::Vector3D B, GeoTrf::Vector3D b, double& da, double& db, double& dx) const +{ + // lines parametrization : A + a*da , B + b*db ; a,b define "units" of distances da, db + // return value : closest distance between lines in native [mm] units + + // scalars + double ab = a.dot(b); double a2 = a.dot(a); double b2 = b.dot(b); + double ABa = (A-B).dot(a); + double ABb = (A-B).dot(b); + + + // special cases + if ( std::abs(ab)/sqrt(a2*b2)<1.e-9 ) { // to ensure sufficient precision + da = -ABa / a2; + db = ABb / b2; + dx = (A + da*a -B - db*b).norm(); + return true; + } + + if ( a2*b2 == ab*ab ) { // parallel lines + da = 0; db = 0; + dx = ((A-B).cross(b)).norm()/b.norm(); + return false; + } + + da = (ABb*ab -b2*ABa) / (a2*b2 - ab*ab); + db = (ABa + a2*da) / ab; + dx = (A + da*a -B - db*b).norm(); + return true; + +} + +// this is obsolete +std::vector< std::pair< double, std::pair<size_t,size_t> > > GeoPolyhedronGeneric::edgeFaceIntersect( int i, GeoTrf::Vector3D eInit, GeoTrf::Vector3D edge ) const +{ + std::vector< std::pair< double, std::pair<size_t,size_t> > > output; + + std::vector<GeoTrf::Vector3D> vtx; + std::vector<size_t> face= getFace(i); + for (auto vi : face ) vtx.push_back(m_Vertices[vi]); + + double d,dist,t; + for (unsigned int iv = 0; iv < vtx.size(); iv++) { + + std::pair<size_t,size_t> fvx(face[iv], (iv<face.size()-1) ? face[iv+1] : face[0]); + + GeoTrf::Vector3D fi = (iv<vtx.size()-1) ? vtx[iv+1] - vtx[iv] : vtx[0] - vtx[iv]; + + // dist and t in units (probe, edge) + bool lineIntersect = intersectLines( vtx[iv], fi, eInit, edge, dist, t, d); + + if (lineIntersect) { + if ( std::abs(d)<1.e-6 && dist>=0 && dist <= 1 && t>=0. && t<=1.) { + std::pair< double, std::pair<size_t,size_t> > ix(t, fvx); + output.push_back(ix); + } + } + } + return output; +} + + +bool GeoPolyhedronGeneric::isValid( bool debugMode ) const +{ + if (getNFaces()<4) return false; + + // loop over edges, check that each appears with its mirrored version ( attached faces ) + for ( unsigned int ic=0; ic < getNFaces(); ic++) { + std::vector<size_t> vtx = getFace(ic); + if (vtx.size()<2) continue; // should not happen though + for (unsigned int iv=0; iv<vtx.size(); iv++) { + std::pair<size_t,size_t> edge(vtx[iv], iv<vtx.size()-1 ? vtx[iv+1] : vtx[0]); // probe + bool mirrored = false; // search mirrored edge + for ( unsigned int icm=0; icm < getNFaces(); icm++) { + std::vector<size_t> vtm = getFace(icm); + if (vtm.size()<2) continue; // should not happen though + for (unsigned int ivm=0; ivm<vtm.size(); ivm++) { + std::pair<size_t,size_t> edgm(vtm[ivm], ivm<vtm.size()-1 ? vtm[ivm+1] : vtm[0]); // test + if ( edgm.first == edge.second && edgm.second == edge.first) mirrored = true; + if (mirrored) break; + } // end loop over other edges + if (mirrored) break; + } // end loop over other faces + if (!mirrored) { + if (debugMode) std::cout << "problem in polyhedron, edge not mirrored:face:edge:" << ic<<":" <<iv << std::endl; + return false; + } + } + } + + return true; +} + +void GeoPolyhedronGeneric::fillSpans() { + + // calculate reference points and span for volumes + m_cog = GeoTrf::Vector3D(0.,0.,0.); m_span = 0.; + if (m_Vertices.size()) { + for ( auto vx : m_Vertices ) m_cog = m_cog + vx; m_cog = 1./m_Vertices.size()*m_cog; + for ( auto vx : m_Vertices ) m_span = std::max( m_span, (vx-m_cog).norm() ); + } + // and faces + m_face_cog.clear(); m_face_span.clear(); + for ( auto fi : m_Faces ) { GeoTrf::Vector3D fc(0.,0.,0.); double fs=0.; + if (fi.size()) { + for ( auto vi : fi ) fc = fc + m_Vertices[vi]; fc = 1./fi.size() * fc; + for ( auto vi : fi) fs = std::max( fs, (m_Vertices[vi] - fc).norm() ); + } + m_face_cog.push_back(fc); m_face_span.push_back(fs); + } +} + +GeoTrf::Vector3D GeoPolyhedronGeneric::getRefPoint(int face) const { + + // first check that info available + if ( m_face_cog.size() < m_Faces.size() ) { + std::cout << "WARNING: GeoPolyhedronGeneric object not constructed: run ::construct() first"<< std::endl; + return GeoTrf::Vector3D(1.e5,1.e5,1.e5); // dummy return value + } + + if (face<0) return m_cog; + else if ( face < m_face_cog.size() ) return m_face_cog[face]; + else return GeoTrf::Vector3D(1.e5,1.e5,1.e5); // return dummy + +} + +double GeoPolyhedronGeneric::getRefSpan(int face) const { + + // first check that info available + if ( m_face_span.size() < m_Faces.size() ) { + std::cout << "WARNING: GeoPolyhedronGeneric object not constructed: run ::construct() first"<< std::endl; + return 1.e5; // dummy return value + } + + if (face<0) return m_span; + else if ( face < m_face_span.size() ) return m_face_span[face]; + else return 1.e5; // return dummy + +} + +int GeoPolyhedronGeneric::getOrigin() const { return m_origin; } + +void GeoPolyhedronGeneric::setOrigin( int ori ) { m_origin = ori; } + +void GeoPolyhedronGeneric::cleanAfterBooleanOperation() { + + // remove dummy faces ( less than 3 vertices ) + std::vector<std::vector<size_t> > nfaces; + std::vector< GeoTrf::Vector3D > normals; + for (unsigned ip = 0; ip < m_Faces.size(); ip++) { + if (m_Faces[ip].size()>2) { + nfaces.push_back(m_Faces[ip]); + normals.push_back(m_Normals[ip]); + } + } + + // removing unused vertices + std::vector<GeoTrf::Vector3D> nvtx; + std::map<size_t,size_t> m; + for (unsigned ip = 0; ip < nfaces.size(); ip++) { + for (unsigned iv = 0; iv < nfaces[ip].size(); iv++ ) { + auto const& pair = m.try_emplace(nfaces[ip][iv], nvtx.size()); + if (pair.second) nvtx.push_back(m_Vertices[nfaces[ip][iv]]); + nfaces[ip][iv] = (*pair.first).second; + } + } + + m_Vertices = nvtx; + m_Faces = nfaces; + m_Normals = normals; + + fillSpans(); +} + +bool GeoPolyhedronGeneric::construct(bool debugMode) { + + fillSpans(); + return isValid(debugMode); + +} diff --git a/GeoModelCore/GeoModelKernel/src/GeoPolyhedronManipulator.cxx b/GeoModelCore/GeoModelKernel/src/GeoPolyhedronManipulator.cxx new file mode 100644 index 0000000000000000000000000000000000000000..b1632ff9d48df1bb948d1a2579db2b30203a5792 --- /dev/null +++ b/GeoModelCore/GeoModelKernel/src/GeoPolyhedronManipulator.cxx @@ -0,0 +1,1564 @@ +/* + Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration +*/ + +#include "GeoModelKernel/GeoPolyhedronManipulator.h" +#include "GeoModelKernel/GeoShape.h" +#include "GeoModelKernel/GeoShapeIntersection.h" +#include "GeoModelKernel/GeoShapeUnion.h" +#include "GeoModelKernel/GeoShapeSubtraction.h" +#include "GeoModelKernel/GeoBox.h" +#include <cmath> +#include <cstdint> +#include <iostream> +#include <utility> + +GeoPolyhedronGeneric* GeoPolyhedronManipulator::intersection( const GeoPolyhedronGeneric* phA, const GeoPolyhedronGeneric* phB, bool debugMode) +{ return resolveBoolean( phA, phB, 0, debugMode); } + +GeoPolyhedronGeneric* GeoPolyhedronManipulator::subtraction( const GeoPolyhedronGeneric* phA, const GeoPolyhedronGeneric* phB, bool debugMode) +{ return resolveBoolean( phA, phB, 1, debugMode); } + +GeoPolyhedronGeneric* GeoPolyhedronManipulator::uni( const GeoPolyhedronGeneric* phA, const GeoPolyhedronGeneric* phB, bool debugMode) +{ return resolveBoolean( phA, phB, 2, debugMode); } + +GeoPolyhedronGeneric* GeoPolyhedronManipulator::resolveBoolean(const GeoPolyhedronGeneric* phA, const GeoPolyhedronGeneric* phB, int modeBoolean, bool debugMode) { + // modeBoolean : 0 intersection (A and B): shared(A,B) non-overlapping + shared A overlapping + // 1 subtraction (A and not B): non-shared(A) + inverted shared (B) + // 2 union (A or B) : non-shared(A,B) + shared A overlapping + + // check inputs + if ( !phA->isValid() || !phB->isValid()) { + return nullptr; + } else { + m_phA = phA; m_phB = phB; + } + // collect all planes / vertices + size_t nVA = phA->getNVertices(); size_t nPA = phA->getNFaces(); + std::vector<GeoTrf::Vector3D> vtx; std::vector< NPH_vertex > vlog; std::vector< NPH_plane > planes; + for (unsigned int iv=0; iv < nVA; iv++) { + vtx.push_back( phA->getVertex(iv) ); + NPH_vertex phv; phv.index=vlog.size(); phv.index_ph=0; phv.shared = phB->contains( vtx.back(), 1.e-3 ); vlog.push_back( phv ); + } + for (unsigned int ip=0; ip < phA->getNFaces(); ip++) { + planes.push_back(NPH_plane(0)); + planes.back().vertices = phA->getFace(ip); + planes.back().normal = phA->faceNormal(ip); + if (debugMode) std::cout <<"phA:face:" << ip <<":vtx:"; for ( auto jv : planes.back().vertices ) std::cout << jv<<","; std::cout <<"" << std::endl; + } + // phB + overlapping vertices + overlapping planes + std::vector<std::pair<int,int>> overlapping_vertices; + for (unsigned int iv=0; iv < phB->getNVertices(); iv++) { + GeoTrf::Vector3D new_vtx = phB->getVertex(iv); + for (unsigned int jv=0; jv<vtx.size(); jv++) if ( (vtx[jv]-new_vtx).norm() < m_tolerance ) overlapping_vertices.push_back(std::pair<int,int>(iv+nVA,jv)); + vtx.push_back( new_vtx ); + NPH_vertex phv; phv.index = vlog.size(); phv.index_ph = 1; phv.shared = phA->contains( vtx.back(), 1.e-3 ); vlog.push_back( phv ); + } + // + for (unsigned int ip=0; ip < phB->getNFaces(); ip++) { + planes.push_back(NPH_plane(1)); + std::vector<size_t> vtx_ph = phB->getFace(ip); // auxiliary + for (auto vh : vtx_ph ) { + int overlap = -1; for (auto ov : overlapping_vertices) if ( ov.first == vh+nVA ) overlap = ov.second; + planes.back().vertices.push_back( overlap>=0 ? overlap : vh+nVA); + } + planes.back().normal = phB->faceNormal(ip); + if (debugMode) std::cout <<"phB:face:" << ip <<":vtx:"; for ( auto jv : planes.back().vertices ) std::cout << jv<<","; std::cout <<"" << std::endl; + // overlapping planes + for (unsigned int ipA=0; ipA < nPA; ipA++) { NPH_plane plA = planes[ipA]; + double nn = plA.normal.dot(phB->faceNormal(ip)); + if (std::abs(std::abs( nn ) - 1.) < m_tolerance ) { + if ( std::abs( (vtx[plA.vertices[0]]-vtx[planes.back().vertices[0]]).dot(plA.normal)) < m_tolerance ) { + planes[ipA].overlapping.push_back( nn>0 ? nPA+ip : nPA+ip+1000); + planes[nPA+ip].overlapping.push_back( nn>0 ? ipA : ipA+1000); + } + } + } + } + + if (debugMode) { + for (unsigned int iv=0; iv<vlog.size(); iv++) if (vlog[iv].shared) + std::cout << "original shared vertices:" << iv <<":origin:" << vlog[iv].index_ph << std::endl; + } + + // collect edges + std::vector<NPH_edge> edges = collectEdges(planes, vlog, debugMode); + + // collect new vertices ( edge-plane intersections ) + collectLines(planes, vtx, vlog, edges, debugMode); + + // ===================== loop over faces ================================================== + + bool facesOK = true; unsigned int fail = 0; + for (unsigned int ip = 0; ip< planes.size(); ip++) { + NPH_plane plane = planes[ip]; + // collect shared vertices + unsigned int ns = 0; for (auto iv : plane.vertices ) { + if (vlog[iv].shared) { + bool foundVtx = false; for ( auto ihv : plane.hit_vertices ) if (ihv.index == iv) {foundVtx = true; break;} + if (!foundVtx) { NPL_vertex plv = filterLog(vlog[iv],edges, ip, planes); + planes[ip].hit_vertices.push_back(plv); + } + } + } + if (debugMode) { + std::cout << "processing plane:" << ip <<":origin:" << plane.index_ph <<":overlapping planes:"; + for (auto op : plane.overlapping) std::cout << op <<","; std::cout <<" " << std::endl; + } + for ( auto ov : plane.overlapping ) { // add shared vertices TO DO : this loop seem obsolete / superflu + for ( auto iv : planes[ov%1000].vertices ) { if (!vlog[iv].shared) continue; + if (!insideFace( ip, vtx[iv]) ) continue; + bool foundVtx = false; for ( auto ihv : plane.hit_vertices ) if (ihv.index == iv) {foundVtx = true; break;} + if (!foundVtx) { NPL_vertex plv = filterLog(vlog[iv],edges, ov%1000, planes); + planes[ip].hit_vertices.push_back(plv); + } + } + } + + plane = planes[ip]; + if (debugMode) std::cout << "number of shared vertices:" << plane.hit_vertices.size() << std::endl; + + // =================== all vertices collected ========================================== + + // run first loop over edges to (1) collect endpoint basic info (2) mark "onEdge" ( on "native" edge) vertices + // plane edges + std::vector<NPL_edge> plane_edges; + for (unsigned int ie = 0; ie<plane.edges.size(); ie++) { + NPH_edge edge = edges[plane.edges[ie]]; + int ihit = -1; int ihit_start = -1; double dist = 0.; + int vin = edge.endpoints.first; int fin = plane.vlog_ori[ie].faces.first; + if (vlog[vin].shared) { + for (unsigned int ivl = 0; ivl < planes[ip].hit_vertices.size(); ivl++) { + if ( vin == plane.hit_vertices[ivl].index ) { planes[ip].hit_vertices[ivl].onEdge = ie; break; } + } + } + for (unsigned int iv = 0; iv < edge.evtx.size() ; iv++) { + // mark vertex on edge + int ivx_hit = -1; + for (unsigned int ivl = 0; ivl < planes[ip].hit_vertices.size(); ivl++) { + if ( edge.evtx[iv].index == planes[ip].hit_vertices[ivl].index ) { planes[ip].hit_vertices[ivl].onEdge = ie; ivx_hit = ivl; break; } + } + // collect edge piece + NPL_edge ple( std::pair<int,int>(vin,edge.evtx[iv].index),edge.faces.second); ple.generating_edge = plane.edges[ie]; + // check mid-edge before shared classification + if (vlog[ple.endpoints.first].shared && vlog[ple.endpoints.second].shared ) { + GeoTrf::Vector3D vxmid = 0.5*(vtx[ple.endpoints.first] + vtx[ple.endpoints.second]); + if (phA->contains(vxmid,1.e-3) && phB->contains(vxmid,1.e-3) ) ple.shared = true; + if (!ple.shared ) { + if (debugMode) { std::cout <<"mid-edge point cancels shared edge:"<< ple.endpoints.first<<"," << ple.endpoints.second << std::endl; + if (!phA->contains(vxmid,1.e-3)) std::cout << "repeat in debug mode A:" << phA->contains(vxmid,1.e-3,true) << std::endl; + if (!phB->contains(vxmid,1.e-3)) std::cout << "repeat in debug mode B:" << phB->contains(vxmid,1.e-3,true) << std::endl; + } + } + } + if (ivx_hit>-1) ple.endpoint_hitvx.second = ivx_hit; + plane_edges.push_back(ple); + // prepare next piece + vin = edge.evtx[iv].index; + } + // collect last + if (vin != edge.endpoints.second) { + NPL_edge ple( std::pair<int,int>(vin,edge.endpoints.second),edge.faces.second); + if (vlog[ple.endpoints.first].shared && vlog[ple.endpoints.second].shared ) { + GeoTrf::Vector3D vxmid = 0.5*(vtx[ple.endpoints.first] + vtx[ple.endpoints.second]); + if (plane.index_ph == 0 && phB->contains(vxmid,1.e-3) ) ple.shared = true; + else if (plane.index_ph == 1 && phA->contains(vxmid,1.e-3) ) ple.shared = true; + if (!ple.shared) { + if (debugMode) { + std::cout <<"mid-edge point cancels last shared edge:"<< ple.endpoints.first<<"," << ple.endpoints.second << std::endl; + if (!phA->contains(vxmid)) std::cout << "repeat in debug mode A:" << phA->contains(vxmid,1.e-3,true) << std::endl; + if (!phB->contains(vxmid)) std::cout << "repeat in debug mode B:" << phB->contains(vxmid,1.e-3,true) << std::endl; + } + } + + } + if ( vlog[ple.endpoints.second].shared ) { + int ivx_hit = -1; + for (unsigned int ivl = 0; ivl < plane.hit_vertices.size(); ivl++) { + if ( edge.endpoints.second == plane.hit_vertices[ivl].index ) { plane.hit_vertices[ivl].onEdge = ie; ivx_hit = ivl; break; } + } + ple.endpoint_hitvx.second = ivx_hit; + } + ple.generating_edge = plane.edges[ie]; + plane_edges.push_back(ple); + } + } + // loop over edges to identify vertices with protected line connection + for (unsigned int ie = 0; ie< plane_edges.size()-1; ie++) { + if ( plane_edges[ie].shared != plane_edges[ie+1].shared ) planes[ip].hit_vertices[plane_edges[ie].endpoint_hitvx.second].onEdgeProtected = true; + } + if ( plane_edges.back().shared != plane_edges.front().shared ) planes[ip].hit_vertices[plane_edges.back().endpoint_hitvx.second].onEdgeProtected = true; + // loop over edges to identify dummy overlaps + for (unsigned int ie = 0; ie< plane_edges.size()-1; ie++) { NPL_edge ple = plane_edges[ie]; + for ( auto ov : planes[ple.adjacent_face].overlapping ) { if ( ov < 1000 ) continue; + if ( insideFace( ov%1000, vtx[ple.endpoints.first]) && insideFace( ov%1000, vtx[ple.endpoints.second]) ) { + plane_edges[ie].dummy_overlap = ov; + } + } + } + // + // ======================== prepare active lines ( mixed edges ) ==================================== + + std::vector<NPL_edge> resolved_lines = evaluateVertexLogic(ip, vlog, edges, planes, vtx); + if (debugMode) { + std::cout << "resolved lines:" << resolved_lines.size() <<":"; for (auto il : resolved_lines) std::cout << il.adjacent_face <<","; + std::cout <<""<<std::endl; + } + plane = planes[ip]; + + // ====================== second loop over edges and lines ========================================= + + // resolve endpoint faces + unsigned int plane_edge_non_shared=0; + for (unsigned int ie = 0; ie<plane_edges.size(); ie++) { + NPH_edge edge = edges[plane_edges[ie].generating_edge]; + int ie_next = ie<plane_edges.size()-1 ? ie+1 : 0; + int ie_prev = ie>0 ? ie-1 : plane_edges.size()-1; + NPL_edge ple = plane_edges[ie]; + NPL_edge ple_next = plane_edges[ie_next]; + NPL_edge ple_prev = plane_edges[ie_prev]; + plane_edges[ie].endpoint_hitvx.first = ple_prev.endpoint_hitvx.second; + + if (!ple.shared && !ple_next.shared) { // non-shared edges + if (!vlog[ple.endpoints.second].shared) { + plane_edges[ie].endpoint_faces.second = ple_next.adjacent_face; + plane_edges[ie_next].endpoint_faces.first = ple.adjacent_face; + } else { + if (plane.hit_vertices[ple.endpoint_hitvx.second].active_lines.size() == 2) { + plane_edges[ie].endpoint_faces.second = ple_next.adjacent_face; + plane.hit_vertices[ple.endpoint_hitvx.second].faces.first = ple.adjacent_face; + plane.hit_vertices[ple.endpoint_hitvx.second].faces.second = ple_next.adjacent_face; + plane.hit_vertices[ple.endpoint_hitvx.second].faces_shared_loop.first = plane.hit_vertices[ple.endpoint_hitvx.second].active_lines[0]; + plane.hit_vertices[ple.endpoint_hitvx.second].faces_shared_loop.second = plane.hit_vertices[ple.endpoint_hitvx.second].active_lines[1]; + } else if (plane.hit_vertices[ple.endpoint_hitvx.second].active_lines.size() == 0) { + plane_edges[ie].endpoint_faces.second = ple_next.adjacent_face; + plane_edges[ie_next].endpoint_faces.first = ple.adjacent_face; + plane.hit_vertices[ple.endpoint_hitvx.second].faces.first = ple.adjacent_face; + plane.hit_vertices[ple.endpoint_hitvx.second].faces.second = ple_next.adjacent_face; + plane.hit_vertices[ple.endpoint_hitvx.second].faces_shared_loop.first = ple.adjacent_face; + plane.hit_vertices[ple.endpoint_hitvx.second].faces_shared_loop.second = ple_next.adjacent_face; + plane.hit_vertices[ple.endpoint_hitvx.second].oriented = true; + } + } + } else if (ple.shared) { + // single valid line + edge not in plane + std::vector<int> pruned_lines; bool edgeInPlane; + for ( auto ap : plane.hit_vertices[ple.endpoint_hitvx.second].active_lines ) { + edgeInPlane = false; for ( auto ep : edge.inPlane ) if (ap == ep) edgeInPlane = true; + if (!edgeInPlane && inPlane(vlog[ple_next.endpoints.second],edges,ap) ) edgeInPlane = true; + if (!edgeInPlane) pruned_lines.push_back(ap); + if (edgeInPlane && ( plane.hit_vertices[ple.endpoint_hitvx.first].onEdgeProtected || plane.hit_vertices[ple.endpoint_hitvx.second].onEdgeProtected) ) + std::cout << "pruning removed protected line:" << ap <<":endpoints:" << ple.endpoints.first <<"," << ple.endpoints.second << std::endl; + } + int nlines = pruned_lines.size(); + if ( nlines == 1 ) { + int al = pruned_lines[0]; + bool activeLine = false; for ( auto jl : resolved_lines ) if ( jl.adjacent_face == al ) activeLine = true; + + if (activeLine) { + plane.hit_vertices[ple.endpoint_hitvx.second].faces.first = ple.adjacent_face ; + plane.hit_vertices[ple.endpoint_hitvx.second].faces.second = al; + plane.hit_vertices[ple.endpoint_hitvx.second].oriented = true; + plane.hit_vertices[ple.endpoint_hitvx.second].faces_shared_loop.first = ple.adjacent_face; + plane.hit_vertices[ple.endpoint_hitvx.second].faces_shared_loop.second = al; + plane_edges[ie].endpoint_faces.second = al; + plane_edges[ie_next].endpoint_faces.first = al; + for (unsigned int ia=0; ia<resolved_lines.size(); ia++) {if (resolved_lines[ia].adjacent_face!=al) continue; + if (!ple.shared) resolved_lines[ia].endpoint_hitvx.second = ple.endpoint_hitvx.second; + else resolved_lines[ia].endpoint_hitvx.first = ple.endpoint_hitvx.second; + } + } + } else if (nlines==0) { + plane_edges[ie].endpoint_faces.second = ple_next.adjacent_face; + plane_edges[ie_next].endpoint_faces.first = ple.adjacent_face; + plane.hit_vertices[ple.endpoint_hitvx.second].faces.first = ple.adjacent_face ; + plane.hit_vertices[ple.endpoint_hitvx.second].faces.second = ple_next.adjacent_face; + } else { // implies shared vertex adjacent to double line + // there will be an ( unoriented back and forth loop ) + two active overlapping lines + // initial choice random but needs to be consistent for endpoints + NPL_edge l0; for (auto il : resolved_lines) if ( pruned_lines[0] == il.adjacent_face ) l0 = il; + NPL_edge l1; for (auto il : resolved_lines) if ( pruned_lines[1] == il.adjacent_face ) l1 = il; + if (l0.endpoints.first == l1.endpoints.first && l0.endpoints.second == l1.endpoints.second ) { + l1.endpoints.first = l0.endpoints.second; l1.endpoints.second = l0.endpoints.first; + if (debugMode) std::cout << "double line, hit vertices?" << l0.endpoint_hitvx.first << "," << l0.endpoint_hitvx.second << ":" << + l1.endpoint_hitvx.first << "," << l1.endpoint_hitvx.second<<std::endl; + } + // anticipate link to sym lines + if ( plane_edges[ie].endpoints.second == l0.endpoints.first ) { + plane_edges[ie].endpoint_faces.second = pruned_lines[1]; + plane_edges[ie_next].endpoint_faces.first = pruned_lines[0]; + } else { + plane_edges[ie].endpoint_faces.second = pruned_lines[0]; + plane_edges[ie_next].endpoint_faces.first = pruned_lines[1]; + } + plane.hit_vertices[ple.endpoint_hitvx.second].faces.first = ple.adjacent_face ; + plane.hit_vertices[ple.endpoint_hitvx.second].faces.second = plane_edges[ie].endpoint_faces.second; + plane.hit_vertices[ple_next.endpoint_hitvx.second].faces.second = ple_next.adjacent_face ; + plane.hit_vertices[ple_next.endpoint_hitvx.second].faces.first = plane_edges[ie].endpoint_faces.second; + } + } // end non/shared vertices + } // end second loop over edges + + if (debugMode) { // print overview of shared edges + for (unsigned int ie = 0; ie<plane_edges.size(); ie++) { + if (!plane_edges[ie].shared ) continue; + NPL_edge ple = plane_edges[ie]; + std::cout <<"edge resolved:" << ple.endpoints.first <<":" << ple.endpoints.second<<"("<<ple.endpoint_faces.first <<"," << ple.adjacent_face + <<"," << ple.endpoint_faces.second <<")"<<":faces:" + << plane.hit_vertices[ple.endpoint_hitvx.first].faces.second<< "," << plane.hit_vertices[ple.endpoint_hitvx.first].faces.first + << ":" << plane.hit_vertices[ple.endpoint_hitvx.second].faces.first <<"," + << plane.hit_vertices[ple.endpoint_hitvx.second].faces.second <<":faces shared loop:" + << plane.hit_vertices[ple.endpoint_hitvx.first].faces_shared_loop.second <<"," + << plane.hit_vertices[ple.endpoint_hitvx.first].faces_shared_loop.first <<":" + << plane.hit_vertices[ple.endpoint_hitvx.second].faces_shared_loop.first<<"," + << plane.hit_vertices[ple.endpoint_hitvx.second].faces_shared_loop.second << std::endl; + } + } + // ============== merge edges and lines ================================================ + // at this point, lines are not oriented and have not been symmetrized yet + // the ordering ensures oriented edges are recovered first + + // lines duplicating shared edges are maintained but pushed to the end of the buffer + + std::vector<NPL_edge> duplicated_edges; + + for ( unsigned int il=0; il<resolved_lines.size(); il++) { + // sanity check : copy problems TODO use map index(global)->hit_vx(in plane) + if (plane.hit_vertices[resolved_lines[il].endpoint_hitvx.first].index != resolved_lines[il].endpoints.first) { + for (unsigned int ih=0; ih<plane.hit_vertices.size(); ih++) { + if (plane.hit_vertices[ih].index == resolved_lines[il].endpoints.first ) { resolved_lines[il].endpoint_hitvx.first = ih; break; } + } + } + if (plane.hit_vertices[resolved_lines[il].endpoint_hitvx.second].index != resolved_lines[il].endpoints.second) { + for (unsigned int ih=0; ih<plane.hit_vertices.size(); ih++) { + if (plane.hit_vertices[ih].index == resolved_lines[il].endpoints.second ) { resolved_lines[il].endpoint_hitvx.second = ih; break; } + } + } + + // catch duplicated edges + bool foundDuplicate = false; int ei = resolved_lines[il].endpoints.first; int eo = resolved_lines[il].endpoints.second; + for (unsigned int ie=0; ie<plane_edges.size(); ie++) { + if ( ( ei == plane_edges[ie].endpoints.first && eo == plane_edges[ie].endpoints.second ) + || ( ei == plane_edges[ie].endpoints.second && eo == plane_edges[ie].endpoints.first ) ) { + foundDuplicate = true; // plane_edges[ie].shared = false; + } + if (foundDuplicate) break; + } + + if (!foundDuplicate || resolved_lines[il].protectedLine) plane_edges.push_back(resolved_lines[il]); + else duplicated_edges.push_back(resolved_lines[il]); + } + if (debugMode && duplicated_edges.size() ) std::cout << "number of duplicated edges:" << duplicated_edges.size() << std::endl; + + if ( plane.hit_vertices.size()>2 ) { + for (auto ld : duplicated_edges) { bool protectedLine = false; + if ( plane.hit_vertices[ld.endpoint_hitvx.first].onEdgeProtected && plane.hit_vertices[ld.endpoint_hitvx.first].active_lines.size()==1) + protectedLine = true; + if ( plane.hit_vertices[ld.endpoint_hitvx.second].onEdgeProtected && plane.hit_vertices[ld.endpoint_hitvx.second].active_lines.size()==1) + protectedLine = true; + if (protectedLine) { + if (debugMode) std::cout << "protected line added:" << ld.endpoints.first <<":" << ld.endpoints.second <<":active lines:" + << plane.hit_vertices[ld.endpoint_hitvx.first].active_lines.size()<<"," + << plane.hit_vertices[ld.endpoint_hitvx.second].active_lines.size() <<std::endl; + plane_edges.push_back(ld); + } + } + } + + if (debugMode) { + std::cout << "combined edges:" << plane_edges.size() <<":(lines)"<< resolved_lines.size() << std::endl; + for ( auto ie : plane_edges) std::cout << ie.endpoints.first <<":" << ie.endpoints.second <<":adj:" << ie.adjacent_face<<":gen.edge:" << ie.generating_edge <<":sym:" << ie.symmetrized <<":shared:" << ie.shared <<":hitvx:" << ie.endpoint_hitvx.first <<"," << ie.endpoint_hitvx.second <<":dummy overlap:" << ie.dummy_overlap << std::endl; + } + + // ============== shared loops : overlapping faces ========================================================= + + std::vector<NPL_ordered> shared_loops; + for ( auto ov : planes[ip].overlapping ) { + std::vector<int> ovtx; + for (unsigned int iv=0; iv<plane.hit_vertices.size(); iv++ ) { + if (debugMode) { + if ( !insideFace( ov%1000, vtx[plane.hit_vertices[iv].index]) && + inPlane(vlog[plane.hit_vertices[iv].index],edges,ov%1000) ) + std::cout << "fake overlap vtx?" << plane.hit_vertices[iv].index << std::endl; + } + + if ( insideFace( ov%1000, vtx[plane.hit_vertices[iv].index]) + || inPlane(vlog[plane.hit_vertices[iv].index],edges,ov%1000)) { + if (!ovtx.size() || plane.hit_vertices[iv].onEdge>=0 || plane.hit_vertices[ovtx.front()].oriented) ovtx.push_back(iv); + else if ( plane.hit_vertices[iv].oriented ) ovtx.insert(ovtx.begin(),iv); + else if ( plane.hit_vertices[iv].onEdge>=0 ) ovtx.insert(ovtx.begin(),iv); + else ovtx.push_back(iv); + } + } + if (ovtx.size()<2) continue; + + int nedge = 0; std::vector<int> adjDiff; std::vector<int> edgeSelect; + for (unsigned int ie=0; ie<plane_edges.size(); ie++ ) + if ( (insideFace( ov%1000, vtx[plane_edges[ie].endpoints.first]) || inPlane(vlog[plane_edges[ie].endpoints.first],edges,ov%1000)) && + (insideFace( ov%1000, vtx[plane_edges[ie].endpoints.second]) || inPlane(vlog[plane_edges[ie].endpoints.second],edges,ov%1000) ) + && !plane_edges[ie].used) { + GeoTrf::Vector3D vxmid = 0.5*(vtx[plane_edges[ie].endpoints.first] + vtx[plane_edges[ie].endpoints.second]); + bool midIn = insideFace( ov%1000, vxmid); + if (midIn) { + nedge++; plane_edges[ie].overlap = ov; + if (!adjDiff.size()) adjDiff.push_back(plane_edges[ie].adjacent_face); + else { + bool adjFound = false; for (auto ia : adjDiff) if (ia == plane_edges[ie].adjacent_face ) adjFound= true; + if (!adjFound ) adjDiff.push_back(plane_edges[ie].adjacent_face); + } + edgeSelect.push_back(ie); + } + } + + if ( nedge<2 ) continue; + if ( adjDiff.size()<3 ) continue; // loop needs at least 3 different edges + + NPL_ordered loop; loop.edges.push_back(edgeSelect[0]); plane_edges[edgeSelect[0]].used = true; + loop.vtx.push_back(plane_edges[edgeSelect[0]].endpoints.first); + loop.vtx.push_back(plane_edges[edgeSelect[0]].endpoints.second); + loop.shared=true; loop.top_overlap=ov; loop.oriented = plane_edges[edgeSelect[0]].generating_edge>=0 ? true : false; + std::vector<int>::iterator it = edgeSelect.begin(); + while ( loop.edges.size() < ovtx.size() && loop.vtx.front() != loop.vtx.back() && it != edgeSelect.end()) { + if (!plane_edges[*it].used && plane_edges[*it].endpoints.first == loop.vtx.back()) { + loop.edges.push_back(*it); loop.vtx.push_back(plane_edges[*it].endpoints.second); + plane_edges[*it].used = true; it = edgeSelect.begin(); + } else if (!plane_edges[*it].used && plane_edges[*it].generating_edge<0 && plane_edges[*it].symmetrized<0 + && plane_edges[*it].endpoints.second == loop.vtx.back()) { // need to symmetrize the line now + NPL_edge symLine = symmetrize(plane_edges[*it]); symLine.used=true; plane_edges[*it].symmetrized = plane_edges.size(); symLine.symmetrized=(*it); + (*it) = plane_edges.size(); plane_edges.push_back(symLine); + loop.edges.push_back(*it); loop.vtx.push_back(symLine.endpoints.second); it = edgeSelect.begin(); + } else it++; + } + + if (debugMode) { + std::cout << "overlapping shared loop done:" << loop.vtx.size() << ":" << loop.edges.size() <<std::endl; + for ( auto ie : loop.edges) std::cout << "loop content:" << plane_edges[ie].endpoints.first <<"," << plane_edges[ie].endpoints.second <<":" << + plane_edges[ie].used << std::endl; + } + + shared_loops.push_back(loop); + + } + + // =================== shared loops : base =========================================== + // select shared non-overlapping first, then add all unused shared + + int nedge = 0; std::vector<int> edgeSelect; + for (unsigned int ie=0; ie<plane_edges.size(); ie++ ) { + if ( !plane_edges[ie].used && plane_edges[ie].shared && plane_edges[ie].overlap<0 ) { + edgeSelect.push_back(ie); nedge++; + } + } + + if (nedge>1) { + // add overlaps: symmetrize if not done already + for (unsigned int ie=0; ie<plane_edges.size(); ie++ ) { + if (plane_edges[ie].shared && plane_edges[ie].generating_edge<0 && plane_edges[ie].overlap>=0 && plane_edges[ie].symmetrized<0 ) { + NPL_edge symLine = symmetrize(plane_edges[ie]); plane_edges[ie].symmetrized=plane_edges.size(); symLine.symmetrized = ie; + plane_edges.push_back(symLine); + } + } + for (unsigned int ie=0; ie<plane_edges.size(); ie++ ) { + if (!plane_edges[ie].used && plane_edges[ie].shared && plane_edges[ie].overlap>=0 ) { + edgeSelect.push_back(ie); nedge++; + } + } + + if (edgeSelect.size()>1) { + std::vector<int>::iterator it = edgeSelect.begin(); + NPL_ordered loop; loop.edges.push_back(*it); plane_edges[*it].used = true; + loop.vtx.push_back(plane_edges[*it].endpoints.first); + loop.vtx.push_back(plane_edges[*it].endpoints.second); + loop.shared=true; loop.oriented = plane_edges[*it].generating_edge>=0 ? true : false; // after including check on dummy shared edges, oriented edges may appear in the middle of the loop + it = edgeSelect.begin(); + while ( loop.vtx.front() != loop.vtx.back() && it != edgeSelect.end()) { + if ( loop.edges.size() && plane_edges[loop.edges.back()].symmetrized == (*it) ) { + it++; + } else if (!plane_edges[*it].used && plane_edges[*it].endpoints.first == loop.vtx.back() ) { + // + loop.edges.push_back(*it); loop.vtx.push_back(plane_edges[*it].endpoints.second); + plane_edges[*it].used = true; it = edgeSelect.begin(); // std::cout << ":adding:" << (*it) << std::endl; + + } else if (!plane_edges[*it].used && plane_edges[*it].generating_edge<0 && plane_edges[*it].symmetrized<0 + && plane_edges[*it].endpoints.second == loop.vtx.back()) { // need to symmetrize the line now + NPL_edge symLine = symmetrize(plane_edges[*it]); plane_edges[*it].symmetrized=plane_edges.size(); symLine.used=true; symLine.symmetrized = (*it); + (*it) = plane_edges.size(); plane_edges.push_back(symLine); // std::cout << ":adding symmetrized:" << (*it) << std::endl; + loop.edges.push_back(*it); loop.vtx.push_back(symLine.endpoints.second); it = edgeSelect.begin(); + + } else it++; + } + + if (loop.vtx.front() != loop.vtx.back() || loop.edges.size()<2 ) { + for ( auto ie : loop.edges ) plane_edges[ie].used = false; // undo loop if not closed + } else + shared_loops.push_back(loop); + + int nseed = 0; // keep trace of non-symmetrized unused shared edges + for (auto ie : edgeSelect ) if (!plane_edges[ie].used) { + if (plane_edges[ie].symmetrized<0 ) nseed++; + else if (!plane_edges[plane_edges[ie].symmetrized].used) nseed++; + } + + if (debugMode) { + std::cout << "base shared loop:selected:" << edgeSelect.size() <<":loop size:vtx:"<< loop.vtx.size() + <<":loop size:edge:" << loop.edges.size() << std::endl; + if (nseed>0) { + std::cout <<"WARNING:remaining seeds:" << nseed << std::endl; + for (auto iv : planes[ip].vertices) std::cout << "vtx ori:" <<iv<<":" << vtx[iv].x() <<"," << vtx[iv].y() <<"," << vtx[iv].z() << std::endl; + for (auto iv : planes[ip].hit_vertices) std::cout << "vtx hit:" <<iv.index<<":" << vtx[iv.index].x() <<"," << vtx[iv.index].y() <<"," << vtx[iv.index].z() << std::endl; + } + } + } + } + + //================ symmetrized remaining active lines ===================================== + + for (unsigned int ie=0; ie<plane_edges.size(); ie++ ) { + if (plane_edges[ie].shared && plane_edges[ie].generating_edge<0 && plane_edges[ie].symmetrized < 0 ) { + NPL_edge symLine = symmetrize(plane_edges[ie]); plane_edges[ie].symmetrized=plane_edges.size(); symLine.symmetrized = ie; + plane_edges.push_back(symLine); + } + } + + //============== symmetrized inverted non-oriented shared loops ( -> non-shared ) ========== + //============== orientation resolved with help of area calculation ======================== + + for (unsigned int il = 0; il< shared_loops.size(); il++) shared_loops[il].vtx.pop_back(); + std::vector< NPL_ordered> shared_inverted_subface; + + for (auto il : shared_loops) { + if ( il.edges.size()>2 ) { + if (il.oriented) planes[ip].subfaces.push_back(il); + else { // prepare inverted loop + NPL_ordered loopi; loopi.vtx.insert(loopi.vtx.begin(),il.vtx.rbegin(),il.vtx.rend()); + for (unsigned int iv = 0; iv<loopi.vtx.size(); iv++) { + int ivn = iv<loopi.vtx.size()-1 ? iv+1 : 0; + for (unsigned int ie=0; ie<plane_edges.size(); ie++ ) { if (plane_edges[ie].used) continue; + if (plane_edges[ie].endpoints.first == loopi.vtx[iv] && plane_edges[ie].endpoints.second == loopi.vtx[ivn]) { + loopi.edges.push_back(ie); plane_edges[ie].used = true; break; + } + } + } + loopi.top_overlap = il.top_overlap; + loopi.shared = il.shared; + // resolve orientation + if ( areaVec(il.vtx,vtx).dot(plane.normal)>=0 ) { + il.oriented = true; loopi.oriented = true; + planes[ip].subfaces.push_back(il); + shared_inverted_subface.push_back(loopi); + } else { + il.oriented = true; loopi.oriented = true; + planes[ip].subfaces.push_back(loopi); + shared_inverted_subface.push_back(il); + } + } // end orientation + } // valid shared loops + } // shared loops + + + //=============== non-shared loop ========================================================= + // vertices may be unshared ( no endpoint_hitvx info ) + std::vector<NPL_ordered> non_shared_loops; + + nedge = 0; std::vector<int> edgeNS; int istart =-1; + for (unsigned int ie=0; ie<plane_edges.size(); ie++ ) { + if ( !plane_edges[ie].used ) { + edgeNS.push_back(ie); nedge++; + if (istart<0) istart = edgeNS.size()-1; + } + } + + if (debugMode) { + std::cout <<"non-shared loop(s):" <<edgeNS.size() <<":" << istart << std::endl; + for ( auto ie : edgeNS) std::cout << plane_edges[ie].endpoints.first <<":" << plane_edges[ie].endpoints.second <<":adj:" << plane_edges[ie].adjacent_face<<":gen.edge:" << plane_edges[ie].generating_edge <<":sym:" << plane_edges[ie].symmetrized << std::endl; + } + + int istart_last=istart; int nfail = 0; + + while ( nedge > 1 && istart>=0 ) { + + NPL_ordered loop; loop.edges.push_back(edgeNS[istart]); plane_edges[edgeNS[istart]].used = true; + loop.vtx.push_back(plane_edges[edgeNS[istart]].endpoints.first); + loop.vtx.push_back(plane_edges[edgeNS[istart]].endpoints.second); + loop.oriented = plane_edges[edgeNS[istart]].generating_edge>=0 ? true : false; + std::vector<int>::iterator it = edgeNS.begin(); + while ( loop.vtx.front() != loop.vtx.back() && it != edgeNS.end()) { + if ( loop.edges.size() && plane_edges[loop.edges.back()].symmetrized == (*it) ) { + it++; + } else if (!plane_edges[*it].used && plane_edges[*it].endpoints.first == loop.vtx.back() ) { + loop.edges.push_back(*it); loop.vtx.push_back(plane_edges[*it].endpoints.second); + plane_edges[*it].used = true; it = edgeNS.begin(); + } else it++; + } + + istart_last = istart; + istart = -1; for (unsigned int ie=0; ie<edgeNS.size(); ie++ ) if (!plane_edges[edgeNS[ie]].used) { istart=ie; break; } + if (debugMode) std::cout << "non-shared loop done:" << loop.edges.size() << ":start next:" << istart << std::endl; + + if (loop.vtx.front() != loop.vtx.back() ) { + // find alternative loop before releasing loop + if (debugMode) std::cout << "non-shared loop not closed, undo" << std::endl; + for ( auto ie : loop.edges ) plane_edges[ie].used = false; + if (debugMode) { + for ( auto ie : loop.edges) std::cout <<"failed loop:" << plane_edges[ie].endpoints.first <<":" << plane_edges[ie].endpoints.second <<":adj:" << plane_edges[ie].adjacent_face<<":gen.edge:" << plane_edges[ie].generating_edge <<":sym:" << plane_edges[ie].symmetrized << std::endl; + } + nfail++; if (istart == istart_last || nfail>10) break; + } else { + // shared loop left-over ? + bool nshared = false; for (auto iv : loop.vtx ) if (!vlog[iv].shared) nshared = true; + if (nshared) non_shared_loops.push_back(loop); + else { loop.shared = true; loop.vtx.pop_back(); shared_loops.push_back(loop); } + nedge -= loop.edges.size(); + } + } + + if (debugMode) { + std::cout << "non-shared loops:" << non_shared_loops.size() <<":remaining:"<< nedge << std::endl; + if (nedge>0 && plane.hit_vertices.size() > 2 ) { + std::cout <<"WARNING:edges left over:" << nedge <<":hit vertices:" << plane.hit_vertices.size() << std::endl; + for (auto ie : edgeNS) if (!plane_edges[ie].used) std::cout <<"not used:" << plane_edges[ie].endpoints.first <<"," + << plane_edges[ie].endpoints.second <<":shared:" + << plane_edges[ie].shared <<":gen.edge:" + << plane_edges[ie].generating_edge <<":adj:" + <<plane_edges[ie].adjacent_face << std::endl; + } + } + + // =============================== loop overview =============================================== + + for (unsigned int il = 0; il< non_shared_loops.size(); il++) non_shared_loops[il].vtx.pop_back(); + + //std::cout << "original face:" <<ip<<":areaVec.dot(normal):" << areaVec(plane.vertices,vtx).dot(plane.normal) << std::endl; + if (debugMode) { + std::cout <<"loop overview for face:" << ip << ":original vertices:"<< plane.vertices.size() << std::endl; + for (auto iv : plane.vertices) std::cout << iv <<"," ; std::cout <<"" << std::endl; + std::cout <<"loops:shared:" << shared_loops.size() <<":inverted:" << shared_inverted_subface.size() <<":non-shared:" << non_shared_loops.size() << std::endl; + } + + for (auto il : plane.subfaces) { + if (debugMode) { + std::cout <<"to face:" << ip <<":shared:" << il.shared <<":oriented:" << il.oriented <<":size:" << il.vtx.size() <<"," << il.edges.size() << std::endl; + for (auto iv : il.vtx) std::cout << iv <<"," ; std::cout <<"" << std::endl; + } + } + + /* this part removes the self-eliminating parts of loop, simplifying the description + for (auto il : shared_loops) { + if (debugMode) { + std::cout <<"to face:" << ip <<":shared:" << il.shared <<":oriented:" << il.oriented <<":overlap:" << il.top_overlap << ":size:" << il.vtx.size() <<"," << il.edges.size() << std::endl; + std::cout << "shared face area:" << areaVec(il.vtx,vtx).dot(plane.normal) <<":oriented:" << il.oriented << std::endl; + for (auto iv : il.vtx) std::cout << iv <<"," ; std::cout <<"" << std::endl; + for (auto ie : il.edges) std::cout << plane_edges[ie].dummy_overlap <<"," ; std::cout <<"" << std::endl; + } + if (il.vtx.size()>2) { + std::vector<size_t> vx_lobe; std::vector<int> ee_lobe; bool lobe = false; unsigned int vmax = il.vtx.size()-1; + for (unsigned int iv=0; iv< il.vtx.size(); iv++) { + if (iv>vmax) { + vx_lobe.push_back(il.vtx[iv]); + ee_lobe.push_back(il.edges[iv]); + continue; + } + unsigned int ivm2 = iv<il.vtx.size()-2 ? iv+2 : iv+2-il.vtx.size(); + unsigned int ivm1 = iv<il.vtx.size()-1 ? iv+1 : iv+1-il.vtx.size(); + unsigned int ivn1 = iv>0 ? iv-1 : il.vtx.size()-1; + if ( il.vtx[iv] == il.vtx[ivm2] && ( plane_edges[il.edges[iv]].dummy_overlap>=1000 || plane_edges[il.edges[ivm1]].dummy_overlap>=1000) ) { + if (debugMode) std::cout << "lobe found at:" << iv <<":" << il.vtx[iv] << std::endl; + lobe = true; iv++; + } else if (il.vtx[ivm1] == il.vtx[ivn1] && ( plane_edges[il.edges[ivn1]].dummy_overlap>=1000 || plane_edges[il.edges[iv]].dummy_overlap>=1000) ) { + lobe = true; iv++; vmax = il.vtx.size()-2; + } else { + vx_lobe.push_back(il.vtx[iv]); + ee_lobe.push_back(il.edges[iv]); + } + } + if (lobe) { il.vtx = vx_lobe; il.edges = ee_lobe; } + } + planes[ip].shared_subface.push_back(il); + } + */ + + if ( non_shared_loops.size() ) { + // merge inverted shared with non-shared loops + for (unsigned int il=0; il<shared_inverted_subface.size(); il++ ) { + non_shared_loops[0] = mergeLoops(non_shared_loops[0], shared_inverted_subface[il], vtx); + } + } + + for (auto il : non_shared_loops) { + if (debugMode) { + std::cout <<"to face:" << ip <<":shared:" << il.shared <<":oriented:" << il.oriented <<":size:" << il.vtx.size() <<"," << il.edges.size() << std::endl; + for (auto iv : il.vtx) std::cout << iv <<"," ; std::cout <<"" << std::endl; + } + planes[ip].subfaces.push_back(il); + } + + // save edges for further processing + planes[ip].plane_edges = plane_edges; + + /* + // sanity check : matching areas + double sa = 0.; + for ( auto il : planes[ip].subfaces ) { + sa += areaVec(il.vtx,vtx).dot(planes[ip].normal); + } + double aori = areaVec(planes[ip].vertices,vtx).dot(planes[ip].normal); + if ( std::abs(aori - sa) > 1 ) std::cout << "sanity check plane:area:"<< ip << ":" << sa << ":original:" << aori << std::endl; + */ + + } // end loop over planes + + GeoPolyhedronGeneric* gpg = nullptr; + + // construct intersection + GeoPolyhedronGeneric intersection; + intersection.addVertices(vtx); + for ( unsigned int ip=0; ip<planes.size(); ip++ ) { NPH_plane plane = planes[ip]; + for ( unsigned int il=0; il<plane.subfaces.size(); il++) { + if ( !plane.subfaces[il].shared ) continue; + NPL_ordered loop = plane.subfaces[il]; + if ( loop.top_overlap<0 || (plane.index_ph==0 && loop.top_overlap<1000) ) { + intersection.addFace(loop.vtx); + intersection.addNormal( plane.index_ph==0 ? phA->faceNormal(ip) : phB->faceNormal(ip-nPA) ); + } + } + } + if (debugMode && intersection.getNFaces()>0 && !intersection.construct(true)) { + std::cout <<"VERIFY intersection:GPG origin:" << phA->getOrigin() <<":"<<phB->getOrigin() << std::endl; + for (unsigned int ii=0; ii<intersection.getNFaces(); ii++) { + std::vector<size_t> vtx = intersection.getFace(ii); + std::cout <<"face:"<<ii<<":"; for (auto vx : vtx) std::cout <<vx <<","; std::cout <<"" << std::endl; + } + for (unsigned int ip=0; ip<planes.size(); ip++) { NPH_plane plane = planes[ip]; + int nShared = 0; for ( auto il : plane.subfaces ) if (il.shared) nShared++; + if ( plane.hit_vertices.size()>2 && nShared<1 ) std::cout <<"PROBLEM in plane:" << ip << std::endl; + } + } + + if ( modeBoolean == 0 && intersection.getNFaces() > 3 ) gpg = new GeoPolyhedronGeneric(intersection); + + // construct subtraction + GeoPolyhedronGeneric subtraction; + subtraction.addVertices(vtx); + for ( unsigned int ip=0; ip<planes.size(); ip++ ) { NPH_plane plane = planes[ip]; + if ( plane.index_ph == 0 ) { // collect non-shared && inverted + for ( unsigned int il=0; il<plane.subfaces.size(); il++) { if (plane.subfaces[il].shared) continue; + NPL_ordered loop = plane.subfaces[il]; + subtraction.addFace(loop.vtx); + subtraction.addNormal( phA->faceNormal(ip)); + } + for ( unsigned int il=0; il<plane.subfaces.size(); il++) { NPL_ordered loop = plane.subfaces[il]; + if ( !loop.shared ) continue; + if ( loop.top_overlap<1000) continue; + subtraction.addFace(loop.vtx); + subtraction.addNormal( phA->faceNormal(ip)); + } + } else { // invert shared non-overlapping + for ( unsigned int il=0; il<plane.subfaces.size(); il++) { + if (!plane.subfaces[il].shared) continue; + NPL_ordered loop = plane.subfaces[il]; + if ( loop.top_overlap >= 0 ) continue; + std::vector<size_t> lvtx; for (auto iv : loop.vtx) lvtx.insert(lvtx.begin(),iv); + subtraction.addFace(lvtx); + subtraction.addNormal( (-1.)* phB->faceNormal(ip-nPA) ); + } + } + } + if (debugMode && subtraction.getNFaces()>0 && !subtraction.construct(true)) { + std::cout <<"VERIFY subtraction:GPG origin:" << phA->getOrigin() <<":"<<phB->getOrigin() << std::endl; + for (unsigned int ii=0; ii<subtraction.getNFaces(); ii++) { + std::vector<size_t> vtx = subtraction.getFace(ii); + std::cout <<"face:"<<ii<<":"; for (auto vx : vtx) std::cout <<vx <<","; std::cout <<"" << std::endl; + } + } + + if ( modeBoolean == 1 && subtraction.getNFaces()>3 ) gpg = new GeoPolyhedronGeneric(subtraction); + + // construct union : non-shared + half of overlaps + GeoPolyhedronGeneric uni; + uni.addVertices(vtx); + for ( unsigned int ip=0; ip<planes.size(); ip++ ) { NPH_plane plane = planes[ip]; + for ( unsigned int il=0; il<plane.subfaces.size(); il++) { if (plane.subfaces[il].shared) continue; + NPL_ordered loop = plane.subfaces[il]; + if (loop.shared) continue; + uni.addFace(loop.vtx); + uni.addNormal( plane.index_ph==0 ? phA->faceNormal(ip) : phB->faceNormal(ip-nPA) ); + } + + if ( plane.index_ph == 0 ) { + for ( unsigned int il=0; il<plane.subfaces.size(); il++) { NPL_ordered loop = plane.subfaces[il]; + if ( loop.shared && loop.top_overlap>=0 && loop.top_overlap<1000 ) { + uni.addFace(loop.vtx); + uni.addNormal( plane.index_ph==0 ? phA->faceNormal(ip) : phB->faceNormal(ip-nPA) ); + } + } + } + } + if (debugMode && !uni.construct(true)) { + std::cout <<"VERIFY union:GPG origin:" << phA->getOrigin() <<":"<<phB->getOrigin()<<":valid? " << uni.isValid() << std::endl; + std::cout << "review input polyhedrons:A:vtx:faces:" << phA->getNVertices() <<":" << phA->getNFaces() << std::endl; + for (unsigned int iv=0 ; iv<phA->getNVertices(); iv++) { GeoTrf::Vector3D xx = phA->getVertex(iv); + std::cout << iv <<":" << xx.x() << "," << xx.y() <<"," << xx.z() << std::endl; + } + for (unsigned int ip=0; ip<phA->getNFaces(); ip++) { + std::vector<size_t> vtx = phA->getFace(ip); + GeoTrf::Vector3D nA = phA->faceNormal(ip); + std::cout << "face A:" << ip <<":normal:" << nA.x() <<"," << nA.y() <<"," << nA.z() << std::endl; + for ( auto iv : vtx ) std::cout << iv<<","; std::cout<<""<<std::endl; + } + std::cout << "review input polyhedrons:B:vtx:faces:" << phB->getNVertices() <<":" << phB->getNFaces() << std::endl; + for (unsigned int iv=0 ; iv<phB->getNVertices(); iv++) { GeoTrf::Vector3D xx = phB->getVertex(iv); + std::cout << iv <<":" << xx.x() << "," << xx.y() <<"," << xx.z() << std::endl; + } + for (unsigned int ip=0; ip<phB->getNFaces(); ip++) { + std::vector<size_t> vtx = phB->getFace(ip); + GeoTrf::Vector3D nB = phB->faceNormal(ip); + std::cout << "face B:" << ip <<":normal:" << nB.x() <<"," << nB.y() <<"," << nB.z() << std::endl; + for ( auto iv : vtx ) std::cout << iv<<","; std::cout<<""<<std::endl; + } + // + for (unsigned int ii=0; ii<uni.getNFaces(); ii++) { + std::vector<size_t> vtx = uni.getFace(ii); + std::cout <<"face:"<<ii<<":"; for (auto vx : vtx) std::cout <<vx <<","; std::cout <<"" << std::endl; + } + } + + if ( modeBoolean == 2 ) gpg = new GeoPolyhedronGeneric(uni); + + if (debugMode) { + if (uni.isValid() && intersection.isValid()) std::cout <<"VOLUME check uni:" << phA->volume()+phB->volume()-intersection.volume() + <<"=?" << uni.volume() << std::endl; + if (subtraction.isValid() && intersection.isValid()) std::cout <<"VOLUME check subtr:" << phA->volume() - intersection.volume() + <<"=?" << subtraction.volume() << std::endl; + } + + double v0 = (gpg && gpg->isValid()) ? gpg->volume() : 0.; + + if (debugMode && gpg) std::cout << "test gpg before cleanup:" << gpg->isValid() <<":volume:" << v0 << std::endl; + + if (gpg && gpg->isValid()) gpg->cleanAfterBooleanOperation(); + + if (debugMode) { + if (gpg) { std::cout << "gpg conversion test:valid:" << gpg->isValid(); + if (gpg->isValid()) std::cout <<":volume:" << gpg->volume() <<":v0:"<<v0; + std::cout<<""<< std::endl; + } + } + + return gpg; +} + +std::vector<NPH_edge> GeoPolyhedronManipulator::collectEdges(std::vector<NPH_plane>& planes, std::vector<NPH_vertex>& vlog, bool debugMode ) const { + + std::vector<NPH_edge> edges; + // each edge belongs to 2 planes, attach defining vertices + unsigned int nf = planes.size(); + for (unsigned int index = 0; index<2; index++) { + for (unsigned int iplane = 0; iplane < planes.size(); iplane++) { + NPH_plane plane = planes[iplane]; + if ( plane.index_ph != index ) continue; + std::vector<size_t> vs = plane.vertices; + for (unsigned int iv = 0; iv < vs.size(); iv++) { + int evi = vs[iv]; int evf = iv < vs.size()-1 ? vs[iv+1] : vs[0]; + NPH_edge new_edge(std::pair<int,int>(evi,evf)); + new_edge.faces.first = iplane; new_edge.index_ph = index; + // find the associated plane - full loop needed before put in use + for (unsigned int ie=0; ie<edges.size(); ie++) { + NPH_edge edge = edges[ie]; if (edge.index_ph != index ) continue; + if (edge.endpoints.first == new_edge.endpoints.second && edge.endpoints.second == new_edge.endpoints.first ) + { edges[ie].faces.second = iplane; new_edge.faces.second = edge.faces.first; new_edge.edge_inverse=ie; edges[ie].edge_inverse=edges.size(); break; } + } + edges.push_back(new_edge); + vlog[evf].edges.push_back(std::make_pair<int>(edges.size()-1,-1)); + planes[iplane].edges.push_back(edges.size()-1); + } + } + // second loop to add corresponding faces + for (unsigned int iplane = 0; iplane < nf; iplane++) { + NPH_plane plane = planes[iplane]; + if ( plane.index_ph != index ) continue; + std::vector<size_t> vs = plane.vertices; + for (unsigned int iv = 0; iv < vs.size(); iv++) { + int evi = vs[iv]; int evf = iv < vs.size()-1 ? vs[iv+1] : vs[0]; + // search edge with first face iplane and starting vertex at vs[iv+1] + for (unsigned int ie = 0; ie<edges.size(); ie++) { + if (edges[ie].index_ph != index) continue; + if (edges[ie].faces.first==iplane && edges[ie].endpoints.first==evf) { + for (unsigned int ii=0; ii<vlog[evf].edges.size(); ii++) { + if (edges[vlog[evf].edges[ii].first].faces.first==iplane + && edges[vlog[evf].edges[ii].first].endpoints.second==evf) { vlog[evf].edges[ii].second = edges[ie].faces.second; break; } + } + } + } // end search edge + } // end loop vertices + } // end loop planes + + // third loop to retrieve vertex logic + for (unsigned int iplane = 0; iplane < nf; iplane++) { + NPH_plane plane = planes[iplane]; + if ( plane.index_ph != index ) continue; + std::vector<size_t> vs = plane.vertices; + for (unsigned int iv = 0; iv < vs.size(); iv++) { + // collect vertex ordering logic + NPL_vertex plv = filterLog(vlog[vs[iv]],edges,iplane,planes); + planes[iplane].vlog_ori.push_back(plv); + } // end loop vertices + } // end loop planes + + } // end loop ph + + if (debugMode) { + for (unsigned int iv=0; iv<vlog.size(); iv++) { + std::cout << "vertex logic:" << iv<< std::endl; + for (unsigned int ie = 0; ie<vlog[iv].edges.size(); ie++) std::cout<< ie <<":" << edges[vlog[iv].edges[ie].first].faces.first<<"," << + edges[vlog[iv].edges[ie].first].faces.second<< ":" << vlog[iv].edges[ie].second << std::endl; + } + } + + return edges; +} + +void GeoPolyhedronManipulator::collectLines(std::vector<NPH_plane>& planes, std::vector<GeoTrf::Vector3D>& vtx, std::vector<NPH_vertex>& vlog, + std::vector<NPH_edge>& edges, bool debugMode ) const { + GeoTrf::Vector3D eInit; GeoTrf::Vector3D edge; GeoTrf::Vector3D point; GeoTrf::Vector3D normal; + for (unsigned int ie = 0; ie < edges.size(); ie++ ) { + eInit = vtx[edges[ie].endpoints.first]; + edge = vtx[edges[ie].endpoints.second] - eInit; + if (debugMode) std::cout << "collectLines:edge:" << ie <<":" << edges[ie].endpoints.first <<"," << edges[ie].endpoints.second + <<":ori:" <<edges[ie].index_ph<<":faces:" << edges[ie].faces.first <<":" << edges[ie].faces.second << std::endl; + for (unsigned int ip = 0; ip < planes.size() ; ip++) { + if (edges[ie].index_ph == planes[ip].index_ph ) continue; + std::vector<size_t> pvx = planes[ip].vertices; + for (auto vx : pvx) if ( std::abs(edge.cross(vtx[vx] - eInit).norm()) > m_tolerance ) { + point = vtx[vx]; break; //if (debugMode) std::cout << "setting ref point:" << point.x()<<"," <<point.y()<<"," << point.z() << std::endl; break; + } + normal = planes[ip].normal; + if (std::abs(edge.dot(normal)) > m_tolerance) { + // intersection of the edge line with plane + double d = edgeFaceIntersection(eInit, edge, point, normal); + if (d*edge.norm()<-m_tolerance || (d-1.)*edge.norm()>m_tolerance) continue; + GeoTrf::Vector3D xyz = eInit + edge * d; + if ( insideFace(ip,xyz,1.e-5) ) { + //if (debugMode) std::cout <<"intersecting plane:" << ip <<":ori:" << planes[ip].index_ph <<":d:" << d + // <<":intersection:" <<xyz.x()<<","<<xyz.y()<<","<<xyz.z() <<std::endl; + int index = -1; // valid vertex : already found ? + double closest = 1.e10; + if (std::abs(d*edge.norm())<m_tolerance) { index = edges[ie].endpoints.first; + if (!vlog[index].shared) std::cout << "ERROR:i/ endpoints crossing found for unshared vertex:" << index << std::endl; + } + else if (std::abs((d-1.)*edge.norm())<m_tolerance) { index = edges[ie].endpoints.second; + if (!vlog[index].shared) std::cout << "ERROR:ii/ endpoints crossing found for unshared vertex:" << index << std::endl; + } + else { + for (unsigned int iv = 0; iv<vtx.size(); iv++ ) { + if ( (vtx[iv] - xyz).norm() < m_tolerance ) { index = iv; break; } + if ( (vtx[iv] - xyz).norm() < closest ) { closest = (vtx[iv] - xyz).norm(); } + } + if (index<0) { // new vertex + vtx.push_back(xyz); index = vtx.size()-1; + NPH_vertex vc; vc.shared = true; + vlog.push_back(vc); + } + } + if (debugMode) std::cout <<"vertex:" << index << ":merge: valid intersection with plane:" << ip << ":normal:"<<normal.x()<<","<<normal.y()<<","<<normal.z()<<"),"<< d << std::endl; + bool foundOnEdge = false; + if (!foundOnEdge) for (auto iv : edges[ie].evtx) if ( iv.index == index ) { foundOnEdge = true; break; } + if (!foundOnEdge) { + NPH_vtx nvtx(index); nvtx.edge=ie ; nvtx.hitPlane = ip; nvtx.distance = d; nvtx.faces = std::pair<int,int>(edges[ie].faces.second,ip); + std::vector<NPH_vtx>::iterator vIt = edges[ie].evtx.begin(); + while ( vIt< edges[ie].evtx.end()) { + if ( (vtx[(*vIt).index]-eInit).dot(edge)/pow(edge.norm(),2) < d) vIt++; + else break; + } + edges[ie].evtx.insert(vIt,nvtx); + if (debugMode) std::cout <<"0:inserting evtx:"<< nvtx.index <<":at:" << (vtx[nvtx.index]-eInit).dot(edge)/pow(edge.norm(),2) + <<":endpoints:" << edges[ie].endpoints.first << "," << edges[ie].endpoints.second<<":hit plane:" << nvtx.hitPlane + <<":edge faces:" << edges[ie].faces.first << "," << edges[ie].faces.second <<":closest:" << closest << std::endl; + } + // add the hit vertex to the list of new vertices on plane + bool foundOnFace = false; for (auto iv : planes[ip].hit_vertices ) if ( iv.index == index ) { foundOnFace = true; break; } + if (!foundOnFace) { + NPL_vertex plv; plv.index = index; plv.shared = true; plv.faces = edges[ie].faces; + planes[ip].hit_vertices.push_back(plv); + } + bool foundOnFace1 = false; for (auto iv : planes[edges[ie].faces.first].hit_vertices ) if ( iv.index == index ) { foundOnFace1 = true; break; } + if (!foundOnFace1) { + NPL_vertex plv1; plv1.index = index; plv1.shared = true; plv1.faces.first = edges[ie].faces.second; plv1.faces.second = ip; + planes[edges[ie].faces.first].hit_vertices.push_back(plv1); + } + bool foundOnFace2 = false; for (auto iv : planes[edges[ie].faces.second].hit_vertices ) if ( iv.index == index ) { foundOnFace2 = true; break; } + if (!foundOnFace2) { + NPL_vertex plv2; plv2.index = index; plv2.shared = true; plv2.faces.first = ip; plv2.faces.second = edges[ie].faces.first; + planes[edges[ie].faces.second].hit_vertices.push_back(plv2); + } + // + vlog[index].edges.push_back(std::make_pair<int>(ie,ip)); + } // valid edge intersection with face + } else { + // special configuration: edge laying on face plane, need to calculate edge intersections + if ( std::abs((eInit-point).dot(normal))< m_tolerance) { + edges[ie].inPlane.push_back(ip); + intersectFace(ie, edges, ip, planes, vtx, vlog); + } + } + } // end loop over faces + } // end loop over edges + + // loop over intersection vertices to resolve vertex logic + for (unsigned int ie = 0; ie < edges.size(); ie++ ) { + for (unsigned int iv = 0; iv < edges[ie].evtx.size(); iv++ ) { NPH_vtx vx = edges[ie].evtx[iv]; + if ( vlog[vx.index].index_ph >= 0 && vlog[vx.index].index_ph != edges[ie].index_ph ) { + vx.hitVertex = vx.index; + } else if (vlog[vx.index].index_ph<0) { + for (auto vl : vlog[vx.index].edges) { + if ( vl.first != ie && vl.first != edges[ie].edge_inverse ) { + if ( vx.hitEdge < 0 ) { vx.hitEdge = vl.first; + vlog[vx.index].hit_edge_edge.first = edges[ie].index_ph == 0 ? ie : vl.first; + vlog[vx.index].hit_edge_edge.second = edges[ie].index_ph == 1 ? ie : vl.first; + } + } + } + } else if ( vlog[vx.index].index_ph == edges[ie].index_ph ) { + for (auto vl : vlog[vx.index].edges) { + if ( edges[vl.first].index_ph == edges[ie].index_ph ) continue; + if ( vx.hitEdge < 0 ) vx.hitEdge = vl.first; + } + } + } + } + + // loop over intersections to remove double hits + std::vector<NPH_vtx>::iterator vIt; std::vector<NPH_vtx>::iterator wIt; + for (unsigned int ie = 0; ie < edges.size(); ie++ ) { + vIt = edges[ie].evtx.begin(); int vin = edges[ie].endpoints.first; + while ( vIt != edges[ie].evtx.end() ) { + if ( (*vIt).index == vin ) edges[ie].evtx.erase(vIt); + else { vin = (*vIt).index; vIt++;} + } + if (vin == edges[ie].endpoints.second && edges[ie].evtx.size()>0 ) edges[ie].evtx.pop_back(); + } + + // check symmetrization + for (unsigned int ie = 0; ie < edges.size(); ie++ ) { + for ( auto iv : edges[ie].evtx) { + bool foundHit = false; + for (auto jv : edges[edges[ie].edge_inverse].evtx) if ( jv.index == iv.index) foundHit = true; + if (!foundHit) { + NPH_vtx nvtx(iv.index); nvtx.edge = edges[ie].edge_inverse ; nvtx.hitPlane = iv.hitPlane; nvtx.distance = 1.-iv.distance; + nvtx.faces = std::pair<int,int>(iv.faces.second,iv.faces.first); + saveHitOnEdge( nvtx, edges); + } + } + } + +} + + +void GeoPolyhedronManipulator::intersectFace(unsigned int in_edge, std::vector<NPH_edge>& edges, unsigned int plane_index, std::vector<NPH_plane>& planes, std::vector<GeoTrf::Vector3D>& vtx, std::vector<NPH_vertex>& vlog) const +{ + // define probe + GeoTrf::Vector3D point = vtx[edges[in_edge].endpoints.first]; + GeoTrf::Vector3D probe = vtx[edges[in_edge].endpoints.second] - point; + double pn = probe.norm(); + + double d,dist,t; double en; + + for (unsigned int ie = 0; ie < edges.size(); ie++) { + + if (edges[ie].faces.first != plane_index ) continue; + + GeoTrf::Vector3D eInit = vtx[edges[ie].endpoints.first]; + GeoTrf::Vector3D edge = vtx[edges[ie].endpoints.second] - eInit; + en = edge.norm(); + + // dist and t in units (probe, edge) + bool lineIntersect = intersectLines( point, probe, eInit, edge, dist, t, d); + if (!lineIntersect) { + if (std::abs(d)< m_tolerance) { // overlapping edges ? TODO : figure out the actual overlap + setDoubleEdge(ie,in_edge,edges); + } + continue; + } + if ( std::abs(d) > m_tolerance || t*en < -m_tolerance || t*en > en+m_tolerance || dist*pn < -m_tolerance || dist*pn > pn+m_tolerance ) continue; + // valid intersection - may be known already + int index = -1; bool foundHit; + if (std::abs(t*en)<m_tolerance) index = edges[ie].endpoints.first; + else if (std::abs(dist*pn)<m_tolerance) index = edges[in_edge].endpoints.first; + else if ( std::abs(t*en -en)<m_tolerance) index = edges[ie].endpoints.second; + else if ( std::abs(dist*pn -pn)<m_tolerance) index = edges[in_edge].endpoints.second; + else { + GeoTrf::Vector3D hit = eInit + t*edge; + for (unsigned int jv = 0; jv<vtx.size(); jv++) if ( (hit - vtx[jv]).norm() < m_tolerance ) { index = jv ; break ;} + if (index < 0) { vtx.push_back(hit); index = vtx.size()-1; NPH_vertex pv; pv.index = index; pv.shared = true; vlog.push_back(pv); } + } + // save on probe edge + NPH_vtx nvtx(index); nvtx.edge=in_edge ; nvtx.hitPlane = edges[in_edge].faces.second ; nvtx.distance = dist; nvtx.faces = std::pair<int,int>(edges[ie].faces); + saveHitOnEdge(nvtx,edges); + // save on plane edge + NPH_vtx nptx(index); nptx.edge=ie ; nptx.hitPlane = edges[ie].faces.second ; nptx.distance = t; nptx.faces = std::pair<int,int>(edges[in_edge].faces); + saveHitOnEdge(nptx,edges); + // + vlog[index].edges.push_back(std::pair<int,int>(in_edge,edges[ie].faces.second)); + vlog[index].edges.push_back(std::pair<int,int>(ie,edges[in_edge].faces.second)); + vlog[index].hit_edge_edge.first = edges[in_edge].index_ph == 0 ? in_edge : ie; + vlog[index].hit_edge_edge.second = edges[in_edge].index_ph == 1 ? ie : in_edge; + // add the hit vertex to the list of new vertices on plane + bool foundOnFace = false; for (auto iv : planes[plane_index].hit_vertices ) if ( iv.index == index ) { foundOnFace = true; break; } + if (!foundOnFace) { + NPL_vertex plv; plv.index = index; plv.shared = true; plv.faces = edges[in_edge].faces; + planes[plane_index].hit_vertices.push_back(plv); + } + bool foundOnFaca = false; for (auto iv : planes[edges[ie].faces.second].hit_vertices ) if ( iv.index == index ) { foundOnFaca = true; break; } + if (!foundOnFaca) { // vertex saved on adjacent face + NPL_vertex plva; plva.index = index; plva.shared = true; plva.faces = edges[in_edge].faces; // plva.faces = edges[in_edge].faces; + planes[edges[ie].faces.second].hit_vertices.push_back(plva); + } + bool foundOnFace1 = false; for (auto iv : planes[edges[in_edge].faces.first].hit_vertices ) if ( iv.index == index ) { foundOnFace1 = true; break; } + if (!foundOnFace1) { // vertex to be saved on probe edge faces + NPL_vertex plv1; plv1.index = index; plv1.shared = true; plv1.faces = edges[ie].faces; + planes[edges[in_edge].faces.first].hit_vertices.push_back(plv1); + } + bool foundOnFace2 = false; for (auto iv : planes[edges[in_edge].faces.second].hit_vertices ) if ( iv.index == index ) { foundOnFace2 = true; break; } + if (!foundOnFace2) { // vertex to be saved on probe edge faces + NPL_vertex plv2; plv2.index = index; plv2.shared = true; plv2.faces = edges[ie].faces; + planes[edges[in_edge].faces.second].hit_vertices.push_back(plv2); + } + } // end loop over face edges + + return; +} + +NPL_vertex GeoPolyhedronManipulator::filterLog(NPH_vertex vlog, std::vector<NPH_edge> edges, int ip, std::vector<NPH_plane> planes) const { + + NPL_vertex plv; + plv.index = vlog.index; + plv.shared = vlog.shared; + plv.overlapping_face = -1; + plv.oriented = false; + + int edgeIn = -1; + // original vertices define orientation + if ( vlog.index_ph == planes[ip].index_ph ) { + for ( auto ee : vlog.edges ) { + if ( ee.second == ip && edges[ee.first].index_ph == vlog.index_ph ) { plv.faces = edges[ee.first].faces; edgeIn = ee.first; } + } + return plv; + } + + for ( auto ee : vlog.edges ) { + if ( ee.second == ip) { plv.faces = edges[ee.first].faces; edgeIn = ee.first; } + } + + // check overlaps + for ( auto ee : vlog.edges ) { + if ( ee.first == edgeIn && ee.second!=ip ) { plv.overlapping_face = ee.second ; plv.shared = true; } // shared used to be 2 here, verify + } + return plv; +} + +void GeoPolyhedronManipulator::filterLogPlane(NPL_vertex& plv, NPH_vertex& vlog, std::vector<NPH_edge> edges, int ip, std::vector<NPH_plane> planes) const { + + for ( auto ee : vlog.edges ) { + if ( ee.second == ip ) { + plv.faces = edges[ee.first].faces; + if (edges[ee.first].index_ph == planes[ip].index_ph) plv.oriented = true; + } + } + for (auto ov : planes[ip].overlapping) { + for ( auto ee : vlog.edges ) { + if ( ee.second == ov%1000 ) { + plv.faces_shared_loop = edges[ee.first].faces; + } + } + } + +} + +bool GeoPolyhedronManipulator::intersectLines(GeoTrf::Vector3D A, GeoTrf::Vector3D a, GeoTrf::Vector3D B, GeoTrf::Vector3D b, double& da, double& db, double& d) const +{ + // lines parametrization : A + a*da , B + b*db ; a,b define "units" of distances da, db + // return value : closest distance between lines in native [mm] units + + // scalars + double ab = a.dot(b); double a2 = a.dot(a); double b2 = b.dot(b); + double ABa = (A-B).dot(a); + double ABb = (A-B).dot(b); + + // special cases + //if ( !ab ) { + if (std::abs(ab)/sqrt(a2*b2) < 1.e-9 ) { // to ensure sufficient precision + da = -ABa / a2; + db = ABb / b2; + d = (A + da*a -B - db*b).norm() ; + return true; + } + + if ( a2*b2 == ab*ab ) { // parallel lines + da = 0; db = 0; + d = ((A-B).cross(b)).norm()/b.norm(); + return false; + } + + da = (ABb*ab -b2*ABa) / (a2*b2 - ab*ab); + db = (ABa + a2*da) / ab; + d = (A + da*a -B - db*b).norm() ; + return true; + +} + +// method to find intersection of a (polyhedron) edge with (another) polyhedron face plane +// arguments : edge: (1) starting point, (2) endpoint-starting point, plane: (3) point , (4) normal +double GeoPolyhedronManipulator::edgeFaceIntersection( GeoTrf::Vector3D eInit, GeoTrf::Vector3D edge, GeoTrf::Vector3D facePoint, GeoTrf::Vector3D faceNormal ) const +{ + if (!edge.dot(faceNormal)) { + std::cout << "ERROR, edgeFace intersection called for edge parallel to plane" << std::endl; + return 1.e10; + } + + // scalars + double en = edge.dot(faceNormal); + double pen = ( facePoint - eInit ).dot(faceNormal); + + return pen / en ; +} + +std::vector<NPL_edge> GeoPolyhedronManipulator::evaluateVertexLogic(int plane_index, std::vector<NPH_vertex>& vlog, std::vector<NPH_edge> edges, std::vector<NPH_plane>& planes, std::vector<GeoTrf::Vector3D> vtx) const { + + // loop over vertices to determine the level of ambiguity : results save as NPL_vertex properties + // default mode processes hit vertices ( intersections ) + + NPH_plane plane = planes[plane_index]; + + std::vector<NPL_edge> lines; std::vector< std::pair< int, std::vector< int > > > active_intersected; + + for (unsigned int ih = 0; ih< plane.hit_vertices.size(); ih++) { NPL_vertex iplv = plane.hit_vertices[ih]; NPH_vertex iv = vlog[iplv.index]; + // overview of adjacent planes + std::vector<int> np; std::vector<int> native_planes; std::vector<int> intersected_planes; int pl; bool found; + for (auto ie : iv.edges) { + if (edges[ie.first].index_ph==0) iv.hit_edge_edge.first = ie.first; + else if ( edges[ie.first].index_ph==1) iv.hit_edge_edge.second = ie.first; + // + pl = edges[ie.first].faces.first; + if (pl>=0) { found = false; for (auto pp : np ) if (pl==pp) {found=true; break; } if (!found) np.push_back(pl);} + pl = edges[ie.first].faces.second; + if (pl>=0) { found = false; for (auto pp : np ) if (pl==pp) {found=true; break; } if (!found) np.push_back(pl);} + pl = ie.second; + if (pl>=0) { found = false; for (auto pp : np ) if (pl==pp) {found=true; break; } if (!found) np.push_back(pl);} + } + // + for (auto pp : np ) if (planes[pp].index_ph==plane.index_ph) native_planes.push_back(pp); else intersected_planes.push_back(pp); + for (auto ip : intersected_planes) { bool foundIntersected = false ; + for (unsigned int ii=0; ii<active_intersected.size(); ii++) { + if ( active_intersected[ii].first == ip ) { foundIntersected = true; + active_intersected[ii].second.push_back(iplv.index); + } + } + if (!foundIntersected) active_intersected.push_back(std::pair<int, std::vector<int> > (ip, std::vector<int> (1,iplv.index)) ); + } + // + if (iv.index_ph < 0) { // new vertex / mixed planes : "native" fit the top plane index + // + if ( native_planes.size()==1 && intersected_planes.size() == 2 ) { filterLogPlane(iplv, iv, edges, plane_index, planes); iplv.oriented = false; } + else if ( native_planes.size()==2 && intersected_planes.size() == 1 ) { + std::vector<int> hitFaces = findHitPlane( iv, plane_index, edges, planes); + if ( hitFaces.size() >1 ) { iplv.faces.first = hitFaces[0]; iplv.faces.second = hitFaces[1]; } + } else { // see if number of hit faces reasonable when taking into account overlaps + std::vector<int> hitFaces = findHitPlane( iv, plane_index, edges, planes); + std::vector<int>::iterator hIt = hitFaces.begin(); + while ( hIt!= hitFaces.end() ) { + if ( matchPlanes(plane_index, (*hIt), planes ) ) hitFaces.erase(hIt); + else hIt++; + } + if (hitFaces.size() == 2) { iplv.faces.first = hitFaces[0]; iplv.faces.second = hitFaces[1]; } + else if (intersected_planes.size()>=2) { + std::vector<int> nov; for ( auto ii : intersected_planes ) if ( !matchPlanes(plane_index,ii,planes) ) nov.push_back(ii); + if (nov.size()==2) { iplv.faces.first = nov[0]; iplv.faces.second = nov[1]; } + else { iplv.faces.first = -1; iplv.faces.second = -1; } + } else { iplv.faces.first = -1; iplv.faces.second = -1; } + } + + } else { + + filterLogPlane( planes[plane_index].hit_vertices[ih],iv,edges,plane_index, planes); + } + + plane.hit_vertices[ih].overlapping_face = -1; + for (auto ov : planes[plane_index].overlapping) if (inPlane(iv,edges,ov%1000) ) plane.hit_vertices[ih].overlapping_face=ov%1000; + + if ( np.size() == 5) { + std::pair<int,int> doublEdg = hitDoubleEdge(iplv.index, plane_index, plane.index_ph, vlog, edges); // indicates adjacent face + if (doublEdg.first>=0 && iv.index_ph==plane.index_ph) { + vlog[iplv.index].hitDoublEdge = doublEdg.first; + vlog[iplv.index].onDoublEdge = (planes[doublEdg.first].index_ph == plane.index_ph) ? 0 : 1; + } + if (doublEdg.first>=0 && iv.index_ph!=plane.index_ph) { + planes[plane_index].hit_vertices[ih].faces.first = doublEdg.second; + // check if valid intersecting plane + bool validPlane = false; + for ( unsigned int jh=0; jh<plane.hit_vertices.size(); jh++) { if (jh==ih) continue; + if ( inPlane(vlog[plane.hit_vertices[jh].index],edges,doublEdg.first) ) validPlane = true; + } + planes[plane_index].hit_vertices[ih].faces.second = validPlane ? doublEdg.first : doublEdg.second; + if (!validPlane) iplv.onEdgeDummy = true; + vlog[iplv.index].hitDoublEdge = validPlane ? doublEdg.first : doublEdg.second ; + vlog[iplv.index].onDoublEdge = validPlane ? 1 : 0; + } + } + } + + for (auto ii : active_intersected) { // std::cout << "protoline?" << ii.first <<":" << ii.second.size() ; + bool ol = false; for (auto ov : plane.overlapping) if ( ov%1000 == ii.first) ol = true; + if (ol) continue; // do not consider overlapping faces + if ( ii.second.size()<2) continue; // skip incomplete connections + // order vertices along line ( needs acces to vertex position ) + std::vector<std::pair<int,double>> vxord; + vxord.push_back(std::pair<int,double>(ii.second[0],0.) ); + vxord.push_back(std::pair<int,double>(ii.second[1],1.) ); + double al = (vtx[ii.second[1]] -vtx[ii.second[0]]).norm(); + std::vector<std::pair<int,double> >::iterator ita; + for (unsigned int iv = 2; iv<ii.second.size(); iv++) { + double da = (vtx[ii.second[iv]] -vtx[ii.second[0]]).dot(vtx[ii.second[1]] -vtx[ii.second[0]])/al/al; + ita = vxord.begin(); + while ( ita!= vxord.end() ) { + if (da < (*ita).second ) { vxord.insert(ita,std::pair<int,double>(ii.second[iv],da) ); break; } + else ita++; + } + if (ita==vxord.end()) vxord.insert(ita,std::pair<int,double>(ii.second[iv],da) ); + } + // count number of hit vtx + int nhit = 0; int native = 0; std::vector<int> hits; + for (auto vv : vxord) { + if (vlog[vv.first].index_ph < 0) { nhit++; + for (unsigned int ih=0; ih<plane.hit_vertices.size(); ih++) { + if (vv.first == plane.hit_vertices[ih].index) hits.push_back(ih); + } + } + if (vlog[vv.first].index_ph == planes[plane_index].index_ph ) native++; + } + + int noe = 0; // not-on-edge + for (auto vv : vxord) { + for (unsigned int ih=0; ih<plane.hit_vertices.size(); ih++) { + if (vv.first == plane.hit_vertices[ih].index) { + if ( plane.hit_vertices[ih].onEdge < 0 ) noe++; + break; + } + } + } + + // examine different configurations + bool splitLine = true; + + if (vxord.size()>2 && vlog[vxord.front().first].index_ph < 0 && vlog[vxord.back().first].index_ph < 0 ) splitLine = false; + if (noe>0) splitLine = true; + + // create line edge + for (unsigned int jl =0; jl<vxord.size()-1; jl++) { + lines.push_back(createLine(vxord[jl].first,vxord[jl+1].first,ii.first, plane) ); + if (!splitLine) lines.back().protectedLine = true; + } + + for (auto iv : ii.second) { // std::cout <<":index:"<< iv; + for (unsigned int ih=0; ih<plane.hit_vertices.size(); ih++) { + if (iv == plane.hit_vertices[ih].index) { + //std::cout <<":faces:" << plane.hit_vertices[ih].faces.first <<"," << plane.hit_vertices[ih].faces.second ; + planes[plane_index].hit_vertices[ih].active_lines.push_back(ii.first); + } + } + } + //std::cout <<""<< std::endl; + } + + return lines; +} + +bool GeoPolyhedronManipulator::inPlane(NPH_vertex vlog, std::vector<NPH_edge> edges, int ip) const { + + bool planeVtx = false; + + int f1,f2,f3; bool vin = false; + for ( auto ef : vlog.edges ) { + f1 = edges[ef.first].faces.first; + f2 = edges[ef.first].faces.second; + f3 = ef.second; + vin = ( (ip==f1) || (ip==f2) || (ip==f3) ) ; + if (vin) planeVtx = true; + } + + return planeVtx; + +} + +bool GeoPolyhedronManipulator::matchPlanes( int p1, int p2, std::vector<NPH_plane> planes) const { + + bool match = false; + + if (p1 == p2) return match=true; + + for ( auto ov : planes[p1].overlapping ) if ( ov%1000 == p2 ) match = true; + + return match; + +} + +std::vector<int> GeoPolyhedronManipulator::findHitPlane( NPH_vertex vlog, int plane_index, std::vector<NPH_edge> edges, std::vector<NPH_plane> planes) const { + + // ph_index stands for the polyhedron index of the currently processed plane + std::vector<int> faceOut; bool foundFace; + for ( auto ee : vlog.edges ) { + if ( !matchPlanes(edges[ee.first].faces.first,plane_index,planes) ) { + foundFace = false ; for (auto iv : faceOut ) if ( iv == edges[ee.first].faces.first ) foundFace = true; + if (!foundFace ) faceOut.push_back(edges[ee.first].faces.first); + } + if ( !matchPlanes(edges[ee.first].faces.second,plane_index,planes) ) { + foundFace = false ; for (auto iv : faceOut ) if ( iv == edges[ee.first].faces.second ) foundFace = true; + if (!foundFace) faceOut.push_back(edges[ee.first].faces.second); + } + if ( !matchPlanes(ee.second,plane_index,planes) ) { + foundFace = false ; for (auto iv : faceOut ) if ( iv == ee.second ) foundFace = true; + if (!foundFace) faceOut.push_back(ee.second); + } + } + + return faceOut; +} + +std::pair<int,int> GeoPolyhedronManipulator::hitDoubleEdge(int index, int plane_index, int ph_index, std::vector<NPH_vertex> vlog, std::vector<NPH_edge> edges) const { + + int hitFace = -1; int adjFace = -1; + + int edgeExt = vlog[index].index_ph==0 ? vlog[index].hit_edge_edge.second : vlog[index].hit_edge_edge.first ; + int edgeInt = ph_index==0 ? vlog[index].hit_edge_edge.first : vlog[index].hit_edge_edge.second ; + if ( edgeExt < 0 ) return std::pair<int,int>(hitFace,adjFace); + + if (edges[edgeInt].faces.first == plane_index) adjFace = edges[edgeInt].faces.second; + else adjFace = edges[edgeInt].faces.first; + + for (auto ie : vlog[index].edges) { if ( edges[ie.first].index_ph != vlog[index].index_ph ) continue; + bool inPlaneFirst = false; for ( auto ii : edges[ie.first].inPlane ) if ( ii== edges[edgeExt].faces.first ) inPlaneFirst = true; + bool inPlaneSecond = false; for ( auto ii : edges[ie.first].inPlane ) if ( ii== edges[edgeExt].faces.second ) inPlaneSecond = true; + if (inPlaneFirst && inPlaneSecond ) hitFace = ie.second; + } + + return std::pair<int,int>(hitFace,adjFace); + +} + +void GeoPolyhedronManipulator::setDoubleEdge(int ie,int ia, std::vector<NPH_edge>& edges) const { + + bool foundEdge = false; for (auto je : edges[ie].doubleEdge) if (je == ia) foundEdge = true; + if (!foundEdge) edges[ie].doubleEdge.push_back(ia); + + foundEdge = false; for (auto je : edges[ia].doubleEdge) if (je == ie) foundEdge = true; + if (!foundEdge) edges[ia].doubleEdge.push_back(ie); + +} + +NPL_edge GeoPolyhedronManipulator::symmetrize( NPL_edge line ) const { + + NPL_edge sym(std::pair<int,int>(line.endpoints.second,line.endpoints.first), line.adjacent_face); + sym.endpoint_faces.first = line.endpoint_faces.second; + sym.endpoint_faces.second = line.endpoint_faces.first; + sym.endpoint_hitvx.first = line.endpoint_hitvx.second; + sym.endpoint_hitvx.second = line.endpoint_hitvx.first; + sym.overlap = line.overlap; + sym.shared = line.shared; + sym.protectedLine = line.protectedLine; + sym.dummy_overlap = line.dummy_overlap; + return sym; +} + + +void GeoPolyhedronManipulator::saveHitOnEdge(NPH_vtx nvtx, std::vector<NPH_edge>& edges) const{ + + if (edges[nvtx.edge].endpoints.first == nvtx.index ) return; + if (edges[nvtx.edge].endpoints.second == nvtx.index ) return; + for (auto iv : edges[nvtx.edge].evtx ) if (iv.index == nvtx.index) return; + + std::vector<NPH_vtx>::iterator vIt = edges[nvtx.edge].evtx.begin(); + while ( vIt< edges[nvtx.edge].evtx.end()) { + if ( (*vIt).distance < nvtx.distance) vIt++; + else break; + } + edges[nvtx.edge].evtx.insert(vIt,nvtx); + //std::cout <<"inserting evtx(method):" << nvtx.index <<":at:" << nvtx.distance <<":endpoints:" << + // edges[nvtx.edge].endpoints.first << "," << edges[nvtx.edge].endpoints.second<<":hit plane:" << nvtx.hitPlane<< std::endl; +} + +NPL_edge GeoPolyhedronManipulator::createLine(int iv1, int iv2, int adj, NPH_plane plane) const { + + NPL_edge line( std::pair<int,int>(iv1,iv2),adj); line.generating_edge = -1; line.shared = true; + + int h1=-1; int h2=-1; + for (int ih=0; ih<plane.hit_vertices.size(); ih++) { + if ( iv1 == plane.hit_vertices[ih].index) h1 = ih; + if ( iv2 == plane.hit_vertices[ih].index) h2 = ih; + } + + if (h1>=0) { + line.endpoint_hitvx.first = h1; + line.endpoint_faces.first = adj == plane.hit_vertices[h1].faces.first ? + plane.hit_vertices[h1].faces.second : plane.hit_vertices[h1].faces.first ; + } + if (h2>=0) { + line.endpoint_hitvx.second = h2; + line.endpoint_faces.second = adj == plane.hit_vertices[h2].faces.first ? + plane.hit_vertices[h2].faces.second : plane.hit_vertices[h2].faces.first ; + } + + return line; +} + +NPL_ordered GeoPolyhedronManipulator::mergeLoops( NPL_ordered loop1, NPL_ordered loop2, std::vector< GeoTrf::Vector3D > vtx) { + + NPL_ordered merged = loop1; + + if ( !loop2.vtx.size() ) return merged; + + std::vector<size_t> shared; + + for (auto iv1 : loop1.vtx) { + for ( auto iv2 : loop2.vtx) { + if ( iv1 == iv2 ) shared.push_back(iv1); + } + } + + if (shared.size()) { + + loop2.reorder(shared[0]); + merged.reorder(shared[0]); + merged.vtx.insert(merged.vtx.begin(),loop2.vtx.begin(),loop2.vtx.end()); + merged.edges.insert(merged.edges.begin(),loop2.edges.begin(),loop2.edges.end()); + + } else { // there is no common vertex : new dummy symmetrized edge created + + // find closest + double dist = 1.e10; int im1 = -1; int im2 = -1; + for (unsigned int iv1=0; iv1<loop1.vtx.size(); iv1++) { + for (unsigned int iv2=0; iv2<loop2.vtx.size(); iv2++) { + double d = ( vtx[loop1.vtx[iv1]] - vtx[loop2.vtx[iv2]] ).norm(); + if ( d < dist ) { + im1 = loop1.vtx[iv1] ; im2 = loop2.vtx[iv2] ; dist = d; + } + }} + merged.reorder(im1); + loop2.reorder(im2); + + merged.vtx.push_back(merged.vtx.front()); + loop2.vtx.push_back(loop2.vtx.front()); + + merged.vtx.insert(merged.vtx.begin(),loop2.vtx.begin(),loop2.vtx.end()); + } + + return merged; +} + + +GeoTrf::Vector3D GeoPolyhedronManipulator::areaVec( std::vector<size_t> loop, std::vector< GeoTrf::Vector3D > vtx) const { + + int n = loop.size(); + GeoTrf::Vector3D area = vtx[loop[0]].cross(vtx[loop[n - 1]]); + for (int k = 1; k < n; ++k) area += vtx[loop[k]].cross(vtx[loop[k-1]]); + + return area; + +}