Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

swh:1:snp:c78d7a14cc07423acb6a43d0e3059b9afd67996a
  • Code
  • Branches (1)
  • Releases (0)
    • Branches
    • Releases
    • HEAD
    • refs/heads/master
    No releases to show
  • f9aac5f
  • /
  • src
  • /
  • BSP.cpp
Raw File Download

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
  • revision
  • snapshot
content badge
swh:1:cnt:4d2fb87f88f7999e19b3e486ff1b783c934bcbd2
directory badge
swh:1:dir:4f575ba51229ed8a439e690987eb7223782b67ec
revision badge
swh:1:rev:c5be799ee3dad53f5edf10eeb39527bbca5add11
snapshot badge
swh:1:snp:c78d7a14cc07423acb6a43d0e3059b9afd67996a

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
  • revision
  • snapshot
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Tip revision: c5be799ee3dad53f5edf10eeb39527bbca5add11 authored by LorenzoDiazzi on 29 May 2025, 10:15:58 UTC
Update README.md
Tip revision: c5be799
BSP.cpp
#include <iostream>
#include <fstream>
#include <set>
#include <time.h>
#include <algorithm>
#include "BSP.h"
#include "delaunay.h"

#define OPPOSITE_SIGNE(a,b) (a<0 && b>0) || (a>0 && b<0)
#define MIN_VECT_ELEM(v,n,it,i_min)  it=1; i_min=0; do{ if(v[it]<v[i_min]) i_min=it; it++; }while(it<n)
#define SAME_EDGE_ENDPTS(e1,e2,E1,E2)  ((e1==E1 && e2==E2) || (e1==E2 && e2==E1))
#define CONSECUTIVE_EDGES(v1,v2,u1,u2) (v1==u1 || v1==u2 || v2==u1 || v2==u2) // assuming <u1,u2> != <v1,v2>
#define FIND_VECT_POS(e,v,pos) pos=0;  do{ if(v[pos]==e) break; pos++; }while(pos<v.size()) // !! assumes e in v. !!
#define REMOVE_ELEM_VECT(e,v) v.erase(std::find(v.begin(), v.end(), e))
#define IS_GHOST_CELL(c) (c==UINT64_MAX)
#define IS_GHOST_TET(t) (mesh->tet_node[4*t+3]==UINT32_MAX)

//----------------
// General purpose
//----------------

// Input: vector of uint64_t type elements: vect,
//        number of elements to shift: num_shift.
// Output: by using vect returns the original vector shifted-down (i.e.
//         according to increasing order) of num_shift position.
// EX. vect = {3,7,2,5,2} num_shift = 2 -> vect = {5,2,3,7,2}.
inline void UINT64_vect_down_shift(vector<uint64_t>& vect, uint64_t num_shift){
  vector<uint64_t> tmp(num_shift, UINT64_MAX);
  uint64_t shift_start_pos = vect.size() - num_shift;
  for(uint64_t pos=0; pos<num_shift; pos++)
     tmp[pos] = vect[shift_start_pos + pos];
  for(uint64_t pos=shift_start_pos-1; pos>=0; pos--){
     vect[pos + num_shift] = vect[pos];
     if(pos==0) break; // Otherwise pos(uint64_t) becomes negative, i.e. error!!
   }
  for(uint64_t pos=0; pos<num_shift; pos++)  vect[pos] = tmp[pos];
}

//  Input: vector of uint64_t type elements: vect,
//        number of elements to shift: num_shift.
// Output: by using vect returns the original vector shifted-up (i.e.
//         according to decreasing order) of num_shift position.
// EX. vect = {3,7,2,5,2} num_shift = 2 -> vect = {3,7,2,5,2}.
inline void UINT64_vect_up_shift(vector<uint64_t>& vect, uint64_t num_shift){
  vector<uint64_t> tmp(num_shift, UINT64_MAX);
  uint64_t pos;
  for(pos=0; pos<vect.size(); pos++){
    if(pos<num_shift) tmp[pos] = vect[pos];
    else vect[pos - num_shift] = vect[pos];
  }
  uint64_t refill_pos = vect.size() - num_shift;
  for(pos=0; pos<num_shift; pos++)
    vect[refill_pos + pos] = tmp[pos];
}

//  Input: the endpoints of two CONSECUTIVE edges u = <u0,u1>, v=<v0,v1>
// Output: the common endpoint.
inline uint32_t consecEdges_common_endpt(uint32_t u0, uint32_t u1,
                                         uint32_t v0, uint32_t v1){
    if(u0 == v0 || u0 == v1) return u0;
    if(u1 == v0 || u1 == v1) return u1;
    // If goes here, something wrong.
    printf("\n[BSP.cpp]consecEdges_common_endpt: ERROR no match.\n");
    return UINT32_MAX;
}

//  Input: the endpoints of an edge u = <u0,u1>,
//         the common endpoint between an edge v CONSECUTIVE to u,
//         and u itself: w.
// Output: the endpoint of u different from v_comm.
inline uint32_t other_edge_endpt(uint32_t u0, uint32_t u1, uint32_t w){
    if(w == u0) return u1;
    if(w == u1) return u0;
    // If goes here, something wrong.
    printf("\n[BSP.cpp]other_edge_endpt_ind: ERROR no match.\n");
    return UINT32_MAX;

}

//----------------
// BSPedge Methods
//----------------

BSPedge BSPedge::split(uint32_t new_point){
    BSPedge e;
    if(meshVertices[2]==UINT32_MAX){
        e.meshVertices[0] = meshVertices[0];
        e.meshVertices[1] = meshVertices[1];
        e.meshVertices[2] = UINT32_MAX;
    }
    else{
        e.meshVertices[0] = meshVertices[0];
        e.meshVertices[1] = meshVertices[1];
        e.meshVertices[2] = meshVertices[2];
        e.meshVertices[3] = meshVertices[3];
        e.meshVertices[4] = meshVertices[4];
        e.meshVertices[5] = meshVertices[5];
    }
    e.vertices[0] = vertices[0];
    e.vertices[1] = new_point;
    vertices[0] = new_point;
    e.conn_face_0 = conn_face_0;
    return e;
}


//----------------
// BSPface Methods
//----------------
inline void BSPface::exchange_conn_cell(uint64_t cell,uint64_t newCell){

    #ifdef DEBUG_BSP
      if(conn_cells[0]!=cell && conn_cells[1]!=cell)
         printf("\n[BSP.h]BSPface::exchange_conn_cell: ERROR no macth for cell "
                "#%llu with this face.\n",cell);
    #endif

    if(conn_cells[0]==cell)   conn_cells[0]=newCell;
    else                      conn_cells[1]=newCell;
}

inline void BSPface::removeEdge(uint64_t edge_face_ind){
  // Special case: edge is the last element of edges.
  if(edge_face_ind == edges.size() -1) edges.pop_back();
  // General case: erase it, but later.. at the moment mark it
  else edges[edge_face_ind] = UINT64_MAX;
}

//----------------
// BSPcell Methods
//----------------

inline void BSPcell::removeFace(uint64_t face_pos){
  if(face_pos != faces.size()-1)  faces[face_pos] = faces[faces.size()-1];
  faces.pop_back();
}

//--------------------
// BSPcomplex Methods
//--------------------

//  Input: the index of a BSPedge: edge,
//         the index of a BSPface: face.
// Output: nothing.
// Note. BSPedge is attached to BSPface and viceversa.
// Note. this function is used ONLY during the conversion from Delaunay mesh to
//       BSPcomplex.
inline void BSPcomplex::assigne_edge_to_face(uint64_t edge, uint64_t face){
  faces[face].edges.push_back(edge);
  //edges[edge].conn_faces.push_back(face);
  edges[edge].conn_face_0 = face;
}

//  Input: the index of 2 cells w.r.t. the vector cells: c1, c2,
// Output: the index of the fece, w.r.t. the vector faces, that lies between
//         the two input-cells.
// Note. It is assumed that both the input-cells are bounded by the input-face.
uint64_t BSPcomplex::faceSharedWithCell(uint64_t c1, uint64_t c2){

  BSPcell& cell1 = cells[c1];
  for(uint64_t i=0; i<cell1.faces.size(); i++)
    if(faces[ cell1.faces[i] ].conn_cells[0] == c2 ||
       faces[ cell1.faces[i] ].conn_cells[1] == c2   )
       return cell1.faces[i];

  #ifdef DEBUG_BSP
  printf("\n[BSP.cpp]BSPcomplex::faceSharedWithCell: ERROR no common face "
         "between cell #%llu and cell #%llu.\n", c1, c2);
  #endif
  return UINT64_MAX;  // Never reached.
}

//  Input: a BSPcell: cell.
// Output: returns the number of edges of the BSPcell.
uint64_t BSPcomplex::count_cellEdges(const BSPcell& cell){
    uint64_t num_halfedges = 0;
    for(const uint64_t f : cell.faces)  num_halfedges += faces[f].edges.size();
    return num_halfedges / 2;
}

//  Input: a BSPcell: cell.
// Output: returns the number of edges of the BSPcell.
uint32_t BSPcomplex::count_cellVertices(const BSPcell& cell,
                                               uint64_t* num_cellEdges){
  if((*num_cellEdges) == UINT64_MAX)  (*num_cellEdges)=count_cellEdges(cell);
  // Euler formula: num_cellVrts = num_cellEdges + 2 - num_cellFaces
  return (uint32_t)((*num_cellEdges) + 2 - cell.faces.size());
}

//  Input: a BSPcell: cell,
//         vector of type edge index: cell_edges.
// Output: by using cell_edges returns the indices of
//         the edges (w.r.t. vector edges) of the BSPcell.
void BSPcomplex::list_cellEdges(BSPcell& cell, vector<uint64_t>& cell_edges){

    uint64_t edge_ind, ce_ind=0;
    for(uint64_t f=0; f<cell.faces.size(); f++){
        BSPface& face = faces[ cell.faces[f] ];
        for(uint64_t e=0; e<face.edges.size(); e++){
            edge_ind = face.edges[e];
            if(edge_visit[edge_ind]==0){
              cell_edges[ce_ind++] = edge_ind;
              edge_visit[edge_ind]=1;
            }
        }
    }

    // Reset edge_visit
    for(uint64_t e=0; e<cell_edges.size(); e++) edge_visit[ cell_edges[e] ]=0;
}

//  Input: a BSPcell: cell,
//         the number of edges of the BSPcell: num_cellEdges,
//         vector of type vertex index: cell_vrts.
// Output: by using cell_vrts returns the indices of
//         the vertices (w.r.t. vector vertices) of the BSPcell.
void BSPcomplex::list_cellVertices(BSPcell& cell, uint64_t num_cellEdges,
                                   vector<uint32_t>& cell_vrts){

    vector<uint64_t> cell_edges(num_cellEdges, UINT64_MAX);
    list_cellEdges(cell, cell_edges);

    uint32_t v, cv_ind=0;
    for(uint64_t e=0; e<cell_edges.size(); e++){
        BSPedge& edge = edges[ cell_edges[e] ];
        v = edge.vertices[0];
        if(vrts_visit[v]==0){
          cell_vrts[cv_ind++] = v;
          vrts_visit[v]=1;
        }
        v = edge.vertices[1];
        if(vrts_visit[v]==0){
          cell_vrts[cv_ind++] = v;
          vrts_visit[v]=1;
        }
    }

    // Reset vrts_visit
    for(uint32_t u=0; u<cell_vrts.size(); u++) vrts_visit[ cell_vrts[u] ]=0;
}

//  Input: a BSPface: face,
//         vector of type vertex index: face_vrts.
// Output: by using face_vrts returns the indices of
//         the vertices (w.r.t. vector vertices) of the BSPface.
void BSPcomplex::list_faceVertices(BSPface& face, vector<uint32_t>& face_vrts){

    uint32_t fv_ind=0;

    // Add both endpoints of first edge.
    BSPedge& edge0 = edges[ face.edges[0] ];
    uint32_t e0 = edge0.vertices[0];
    uint32_t e1 = edge0.vertices[1];
    face_vrts[fv_ind++] = e0;
    face_vrts[fv_ind++] = e1;

    // Find the common endpoint between first and second edge.
    BSPedge& edge1 = edges[ face.edges[1] ];
    uint32_t link_vrt = consecEdges_common_endpt(e0, e1, edge1.vertices[0],
                                                         edge1.vertices[1]);
    if(link_vrt==e0){
      face_vrts[0] = e1;
      face_vrts[1] = e0;
    }

    // Walk the boundary and add the endpoint != link_vrt.
    for(uint64_t e=1; e<face.edges.size()-1; e++){
      BSPedge& edge = edges[ face.edges[e] ];

      if(link_vrt == edge.vertices[0]) link_vrt = edge.vertices[1];
      else                             link_vrt = edge.vertices[0];

      face_vrts[fv_ind++] = link_vrt;
    }
}

//  Input: a BSPcell: cell,
//         vector of edges indices type: cell_edges,
//         vector of vertices indices type: cell_vrts.
// Output: by using cell_edges returns the indices (w.r.t. vector edges)
//         of cell's edges,
//         by using cell_vrts returns the indices (w.r.t. vector vertices)
//         of cell's vertices,
void BSPcomplex::fill_cell_locDS(BSPcell& cell, vector<uint64_t>& cell_edges,
                                 vector<uint32_t>& cell_vrts){

  uint64_t edge_ind, ce_ind=0, cv_ind=0;
  uint32_t e0, e1;
  for(uint64_t f=0; f<cell.faces.size(); f++){
    BSPface& face = faces[ cell.faces[f] ];
    for(uint64_t e=0; e<face.edges.size(); e++){
        edge_ind = face.edges[e];
        BSPedge& edge = edges[edge_ind];

        if(edge_visit[edge_ind]==0){
          // Fill cell_edges.
          cell_edges[ce_ind++] = edge_ind;
          edge_visit[edge_ind]=1;

          e0 = edge.vertices[0];
          e1 = edge.vertices[1];

          // Fill cell_vrts.
          if(vrts_visit[e0]==0){
            cell_vrts[cv_ind++] = e0;
            vrts_visit[e0]=1;
          }
          if(vrts_visit[e1]==0){
            cell_vrts[cv_ind++] = e1;
            vrts_visit[e1]=1;
          }
        }

    }
  }

  // Reset edge_visit and vrts_visit.
  for(uint64_t e=0; e<cell_edges.size(); e++) edge_visit[cell_edges[e]]=0;
  for(uint64_t v=0; v<cell_vrts.size(); v++) vrts_visit[cell_vrts[v]]=0;
}

//  Input: a BSPface: face,
//         indices of two edge endpoints (w.r.t. vector vertices): u, v,
// Output: if exists returns the index of a BSPedge with endpoints u and v
//         that belong to faces[face], otherwise return UINT64_MAX.
inline uint64_t BSPcomplex::find_face_edge(const BSPface& face,
                                           uint32_t v, uint32_t u){

  #ifdef DEBUG_BSP
  if(face.edges.size()==0)
    printf("\n[BSP.cpp]find_face_edge: ERROR face have no edges.\n");
  #endif

  uint64_t edge_ind;
  for(uint64_t e=0; e<face.edges.size(); e++){
    edge_ind = face.edges[e];
    BSPedge& edge = edges[edge_ind];
    if(SAME_EDGE_ENDPTS(u, v, edge.vertices[0], edge.vertices[1]) )
      return edge_ind;
  }

  #ifdef DEBUG_BSP
  printf("[BSP.cpp]BSPcomplex::find_face_edge: "
         "WARNING no match for edge <%u,%u> with this face\n", u, v);
  #endif

  return UINT64_MAX;  // Never reached if <u,v> belong to the BSPcell.
}

//
//
uint64_t BSPcomplex::count_cellFaces_inc_cellVrt(const BSPcell& cell, uint32_t v){
  uint64_t k=0;
  for(const uint64_t fi : cell.faces)
      for(const uint64_t ei : faces[fi].edges)
          if(edges[ei].vertices[0] == v || edges[ei].vertices[1] == v) k++;
  return k / 2;
}

