]> SALOME platform Git repositories - tools/medcoupling.git/commitdiff
Salome HOME
WIP warped faces ... still not working properly ... abn/bs3d_warp
authorabn <adrien.bruneton@cea.fr>
Thu, 16 Dec 2021 14:30:25 +0000 (15:30 +0100)
committerabn <adrien.bruneton@cea.fr>
Thu, 16 Dec 2021 14:30:25 +0000 (15:30 +0100)
src/MEDCoupling/MEDCouplingUMesh_internal.cxx

index 5deca19a67fdaa99b68120f7108e5a3108e95dd1..5cafb7e47a584ecbb9973e1e96ea21ec9551e31d 100755 (executable)
@@ -581,7 +581,7 @@ void MEDCouplingUMesh::split3DCurveWithPlane(const double *origin, const double
                   const double *st2=coo+3*st;
                   vec4[0]=st2[0]-origin[0]; vec4[1]=st2[1]-origin[1]; vec4[2]=st2[2]-origin[2];
                   double pos=-(vec4[0]*vec2[0]+vec4[1]*vec2[1]+vec4[2]*vec2[2])/((vec3[0]*vec2[0]+vec3[1]*vec2[1]+vec3[2]*vec2[2]));
-                  if(pos>eps && pos<1-eps)
+                  if(pos>eps && pos<1-eps) // intersecting and creating a new point sufficiently far away from the extremities
                     {
                       mcIdType nNode=ToIdType(addCoo.size())/3;
                       vec4[0]=st2[0]+pos*vec3[0]; vec4[1]=st2[1]+pos*vec3[1]; vec4[2]=st2[2]+pos*vec3[2];
@@ -1612,65 +1612,115 @@ void MEDCouplingUMesh::AssemblyForSplitFrom3DCurve(const std::vector<mcIdType>&
   mcIdType nbOf3DSurfCell=ToIdType(cut3DSurf.size());
   for(mcIdType i=0;i<nbOf3DSurfCell;i++)
     {
-      std::vector<mcIdType> res;
+      std::vector<mcIdType> res;    // list of points that will be used to reconstruct intersection polygon
+      std::vector<bool> resIsSeg;   // same size as res, true if the point is part of a segment
+      mcIdType segCnt = 0;
       mcIdType offset=descIndx[i];
       mcIdType nbOfSeg=descIndx[i+1]-offset;
-      for(mcIdType j=0;j<nbOfSeg;j++)  // for all segments of a face of the initial 3D mesh
+      // Loop: for all segments of a face of the initial 3D mesh, record either a (single) point of intersection with the given segment
+      // or a full segment if it is coplanar.
+      // We also should deal with limiting cases here: if we just added a full segment, the next edge can not have a single
+      // intersecting point (if it is so, it means that this point is actually very close to one of the extremity, and we should
+      // discard it).
+      unsigned lastPushed=-1; // 1 means single point, 2 means seg, 0 means nothing
+      bool lastWillBeSeg = cut3DCurve[nbOfSeg-1] > -1 ;  // whether a full segment will be added when scanning the last edge
+      for(mcIdType j=0;j<nbOfSeg;j++)
         {
           mcIdType edgeId=desc[offset+j];
           mcIdType status=cut3DCurve[edgeId];
           if(status!=-2)    // segment is being intersected (BUT a segment might be colinear with a -2)
             {
               if(status>-1)  // ... just once, hence using one of the newly created point
-                res.push_back(status);
-              else  // twice: the segment is inside the plane. Take two existing points
                 {
-                  res.push_back(nodal3DCurve[nodalIndx3DCurve[edgeId]+1]);
-                  res.push_back(nodal3DCurve[nodalIndx3DCurve[edgeId]+2]);
+                  // if we pushed a seg just before, this intersection point is very close to one extremity
+                  // "just before" can also mean the last edge in the face, so we need to look this too ...
+                  if (lastPushed != 2 && !lastWillBeSeg)
+                    { res.push_back(status);  resIsSeg.push_back(false);
+                      lastPushed = 1; }
+                  else
+                    lastPushed = 0;
+                }
+              else  // status==-1 : two intersec: the segment is inside the plane. Take two existing points.
+                {
+                  if (lastPushed == 1) // Remove last added point as it is very close to extremity
+                    { res.pop_back(); resIsSeg.pop_back(); }
+                  res.push_back(nodal3DCurve[nodalIndx3DCurve[edgeId]+1]); resIsSeg.push_back(true);
+                  res.push_back(nodal3DCurve[nodalIndx3DCurve[edgeId]+2]); resIsSeg.push_back(true);
+                  lastPushed = 2;
+                  segCnt++;
                 }
             }
+          else
+            lastPushed = 0;
         }
+
       // At the end of the loop, res might have been filled several times even for a single face.
-      switch(res.size())
-      {
-        case 2:  // regular case, two intersection points
-          {
-            cut3DSurf[i].first=res[0]; cut3DSurf[i].second=res[1];
-            break;
-          }
-        case 1:  // 1 and 0 means colinear or surface cell touching plane by exact corner points
-        case 0:
-          {
-            std::set<mcIdType> s1(nodal3DSurf+nodalIndx3DSurf[i]+1,nodal3DSurf+nodalIndx3DSurf[i+1]);
-            std::set_intersection(nodesOnP.begin(),nodesOnP.end(),s1.begin(),s1.end(),std::back_insert_iterator< std::vector<mcIdType> >(res));
-            if(res.size()==2)
-              {
-                cut3DSurf[i].first=res[0]; cut3DSurf[i].second=res[1];
-              }
-            else  // only one touching point (or none) - nothing recorded
-              {
-                cut3DSurf[i].first=-1; cut3DSurf[i].second=-1;
-              }
-            break;
-          }
-        default:
-          {// case when the face is completly included in the plane
-            if(ToIdType(res.size())==2*nbOfSeg)
-              {
-                cut3DSurf[i].first=-2; cut3DSurf[i].second=i;
-              }
-            else {
-                cut3DSurf[i].first = -3; cut3DSurf[i].second=i;
-                // Fills in warpMap: it is important for the rest of the algo that the nodes are filled in the proper order
-                // i.e. in the same order as the edges around the face. Luckily for us, we have this for free:
-                // the for loop above on the edges in the right order because the descending connectivity of a mesh already ensures
-                // the proper ordering of the segments in desc/descI!!
-                std::vector<mcIdType>& nodesW = warpMap[i];
-                nodesW.resize(res.size());
-                std::copy(res.begin(), res.end(), nodesW.begin());
+      if(res.size() == 2)
+        { // regular case, two intersection points
+          cut3DSurf[i].first=res[0]; cut3DSurf[i].second=res[1];
+        }
+      else if (res.size() <= 1 && segCnt == 0)
+        { // one segment included in the plane, or just one point touching
+          std::set<mcIdType> s1(nodal3DSurf+nodalIndx3DSurf[i]+1,nodal3DSurf+nodalIndx3DSurf[i+1]);
+          std::set_intersection(nodesOnP.begin(),nodesOnP.end(),s1.begin(),s1.end(),
+                                std::back_insert_iterator< std::vector<mcIdType> >(res));
+          if(res.size()==2)
+            {  cut3DSurf[i].first=res[0]; cut3DSurf[i].second=res[1];  }
+          else  // only one touching point (or none) - nothing recorded
+            {  cut3DSurf[i].first=-1; cut3DSurf[i].second=-1;          }
+        }
+      else if(segCnt == nbOfSeg)
+        {   // exact inclusion of a (clean plane) face into the plane
+          cut3DSurf[i].first=-2; cut3DSurf[i].second=i;
+          if(ToIdType(res.size()) != 2*nbOfSeg)
+            throw INTERP_KERNEL::Exception("MEDCouplingUMesh::AssemblyForSplitFrom3DCurve: woopsy, unexpected situation...");
+        }
+      else if (res.size() == 4 || segCnt >=2)   // warped case!! Only support warped QUAD4 for now
+        {
+          cut3DSurf[i].first = -3; cut3DSurf[i].second=i;
+          // Rebuild the proper connectivity of the central polygon that will be inserted for this face
+
+          // With QUAD4, the coplanar segments are necessarily consecutive (even if warped)
+          // Fills in warpMap: it is important for the rest of the algo that the nodes are filled in the proper order
+          // i.e. in the same order as the edges around the face. Luckily for us, we have this for free:
+          // the for loop above on the edges in the right order because the descending connectivity of a mesh already ensures
+          // the proper ordering of the segments in desc/descI!!
+          std::vector<mcIdType>& nodesW = warpMap[i];
+          bool inSegs = false;
+          std::vector<mcIdType> segs;
+          for (mcIdType kk = 0; kk <= ToIdType(res.size()); kk++)  // yes one slot more to cover last iteration
+            {
+              if (inSegs && (kk==ToIdType(res.size()) || !resIsSeg[kk]))
+                { // Unstack properly previously recorded set of (consecutive) segments to re-order points correctly
+                  if (segs[1]==segs[2] || segs[1]==segs[3])
+                    { nodesW.push_back(segs[0]);
+                      nodesW.push_back(segs[1]);
+                    }
+                  else {
+                      nodesW.push_back(segs[1]);
+                      nodesW.push_back(segs[0]);
+                  }
+                  if(nodesW[1] == segs[2]) nodesW.push_back(segs[3]);
+                  else                     nodesW.push_back(segs[2]);
+
+                  for (mcIdType jj = 2; jj < ToIdType(segs.size()/2); jj++)
+                    if (segs[jj*2] != segs[(jj-1)*2+1])
+                      nodesW.push_back(segs[jj*2+1]);
+                    else
+                      nodesW.push_back(segs[jj*2]);
+                  inSegs = false;
+                }
+              if (kk==ToIdType(res.size())) break; // last iteration, we should stop here
+              if (!resIsSeg[kk]) // regular point (not part of a segment)
+                  nodesW.push_back(res[kk]);
+              else
+                { inSegs = true; segs.push_back(res[kk]); }
             }
-          }
-      }
+        }
+      else  // face close to a warp face might lead to funny things - discard
+        {
+          cut3DSurf[i].first = -4; cut3DSurf[i].second=i;
+        }
     }
 }
 
@@ -1696,43 +1746,36 @@ void MEDCouplingUMesh::assemblyForSplitFrom3DSurf(const std::vector< std::pair<m
   mcIdType nbOfCells=getNumberOfCells();
   for(mcIdType i=0;i<nbOfCells;i++)
     {
-      std::map<mcIdType, std::set<mcIdType> > m;
+      std::map<mcIdType, std::set<mcIdType> > m; // map giving for each vertex of the reconstructed 2D cells, the set set of points
+                                                 // connected to it - said differently this describes the edges of the polygon to build
       mcIdType offset=descIndx[i];
       mcIdType nbOfFaces=descIndx[i+1]-offset;
       mcIdType start=-1;
       mcIdType end=-1;
+      const std::vector<mcIdType> *vecWarp = nullptr;
       for(int j=0;j<nbOfFaces;j++)
         {
           const std::pair<mcIdType,mcIdType>& p=cut3DSurf[desc[offset+j]];
           if(p.first!=-1 && p.second!=-1)
             {
-              if(p.first!=-2 && p.first!=-3)  // not a coplanar polygon, not a warped face
+              if(p.first > -2)  // not a coplanar polygon (-2), not a warped face (-3), not an ignored case (-4)
                 {
                   start=p.first; end=p.second;
                   m[p.first].insert(p.second);
                   m[p.second].insert(p.first);
                 }
               else if (p.first == -3)
-                { // Warped face ... for now we only handle warped QUADs ...
-//                  const std::vector<mcIdType> & vec = warpMap.at(offset+j);
-//                  if (vec.size() != 4)
-//                    throw INTERP_KERNEL::Exception("MEDCouplingUMesh::assemblyForSplitFrom3DSurf: only warped QUAD4 are properly handled! Looks like we are hitting a more complex case ... sorry.");
-//                  start = vec.front(); end = vec.back();
-//                  mcIdType k=0, sz=vec.size();
-//                  // Handle this as if we had two different faces: insert (0,1), (1,0) in the map
-//                  // and then (2,3) and (3,2). Nothing more (not (1,2) ...)
-//                  m[vec[0]].insert(vec[1]);
-//                  m[vec[1]].insert(vec[0]);
-//                  m[vec[2]].insert(vec[3]);
-//                  m[vec[3]].insert(vec[2]);
-//                  for (auto it = vec.begin(); it != vec.end(); it++, k++)
-//                    {
-//                      mcIdType id1=*it, id2=vec[(k+1)%sz];
-//                      m[id1].insert(id2);
-//                      m[id2].insert(id1);
-//                    }
+                { // Warped face ... for now we only handle warped QUADs, and only one warped face per 3D cell
+                  if (vecWarp)
+                    throw INTERP_KERNEL::Exception("MEDCouplingUMesh::assemblyForSplitFrom3DSurf: more than one warped face in a cell!! Not supported.");
+                  vecWarp = &warpMap.at(p.second);
                 }
-              else  // face is coplanar to the plane, copy it entirely. TODO: here we do this
+              else if (p.first == -4)
+                {
+                  // Do nothing ... face close to another warped face
+                }
+              else  // p.first == -2
+                    // face is coplanar to the plane, copy it entirely. TODO: here we do this
                     // by extracting its descending connectivity so that the reconstruction algo (10 lines down below) works the same.
                     // this could probably be done more efficiently! (convertToPoly and copy connectivity???)
                 {
@@ -1753,33 +1796,73 @@ void MEDCouplingUMesh::assemblyForSplitFrom3DSurf(const std::vector< std::pair<m
       if(m.empty())
         continue;
       // Now rebuild new 2D cell connectivity corresponding to the currently intersected 3D cell:
-      std::vector<mcIdType> conn(1,ToIdType(INTERP_KERNEL::NORM_POLYGON));
-      mcIdType prev=end;
-      while(end!=start)
+      // Classical (non-warped) case:
+      if (!vecWarp)
         {
-          std::map<mcIdType, std::set<mcIdType> >::const_iterator it=m.find(start);
-          const std::set<mcIdType>& s=(*it).second;
-          std::set<mcIdType> s2; s2.insert(prev);
-          std::set<mcIdType> s3;
-          std::set_difference(s.begin(),s.end(),s2.begin(),s2.end(),inserter(s3,s3.begin()));
-          if(s3.size()==1)
+          std::vector<mcIdType> conn(1,ToIdType(INTERP_KERNEL::NORM_POLYGON));
+          mcIdType prev=end;
+          while(end!=start)
+            {
+              std::map<mcIdType, std::set<mcIdType> >::const_iterator it=m.find(start);
+              const std::set<mcIdType>& s=(*it).second;
+              std::set<mcIdType> s2; s2.insert(prev);
+              std::set<mcIdType> s3;
+              std::set_difference(s.begin(),s.end(),s2.begin(),s2.end(),inserter(s3,s3.begin()));
+              if(s3.size()==1)
+                {
+                  mcIdType val=*s3.begin();
+                  conn.push_back(start);
+                  prev=start;
+                  start=val;
+                }
+              else
+                start=end;
+            }
+          conn.push_back(end);
+          if(conn.size()>3)
             {
-              mcIdType val=*s3.begin();
-              conn.push_back(start);
-              prev=start;
-              start=val;
+              nodalRes->insertAtTheEnd(conn.begin(),conn.end());
+              nodalResIndx->pushBackSilent(nodalRes->getNumberOfTuples());
+              cellIds->pushBackSilent(i);
             }
-          else
-            start=end;
         }
-      conn.push_back(end);
-      if(conn.size()>3)
+      else  // Cell with a warped face
         {
-          nodalRes->insertAtTheEnd(conn.begin(),conn.end());
+          // When a plane intersects a warped face, we have (in the worst case):
+          // - two (disjoint) triangles
+          // - connected by a single quad
+          // We start with the two triangles by identifying the points with a set containing two items
+          std::set<mcIdType> consumed;
+          mcIdType kk=0;
+          for(auto it=m.begin(); it != m.end(); it++, kk++)
+            {
+              const std::set<mcIdType>& s = (*it).second;
+              const mcIdType nodId = (*it).first;
+              if (consumed.find(nodId) != consumed.end()) continue;
+              if(s.size() == 2)
+                {
+                  const mcIdType nod1=*s.begin(), nod2=*s.rbegin();
+                  const auto &s1 = m.at(nod1), &s2 = m.at(nod2);
+                  if (s1.size() != 1 || s2.size() != 1 )
+                    throw INTERP_KERNEL::Exception("MEDCouplingUMesh::assemblyForSplitFrom3DSurf: unexpected situation when building intersection with warped face ...");
+                  nodalRes->pushBackSilent(ToIdType(INTERP_KERNEL::NORM_POLYGON));
+                  nodalRes->pushBackSilent(nod1);    consumed.insert(nod1);
+                  nodalRes->pushBackSilent(nodId);   consumed.insert(nodId);
+                  nodalRes->pushBackSilent(nod2);    consumed.insert(nod2);
+                  nodalResIndx->pushBackSilent(nodalRes->getNumberOfTuples());
+                  cellIds->pushBackSilent(i);
+                }
+            }
+          // Finally insert the QUAD4 (but as a polygon) corresponding to the central piece
+          if (vecWarp->size() < 4)
+            throw INTERP_KERNEL::Exception("MEDCouplingUMesh::assemblyForSplitFrom3DSurf: should not happen?");
+          std::vector<mcIdType> connQuad(vecWarp->size()+1,ToIdType(INTERP_KERNEL::NORM_POLYGON));
+          std::copy(vecWarp->begin(), vecWarp->end(), connQuad.begin()+1); // +1 to skip type
+          nodalRes->insertAtTheEnd(connQuad.begin(), connQuad.end());
           nodalResIndx->pushBackSilent(nodalRes->getNumberOfTuples());
           cellIds->pushBackSilent(i);
         }
-    }
+    } // for(mcIdType i=0;i<nbOfCells ...
 }