//
//
void BSPcomplex::cell_VFrelation(const BSPcell& cell, uint32_t v,
                                 vector<uint64_t>& v_incFaces_ind){
  uint64_t k=0;
  for(const uint64_t fi : cell.faces)
      for(const uint64_t ei : faces[fi].edges)
          if(edges[ei].vertices[0] == v || edges[ei].vertices[1] == v)
          {
            v_incFaces_ind[k++] = fi;
            break;
          }
}

//
//
void BSPcomplex::COMPL_cell_VFrelation(const BSPcell& cell, uint32_t v,
                                      vector<uint64_t>& v_NOT_incFaces_ind){
    uint64_t k = 0;
    for(const uint64_t fi : cell.faces){
        bool has_edge = false;
        for(const uint64_t ei : faces[fi].edges)
            if(edges[ei].vertices[0] == v || edges[ei].vertices[1] == v){
              has_edge = true;
              break;
            }
        if(!has_edge) v_NOT_incFaces_ind[k++] = fi;
    }
}

//
//
bool BSPcomplex::is_virtual(uint32_t constr_ind){
  return (constr_ind >= first_virtual_constraint);
}

// -- Geometric Predicates ---------------------------------------------------

// Returns TRUE if v either is a vertex of the plane p0, p1, p2, or it was built
// by intersecting such a plane with other simplexes.
bool isVertexBuiltFromPlane(const genericPoint* v,
                            const explicitPoint3D *p0,
                            const explicitPoint3D *p1,
                            const explicitPoint3D *p2){
    if(v->isExplicit3D()){
      return ((v == p0) || (v == p1) || (v == p2));
    }
    else if(v->isLPI()){
          const implicitPoint3D_LPI& lpi = v->toLPI();
          return (
            (((p0 == &lpi.P()) && (p1 == &lpi.Q())) || ((p1 == &lpi.P()) && (p0 == &lpi.Q()))) ||
            (((p1 == &lpi.P()) && (p2 == &lpi.Q())) || ((p2 == &lpi.P()) && (p1 == &lpi.Q()))) ||
            (((p2 == &lpi.P()) && (p0 == &lpi.Q())) || ((p0 == &lpi.P()) && (p2 == &lpi.Q()))) ||
            ((p0 == &lpi.R()) && (p1 == &lpi.S()) && (p2 == &lpi.T()))
            );
          }
          else{
            // TPI
            const implicitPoint3D_TPI& tpi = v->toTPI();
            return (((p0 == &tpi.V1()) && (p1 == &tpi.V2()) && (p2 == &tpi.V3())) ||
                   ((p0 == &tpi.W1()) && (p1 == &tpi.W2()) && (p2 == &tpi.W3())) ||
                   ((p0 == &tpi.U1()) && (p1 == &tpi.U2()) && (p2 == &tpi.U3())));
          }
}


//  Input: vector of vertices indices (w.r.t. vector vertices): vrts_inds,
//         3 indices of vertices that define a plane: plane_pt0, plane_pt1,
//         plane_pt2.
// Output: by using the global vector vrts_orBin returns the orientations of
//         each point of vrts_inds w.r.t. the plane for
//         {plane_pt0, plane_pt1, plane_pt2}.
void BSPcomplex::vrts_orient_wrtPlane(const vector<uint32_t>& vrts_inds,
                uint32_t plane_pt0, uint32_t plane_pt1, uint32_t plane_pt2,
                uint32_t count){

   const explicitPoint3D& p0 = vertices[ plane_pt0 ]->toExplicit3D();
   const explicitPoint3D& p1 = vertices[ plane_pt1 ]->toExplicit3D();
   const explicitPoint3D& p2 = vertices[ plane_pt2 ]->toExplicit3D();

   for(uint32_t v=0; v<vrts_inds.size(); v++){
      genericPoint* vrt = vertices[vrts_inds[v]];
      if(isVertexBuiltFromPlane(vrt, &p0, &p1, &p2)) vrts_orBin[vrts_inds[v]]=0;
      else{
        vrts_orBin[vrts_inds[v]] = genericPoint::orient3D(*vrt, p0, p2, p1);

      }
    }
}


//  Input:
// Output:
inline void BSPcomplex::count_vrt_orBin(const vector<uint32_t>& inds,
                                uint32_t* pos, uint32_t* neg, uint32_t* zero){

  (*pos)=0;
  (*neg)=0;
  (*zero)=0;

  for(uint32_t i=0; i<inds.size(); i++)
    if(vrts_orBin[inds[i]]==0)       (*zero)++;
    else if(vrts_orBin[inds[i]]==1)  (*pos)++;
    else if(vrts_orBin[inds[i]]==-1) (*neg)++;
    #ifdef DEBUG_BSP
    else printf("[BSP.cpp]BSPcomplex::count_vrt_orBin: ERROR "
                "wrong access to vrt_orBin[%u]\n", inds[i]);
    #endif
}

//  Input: a BSPedge: edge,
//         vector of the inices of the vertices of the BSPcell to which the
//         BSPedge belong to: cell_vrts.
// Output: true if the constraint intersects the edge interior,
//         false otherwise.
inline bool BSPcomplex::constraint_innerIntersects_edge(const BSPedge& e,
                                            const vector<uint32_t>& cell_vrts){
  return OPPOSITE_SIGNE(vrts_orBin[ e.vertices[0] ], vrts_orBin[ e.vertices[1] ]);
}

//  Input: vector of the inices of the vertices of the BSPface: face_vrts.
// Output: true if the constraint intersects the face interior,
//         false otherwise.
inline bool BSPcomplex::constraint_innerIntersects_face(
                                             const vector<uint32_t>& face_vrts){

  // Face vertices disposition w.r.t. constraint-plane.
  uint32_t vrtsOVER, vrtsUNDER, vrtsON;
  count_vrt_orBin(face_vrts, &vrtsOVER, &vrtsUNDER, &vrtsON);

  // Check if faces[face_ind] has to be splitted
  // (at least 2 vertices with non-zero opposite cell_vrts_orient)
  return (vrtsUNDER > 0 && vrtsOVER > 0);

}

//
//
bool BSPcomplex::coplanar_constraint_innerIntersects_face(const std::vector<uint64_t>& fedges,
                                                          const uint32_t tri[3], int xyz){
    // ASSUMPTION: No face vertices are in the interior of the constraint

    // Process: intersection must be bound by at least three unaligned points on the boundary of tri
    // These points must be on either two different tri_edges,
    // or one of a vertex and two on the opposite edge, or on three vertices

    int mask = 0;

    const BSPedge& edge0 = edges[fedges.back()];
    const BSPedge& edge1 = edges[fedges[0]];
    uint32_t vid_0 = consecEdges_common_endpt(edge0.vertices[0], edge0.vertices[1],
                                              edge1.vertices[0], edge1.vertices[1]);

    // 1) Cerca i tre vertici sul bordo della faccia
    // Per ogni tri_vertex
    //  cerca vertici coincidenti in faccia: se trovi attiva campo in mask e passa al vertice successivo
    //  se campo in mask non è gia attivo, cerca edge della faccia che lo contengono:
    //     se trovi attiva campo in mask e passa al vertice successivo
    // Se mask == 7 torna true
    for (int i = 0; i < 3; i++)
    {
        uint32_t vid = vid_0;
        for (uint64_t e = 0; e < fedges.size(); e++) {
            const BSPedge& edge = edges[fedges[e]];
            if (vid == edge.vertices[0]) vid = edge.vertices[1];
            else vid = edge.vertices[0];
            if (tri[i] == vid) { mask |= (1 << i); break; }
        }
    }
    if (mask == 7) return true;

    for (int i = 0; i < 3; i++) if (!(mask & (1 << i)))
    {
        for (uint64_t e = 0; e < fedges.size(); e++) {
            const BSPedge& edge = edges[fedges[e]];
            const genericPoint* ev1 = vertices[edge.vertices[0]];
            const genericPoint* ev2 = vertices[edge.vertices[1]];
            if (genericPoint::pointInInnerSegment(*vertices[tri[i]], *ev1, *ev2, xyz)) { mask |= (1 << i); break; }
        }
    }
    if (mask == 7) return true;

    // 2) Cerca vertici della faccia su edge del constraint
    //  Per ogni tri_edge
    //   cerca vertici in faccia che stanno innerEdge: se trovi attiva campi in mask e edge successivo
    //   se campi in mask non sono gia attivi, cerca edge della faccia che lo innerCrossano: se trovi attiva campi in mask e passa a edge successivo
    // Se mask == 7 torna true altrimenti false
    for (int i = 0; i < 3; i++)
    {
        uint32_t ti0 = (i + 1) % 3;
        uint32_t ti1 = (i + 2) % 3;
        if ((mask & (1 << ti0)) && (mask & (1 << ti1))) continue;
        uint32_t vid = vid_0;
        for (uint64_t e = 0; e < fedges.size(); e++) {
            const BSPedge& edge = edges[fedges[e]];
            if (vid == edge.vertices[0]) vid = edge.vertices[1];
            else vid = edge.vertices[0];
            const genericPoint* ev1 = vertices[tri[ti0]];
            const genericPoint* ev2 = vertices[tri[ti1]];
            if (genericPoint::pointInInnerSegment(*vertices[vid], *ev1, *ev2, xyz)) { mask |= (1 << ti0); mask |= (1 << ti1); break; }
        }
    }
    if (mask == 7) return true;

    for (int i = 0; i < 3; i++)
    {
        uint32_t ti0 = (i + 1) % 3;
        uint32_t ti1 = (i + 2) % 3;
        if ((mask & (1 << ti0)) && (mask & (1 << ti1))) continue;
        for (uint64_t e = 0; e < fedges.size(); e++) {
            const BSPedge& edge = edges[fedges[e]];
            const genericPoint* ev1 = vertices[edge.vertices[0]];
            const genericPoint* ev2 = vertices[edge.vertices[1]];
            const genericPoint* fv1 = vertices[tri[ti0]];
            const genericPoint* fv2 = vertices[tri[ti1]];
            if (genericPoint::innerSegmentsCross(*ev1, *ev2, *fv1, *fv2, xyz)) return true;
        }
    }

    return false;
}

// Returns 1 if point is on the boundary of the triangle, 2 if it is in the interior, 0 otherwise
int localizedPointInTriangle(const genericPoint& P, const genericPoint& A,
                             const genericPoint& B, const genericPoint& C, int xyz)
{
    int o1, o2, o3;
    if (xyz == 2)
    {
        o1 = genericPoint::orient2Dxy(P, A, B);
        o2 = genericPoint::orient2Dxy(P, B, C);
        o3 = genericPoint::orient2Dxy(P, C, A);
    }
    else if (xyz == 0)
    {
        o1 = genericPoint::orient2Dyz(P, A, B);
        o2 = genericPoint::orient2Dyz(P, B, C);
        o3 = genericPoint::orient2Dyz(P, C, A);
    }
    else
    {
        o1 = genericPoint::orient2Dzx(P, A, B);
        o2 = genericPoint::orient2Dzx(P, B, C);
        o3 = genericPoint::orient2Dzx(P, C, A);
    }
    return ((o1 >= 0 && o2 >= 0 && o3 >= 0) || (o1 <= 0 && o2 <= 0 && o3 <= 0))  +
           ((o1 > 0 && o2 > 0 && o3 > 0) ||  (o1 < 0 && o2 < 0 && o3 < 0)   );
}

//-Upload Delaunay triangolation----------------------

//  Input: pointer to mesh,
//         vector of tetrahedra index type: new_order.
// Output: by using new_order returns new cells indexing (the same as Delaunay
//         tetrahedra indexing, but without ghost-tets).
uint64_t BSPcomplex::removing_ghost_tets(const TetMesh* mesh,
                                         vector<uint64_t>& new_order){
  // new_order have as many element as mesh->tet_num,
  // new_order[i-th tet] =
  //    i - (num of ghost_tet between 0 and i) IF i-th tet is non-ghost
  //    UINT64_MAX                             IF i-th tet is ghost
  uint64_t ghost_tet_count = 0;
  for(uint64_t tet_ind=0; tet_ind<mesh->tet_num; tet_ind++){
      if(IS_GHOST_TET(tet_ind)){
        new_order[tet_ind] = UINT64_MAX;
        ghost_tet_count++;
      }
      else  new_order[tet_ind] = tet_ind - ghost_tet_count;
  }
  return mesh->tet_num - ghost_tet_count;
}

//  Input: pointer to mesh,
//         the 2 endpoints of a tetrahedron edge: e0, e1,
//         the index of the tetrahedron (tet) to which edge belongs: tet_ind,
//         new cells indexing (the same as tetrahedra indexing, but without
//         ghost-tets): new_order.
// Output: the index of the edge (w.r.t. the vector edges) of the
//         edge <endpt0, endpt1>.
// Note. tetrahedra are added in crescent index order.
uint64_t BSPcomplex::add_tetEdge(const TetMesh* mesh, uint32_t e0, uint32_t e1,
                                 uint64_t tet_ind,
                                 const vector<uint64_t>& new_order){
    // Tetrahedra incident in <endpt0,endpt1>
    uint32_t edge_ends[2] = { e0, e1 };
    uint64_t num_incTet;
    uint64_t* incTet = mesh->ETrelation(edge_ends, tet_ind, &num_incTet);
    // Note. ETrelation can return ghost-tet.

    uint64_t min = UINT64_MAX;
    for (uint32_t i = 0; i < num_incTet; i++)
        if (!IS_GHOST_TET(incTet[i]) && incTet[i] < min) min = incTet[i];

    free(incTet);

    if(min == tet_ind){
      // None of the tetrahedra incident in <e0,e1> has been visited,
      // furthermore the edge <e0,e1> do not belongs to a face of tet_ind
      // (as a consequence of the constructor BSPcomplex).
      // A new BSPedge has to be created.
      edges.push_back( BSPedge(e0,e1, e0,e1) );
      return edges.size()-1;
    }

    uint64_t edge_ind;
    for(uint64_t f=0; f<4; f++){        // Note. also with f<3 sholud work.
      BSPface& face = faces[ cells[ new_order[min] ].faces[f] ];
      for(uint64_t e=0; e<3; e++){
        edge_ind = face.edges[e];
        BSPedge& edge = edges[edge_ind];
        if(SAME_EDGE_ENDPTS(e0, e1, edge.vertices[0], edge.vertices[1]) ) break;
      }
      BSPedge& edge = edges[edge_ind];
      if(SAME_EDGE_ENDPTS(e0, e1, edge.vertices[0], edge.vertices[1]) ) break;
    }
    return edge_ind;
}

//  Input: three face (triangle) vertices: v0, v1, v2,
//         index of a BSPcell (w.r.t. vector cells) owning the face: cell_ind,
//         index of a BSPcell (w.r.t. vector cells) faced at the previous one
//         throught the face, if it does not exists (i.e. the previous cell is
//         a complex boundary cell) then it is UINT64_MAX: adjCell_ind.
// Output: returns the index (w.r.t. the vector faces) of the new BSPface.
inline uint64_t BSPcomplex::add_tetFace(uint32_t v0, uint32_t v1, uint32_t v2,
                                      uint64_t cell_ind, uint64_t adjCell_ind){
  // Note. adjCell==UINT64_MAX -> convex-hull face.
  faces.push_back( BSPface(v0,v1,v2, cell_ind,adjCell_ind) );
  uint64_t face_ind = faces.size() -1;
  cells[cell_ind].faces.push_back(face_ind);
  if(!IS_GHOST_CELL(adjCell_ind))  cells[adjCell_ind].faces.push_back(face_ind);

  return face_ind;
}

//  Input: the index of a Delaunay mesh tetrahedron: tet_ind,
//         the index of a Delaunay mesh tetrahedron adjacent to tet: adjTet_ind,
//         the index of the BSPcell corresponding to the adjacent tetrahedron,
//         (in the case it is a ghost-tet it is UINT64_MAX): adjCell_ind.
// Output: returns true if the face between the two input tetrahedra has not
//         been turned into a BSPface yet, false otherwise.
inline bool BSPcomplex::tet_face_isNew(uint64_t tet_ind, uint64_t adjTet_ind,
                                       uint64_t adjCell_ind){
  // The face is the common one between tetrahedra indexed as tet and adjTet.
  // Assuming that the tetrahedron indexed as tet has not been visited yet.
  // Check if:
  // - adjTet has not been visited yet -> new face.
  // - adjTet has been already visited -> face already exists.(not new)
  // - adjTet is ghost (i.e. adjCell is UINT64_MAX by new_order) -> new face.
  return ( adjTet_ind > tet_ind || IS_GHOST_CELL(adjCell_ind) );
}

//
//
inline void BSPcomplex::fill_face_colour(uint64_t tet_ind, uint64_t face_ind,
                                         const uint32_t** map_fi,
                                         const uint32_t* num_map_fi){

  if(num_map_fi[tet_ind] == 0) faces[face_ind].colour = WHITE;
  else{
    // Count non-virtual constraints.
    uint32_t n = 0;
    for(uint32_t cc=0; cc<num_map_fi[tet_ind]; cc++)
      if(!is_virtual(map_fi[tet_ind][cc])) n++;

    if(n == 0) faces[face_ind].colour = WHITE;
    else{
      faces[face_ind].colour = GREY;
      faces[face_ind].coplanar_constraints.resize(n);
      uint32_t pos = 0;
      for(uint32_t cc=0; cc<num_map_fi[tet_ind]; cc++)
        if(!is_virtual(map_fi[tet_ind][cc]))
          faces[face_ind].coplanar_constraints[pos++] = map_fi[tet_ind][cc];
    }

  }
}


//bool faceHasCorrectOrientation(BSPcomplex* cpx, uint64_t f_id)
//{
//    const BSPface& f = cpx->faces[f_id];
//    const uint64_t c_id = f.conn_cells[0];
//    BSPcell& c = cpx->cells[c_id];
//    const uint64_t e0_id = f.edges[0];
//    const uint64_t e1_id = f.edges[1];
//    const uint64_t e2_id = f.edges[2];
//    const BSPedge& e0 = cpx->edges[e0_id];
//    const BSPedge& e1 = cpx->edges[e1_id];
//    const BSPedge& e2 = cpx->edges[e2_id];
//    const uint32_t v0_id = cpx->getFaceVertex(f, 0);
//    const uint32_t v1_id = cpx->getFaceVertex(f, 1);
//    genericPoint* v0 = cpx->vertices[v0_id];
//    genericPoint* v1 = cpx->vertices[v1_id];
//    genericPoint* v2;
//
//    size_t i;
//    for (i = 2; i < f.edges.size(); i++)
//    {
//        const uint32_t v2_id = cpx->getFaceVertex(f, i);
//        v2 = cpx->vertices[v2_id];
//        if (genericPoint::misaligned(*v0, *v1, *v2)) break;
//    }
//    if (i == f.edges.size()) ip_error("Degenerate face\n");
//
//    uint64_t num_cellEdges = UINT64_MAX;
//    uint32_t num_cellVrts = cpx->count_cellVertices(c, &num_cellEdges);
//    vector<uint32_t> cell_vrts(num_cellVrts, UINT32_MAX);
//    cpx->list_cellVertices(c, num_cellEdges, cell_vrts);
//    for (uint32_t vi : cell_vrts) if (!cpx->faceHasVertex(f, vi))
//    {
//        genericPoint* ov = cpx->vertices[vi];
//
//        int ori = genericPoint::orient3D(*ov, *v0, *v1, *v2);
//        if (ori == 0) continue;
//        return (ori < 0);
//    }
//    ip_error("Degenerate cell\n");
//}
//
//// Returns the index of the v_ind'th vertex in f.
//// Vertex 'i' is the common vertex between edge 'i' and edge 'i+1'%num_edges
//uint32_t BSPcomplex::getFaceVertex(const BSPface& f, uint32_t v_ind)
//{
//    const BSPedge& e0 = edges[f.edges[v_ind]];
//    const BSPedge& e1 = edges[f.edges[(v_ind + 1) % (f.edges.size())]];
//    return consecEdges_common_endpt(e0.vertices[0], e0.vertices[1], e1.vertices[0], e1.vertices[1]);
//}
//
//bool BSPcomplex::faceHasVertex(const BSPface& f, uint32_t v_ind)
//{
//    for (uint64_t eid : f.edges)
//    {
//        const BSPedge& e = edges[eid];
//        if (e.vertices[0] == v_ind || e.vertices[1] == v_ind) return true;
//    }
//    return false;
//}

//
// Fills the data scruture with the information of the Delauany mesh.
BSPcomplex::BSPcomplex(const TetMesh* mesh, const constraints_t* _constraints,
                       const uint32_t** map, const uint32_t* num_map,
                       const uint32_t** map_f0, const uint32_t* num_map_f0,
                       const uint32_t** map_f1, const uint32_t* num_map_f1,
                       const uint32_t** map_f2, const uint32_t* num_map_f2,
                       const uint32_t** map_f3, const uint32_t* num_map_f3 ){

  // Uploading the vertices of the mesh
  vertices.resize(mesh->num_vertices);
  for(uint32_t vrt=0; vrt<mesh->num_vertices; vrt++)
    vertices[vrt] = new explicitPoint3D(mesh->vertices[vrt].coord[0],
                                            mesh->vertices[vrt].coord[1],
                                            mesh->vertices[vrt].coord[2]);

  // Initialize vrts_orBin:
  // since orient3D can be -1, 0 or 1 all elements are set to 2.
  vrts_orBin.resize(mesh->num_vertices, 2);

  // Uploading the constraints (the last num_virtual_triangles constraints are virtual.)
  first_virtual_constraint = _constraints->num_triangles - _constraints->num_virtual_triangles;
  constraints_vrts.resize(3*_constraints->num_triangles);
  constraint_group.resize(_constraints->num_triangles);
  for(uint32_t i=0; i < _constraints->num_triangles; i++){
    constraints_vrts[3*i   ] = _constraints->tri_vertices[3*i   ];
    constraints_vrts[3*i +1] = _constraints->tri_vertices[3*i +1];
    constraints_vrts[3*i +2] = _constraints->tri_vertices[3*i +2];
    constraint_group[i] = _constraints->constr_group[i];
  }

  // Establish new tetrahedtra-(cell) indexing: only non-ghost cell are indexed.
  vector<uint64_t> new_order(mesh->tet_num, UINT64_MAX);
  uint64_t cell_num = removing_ghost_tets(mesh, new_order);

  // Creating as many empty cells as the number of non-ghost tet_
  cells.resize(cell_num);
  edges.reserve(cell_num + mesh->num_vertices);
  faces.reserve(cell_num * 2);


  // Loading the cells, creating faces and eadges of the BSP:
  // cells -> the non-ghost tetrahedra in the mesh,
  // faces -> the faces of the non-ghost tetrahedra in the mesh,
  // edges -> the edges of the non-ghost tetrahedra in the mesh.
  for(uint64_t tet_ind=0; tet_ind<mesh->tet_num; tet_ind++){
    // Here each BSPcell is a non-ghost tetrahedron of the mesh:
    // consider a tetrahedron (tet) whose index is tet_ind.
    uint64_t cell_ind = new_order[tet_ind];
    if( IS_GHOST_CELL(cell_ind) )   continue; // Skip ghost-tet.

    // Create BSPcells from tetrahedra by following increasing indexing:
    // all non-ghost tetrahedra which have index lower than tet_ind
    // have been already turned into BSP cells.

    // Constraints improperly intersecated by tet.
    if(num_map[tet_ind]>0){
      cells[cell_ind].constraints.resize( num_map[tet_ind] );
      for(uint32_t i=0; i<num_map[tet_ind]; i++)
        cells[cell_ind].constraints[i] = map[tet_ind][i];
    }

    // Adding BSPface and BSPedges to create a BSPcell conformed to tetrahedron.
    uint32_t v[4]; // Indices of tet vertices.
    v[0] = mesh->tet_node[4 * tet_ind];
    v[1] = mesh->tet_node[4 * tet_ind + 1];
    v[2] = mesh->tet_node[4 * tet_ind + 2];
    v[3] = mesh->tet_node[4 * tet_ind + 3];

    uint64_t face_ind, adjCell_ind, adjTet_ind;
    uint64_t tet_edge[6];
    // --- face <v0,v1,v2> -----------------------------
    adjTet_ind = mesh->tet_neigh[4 * tet_ind + 3] >> 2;
    adjCell_ind = new_order[adjTet_ind];
    if (tet_face_isNew(tet_ind, adjTet_ind, adjCell_ind)) {
        face_ind = add_tetFace(v[0], v[1], v[2], cell_ind, adjCell_ind);
        // At most three edges may have to be created <v0,v1>, <v1,v2>, <v2,v0>.
        tet_edge[0] = add_tetEdge(mesh, v[0], v[1], tet_ind, new_order);
        assigne_edge_to_face(tet_edge[0], face_ind);
        tet_edge[2] = add_tetEdge(mesh, v[2], v[0], tet_ind, new_order);
        assigne_edge_to_face(tet_edge[2], face_ind);
        tet_edge[1] = add_tetEdge(mesh, v[1], v[2], tet_ind, new_order);
        assigne_edge_to_face(tet_edge[1], face_ind);
        // Color and coplanar-constraints
        fill_face_colour(tet_ind, face_ind, map_f3, num_map_f3);
    }
    else {
        face_ind = faceSharedWithCell(cell_ind, adjCell_ind);
        BSPface& face = faces[face_ind];
        tet_edge[0] = find_face_edge(face, v[0], v[1]);
        tet_edge[1] = find_face_edge(face, v[1], v[2]);
        tet_edge[2] = find_face_edge(face, v[2], v[0]);
    }
    // --- face <v3,v0,v1> -----------------------------
    adjTet_ind = mesh->tet_neigh[4 * tet_ind + 2] >> 2;
    adjCell_ind = new_order[adjTet_ind];
    if (tet_face_isNew(tet_ind, adjTet_ind, adjCell_ind)) {
        face_ind = add_tetFace(v[3], v[0], v[1], cell_ind, adjCell_ind);
        // At most two edges may have to be created <v3,v0>, <v1,v3>.
        tet_edge[4] = add_tetEdge(mesh, v[1], v[3], tet_ind, new_order);
        assigne_edge_to_face(tet_edge[4], face_ind);
        tet_edge[3] = add_tetEdge(mesh, v[3], v[0], tet_ind, new_order);
        assigne_edge_to_face(tet_edge[3], face_ind);
        // <v0,v1> is tet_edge[0].
        assigne_edge_to_face(tet_edge[0], face_ind);
        // Color and coplanar-constraints
        fill_face_colour(tet_ind, face_ind, map_f2, num_map_f2);
    }
    else {
        face_ind = faceSharedWithCell(cell_ind, adjCell_ind);
        BSPface& face = faces[face_ind];
        tet_edge[3] = find_face_edge(face, v[3], v[0]);
        tet_edge[4] = find_face_edge(face, v[1], v[3]);
    }
    // --- face <v2,v3,v0> -----------------------------
    adjTet_ind = mesh->tet_neigh[4 * tet_ind + 1] >> 2;
    adjCell_ind = new_order[adjTet_ind];
    if (tet_face_isNew(tet_ind, adjTet_ind, adjCell_ind)) {
        face_ind = add_tetFace(v[2], v[3], v[0], cell_ind, adjCell_ind);
        // At most one edges may have to be created <v2,v3>.
        tet_edge[5] = add_tetEdge(mesh, v[2], v[3], tet_ind, new_order);
        assigne_edge_to_face(tet_edge[5], face_ind);
        // <v3,v0> is tet_edge[3], <v0,v2> is tet_edge[2].
        assigne_edge_to_face(tet_edge[2], face_ind);
        assigne_edge_to_face(tet_edge[3], face_ind);
        // Color and coplanar-constraints
        fill_face_colour(tet_ind, face_ind, map_f1, num_map_f1);
    }
    else {
        face_ind = faceSharedWithCell(cell_ind, adjCell_ind);
        tet_edge[5] = find_face_edge(faces[face_ind], v[2], v[3]);
    }
    // --- face <v1,v2,v3> -----------------------------
    adjTet_ind = mesh->tet_neigh[4 * tet_ind] >> 2;
    adjCell_ind = new_order[adjTet_ind];
    if (tet_face_isNew(tet_ind, adjTet_ind, adjCell_ind)) {
        face_ind = add_tetFace(v[1], v[2], v[3], cell_ind, adjCell_ind);
        // No edges have to be created.
        // <v1,v2> is tet_edge[1], <v2,v3> is tet_edge[5], <v3,v1> is tet_edge[4].
        assigne_edge_to_face(tet_edge[1], face_ind);
        assigne_edge_to_face(tet_edge[5], face_ind);
        assigne_edge_to_face(tet_edge[4], face_ind);
        // Color and coplanar-constraints
        fill_face_colour(tet_ind, face_ind, map_f0, num_map_f0);
    }
  }

  // Initialize visit-flag vectors: all the values are set to upper-limit.
  vrts_visit.resize(vertices.size(), 0);
  edge_visit.resize(edges.size(), 0);


  // Verify that conn_cell[0] is below the face for every face
  //for (size_t fid = 0; fid < faces.size(); fid++)
  //    if (!faceHasCorrectOrientation(this, fid))
  //        ip_error("Wrong orientation\n");
}

//-BSP subdivision----------------------

//  Input: index of a BSPedge (w.r.t. vector face.edges): edge_face_ind,
//         index of two BSPfaces (w.r.t. vector faces): face_ind, newFace_ind.
// Output: nothing.
// Note. edge is removed from faces[face_ind] and assigned to faces[newFace_ind].
inline void BSPcomplex::move_edge(uint64_t edge_face_ind, uint64_t face_ind,
                                  uint64_t newFace_ind){
   uint64_t edge_ind = faces[face_ind].edges[edge_face_ind];
   edges[edge_ind].conn_face_0 = newFace_ind;
   faces[newFace_ind].edges.push_back(edge_ind);
   faces[face_ind].removeEdge(edge_face_ind);
}

//  Input: index of a BSPface (w.r.t. vector cells[cell].faces): face_cell_ind,
//         index of two BSPcells (w.r.t. vector cells): cell_ind, newCell_ind.
// Output: nothing.
// Note. face is removed from cells[cell_ind] and assigned to cells[newCell_ind].
inline void BSPcomplex::move_face(uint64_t face_cell_ind, uint64_t cell_ind,
                                  uint64_t newCell_ind){
   uint64_t face_ind = cells[cell_ind].faces[face_cell_ind];
   faces[face_ind].exchange_conn_cell(cell_ind, newCell_ind);
   cells[newCell_ind].faces.push_back(face_ind);
   cells[cell_ind].removeFace(face_cell_ind);
}

//  Input: index of a constraint (w.r.t. vector cells[cell].constraints):
//         constr_cell_ind,
//         index of a BSPcells (w.r.t. vector cells): cell_ind.
// Output: nothing.
inline void BSPcomplex::remove_constraint(uint32_t constr_cell_ind,
                                          uint64_t cell_ind){
  BSPcell& cell = cells[cell_ind];
  size_t last_ind = cell.constraints.size()-1;
  if(constr_cell_ind != last_ind)
    cell.constraints[constr_cell_ind] = cell.constraints[last_ind];

  cell.constraints.pop_back();
}

// This function is used to identify the edges that have been marked as to
// remove (with UINT64_MAX) from a certain faces[face].edge vector
inline bool remove_edge_from_face(uint64_t edge_face_ind){
  return edge_face_ind == UINT64_MAX;
}

//  Input: index of the splitted BSPface (w.r.t. vector faces) that is going
//         to be turned into the downSubface: face_ind,
//         index of the upSubface (w.r.t. vector faces): newFace_ind.
// Output: nothing.
void BSPcomplex::edgesPartition(uint64_t face_ind, uint64_t newFace_ind){
  // Partition of the edges between upSubface and downSubface.
  BSPface& face = faces[face_ind];

  // Search the first edge having first vertex ==0 and second vertex <0
  // (in face order). This will be the first in upFace.
  uint64_t e;
  for (e = 0; e < face.edges.size(); e++) {
      const uint64_t edge_ind = face.edges[e];
      const uint64_t next_edge_ind = face.edges[((e + 1) == face.edges.size()) ? (0) : (e + 1)];
      const BSPedge& edge = edges[edge_ind];
      const BSPedge& nedge = edges[next_edge_ind];
      const uint32_t comm_vert = (edge.vertices[0] == nedge.vertices[0] || edge.vertices[0] == nedge.vertices[1]) ? 0 : 1;
      if (vrts_orBin[edge.vertices[comm_vert]] < 0 && vrts_orBin[edge.vertices[!comm_vert]] == 0) break;
  }
  if (e == face.edges.size()) ip_error("pippo1\n");

  std::rotate(face.edges.begin(), face.edges.begin() + e, face.edges.end());

  // Search the last edge belonging to upFace
  for (e = 1; e < face.edges.size(); e++) {
      const uint64_t edge_ind = face.edges[e];
      const BSPedge& edge = edges[edge_ind];
      if (vrts_orBin[edge.vertices[0]] == 0 || vrts_orBin[edge.vertices[1]] == 0) break;
  }
  if (e == face.edges.size()) ip_error("pippo2\n");
  e++;
  // Move tail edges to new face
  faces[newFace_ind].edges.assign(face.edges.begin() + e, face.edges.end());
  face.edges.resize(e);
  for (uint64_t ei : faces[newFace_ind].edges) edges[ei].conn_face_0 = newFace_ind;

  //std::move(face.edges.begin() + e, face.edges.end(), faces[newFace_ind].edges.begin());

  //for(uint64_t e=0; e<face.edges.size(); e++){
  //  const uint64_t edge_ind = face.edges[e];
  //  BSPedge& edge = edges[edge_ind];

  //  if(vrts_orBin[ edge.vertices[0] ]>0 || vrts_orBin[ edge.vertices[1] ] >0 )
  //      move_edge(e, face_ind, newFace_ind);
  //}

  //// Remove all edges of face.edges marked as UINT64_MAX
  //face.edges.erase(
  //  std::remove_if(face.edges.begin(), face.edges.end(), remove_edge_from_face),
  //  face.edges.end() );
}

//  Input: index of the splitted BSPcell (w.r.t. vector cells) that is going
//         to be turned into the downSubcell: cell_ind,
//         index of the upSubcell (w.r.t. vector cells): newCell_ind,
//         vector of the indices of the vertices (w.r.t. vector vertices)
//         of the BSPcell to which the BSPface belong to: cell_vrts.
// Output: nothing.
void BSPcomplex::facesPartition(uint64_t cell_ind, uint64_t newCell_ind,
                                const vector<uint32_t>& cell_vrts){

  // Faces whose indices (w.r.t. vector faces) are listed in
  // cells[cell_ind].faces have to be partitioned between
  // upSubcell (i.e. cells[newCell]) and downSubcell (i.e. cells[cell]).
  BSPcell& cell = cells[cell_ind];
  uint64_t num_faces = cell.faces.size();
  uint64_t face_ind;
  for(uint64_t f = 0; f<num_faces; f++){
     face_ind = cell.faces[f];
     BSPface& face = faces[face_ind];

     vector<uint32_t> face_vrts(face.edges.size(), UINT32_MAX);
     list_faceVertices(face, face_vrts);

     // Face vertices disposition w.r.t. constraint-plane.
     uint32_t vrtsOVER, vrtsUNDER, vrtsON;
     count_vrt_orBin(face_vrts, &vrtsOVER, &vrtsUNDER, &vrtsON);

     #ifdef DEBUG_BSP_DEEP
     print_BSPface_edges(edges, face_ind, face.edges);
     print_vrt_orBin(vrts_orBin, face_vrts);
     #endif

     // It is impossible that a face has:
     // - all vertices ON the constraint-plane,
     // - two (or more) vertices on opposite sides w.r.t. the constraint-plane.

     #ifdef DEBUG_BSP
     if(vrtsOVER==0 && vrtsUNDER==0)
        printf("\n[BSP.cpp]BSPcomplex::facesPartition: ERROR face have "
               "all vertices on the constraint plane.\n");
     if(vrtsOVER>0 && vrtsUNDER>0)
        printf("\n[BSP.cpp]BSPcomplex::facesPartition: ERROR face intersects "
               "the constraint plane.\n");
     #endif

     // IF one of the face vertices is OVER the constraint-plane (vrtsOVER>0),
     // the face is assigned to the upSubcell (i.e. cells[newCell]).
     if(vrtsOVER > 0){
       move_face(f, cell_ind, newCell_ind);
       f--;
       num_faces--;
     }
     // OTHERWISE
     // faces[face_ind] goes to down-subcell (i.e. remain to cells[cell_ind])
     // indeed, all its vertices have non-positive cell_vrts_orient.

     #ifdef DEBUG_BSP_DEEP
     if(vrtsOVER > 0)
      printf("\n\tface #%llu goes to up-subcell (cell #%llu).\n",
              face_ind, newCell_ind);
     else
      printf("\n\tface #%llu goes to down-subcell (cell #%llu).\n",
              face_ind, cell_ind);
     #endif
 }
}

//  Input: index of the constraint that has divided the original BSPcell
//         cells[down_cell] in to the current sub-cells cells[up_cell] and
//         the sub-cells cells[down_cell]: ref_constr,
//         index of the down sub-cell (w.r.t. vector cells): down_cell_ind,
//         index of the up sub-cell (w.r.t. vector cells): up_cell_ind,
//         vector of the indices of the vertices (w.r.t. vector vertices)
//         of the down sub-cell (before splitting): cell_vrts,
// Output: nothing.
void BSPcomplex::constraintsPartition(uint32_t ref_constr,
                                      uint64_t down_cell_ind,
                                      uint64_t up_cell_ind,
                                      const vector<uint32_t>& cell_vrts){

  // At this point down sub-cell has all the constraints,
  // while up sub-cell has none.
  BSPcell& down_cell = cells[down_cell_ind];
  BSPcell& up_cell = cells[up_cell_ind];

  #ifdef DEBUG_BSP_DEEP
  printf("\tCurrent constraint is: ");
  print_constraint(constraints_vrts, ref_constr);
  vector<uint32_t> vrts_to_print;
  for(uint32_t v=0; v<cell_vrts.size(); v++)
    if(vrts_orBin[cell_vrts[v]] >= 0) vrts_to_print.push_back(cell_vrts[v]);
  print_BSPcell_vrts(vrts_to_print, up_cell_ind);
  vrts_to_print.clear();
  for(uint32_t v=0; v<cell_vrts.size(); v++)
    if(vrts_orBin[cell_vrts[v]] <= 0) vrts_to_print.push_back(cell_vrts[v]);
  print_BSPcell_vrts(vrts_to_print, down_cell_ind);
  printf("\tConstraints intersecting (original)cell #%llu are:\n", down_cell_ind);
  for(uint32_t c=0; c<down_cell.constraints.size(); c++){
      printf("\t");
      print_constraint(constraints_vrts, down_cell.constraints[c]);
  }
  printf("\n");
  #endif

  // 3 vertices of the ref_constraint seen as triangle (k0,k1,k2).
  uint32_t ref_constr_ID = 3*ref_constr;
  uint32_t k0 = constraints_vrts[ref_constr_ID   ];
  uint32_t k1 = constraints_vrts[ref_constr_ID +1];
  uint32_t k2 = constraints_vrts[ref_constr_ID +2];

  uint64_t num_constr = down_cell.constraints.size();
  uint32_t constr, constr_ID;
  vector<uint32_t> constr_vrts(3, UINT32_MAX);
  for(uint32_t c=0; c<num_constr; c++){
      constr = down_cell.constraints[c];
      constr_ID = 3*constr;
      constr_vrts[0] = constraints_vrts[constr_ID   ];
      constr_vrts[1] = constraints_vrts[constr_ID +1];
      constr_vrts[2] = constraints_vrts[constr_ID +2];

      // commFace_vrts disposition w.r.t. constr vertices.
      vrts_orient_wrtPlane(constr_vrts, k0, k1, k2, 2);
      uint32_t vrtsOVER, vrtsUNDER, vrtsON;
      count_vrt_orBin(constr_vrts, &vrtsOVER, &vrtsUNDER, &vrtsON);

      #ifdef DEBUG_BSP_DEEP
      printf("\n\t orient3d of constraint %u vertices w.r.t. plane for ",
             down_cell.constraints[c]);
      print_constraint(constraints_vrts, ref_constr);
      printf("\n");
      print_vrt_orBin(vrts_orBin, constr_vrts);
      printf("\n");
      #endif

      // If constr and commFace_vrts define the same plane, remove constr since
      // the cut will not produce further cell-split.
      if(vrtsOVER == 0 && vrtsUNDER == 0){
        remove_constraint(c, down_cell_ind);
        c--;
        num_constr--;

        #ifdef DEBUG_BSP_DEEP
        printf("\tConsraints %u and %u define the same plane, constraint %u have "
               "been removed.\n", ref_constr, constr, constr);
        if(vrtsON==0)
          printf("\n[BSP.cpp]BSPcomplex::constraintsPartition: ERROR vertices "
                 "of consraints %u have an undefined position wrt constraint %u.\n",
                 constr, ref_constr);
        #endif

        continue;  // jump to next constraint.
      }

      const bool up = (vrtsOVER > 0);
      const bool down = (vrtsUNDER > 0);

      #ifdef DEBUG_BSP
      if(!up && !down)
        printf("[BSP.cpp]BSPcomplex::constraintsPartition: WARNING constraint "
               "#%u will be removed from down sub-cell #%llu, there are no "
               "intersection with both sub-cells.\n", constr, down_cell_ind );
      #endif

      if(up)  up_cell.constraints.push_back(constr);
      if(!down){
        remove_constraint(c, down_cell_ind);
        c--;
        num_constr--;
      }
      // OTHERWISE
      // the constraint indexed as constr goes to down-subcell (i.e. remain
      // to cells[down_cell]).

      #ifdef DEBUG_BSP_DEEP
      if(up && down)  printf("\tconstraint #%u goes to both subcell (cell "
                             "#%llu and #%llu).\n", constr, up_cell_ind, down_cell_ind);

      if(up && !down) printf("\tconstraint #%u goes to up-subcell (cell "
                             "#%llu).\n", constr, up_cell_ind);
      if(!up && down) printf("\tconstraint #%u goes to down-subcell (cell "
                             "#%llu).\n", constr, down_cell_ind);
      #endif
  }
}

//
//
void BSPcomplex::add_edgeToOrdFaceEdges(BSPface& face, uint64_t newEdge_ind){

  BSPedge& newEdge = edges[newEdge_ind];
  uint64_t edge_ind, num_faceEdges = face.edges.size();
  uint32_t n0, n1, e0, e1;
  n0 = newEdge.vertices[0];
  n1 = newEdge.vertices[1];

  for(uint64_t e=0; e<num_faceEdges; e++){
    edge_ind = face.edges[e];
    e0 = edges[edge_ind].vertices[0];
    e1 = edges[edge_ind].vertices[1];

    if(CONSECUTIVE_EDGES(n0, n1, e0, e1) ){

      // Special case: edge is the first vector element
      if(e==0){
        BSPedge& cons = edges[ face.edges.back() ];
        if(CONSECUTIVE_EDGES(n0, n1, cons.vertices[0], cons.vertices[1]) )
          face.edges.push_back(newEdge_ind);
        else{ // cons is faces[face].edges[1]
          face.edges.push_back(edge_ind);
          face.edges[0] = newEdge_ind;
        }

        return;
      }

      // Special case: edge is the last vector element
      if(e == num_faceEdges-1){
        BSPedge& cons = edges[ face.edges[0] ];
        if(CONSECUTIVE_EDGES(n0, n1, cons.vertices[0], cons.vertices[1]) )
          face.edges.push_back(newEdge_ind);
        else{ // cons is faces[face].edges.size() -2
          face.edges.push_back(edge_ind);
          face.edges[num_faceEdges-1] = newEdge_ind;
        }

        return;
      }

      // General case
      BSPedge& cons = edges[ face.edges[e+1] ];
      if(CONSECUTIVE_EDGES(n0, n1, cons.vertices[0], cons.vertices[1]) )
        face.edges.insert(face.edges.begin() + e+1, newEdge_ind);
      else  // cons = edges[ faces[face].edges[e-1] ]
        face.edges.insert(face.edges.begin() + e, newEdge_ind);

      return;
    }
  }
}

//  Input: index of the constraint splitting the BSPface: constr,
//         index of the splitted BSPface (w.r.t. vector faces) that is going
//         to be turned into the downSubface: face_ind,
//         index of the upSubface (w.r.t. vector cells): newFace_ind,
//         vector of the indices of the 2 vertices (w.r.t. vector vertices)
//         of the original BSPface faces[face] that lie
//         on the constraint-plane: endpts.
// Output: nothing.
void BSPcomplex::add_commonEdge(uint32_t constr, uint64_t face_ind,
                                uint64_t newFace_ind, const uint32_t* endpts){
  BSPface& face = faces[face_ind];
  BSPface& newFace = faces[newFace_ind];
  // The new faces "face" and "newFace", originated by splitting the original
  // "face" with the constraint-plane (constr), have a common edge.
  // The endpoint of the common edge are those that have vrt_orBin=0, i.e.
  // endpts[0], endpts[1].

  // New edge originated by the face-constraint intersection:
  // it is defined by the intersection of two planes (face and constraint)
  uint32_t constr_ID = 3*constr;
  edges.push_back( BSPedge(endpts[0], endpts[1],
                           face.meshVertices[0],
                           face.meshVertices[1],
                           face.meshVertices[2],
                           constraints_vrts[constr_ID   ],
                           constraints_vrts[constr_ID +1],
                           constraints_vrts[constr_ID +2] ) );
  uint64_t commEdge_ind = edges.size() -1;
  BSPedge& commEdge = edges[commEdge_ind];
  //commEdge.conn_faces.push_back(newFace_ind);
  //commEdge.conn_faces.push_back(face_ind);
  commEdge.conn_face_0 = face_ind;

  // Add an element to global vector edge_visit.
  edge_visit.push_back(0);

  // Add the common edge to face and newFace.
  //add_edgeToOrdFaceEdges(face, commEdge_ind);
  //add_edgeToOrdFaceEdges(newFace, commEdge_ind);
  face.edges.push_back(commEdge_ind);
  newFace.edges.push_back(commEdge_ind);
}

//  Input: an empty BSPface, created by intersecating a BSPcell with a
//         constraint: face,
//         the indices of all BSPedges (w.r.t. vector edges) that bound the
//         BSPface: edges_ind.
// Output: nothing.
void BSPcomplex::add_edges_toCommFaceEdges(BSPface& face,
                                       const vector<uint64_t>& edges_ind){
  // Find face vertices: since the face boundary is closed,
  //                     there are as many vertices as are the edges.
  vector<uint32_t> face_vrts(edges_ind.size(), UINT32_MAX);
  uint64_t edge_ind;
  uint32_t e0, e1, fv_ind=0;
  for(uint64_t e=0; e<edges_ind.size(); e++){
    BSPedge& edge = edges[ edges_ind[e] ];
    e0 = edge.vertices[0];
    if(vrts_visit[e0]==0){
      face_vrts[fv_ind++] = e0;
      vrts_visit[e0]=1;
    }
    e1 = edge.vertices[1];
    if(vrts_visit[e1]==0){
      face_vrts[fv_ind++] = e1;
      vrts_visit[e1]=1;
    }
  }

  //Relate each face vertex with its two incident edges.
  vector<uint64_t> rel_VE(2*face_vrts.size(), UINT64_MAX);

  // Set vrts_visit of face_vrts indices in order to memory positions.
  for(uint32_t u=0; u<face_vrts.size(); u++)
    vrts_visit[ face_vrts[u] ]=UINT32_MAX;

  // Fill rel_VE
  uint32_t pos=0;
  for(uint64_t e=0; e<edges_ind.size(); e++){
    edge_ind = edges_ind[e];
    BSPedge& edge = edges[edge_ind];
    e0 = edge.vertices[0];
    e1 = edge.vertices[1];
    if( vrts_visit[e0] == UINT32_MAX ){
      rel_VE[2*pos]=edge_ind;
      vrts_visit[e0]=pos++;
    }
    else  rel_VE[ 2*vrts_visit[e0] +1 ] = edge_ind;

    if( vrts_visit[e1] == UINT32_MAX ){
      rel_VE[2*pos]=edge_ind;
      vrts_visit[e1]=pos++;
    }
    else  rel_VE[ 2*vrts_visit[e1] +1 ] = edge_ind;

  }

  // Fill faces[face_ind].edges
  uint32_t num_ins_vrts=0, next_vrt=face_vrts[0];
  edge_ind = rel_VE[ 2*vrts_visit[next_vrt] ];

  face.edges.resize(edges_ind.size());
  while( num_ins_vrts < face_vrts.size() ){
    if(edge_visit[edge_ind]==0){
      edge_visit[edge_ind]=1;
      face.edges[num_ins_vrts++] = edge_ind;

      e0 = edges[edge_ind].vertices[0];
      e1 = edges[edge_ind].vertices[1];
      if( next_vrt == e0 ) next_vrt = e1;
      else next_vrt = e0;
      edge_ind = rel_VE[ 2*vrts_visit[next_vrt] ];
    }
    else{
      if(edge_ind == rel_VE[ 2*vrts_visit[next_vrt] ])
        edge_ind = rel_VE[ 2*vrts_visit[next_vrt] +1 ];
      else edge_ind = rel_VE[ 2*vrts_visit[next_vrt] ];
    }
  }

  // Reset vrts_visit and edge_visit
  for(uint32_t u=0; u<face_vrts.size(); u++) vrts_visit[ face_vrts[u] ]=0;
  for(uint64_t u=0; u<edges_ind.size(); u++) edge_visit[ edges_ind[u] ]=0;
}

//  Input: index of the constraint splitting the BSPcell: constr,
//         index of the splitted BSPcell (w.r.t. vector cells) that is going
//         to be turned into the downSubcell: cell_ind,
//         index of the upSubcell (w.r.t. vector cells): newCell_ind,
//         vector of the indices of the vertices (w.r.t. vector vertices)
//         of the BSPcell to which the BSPface belong to: cell_vrts.
// Output: nothing.
void BSPcomplex::add_commonFace(uint32_t constr,
                                uint64_t cell_ind, uint64_t newCell_ind,
                                const vector<uint32_t>& cell_vrts,
                                const vector<uint64_t>& cell_edges){
  // Common face between up-subcell and down-subcell: the edge of that face
  // are those of cells[cell_ind] that have vrts_orBin = 0.
  uint32_t constr_ID = 3*constr;
  COLOUR_T colour = GREY;
  if(is_virtual(constr)) colour = WHITE;
  faces.push_back( BSPface(constraints_vrts[constr_ID],
                           constraints_vrts[constr_ID+1],
                           constraints_vrts[constr_ID+2],
                           cell_ind, newCell_ind, colour )   );
  uint64_t face_ind = faces.size() - 1;

  uint64_t edge_ind, num_commFace_edges=0;
  // Count edges whose endpoints are both on the constraint-plane.
  for(uint64_t e=0; e<cell_edges.size(); e++){
      BSPedge& edge = edges[ cell_edges[e] ];
      if(vrts_orBin[ edge.vertices[0] ]==0 &&
         vrts_orBin[ edge.vertices[1] ]==0    )
          num_commFace_edges++;
  }
  // Fill a vector with those edges.
  vector<uint64_t> commFace_edges(num_commFace_edges, UINT64_MAX);
  num_commFace_edges = 0;
  for(uint64_t e=0; e<cell_edges.size(); e++){
      edge_ind = cell_edges[e];
      BSPedge& edge = edges[edge_ind];
      if(vrts_orBin[ edge.vertices[0] ]==0 &&
         vrts_orBin[ edge.vertices[1] ]==0    )
          commFace_edges[num_commFace_edges++] = edge_ind;
  }

  add_edges_toCommFaceEdges(faces[face_ind], commFace_edges);

  //for (uint64_t e = 0; e < commFace_edges.size(); e++)
  //    edges[commFace_edges[e]].conn_faces.push_back(face_ind);
  for (uint64_t e = 0; e < commFace_edges.size(); e++)
      edges[commFace_edges[e]].conn_face_0 = face_ind;

  cells[cell_ind].faces.push_back(face_ind);
  cells[newCell_ind].faces.push_back(face_ind);

  fixCommonFaceOrientation(face_ind);
}

inline bool edges_ShareCommonPlanes(const BSPedge& a, const BSPedge& b)
{
    const uint32_t* mva = a.meshVertices;
    const uint32_t* mvb = b.meshVertices;
    return (mva[0] == mvb[0] && mva[1] == mvb[1] && mva[2] == mvb[2] && mva[3] == mvb[3] && mva[4] == mvb[4] && mva[5] == mvb[5]);
}

void BSPcomplex::fixCommonFaceOrientation(uint64_t cf_id)
{
    BSPface& f = faces[cf_id];
    const uint32_t* mv = f.meshVertices;
    double mvc[9]; // Coords of the original input triangle
    vertices[mv[0]]->getApproxXYZCoordinates(mvc[0], mvc[1], mvc[2]);
    vertices[mv[1]]->getApproxXYZCoordinates(mvc[3], mvc[4], mvc[5]);
    vertices[mv[2]]->getApproxXYZCoordinates(mvc[6], mvc[7], mvc[8]);
    int xyz = genericPoint::maxComponentInTriangleNormal(mvc[0], mvc[1], mvc[2],
        mvc[3], mvc[4], mvc[5], mvc[6], mvc[7], mvc[8]);

    int ori0;
    if (xyz == 2) ori0 = orient2d(mvc[0], mvc[1], mvc[3], mvc[4], mvc[6], mvc[7]);
    else if (xyz == 0) ori0 = orient2d(mvc[1], mvc[2], mvc[4], mvc[5], mvc[7], mvc[8]);
    else ori0 = orient2d(mvc[2], mvc[0], mvc[5], mvc[3], mvc[8], mvc[6]);

    const BSPedge& edge0 = edges[f.edges.back()];
    const BSPedge& edge1 = edges[f.edges[0]];
    uint32_t vid = consecEdges_common_endpt(edge0.vertices[0], edge0.vertices[1], edge1.vertices[0], edge1.vertices[1]);

    genericPoint* v0 = vertices[vid];
    if (vid == edge1.vertices[0]) vid = edge1.vertices[1];
    else vid = edge1.vertices[0];
    genericPoint* v1 = vertices[vid];
    for (uint64_t e = 1; e < f.edges.size(); e++) {
        const BSPedge& edge = edges[f.edges[e]];
        if (vid == edge.vertices[0]) vid = edge.vertices[1];
        else vid = edge.vertices[0];
        if (edges_ShareCommonPlanes(edge1, edge)) continue;
        genericPoint* v2 = vertices[vid];
        int ori = ori0*genericPoint::orient2D(*v0, *v1, *v2, xyz);
        if (ori < 0) return;
        if (ori > 0) {
            if (f.conn_cells[1] == UINT64_MAX)
            {
                printf("Mmh... this should not happen\n");
                return;
            }
            std::swap(f.conn_cells[0], f.conn_cells[1]);
            return;
        }
    }
    ip_error("Degenerate face\n");
}

//  Input: a BSPedge: edge,
//         index of a constraint (w.r.t. vector constraints_vrt): constr.
// Output: returns the index of the new vertex (w.r.t. vector vertices).
// Note: a LPI (Line Plane Intersection) vertex can be generated only as
//       intesection beween a constraint-plane and an edge that is part of the
//       Delaunay triangulation (or a pice of Delaunay edge).
uint32_t BSPcomplex::add_LPIvrt(const BSPedge& edge, uint32_t constr){

  uint32_t constr_ID = 3*constr;
  genericPoint* c0 = vertices[ constraints_vrts[constr_ID  ] ];
  genericPoint* c1 = vertices[ constraints_vrts[constr_ID+1] ];
  genericPoint* c2 = vertices[ constraints_vrts[constr_ID+2] ];
  genericPoint* e0 = vertices[ edge.meshVertices[0] ];
  genericPoint* e1 = vertices[ edge.meshVertices[1] ];

  #ifdef DEBUG_BSP
  if(!(e0->isExplicit3D()) ||
     !(e1->isExplicit3D()) ||
     !(c0->isExplicit3D()) ||
     !(c1->isExplicit3D()) ||
     !(c2->isExplicit3D())    )
    printf("\n[BSP.cpp]BSPcomplex::add_LPIvrt: ERROR explicitPoint3D are "
           "expected (LPI).\n");
  #endif

  vertices.push_back(
        new implicitPoint3D_LPI(
            e0->toExplicit3D(), e1->toExplicit3D(),
            c0->toExplicit3D(), c1->toExplicit3D(), c2->toExplicit3D() ) );

  // Add new element to global vectors vrts_orBin and vrts_visit.
  vrts_orBin.push_back(2);
  vrts_visit.push_back(0);

  return (uint32_t)(vertices.size() -1);

}

// Return TRUE if the intersection of the sets {p,q,r} and {t,u,v} has at least two elements.
// Fill 'e' with the intersecting elements.
inline bool twoEqualVertices(uint32_t p, uint32_t q, uint32_t r,
                             uint32_t s, uint32_t t, uint32_t u, uint32_t *e){
    int i = 0;
    if (p == s || p == t || p == u) e[i++] = p;
    if (q == s || q == t || q == u) e[i++] = q;
    if (r == s || r == t || r == u) e[i++] = r;
    return (i >= 2);
}

//  Input: a BSPedge: edge,
//         index of a constraint (w.r.t. vector constraints_vrt): constr.
// Output: returns the index of the new vertex (w.r.t. vector vertices).
// Note: a TPI (Triple Plane Intersection) vertex can be generated only as
//       intesection beween a constraint-plane and an edge that is defined as
//       the intersection between 3 constraint-planes (i.e. not included in
//       the original Delaunay triangulation).
uint32_t BSPcomplex::add_TPIvrt(const BSPedge& edge, uint32_t constr){

  const uint32_t constr_ID = 3*constr;

  const uint32_t ic0 = constraints_vrts[constr_ID];
  const uint32_t ic1 = constraints_vrts[constr_ID + 1];
  const uint32_t ic2 = constraints_vrts[constr_ID + 2];
  const uint32_t ie0 = edge.meshVertices[0];
  const uint32_t ie1 = edge.meshVertices[1];
  const uint32_t ie2 = edge.meshVertices[2];
  const uint32_t ie3 = edge.meshVertices[3];
  const uint32_t ie4 = edge.meshVertices[4];
  const uint32_t ie5 = edge.meshVertices[5];

  // If two of the three triangles share two vertices -> create an LPI
  uint32_t comm[3];
  if(twoEqualVertices(ic0, ic1, ic2, ie0, ie1, ie2, comm))
      vertices.push_back(new implicitPoint3D_LPI(
          vertices[comm[0]]->toExplicit3D(),
          vertices[comm[1]]->toExplicit3D(),
          vertices[ie3]->toExplicit3D(),
          vertices[ie4]->toExplicit3D(),
          vertices[ie5]->toExplicit3D()       ) );
  else if(twoEqualVertices(ic0, ic1, ic2, ie3, ie4, ie5, comm))
          vertices.push_back(new implicitPoint3D_LPI(
              vertices[comm[0]]->toExplicit3D(),
              vertices[comm[1]]->toExplicit3D(),
              vertices[ie0]->toExplicit3D(),
              vertices[ie1]->toExplicit3D(),
              vertices[ie2]->toExplicit3D()     ) );
  else if(twoEqualVertices(ie3, ie4, ie5, ie0, ie1, ie2, comm))
          vertices.push_back(new implicitPoint3D_LPI(
              vertices[comm[0]]->toExplicit3D(),
              vertices[comm[1]]->toExplicit3D(),
              vertices[ic0]->toExplicit3D(),
              vertices[ic1]->toExplicit3D(),
              vertices[ic2]->toExplicit3D()         ) );
  else{
    vertices.push_back( new implicitPoint3D_TPI( vertices[ie0]->toExplicit3D(),
                                                 vertices[ie1]->toExplicit3D(),
                                                 vertices[ie2]->toExplicit3D(),
                                                 vertices[ie3]->toExplicit3D(),
                                                 vertices[ie4]->toExplicit3D(),
                                                 vertices[ie5]->toExplicit3D(),
                                                 vertices[ic0]->toExplicit3D(),
                                                 vertices[ic1]->toExplicit3D(),
                                                 vertices[ic2]->toExplicit3D() ));
  }

  // Add new element to global vectors vrts_orBin and vrts_visit.
  vrts_orBin.push_back(2);
  vrts_visit.push_back(0);

  return (uint32_t)(vertices.size() -1);
}



// 2 funzioni
// 1 - 

// Given a cell 'c', one of its faces 'f0', and one of f0 edges 'e0'
// return the face in 'c' that shares 'e0' with 'f0'
uint64_t BSPcomplex::getOppositeEdgeFace(const uint64_t e0, const uint64_t f0, const uint64_t c)
{
    const std::vector<uint64_t>& cfaces = cells[c].faces;
    for (uint64_t fid : cfaces) if (fid != f0)
    {
        const std::vector<uint64_t>& fedges = faces[fid].edges;
        for (uint64_t e : fedges) if (e == e0) return fid;
    }
    return UINT64_MAX;
}

static inline uint64_t oppositeCellId(const uint64_t c_id, const BSPface& f)
{
    if (f.conn_cells[0] == c_id) return f.conn_cells[1];
    else return f.conn_cells[0];
}

void BSPcomplex::makeEFrelation(const uint64_t e_id, std::vector<uint64_t>& ef)
{
    const BSPedge& e = edges[e_id];
    uint64_t f = e.conn_face_0;
    uint64_t c = faces[f].conn_cells[0];
    ef.push_back(f);

    for (;;)
    {
        f = getOppositeEdgeFace(e_id, f, c);
        if (f == e.conn_face_0) return;
        else ef.push_back(f);
        c = oppositeCellId(c, faces[f]);
        if (c == UINT64_MAX) break;
    }

    f = e.conn_face_0;
    if ((c = faces[f].conn_cells[1]) == UINT64_MAX) return;

    for (;;)
    {
        f = getOppositeEdgeFace(e_id, f, c);
        ef.push_back(f);
        c = oppositeCellId(c, faces[f]);
        if (c == UINT64_MAX) return;
    }
}
//  Input: a BSPedge: edge,
//         index of a constraint that intersects the edge interior: constr.
// Output: the index of the sub-edge (w.r.t. vector edges) originated by the
//         intersection between the edge and the constraint, whose endpoints
//         have non-negative orient3D w.r.t. the constraint plane.
void BSPcomplex::splitEdge(uint64_t edge_id, uint32_t constr){

  BSPedge& edge = edges[edge_id];

  std::vector<uint64_t> ef;
  makeEFrelation(edge_id, ef);

  // Add the point in which the edge intersects the constraint-plane
  // to the vector vertices.
  // edge is a Delauany mesh edge -> Line Plane Intersection.
  // edge is a 2 constraints intersection -> Triple Plane Intersection.
  uint32_t new_point;
  if(edge.meshVertices[2]==UINT32_MAX) new_point = add_LPIvrt(edge, constr);
  else                                 new_point = add_TPIvrt(edge, constr);

  // Split the edge <e0,e1> -> <e0,new_point> + <new_point,e1>
  // edge <- <new_point,e1>
  // new_edge = edges[edges.size()-1] <- <e0,new_point>.
  edges.push_back( edge.split(new_point) );
  uint64_t new_edge_ind = edges.size()-1;
  // Note. new edge is created with the same conn_faces of the old edge.

  // Add new element to edge_visit
  edge_visit.push_back(0);

  // Add new edge to all conn_faces of old edge.
  //for(uint64_t f=0; f< edges[edge_id].conn_faces.size(); f++)
  //  add_edgeToOrdFaceEdges(faces[edges[edge_id].conn_faces[f] ], new_edge_ind);

  for (uint64_t f : ef) add_edgeToOrdFaceEdges(faces[f], new_edge_ind);
}

//  Input: index of a BSPface: face_ind,
//         index of a constraint that intersects the face interior: constr,
//         index of the BSPcell to which the BSPface belongs to: cell_ind,
//         a vector with the indices of the face vertices: face_vrts.
// Output: nothing.
void BSPcomplex::splitFace(uint64_t face_ind, uint32_t constr,
                          uint64_t cell_ind, const vector<uint32_t>& face_vrts){

  #ifdef DEBUG_BSP_DEEP
  printf("\n\tDividing face #%llu with constraint %u.\n", face_ind, constr);
  #endif

  BSPface& face = faces[face_ind];

  // The face faces[face] is divided in two subfaces:
  // the up-subface and the down-subface.
  // The edges of faces[face] have to be partitioned between them.
  // up-subface inherits edges whose vertices have non-negative vrt_orBin.
  // down-subface inherits edges whose vertices have non-positive vrt_orBin.

  // Face faces[face], after splitting, becomes the down-subface.
  // New BSPface is the up-subface of the original faces[face].
  faces.push_back( BSPface(face.meshVertices[0],
                           face.meshVertices[1],
                           face.meshVertices[2],
                           face.conn_cells[0],
                           face.conn_cells[1],
                           face.colour,
                           face.coplanar_constraints) );
  uint64_t newFace_ind = faces.size() -1;
  // Note. the edge of the new face (i.e. up-subface) will be assigned later.

  #ifdef DEBUG_BSP_DEEP
  printf("\tSplit face %llu -> up-subface (face #%llu) + down-subface "
         "(face #%llu)\n", face_ind, newFace_ind, face_ind);
  #endif

  // Add the new face to its adjacent cell (the same of faces[face]).
  // If it is a convex-hull face (i.e. conn_cells[1] = UINT64_MAX),
  // there is no adjacent cell.

  cells[faces[face_ind].conn_cells[0] ].faces.push_back(newFace_ind);
  if( !IS_GHOST_CELL(faces[face_ind].conn_cells[1]) )
    cells[faces[face_ind].conn_cells[1] ].faces.push_back(newFace_ind);

  // Find face vertices that have vrts_orBin=0.
  uint32_t zero_vrts[2];
  uint32_t pos=0;
  for(uint32_t v=0; v<face_vrts.size(); v++)
    if(vrts_orBin[ face_vrts[v] ]==0)  zero_vrts[pos++]=face_vrts[v];

  #ifdef DEBUG_BSP
  if(pos!=2)
    printf("\n[BSP.cpp]BSPcomplex::splitFace: ERROR there must be exactly "
           "2 (not %u) face-vertices on the constraint plane.\n", pos);
  #endif

  // Partition of the edges between up-subface and down-subface.
  edgesPartition(face_ind, newFace_ind);

  // The new faces faces[face_ind] and faces[newFace_ind] have a common edge.
  add_commonEdge(constr, face_ind, newFace_ind, zero_vrts);


  //if (!faceHasCorrectOrientation(this, face_ind)) ip_error("Wrong f1\n");
  //if (!faceHasCorrectOrientation(this, newFace_ind)) ip_error("Wrong f2\n");
}

//
//
void BSPcomplex::find_coplanar_constraints(uint64_t cell_ind, uint32_t constr,
                                               vector<uint32_t>& coplanar_c){
  BSPcell& cell = cells[cell_ind];
  uint32_t constr_ID = 3*constr;
  uint32_t c0 = constraints_vrts[constr_ID  ];
  uint32_t c1 = constraints_vrts[constr_ID+1];
  uint32_t c2 = constraints_vrts[constr_ID+2];

  // Count coplanar constraints.
  uint32_t num_coplanar = 0;
  vector<uint32_t> k_vrts(3, UINT32_MAX);
  for(uint32_t k=0; k < cell.constraints.size(); k++){
      if (is_virtual(cell.constraints[k])) continue;
      uint32_t kID = 3*cell.constraints[k];
      k_vrts[0] = constraints_vrts[kID  ];
      k_vrts[1] = constraints_vrts[kID+1];
      k_vrts[2] = constraints_vrts[kID+2];
      vrts_orient_wrtPlane(k_vrts, c0, c1, c2, 0);
      if(vrts_orBin[ k_vrts[0] ]==0 &&
         vrts_orBin[ k_vrts[1] ]==0 &&
         vrts_orBin[ k_vrts[2] ]==0    ){

        #ifdef DEBUG_BSP_DEEP
        printf("cell #%llu: constraints %u and %u are aligned.\n",
                cell_ind, constr_ID/3, kID/3);
        #endif

        num_coplanar++;
      }
  }
  if (!is_virtual(constr)) num_coplanar++;

  // Save coplanar constraints in a new vector and remove them from cell's list.
  if(num_coplanar > 0){
    coplanar_c.resize(num_coplanar, UINT32_MAX);
    uint32_t pos=0;
    for(uint32_t k=0; k < cell.constraints.size(); k++){
        if (is_virtual(cell.constraints[k])) continue;
        uint32_t kID = 3*cell.constraints[k];
        k_vrts[0] = constraints_vrts[kID  ];
        k_vrts[1] = constraints_vrts[kID+1];
        k_vrts[2] = constraints_vrts[kID+2];

        if(vrts_orBin[ k_vrts[0] ]==0 &&
           vrts_orBin[ k_vrts[1] ]==0 &&
           vrts_orBin[ k_vrts[2] ]==0    ){

          coplanar_c[pos++] = cell.constraints[k];
          cell.constraints[k] = cell.constraints.back();
          cell.constraints.pop_back();
          k--;
        }
    }
    if (!is_virtual(constr)) coplanar_c[pos++] = constr;
  }

}

//  Input: index of a BSPcell: cell_ind.
// Output: nothing.
// Note. it is assumed that the BScell is (only) convex,
//       strictly convexity is not guaranteed.
void BSPcomplex::splitCell(uint64_t cell_ind){

  // Extract the last contraint that intersect the cell and remove it from
  // the list. The cell will be splitted by that constraint.
  BSPcell& cell = cells[cell_ind];
  uint32_t constr = cell.constraints.back();
  uint32_t constr_ID = 3*constr;
  uint32_t c0 = constraints_vrts[constr_ID  ];
  uint32_t c1 = constraints_vrts[constr_ID+1];
  uint32_t c2 = constraints_vrts[constr_ID+2];

  cell.constraints.pop_back();

  // Search for coplanar constraints.
  vector<uint32_t> coplanar_constr;
  find_coplanar_constraints(cell_ind, constr, coplanar_constr);

  // Distinguish between two mutually exclusive cases:
  // CASE. NO SPLIT: only cell boundary elements (face or edge) lie on the
  //                 constraint-plane, while the other elements belong to
  //                 the same half-space w.r.t. the constraint-plane.
  // CASE. SPLIT-INTERIOR: constraint-plane pass through the cell interior.
  // Note. it is not possible that only a vertex lies on the constraint-plane.

  // Create a local richer data structure to avoid multilple extracions of cell
  // edges and vertices.
  uint64_t num_cellEdges = count_cellEdges(cell);
  uint64_t num_cellVrts = count_cellVertices(cell, &num_cellEdges);
  vector<uint64_t> cell_edges(num_cellEdges, UINT64_MAX);
  vector<uint32_t> cell_vrts(num_cellVrts, UINT32_MAX);
  fill_cell_locDS(cell, cell_edges, cell_vrts);

  // Compute the orientation of cell vertices w.r.t. the constraint plane.
  vrts_orient_wrtPlane(cell_vrts, c0, c1, c2, 1);

  // Analysis of cell vertices disposition w.r.t. constraint-plane.
  uint32_t vrtsON, vrtsOVER, vrtsUNDER;
  count_vrt_orBin(cell_vrts, &vrtsOVER, &vrtsUNDER, &vrtsON);

  // CASE. NO SPLIT:
  // at least two cell_vrts_or are 0, the other (!=0) have the same signe.
  if(vrtsUNDER==0 || vrtsOVER==0) return;

  // (else) CASE. SPLIT-INTERIOR:
  // at least two cell_vrts_or have opposite signe.

  // Split cell edges whose endpoints have opposite cell_vrts_or signe.

  for(uint64_t e=0; e<cell_edges.size(); e++){
      BSPedge& edge = edges[cell_edges[e]];
      if(constraint_innerIntersects_edge(edge, cell_vrts)){
        splitEdge(cell_edges[e], constr);  // Here the edge is splitted.
        // Add the new vertex to cell_vrts, compute its orient3D w.r.t. the
        // constraint-plane and add it to cell_vrts_or.
        uint32_t new_vrt = (uint32_t)(vertices.size() -1);
        cell_vrts.push_back(new_vrt);
        cell_edges.push_back(edges.size() -1);
        vrts_orBin[new_vrt] = 0;

        #ifdef DEBUG_BSP_DEEP
        vector<uint32_t> vrt_to_print;
        vrt_to_print.push_back(new_vrt);
        printf("\n");
        print_vrt_orBin(vrts_orBin, vrt_to_print);
        #endif
      }
  }
  // Now all the BSPcell edges are over or under the constraint.

  // Split cell-faces that have at lesat two vertices with opposite
  // cell_vrts_or signe.

  uint64_t num_faces = cell.faces.size();
  for(uint64_t f=0; f<num_faces; f++){
    uint64_t face_ind = cell.faces[f];
    BSPface& face = faces[face_ind];
    vector<uint32_t> face_vrts(face.edges.size(), UINT32_MAX);
    list_faceVertices(face, face_vrts);

    if(constraint_innerIntersects_face(face_vrts) ){

      // Here the face is splitted.
      // The intersection between face and constraint-plane is a new edge.
      splitFace(face_ind, constr, cell_ind, face_vrts);

      // Add new edge (the face-slpitting one) to cell_edges
      cell_edges.push_back(edges.size()-1);
    }
  }

  // The cell is divided in two subcells: the up-subcell and the down-subcell.
  // The up-subcell have vertices with non-negative cell_vrts_or.
  // The down-subcell have vertices with non-positive cell_vrts_or.
  //
  // The cell faces are partitioned between the two subcells.
  // A cell face intesecting the constraint-plane is splitted in sub-faces.
  //
  // A new face is created: the common face between up-subcell and down-subcell.
  //
  // Conventionally the down-subcell replace cell in the vector cells,
  // while up-subcell is appended to the vector cells.

  // up-subcell
  cells.push_back( BSPcell() );
  uint64_t newCell_ind = cells.size() - 1;

  facesPartition(cell_ind, newCell_ind, cell_vrts);
  // Add common face between up-subcell and down-subcell.
  add_commonFace(constr, cell_ind, newCell_ind, cell_vrts, cell_edges);

  // If there are coplanar constraints (to the one used for splitting) those
  // constraints have to be aded to the common face.
  uint32_t num_coplanar_constr = (uint32_t)coplanar_constr.size();
  faces.back().coplanar_constraints.resize(1 + num_coplanar_constr);
  faces.back().coplanar_constraints[0] = constr;
  if(num_coplanar_constr > 0)
    for(uint32_t cc=0; cc<num_coplanar_constr; cc++)
      faces.back().coplanar_constraints[1+cc] = coplanar_constr[cc];

  // Constraints that have to be partitioned between up-subcell
  // and down-subcell are: cells[cell].constarints.

  constraintsPartition(constr, cell_ind, newCell_ind, cell_vrts);
}

//--Complex tetrahedralization-------------------------

//  Input: the index of a BSPface w.r.t. vector faces: face_ind.
// Output: nothing.
// Note. The last 2 edges of the vector face[face_ind].edges are used to
//       detach a triangualr face from the face faces[face_ind].
//       This 2 last edges are replaced in the vector face[face_ind].edges by
//       one new edge: the one that closes the detached triangualr face.
void BSPcomplex::triangle_detach(uint64_t face_ind){
  BSPface& face = faces[face_ind];
  uint64_t num_face_edges = face.edges.size();

  // A triangle will be created by using:
  // - edges[face.edges.size()-1] and edges[face.edges.size()-2],
  // - introducing a new_edge to close the triangle.
  uint64_t s_01_ind = face.edges[num_face_edges-1];
//  BSPedge& s_01 = edges[s_01_ind];
  uint64_t s_12_ind = face.edges[num_face_edges-2];
//  BSPedge& s_12 = edges[s_12_ind];
  uint32_t t1 = consecEdges_common_endpt(edges[s_01_ind].vertices[0], edges[s_01_ind].vertices[1],
      edges[s_12_ind].vertices[0], edges[s_12_ind].vertices[1]);
  uint32_t t0 = other_edge_endpt(edges[s_01_ind].vertices[0], edges[s_01_ind].vertices[1], t1);
  uint32_t t2 = other_edge_endpt(edges[s_12_ind].vertices[0], edges[s_12_ind].vertices[1], t1);

  // Connect t2 and t0 with a new edge.
  edges.push_back(BSPedge());
  uint64_t s_20_ind = edges.size() -1;
  BSPedge& s_20 = edges.back();
  s_20.vertices[0] = t2;
  s_20.vertices[1] = t0;
  // meshVertices -> all UINT32_MAX.
  s_20.meshVertices[0] = UINT32_MAX;
  s_20.meshVertices[1] = UINT32_MAX;
  s_20.meshVertices[2] = UINT32_MAX;
  s_20.meshVertices[3] = UINT32_MAX;
  s_20.meshVertices[4] = UINT32_MAX;
  s_20.meshVertices[5] = UINT32_MAX;
  //s_20.conn_faces.push_back(face_ind);

  // The other conn_faces element is the new triangle that is going to be
  // created below.

  // Add an element to edge_visit
  edge_visit.push_back(0);

  // Remove triangle <t0,t1,t2> from current BSPface and save it as a new one.
  face.edges.pop_back();
  face.edges[ face.edges.size()-1 ] = s_20_ind;
  uint64_t i=0;
  //REMOVE_ELEM_VECT(face_ind, edges[s_01_ind].conn_faces);
  //REMOVE_ELEM_VECT(face_ind, edges[s_12_ind].conn_faces);

  // New face-triangle is created.
  faces.push_back( BSPface(face.meshVertices[0],
                           face.meshVertices[1],
                           face.meshVertices[2],
                           face.conn_cells[0],
                           face.conn_cells[1],
                           face.colour           ) );
  uint64_t new_face_ind = faces.size() -1;
  BSPface& new_face = faces.back();
  new_face.edges.push_back(s_20_ind);
  new_face.edges.push_back(s_12_ind);
  new_face.edges.push_back(s_01_ind);

  cells[faces[face_ind].conn_cells[0] ].faces.push_back(new_face_ind);
  if( !IS_GHOST_CELL(faces[face_ind].conn_cells[1]) )
     cells[faces[face_ind].conn_cells[1] ].faces.push_back(new_face_ind);

  //edges[s_01_ind].conn_faces.push_back(new_face_ind);
  //edges[s_12_ind].conn_faces.push_back(new_face_ind);
  //s_20.conn_faces.push_back(new_face_ind);
  edges[s_01_ind].conn_face_0 = new_face_ind;
  edges[s_12_ind].conn_face_0 = new_face_ind;
  s_20.conn_face_0 = new_face_ind;

  #ifdef DEBUG_BSP_DEEP
  printf("detached triangle %llu -> <%u,%u,%u>\n", new_face_ind, t0, t1, t2);
  triangular_BSPface_isDegenerate(faces, edges, vertices, new_face_ind);
  #endif

}

//  Input:
// Output:
// Note. assumes that aligned face-edges are sub-edges of the same original edge.
bool BSPcomplex::aligned_face_edges(uint64_t fe0, uint64_t fe1, const BSPface& face){
  BSPedge& edge0 = edges[ face.edges[fe0] ];
  BSPedge& edge1 = edges[ face.edges[fe1] ];
  if(edge0.meshVertices[0] == UINT32_MAX) return false;
  if(edge0.meshVertices[0] != edge1.meshVertices[0]) return false;
  if(edge0.meshVertices[1] != edge1.meshVertices[1]) return false;
  if(edge0.meshVertices[2] != edge1.meshVertices[2]) return false;
  if(edge0.meshVertices[2] == UINT32_MAX) return true;
  if(edge0.meshVertices[3] != edge1.meshVertices[3]) return false;
  if(edge0.meshVertices[4] != edge1.meshVertices[4]) return false;
  if(edge0.meshVertices[5] != edge1.meshVertices[5]) return false;
  return true;
}

//
//
//  Input: the index of a BSPface: face_ind.
// Output: nothing.
void BSPcomplex::triangulateFace(uint64_t face_ind){

  // edges indices, of BSPface faces[face_ind], are listed in the vector
  // faces[face_ind].edges ordered as walking face-boundary clockwise
  // (or counterclockwise).
  BSPface& face = faces[face_ind];
  uint64_t num_face_edges = face.edges.size();

  while(num_face_edges > 3){

    // Check if last two edges are not-aligned.
    if(!aligned_face_edges(num_face_edges-1, num_face_edges-2, faces[face_ind]) ){

      // To remove the triangle with the vertices of the last two edges
      // one must be sure that the remaining face does not become degenerate.

      if(!aligned_face_edges(0, num_face_edges-3, faces[face_ind]) ){

        triangle_detach(face_ind);
        num_face_edges--;
      }
      else UINT64_vect_down_shift(faces[face_ind].edges, 1);

    }
    else UINT64_vect_down_shift(faces[face_ind].edges, 1);

  }
}

//  Input: vertices indices of a BSPelement: vrts.
// Output: nothing.
// Note. a point representing the baricenter (or its approximation) is created
//       and added to the vector vertices.
void BSPcomplex::computeBaricenter(const vector<uint32_t>& vrts){
    double cx, cy, cz;
    double sum_x=0.0, sum_y=0.0, sum_z=0.0;
    uint32_t np = 0;
    for (const uint32_t v : vrts)
        if (vertices[v]->getApproxXYZCoordinates(cx, cy, cz))
        {
            sum_x += cx;
            sum_y += cy;
            sum_z += cz;
            np++;
            break; // This line should be commented to have an actual barycenter !!!!!!
        }

    vertices.push_back(new explicitPoint3D(sum_x / np, sum_y / np, sum_z / np));
    vrts_visit.push_back(0);
}

//
//
inline uint64_t BSPcomplex::triFace_oppEdge(const BSPface& face, uint32_t v){
  uint64_t edge_ind = face.edges[0];
  uint32_t e0 = edges[ edge_ind ].vertices[0];
  uint32_t e1 = edges[ edge_ind ].vertices[1];
  if(e0 == v || e1 == v){
    edge_ind = face.edges[1];
    e0 = edges[ edge_ind ].vertices[0];
    e1 = edges[ edge_ind ].vertices[1];
    if(e0 == v || e1 == v){
      edge_ind = face.edges[2];
      e0 = edges[ edge_ind ].vertices[0];
      e1 = edges[ edge_ind ].vertices[1];
    }
  }
  return edge_ind;
}

//
//
uint64_t BSPcomplex::triFace_shareEdge(const BSPcell& cell, uint64_t face_ind,
                                       uint64_t vOppEdge_ind){
  for(uint64_t f=0; f<cell.faces.size(); f++){
    uint64_t adj_face_ind = cell.faces[f];
    if( (faces[ adj_face_ind ].edges[0] == vOppEdge_ind ||
         faces[ adj_face_ind ].edges[1] == vOppEdge_ind ||
         faces[ adj_face_ind ].edges[2] == vOppEdge_ind   ) &&
        face_ind != adj_face_ind                               )
      return adj_face_ind;
  }

  // never reached
  printf("[BSP.cpp]BSPcomplex::triFace_shareEdge: ERROR return UINT64_MAX.\n");
  return UINT64_MAX;
}

//
//
bool BSPcomplex::cell_is_tetrahedrizable_from_v(const BSPcell& cell, uint32_t v){
  uint64_t num_incFaces = count_cellFaces_inc_cellVrt(cell, v);
  vector<uint64_t> v_incFaces(num_incFaces, UINT64_MAX);
  cell_VFrelation(cell, v, v_incFaces);

  //bool return_zero = false;

  for(uint64_t f=0; f<num_incFaces; f++){
    //return_zero = false;
    BSPface& face = faces[ v_incFaces[f] ];
    uint64_t vOppEdge_ind = triFace_oppEdge(face, v);
    uint64_t faceShareEdge_ind = triFace_shareEdge(cell, v_incFaces[f], vOppEdge_ind);
    BSPface& oppFace = faces[faceShareEdge_ind];
    if(oppFace.meshVertices[0] == face.meshVertices[0] &&
       oppFace.meshVertices[1] == face.meshVertices[1] &&
       oppFace.meshVertices[2] == face.meshVertices[2]   ) //return_zero = true;
       return false;
  }

  return true;
}


//
//
void BSPcomplex::makeTetrahedra()
{
    uint64_t tet_num = 0; // total number of tetrahedra in which the cell will
                          // be decomposed.
    std::vector<uint32_t> decomposition_type(cells.size(), 0);
    // Possible decoposition techniques are:
    // 0 - cell is a tetrahedron -> no decomposition.
    // 1 - cell can be decomposed in tetrahedra by connecting a vertex with
    //     the other vertices that do not belong to its link.
    // 2 - cell is decomposed by connecting its baricenter with all the cell's
    //     vertices.
    std::vector<uint32_t> decomposition_vrt(cells.size(), UINT32_MAX);
    // decomposition_vrt is:
    // - UINT32_MAX for a tetrhedron,
    // - a cell vertex for decomposition type 1,
    // - the cell baricenter for decomposition type 2.

    for(uint64_t cell_i = 0; cell_i < cells.size(); cell_i++) {
        BSPcell& cell = cells[cell_i];
        if (cell.place != INTERNAL_A) continue;

        // If cell has more than 4 faces -> chose between types 1 and 2
        if(cell.faces.size() > 4) {

          uint64_t num_cellEdges = UINT64_MAX;
          uint32_t num_cellVrts = count_cellVertices(cell, &num_cellEdges);
          vector<uint32_t> cell_vrts(num_cellVrts, UINT32_MAX);
          list_cellVertices(cell, num_cellEdges, cell_vrts);

          // Check if cell is tetrahedralizable from a vertex.
          bool needs_barycenter = true;
          for(uint32_t cv=0; cv<cell_vrts.size(); cv++)
            if(cell_is_tetrahedrizable_from_v(cell, cell_vrts[cv]) ){
              decomposition_type[cell_i] = 1;
              decomposition_vrt[cell_i] = cell_vrts[cv];
              tet_num += cell.faces.size() - count_cellFaces_inc_cellVrt(cell, cell_vrts[cv]);
              needs_barycenter = false;
              break;
            }

          if(needs_barycenter){ // Cell need baricenter
            decomposition_type[cell_i] = 2;
            computeBaricenter(cell_vrts);
            decomposition_vrt[cell_i] = ((uint32_t)vertices.size() - 1);
            tet_num += cell.faces.size();
          }

        }
        else  tet_num++; // The cell is a tet.
    }

    final_tets.reserve(tet_num * 4);

    // Make tets
    for(uint64_t cell_i = 0; cell_i < cells.size(); cell_i++) {
        BSPcell& cell = cells[cell_i];
        if (cell.place != INTERNAL_A) continue;
        if(decomposition_type[cell_i] == 0){ // Simple tet
        vector<uint32_t> cell_vrts(4, UINT32_MAX);
        list_cellVertices(cells[cell_i], 6, cell_vrts);
        final_tets.insert(final_tets.end(), cell_vrts.begin(), cell_vrts.end());
      }
      else if(decomposition_type[cell_i] == 1){ // Tetrahedralizable from vertex
        uint32_t v = decomposition_vrt[cell_i];
        uint64_t num_incFaces = count_cellFaces_inc_cellVrt(cells[cell_i], v);
        uint64_t num_NOT_incFaces = cells[cell_i].faces.size() - num_incFaces;
        vector<uint64_t> v_NOT_incFaces(num_NOT_incFaces, UINT64_MAX);
        COMPL_cell_VFrelation(cells[cell_i], v, v_NOT_incFaces);
        for(uint64_t face_i : v_NOT_incFaces){
          // Simple triangle
          vector<uint32_t> face_vrts(3, UINT32_MAX);
          list_faceVertices(faces[face_i], face_vrts);
          final_tets.insert(final_tets.end(), face_vrts.begin(), face_vrts.end());
          final_tets.push_back(v);
        }
      }
      else{ // Uses cell barycenter
        for(uint64_t face_i : cells[cell_i].faces) {
          // Simple triangle
          vector<uint32_t> face_vrts(3, UINT32_MAX);
          list_faceVertices(faces[face_i], face_vrts);
          final_tets.insert(final_tets.end(), face_vrts.begin(), face_vrts.end());
          final_tets.push_back(decomposition_vrt[cell_i]);
        }
      }
    }

}




//-Decide colour of GREY faces--------------------------------------------------

//
//
int BSPcomplex::face_dominant_normal_component(const BSPface& face) {
    const uint32_t* mv = face.meshVertices;
    double mvc[9]; // Coords of the original input triangle
    vertices[mv[0]]->getApproxXYZCoordinates(mvc[0], mvc[1], mvc[2]);
    vertices[mv[1]]->getApproxXYZCoordinates(mvc[3], mvc[4], mvc[5]);
    vertices[mv[2]]->getApproxXYZCoordinates(mvc[6], mvc[7], mvc[8]);
    return genericPoint::maxComponentInTriangleNormal(mvc[0], mvc[1], mvc[2],
        mvc[3], mvc[4], mvc[5],
        mvc[6], mvc[7], mvc[8]);
}

//
//
void BSPcomplex::get_approx_faceBaricenterCoord(const BSPface& face, double* bar){
  bar[0] = 0;
  bar[1] = 0;
  bar[2] = 0;
  double tp[3];
  const BSPedge& edge0 = edges[face.edges.back()];
  const BSPedge& edge1 = edges[face.edges[0]];
  uint32_t vid = consecEdges_common_endpt(edge0.vertices[0], edge0.vertices[1],
                                          edge1.vertices[0], edge1.vertices[1]);
  for(uint64_t e = 0; e < face.edges.size(); e++) {
      const BSPedge& edge = edges[face.edges[e]];
      if (vid == edge.vertices[0]) vid = edge.vertices[1];
      else vid = edge.vertices[0];
      vertices[vid]->getApproxXYZCoordinates(tp[0], tp[1], tp[2]);
      bar[0] += tp[0];
      bar[1] += tp[1];
      bar[2] += tp[2];
  }
  bar[0] /= face.edges.size();
  bar[1] /= face.edges.size();
  bar[2] /= face.edges.size();
}

//
//
bool BSPcomplex::is_baricenter_inFace(const BSPface& face,
                                      const explicitPoint3D& face_center,
                                      int max_normComp){
  const BSPedge& edge0 = edges[face.edges.back()];
  const BSPedge& edge1 = edges[face.edges[0]];
  uint32_t vid = consecEdges_common_endpt(edge0.vertices[0], edge0.vertices[1],
                                          edge1.vertices[0], edge1.vertices[1]);

  int oro = 0;
  uint64_t e;
  for(e = 0; e < face.edges.size(); e++) {
      const BSPedge& edge = edges[face.edges[e]];
      const uint32_t pvid = vid;
      if (vid == edge.vertices[0]) vid = edge.vertices[1];
      else vid = edge.vertices[0];

      const int ao = genericPoint::orient2D(face_center, *vertices[pvid], *vertices[vid], max_normComp);
      if (ao == 0) break;
      if (ao != oro)
      {
          if (oro) break;
          else oro = ao;
      }
  }

  if(e == face.edges.size() ) return true;
  return false;
}

//
//
inline bool faceColour_matches_constrGroup(CONSTR_GROUP_T group, bool f_blackA, bool f_blackB){
  return (group == CONSTR_A && f_blackA) || (group == CONSTR_B && f_blackB);
}

//
//
COLOUR_T BSPcomplex::blackAB_or_white(uint64_t face_ind, bool two_input){
  const BSPface& face = faces[face_ind];

  // Get dominant normal component
  int xyz = face_dominant_normal_component(face);

  // Calculate approximated face barycenter
  double p[3];
  get_approx_faceBaricenterCoord(face, p);
  const explicitPoint3D face_center(p[0], p[1], p[2]);

  // Needed only for two input case.
  bool face_is_blackA = false;
  bool face_is_blackB = false;

  // Check whether the barycenter is indeed inside the face (might be not due to approximation)
  if ( is_baricenter_inFace(face, face_center, xyz) ) // Barycenter is inside the face: just check that it is in one of the constraints too
  {
      for (uint32_t c = 0; c < face.coplanar_constraints.size(); c++) {
          const uint32_t constr = face.coplanar_constraints[c];
          const CONSTR_GROUP_T c_group = constraint_group[constr];

          if( two_input && faceColour_matches_constrGroup(c_group, face_is_blackA, face_is_blackB) ) continue;

          const uint32_t constr_ID = 3 * constr;
          const genericPoint* c0 = vertices[ constraints_vrts[constr_ID   ] ];
          const genericPoint* c1 = vertices[ constraints_vrts[constr_ID +1] ];
          const genericPoint* c2 = vertices[ constraints_vrts[constr_ID +2] ];

          if (genericPoint::pointInTriangle(face_center, *c0, *c1, *c2, xyz)){

            if(!two_input) return BLACK_A;

            if(c_group == CONSTR_A)      face_is_blackA = true;
            else if(c_group == CONSTR_B) face_is_blackB = true;
            if(face_is_blackA && face_is_blackB) return BLACK_AB;
          }
      }

      if(two_input){
        if(face_is_blackA) return BLACK_A;
        else if(face_is_blackB) return BLACK_B;
      }

      return WHITE;
  }
  else // Barycenter is not inside the face: revert to slow version
  {
      const BSPedge& edge0 = edges[face.edges.back()];
      const BSPedge& edge1 = edges[face.edges[0]];
      uint32_t vid = consecEdges_common_endpt(edge0.vertices[0], edge0.vertices[1],
                                              edge1.vertices[0], edge1.vertices[1]);

      for (uint64_t e = 0; e < face.edges.size(); e++) {
          const BSPedge& edge = edges[face.edges[e]];
          if (vid == edge.vertices[0]) vid = edge.vertices[1];
          else vid = edge.vertices[0];

          uint32_t out_from_all = 0;
          const genericPoint* face_pt = vertices[vid];
          for (uint32_t c = 0; c < face.coplanar_constraints.size(); c++) {
              const uint32_t constr = face.coplanar_constraints[c];
              const CONSTR_GROUP_T c_group = constraint_group[constr];

              if( two_input && faceColour_matches_constrGroup(c_group, face_is_blackA, face_is_blackB) ) continue;

              const uint32_t constr_ID = 3 * constr;
              const uint32_t vid1 = constraints_vrts[constr_ID];
              const uint32_t vid2 = constraints_vrts[constr_ID + 1];
              const uint32_t vid3 = constraints_vrts[constr_ID + 2];
              const genericPoint* c0 = vertices[vid1];
              const genericPoint* c1 = vertices[vid2];
              const genericPoint* c2 = vertices[vid3];

              if (vid == vid1 || vid == vid2 || vid == vid3) break; // point on boundary.
              int lpt = localizedPointInTriangle(*face_pt, *c0, *c1, *c2, xyz);
              // lpt = 1 -> point on boundary, lpt = 2 -> point in interior, otherwise lpt = 0.

              if (lpt == 2){

                if(!two_input) return BLACK_A;

                if(c_group == CONSTR_A) face_is_blackA = true;
                else if(c_group == CONSTR_B) face_is_blackB = true;
                if(face_is_blackA && face_is_blackB) return BLACK_AB;
              }

              if (lpt)  break;

              out_from_all++;
          }
          if (out_from_all == face.coplanar_constraints.size()) return WHITE;
      }

      // All face vertices are on the boundary of some coplanar constraints.
      for (uint32_t c = 0; c < face.coplanar_constraints.size(); c++) {

          const uint32_t constr = face.coplanar_constraints[c];
          const CONSTR_GROUP_T c_group = constraint_group[ constr ];
          if( two_input && faceColour_matches_constrGroup(c_group, face_is_blackA, face_is_blackB) ) continue;

          const uint32_t *constraint = constraints_vrts.data() + 3*constr;
          if (coplanar_constraint_innerIntersects_face(face.edges, constraint, xyz)){

            if(!two_input) return BLACK_A;

            if(c_group == CONSTR_A) face_is_blackA = true;
            else if(c_group == CONSTR_B) face_is_blackB = true;
            if(face_is_blackA && face_is_blackB) return BLACK_AB;

          }
      }

      if(face_is_blackA) return BLACK_A;
      else if(face_is_blackB) return BLACK_B;

      return WHITE;
  }

}


//----------------
// BSP subdivision
//----------------

//
//

void BSPcomplex::extractSkinTriMesh(const char* filename, const char bool_opcode,
    double** coords, uint32_t* npts, uint32_t** tri_idx, uint32_t* ntri)
{
    const uint64_t num_faces = faces.size();
    for (uint64_t face_ind = 0; face_ind < num_faces; face_ind++) triangulateFace(face_ind);

    if (bool_opcode == 'U') { // Union
        for (BSPcell& cell : cells)
            cell.place = (cell.place == INTERNAL_A || cell.place == INTERNAL_B || cell.place == INTERNAL_AB) ? (INTERNAL_A) : (EXTERNAL);
    }

    else if (bool_opcode == 'I') { // Intersection
        for (BSPcell& cell : cells)
            cell.place = (cell.place == INTERNAL_AB) ? (INTERNAL_A) : (EXTERNAL);
    }

    else if (bool_opcode == 'D') { // Difference A\B
        for (BSPcell& cell : cells)
            cell.place = (cell.place == INTERNAL_A && !(cell.place == INTERNAL_AB)) ? (INTERNAL_A) : (EXTERNAL);
    }

    // Set "internal" depending on bool_opcode and find border faces to save
    vector<uint64_t> mark(faces.size(), 0);
    for (BSPcell& cell : cells)
        if (cell.place == INTERNAL_A)
            for (uint64_t fi = 0; fi < cell.faces.size(); fi++)
                mark[cell.faces[fi]]++;

    for (size_t i = 0; i < vrts_visit.size(); i++) vrts_visit[i] = 0;
    for (size_t i = 0; i < edges.size(); i++) edge_visit[i] = 0;

    uint64_t num_border_faces = 0;
    for (uint64_t f_i = 0; f_i < faces.size(); f_i++) if (mark[f_i] == 1)
    {
        num_border_faces++;
        for (uint64_t eid : faces[f_i].edges) edge_visit[eid] = 1;
    }
    for (size_t i = 0; i < edges.size(); i++) if (edge_visit[i])
        vrts_visit[edges[i].vertices[0]] = vrts_visit[edges[i].vertices[1]] = 1;

    std::vector<uint32_t> vmap(vertices.size(), 0);
    size_t num_v = 0;
    for (size_t i = 0; i < vrts_visit.size(); i++)
    {
        vmap[i] = (uint32_t)num_v;
        if (vrts_visit[i]) num_v++;
    }

    *coords = (double*)malloc(sizeof(double) * 3 * num_v);
    *tri_idx = (uint32_t*)malloc(sizeof(uint32_t) * 3 * num_border_faces);
    *npts = num_v;
    *ntri = num_border_faces;

    // Store vertex coordinates
    for (uint32_t v = 0, i = 0; v < vertices.size(); v++) if (vrts_visit[v])
    {
        vertices[v]->getApproxXYZCoordinates((*coords)[i], (*coords)[i+1], (*coords)[i+2], true);
        i += 3;
    }

    // Print border faces
    for (uint32_t f_i = 0, i = 0; f_i < faces.size(); f_i++)
        if (mark[f_i] == 1) {
            BSPface& face = faces[f_i];
            vector<uint32_t> face_vrts(face.edges.size(), UINT32_MAX);
            list_faceVertices(face, face_vrts);

            if (cells[face.conn_cells[0]].place == INTERNAL_A)
                for (uint32_t v = (uint32_t)face_vrts.size(); v > 0; v--) (*tri_idx)[i++] = vmap[face_vrts[v - 1]];
            else
                for (uint32_t v = 0; v < face_vrts.size(); v++) (*tri_idx)[i++] = vmap[face_vrts[v]];
        }
}


void BSPcomplex::saveSkin(const char *filename, const char bool_opcode, bool triangulate) {

    if (triangulate)
    {
        const uint64_t num_faces = faces.size();
        for (uint64_t face_ind = 0; face_ind < num_faces; face_ind++) triangulateFace(face_ind);
    }

    if (bool_opcode == 'U') { // Union
        for (BSPcell& cell : cells)
            cell.place = (cell.place == INTERNAL_A || cell.place == INTERNAL_B || cell.place == INTERNAL_AB) ? (INTERNAL_A) : (EXTERNAL);
    }

    else if (bool_opcode == 'I') { // Intersection
        for (BSPcell& cell : cells)
            cell.place = (cell.place == INTERNAL_AB) ? (INTERNAL_A) : (EXTERNAL);
    }

    else if (bool_opcode == 'D') { // Difference A\B
        for (BSPcell& cell : cells)
            cell.place = (cell.place == INTERNAL_A && !(cell.place == INTERNAL_AB)) ? (INTERNAL_A) : (EXTERNAL);
    }

    // Set "internal" depending on bool_opcode and find border faces to save
    vector<uint64_t> mark(faces.size(), 0);
    for (BSPcell& cell : cells)
            if (cell.place == INTERNAL_A)
                for (uint64_t fi = 0; fi < cell.faces.size(); fi++)
                    mark[cell.faces[fi]]++;

    for (size_t i = 0; i < vrts_visit.size(); i++) vrts_visit[i] = 0;
    for (size_t i = 0; i < edges.size(); i++) edge_visit[i] = 0;

    uint64_t num_border_faces = 0;
    for (uint64_t f_i = 0; f_i < faces.size(); f_i++) if (mark[f_i] == 1)
    {
         num_border_faces++;
         for (uint64_t eid : faces[f_i].edges) edge_visit[eid] = 1;
    }
    for (size_t i = 0; i < edges.size(); i++) if (edge_visit[i])
        vrts_visit[edges[i].vertices[0]] = vrts_visit[edges[i].vertices[1]] = 1;

    std::vector<uint32_t> vmap(vertices.size(), 0);
    size_t num_v = 0;
    for (size_t i = 0; i < vrts_visit.size(); i++)
    {
        vmap[i] = (uint32_t)num_v;
        if (vrts_visit[i]) num_v++;
    }

    ofstream f(filename);

    if (!f)
        ip_error("BSPcomplex::saveSkin: cannot open the file.\n");

    f << "OFF\n";
    f << num_v << " ";
    f << num_border_faces << " ";
    f << "0\n";

    // Print vertices coordinates
    for (uint32_t v = 0; v < vertices.size(); v++) if (vrts_visit[v])
        f << (*(vertices)[v]) << "\n";

    // Print border faces
    for (uint32_t f_i = 0; f_i < faces.size(); f_i++)
        if (mark[f_i] == 1) {
            BSPface& face = faces[f_i];
            vector<uint32_t> face_vrts(face.edges.size(), UINT32_MAX);
            list_faceVertices(face, face_vrts);
            f << face_vrts.size();

            if (cells[face.conn_cells[0]].place == INTERNAL_A)
                for (uint32_t v = (uint32_t)face_vrts.size(); v > 0; v--) f << " " << vmap[face_vrts[v - 1]];
            else
                for (uint32_t v = 0; v < face_vrts.size(); v++) f << " " << vmap[face_vrts[v]];

            f << "\n";
        }

    f.close();

}


//
//
void BSPcomplex::saveMesh(const char* filename, const char bool_opcode, bool tetrahedrize)
{
    ofstream f(filename);

    if (!f) ip_error("\nBSPcomplex::[BSP.cpp]saveTetMesh: FATAL ERROR cannot open the file.\n");

    const uint64_t num_faces = faces.size();

    if (tetrahedrize)
        for (uint64_t face_ind = 0; face_ind < num_faces; face_ind++) triangulateFace(face_ind);

    if (bool_opcode == 'U') { // Union
        for (BSPcell& cell : cells)
            cell.place = (cell.place == INTERNAL_A || cell.place == INTERNAL_B || cell.place == INTERNAL_AB) ? (INTERNAL_A) : (EXTERNAL);
    }

    else if (bool_opcode == 'I') { // Intersection
        for (BSPcell& cell : cells)
            cell.place = (cell.place == INTERNAL_AB) ? (INTERNAL_A) : (EXTERNAL);
    }

    else if (bool_opcode == 'D') { // Difference A\B
        for (BSPcell& cell : cells)
            cell.place = (cell.place == INTERNAL_A && !(cell.place == INTERNAL_AB)) ? (INTERNAL_A) : (EXTERNAL);
    }

    if (tetrahedrize)
    {
        makeTetrahedra();

        for (size_t i = 0; i < vrts_visit.size(); i++) vrts_visit[i] = 0;
        for (uint32_t t = 0; t < final_tets.size(); t++) vrts_visit[final_tets[t]] = 1;
        uint32_t final_numver = 0;
        for (size_t i = 0; i < vrts_visit.size(); i++) if (vrts_visit[i]) final_numver++;

        std::vector<uint32_t> vmap(vertices.size(), 0);
        size_t num_v = 0;
        for (size_t i = 0; i < vrts_visit.size(); i++)
        {
            vmap[i] = (uint32_t)num_v;
            if (vrts_visit[i]) num_v++;
        }

        f << final_numver << " vertices\n";
        f << final_tets.size() / 4 << " tets\n";

        // Print vertices coordinates
        for (uint32_t v = 0; v < vertices.size(); v++) if (vrts_visit[v]) f << (*vertices[v]) << "\n";

        // Print tets
        for (uint32_t t = 0; t < final_tets.size(); t += 4)
            f << "4 " << vmap[final_tets[t]] << " "
            << vmap[final_tets[t + 1]] << " "
            << vmap[final_tets[t + 2]] << " "
            << vmap[final_tets[t + 3]] << "\n";
    }
    else
    {
        size_t internal_cell_num = 0;
        std::vector<uint32_t> face_visit(faces.size(), 0);
        for (size_t i = 0; i < vrts_visit.size(); i++) vrts_visit[i] = 0;
        for (size_t i = 0; i < edge_visit.size(); i++) edge_visit[i] = 0;
        for (BSPcell& cell : cells) if (cell.place == INTERNAL_A)
        {
            internal_cell_num++;
            for (uint64_t f : cell.faces) face_visit[f] = 1;
        }
        for (size_t f = 0; f < faces.size(); f++) if (face_visit[f]) for (uint64_t e : faces[f].edges) edge_visit[e] = 1;
        for (size_t e = 0; e < edges.size(); e++) if (edge_visit[e]) vrts_visit[edges[e].vertices[0]] = vrts_visit[edges[e].vertices[1]] = 1;
        uint32_t final_numver = 0;
        for (size_t i = 0; i < vrts_visit.size(); i++) if (vrts_visit[i]) final_numver++;

        std::vector<uint32_t> vmap(vertices.size(), 0);
        size_t num_v = 0;
        for (size_t i = 0; i < vrts_visit.size(); i++)
        {
            vmap[i] = (uint32_t)num_v;
            if (vrts_visit[i]) num_v++;
        }

        f << final_numver << " vertices\n";
        f << internal_cell_num << " cells\n";

        // Print vertices coordinates
        for (uint32_t v = 0; v < vertices.size(); v++) if (vrts_visit[v]) f << (*vertices[v]) << "\n";

        // Print cells
        for (size_t v = 0; v < vertices.size(); v++) vrts_visit[v] = 0;
        for (size_t e = 0; e < edges.size(); e++) edge_visit[e] = 0;
        for (BSPcell& cell : cells) if (cell.place == INTERNAL_A)
        {
            uint64_t numce = count_cellEdges(cell);
            uint32_t numcv = count_cellVertices(cell, &numce);
            std::vector<uint32_t> cell_vrts(numcv);
            list_cellVertices(cell, numce, cell_vrts);
            f << cell_vrts.size() << " ";
            for (uint32_t i : cell_vrts) f << vmap[i] << " ";
            f << "\n";
        }
    }

    f.close();

    final_tets.clear();
}


void BSPcomplex::saveBlackFaces(const char* filename, bool triangulate) {
    ofstream f(filename);

    if (!f)
        ip_error("BSPcomplex::saveBlackFaces: cannot open the file.\n");

    if (triangulate)
    {
        const uint64_t num_faces = faces.size();
        for (uint64_t face_ind = 0; face_ind < num_faces; face_ind++) triangulateFace(face_ind);
    }

    for (size_t i = 0; i < vrts_visit.size(); i++) vrts_visit[i] = 0;
    for (size_t i = 0; i < edges.size(); i++) edge_visit[i] = 0;

    uint64_t num_border_faces = 0;
    for (uint64_t f_i = 0; f_i < faces.size(); f_i++) if (faces[f_i].colour != WHITE)
    {
        num_border_faces++;
        for (uint64_t eid : faces[f_i].edges) edge_visit[eid] = 1;
    }
    for (size_t i = 0; i < edges.size(); i++) if (edge_visit[i])
        vrts_visit[edges[i].vertices[0]] = vrts_visit[edges[i].vertices[1]] = 1;

    std::vector<uint32_t> vmap(vertices.size(), 0);
    size_t num_v = 0;
    for (size_t i = 0; i < vrts_visit.size(); i++)
    {
        vmap[i] = (uint32_t)num_v;
        if (vrts_visit[i]) num_v++;
    }

    f << "OFF\n";
    f << num_v << " ";
    f << num_border_faces << " ";
    f << "0\n";

    // Print vertices coordinates
    for (uint32_t v = 0; v < vertices.size(); v++) if (vrts_visit[v])
        f << (*(vertices)[v]) << "\n";

    // Print border faces
    for (uint32_t f_i = 0; f_i < faces.size(); f_i++)
        if (faces[f_i].colour != WHITE) {
            BSPface& face = faces[f_i];
            vector<uint32_t> face_vrts(face.edges.size(), UINT32_MAX);
            list_faceVertices(face, face_vrts);
            f << face_vrts.size();

            for (uint32_t v = 0; v < face_vrts.size(); v++) f << " " << vmap[face_vrts[v]];

            f << "\n";
        }

    f.close();
}


size_t BSPcomplex::getStructureSize() const
{
    size_t tot = 0;

    // Size for points
    for (genericPoint* p : vertices)
    {
        if (p->isExplicit3D()) tot += sizeof(explicitPoint3D);
        else if (p->isLPI()) tot += sizeof(implicitPoint3D_LPI);
        else tot += sizeof(implicitPoint3D_TPI);
    }

    // Size of the array of pointers
    tot += vertices.size() * sizeof(genericPoint*);

    // Size of edge objects
    tot += edges.size() * sizeof(BSPedge);

    // Size of face objects (including pointed arrays)
    for (const BSPface& f : faces) tot += f.getSize();

    // Size of cell objects (including pointed arrays)
    for (const BSPcell& c : cells) tot += c.getSize();

    // And all the other vectors use by the structure...
    tot += sizeof(uint32_t) * constraints_vrts.size();
    tot += sizeof(CONSTR_GROUP_T) * constraint_group.size();
    tot += sizeof(uint32_t) * final_tets.size();
    tot += sizeof(char) * vrts_orBin.size();
    tot += sizeof(uint32_t) * vrts_visit.size();
    tot += sizeof(uint64_t) * edge_visit.size();
    tot += sizeof(BSPcomplex);

    return tot;
}

back to top

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API