diff --git a/core/ig/inc/gmds/ig/Face.h b/core/ig/inc/gmds/ig/Face.h index 539600d5f..80b29c1ff 100644 --- a/core/ig/inc/gmds/ig/Face.h +++ b/core/ig/inc/gmds/ig/Face.h @@ -116,6 +116,13 @@ namespace gmds{ */ TCoord area() const; + /*------------------------------------------------------------------------*/ + /** \brief Compute the signed area of the face + * + * \return the signed area of the face + */ + TCoord signedArea() const; + /*------------------------------------------------------------------------*/ /** \brief Compute the scaled jacobian of the face * diff --git a/core/ig/src/Face.cpp b/core/ig/src/Face.cpp index e399615f5..41e6ac98e 100644 --- a/core/ig/src/Face.cpp +++ b/core/ig/src/Face.cpp @@ -157,6 +157,38 @@ namespace gmds { } } /*----------------------------------------------------------------------------*/ + TCoord + Face::signedArea() const { + math::Vector3d n; + std::vector nodes = this->get(); + auto nb_nodes = nodes.size(); + + if (nb_nodes == 3) { + + Node n1 = nodes[0]; + Node n2 = nodes[1]; + Node n3 = nodes[2]; + math::Triangle t(n1.point(),n2.point(),n3.point()); + return t.signedArea(); + + } else if (nb_nodes == 4) { + math::Point p1 = nodes[0].point(); + math::Point p2 = nodes[1].point(); + math::Point p3 = nodes[2].point(); + math::Point p4 = nodes[3].point(); + math::Point c = center(); + math::Triangle t1(p1,p2,c); + math::Triangle t2(p2,p3,c); + math::Triangle t3(p3,p4,c); + math::Triangle t4(p4,p1,c); + + return t1.signedArea()+t2.signedArea()+t3.signedArea()+t4.signedArea(); + } else { + throw GMDSException("Not yet implemented!"); + + } + } +/*----------------------------------------------------------------------------*/ math::Point Face::center() const { diff --git a/core/math/inc/gmds/math/Triangle.h b/core/math/inc/gmds/math/Triangle.h index c7bfc23a5..8aa6bec9f 100644 --- a/core/math/inc/gmds/math/Triangle.h +++ b/core/math/inc/gmds/math/Triangle.h @@ -83,6 +83,11 @@ class Plane; */ double area() const; + /*------------------------------------------------------------------------*/ + /** \brief Computes the signed area of the triangle (works for triangles in the plane) + */ + double signedArea() const; + /*------------------------------------------------------------------------*/ /** \brief Computes the angle (in rad) of the triangle, as seen by its first vertex */ diff --git a/core/math/src/Triangle.cpp b/core/math/src/Triangle.cpp index 9c6ef6db5..716058f79 100644 --- a/core/math/src/Triangle.cpp +++ b/core/math/src/Triangle.cpp @@ -66,6 +66,17 @@ Triangle::~Triangle(){} return 0.5 * (v1.cross(v2)).norm(); } /*----------------------------------------------------------------------------*/ + double Triangle::signedArea() const + { + Vector3d v1 = m_pnts[1] - m_pnts[0]; + Vector3d v2 = m_pnts[2] - m_pnts[0]; + Vector3d cross_prod = v1.cross(v2); + if (cross_prod.Z() >= 0) + return 0.5 * cross_prod.norm(); + else + return -0.5 * cross_prod.norm(); + } + /*----------------------------------------------------------------------------*/ double Triangle::angle() const { Vector3d v1 = m_pnts[1] - m_pnts[0]; @@ -105,7 +116,7 @@ Point Triangle::getCircumcenter() const double yB = B.Y(); double xC = C.X(); double yC = C.Y(); - double S = area(); + double S = signedArea(); double xO = ((xA*xA+yA*yA)*(yB-yC)-(xB*xB+yB*yB)*(yA-yC)+(xC*xC+yC*yC)*(yA-yB))*(1./(4.*S)); double yO = -((xA*xA+yA*yA)*(xB-xC)-(xB*xB+yB*yB)*(xA-xC)+(xC*xC+yC*yC)*(xA-xB))*(1./(4.*S)); math::Point Ctr(xO, yO, 0.); diff --git a/modules/medialaxis/CMakeLists.txt b/modules/medialaxis/CMakeLists.txt index 2931822c3..ad1103ee8 100644 --- a/modules/medialaxis/CMakeLists.txt +++ b/modules/medialaxis/CMakeLists.txt @@ -2,7 +2,7 @@ # LIBRARY DEFINITION (SOURCE FILES) #============================================================================== # Explicitly used the name given in this preamble -set(GMDS_LIB ${LIB_GMDS_MEDIALAXIS}) +set(GMDS_LIB ${LIB_GMDS_MEDIAL_AXIS}) set(GMDS_LIB_PREFIX gmds/medialaxis) set(GMDS_INC @@ -13,6 +13,15 @@ set(GMDS_INC inc/gmds/medialaxis/MedialAxis3D.h inc/gmds/medialaxis/MedialAxis3DBuilder.h inc/gmds/medialaxis/CrossField.h + inc/gmds/medialaxis/NonConformalHalfEdge.h + inc/gmds/medialaxis/QuantizationSolver.h + inc/gmds/medialaxis/QuantizationGraph.h + inc/gmds/medialaxis/ConformalMeshBuilder.h + inc/gmds/medialaxis/BlockStructureSimplifier.h + inc/gmds/medialaxis/MinDelaunayCleaner.h + inc/gmds/medialaxis/Conformalizer.h + inc/gmds/medialaxis/MedaxBasedTMeshBuilder.h + inc/gmds/medialaxis/TrianglesRemover.h ) set(GMDS_SRC src/MedialAxis2D.cpp @@ -22,11 +31,14 @@ set(GMDS_SRC src/MedialAxis3DBuilder.cpp src/CrossField.cpp src/NonConformalHalfEdge.cpp - inc/gmds/medialaxis/NonConformalHalfEdge.h - src/QuantizationSolver.cpp - inc/gmds/medialaxis/QuantizationSolver.h + src/QuantizationSolver.cpp src/QuantizationGraph.cpp - inc/gmds/medialaxis/QuantizationGraph.h + src/ConformalMeshBuilder.cpp + src/BlockStructureSimplifier.cpp + src/MinDelaunayCleaner.cpp + src/Conformalizer.cpp + src/MedaxBasedTMeshBuilder.cpp + src/TrianglesRemover.cpp ) #============================================================================== add_library(${GMDS_LIB} ${GMDS_INC} ${GMDS_SRC}) diff --git a/modules/medialaxis/inc/gmds/medialaxis/BlockStructureSimplifier.h b/modules/medialaxis/inc/gmds/medialaxis/BlockStructureSimplifier.h new file mode 100644 index 000000000..c109c0a97 --- /dev/null +++ b/modules/medialaxis/inc/gmds/medialaxis/BlockStructureSimplifier.h @@ -0,0 +1,200 @@ +#ifndef GMDS_BLOCKSTRUCTURESIMPLIFIER_H +#define GMDS_BLOCKSTRUCTURESIMPLIFIER_H +/*----------------------------------------------------------------------------*/ +#include "GMDSMedialaxis_export.h" +#include "gmds/medialaxis/MedialAxis2D.h" +#include "gmds/medialaxis/NonConformalHalfEdge.h" +#include "gmds/medialaxis/MedialAxisMath.h" +#include "gmds/medialaxis/QuantizationGraph.h" +#include "gmds/io/IGMeshIOService.h" +#include "gmds/io/VTKWriter.h" +#include "gmds/ig/MeshDoctor.h" +#include +/*----------------------------------------------------------------------------*/ +namespace gmds{ +/** \class dummy + * \brief dummy class. + */ +class GMDSMedialaxis_API BlockStructureSimplifier +{ + private: + // Non conformal quad block decomposition + Mesh* m_mesh; + // Corresponding non-conformal half edges + std::vector m_half_edges; + // Quantization graph + QuantizationGraph* m_quantization_graph; + // Half edges length + std::vector m_half_edges_lengths; + // Mesh after removing half edges of length zero + Mesh* m_simplified_mesh; + + public: + + /*-------------------------------------------------------------------------*/ + /** @brief Constructor. + * @param AMesh + */ + explicit BlockStructureSimplifier(Mesh &AMesh); + + /*-------------------------------------------------------------------------*/ + /** @brief Default destructor. + * @param + */ + virtual ~BlockStructureSimplifier()=default; + + /*-------------------------------------------------------------------------*/ + /** @brief Getters. + * @param + */ + std::vector halfEdges(){return m_half_edges;} + std::vector halfEdgesLengths(){return m_half_edges_lengths;} + + /*-------------------------------------------------------------------------*/ + /** @brief Build the half edges. + * @param + */ + void buildHalfEdges(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the quantization graph nodes. + * @param + */ + void buildQuantizationGraphNodes(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the connected component of the given half edge. + * @param AHalfEdgeID an half edge ID + */ + void buildConnectedComponent(int AHalfEdgeID); + + /*-------------------------------------------------------------------------*/ + /** @brief Return the next of the next half edge of the given half edge. + * @param AHalfEdgeID an half edge ID + */ + int oppositeInQuad(int AHalfEdgeID); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the connected component graph. + * @param + */ + void buildQuantizationGraph(); + + /*-------------------------------------------------------------------------*/ + /** @brief Get the quantization graph. + * @param + */ + QuantizationGraph* getQuantizationGraph(); + + /*-------------------------------------------------------------------------*/ + /** @brief Set half edges length. + * @param + */ + void setHalfEdgesLength(); + + /*-------------------------------------------------------------------------*/ + /** @brief Return the set of sides of the input geometry, each side being a set of half-edge Ids. + * @param + */ + std::vector> sides(); + + /*-------------------------------------------------------------------------*/ + /** @brief Mark the T-junctions. + * @param + */ + void markTJunctions(); + + /*-------------------------------------------------------------------------*/ + /** @brief Return true if the given node is on the boundary. + * @param + */ + bool isOnBoundary(Node AN); + + /*-------------------------------------------------------------------------*/ + /** @brief Return the set of half-edges that must be non zero, ie that are adjacent to a singularity. + * @param + */ + std::vector nonZeroHalfEdges(); + + /*-------------------------------------------------------------------------*/ + /** @brief Return the set of groups of nodes, each group being a set of nodes connected by half-edges of length 0. + * @param + */ + std::vector> groupsOfConfundedNodes(); + + /*-------------------------------------------------------------------------*/ + /** @brief Return true if the given node is a corner of the boundary. + * @param + */ + bool isABoundaryCorner(Node AN); + + /*-------------------------------------------------------------------------*/ + /** @brief Compute the disappearance gain for each node. + * @param + */ + void computeChosingGains(); + + /*-------------------------------------------------------------------------*/ + /** @brief Return a representative for the given group of confunded nodes. + * @param AV + */ + Node representative(std::vector AV); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the simplified mesh. + * @param + */ + void buildSimplifiedMesh(); + + /*-------------------------------------------------------------------------*/ + /** @brief Set the connectivity of the simplified mesh. + * @param + */ + void setSimplifiedMeshConnectivity(); + + /*-------------------------------------------------------------------------*/ + /** @brief Get the simplified mesh. + * @param + */ + Mesh getSimplifiedMesh(); + + /*-------------------------------------------------------------------------*/ + /** @brief Write the simplified mesh. + * @param + */ + void writeSimplifiedMesh(std::basic_string AFileName); + + /*-------------------------------------------------------------------------*/ + /** @brief Execute. + * @param + */ + void execute(); + + /*-------------------------------------------------------------------------*/ + /** @brief Attach 1 to edges belonging to separatricies. + * @param + */ + void markSeparatrices(); + + /*-------------------------------------------------------------------------*/ + /** @brief Attach to each face the id of the block it belongs to. + * @param + */ + void setBlocksIDs(); + + /*-------------------------------------------------------------------------*/ + /** @brief Trace the outlines of the TopMaker blocks. + * @param + */ + void traceTopMakerBlocksOutlines(); + + /*-------------------------------------------------------------------------*/ + /** @brief Attach to each face the id of the TopMaker block it belongs to. + * @param + */ + void setTopMakerBlocksIDs(); +}; +/*----------------------------------------------------------------------------*/ +} // end namespace gmds +/*----------------------------------------------------------------------------*/ +#endif // GMDS_BLOCKSTRUCTURESIMPLIFIER_H \ No newline at end of file diff --git a/modules/medialaxis/inc/gmds/medialaxis/ConformalMeshBuilder.h b/modules/medialaxis/inc/gmds/medialaxis/ConformalMeshBuilder.h new file mode 100644 index 000000000..dc32a2d88 --- /dev/null +++ b/modules/medialaxis/inc/gmds/medialaxis/ConformalMeshBuilder.h @@ -0,0 +1,102 @@ +#ifndef GMDS_CONFORMALMESHBUILDER_H +#define GMDS_CONFORMALMESHBUILDER_H +/*----------------------------------------------------------------------------*/ +#include "GMDSMedialaxis_export.h" +#include "gmds/medialaxis/NonConformalHalfEdge.h" +#include "gmds/medialaxis/MedialAxisMath.h" +#include "gmds/io/IGMeshIOService.h" +#include "gmds/io/VTKWriter.h" +#include "gmds/ig/MeshDoctor.h" +#include +/*----------------------------------------------------------------------------*/ +namespace gmds{ +/** \class dummy + * \brief dummy class. + */ +class GMDSMedialaxis_API ConformalMeshBuilder +{ + private: + // Non conformal quad block decomposition + Mesh* m_mesh; + // Corresponding non-conformal half edges + std::vector m_half_edges; + // Half edges length + std::vector m_half_edges_lengths; + // Quantized mesh + Mesh* m_quantized_mesh; + // New nodes corresponding to half edges + std::vector> m_half_edges_to_new_nodes; + + public: + + /*-------------------------------------------------------------------------*/ + /** @brief Constructor. + * @param AMesh + */ + explicit ConformalMeshBuilder(Mesh &AMesh, std::vector AHalfEdges, std::vector ALengths); + + /*-------------------------------------------------------------------------*/ + /** @brief Default destructor. + * @param + */ + virtual ~ConformalMeshBuilder()=default; + + /*-------------------------------------------------------------------------*/ + /** @brief Write the quantized mesh. + * @param + */ + void writeQuantizedMesh(std::basic_string AFileName); + + /*-------------------------------------------------------------------------*/ + /** @brief Get the quantized mesh. + * @param + */ + Mesh getQuantizedMesh(); + + /*-------------------------------------------------------------------------*/ + /** @brief Set the connectivity of the quantized mesh. + * @param + */ + void setQuantizedMeshConnectivity(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the nodes of the quantized mesh that are on edges of the non-conformal mesh. + * @param + */ + void buildQuantizedMeshNodesOnEdges(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the nodes of the quantized mesh that are internal to the faces of the non-conformal mesh. + * @param + */ + void buildQuantizedMeshInternalNodes(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build quantized mesh faces. + * @param + */ + void buildQuantizedMeshFaces(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the group of half edges. + * @param AID, AV a half edge ID and a vector to mark already seen half edges + */ + std::vector> halfEdgesGroup(int AID, std::vector &AV); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the groups of half edges. + * @param + */ + std::vector>> halfEdgesGroups(); + + /*-------------------------------------------------------------------------*/ + /** @brief Execute. + * @param + */ + void execute(); + +}; +/*----------------------------------------------------------------------------*/ +} // end namespace gmds +/*----------------------------------------------------------------------------*/ +#endif // GMDS_CONFORMALMESHBUILDER_H \ No newline at end of file diff --git a/modules/medialaxis/inc/gmds/medialaxis/Conformalizer.h b/modules/medialaxis/inc/gmds/medialaxis/Conformalizer.h new file mode 100644 index 000000000..4677e6b01 --- /dev/null +++ b/modules/medialaxis/inc/gmds/medialaxis/Conformalizer.h @@ -0,0 +1,175 @@ +// An object that build a conformal mesh given a non conformal mesh, its list of non conformal half-edges, and the length of its edges. + +#ifndef GMDS_CONFORMALIZER_H +#define GMDS_CONFORMALIZER_H +/*----------------------------------------------------------------------------*/ +#include "GMDSMedialaxis_export.h" +#include "gmds/medialaxis/NonConformalHalfEdge.h" +#include "gmds/medialaxis/MedialAxisMath.h" +#include "gmds/io/IGMeshIOService.h" +#include "gmds/io/VTKWriter.h" +#include "gmds/ig/MeshDoctor.h" +#include +/*----------------------------------------------------------------------------*/ +namespace gmds{ +/** \class dummy + * \brief dummy class. + */ +class GMDSMedialaxis_API Conformalizer +{ + private: + // Non conformal quad block decomposition + Mesh* m_mesh; + // Corresponding non-conformal half edges + std::vector m_half_edges; + // Half edges length + std::vector m_half_edges_lengths; + // Groups of nodes of the non-conformal mesh + std::vector> m_non_conformal_nodes_groups; + // Conformal mesh + Mesh* m_conformal_mesh; + // New nodes corresponding to half edges + std::vector> m_half_edges_to_new_nodes; + // Intermediate mesh + Mesh* m_intermediate_mesh; + // Groups of nodes of the intermediate mesh + std::vector> m_intermediate_nodes_groups; + + public: + + /*-------------------------------------------------------------------------*/ + /** @brief Constructor. + * @param AMesh + */ + explicit Conformalizer(Mesh &AMesh, std::vector AHalfEdges, std::vector ALengths); + + /*-------------------------------------------------------------------------*/ + /** @brief Default destructor. + * @param + */ + virtual ~Conformalizer()=default; + + /*-------------------------------------------------------------------------*/ + /** @brief Write the intermediate mesh. + * @param + */ + void writeConformalMesh(std::basic_string AFileName); + + /*-------------------------------------------------------------------------*/ + /** @brief Write the intermediate mesh. + * @param + */ + void writeIntermediateMesh(std::basic_string AFileName); + + /*-------------------------------------------------------------------------*/ + /** @brief Get the conformal mesh. + * @param + */ + Mesh getConformalMesh(); + + /*-------------------------------------------------------------------------*/ + /** @brief Set the connectivity of the conformal mesh. + * @param + */ + void setConformalMeshConnectivity(); + + /*-------------------------------------------------------------------------*/ + /** @brief Set the connectivity of the intermediate mesh. + * @param + */ + void setIntermediateMeshConnectivity(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the groups of nodes of the non-conformal mesh. + * @param + */ + void buildNonConformalNodesGroups(); + + /*-------------------------------------------------------------------------*/ + /** @brief Add old nodes on the conformal mesh, each node corresponding to a group of old nodes. + * @param + */ + void addOldNodesOnConformalMesh(); + + /*-------------------------------------------------------------------------*/ + /** @brief Adds on the conformal mesh nodes subdiving edges of the non conformal mesh, according to their length. + * @param + */ + void addSubdividingNodes(); + + /*-------------------------------------------------------------------------*/ + /** @brief Attach to each half-edge its corresponding new nodes. + * @param + */ + void buildHalfEdges2newNodesConnectivity(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build intermediate mesh nodes. + * @param + */ + void buildIntermediateMeshNodes(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build intermediate mesh edges. + * @param + */ + void buildIntermediateMeshEdges(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build groups of intermediate nodes. + * @param + */ + void buildIntermediateNodesGroups(); + + /*-------------------------------------------------------------------------*/ + /** @brief Builds one node per group of intermediate nodes, and ensure previous node/new node correspondance. + * @param + */ + void buildRepresentativeNodes(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the internal nodes of the conformal mesh, and the face/new nodes connectivity. + * @param + */ + void builIntNodesAndFaces2newNodesConnectivity(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build conformal mesh faces. + * @param + */ + void buildConformalMeshFaces(); + + /*-------------------------------------------------------------------------*/ + /** @brief Deletes superfluous nodes. + * @param + */ + void deleteSuperfluousNodes(); + + /*-------------------------------------------------------------------------*/ + /** @brief Mark edges of the conformal mesh belonging to internal constraints. + * @param + */ + void markInternalConstraints(); + + /*-------------------------------------------------------------------------*/ + /** @brief Execute the algorithm building the conformal mesh. + * @param + */ + void execute(); + + /*-------------------------------------------------------------------------*/ + /** @brief Project the points on the boundary. + * @param ARefMesh a reference mesh. + */ + void projectOnBoundary(Mesh &ARefMesh); + + /*-------------------------------------------------------------------------*/ + /** @brief Smooth the conformal mesh. + * @param ARefMesh a reference mesh, used to project nodes on the boundary during the smoothing. + */ + void smooth(Mesh &ARefMesh); +}; +/*----------------------------------------------------------------------------*/ +} // end namespace gmds +/*----------------------------------------------------------------------------*/ +#endif // GMDS_CONFORMALIZER_H \ No newline at end of file diff --git a/modules/medialaxis/inc/gmds/medialaxis/MedaxBasedTMeshBuilder.h b/modules/medialaxis/inc/gmds/medialaxis/MedaxBasedTMeshBuilder.h new file mode 100644 index 000000000..3975912b2 --- /dev/null +++ b/modules/medialaxis/inc/gmds/medialaxis/MedaxBasedTMeshBuilder.h @@ -0,0 +1,388 @@ +#ifndef GMDS_MEDIALAXIS_MEDAXBASEDTMESHBUILDER_H +#define GMDS_MEDIALAXIS_MEDAXBASEDTMESHBUILDER_H +/*----------------------------------------------------------------------------*/ +#include "GMDSMedialaxis_export.h" +#include +/*----------------------------------------------------------------------------*/ +namespace gmds{ +/*----------------------------------------------------------------------------*/ +namespace medialaxis{ +/*----------------------------------------------------------------------------*/ +/** \class dummy + * \brief dummy class. + */ +class GMDSMedialaxis_API MedaxBasedTMeshBuilder +{ + + public: + /*-------------------------------------------------------------------------*/ + /** @brief Constructor. + * @param + */ + explicit MedaxBasedTMeshBuilder(Mesh &AMedax, Mesh &AMinDel); + + /*-------------------------------------------------------------------------*/ + /** @brief Default destructor. + * @param + */ + virtual ~MedaxBasedTMeshBuilder()=default; + + /*-------------------------------------------------------------------------*/ + /** \brief Get a point's adjacent point with respect to one of its edges + * @param A medial point id and a medial edge id. + */ + Node getNextPoint(const TCellID &APointID, const TCellID &AEdgeID); + + /*-------------------------------------------------------------------------*/ + /** \brief Get an edge's adjacent edge with respect to one of its points + * @param A medial edge id and a medial point id. + */ + Edge getNextEdge(const TCellID &AEdgeID, const TCellID &APointID); + + /*-------------------------------------------------------------------------*/ + /** \brief Get the extremal point of a node's branch in the direction given by one of its adjacent edges + * @param A medial point id and a medial edge id. + */ + Node getExtremPoint(const TCellID &APointID, const TCellID &AEdgeID); + + /*-------------------------------------------------------------------------*/ + /** \brief Set the sections IDs + * @param + */ + void setSectionID(); + + /*-------------------------------------------------------------------------*/ + /** \brief Compute the type of the medial sections + * @param + */ + void computeSectionType(); + + /*-------------------------------------------------------------------------*/ + /** \brief Transform the gray sections (sections of type 2) into red sections (sections of type 1) + * @param + */ + void transformGraySectionsIntoRed(); + + /*-------------------------------------------------------------------------*/ + /** \brief Perform the TopMaker coloring + * @param + */ + void setTopMakerColoring(); + + /*-------------------------------------------------------------------------*/ + /** \brief Returns a vector containing the extrem nodes of the medial branch containing the given node + * @param + */ + std::vector getBranchExtremPoints(Node AN); + + /*-------------------------------------------------------------------------*/ + /** \brief Avoid that intersection points are transformed into end points + * @param + */ + void avoidIPsBecomingEPs(); + + /*-------------------------------------------------------------------------*/ + /** \brief Returns the set of nodes forming the input section. + * @param AID a section ID + */ + std::vector sectionNodes(int AID); + + /*-------------------------------------------------------------------------*/ + /** \brief Refine the topo rep by adding artificially singular points on the medial axis. YET TO FINALIZE + * @param + */ + void refineByAddingSingularNodes(); + + /*-------------------------------------------------------------------------*/ + /** \brief Refine the topo rep by adding artificially singular points on the medial axis, according to the input mesh size. + * @param + */ + void refineByAddingSingularNodes(double AMeshSize); + + /*-------------------------------------------------------------------------*/ + /** \brief Build the nodes of the topological representation (IP, EP, singularities) + * @param + */ + void buildTopoRepNodes(); + + /*-------------------------------------------------------------------------*/ + /** \brief Build the edges of the topological representation (sections of the medial axis) + * @param + */ + void buildTopoRepEdges(); + + /*-------------------------------------------------------------------------*/ + /** \brief Mark with 1 refinable sections + * @param + */ + void markRefinableSections(); + + /*-------------------------------------------------------------------------*/ + /** \brief Mark with 1 forbidden singular points and modify boundary points + * @param + */ + void markForbiddenSingularPointsAndModifyBoundaryPoints(); + + /*-------------------------------------------------------------------------*/ + /** \brief Optimize the medial axis coloring by moving separatrices to intersection points + * @param + */ + void optimizeMedaxColoring(); + + /*-------------------------------------------------------------------------*/ + /** \brief In order to have nice pictures : color of the edges of the discretized medial axis. + * @param + */ + void setTopoRepEdgesColor(); + + /*-------------------------------------------------------------------------*/ + /** \brief Write the topological representation + * @param + */ + void writeTopoRep(std::basic_string AFileName); + + /*-------------------------------------------------------------------------*/ + /** \brief Return 1 if the wings of the section wrap around the point, 0 if not + * @param AN, AE a node and an edge of the topological representation + */ + int wings(Node &AN, Edge &AE); + + /*-------------------------------------------------------------------------*/ + /** \brief Return 1 if the node is the basis of the edge, -1 if it is the end, 0 if non of them + * @param AN, AE a node and an edge of the topological representation + */ + int orientation(Node &AN, Edge &AE); + + /*-------------------------------------------------------------------------*/ + /** \brief Set the connectivity of the topological representation + * @param + */ + void setTopoRepConnectivity(); + + /*-------------------------------------------------------------------------*/ + /** \brief Computing the distance to the end point for each point of the topological representation belonging to an end branch + * @param + */ + void computeDistanceToEndPoints(); + + /*-------------------------------------------------------------------------*/ + /** \brief Ensure that the end sections of the topo rep are of type 1 + * @param + */ + void ensureType1ForTopoRepEndSections(); + + /*-------------------------------------------------------------------------*/ + /** \brief Return the given node's neighbouring node of the topological representation with respect to the given edge + * @param AN, AE a node and an edge which contains the node + */ + Node getNextSingularNode(Node &AN, Edge &AE); + + /*-------------------------------------------------------------------------*/ + /** \brief Return the given edge's neighbouring edge of the topological representation with respect to the given node + * @param AE, AN an edge and a node contained by th edge + */ + Edge getNextMedialSection(Edge &AE, Node &AN); + + /*-------------------------------------------------------------------------*/ + /** \brief Wander around the medial axis topological representation + * @param + */ + void browseTopoRep(); + + /*-------------------------------------------------------------------------*/ + /** \brief Takes as input two nodes of the minimal triangulation, and returns the nodes forming the shortest path to go from the first to the second, + * following the boundary or the constraints. + * @param + */ + std::vector shortestPathAlongBoundaryOrConstraints(Node &AN1, Node &AN2); + + /*-------------------------------------------------------------------------*/ + /** \brief Mark with 1 edges of the topological represetnation that generate triangles. + * @param + */ + void markEdgesGeneratingTriangles(); + + /*-------------------------------------------------------------------------*/ + /** \brief Mark the corners on the minimal triangulation. + * @param + */ + void markCornersOnMinDel(); + + /*-------------------------------------------------------------------------*/ + /** \brief Build the nodes of the T-mesh from the nodes of the minimal triangulation. + * @param + */ + void buildTMeshNodesFromMinDelNodes(); + + /*-------------------------------------------------------------------------*/ + /** \brief Build the nodes of the medial axis based block decomposition + * @param + */ + void buildBlockDecompMedialAndBoundaryNodes(); + + /*-------------------------------------------------------------------------*/ + /** \brief Write the block decomposition + * @param + */ + void writeBlockDecomp(std::basic_string AFileName); + + /*-------------------------------------------------------------------------*/ + /** \brief Get the T-mesh. + * @param + */ + Mesh getTMesh(); + + /*-------------------------------------------------------------------------*/ + /** \brief + * @param + */ + void buildSection2MedialAndBoundaryNodesAdjacency(); + + /*-------------------------------------------------------------------------*/ + /** \brief Build middle nodes + * @param + */ + void buildMiddleNodes(); + + /*-------------------------------------------------------------------------*/ + /** \brief Build blocks + * @param + */ + void buildBlocks(); + + /*-------------------------------------------------------------------------*/ + /** \brief Transforms the degenerate quads of the T-mesh into triangles. + * @param + */ + void transformDegenerateQuadsIntoTriangles(); + + /*-------------------------------------------------------------------------*/ + /** \brief Sorts the input fan of edges. + * @param AFan a vector of edges + */ + std::vector sortEdgesFan(std::vector &AFan); + + /*-------------------------------------------------------------------------*/ + /** \brief Transforming triangles of the T-mesh into non-degenerate quads. + * @param + */ + void transformTrianglesIntoQuads(); + + /*-------------------------------------------------------------------------*/ + /** \brief Mark t-junctions. + * @param + */ + void markTJunctions(); + + /*-------------------------------------------------------------------------*/ + /** \brief Sort adjacent edges with respect to their angle with the x-axis. + * @param AN a node + */ + std::vector sortedAdjacentEdges(Node &AN); + + /*-------------------------------------------------------------------------*/ + /** \brief Take an edge and a node belonging to the edge. Return the edges closest to + * the given edge in the list of sorted edges of n. + * @param AE, AN an edge and a node + */ + std::vector neighbouringEdges(Edge &AE, Node &AN); + + /*-------------------------------------------------------------------------*/ + /** \brief Take an section and a node belonging to the edge. Return the section closest to + * the given edge in the list of ordered sections of n. + * @param ASection, AN a section and a node + */ + std::vector orderedNeigbourSections(Edge &ASection, Node &AN); + + /*-------------------------------------------------------------------------*/ + /** \brief Set block decomposition connectivity + * @param + */ + void setBlockDecompConnectivity(); + + /*-------------------------------------------------------------------------*/ + /** \brief Mark with 1 edges separating different blocks + * @param + */ + void markBlocksSeparatingEdges(); + + /*-------------------------------------------------------------------------*/ + /** \brief Add big T-junctions (to have nice pictures) + * @param + */ + void addBigTJunctions(); + + /*-------------------------------------------------------------------------*/ + /** \brief Modify faces touching a constraint to ensure good connectivity TO MODIFY + * @param + */ + void ensureConnectivityThroughConstraints(); + + /*----------------------------------------------------------------------------*/ + /** @brief Find the connected components of the boundary and attach an ID to each of them. + * @param + */ + void setBoundaryConnectedComponents(); + + /*----------------------------------------------------------------------------*/ + /** @brief Build the final T-mesh. + * @param + */ + void buildFinalTMesh(); + + /*----------------------------------------------------------------------------*/ + /** @brief Write the final T-mesh. + * @param + */ + void writeFinalTMesh(std::basic_string AFileName); + + /*-------------------------------------------------------------------------*/ + /** \brief Set block connectivity of the T-mesh. + * @param + */ + void setFinalTMeshConnectivity(); + + /*-------------------------------------------------------------------------*/ + /** \brief Set block connectivity of the T-mesh. + * @param + */ + Mesh getFinalTMesh(); + + /*-------------------------------------------------------------------------*/ + /** \brief Mark the internal constraints on the final T-mesh. + * @param + */ + void markInternalConstraintsOnFinalTMesh(); + + private: + + // A mesh representation of a medial axis, as built by the class MedialAxisBuilder + Mesh* m_medax; + + // The minimal Delaunay triangulation from which the medial axis was created + Mesh* m_min_delaunay; + + // Topological representation of the medial axis (used to build the quad) + Mesh* m_topological_representation; + // Nb of sections of the medial axis + int m_nb_medial_sections; + // Type of each medial section + std::vector m_medial_section_type; + // Extremal nodes of each medial section + std::vector> m_medial_section_extremal_nodes; + // Mark with 1 refinable sections + std::vector m_is_refinable; + // medial axis based T-mesh + Mesh* m_t_mesh; + // Final T-mesh + Mesh* m_final_t_mesh; + + // Connected components of the T-mesh + std::vector> m_boundary_connected_components; +}; +/*----------------------------------------------------------------------------*/ +} // end namespace medialaxis +/*----------------------------------------------------------------------------*/ +} // end namespace gmds +/*----------------------------------------------------------------------------*/ +#endif // GMDS_MEDIALAXIS_MEDAXBASEDTMESHBUILDER_H \ No newline at end of file diff --git a/modules/medialaxis/inc/gmds/medialaxis/MedialAxis2D.h b/modules/medialaxis/inc/gmds/medialaxis/MedialAxis2D.h index 55de2dbe2..8950ef9b5 100644 --- a/modules/medialaxis/inc/gmds/medialaxis/MedialAxis2D.h +++ b/modules/medialaxis/inc/gmds/medialaxis/MedialAxis2D.h @@ -27,6 +27,12 @@ class GMDSMedialaxis_API MedialAxis2D */ virtual ~MedialAxis2D(); + /*-------------------------------------------------------------------------*/ + /** \brief Add a medial point. + * @param a point. + */ + Mesh getMeshRepresentation(); + /*-------------------------------------------------------------------------*/ /** \brief Add a medial point. * @param a point. @@ -173,12 +179,42 @@ class GMDSMedialaxis_API MedialAxis2D */ void setTouchingPoints(const TCellID &APointID, std::vector APoints); + /*-------------------------------------------------------------------------*/ + /** \brief Attach to a medial point its corresponding tengancy nodes + * @param A medial point ID, a vector of nodes + */ + void setTangentNodes(const TCellID &APointID, std::vector ANodes); + + /*-------------------------------------------------------------------------*/ + /** \brief Attach to a medial point its corresponding dual triangles + * @param A medial point ID, a vector of faces + */ + void setDualTriangles(const TCellID &APointID, std::vector ATriangles); + + /*-------------------------------------------------------------------------*/ + /** \brief Mark if a medial point is a dangle + * @param A medial point ID, a boolean + */ + void setDangle(const TCellID &APointID, bool ADangleValue); + + /*-------------------------------------------------------------------------*/ + /** \brief Mark if a medial point belongs to an extension of the medial axis + * @param A medial point ID + */ + void markAsExtension(const TCellID &APointID); + /*-------------------------------------------------------------------------*/ /** \brief Get the touching points of a given medial point * @param A medial point ID */ std::vector getTouchingPoints(const TCellID &APointID); + /*-------------------------------------------------------------------------*/ + /** \brief Get the tengancy nodes of a given medial point + * @param A medial point ID + */ + std::vector getTangentNodes(const TCellID &APointID); + /*-------------------------------------------------------------------------*/ /** \brief Set the optimal change in angle of a cross following the curve formed by two adjacent medial radii * @param @@ -228,6 +264,12 @@ class GMDSMedialaxis_API MedialAxis2D */ void placeSingularities(const double& AMedRadiusFraction); + /*-------------------------------------------------------------------------*/ + /** \brief Remove singularity dipoles + * @param + */ + void removeSingularityDipoles(); + /*-------------------------------------------------------------------------*/ /** \brief Check if placed singularities are coherent, ie if the med radius orthogonality default is smaller than a tolerance * @param AOrthogonalityDefaultTol A tolerance @@ -301,185 +343,6 @@ class GMDSMedialaxis_API MedialAxis2D */ void write(std::basic_string AFileName); - /*-------------------------------------------------------------------------*/ - /** \brief Build the nodes of the topological representation (IP, EP, singularities) - * @param - */ - void buildTopoRepNodes(); - - /*-------------------------------------------------------------------------*/ - /** \brief Set the sections IDs - * @param - */ - void setSectionID(); - - /*-------------------------------------------------------------------------*/ - /** \brief Build the edges of the topological representation (sections of the medial axis) - * @param - */ - void buildTopoRepEdges(); - - /*-------------------------------------------------------------------------*/ - /** \brief Write the topological representation - * @param - */ - void writeTopoRep(std::basic_string AFileName); - - /*-------------------------------------------------------------------------*/ - /** \brief Return 1 if the wings of the section wrap around the point, 0 if not - * @param AN, AE a node and an edge of the topological representation - */ - int wings(Node &AN, Edge &AE); - - /*-------------------------------------------------------------------------*/ - /** \brief Return 1 if the node is the basis of the edge, -1 if it is the end, 0 if non of them - * @param AN, AE a node and an edge of the topological representation - */ - int orientation(Node &AN, Edge &AE); - - /*-------------------------------------------------------------------------*/ - /** \brief Set the connectivity of the topological representation - * @param - */ - void setTopoRepConnectivity(); - - /*-------------------------------------------------------------------------*/ - /** \brief Return the number of degrees of freedom for the quantisation, and attach to each section its position in the matrix - * @param - */ - int NbDoF(); - - /*-------------------------------------------------------------------------*/ - /** \brief Return the number of quantisation equation (each node bring its type as much equations, plus 1 extra equation for each node of type 1 wrapped by wings) - * @param - */ - int NbEquations(); - - /*-------------------------------------------------------------------------*/ - /** \brief Build the constraint matrix of the quantisation system - * @param - */ - Eigen::MatrixXd constraintMatrix(); - - /*-------------------------------------------------------------------------*/ - /** \brief Return the given node's neighbouring node of the topological representation with respect to the given edge - * @param AN, AE a node and an edge which contains the node - */ - Node getNextSingularNode(Node &AN, Edge &AE); - - /*-------------------------------------------------------------------------*/ - /** \brief Return the given edge's neighbouring edge of the topological representation with respect to the given node - * @param AE, AN an edge and a node contained by th edge - */ - Edge getNextMedialSection(Edge &AE, Node &AN); - - /*-------------------------------------------------------------------------*/ - /** \brief Wander around the medial axis topological representation - * @param - */ - void browseTopoRep(); - - /*-------------------------------------------------------------------------*/ - /** \brief Build the degrees of freedom graph nodes - * @param - */ - void buildDofGraphNodes(); - - /*-------------------------------------------------------------------------*/ - /** \brief Build the degrees of freedom edge corresponding to the two given sections of type 0 and 0, - * oriented by the two given sides (0 = left, 1 = right) - * @param ASection1, ASection2, ASide1, ASide2 - */ - void dofEdge00(Edge &ASection1, Edge &ASection2, int ASide1, int ASide2); - - /*-------------------------------------------------------------------------*/ - /** \brief Build the degrees of freedom edges corresponding to the two given sections of type 1 and 1, - * oriented by the two given sides (0 = left, 1 = right) - * @param ASection1, ASection2, ASide1, ASide2 - */ - void dofEdges11(Edge &ASection1, Edge &ASection2, int ASide1, int ASide2); - - /*-------------------------------------------------------------------------*/ - /** \brief Build the degrees of freedom edges corresponding to the two given sections of type 0 and 1, - * oriented by the two given sides (0 = left, 1 = right) - * @param ASection1, ASection2, ASide1, ASide2 - */ - void dofEdges01(Edge &ASection1, Edge &ASection2, int ASide1, int ASide2); - - /*-------------------------------------------------------------------------*/ - /** \brief Build the degrees of freedom edges corresponding to the two given sections of type 1 and 0, - * oriented by the two given sides (0 = left, 1 = right) - * @param ASection1, ASection2, ASide1, ASide2 - */ - void dofEdges10(Edge &ASection1, Edge &ASection2, int ASide1, int ASide2); - - /*-------------------------------------------------------------------------*/ - /** \brief Build the degrees of freedom edges corresponding to the right side of the path formed by the two given edges - * @param ASection1, ASection2 - */ - void dofEdges(Edge &ASection1, Edge &ASection2); - - /*-------------------------------------------------------------------------*/ - /** \brief Build the degrees of freedom graph edges - * @param - */ - void buildDofGraphEdges(); - - /*-------------------------------------------------------------------------*/ - /** \brief Set the connectivity of the quantization degrees of freedom graph - * @param - */ - void setDofGraphConnectivity(); - - /*-------------------------------------------------------------------------*/ - /** \brief Build a solution to the quantization problem in the case without cycle - * @param - */ - void buildQuantizationWithoutCycleSolution(); - - /*-------------------------------------------------------------------------*/ - /** \brief Build the nodes of the medial axis based block decomposition - * @param - */ - void buildBlockDecompMedialAndBoundaryNodes(); - - /*-------------------------------------------------------------------------*/ - /** \brief Write the block decomposition - * @param - */ - void writeBlockDecomp(std::basic_string AFileName); - - /*-------------------------------------------------------------------------*/ - /** \brief Build blocks - * @param - */ - void buildSection2MedialAndBoundaryNodesAdjacency(); - - /*-------------------------------------------------------------------------*/ - /** \brief Build middle nodes - * @param - */ - void buildMiddleNodes(); - - /*-------------------------------------------------------------------------*/ - /** \brief Build blocks - * @param - */ - void buildBlocks(); - - /*-------------------------------------------------------------------------*/ - /** \brief Sort adjacent edges with respect to their angle with the x-axis. - * @param AN a node - */ - std::vector sortedAdjacentEdges(Node &AN); - - /*-------------------------------------------------------------------------*/ - /** \brief Take an edge and a node belonging to the edge. Return the edges closest to - * the given edge in the list of sorted edges of n. - * @param AE, AN an edge and a node - */ - std::vector neighbouringEdges(Edge &AE, Node &AN); - private: // Geometrical representation of the medial axis @@ -488,13 +351,6 @@ class GMDSMedialaxis_API MedialAxis2D // Singularities identified on the medial axis std::vector m_singular_nodes; std::vector m_singularity_indexes; - - // Topological representation of the medial axis (used for quantization) - Mesh* m_topological_representation; - // Quantization degrees of freedom graph - Mesh* m_dof_graph; - // Topology of a medial axis based block decomposition - Mesh* m_ma_block_decomposition; }; /*----------------------------------------------------------------------------*/ } // end namespace medialaxis diff --git a/modules/medialaxis/inc/gmds/medialaxis/MedialAxis2DBuilder.h b/modules/medialaxis/inc/gmds/medialaxis/MedialAxis2DBuilder.h index 872861674..4d2866d06 100644 --- a/modules/medialaxis/inc/gmds/medialaxis/MedialAxis2DBuilder.h +++ b/modules/medialaxis/inc/gmds/medialaxis/MedialAxis2DBuilder.h @@ -39,11 +39,13 @@ class GMDSMedialaxis_API MedialAxis2DBuilder{ * @param */ - explicit MedialAxis2DBuilder(Mesh &AMesh); + explicit MedialAxis2DBuilder(Mesh &AMesh, std::vector ABoundEdgesIds); public: // Primal minimal Delaunay mesh Mesh* m_mesh; + // List of ids of the boundary edges of the minimal Delaunay mesh + std::vector m_boundary_edges_ids; // Dual Voronoï medial axis MedialAxis2D *m_voronoi_medax; // Smoothed medial axis built from the Voronoï one @@ -95,6 +97,11 @@ class GMDSMedialaxis_API MedialAxis2DBuilder{ */ void setSideId(); + /*-------------------------------------------------------------------------*/ + /** \brief Mark with 1 Delaunay edges which correspond to internal constraints + */ + void markIntConstraints(); + /*-------------------------------------------------------------------------*/ /** \brief Compute the inner product between medial radii and the corresponding boundary edges, which should be 0 * \param ABoundaryCurvatureTol a maximum distance away from 1 of the norm of the cosine of the boundary curvature angle @@ -136,6 +143,24 @@ class GMDSMedialaxis_API MedialAxis2DBuilder{ */ void buildVoronoiMedialAxis(); + /*-------------------------------------------------------------------------*/ + /** \brief Takes as input a set of connected nodes forming boundary arcs, and returns the ordered sets. + * \param ABoundNodes a set of connected boundary nodes + */ + std::vector> boundaryArcs(std::vector ABoundNodes); + + /*-------------------------------------------------------------------------*/ + /** \brief Takes as input a set of connected nodes forming a boundary arc, and returns the ordered set. LATER : TO MODIFY TO TAKE INTO ACCOUNT CIRCLE AND SEVERAL ARCS + * \param ABoundNodes a set of connected boundary nodes + */ + std::vector boundaryArc(std::vector ABoundNodes); + + /*-------------------------------------------------------------------------*/ + /** \brief Unfolds the medial axis at the input medial point + * \param AN,ABoundaryArc a medial node and a boundary arc + */ + void unfoldMedax(Node AN, std::vector ABoundaryArc, bool AIsADangle); + /*-------------------------------------------------------------------------*/ /** \brief Build the smoothed medial axis from the groups of nodes of the Voronoï medial axis * \param AGroups the set of medial points groups diff --git a/modules/medialaxis/inc/gmds/medialaxis/MedialAxisMath.h b/modules/medialaxis/inc/gmds/medialaxis/MedialAxisMath.h index c7223845c..4263b5d5a 100644 --- a/modules/medialaxis/inc/gmds/medialaxis/MedialAxisMath.h +++ b/modules/medialaxis/inc/gmds/medialaxis/MedialAxisMath.h @@ -41,4 +41,43 @@ std::vector orientateEdges(Face &AF); // Regroup aligned conformal edges of a face std::vector> groupsOfAlignedEdges(Face &AF); + +// Check if the first point is on the segment formed by the two last points. +bool isOnSegment(math::Point AP0, math::Point AP1, math::Point AP2); + +// Insert a given point in the given set of points. +std::vector insertPoint(Node AN, std::vector AV); + +// Merge the twp given vectors of points. +std::vector merge(std::vector AV1, std::vector AV2); + +// Takes a vector of edges and a vector of the same size containing quantities assotiated to each edge. Returns the +// vector of the edges sorted with respect to this quantity. +std::vector order(std::vector AVE, std::vector AVX); + +// Returns, if it exists, the edge corresponding to the two given nodes. +Edge getEdge(Node &AN1, Node &AN2); + +// Returns true if the node is internal. +bool isInterior(Node &AN); + +// Returns the area delimited by the edges containted in the input vector. These edges must sourround a convex region. +double delimitedArea(std::vector AV); + +// Returns true if the edge touches the boundary. +bool touchesBoundary(Edge &AE); + +// Returns the common face of the two input edges. +Face getCommonFace(Edge &AE1, Edge &AE2); + +// Returns the projection of the second input vector on the first input vector. +math::Vector projection(math::Vector &AV1, math::Vector &AV2); + +// Takes as input two nodes of the input minimal triangulation, and returns the nodes forming the shortest path to go from the first to the second. +std::vector shortestPathAlongBoundaryOrConstraints(Node &AN1, Node &AN2, Mesh &AMesh); + +// Takes a quad and an edge belonging to the face, and return the opposite face +Edge opp(Face AFace, Edge AEdge); + + #endif // GMDS_MEDIALAXISMATH_H diff --git a/modules/medialaxis/inc/gmds/medialaxis/MinDelaunayCleaner.h b/modules/medialaxis/inc/gmds/medialaxis/MinDelaunayCleaner.h new file mode 100644 index 000000000..d1abdedee --- /dev/null +++ b/modules/medialaxis/inc/gmds/medialaxis/MinDelaunayCleaner.h @@ -0,0 +1,104 @@ +#ifndef GMDS_MINDELAUNAYCLEANER_H +#define GMDS_MINDELAUNAYCLEANER_H +/*----------------------------------------------------------------------------*/ +#include "GMDSMedialaxis_export.h" +#include "gmds/medialaxis/MedialAxis2D.h" +#include "gmds/medialaxis/NonConformalHalfEdge.h" +#include "gmds/medialaxis/MedialAxisMath.h" +#include "gmds/medialaxis/QuantizationGraph.h" +#include "gmds/io/IGMeshIOService.h" +#include "gmds/io/VTKWriter.h" +#include "gmds/ig/MeshDoctor.h" +#include +/*----------------------------------------------------------------------------*/ +namespace gmds{ +/** \class dummy + * \brief dummy class. + */ +class GMDSMedialaxis_API MinDelaunayCleaner +{ + private: + // Minimal Delaunay triangulation to clean + Mesh* m_mesh; + // Cleaned mesh + Mesh* m_cleaned_mesh; + + public: + + /*-------------------------------------------------------------------------*/ + /** @brief Constructor. + * @param AMesh + */ + explicit MinDelaunayCleaner(Mesh &AMesh); + + /*-------------------------------------------------------------------------*/ + /** @brief Default destructor. + * @param + */ + virtual ~MinDelaunayCleaner()=default; + + /*-------------------------------------------------------------------------*/ + /** @brief Compute the number of neighbours of each face. + * @param + */ + void setFacesTypes(); + + /*-------------------------------------------------------------------------*/ + /** @brief Compute the medial points and radii. + * @param + */ + void computeMedialPointsAndRadii(); + + /*-------------------------------------------------------------------------*/ + /** @brief Return a vector containing the faces of the medial branch of the input face. + * @param AF + */ + std::vector medialBranch(Face &AF); + + /*-------------------------------------------------------------------------*/ + /** @brief Set an ID to each medial branch. + * @param + */ + void setBranchID(); + + /*-------------------------------------------------------------------------*/ + /** @brief Mark small edges. + * @param + */ + void markSmallEdges(); + + /*-------------------------------------------------------------------------*/ + /** @brief Mark the faces to delete, those with too small area. + * @param + */ + void markFacesToDelete(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the cleaned mesh. + * @param + */ + void buildCleanedMesh(); + + /*-------------------------------------------------------------------------*/ + /** @brief Set the cleaned mesh connectivity. + * @param + */ + void setCleanedMeshConnectivity(); + + /*-------------------------------------------------------------------------*/ + /** @brief Get the cleaned mesh. + * @param + */ + Mesh getCleanedMesh(); + + /*-------------------------------------------------------------------------*/ + /** @brief Write the cleaned mesh. + * @param + */ + void writeCleanedMesh(std::string AFileName); + +}; +/*----------------------------------------------------------------------------*/ +} // end namespace gmds +/*----------------------------------------------------------------------------*/ +#endif // GMDS_MINDELAUNAYCLEANER_H \ No newline at end of file diff --git a/modules/medialaxis/inc/gmds/medialaxis/QuantizationGraph.h b/modules/medialaxis/inc/gmds/medialaxis/QuantizationGraph.h index 3e5442fbf..9cf199951 100644 --- a/modules/medialaxis/inc/gmds/medialaxis/QuantizationGraph.h +++ b/modules/medialaxis/inc/gmds/medialaxis/QuantizationGraph.h @@ -19,6 +19,17 @@ class GMDSMedialaxis_API QuantizationGraph // Mesh representation Mesh* m_mesh_representation; + // Non zero conditions for the quantization solution : + + // Set of verticies with non zero condition + std::vector m_non_zero_verticies; + + // Set of groups of verticies, with condition at least one non zero in each group + std::vector> m_non_zero_groups; + + // Maximal number of iterations to find a path + int m_max_it; + public: /*-------------------------------------------------------------------------*/ @@ -33,6 +44,18 @@ class GMDSMedialaxis_API QuantizationGraph */ virtual ~QuantizationGraph()=default; + /*-------------------------------------------------------------------------*/ + /** @brief Accessor to non zero verticies. + * @param + */ + void nonZeroVerticies(std::vector AV){m_non_zero_verticies = AV;} + + /*-------------------------------------------------------------------------*/ + /** @brief Accessor to non zero groups. + * @param + */ + void nonZeroGroups(std::vector> AV){m_non_zero_groups = AV;} + /*-------------------------------------------------------------------------*/ /** @brief New node. * @param @@ -70,11 +93,29 @@ class GMDSMedialaxis_API QuantizationGraph int alreadyPushed(TCellID AID); /*-------------------------------------------------------------------------*/ - /** @brief Mark the given node as already visited. + /** @brief Mark the given node as already pushed. * @param AID */ void markAsPushed(TCellID AID); + /*-------------------------------------------------------------------------*/ + /** @brief Mark the given edge as being in a quad. + * @param AID an edge ID + */ + void markAsInQuad(TCellID AID); + + /*-------------------------------------------------------------------------*/ + /** @brief Attach to the node the geometrical length of the half edge it represents. + * @param AID, ALength a node ID and a length + */ + void setGeometricalLength(TCellID AID, double ALength); + + /*-------------------------------------------------------------------------*/ + /** @brief Mark the node as zero of the quantization solution. + * @param AID a node ID + */ + void markAsZero(TCellID AID); + /*-------------------------------------------------------------------------*/ /** \brief Update the graph connectivity. * @param @@ -82,17 +123,35 @@ class GMDSMedialaxis_API QuantizationGraph void updateConnectivity(); /*-------------------------------------------------------------------------*/ - /** \brief Get the leaving edges. + /** \brief Get the leaving edges. ========= TO DELETE IN THE END ========= * @param AN a node */ std::vector getLeavingEdges(Node &AN); /*-------------------------------------------------------------------------*/ - /** \brief Get the entering edges. + /** \brief Get the entering edges. ========= TO DELETE IN THE END ========= * @param AN a node */ std::vector getEnteringEdges(Node &AN); + /*-------------------------------------------------------------------------*/ + /** \brief Get the unique edge interior to a quad containing the given node. + * @param AN a node + */ + Edge getIntEdge(Node &AN); + + /*-------------------------------------------------------------------------*/ + /** \brief Get set of edges between different quads containing the given node. + * @param AN a node + */ + std::vector getExtEdges(Node &AN); + + /*-------------------------------------------------------------------------*/ + /** \brief Given a node and an edge containing this node, return the other node of the edge. + * @param AN, AE + */ + Node getOtherNode(Node AN, Edge AE); + /*-------------------------------------------------------------------------*/ /** \brief Propagate length from a given root. * @param AID a node ID @@ -106,22 +165,16 @@ class GMDSMedialaxis_API QuantizationGraph void propagateFromRoots(); /*-------------------------------------------------------------------------*/ - /** \brief Add 1 to the solution at every node of the cycle containing the given node. - * @param AID a node ID - */ - void addOnCycle(TCellID AID); - - /*-------------------------------------------------------------------------*/ - /** \brief Propagate length from roots. + /** \brief Propagate length from roots. * @param */ void addOnCycles(); /*-------------------------------------------------------------------------*/ - /** \brief Propagate length from roots. + /** \brief Build the complete quantization solution (minimizing the number of zeros). * @param */ - void buildQuantizationSolution(); + void buildCompleteSolution(); /*-------------------------------------------------------------------------*/ /** @brief Display the quantization solution. @@ -135,7 +188,104 @@ class GMDSMedialaxis_API QuantizationGraph */ int quantizationSolutionValue(TCellID AID); + /*-------------------------------------------------------------------------*/ + /** @brief Get the value of the flux through the given edge. + * @param AID a edge ID + */ + int fluxValue(TCellID AID); + + /*-------------------------------------------------------------------------*/ + /** @brief Return the shortest path joining the given node to a root, starting in the given direction (if 1 through the quad, if -1 else). + * @param AID a node ID + */ + std::vector shortestHalfPath(TCellID AID, int ADirection); + + /*-------------------------------------------------------------------------*/ + /** @brief Return the shortest cycle containing the given node. + * @param AID a node ID + */ + std::vector shortestCycle(TCellID AID); + + /*-------------------------------------------------------------------------*/ + /** @brief Return the shortest elementary path containing the given node. + * @param AID a node ID + */ + std::vector shortestElementaryPath(TCellID AID); + + /*-------------------------------------------------------------------------*/ + /** @brief Return a cycle reachable from the given node. + * @param AID a node ID + */ + std::vector problematicCycle(TCellID AID); + + /*-------------------------------------------------------------------------*/ + /** @brief Get the edge corresponding to the two given nodes, in the right order. + * @param AN1, AN2 two nodes + */ + Edge getCorrespondingEdge(Node AN1, Node AN2); + + /*-------------------------------------------------------------------------*/ + /** @brief Return a quad edge approximatively in the middle of the given cycle. The edge is given by a set of 2 nodes which orientates the edge. + * @param AV a vector of nodes forming a cycle + */ + std::vector middleQuadEdge(std::vector AV); + + /*-------------------------------------------------------------------------*/ + /** @brief Increase the quantization solution by 1 on the shortest elementary path containing the given node. The function return true if it succeeded + * increasing the solution, ie if the shortest elementary path containing the given node is not empty. + * @param AID a node ID + */ + bool increaseSolution(TCellID AID); + /*-------------------------------------------------------------------------*/ + /** \brief Build minimal quantization solution (trying to minimize the number of blocks). The function returns couples of nodes + * preventing from increasing the solution. + * @param + */ + std::vector> buildMinimalSolution(); + + /*-------------------------------------------------------------------------*/ + /** \brief Check is for all half-edge e, l(e)=l(e.next.next). If not, artificially repair it. WARNING : this solution doesn't garantee that the new solution is valid. + * @param + */ + void roughlyRepairSolution(); + + /*-------------------------------------------------------------------------*/ + /** \brief Check solution validity. The function returns couples of nodes + * preventing from increasing the solution, in the cas where the solution is not valid. + * @param + */ + std::vector> checkSolutionValidity(); + + /*-------------------------------------------------------------------------*/ + /** \brief Return the optimal (according to the input mesh size) path joining the given node to a root, starting in the given direction (if 1 through the quad, if -1 else). + * @param AID, ADirection a node ID and a direction. + */ + std::vector optimalHalfPath(TCellID AID, int ADirection); + + /*-------------------------------------------------------------------------*/ + /** \brief Return the optimal (according to the input mesh size) path joining two roots and containing the given node. + * @param AID a node ID. + */ + std::vector optimalPath(TCellID AID); + + /*-------------------------------------------------------------------------*/ + /** \brief Return the optimal (according to the input mesh size) cycle containing the given node. + * @param AID a node ID. + */ + std::vector optimalCycle(TCellID AID); + + /*-------------------------------------------------------------------------*/ + /** \brief Return the optimal (according to the input mesh size) elementary path containing the given node. NOT USED + * @param AID a node ID. + */ + std::vector optimalElementaryPath(TCellID AID); + + /*-------------------------------------------------------------------------*/ + /** \brief Imroves the complete solution with respect to the target mesh size. + * @param AMeshSize a target mesh size. + */ + void improveSolution(double AMeshSize); }; /*----------------------------------------------------------------------------*/ diff --git a/modules/medialaxis/inc/gmds/medialaxis/QuantizationSolver.h b/modules/medialaxis/inc/gmds/medialaxis/QuantizationSolver.h index 4327af52c..ef925facd 100644 --- a/modules/medialaxis/inc/gmds/medialaxis/QuantizationSolver.h +++ b/modules/medialaxis/inc/gmds/medialaxis/QuantizationSolver.h @@ -12,6 +12,7 @@ #include "gmds/medialaxis/QuantizationGraph.h" #include "gmds/io/IGMeshIOService.h" #include "gmds/io/VTKWriter.h" +#include "gmds/ig/MeshDoctor.h" #include /*----------------------------------------------------------------------------*/ namespace gmds{ @@ -23,16 +24,16 @@ class GMDSMedialaxis_API QuantizationSolver private: // Non conformal quad block decomposition Mesh* m_mesh; + // Target mesh size + double m_mesh_size; // Corresponding non-conformal half edges std::vector m_half_edges; // Quantization graph QuantizationGraph* m_quantization_graph; // Half edges length std::vector m_half_edges_lengths; - // Quantized mesh - Mesh* m_quantized_mesh; - // New nodes corresponding to half edges - std::vector> m_half_edges_to_new_nodes; + // Mesh after adding a singularity dipole + Mesh* m_fixed_mesh; public: @@ -40,7 +41,7 @@ class GMDSMedialaxis_API QuantizationSolver /** @brief Constructor. * @param AMesh */ - explicit QuantizationSolver(Mesh &AMesh); + explicit QuantizationSolver(Mesh &AMesh, double AMeshSize); /*-------------------------------------------------------------------------*/ /** @brief Default destructor. @@ -48,12 +49,37 @@ class GMDSMedialaxis_API QuantizationSolver */ virtual ~QuantizationSolver()=default; + /*-------------------------------------------------------------------------*/ + /** @brief Getters. + * @param + */ + std::vector halfEdges(){return m_half_edges;} + std::vector halfEdgesLengths(){return m_half_edges_lengths;} + + /*-------------------------------------------------------------------------*/ + /** @brief Find and mark the T-junctions of the input mesh. We associate to each t-junction the id of the face for which it is a non_conformal node. WARNING : GEOMETRY-DEPENDANT + * @param + */ + void findTJunctions(); + + /*-------------------------------------------------------------------------*/ + /** @brief Regroups edges of the given face in groups of aligned edges. + * @param AF a face + */ + std::vector> alignedEdgesGroups(Face &AF); + /*-------------------------------------------------------------------------*/ /** @brief Build the half edges. * @param */ void buildHalfEdges(); + /*-------------------------------------------------------------------------*/ + /** @brief Returns the common edge of two opposite half-edges. + * @param AHalfEdgeID1, AHalfEdgeID2 two half-edge IDs + */ + Edge getCommonEdge(int AHalfEdgeID1, int AHalfEdgeID2); + /*-------------------------------------------------------------------------*/ /** @brief Build the quantization graph nodes. * @param @@ -85,46 +111,113 @@ class GMDSMedialaxis_API QuantizationSolver QuantizationGraph* getQuantizationGraph(); /*-------------------------------------------------------------------------*/ - /** @brief Build the group of half edges. - * @param AID, AV a half edge ID and a vector to mark already seen half edges + /** @brief Set half edges length. + * @param */ - std::vector> halfEdgesGroup(int AID, std::vector &AV); + void setHalfEdgesLength(); /*-------------------------------------------------------------------------*/ - /** @brief Build the groups of half edges. + /** @brief Set edges length. * @param */ - std::vector>> halfEdgesGroups(); + void setEdgesLength(); /*-------------------------------------------------------------------------*/ - /** @brief Set half edges length. + /** @brief Return the set of sides of the input geometry, each side being a set of half-edge Ids. * @param */ - void setHalfEdgesLength(); + std::vector> sides(); /*-------------------------------------------------------------------------*/ - /** @brief Write the quantized mesh. + /** @brief Mark the T-junctions. * @param */ - void writeQuantizedMesh(std::basic_string AFileName); + void markTJunctions(); /*-------------------------------------------------------------------------*/ - /** @brief Build the nodes of the quantized mesh that are on edges of the non-conformal mesh. + /** @brief Return true if the given node is on the boundary. * @param */ - void buildQuantizedMeshNodesOnEdges(); + bool isOnBoundary(Node AN); /*-------------------------------------------------------------------------*/ - /** @brief Build the nodes of the quantized mesh that are internal to the faces of the non-conformal mesh. + /** @brief Return the set of half-edges that must be non zero, ie that are adjacent to a singularity. * @param */ - void buildQuantizedMeshInternalNodes(); + std::vector nonZeroHalfEdges(); /*-------------------------------------------------------------------------*/ - /** @brief Build quantized mesh faces. + /** @brief Return the set of groups of nodes, each group being a set of nodes connected by half-edges of length 0. * @param */ - void buildQuantizedMeshFaces(); + std::vector> groupsOfConfundedNodes(); + + /*-------------------------------------------------------------------------*/ + /** @brief Return true if the given node is a corner of the boundary. + * @param + */ + bool isABoundaryCorner(Node AN); + + /*-------------------------------------------------------------------------*/ + /** @brief Add on the fixed mesh the already existing nodes. + * @param + */ + void addOldNodesOnFixedMesh(); + + /*-------------------------------------------------------------------------*/ + /** @brief Write the fixed mesh. + * @param + */ + void writeFixedMesh(std::basic_string AFileName); + + /*-------------------------------------------------------------------------*/ + /** @brief Mark with 1 the faces affected when adding a singularity dipole in the face deliminted by the two given half edges, + * ie the face itself and its neighbours not adjacent to AId1 or AId2. + * @param AId1, AId2 two half edges Ids delimiting a quad. + */ + void markAffectedFaces(int AId1, int AId2); + + /*-------------------------------------------------------------------------*/ + /** @brief Add on the fixed mesh the already existing faces not affected by the dipole. + * @param + */ + void addOldFacesOnFixedMesh(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build faces of the fixed mesh oppposite to the given half-edge, taking into account eventual new nodes. + * @param AHalfEdgeId, AN1, AN2 a half-edge Id and two nodes of the fixed-mesh belonging to the half-edge. + */ + void buildOppFacesInFixedMesh(int AHalfEdgeId, Node AN1, Node AN2); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the fixed mesh, where a singularity dipole has been added and oriented in the face delimited by two half edges whose ids are given. + * @param AId1, AId2 two half-edge ids. The dipole 3-5 is placed in the direction AId1->AId2. + */ + void buildFixedMesh(int AId1, int AId2); + + /*-------------------------------------------------------------------------*/ + /** @brief Get the fixed mesh. + * @param + */ + Mesh getFixedMesh(); + + /*-------------------------------------------------------------------------*/ + /** @brief Set the connectivity of the fixed mesh. + * @param + */ + void setFixedMeshConnectivity(); + + /*-------------------------------------------------------------------------*/ + /** @brief Set the connectivity of the fixed mesh. + * @param + */ + void forceZeroEdgesToZeroLength(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the complete quantization solution. + * @param + */ + std::vector> buildCompleteSolution(); }; /*----------------------------------------------------------------------------*/ } // end namespace gmds diff --git a/modules/medialaxis/inc/gmds/medialaxis/TrianglesRemover.h b/modules/medialaxis/inc/gmds/medialaxis/TrianglesRemover.h new file mode 100644 index 000000000..9137365af --- /dev/null +++ b/modules/medialaxis/inc/gmds/medialaxis/TrianglesRemover.h @@ -0,0 +1,150 @@ +// An object that tries to remove the triangles of a given almost-quad mesh. + +#ifndef GMDS_TRIANGLESREMOVER_H +#define GMDS_TRIANGLESREMOVER_H +/*----------------------------------------------------------------------------*/ +#include "GMDSMedialaxis_export.h" +#include "gmds/medialaxis/NonConformalHalfEdge.h" +#include "gmds/medialaxis/MedialAxisMath.h" +#include "gmds/io/IGMeshIOService.h" +#include "gmds/io/VTKWriter.h" +#include "gmds/ig/MeshDoctor.h" +#include +/*----------------------------------------------------------------------------*/ +namespace gmds{ +/** \class dummy + * \brief dummy class. + */ +class GMDSMedialaxis_API TrianglesRemover +{ + private: + // Degenerated quad mesh to fix + Mesh* m_degenerated_mesh; + // T-mesh + Mesh* m_t_mesh; + // Final T-mesh + Mesh* m_final_t_mesh; + // Groups of nodes of the degenerate mesh : two nodes belong to the same group iff they are both triangle vertices of the same fan + std::vector> m_degenerated_nodes_groups; + + public: + + /*-------------------------------------------------------------------------*/ + /** @brief Constructor. + * @param AMesh + */ + explicit TrianglesRemover(Mesh &AMesh); + + /*-------------------------------------------------------------------------*/ + /** @brief Default destructor. + * @param + */ + virtual ~TrianglesRemover()=default; + + /*-------------------------------------------------------------------------*/ + /** @brief Write the degenerated mesh. + * @param + */ + void writeDegeneratedMesh(std::basic_string AFileName); + + /*-------------------------------------------------------------------------*/ + /** @brief Write the T-mesh. + * @param + */ + void writeTMesh(std::basic_string AFileName); + + /*-------------------------------------------------------------------------*/ + /** @brief Faces composing the fan. + * @param AFanId + */ + std::vector fan(int AFanId); + + /*-------------------------------------------------------------------------*/ + /** @brief Return an ordered vector of nodes forming the outline of the shape formed by the given faces. + * @param AV a vector of faces forming the shape + */ + std::vector outline(std::vector &AV); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the T-mesh. + * @param + */ + void buildTMesh(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the degenerated nodes groups. + * @param + */ + void buildDegeneratedNodesGroups(); + + /*-------------------------------------------------------------------------*/ + /** @brief Sub-outlines of the given fan outline of type 1. + * @param AOutline, AFan, AFanId a vector of nodes forming the outline of a fan, the fan, and the Id of the fan + */ + std::vector> subOutlines1(std::vector AOutline, std::vector AFan, int AFanId); + + /*-------------------------------------------------------------------------*/ + /** @brief Sub-outlines of the given fan outline of type 2. + * @param AOutline, AFan, AFanId a vector of nodes forming the outline of a fan, the fan, and the Id of the fan + */ + std::vector> subOutlines2(std::vector AOutline, std::vector AFan, int AFanId); + + /*-------------------------------------------------------------------------*/ + /** @brief Get the T-mesh. + * @param + */ + Mesh getTMesh(); + + /*-------------------------------------------------------------------------*/ + /** @brief Set the connectivity of the T-mesh. + * @param + */ + void setTMeshConnectivity(); + + /*-------------------------------------------------------------------------*/ + /** @brief Mark internal constraints on edges. + * @param + */ + void markInternalConstraintsOnEdges(); + + /*-------------------------------------------------------------------------*/ + /** @brief Transforms the unwanted triangles into quads. + * @param + */ + void transformUnwantedTrianglesIntoQuads(); + + /*-------------------------------------------------------------------------*/ + /** @brief Build the final T-mesh. + * @param + */ + void buildFinalTMesh(); + + /*-------------------------------------------------------------------------*/ + /** @brief Write the final T-mesh. + * @param + */ + void writeFinalTMesh(std::basic_string AFileName); + + /*-------------------------------------------------------------------------*/ + /** @brief Get the final T-mesh. + * @param + */ + Mesh getFinalTMesh(); + + /*-------------------------------------------------------------------------*/ + /** @brief Set the connectivity of the final T-mesh. + * @param + */ + void setFinalTMeshConnectivity(); + + /*-------------------------------------------------------------------------*/ + /** @brief Mark internal constraints on edges of the final T-mesh. + * @param + */ + void markInternalConstraintsOnFinalEdges(); + +}; +/*----------------------------------------------------------------------------*/ +} // end namespace gmds +/*----------------------------------------------------------------------------*/ +#endif // GMDS_TRIANGLESREMOVER_H \ No newline at end of file diff --git a/modules/medialaxis/src/BlockStructureSimplifier.cpp b/modules/medialaxis/src/BlockStructureSimplifier.cpp new file mode 100644 index 000000000..695457545 --- /dev/null +++ b/modules/medialaxis/src/BlockStructureSimplifier.cpp @@ -0,0 +1,1091 @@ +#include "gmds/medialaxis/BlockStructureSimplifier.h" +#include +/*----------------------------------------------------------------------------*/ +namespace gmds { +/*----------------------------------------------------------------------------*/ +BlockStructureSimplifier::BlockStructureSimplifier(gmds::Mesh &AMesh) +{ + // Non conformal quad mesh + m_mesh = &AMesh; + // Correspondence edge/half edge + m_mesh->newVariable("edge2halfEdge1"); + m_mesh->newVariable("edge2halfEdge2"); + // Mark with 1 the nodes forming a T-junction + m_mesh->newVariable("T-junction"); + // Id of the corresponding confunded nodes group + m_mesh->newVariable("groupId"); + // In the mesh simplifying process, gain of the chosing of the node + m_mesh->newVariable("chosing_gain"); + // Attach 1 to edges belonging to a separatrix + m_mesh->newVariable("belongs_to_a_separatrix"); + // Id of the block to which the face belongs + m_mesh->newVariable("block_id"); + // Nodes coming from intersection points of the medial axis + try {m_mesh->getVariable("is_an_intersection_point");} + catch (GMDSException& e){m_mesh->newVariable("is_an_intersection_point");} + // TopMaker blocks outline + m_mesh->newVariable("TopMaker_block_outline"); + // Id of the TopMaker block to which the face belongs + m_mesh->newVariable("TopMaker_block_id"); + + // Quantization graph + m_quantization_graph = new QuantizationGraph(); + + // Simplified mesh(zero half edges removed) + m_simplified_mesh = new Mesh(MeshModel(DIM3 | E | N | F | + E2N | N2E | F2N | N2F | F2E | E2F)); + +} + +/*----------------------------------------------------------------------------*/ +void +BlockStructureSimplifier::buildHalfEdges() +{ + std::cout<<"> Building half edges"<getVariable("edge2halfEdge1"); + auto e2he2 = m_mesh->getVariable("edge2halfEdge2"); + for (auto e_id:m_mesh->edges()) + { + e2he1->set(e_id,-1); + e2he2->set(e_id,-1); + } + + // Build the half edges + int Id = 0; + for (auto f_id:m_mesh->faces()) + { + Face f = m_mesh->get(f_id); + std::vector> edges_groups = groupsOfAlignedEdges(f); + if (edges_groups.size() != 4) + throw GMDSException("buildHalfEdges() : non conformal quads must have 4 non conformal edges"); + for (auto i = 0; i < 4; i++) + { + NonConformalHalfEdge new1 = NonConformalHalfEdge(Id+i,f,edges_groups[i]); + for (auto e:edges_groups[i]) + { + if (e2he1->value(e.id()) == -1) + e2he1->set(e.id(),Id+i); + else + e2he2->set(e.id(),Id+i); + } + // Build next elements + if (i < 3) + new1.next(Id+i+1); + else + new1.next(Id); + // Build first and last nodes + if (i > 0) + new1.firstNode(getCommonNode(edges_groups[i][0],edges_groups[i-1][edges_groups[i-1].size()-1])); + else + new1.firstNode(getCommonNode(edges_groups[i][0],edges_groups[i+3][edges_groups[i+3].size()-1])); + if (i < 3) + new1.endNode(getCommonNode(edges_groups[i][edges_groups[i].size()-1],edges_groups[i+1][0])); + else + new1.endNode(getCommonNode(edges_groups[i][edges_groups[i].size()-1],edges_groups[0][0])); + m_half_edges.push_back(new1); + } + Id += 4; + } + + // Set the opposite half edges + for (auto half_edge:m_half_edges) + { + NonConformalHalfEdge he2 = half_edge; + std::vector opp; + for (auto e:half_edge.edges()) + { + int i1 = e2he1->value(e.id()); + int i2 = e2he2->value(e.id()); + if (i1 == half_edge.id()) + { + bool alreadySeen = false; + for (auto i:opp) + { + if (i == i2) + alreadySeen = true; + } + if (i2 >= 0 && !alreadySeen) + opp.push_back(i2); + } + else + { + bool alreadySeen = false; + for (auto i:opp) + { + if (i == i1) + alreadySeen = true; + } + if (i1 >= 0 && !alreadySeen) + opp.push_back(i1); + } + } + he2.opposite(opp); + // Replace the old edge by the updated edge + m_half_edges[half_edge.id()] = he2; + } + + // test +// for (auto half_edge:m_half_edges) +// { +// std::cout<<"Test "; +// for (auto i:half_edge.opposite()) +// std::cout< Building quantization graph nodes"<newNode(); +} + +/*----------------------------------------------------------------------------*/ +void +BlockStructureSimplifier::buildConnectedComponent(int AHalfEdgeID) +{ + std::cout<<"> Building connected component of half edge "< front; + front.push(AHalfEdgeID); + int current; + while (!front.empty()) + { + current = front.front(); + front.pop(); + m_quantization_graph->markAsVisited(current); + NonConformalHalfEdge e = m_half_edges[current]; + // Build edges linking to opp + for (auto opp:e.opposite()) + { + if (m_quantization_graph->alreadyVisited(opp) == 0) + { + m_quantization_graph->newEdge(current,opp); + if (m_quantization_graph->alreadyPushed(oppositeInQuad(opp)) == 0) + { + front.push(oppositeInQuad(opp)); + m_quantization_graph->markAsPushed(oppositeInQuad(opp)); + } + } + } + + int nxt = oppositeInQuad(current); + if (m_quantization_graph->alreadyVisited(nxt) == 0) + { + // Build edge linking to next(next) + NonConformalHalfEdge e2 = m_half_edges[nxt]; + m_quantization_graph->markAsVisited(nxt); + Edge newGraphEdge = m_quantization_graph->newEdge(nxt,current); + m_quantization_graph->markAsInQuad(newGraphEdge.id()); + // Build edges linking next(next) to opp(next(next)) + for (auto opp:e2.opposite()) + { + if (m_quantization_graph->alreadyVisited(opp) == 0) + { + m_quantization_graph->newEdge(opp,nxt); + if (m_quantization_graph->alreadyPushed(opp) == 0) + { + front.push(opp); + m_quantization_graph->markAsPushed(opp); + } + } + } + } + } + +} + +/*----------------------------------------------------------------------------*/ +int +BlockStructureSimplifier::oppositeInQuad(int AHalfEdgeID) +{ + int opp = m_half_edges[m_half_edges[AHalfEdgeID].next()].next(); + return opp; +} + +/*----------------------------------------------------------------------------*/ +void +BlockStructureSimplifier::buildQuantizationGraph() +{ + std::cout<<" "<alreadyVisited(i) == 0) + buildConnectedComponent(i); + } + std::cout<<"====================================================="<display(); +} + +/*----------------------------------------------------------------------------*/ +QuantizationGraph* +BlockStructureSimplifier::getQuantizationGraph() +{ + return m_quantization_graph; +} + +/*----------------------------------------------------------------------------*/ +void BlockStructureSimplifier::setHalfEdgesLength() +{ + std::vector v(m_half_edges.size()); + for (int i = 0; i < m_half_edges.size(); i++) + v[i] = m_quantization_graph->quantizationSolutionValue(i); + m_half_edges_lengths = v; +} + +/*----------------------------------------------------------------------------*/ +std::vector> BlockStructureSimplifier::sides() +{ + auto e2he1 = m_mesh->getVariable("edge2halfEdge1"); + std::vector> sides; + auto alreadySeen = m_mesh->newMark(); + for (auto e_id:m_mesh->edges()) + { + if (!m_mesh->isMarked(e_id,alreadySeen)) + { + Edge e = m_mesh->get(e_id); + if (e.get().size() == 1) + { + // Then it is a boundary edge, we build a new side + std::vector newSide; + std::queue front; + front.push(e); + m_mesh->mark(e_id,alreadySeen); + while (!front.empty()) + { + Edge e1 = front.front(); + front.pop(); + newSide.push_back(e2he1->value(e1.id())); + for (auto n:e1.get()) + { + for (auto e2:n.get()) + { + if (e2.get().size() == 1) + { + if (!m_mesh->isMarked(e2.id(),alreadySeen)) + { + double alpha = oriented_angle(edge2vec(e1,n),edge2vec(e2,n)); + if (fabs(alpha-M_PI) < 0.1 || fabs(alpha+M_PI) < 0.1) + { + front.push(e2); + m_mesh->mark(e2.id(),alreadySeen); + } + } + } + } + } + } + sides.push_back(newSide); + } + } + } + return sides; +} + +/*----------------------------------------------------------------------------*/ +void BlockStructureSimplifier::markTJunctions() +{ + auto TJ = m_mesh->getVariable("T-junction"); + auto e2he1 = m_mesh->getVariable("edge2halfEdge1"); + auto e2he2 = m_mesh->getVariable("edge2halfEdge2"); + for (auto n_id:m_mesh->nodes()) + { + Node n = m_mesh->get(n_id); + bool isATJ = false; + for (auto e:n.get()) + { + NonConformalHalfEdge he1 = m_half_edges[e2he1->value(e.id())]; + if (n.id() != he1.firstNode().id() && n.id() != he1.endNode().id()) + { + isATJ = true; + break; + } + if (e2he2->value(e.id()) >= 0) + { + he1 = m_half_edges[e2he2->value(e.id())]; + if (n.id() != he1.firstNode().id() && n.id() != he1.endNode().id()) + { + isATJ = true; + break; + } + } + } + if (isATJ) + TJ->set(n_id,1); + } +} + +/*----------------------------------------------------------------------------*/ +bool BlockStructureSimplifier::isOnBoundary(Node AN) +{ + bool boo = false; + for (auto e:AN.get()) + { + if (e.get().size() == 1) + { + boo = true; + break; + } + } + return boo; +} + +/*----------------------------------------------------------------------------*/ +std::vector BlockStructureSimplifier::nonZeroHalfEdges() +{ + auto e2he1 = m_mesh->getVariable("edge2halfEdge1"); + auto e2he2 = m_mesh->getVariable("edge2halfEdge2"); + auto TJ = m_mesh->getVariable("T-junction"); + std::vector alreadyAdded(m_half_edges.size()); + for (int i = 0; i < m_half_edges.size(); i++) + alreadyAdded[i] = 0; + std::vector nzhe; + for (auto n_id:m_mesh->nodes()) + { + Node n = m_mesh->get(n_id); + if (!isOnBoundary(n)) + { + if (TJ->value(n_id) == 0) + { + std::vector adj_edges = n.get(); + if (adj_edges.size() != 4) + { + // Then it is an interior singularity + for (auto e:adj_edges) + { + int he = e2he1->value(e.id()); + if (alreadyAdded[he] == 0) + { + nzhe.push_back(he); + alreadyAdded[he] = 1; + } + he = e2he2->value(e.id()); + if (he >= 0) + { + if (alreadyAdded[he] == 0) + { + nzhe.push_back(he); + alreadyAdded[he] = 1; + } + } + + } + } + } + } + else + { + std::vector adj_edges = n.get(); + if (adj_edges.size() != 3) + { + // Then it is a boundary singularity + for (auto e:adj_edges) + { + if (e.get().size() == 2) + { + int he = e2he1->value(e.id()); + if (alreadyAdded[he] == 0) + { + nzhe.push_back(he); + alreadyAdded[he] = 1; + } + he = e2he2->value(e.id()); + if (he >= 0) + { + if (alreadyAdded[he] == 0) + { + nzhe.push_back(he); + alreadyAdded[he] = 1; + } + } + } + } + } + } + } + return nzhe; +} + +/*----------------------------------------------------------------------------*/ +std::vector> BlockStructureSimplifier::groupsOfConfundedNodes() +{ + std::cout<<"> Building groups of confunded nodes"<getVariable("groupId"); + auto e2he1 = m_mesh->getVariable("edge2halfEdge1"); + auto e2he2 = m_mesh->getVariable("edge2halfEdge2"); + std::vector> groups; + auto visited = m_mesh->newMark(); + int Id = 0; + for (auto n_id:m_mesh->nodes()) + { + if (!m_mesh->isMarked(n_id,visited)) + { + // Then we create a new group + std::vector newGroup; + std::queue toVisit; + Node n0 = m_mesh->get(n_id); + toVisit.push(n0); + m_mesh->mark(n_id,visited); + while (!toVisit.empty()) + { + Node n = toVisit.front(); + toVisit.pop(); + newGroup.push_back(n); + groupId->set(n.id(),Id); + for (auto e:n.get()) + { + int he_id = e2he1->value(e.id()); + if (m_half_edges_lengths[he_id] == 0) + { + NonConformalHalfEdge he = m_half_edges[he_id]; + for (auto n1:he.getOrderedNodes()) + { + if (!m_mesh->isMarked(n1.id(),visited)) + { + toVisit.push(n1); + m_mesh->mark(n1.id(),visited); + } + } + } + he_id = e2he2->value(e.id()); + if (he_id >= 0) + { + if (m_half_edges_lengths[he_id] == 0) + { + NonConformalHalfEdge he = m_half_edges[he_id]; + for (auto n1:he.getOrderedNodes()) + { + if (!m_mesh->isMarked(n1.id(),visited)) + { + toVisit.push(n1); + m_mesh->mark(n1.id(),visited); + } + } + } + } + } + } + groups.push_back(newGroup); + Id += 1; + } + } + return groups; +} + +/*----------------------------------------------------------------------------*/ +bool BlockStructureSimplifier::isABoundaryCorner(Node AN) +{ + if (!isOnBoundary(AN)) + return false; + else + { + // Find the two boundary edges adjacent at AN + Edge e1,e2; + for (auto e:AN.get()) + { + if (e.get().size() == 1) + { + e1 = e; + break; + } + } + for (auto e:AN.get()) + { + if (e.get().size() == 1 && e.id() != e1.id()) + { + e2 = e; + break; + } + } + double alpha = oriented_angle(edge2vec(e1,AN),edge2vec(e2,AN)); + if (fabs(alpha-M_PI) > 0.1 && fabs(alpha+M_PI) > 0.1) + return true; + else + return false; + } +} + +/*----------------------------------------------------------------------------*/ +void BlockStructureSimplifier::computeChosingGains() +{ + auto cost = m_mesh->getVariable("chosing_gain"); + for (auto n_id:m_mesh->nodes()) + { + Node n = m_mesh->get(n_id); + if (isABoundaryCorner(n)) + cost->set(n_id,16.); + else + { + if (isOnBoundary(n)) + cost->set(n_id,15.); + else + { + if (n.get().size() != 4) + cost->set(n_id,14.); + else + { + std::vector adj_edges = n.get(); + std::vector sorted_edges = sortEdges(n,adj_edges); + math::Vector u1 = edge2vec(adj_edges[0],n); + math::Vector u2 = (-1.)*edge2vec(adj_edges[2],n); + math::Vector v1 = edge2vec(adj_edges[1],n); + math::Vector v2 = (-1.)*edge2vec(adj_edges[3],n); + double c = sqrt(fabs(oriented_angle(u1,u2))*fabs(oriented_angle(u1,u2))+fabs(oriented_angle(v1,v2))*fabs(oriented_angle(v1,v2))); + cost->set(n_id,c); + } + } + } + } +} + +/*----------------------------------------------------------------------------*/ +Node BlockStructureSimplifier::representative(std::vector AV) +{ + auto gain = m_mesh->getVariable("chosing_gain"); + Node rep; + double max_gain = -1.; + for (auto n:AV) + { + if (gain->value(n.id()) > max_gain) + { + rep = n; + max_gain = gain->value(n.id()); + } + } + // if (AV.size() == 1) + // return AV[0]; + // for (auto n:AV) + // { + // if (isABoundaryCorner(n)) + // return n; + // } + // for (auto n:AV) + // { + // if (isOnBoundary(n)) + // return n; + // } + // return AV[0]; + return rep; +} + +/*----------------------------------------------------------------------------*/ +void BlockStructureSimplifier::buildSimplifiedMesh() +{ + std::cout<<"=== Building simplified mesh ==="<getVariable("groupId"); + // build the nodes + std::vector> groups = groupsOfConfundedNodes(); + std::vector group2newNode; + for (auto group:groups) + { + Node newNode = m_simplified_mesh->newNode(representative(group).point()); + group2newNode.push_back(newNode.id()); + } + // Build the faces + for (auto f_id:m_mesh->faces()) + { + // Check if it has non zero area + if (m_half_edges_lengths[4*f_id] > 0 && m_half_edges_lengths[4*f_id+1] > 0) + { + std::vector nodes; + Face f = m_mesh->get(f_id); + std::vector seenGroups; + for (auto n:f.get()) + { + int id = groupId->value(n.id()); + bool seen = false; + for (auto i:seenGroups) + { + if (id == i) + { + seen = true; + break; + } + } + if (!seen) + { + nodes.push_back(group2newNode[id]); + seenGroups.push_back(id); + } + } + m_simplified_mesh->newFace(nodes); + } + } + std::cout<<"============================"< AFileName) +{ + std::cout<<"> Writing the simplified mesh"<> sides2 = sides(); + m_quantization_graph->nonZeroGroups(sides2); + // Set condition to separate singularities + markTJunctions(); + std::vector non_zeros2 = nonZeroHalfEdges(); + m_quantization_graph->nonZeroVerticies(non_zeros2); + m_quantization_graph->updateConnectivity(); + m_quantization_graph->buildMinimalSolution(); + //m_quantization_graph->roughlyRepairSolution(); + //m_quantization_graph->displaySolution(); + setHalfEdgesLength(); + computeChosingGains(); + buildSimplifiedMesh(); + + std::cout<<"===================================="< Marking separatricies"<getVariable("belongs_to_a_separatrix"); + for (auto n_id:m_mesh->nodes()) + { + Node n = m_mesh->get(n_id); + if ((isOnBoundary(n) && n.get().size() != 3) || (!isOnBoundary(n) && n.get().size() != 4)) + { + // Then it is a singularity + std::vector adj_edges = n.get(); + for (auto e0:adj_edges) + { + if (e0.get().size() == 2) + { + // Then we trace the separatrix + Node prev = n; + Node nxt; + if (e0.get()[0].id() == prev.id()) + nxt = e0.get()[1]; + else + nxt = e0.get()[0]; + sep->set(e0.id(),1); + Edge current = e0; + while (!isOnBoundary(nxt) && nxt.get().size() == 4) + { + prev = nxt; + Face f1 = current.get()[0]; + Face f2 = current.get()[1]; + Face f3,f4; + for (auto f:prev.get()) + { + if (f.id() != f1.id() && f.id() != f2.id()) + { + f3 = f; + break; + } + } + for (auto f:prev.get()) + { + if (f.id() != f1.id() && f.id() != f2.id() && f.id() != f3.id()) + { + f4 = f; + break; + } + } + bool found = false; + for (auto e1:f3.get()) + { + for (auto e2:f4.get()) + { + if (e1.id() == e2.id()) + { + current = e1; + found = true; + break; + } + } + if (found) + break; + } + if (prev.id() == current.get()[0].id()) + nxt = current.get()[1]; + else + nxt = current.get()[0]; + sep->set(current.id(),1); + // prev = nxt; + // std::vector edges = prev.get(); + // std::vector sorted_edges = sortEdges(prev,edges); + // // Find the current edge in the list + // int pos; + // for (int i = 0; i < sorted_edges.size(); i++) + // { + // if (sorted_edges[i].id() == current.id()) + // { + // pos = i; + // break; + // } + // } + // current = sorted_edges[(pos+2)%4]; + // if (current.get()[0].id() == prev.id()) + // nxt = current.get()[1]; + // else + // nxt = current.get()[0]; + // sep->set(current.id(),1); + } + } + + } + } + } +} + +/*----------------------------------------------------------------------------*/ +void BlockStructureSimplifier::setBlocksIDs() +{ + std::cout<<"> Setting blocks IDs"<getVariable("block_id"); + auto sep = m_mesh->getVariable("belongs_to_a_separatrix"); + auto added = m_mesh->newMark(); + int ID = 0; + // Initialize Ids to -1 + for (auto f_id:m_mesh->faces()) + id->set(f_id,-1); + for (auto f_id:m_mesh->faces()) + { + if (id->value(f_id) == -1) + { + // Then we create a new block + Face f = m_mesh->get(f_id); + std::queue toAdd; + toAdd.push(f); + m_mesh->mark(f.id(),added); + while(!toAdd.empty()) + { + Face f1 = toAdd.front(); + toAdd.pop(); + id->set(f1.id(),ID); + for (auto e:f1.get()) + { + if (sep->value(e.id()) == 0) + { + for (auto f2:e.get()) + { + if (f2.id() != f1.id() && !m_mesh->isMarked(f2.id(),added)) + { + toAdd.push(f2); + m_mesh->mark(f2.id(),added); + } + } + } + } + } + ID += 1; + } + } +} + +/*----------------------------------------------------------------------------*/ +void BlockStructureSimplifier::traceTopMakerBlocksOutlines() +{ + std::cout<<"> Tracing outlines of the TopMaker blocks"<getVariable("is_an_intersection_point"); + auto tmbo = m_mesh->getVariable("TopMaker_block_outline"); + // Traces coming out of intersection points + for (auto n_id:m_mesh->nodes()) + { + if (ip->value(n_id) == 1) + { + Node n = m_mesh->get(n_id); + std::queue to_add; + auto visited = m_mesh->newVariable("visited"); + for (auto e:n.get()) + { + if (e.get().size() == 2) + { + to_add.push(e); + visited->set(e.id(),1); + } + } + while (!to_add.empty()) + { + Edge e1 = to_add.front(); + to_add.pop(); + tmbo->set(e1.id(),1); + for (auto n1:e1.get()) + { + if (isInterior(n1) && n1.get().size() == 4) + { + Face f1 = e1.get()[0]; + Face f2 = e1.get()[1]; + Face f3,f4; + for (auto f:n1.get()) + { + if (f.id() != f1.id() && f.id() != f2.id()) + { + f3 = f; + break; + } + } + for (auto f:n1.get()) + { + if (f.id() != f1.id() && f.id() != f2.id() && f.id() != f3.id()) + { + f4 = f; + break; + } + } + Edge e2; + for (auto e3:f3.get()) + { + for (auto e4:f4.get()) + { + if (e3.id() == e4.id()) + e2 = e3; + } + } + if (visited->value(e2.id()) == 0) + { + visited->set(e2.id(),1); + to_add.push(e2); + } + } + } + } + m_mesh->deleteVariable(GMDS_EDGE,visited); + } + } + // Traces coming out of singularities already belonging to an outline + for (auto n_id:m_mesh->nodes()) + { + Node n = m_mesh->get(n_id); + if (!isInterior(n) || n.get().size() == 4) + continue; + bool belongs_to_an_outline = false; + for (auto e:n.get()) + { + if (tmbo->value(e.id()) == 1) + { + belongs_to_an_outline = true; + break; + } + } + if (belongs_to_an_outline) + { + std::queue to_add; + auto visited = m_mesh->newVariable("visited"); + for (auto e:n.get()) + { + if (e.get().size() == 2) + { + to_add.push(e); + visited->set(e.id(),1); + } + } + while (!to_add.empty()) + { + Edge e1 = to_add.front(); + to_add.pop(); + tmbo->set(e1.id(),1); + for (auto n1:e1.get()) + { + if (isInterior(n1) && n1.get().size() == 4) + { + Face f1 = e1.get()[0]; + Face f2 = e1.get()[1]; + Face f3,f4; + for (auto f:n1.get()) + { + if (f.id() != f1.id() && f.id() != f2.id()) + { + f3 = f; + break; + } + } + for (auto f:n1.get()) + { + if (f.id() != f1.id() && f.id() != f2.id() && f.id() != f3.id()) + { + f4 = f; + break; + } + } + Edge e2; + for (auto e3:f3.get()) + { + for (auto e4:f4.get()) + { + if (e3.id() == e4.id()) + e2 = e3; + } + } + if (visited->value(e2.id()) == 0) + { + visited->set(e2.id(),1); + to_add.push(e2); + } + } + } + } + m_mesh->deleteVariable(GMDS_EDGE,visited); + } + } + // Traces coming out of the last singularities + for (auto n_id:m_mesh->nodes()) + { + Node n = m_mesh->get(n_id); + if (!isInterior(n) || n.get().size() == 4) + continue; + bool already_visited = true; + for (auto e:n.get()) + { + if (tmbo->value(e.id()) == 0) + { + already_visited = false; + break; + } + } + if (!already_visited) + { + std::queue to_add; + auto visited = m_mesh->newVariable("visited"); + for (auto e:n.get()) + { + if (e.get().size() == 2) + { + to_add.push(e); + visited->set(e.id(),1); + } + } + while (!to_add.empty()) + { + Edge e1 = to_add.front(); + to_add.pop(); + tmbo->set(e1.id(),1); + for (auto n1:e1.get()) + { + bool reached_an_outline = false; + for (auto e5:n1.get()) + { + if (e5.id() != e1.id() && tmbo->value(e5.id()) == 1) + { + reached_an_outline = true; + break; + } + } + if (isInterior(n1) && n1.get().size() == 4 && !reached_an_outline) + { + Face f1 = e1.get()[0]; + Face f2 = e1.get()[1]; + Face f3,f4; + for (auto f:n1.get()) + { + if (f.id() != f1.id() && f.id() != f2.id()) + { + f3 = f; + break; + } + } + for (auto f:n1.get()) + { + if (f.id() != f1.id() && f.id() != f2.id() && f.id() != f3.id()) + { + f4 = f; + break; + } + } + Edge e2; + for (auto e3:f3.get()) + { + for (auto e4:f4.get()) + { + if (e3.id() == e4.id()) + e2 = e3; + } + } + if (visited->value(e2.id()) == 0) + { + visited->set(e2.id(),1); + to_add.push(e2); + } + } + } + } + m_mesh->deleteVariable(GMDS_EDGE,visited); + } + } +} + +/*----------------------------------------------------------------------------*/ +void BlockStructureSimplifier::setTopMakerBlocksIDs() +{ + std::cout<<"> Setting TopMaker blocks IDs"<getVariable("TopMaker_block_id"); + auto sep = m_mesh->getVariable("TopMaker_block_outline"); + auto added = m_mesh->newMark(); + int ID = 0; + // Initialize Ids to -1 + for (auto f_id:m_mesh->faces()) + id->set(f_id,-1); + for (auto f_id:m_mesh->faces()) + { + if (id->value(f_id) == -1) + { + // Then we create a new block + Face f = m_mesh->get(f_id); + std::queue toAdd; + toAdd.push(f); + m_mesh->mark(f.id(),added); + while(!toAdd.empty()) + { + Face f1 = toAdd.front(); + toAdd.pop(); + id->set(f1.id(),ID); + for (auto e:f1.get()) + { + if (sep->value(e.id()) == 0) + { + for (auto f2:e.get()) + { + if (f2.id() != f1.id() && !m_mesh->isMarked(f2.id(),added)) + { + toAdd.push(f2); + m_mesh->mark(f2.id(),added); + } + } + } + } + } + ID += 1; + } + } +} +/*----------------------------------------------------------------------------*/ +} // end namespace gmds +/*----------------------------------------------------------------------------*/ \ No newline at end of file diff --git a/modules/medialaxis/src/ConformalMeshBuilder.cpp b/modules/medialaxis/src/ConformalMeshBuilder.cpp new file mode 100644 index 000000000..734dbf53e --- /dev/null +++ b/modules/medialaxis/src/ConformalMeshBuilder.cpp @@ -0,0 +1,315 @@ +#include "gmds/medialaxis/ConformalMeshBuilder.h" +/*----------------------------------------------------------------------------*/ +namespace gmds { +/*----------------------------------------------------------------------------*/ +ConformalMeshBuilder::ConformalMeshBuilder(gmds::Mesh &AMesh, std::vector AHalfEdges, std::vector ALengths) +{ + // Non conformal quad mesh + m_mesh = &AMesh; + // Nodes of the quantized mesh corresponding to each non-conformal face + m_mesh->newVariable>,GMDS_FACE>("face2quantizedNodes"); + + // Corresponding non-conformal half edges + m_half_edges = AHalfEdges; + // Half edges length. WARNING : all lengths must be > 0 + m_half_edges_lengths = ALengths; + + // Quantized mesh + m_quantized_mesh = new Mesh(MeshModel(DIM3 | E | N | F | + E2N | N2E | F2N | N2F | F2E | E2F)); +} + +/*----------------------------------------------------------------------------*/ +void ConformalMeshBuilder::writeQuantizedMesh(std::basic_string AFileName) +{ + std::cout<<"> Writing the quantized mesh"< Setting quantized mesh connectivity"< Building nodes of the quantized mesh that lay on non-conformal edges"<> v(m_half_edges.size()); + m_half_edges_to_new_nodes = v; + // Add the nodes of the non-conformal mesh + for (auto n_id:m_mesh->nodes()) + { + Node n = m_mesh->get(n_id); + m_quantized_mesh->newNode(n.point()); + } + // Build the new nodes + std::vector>> hegroups = halfEdgesGroups(); + for (auto g:hegroups) + { + // Get the groups of half edges on each side + std::vector g1 = g[0]; + std::vector g2 = g[1]; + // Compute the total length of the group + int L = 0; + for (auto i:g1) + L += m_half_edges_lengths[i]; + // If the half edges is on the boundary, ie g2 is empty + if (g2.empty()) + { + NonConformalHalfEdge e = m_half_edges[g1[0]]; + std::vector newNodes; + newNodes.push_back(e.firstNode().id()); + math::Point E = e.endNode().point()+(-1.)*e.firstNode().point(); + math::Point P; + for (int l = 1; l < L; l++) + { + P = e.firstNode().point()+(double (l)/double(L))*E; + Node n = m_quantized_mesh->newNode(P); + newNodes.push_back(n.id()); + } + newNodes.push_back(e.endNode().id()); + m_half_edges_to_new_nodes[e.id()] = newNodes; + } + else // g2 is not empty + { + NonConformalHalfEdge e1 = m_half_edges[g1[0]]; + NonConformalHalfEdge e2 = m_half_edges[g2[0]]; + int i1 = 0; + int i2 = 0; + int L1 = 0; + int L2 = 0; + std::vector newNodes1; + std::vector newNodes2; + newNodes1.push_back(e1.firstNode().id()); + newNodes2.push_back(e2.endNode().id()); + for (int l = 1; l < L; l++) + { + if (l < L1+m_half_edges_lengths[e1.id()] && l < L2+m_half_edges_lengths[e2.id()]) + { + // Create the new node + math::Point dir1 = e1.endNode().point()+(-1.)*m_quantized_mesh->get(newNodes1[newNodes1.size()-1]).point(); + math::Point dir2 = e2.firstNode().point()+(-1.)*m_quantized_mesh->get(newNodes2[newNodes2.size()-1]).point(); + math::Point P; + if (vec(dir1).norm()/double(m_half_edges_lengths[e1.id()]) < vec(dir2).norm()/double(m_half_edges_lengths[e2.id()])) + P = m_quantized_mesh->get(newNodes1[newNodes1.size()-1]).point()+(1./double(m_half_edges_lengths[e1.id()]))*dir1; + else + P = m_quantized_mesh->get(newNodes2[newNodes2.size()-1]).point()+(1./double(m_half_edges_lengths[e2.id()]))*dir2; + Node n = m_quantized_mesh->newNode(P); + newNodes1.push_back(n.id()); + newNodes2.push_back(n.id()); + } + if (l == L1+m_half_edges_lengths[e1.id()] && l < L2+m_half_edges_lengths[e2.id()]) + { + newNodes1.push_back(e1.endNode().id()); + newNodes2.push_back(e1.endNode().id()); + L1 = L1 + m_half_edges_lengths[e1.id()]; + e1 = m_half_edges[g1[i1+1]]; + m_half_edges_to_new_nodes[g1[i1]] = newNodes1; + i1 += 1; + newNodes1.clear(); + newNodes1.push_back(e1.firstNode().id()); + } + if (l < L1+m_half_edges_lengths[e1.id()] && l == L2+m_half_edges_lengths[e2.id()]) + { + newNodes1.push_back(e2.firstNode().id()); + newNodes2.push_back(e2.firstNode().id()); + L2 = L2 + m_half_edges_lengths[e2.id()]; + e2 = m_half_edges[g2[i2+1]]; + std::reverse(newNodes2.begin(),newNodes2.end()); + m_half_edges_to_new_nodes[g2[i2]] = newNodes2; + i2 += 1; + newNodes2.clear(); + newNodes2.push_back(e2.endNode().id()); + } + } + newNodes1.push_back(m_half_edges[g1[g1.size()-1]].endNode().id()); + newNodes2.push_back(m_half_edges[g2[g2.size()-1]].firstNode().id()); + std::reverse(newNodes2.begin(),newNodes2.end()); + m_half_edges_to_new_nodes[g1[i1]] = newNodes1; + m_half_edges_to_new_nodes[g2[i2]] = newNodes2; + } + } +} + +/*----------------------------------------------------------------------------*/ +void ConformalMeshBuilder::buildQuantizedMeshInternalNodes() +{ + std::cout<<"> Building nodes of the quantized mesh that are internal to non-conformal faces"<getVariable>,GMDS_FACE>("face2quantizedNodes"); + for (auto f_id:m_mesh->faces()) + { + // Get the half edges of the face + NonConformalHalfEdge e1 = m_half_edges[4*f_id]; + NonConformalHalfEdge e2 = m_half_edges[4*f_id+1]; + NonConformalHalfEdge e3 = m_half_edges[4*f_id+2]; + NonConformalHalfEdge e4 = m_half_edges[4*f_id+3]; + std::vector> nodesIDs(m_half_edges_lengths[e2.id()]+1); + std::vector row(m_half_edges_lengths[e1.id()]+1); + // first row + for (int i = 0; i < m_half_edges_lengths[e1.id()]+1; i++) + row[i] = m_half_edges_to_new_nodes[e1.id()][i]; + nodesIDs[0] = row; + // Last row + for (int i = 0; i < m_half_edges_lengths[e3.id()]+1; i++) + row[i] = m_half_edges_to_new_nodes[e3.id()][m_half_edges_lengths[e3.id()]-i]; + nodesIDs[m_half_edges_lengths[e2.id()]] = row; + for (int j = 1; j < m_half_edges_lengths[e2.id()]; j++) + { + row[0] = m_half_edges_to_new_nodes[e4.id()][m_half_edges_lengths[e4.id()]-j]; + row[m_half_edges_lengths[e1.id()]] = m_half_edges_to_new_nodes[e2.id()][j]; + for (int i = 1; i < m_half_edges_lengths[e1.id()]; i++) + { + math::Point P1 = m_quantized_mesh->get(m_half_edges_to_new_nodes[e1.id()][i]).point(); + math::Point P2 = m_quantized_mesh->get(m_half_edges_to_new_nodes[e3.id()][m_half_edges_lengths[e3.id()]-i]).point(); + math::Point P = P1 + (double(j)/double(m_half_edges_lengths[e2.id()]))*(P2+(-1.)*P1); + Node n = m_quantized_mesh->newNode(P); + row[i] = n.id(); + } + nodesIDs[j] = row; + } + f2qn->set(f_id,nodesIDs); + } +} + +/*----------------------------------------------------------------------------*/ +void ConformalMeshBuilder::buildQuantizedMeshFaces() +{ + std::cout<<"> Building faces of the quantized mesh"<getVariable>,GMDS_FACE>("face2quantizedNodes"); + for (auto f_id:m_mesh->faces()) + { + std::vector> nodes = f2qn->value(f_id); + int I = nodes.size(); + int J = nodes[0].size(); + TCellID i1,i2,i3,i4; + for (int i = 0; i < I-1; i++) + { + for (int j = 0; j < J-1; j++) + { + i1 = nodes[i][j]; + i2 = nodes[i+1][j]; + i3 = nodes[i+1][j+1]; + i4 = nodes[i][j+1]; + m_quantized_mesh->newFace({i1,i2,i3,i4}); + } + } + } +} + +/*----------------------------------------------------------------------------*/ +std::vector> ConformalMeshBuilder::halfEdgesGroup(int AID, std::vector &AV) +{ + std::vector> groups; + std::vector group1; + std::vector group2; + NonConformalHalfEdge e = m_half_edges[AID]; + bool over = false; + while(!over) + { + group1.push_back(e.id()); + AV[e.id()] = 1; + for (auto opp:e.opposite()) + { + if (AV[opp] == 0) + { + group2.push_back(opp); + AV[opp] = 1; + } + } + if (e.opposite().empty()) + over = true; + else + { + NonConformalHalfEdge e2 = m_half_edges[e.opposite()[e.opposite().size()-1]]; + if (e2.firstNode().id() == e.endNode().id()) + over = true; + else + e = m_half_edges[m_half_edges[m_half_edges[e.next()].opposite()[0]].next()]; + } + } + groups.push_back(group1); + groups.push_back(group2); + return groups; +} + +/*----------------------------------------------------------------------------*/ +std::vector>> ConformalMeshBuilder::halfEdgesGroups() +{ + // Vector to mark the already seen half edges, initialized at 0 + std::vector V(m_half_edges.size()); + for (int i = 0; i < m_half_edges.size(); i++) + V[i] = 0; + + // Build the groups + std::vector>> groups; + std::vector> group; + for (int i = 0; i < m_half_edges.size(); i++) + { + if (V[i] == 0) + { + NonConformalHalfEdge e1 = m_half_edges[i]; + if (e1.opposite().empty()) + { + group = halfEdgesGroup(i,V); + groups.push_back(group); + } + else + { + NonConformalHalfEdge e2 = m_half_edges[e1.opposite()[0]]; + if (e1.firstNode().id() == e2.endNode().id()) + { + group = halfEdgesGroup(i,V); + groups.push_back(group); + } + } + } + } + +// // Test : display the groups +// for (auto g:groups) +// { +// std::cout<<"Group 1 : "; +// for (auto i:g[0]) +// std::cout< +#include +/*----------------------------------------------------------------------------*/ +namespace gmds { +/*----------------------------------------------------------------------------*/ +Conformalizer::Conformalizer(gmds::Mesh &AMesh, std::vector AHalfEdges, std::vector ALengths) +{ + // Non conformal quad mesh + m_mesh = &AMesh; + // Old node/new Node correspondance + m_mesh->newVariable("node2group"); + // List of ordered new nodes of each old edge + m_mesh->newVariable,GMDS_EDGE>("oldEdges2newNodes"); + // Old faces/new nodes correspondance + m_mesh->newVariable>,GMDS_FACE>("face2newNodes"); + // Internal constraints + try {m_mesh->getVariable("internal_constraint");} + catch (GMDSException& e){m_mesh->newVariable("internal_constraint");} + try {m_mesh->getVariable("belong_to_an_internal_constraint");} + catch (GMDSException& e){m_mesh->newVariable("belong_to_an_internal_constraint");} + // Correspondance with min tri nodes + try {m_mesh->getVariable("TMeshNode2MinTriNode");} + catch (GMDSException& e){m_mesh->newVariable("TMeshNode2MinTriNode");} + // Corners + try {m_mesh->getVariable("corner");} + catch (GMDSException& e){m_mesh->newVariable("corner");} + // Triangle fans Ids and angles + try {m_mesh->getVariable("fan_id");} + catch (GMDSException& e){m_mesh->newVariable("fan_id");} + try {m_mesh->getVariable("fan_angle");} + catch (GMDSException& e){m_mesh->newVariable("fan_angle");} + // Triangles + try {m_mesh->getVariable("is_a_triangle");} + catch (GMDSException& e){m_mesh->newVariable("is_a_triangle");} + // Nodes coming from intersection points of the medial axis + try {m_mesh->getVariable("is_an_intersection_point");} + catch (GMDSException& e){m_mesh->newVariable("is_an_intersection_point");} + + // Corresponding non-conformal half edges + m_half_edges = AHalfEdges; + // Half edges length. WARNING : all lengths must be > 0 + m_half_edges_lengths = ALengths; + + // Conformal mesh + m_conformal_mesh = new Mesh(MeshModel(DIM3 | E | N | F | + E2N | N2E | F2N | N2F | F2E | E2F)); + // Node/group of intermediate nodes correspondance + m_conformal_mesh->newVariable("node2group"); + // Node/representative of the group correspondance + m_conformal_mesh->newVariable("node2representative"); + // Mark with 1 edges corresponding to internal constraints + m_conformal_mesh->newVariable("internal_constraint"); + // Mark with 1 nodes belonging to an internal constraint + m_conformal_mesh->newVariable("belong_to_an_internal_constraint"); + // Boundary & constraint nodes/reference node correspondance + m_conformal_mesh->newVariable("Boundary&ConstraintNode2ReferenceNode"); + // Mark with 1 corners of the boundary and internal constraints + m_conformal_mesh->newVariable("corner"); + // Triangle fans Ids and angles + m_conformal_mesh->newVariable("fan_id"); + m_conformal_mesh->newVariable("fan_angle"); + // Mark with 1 degenerate quads + m_conformal_mesh->newVariable("is_a_triangle"); + // Nodes coming from intersection points of the medial axis + m_conformal_mesh->newVariable("is_an_intersection_point"); + + // Intermediate mesh + m_intermediate_mesh = new Mesh(MeshModel(DIM3 | E | N | F | + E2N | N2E | F2N | N2F | F2E | E2F)); + m_intermediate_mesh->newVariable("length"); +} + +/*----------------------------------------------------------------------------*/ +void Conformalizer::writeConformalMesh(std::basic_string AFileName) +{ + std::cout<<"> Writing the conformal mesh"< AFileName) +{ + std::cout<<"> Writing the intermediate mesh"< Setting conformal mesh connectivity"< Setting intermediate mesh connectivity"< Building groups of nodes of the non conformal mesh"<getVariable("node2group"); + auto l = m_mesh->getVariable("length"); + int groupId = 0; + std::vector> groups; + std::vector newGroup; + auto visited = m_mesh->newMark(); + for (auto n_id:m_mesh->nodes()) + { + if (!m_mesh->isMarked(n_id,visited)) + { + // We create a new group + newGroup.clear(); + std::queue toAdd; + Node n = m_mesh->get(n_id); + toAdd.push(n); + m_mesh->mark(n_id,visited); + while (!toAdd.empty()) + { + n = toAdd.front(); + newGroup.push_back(n.id()); + n2g->set(n.id(),groupId); + toAdd.pop(); + for (auto e:n.get()) + { + if (l->value(e.id()) == 0) + { + for (auto n1:e.get()) + { + if (!m_mesh->isMarked(n1.id(),visited)) + { + toAdd.push(n1); + m_mesh->mark(n1.id(),visited); + } + } + } + } + } + groups.push_back(newGroup); + groupId += 1; + } + } + m_non_conformal_nodes_groups = groups; +} + +/*----------------------------------------------------------------------------*/ +void Conformalizer::addOldNodesOnConformalMesh() +{ + std::cout<<"> Adding old nodes on the conformal mesh"<getVariable("Boundary&ConstraintNode2ReferenceNode"); + auto tmn2mtn = m_mesh->getVariable("TMeshNode2MinTriNode"); + auto constr = m_mesh->getVariable("belong_to_an_internal_constraint"); + auto c1 = m_mesh->getVariable("corner"); + auto c2 = m_conformal_mesh->getVariable("corner"); + auto fan_id1 = m_mesh->getVariable("fan_id"); + auto fan_angle1 = m_mesh->getVariable("fan_angle"); + auto fan_id2 = m_conformal_mesh->getVariable("fan_id"); + auto fan_angle2 = m_conformal_mesh->getVariable("fan_angle"); + auto ip1 = m_mesh->getVariable("is_an_intersection_point"); + auto ip2 = m_conformal_mesh->getVariable("is_an_intersection_point"); + for (auto group:m_non_conformal_nodes_groups) + { + double x = 0.; + double y = 0.; + Node rep; + bool boundOrConstr = false; + bool cor = false; + + // Check if there is a corner or a triangle vertex in the group + bool tri_ver_or_corner = false; + bool is_an_ip = false; + Node fixed_node; + + for (auto n_id:group) + { + Node n = m_mesh->get(n_id); + x += n.X(); + y += n.Y(); + if (!isInterior(n) || constr->value(n_id) == 1) + { + boundOrConstr = true; + rep = n; + } + if (c1->value(n_id) == 1) + { + cor = true; + tri_ver_or_corner = true; + fixed_node = n; + } + if (fan_id1->value(n_id) >= 0) + { + tri_ver_or_corner = true; + fixed_node = n; + } + if (ip1->value(n_id) == 1) + is_an_ip = true; + } + x = x/double(group.size()); + y = y/double(group.size()); + math::Point P(x,y,0.); + if (tri_ver_or_corner) + { + P.setX(fixed_node.X()); + P.setY(fixed_node.Y()); + } + Node new_node = m_conformal_mesh->newNode(P); + if (boundOrConstr) + n2rn->set(new_node.id(),tmn2mtn->value(rep.id())); + else + n2rn->set(new_node.id(),-1); + if (cor) + c2->set(new_node.id(),1); + if (is_an_ip) + ip2->set(new_node.id(),1); + // Update the fan information about the new point + fan_id2->set(new_node.id(),-1); + fan_angle2->set(new_node.id(),10.); + for (auto n_id:group) + { + if (fan_id1->value(n_id) >= 0) + { + fan_id2->set(new_node.id(),fan_id1->value(n_id)); + fan_angle2->set(new_node.id(),fan_angle1->value(n_id)); + break; + } + } + } +} + +/*----------------------------------------------------------------------------*/ +void Conformalizer::addSubdividingNodes() +{ + std::cout<<"> Adding subdividing nodes on the conformal mesh"<getVariable("length"); + auto oe2nn = m_mesh->getVariable,GMDS_EDGE>("oldEdges2newNodes"); + auto n2g = m_mesh->getVariable("node2group"); + auto int_constr = m_mesh->getVariable("internal_constraint"); + auto constr_nodes = m_conformal_mesh->getVariable("belong_to_an_internal_constraint"); + auto n2rn = m_conformal_mesh->getVariable("Boundary&ConstraintNode2ReferenceNode"); + auto fan_id1 = m_mesh->getVariable("fan_id"); + auto fan_angle1 = m_mesh->getVariable("fan_angle"); + auto fan_id2 = m_conformal_mesh->getVariable("fan_id"); + auto fan_angle2 = m_conformal_mesh->getVariable("fan_angle"); + // Add nodes subdividing edges + for (auto e_id:m_mesh->edges()) + { + int len = l->value(e_id); + Edge e = m_mesh->get(e_id); + Node n1 = e.get()[0]; + Node n2 = e.get()[1]; + int fanId = -1; + double fanAngle = 10.; + if (fan_id1->value(n1.id()) >= 0 && fan_id1->value(n1.id()) == fan_id1->value(n2.id())) + { + fanId = fan_id1->value(n1.id()); + fanAngle = fan_angle1->value(n1.id()); + } + bool boundOrConstr = (n2rn->value(n1.id()) >= 0 && n2rn->value(n2.id()) >= 0); + std::vector ordered_nodes; + if (len == 0) + { + ordered_nodes.push_back(m_conformal_mesh->get(n2g->value(n1.id()))); + oe2nn->set(e_id,ordered_nodes); + } + if (len >= 1) + { + ordered_nodes.push_back(m_conformal_mesh->get(n2g->value(n1.id()))); + math::Point dir = n2.point()+(-1.)*n1.point(); + for (int i = 1; i < len; i++) + { + Node newNode = m_conformal_mesh->newNode(n1.point()+(double(i)/double(len))*dir); + ordered_nodes.push_back(newNode); + if (boundOrConstr) + n2rn->set(newNode.id(),n2rn->value(n1.id())); + else + n2rn->set(newNode.id(),-1); + // Update the fan information + fan_id2->set(newNode.id(),fanId); + fan_angle2->set(newNode.id(),fanAngle); + } + ordered_nodes.push_back(m_conformal_mesh->get(n2g->value(n2.id()))); + oe2nn->set(e_id,ordered_nodes); + } + } + // Mark the nodes of the conformal mesh belonging to constraints + for (auto e_id:m_mesh->edges()) + { + if (int_constr->value(e_id) == 1) + { + for (auto n:oe2nn->value(e_id)) + { + constr_nodes->set(n.id(),1); + } + } + } +} + +/*----------------------------------------------------------------------------*/ +void Conformalizer::buildHalfEdges2newNodesConnectivity() +{ + std::cout<<"> Building 'half edge 2 new nodes' connectivity"<getVariable,GMDS_EDGE>("oldEdges2newNodes"); + auto n2g = m_mesh->getVariable("node2group"); + std::vector> he2nn(m_half_edges.size()); + for (auto he:m_half_edges) + { + std::vector ordered_old_nodes = he.getOrderedNodes(); + std::vector ordered_new_nodes; + ordered_new_nodes.push_back(n2g->value(he.firstNode().id())); + int previous_id = n2g->value(he.firstNode().id()); + for (int i = 0; i < ordered_old_nodes.size()-1; i++) + { + Node n1 = ordered_old_nodes[i]; + Node n2 = ordered_old_nodes[i+1]; + Edge e = getEdge(n1,n2); + std::vector nodes = oe2nn->value(e.id()); + if (e.get()[1].id() == n1.id()) + std::reverse(nodes.begin(),nodes.end()); + for (int j = 1; j < nodes.size(); j++) + { + if (previous_id != nodes[j].id()) + { + ordered_new_nodes.push_back(nodes[j].id()); + previous_id = nodes[j].id(); + } + } + } + he2nn[he.id()] = ordered_new_nodes; + } + m_half_edges_to_new_nodes = he2nn; +} + +/*----------------------------------------------------------------------------*/ +void Conformalizer::buildIntermediateMeshNodes() +{ + std::cout<<"> Building intermediate mesh nodes"<nodes()) + m_intermediate_mesh->newNode(m_conformal_mesh->get(n_id).point()); +} + +/*----------------------------------------------------------------------------*/ +void Conformalizer::buildIntermediateMeshEdges() +{ + std::cout<<"> Building intermediate mesh edges"<getVariable("length"); + // Build edges between subdivision nodes + for (auto f_id:m_mesh->faces()) + { + // Get the half edges of the face + NonConformalHalfEdge e1 = m_half_edges[4*f_id]; + NonConformalHalfEdge e2 = m_half_edges[4*f_id+1]; + NonConformalHalfEdge e3 = m_half_edges[4*f_id+2]; + NonConformalHalfEdge e4 = m_half_edges[4*f_id+3]; + std::vector nodes1 = m_half_edges_to_new_nodes[e1.id()]; + std::vector nodes2 = m_half_edges_to_new_nodes[e2.id()]; + std::vector nodes3 = m_half_edges_to_new_nodes[e3.id()]; + std::vector nodes4 = m_half_edges_to_new_nodes[e4.id()]; + int I = nodes1.size(); + int J = nodes2.size(); + for (int i = 1; i < I-1; i++) + { + Edge e = m_intermediate_mesh->newEdge(nodes1[i],nodes3[I-1-i]); + l->set(e.id(),m_half_edges_lengths[e2.id()]); + } + for (int i = 1; i < J-1; i++) + { + Edge e = m_intermediate_mesh->newEdge(nodes2[i],nodes4[J-1-i]); + l->set(e.id(),m_half_edges_lengths[e3.id()]); + } + } + +} + +/*----------------------------------------------------------------------------*/ +void Conformalizer::buildIntermediateNodesGroups() +{ + std::cout<<"> Building intermediate nodes groups"<getVariable("node2group"); + auto l = m_intermediate_mesh->getVariable("length"); + int groupId = 0; + std::vector> groups; + std::vector newGroup; + auto visited = m_intermediate_mesh->newMark(); + for (auto n_id:m_intermediate_mesh->nodes()) + { + if (!m_intermediate_mesh->isMarked(n_id,visited)) + { + // We create a new group + newGroup.clear(); + std::queue toAdd; + Node n = m_intermediate_mesh->get(n_id); + toAdd.push(n); + m_intermediate_mesh->mark(n_id,visited); + while (!toAdd.empty()) + { + n = toAdd.front(); + newGroup.push_back(n.id()); + n2g->set(n.id(),groupId); + toAdd.pop(); + for (auto e:n.get()) + { + if (l->value(e.id()) == 0) + { + for (auto n1:e.get()) + { + if (!m_intermediate_mesh->isMarked(n1.id(),visited)) + { + toAdd.push(n1); + m_intermediate_mesh->mark(n1.id(),visited); + } + } + } + } + } + groups.push_back(newGroup); + groupId += 1; + } + } + m_intermediate_nodes_groups = groups; +} + +/*----------------------------------------------------------------------------*/ +void Conformalizer::buildRepresentativeNodes() +{ + std::cout<<"> Building representative nodes"<getVariable("belong_to_an_internal_constraint"); + auto n2r = m_conformal_mesh->getVariable("node2representative"); + auto n2rn = m_conformal_mesh->getVariable("Boundary&ConstraintNode2ReferenceNode"); + auto fan_id2 = m_conformal_mesh->getVariable("fan_id"); + auto fan_angle2 = m_conformal_mesh->getVariable("fan_angle"); + for (auto group:m_intermediate_nodes_groups) + { + if (group.size() == 1) + n2r->set(group[0],group[0]); + else + { + double x = 0.; + double y = 0.; + bool constrained = false; + for (auto n_id:group) + { + x += m_conformal_mesh->get(n_id).X(); + y += m_conformal_mesh->get(n_id).Y(); + if (constr->value(n_id) == 1) + constrained = true; + } + x = x/double(group.size()); + y = y/double(group.size()); + math::Point P(x,y,0.); + Node n = m_conformal_mesh->newNode(P); + n2r->set(n.id(),n.id()); + for (auto n_id:group) + n2r->set(n_id,n.id()); + if (constrained) + constr->set(n.id(),1); + n2rn->set(n.id(),-1); + // Fan information + fan_id2->set(n.id(),-1); + fan_angle2->set(n.id(),10.); + } + } +} + +/*----------------------------------------------------------------------------*/ +void Conformalizer::builIntNodesAndFaces2newNodesConnectivity() +{ + std::cout<<"> Building nodes of the conformal mesh internal to faces of the non conformal mesh"<getVariable>,GMDS_FACE>("face2newNodes"); + auto n2r = m_conformal_mesh->getVariable("node2representative"); + auto n2rn = m_conformal_mesh->getVariable("Boundary&ConstraintNode2ReferenceNode"); + auto fan_id2 = m_conformal_mesh->getVariable("fan_id"); + auto fan_angle2 = m_conformal_mesh->getVariable("fan_angle"); + for (auto f_id:m_mesh->faces()) + { + // Get the half edges of the face + NonConformalHalfEdge e1 = m_half_edges[4*f_id]; + NonConformalHalfEdge e2 = m_half_edges[4*f_id+1]; + NonConformalHalfEdge e3 = m_half_edges[4*f_id+2]; + NonConformalHalfEdge e4 = m_half_edges[4*f_id+3]; + std::vector> nodesIDs(m_half_edges_lengths[e2.id()]+1); + std::vector row(m_half_edges_lengths[e1.id()]+1); + // First row + for (int i = 0; i < m_half_edges_lengths[e1.id()]+1; i++) + row[i] = n2r->value(m_half_edges_to_new_nodes[e1.id()][i]); + nodesIDs[0] = row; + // Last row + for (int i = 0; i < m_half_edges_lengths[e3.id()]+1; i++) + row[i] = n2r->value(m_half_edges_to_new_nodes[e3.id()][m_half_edges_lengths[e3.id()]-i]); + nodesIDs[m_half_edges_lengths[e2.id()]] = row; + for (int j = 1; j < m_half_edges_lengths[e2.id()]; j++) + { + row[0] = n2r->value(m_half_edges_to_new_nodes[e4.id()][m_half_edges_lengths[e4.id()]-j]); + row[m_half_edges_lengths[e1.id()]] = n2r->value(m_half_edges_to_new_nodes[e2.id()][j]); + for (int i = 1; i < m_half_edges_lengths[e1.id()]; i++) + { + math::Point P1 = m_conformal_mesh->get(n2r->value(m_half_edges_to_new_nodes[e1.id()][i])).point(); + math::Point P2 = m_conformal_mesh->get(n2r->value(m_half_edges_to_new_nodes[e3.id()][m_half_edges_lengths[e3.id()]-i])).point(); + math::Point P = P1 + (double(j)/double(m_half_edges_lengths[e2.id()]))*(P2+(-1.)*P1); + Node n = m_conformal_mesh->newNode(P); + row[i] = n.id(); + n2r->set(n.id(),n.id()); + n2rn->set(n.id(),-1); + // Fan information + fan_id2->set(n.id(),-1); + fan_angle2->set(n.id(),10.); + } + nodesIDs[j] = row; + } + f2nn->set(f_id,nodesIDs); + } +} + +/*----------------------------------------------------------------------------*/ +void Conformalizer::buildConformalMeshFaces() +{ + std::cout<<"> Building faces of the conformal mesh"<getVariable>,GMDS_FACE>("face2newNodes"); + auto tri1 = m_mesh->getVariable("is_a_triangle"); + auto tri2 = m_conformal_mesh->getVariable("is_a_triangle"); + auto fan_id = m_conformal_mesh->getVariable("fan_id"); + for (auto f_id:m_mesh->faces()) + { + std::vector> nodes = f2nn->value(f_id); + int I = nodes.size(); + int J = nodes[0].size(); + TCellID i1,i2,i3,i4; + for (int i = 0; i < I-1; i++) + { + for (int j = 0; j < J-1; j++) + { + i1 = nodes[i][j]; + i2 = nodes[i+1][j]; + i3 = nodes[i+1][j+1]; + i4 = nodes[i][j+1]; + Face new_face = m_conformal_mesh->newFace({i1,i2,i3,i4}); + if (tri1->value(f_id) == 1) + { + if (fan_id->value(i1) >= 0 && fan_id->value(i1) == fan_id->value(i2)) + tri2->set(new_face.id(),1); + else if (fan_id->value(i2) >= 0 && fan_id->value(i2) == fan_id->value(i3)) + tri2->set(new_face.id(),1); + else if (fan_id->value(i3) >= 0 && fan_id->value(i3) == fan_id->value(i4)) + tri2->set(new_face.id(),1); + else if (fan_id->value(i4) >= 0 && fan_id->value(i4) == fan_id->value(i1)) + tri2->set(new_face.id(),1); + } + } + } + } + + // Make sur all faces are positively oriented + std::cout<<"> Orientating the faces positively"<faces()) + { + Face f = m_conformal_mesh->get(f_id); + double oriented_area = f.signedArea(); + if (oriented_area < 0.) + { + std::vector nodes = f.get(); + for (auto node:nodes) + f.remove(node); + std::reverse(nodes.begin(),nodes.end()); + for (auto node:nodes) + f.add(node); + } + } +} + +/*----------------------------------------------------------------------------*/ +void Conformalizer::deleteSuperfluousNodes() +{ + std::cout<<"> Deleting superfluous nodes of the conformal mesh"<getVariable("node2representative"); + for (auto n_id:m_conformal_mesh->nodes()) + { + if (n2r->value(n_id) != n_id) + m_conformal_mesh->deleteNode(n_id); + } +} + +/*----------------------------------------------------------------------------*/ +void Conformalizer::markInternalConstraints() +{ + std::cout<<"> Marking internal constraints on the conformal mesh"<getVariable("internal_constraint"); + auto nodes_constr = m_conformal_mesh->getVariable("belong_to_an_internal_constraint"); + for (auto e_id:m_conformal_mesh->edges()) + { + Edge e = m_conformal_mesh->get(e_id); + Node n1 = e.get()[0]; + Node n2 = e.get()[1]; + if (nodes_constr->value(n1.id()) == 1 && nodes_constr->value(n2.id()) == 1) + int_constr->set(e.id(),1); + else if (nodes_constr->value(n1.id()) == 1 && isInterior(n1) && !isInterior(n2)) + int_constr->set(e.id(),1); + else if (nodes_constr->value(n2.id()) == 1 && isInterior(n2) && !isInterior(n1)) + int_constr->set(e.id(),1); + } +} + +/*----------------------------------------------------------------------------*/ +void Conformalizer::execute() +{ + std::cout<<" "< Projecting on the boundary and the internal constraints"<getVariable("belong_to_an_internal_constraint"); + auto n2rn = m_conformal_mesh->getVariable("Boundary&ConstraintNode2ReferenceNode"); + for (auto n_id:m_conformal_mesh->nodes()) + { + Node n = m_conformal_mesh->get(n_id); + if (!isInterior(n) || nodes_constr->value(n_id) == 1) + { + double min_dist = 1e6; + Node closest; + for (auto boundNode_id:ARefMesh.nodes()) + { + Node boundNode = ARefMesh.get(boundNode_id); + if (boundNode.point().distance(n.point()) < min_dist) + { + min_dist = boundNode.point().distance(n.point()); + closest = boundNode; + } + } + n.setX(closest.X()); + n.setY(closest.Y()); + n2rn->set(n.id(),closest.id()); + } + } +} + +/*----------------------------------------------------------------------------*/ +void Conformalizer::smooth(Mesh &ARefMesh) +{ + std::cout<<"> Smoothing the conformal mesh"<getVariable("belong_to_an_internal_constraint"); + auto int_constr = m_conformal_mesh->getVariable("internal_constraint"); + auto n2rn = m_conformal_mesh->getVariable("Boundary&ConstraintNode2ReferenceNode"); + auto corner = m_conformal_mesh->getVariable("corner"); + auto t_mesh_corner = m_mesh->getVariable("corner"); + for (auto n_id:m_conformal_mesh->nodes()) + { + Node n = m_conformal_mesh->get(n_id); + if (isInterior(n)) + { + if (nodes_constr->value(n.id()) == 0) + { + double x = 0.; + double y = 0.; + double NbNeighbours = 0.; + for (auto e:n.get()) + { + for (auto n1:e.get()) + { + if (n1.id() != n.id()) + { + x += n1.X(); + y += n1.Y(); + NbNeighbours += 1.; + } + } + } + x = x/NbNeighbours; + y = y/NbNeighbours; + n.setX(x); + n.setY(y); + } + else + { + int NbConstraintedEdges = 0; + for (auto e:n.get()) + { + if (int_constr->value(e.id()) == 1) + NbConstraintedEdges += 1; + } + if (NbConstraintedEdges == 2 && corner->value(n_id) == 0) + { + // Find the two constrainted edges + Edge e1,e2; + for (auto e:n.get()) + { + if (int_constr->value(e.id()) == 1) + { + e1 = e; + break; + } + } + for (auto e:n.get()) + { + if (int_constr->value(e.id()) == 1 && e.id() != e1.id()) + { + e2 = e; + break; + } + } + + + double x = 0.; + double y = 0.; + + double NbNeighbours = 0.; + for (auto e:n.get()) + { + for (auto n1:e.get()) + { + if (n1.id() != n.id()) + { + x += n1.X(); + y += n1.Y(); + NbNeighbours += 1.; + } + } + } + x = x/NbNeighbours; + y = y/NbNeighbours; + math::Point P(x,y,0.); + + // Projection on the boundary using the reference mesh + Node n1,n2; + if (e1.get()[0].id() == n.id()) + n1 = e1.get()[1]; + else + n1 = e1.get()[0]; + if (e2.get()[0].id() == n.id()) + n2 = e2.get()[1]; + else + n2 = e2.get()[0]; + + Node boundNode1 = ARefMesh.get(n2rn->value(n1.id())); + Node boundNode2 = ARefMesh.get(n2rn->value(n2.id())); + std::vector arc = shortestPathAlongBoundaryOrConstraints(boundNode1,boundNode2,ARefMesh); + double min_dist = 1e6; + Node closest; + for (auto boundNode:arc) + { + if (boundNode.point().distance(P) < min_dist) + { + min_dist = boundNode.point().distance(P); + closest = boundNode; + } + } + // for (auto boundNode_id:ARefMesh.nodes()) + // { + // Node boundNode = ARefMesh.get(boundNode_id); + // if (boundNode.point().distance(P) < min_dist) + // { + // min_dist = boundNode.point().distance(P); + // closest = boundNode; + // } + // } + n.setX(closest.X()); + n.setY(closest.Y()); + n2rn->set(n.id(),closest.id()); + } + } + } + else + { + if (nodes_constr->value(n.id()) == 0 && corner->value(n.id()) == 0) // Prevent the corners from moving + //if (nodes_constr->value(n.id()) == 0 ) // Authorize the corners to move + { + // Find the two boundary edges containing n + Edge e1,e2; + for (auto e:n.get()) + { + if (e.get().size() == 1) + { + e1 = e; + break; + } + } + for (auto e:n.get()) + { + if (e.get().size() == 1 && e.id() != e1.id()) + { + e2 = e; + break; + } + } + + double x = 0.; + double y = 0.; + + double NbNeighbours = 0.; + for (auto e:n.get()) + { + for (auto n1:e.get()) + { + if (n1.id() != n.id()) + { + x += n1.X(); + y += n1.Y(); + NbNeighbours += 1.; + } + } + } + x = x/NbNeighbours; + y = y/NbNeighbours; + math::Point P(x,y,0.); + + // Projection on the boundary using the reference mesh + Node n1,n2; + if (e1.get()[0].id() == n.id()) + n1 = e1.get()[1]; + else + n1 = e1.get()[0]; + if (e2.get()[0].id() == n.id()) + n2 = e2.get()[1]; + else + n2 = e2.get()[0]; + + + Node boundNode1 = ARefMesh.get(n2rn->value(n1.id())); + Node boundNode2 = ARefMesh.get(n2rn->value(n2.id())); + std::vector arc = shortestPathAlongBoundaryOrConstraints(boundNode1,boundNode2,ARefMesh); + double min_dist = 1e6; + Node closest; + for (auto boundNode:arc) + { + if (boundNode.point().distance(P) < min_dist) + { + min_dist = boundNode.point().distance(P); + closest = boundNode; + } + } + // for (auto boundNode_id:ARefMesh.nodes()) + // { + // Node boundNode = ARefMesh.get(boundNode_id); + // if (boundNode.point().distance(P) < min_dist) + // { + // min_dist = boundNode.point().distance(P); + // closest = boundNode; + // } + // } + n.setX(closest.X()); + n.setY(closest.Y()); + n2rn->set(n.id(),closest.id()); + } + } + } + // // Project a node on every corner + // for (auto n_id:m_mesh->nodes()) + // { + // if (t_mesh_corner->value(n_id) == 1) + // { + // Node cor = m_mesh->get(n_id); + // double min_dist = 1e6; + // Node closest_node1,closest_node2; + // for (auto m_id:m_conformal_mesh->nodes()) + // { + // Node m = m_conformal_mesh->get(m_id); + // if (m.point().distance(cor.point()) < min_dist) + // { + // min_dist = m.point().distance(cor.point()); + // closest_node1 = m; + // } + // } + // closest_node2 = m_conformal_mesh->get(closest_node1.id()); + // closest_node2.setX(cor.X()); + // closest_node2.setY(cor.Y()); + // } + // } +} +/*----------------------------------------------------------------------------*/ +} // end namespace gmds +/*----------------------------------------------------------------------------*/ \ No newline at end of file diff --git a/modules/medialaxis/src/MedaxBasedTMeshBuilder.cpp b/modules/medialaxis/src/MedaxBasedTMeshBuilder.cpp new file mode 100644 index 000000000..24df5da2f --- /dev/null +++ b/modules/medialaxis/src/MedaxBasedTMeshBuilder.cpp @@ -0,0 +1,3687 @@ +/*----------------------------------------------------------------------------*/ +#include "gmds/medialaxis/MedaxBasedTMeshBuilder.h" +#include "gmds/ig/MeshDoctor.h" +#include "gmds/io/IGMeshIOService.h" +#include "gmds/io/VTKWriter.h" +#include "gmds/medialaxis/MedialAxisMath.h" +#include +#include +/*----------------------------------------------------------------------------*/ +namespace gmds { +/*----------------------------------------------------------------------------*/ +namespace medialaxis { +/*----------------------------------------------------------------------------*/ +MedaxBasedTMeshBuilder::MedaxBasedTMeshBuilder(Mesh &AMedax, Mesh &AMinDel){ + // Medial axis + m_medax = &AMedax; + + // Corresponding minimal Delaunay triangulation + m_min_delaunay = &AMinDel; + // min tri nodes/T-mesh nodes correspondence + m_min_delaunay->newVariable("minTriNode2TMeshNode"); + // Mark with 1 boundary or constraint corners + m_min_delaunay->newVariable("corner"); + + // Topological representation + m_topological_representation = new Mesh(MeshModel(DIM3 | F | E | N | + F2N | E2N | N2E | N2F)); + // Type of the medial axis section: 0 if it is a quad edge // to the boundary, 1 if it is a quad diagonal, 2 if it is an edge orthogonal to the boundary + m_topological_representation->newVariable("section_type"); + // Type of the node: 0 if it is internal (there is an equation at this point), 1 if not (there is a border condition at this point) + m_topological_representation->newVariable("node_type"); + // ID of the section to which the point belongs + m_medax->newVariable("section_id"); + // ID of the subsection to which the point belongs + m_medax->newVariable("subsection_id"); + // Type of the subsection to which the point belongs + m_medax->newVariable("medial_section_type"); + // Section/section ID correspondence + m_topological_representation->newVariable("section_to_section_id"); + // Correspondence medial point/node of the topological representation + m_medax->newVariable("med_point_to_sing_node"); + // Correspondence medial point/node of the topological representation + m_topological_representation->newVariable("sing_node_to_med_point"); + // Position of the wings on a section (1 if the wings point in the same direction as the oriented section, -1 if in the opposite direction, 0 if no wing) + m_topological_representation->newVariable("wings_position"); + // Position of each section in the vector of degrees of freedom + m_topological_representation->newVariable("section2matrix"); + // Mark with 1 forbidden singular points + m_medax->newVariable("is_a_forbidden_singular_point"); + + // Correspondence degree of freedom/vertex in the dof graph + m_topological_representation->newVariable("section2LeftQuadLength"); + m_topological_representation->newVariable("section2RightQuadLength"); + m_topological_representation->newVariable("section2QuadHeight"); + m_topological_representation->newVariable("section2LeftDiagoQuadLength"); + m_topological_representation->newVariable("section2RightDiagoQuadLength"); + + // Topology of a medial axis based block decomposition + m_t_mesh = new Mesh(MeshModel(DIM3 | F | E | N | F2E | + F2N | E2F | E2N | N2E | N2F)); + // Mark with 1 edges of the topological representation that will generate a triangle + m_topological_representation->newVariable("generates_a_triangle"); + // Going from topo rep singu node to its corresponding block decomp medial node + m_topological_representation->newVariable("singuNode2MedNode"); + // Going from topo rep singu node to its corresponding block decomp boundary nodes + m_topological_representation->newVariable,GMDS_NODE>("singuNode2BoundNodes"); + // Going from topo rep singu node to its corresponding block decomp middle nodes + m_topological_representation->newVariable,GMDS_NODE>("singuNode2MiddleNode"); + // Directions of the medial section + m_topological_representation->newVariable("tailDirection"); + m_topological_representation->newVariable("headDirection"); + // Correspondence section/block decomposition nodes + m_topological_representation->newVariable("tailNode"); + m_topological_representation->newVariable("headNode"); + m_topological_representation->newVariable("leftTailBoundaryNode"); + m_topological_representation->newVariable("rightTailBoundaryNode"); + m_topological_representation->newVariable("leftHeadBoundaryNode"); + m_topological_representation->newVariable("rightHeadBoundaryNode"); + m_topological_representation->newVariable("leftHeadMiddleNode"); + m_topological_representation->newVariable("rightHeadMiddleNode"); + m_topological_representation->newVariable("leftTailMiddleNode"); + m_topological_representation->newVariable("rightTailMiddleNode"); + m_topological_representation->newVariable("farTailNode"); + m_topological_representation->newVariable("farHeadNode"); + // To have nice pictures + m_topological_representation->newVariable("color"); + // Distance to end points + m_topological_representation->newVariable("distance_to_end_point"); + + // Correspondance face/id of the medial section + m_t_mesh->newVariable("face2sectionID"); + // Mark with 1 edges separating different blocks + m_t_mesh->newVariable("separates_blocks"); + // Correspondance face/type of its corresponding medial section + m_t_mesh->newVariable("face2sectionType"); + // ID of the connected component of the section + m_t_mesh->newVariable("boundary_connected_component_id"); + // T-mesh node/min tri node correspondance + m_t_mesh->newVariable("TMeshNode2MinTriNode"); + // Mark with 1 T-junctions + m_t_mesh->newVariable("is_a_T_junction"); + // Each face's T-junctions + m_t_mesh->newVariable,GMDS_FACE>("T_junctions"); + // Mark with 1 triangle verticies + m_t_mesh->newVariable("is_a_triangle_vertex"); + // Mark with 1 triangles + m_t_mesh->newVariable("is_a_triangle"); + // Mark with 1 nodes belonging to an internal constraint + m_t_mesh->newVariable("belong_to_an_internal_constraint"); + // Mark with 1 boundary or constraint corners + m_t_mesh->newVariable("corner"); + // Id of the triangle fan to which the node belongs + m_t_mesh->newVariable("fan_id"); + // Angle of the triangle fan to which the node belongs + m_t_mesh->newVariable("fan_angle"); + // Mark with 1 nodes of the T-mesh coming from intersection points of the medial axis + m_t_mesh->newVariable("is_an_intersection_point"); + + // Final T-mesh + m_final_t_mesh = new Mesh(MeshModel(DIM3 | F | E | N | F2E | + F2N | E2F | E2N | N2E | N2F)); + // Mark with 1 edges corresponding to internal constraints + m_final_t_mesh->newVariable("internal_constraint"); + // Mark with 1 nodes belonging to an internal constraint + m_final_t_mesh->newVariable("belong_to_an_internal_constraint"); + // Mark with 1 T-junctions + m_final_t_mesh->newVariable("is_a_T-junction"); + // T-mesh node/min tri node correspondance + m_final_t_mesh->newVariable("TMeshNode2MinTriNode"); + // Mark with 1 boundary or constraint corners + m_final_t_mesh->newVariable("corner"); + // Id of the triangle fan to which the node belongs + m_final_t_mesh->newVariable("fan_id"); + // Angle of the triangle fan to which the node belongs + m_final_t_mesh->newVariable("fan_angle"); + // Mark with 1 cells that were triangles + m_final_t_mesh->newVariable("is_a_triangle"); + // Mark with 1 nodes of the T-mesh coming from intersection points of the medial axis + m_final_t_mesh->newVariable("is_an_intersection_point"); +} + +/*----------------------------------------------------------------------------*/ +Node +MedaxBasedTMeshBuilder::getNextPoint(const TCellID &APointID, const TCellID &AEdgeID) +{ + // Requires E2N connections + // To use only if the point APointID belongs to the edge AEdgeID + Edge e = m_medax-> get(AEdgeID); + std::vector adj_nodes = e.get(); + Node n1 = adj_nodes[0]; + Node n2 = adj_nodes[1]; + if (n1.id() == APointID) + return n2; + else + return n1; +} + +/*----------------------------------------------------------------------------*/ +Edge +MedaxBasedTMeshBuilder::getNextEdge(const TCellID &AEdgeID, const TCellID &APointID) +{ + // Requires N2E connections + // To use only if the point APointID belongs to the edge AEdgeID and has type 2 + Node n = m_medax-> get(APointID); + std::vector adj_edges = n.get(); + Edge e1 = adj_edges[0]; + Edge e2 = adj_edges[1]; + if (e1.id() == AEdgeID) + return e2; + else + return e1; +} + +/*----------------------------------------------------------------------------*/ +Node +MedaxBasedTMeshBuilder::getExtremPoint(const TCellID &APointID, const TCellID &AEdgeID) +{ + // Requires medial points type set + // Requires E2E connections + // To use only if the point APointID belongs to the edge AEdgeID + auto var = m_medax->getVariable("medial_point_type"); + Node nxtPoint = getNextPoint(APointID, AEdgeID); + int type = var->value(nxtPoint.id()); + Edge nxtEdge = m_medax->get(AEdgeID); + while (type == 2) + { + nxtEdge = getNextEdge(nxtEdge.id(), nxtPoint.id()); + nxtPoint = getNextPoint(nxtPoint.id(), nxtEdge.id()); + type = var->value(nxtPoint.id()); + } + return nxtPoint; +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::setSectionID() +{ + std::cout<<"> Setting sections IDs"<getVariable("med_point_to_sing_node"); + auto sectionID = m_medax->getVariable("section_id"); + auto subSectionID = m_medax->getVariable("subsection_id"); + auto alreadyVisited = m_medax->newVariable("already_visited"); + auto medPointType = m_medax->getVariable("medial_point_type"); + auto singNode2medPoint = m_topological_representation->getVariable("sing_node_to_med_point"); + auto nodeType = m_topological_representation->getVariable("node_type"); + auto singu = m_medax->getVariable("singularity"); + int ID = 0; + std::vector section_start; // Vector storing a starting point for each section + std::vector section_direction; // Vector storing a starting edge containing the starting point for each section + std::vector> medial_section_extremal_nodes; + std::vector extremal_nodes; + for (auto n_id:m_medax->nodes()) + { + if ((medPointType->value(n_id) == 2) && (singu->value(n_id) == 0) && (alreadyVisited->value(n_id) == 0)) + { + // We build a new section (= new edge of the topo rep) + + // Find the extreme nodes of the section + Node n = m_medax->get(n_id); + alreadyVisited->set(n_id,1); + sectionID->set(n.id(),ID); + Node prev, nxt; + Edge e; + // We go to the left + prev = n; + e = n.get()[0]; + nxt = getNextPoint(prev.id(),e.id()); + while ((medPointType->value(nxt.id()) == 2) && (singu->value(nxt.id()) == 0) && alreadyVisited->value(nxt.id()) == 0) + { + alreadyVisited->set(nxt.id(),1); + sectionID->set(nxt.id(),ID); + e = getNextEdge(e.id(),nxt.id()); + prev = nxt; + nxt = getNextPoint(prev.id(),e.id()); + } + extremal_nodes.push_back(nxt); + section_start.push_back(nxt); + section_direction.push_back(e); + //Node n1 = nxt; + // We go to the right + prev = n; + e = n.get()[1]; + nxt = getNextPoint(prev.id(),e.id()); + while ((medPointType->value(nxt.id()) == 2) && (singu->value(nxt.id()) == 0) && alreadyVisited->value(nxt.id()) == 0) + { + alreadyVisited->set(nxt.id(),1); + sectionID->set(nxt.id(),ID); + e = getNextEdge(e.id(),nxt.id()); + prev = nxt; + nxt = getNextPoint(prev.id(),e.id()); + } + if (nxt.id() != extremal_nodes[0].id()) + extremal_nodes.push_back(nxt); + //Node n2 = nxt; + medial_section_extremal_nodes.push_back(extremal_nodes); + extremal_nodes.clear(); + ID += 1; + } + } + + // // Now we build the subsections, in order to refine the mesh + // int subSecID = 0; + // for (int id = 0; id < ID; id++) + // { + // // Chose one medial point every 10 points on the section to refine it + // Node prev = section_start[id]; + // Edge e = section_direction[id]; + // Node nxt = getNextPoint(prev.id(),e.id()); + // int steps = 1; + // bool Continue = true; + // while (sectionID->value(nxt.id()) == id && Continue) + // { + // e = getNextEdge(e.id(),nxt.id()); + // prev = nxt; + // if (medPointType->value(prev.id()) == 1) + // Continue = false; + // nxt = getNextPoint(prev.id(),e.id()); + // if (steps < 15) + // { + // subSectionID->set(prev.id(),subSecID); + // steps += 1; + // } + // else + // { + // if (sectionID->value(nxt.id()) == id) + // { + // Node newNode = m_topological_representation->newNode(prev.point()); + // medPoint2singNode->set(prev.id(),newNode.id()); + // singNode2medPoint->set(newNode.id(),prev.id()); + // nodeType->set(newNode.id(),2); + // subSecID += 1; + // steps = 0; + // } + // } + + + // } + // } + + m_nb_medial_sections = ID; + m_medial_section_extremal_nodes = medial_section_extremal_nodes; + std::cout<<"NB sections : "<deleteVariable(GMDS_NODE,alreadyVisited); +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::computeSectionType() +{ + std::cout<<"> Computing types of the medial sections"<getVariable("section_id"); + auto cosMedAngle = m_medax->getVariable("cos_medial_angle"); + auto type = m_medax->getVariable("medial_section_type"); + // Compute the highest section ID + int maxID = 0; + for (auto n_id:m_medax->nodes()) + { + if (sectionID->value(n_id) > maxID) + maxID = sectionID->value(n_id); + } + // Type of each medial section + std::vector medial_section_type(maxID+1); + // Vectors storing the number of medial point of each section for each type + std::vector type0(maxID+1); + std::vector type1(maxID+1); + std::vector type2(maxID+1); + for (int i = 0; i <= maxID; i++) + { + type0[i] = 0; + type1[i] = 0; + type2[i] = 0; + } + double epsilon = 0.; + //double epsilon = 1e-1; + for (auto n_id:m_medax->nodes()) + { + int id = sectionID->value(n_id); + if (cosMedAngle->value(n_id) < -sqrt(2.)/2.-epsilon) + type0[id] = type0[id] + 1; + else if (cosMedAngle->value(n_id) < sqrt(2.)/2.-epsilon) + { + type1[id] = type1[id] + 1; + // if (fabs(cosMedAngle->value(n_id)) < sqrt(2.)/2.) + + // else + // type0[id] = type0[id] + 1; + } + else + type2[id] = type2[id] + 1; + } + for (auto n_id:m_medax->nodes()) + { + int id = sectionID->value(n_id); + if (type0[id] > type1[id] && type0[id] > type2[id]) + { + type->set(n_id,0); + medial_section_type[id] = 0; + } + else if (type1[id] > type2[id]) + { + type->set(n_id,1); + medial_section_type[id] = 1; + } + else + { + type->set(n_id,2); + medial_section_type[id] = 2; + } + } + m_medial_section_type = medial_section_type; +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::transformGraySectionsIntoRed() +{ + std::cout<<"> Transforming gray sections into red sections"<getVariable("medial_section_type"); + for (auto n_id:m_medax->nodes()) + { + if (type->value(n_id) == 2) + type->set(n_id,1); + } + for (int i = 0; i < m_medial_section_type.size(); i++) + { + if (m_medial_section_type[i] == 2) + m_medial_section_type[i] = 1; + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::setTopMakerColoring() +{ + std::cout<<"> Performing the TopMaker coloring"<getVariable("medial_point_type"); + auto sectionID = m_medax->getVariable("section_id"); + auto type = m_medax->getVariable("medial_section_type"); + auto iad = m_medax->getVariable("is_a_dangle"); + auto iae = m_medax->getVariable("is_an_extension"); + //auto pbt = m_medax->getVariable("point_branch_type"); + for (int id = 0; id < m_medial_section_type.size(); id++) + { + if (m_medial_section_extremal_nodes[id].size() == 2) + { + Node n1 = m_medial_section_extremal_nodes[id][0]; + Node n2 = m_medial_section_extremal_nodes[id][1]; + if (medPointType->value(n1.id()) == 1 && iae->value(n1.id())) + continue; + if (medPointType->value(n2.id()) == 1 && iae->value(n2.id())) + continue; + if (m_medial_section_type[id] != 2 || (iad->value(n1.id()) != 1 && iad->value(n2.id()) != 1)) + { + if (medPointType->value(n1.id()) == 1 || medPointType->value(n2.id()) == 1) + m_medial_section_type[id] = 1; + else if (medPointType->value(n1.id()) >= 3 && medPointType->value(n2.id()) >= 3) + m_medial_section_type[id] = 0; + else if (medPointType->value(n1.id()) == 2) + { + std::vector extrem_nodes = getBranchExtremPoints(n1); + if (extrem_nodes.size() == 2) + { + Node en1 = extrem_nodes[0]; + Node en2 = extrem_nodes[1]; + if (iae->value(en1.id()) == 1 || iae->value(en2.id()) == 1) + m_medial_section_type[id] = 0; + else if (medPointType->value(en1.id()) == 1 || medPointType->value(en2.id()) == 1) + m_medial_section_type[id] = 1; + else + m_medial_section_type[id] = 0; + } + else + m_medial_section_type[id] = 0; + } + else + { + std::vector extrem_nodes = getBranchExtremPoints(n2); + if (extrem_nodes.size() == 2) + { + Node en1 = extrem_nodes[0]; + Node en2 = extrem_nodes[1]; + if (iae->value(en1.id()) == 1 || iae->value(en2.id()) == 1) + m_medial_section_type[id] = 0; + else if (medPointType->value(en1.id()) == 1 || medPointType->value(en2.id()) == 1) + m_medial_section_type[id] = 1; + else + m_medial_section_type[id] = 0; + } + else + m_medial_section_type[id] = 0; + } + + } + } + else + { + m_medial_section_type[id] = 0; + } + } + for (auto n_id:m_medax->nodes()) + { + int id = sectionID->value(n_id); + if (id >= 0) + type->set(n_id,m_medial_section_type[id]); + } +} + +/*----------------------------------------------------------------------------*/ +std::vector MedaxBasedTMeshBuilder::getBranchExtremPoints(Node AN) +{ + std::vector extrem_nodes; + if (AN.get().size() >= 3) + { + extrem_nodes.push_back(AN); + return extrem_nodes; + } + auto visited = m_medax->newVariable("visited"); + std::queue nodes_to_add; + Node en1,en2; + bool found1 = false; + bool found2 = false; + nodes_to_add.push(AN); + visited->set(AN.id(),1); + while (!nodes_to_add.empty()) + { + Node n1 = nodes_to_add.front(); + nodes_to_add.pop(); + if (n1.get().size() >= 3) + { + if (!found1) + { + found1 = true; + en1 = n1; + } + else + { + found2 = true; + en2 = n1; + break; + } + } + else + { + if (n1.get().size() == 1) + { + if (!found1) + { + found1 = true; + en1 = n1; + } + else + { + found2 = true; + en2 = n1; + break; + } + } + for (auto e:n1.get()) + { + for (auto n2:e.get()) + { + if (visited->value(n2.id()) == 0) + { + visited->set(n2.id(),1); + nodes_to_add.push(n2); + } + } + } + } + } + if (found1) + extrem_nodes.push_back(en1); + if (found2) + extrem_nodes.push_back(en2); + m_medax->deleteVariable(GMDS_NODE,visited); + return extrem_nodes; +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::avoidIPsBecomingEPs() +{ + std::cout<<"> Checking and potentially fixing the coloring so that no intersection point is transformed into an end point"<getVariable("medial_point_type"); + auto sectionID = m_medax->getVariable("section_id"); + auto type = m_medax->getVariable("medial_section_type"); + // Attach to each singular point the list of sections to which its belongs + auto los = m_medax->newVariable,GMDS_NODE>("list_of_sections"); + for (int i = 0; i < m_medial_section_extremal_nodes.size(); i++) + { + for (auto n:m_medial_section_extremal_nodes[i]) + { + std::vector l; + l = los->value(n.id()); + l.push_back(i); + los->set(n.id(),l); + } + } + // Check that no IP becomes an EP + for (auto n_id:m_medax->nodes()) + { + if (medPointType->value(n_id) >= 3) + { + int NbNonGraySection = 0; + std::vector l = los->value(n_id); + for (auto id:l) + { + if (m_medial_section_type[id] != 2) + NbNonGraySection += 1; + } + if (NbNonGraySection <= 1) + { + for (auto id:l) + { + if (m_medial_section_type[id] == 2) + m_medial_section_type[id] = 1; + } + } + } + } + + for (auto n_id:m_medax->nodes()) + { + int id = sectionID->value(n_id); + if (id >= 0) + type->set(n_id,m_medial_section_type[id]); + } + + m_medax->deleteVariable(GMDS_NODE,los); +} + +/*----------------------------------------------------------------------------*/ +std::vector MedaxBasedTMeshBuilder::sectionNodes(int AID) +{ + auto sectionID = m_medax->getVariable("section_id"); + auto medPointType = m_medax->getVariable("medial_point_type"); + auto singu = m_medax->getVariable("singularity"); + auto visited = m_medax->newVariable("visited"); + // Find a node of the section + Node n0; + for (auto n_id:m_medax->nodes()) + { + if ((medPointType->value(n_id) == 2) && (singu->value(n_id) == 0) && (sectionID->value(n_id) == AID)) + { + n0 = m_medax->get(n_id); + break; + } + } + // Build the two halfs of the section + std::vector sec; + Node prev, nxt; + Edge e; + // We go to the left + prev = n0; + e = n0.get()[0]; + nxt = getNextPoint(prev.id(),e.id()); + while ((medPointType->value(nxt.id()) == 2) && (singu->value(nxt.id()) == 0) && visited->value(nxt.id()) == 0) + { + if (visited->value(prev.id()) == 0) + sec.push_back(prev); + visited->set(prev.id(),1); + e = getNextEdge(e.id(),nxt.id()); + prev = nxt; + nxt = getNextPoint(prev.id(),e.id()); + } + if (!sec.empty()) + sec.erase(sec.begin()); + std::reverse(sec.begin(),sec.end()); + // We go to the right + prev = n0; + e = n0.get()[1]; + nxt = getNextPoint(prev.id(),e.id()); + while ((medPointType->value(nxt.id()) == 2) && (singu->value(nxt.id()) == 0) && visited->value(nxt.id()) == 0) + { + if (visited->value(prev.id()) == 0) + sec.push_back(prev); + visited->set(prev.id(),1); + e = getNextEdge(e.id(),nxt.id()); + prev = nxt; + nxt = getNextPoint(prev.id(),e.id()); + } + m_medax->deleteVariable(GMDS_NODE,visited); + return sec; +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::refineByAddingSingularNodes() +{ + std::cout<<"> Refining the future block decomposition by adding singular nodes"<getVariable("section_id"); + auto singu = m_medax->getVariable("singularity"); + // Compute the highest section ID + int maxID = 0; + for (auto n_id:m_medax->nodes()) + { + if (sectionID->value(n_id) > maxID) + maxID = sectionID->value(n_id); + } + std::vector section; + for (int id = 0; id <= maxID; id++) + { + section = sectionNodes(id); + int Nb = section.size(); + int q = Nb/30; + for (int i = 1; i < q; i++) + singu->set(section[30*i].id(),10); + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::refineByAddingSingularNodes(double AMeshSize) +{ + std::cout<<"> Refining the future block decomposition by adding singular nodes"<getVariable("section_id"); + auto singu = m_medax->getVariable("singularity"); + // Compute the highest section ID + int maxID = 0; + for (auto n_id:m_medax->nodes()) + { + if (sectionID->value(n_id) > maxID) + maxID = sectionID->value(n_id); + } + std::vector section; + double length,scale; + int count; + for (int id = 0; id <= maxID; id++) + { + if (m_is_refinable[id] == 0) + continue; + + if (m_medial_section_type[id] == 1) + scale = sqrt(2.); + else + scale = 1.; + + section = sectionNodes(id); + length = 0.; + count = 0; + + for (int i = 1; i < section.size() ; i++) + { + length += section[i-1].point().distance(section[i].point()); + count += 1; + if (count >= 10 && length >= scale*AMeshSize && i < section.size() - 5) + { + length = 0.; + count = 0.; + singu->set(section[i].id(),10); + } + } + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::buildTopoRepNodes() +{ + std::cout<<"> Building nodes of the topological representation"< WARNING: if the medial axis has two neighbouring singular points (for example a singu and an IP), the topo rep is not valid"<getVariable("singularity"); + auto medPointType = m_medax->getVariable("medial_point_type"); + auto medPoint2singNode = m_medax->getVariable("med_point_to_sing_node"); + auto singNode2medPoint = m_topological_representation->getVariable("sing_node_to_med_point"); + auto nodeType = m_topological_representation->getVariable("node_type"); + auto touchingPoints = m_medax->getVariable,GMDS_NODE>("touching_points"); + auto forbidden = m_medax->getVariable("is_a_forbidden_singular_point"); + + for (auto n_id:m_medax->nodes()) + { + if (forbidden->value(n_id) == 1) + { + medPoint2singNode->set(n_id,-1); + continue; + } + if (medPointType->value(n_id) == 2) + { + if (singu->value(n_id) != 0) + { + Node n = m_medax->get(n_id); + Node newNode = m_topological_representation->newNode(n.point()); + medPoint2singNode->set(n_id,newNode.id()); + singNode2medPoint->set(newNode.id(),n.id()); + nodeType->set(newNode.id(),2); + } + else + medPoint2singNode->set(n_id,-1); + } + else + { + if (medPointType->value(n_id) == 1) + { + Node n = m_medax->get(n_id); + Node newNode = m_topological_representation->newNode(n.point()); + //Node newNode = m_topological_representation->newNode(touchingPoints->value(n_id)[0]); + medPoint2singNode->set(n_id,newNode.id()); + singNode2medPoint->set(newNode.id(),n_id); + nodeType->set(newNode.id(),medPointType->value(n_id)); + } + else + { + Node n = m_medax->get(n_id); + Node newNode = m_topological_representation->newNode(n.point()); + medPoint2singNode->set(n.id(),newNode.id()); + singNode2medPoint->set(newNode.id(),n.id()); + nodeType->set(newNode.id(),medPointType->value(n_id)); + } + } + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::buildTopoRepEdges() +{ + std::cout<<"> Building edges of the topological representation"< WARNING: if the medial axis has only one section which is a cycle, the topo rep is not valid"<getVariable("medial_point_type"); + auto cosMedAngle = m_medax->getVariable("cos_medial_angle"); + auto sectionType = m_topological_representation->getVariable("section_type"); + auto secTypeOnNodes = m_medax->getVariable("medial_section_type"); + auto medPoint2singNode = m_medax->getVariable("med_point_to_sing_node"); + auto wings = m_topological_representation->getVariable("wings_position"); + auto touchingPoints = m_medax->getVariable,GMDS_NODE>("touching_points"); + auto section2sectionID = m_topological_representation->getVariable("section_to_section_id"); + auto sectionID = m_medax->getVariable("section_id"); + //auto subSectionID = m_medax->getVariable("subsection_id"); + auto tailDir = m_topological_representation->getVariable("tailDirection"); + auto headDir = m_topological_representation->getVariable("headDirection"); + auto forbidden = m_medax->getVariable("is_a_forbidden_singular_point"); + for (auto n_id:m_medax->nodes()) + { + if (medPoint2singNode->value(n_id) == -1 && medPointType->value(n_id) == 2) + { + // We build a new section (= new edge of the topo rep) + // Type of the section + int type = secTypeOnNodes->value(n_id); + if (type == 2) + type = 1; + // if (cosMedAngle->value(n_id) < -sqrt(2.)/2.) + // type = 0; + // else + // { + // if (fabs(cosMedAngle->value(n_id)) < sqrt(2.)/2.) + // type = 1; + // else + // type = 0; + // } + + //int sectID = subSectionID->value(n_id); + int sectID = sectionID->value(n_id); + + // Find the extreme nodes of the section + Node n = m_medax->get(n_id); + medPoint2singNode->set(n_id,-2); + Node prev, nxt; + Edge e; + // We go to the left + prev = n; + e = n.get()[0]; + nxt = getNextPoint(prev.id(),e.id()); + while (medPoint2singNode->value(nxt.id()) < 0 && forbidden->value(nxt.id()) == 0) + { + medPoint2singNode->set(nxt.id(),-2); + e = getNextEdge(e.id(),nxt.id()); + prev = nxt; + nxt = getNextPoint(prev.id(),e.id()); + } + // Tail direction of the section + math::Vector tail_dir = edge2vec(e,nxt); + Node n1 = nxt; + // We go to the right + prev = n; + e = n.get()[1]; + nxt = getNextPoint(prev.id(),e.id()); + while (medPoint2singNode->value(nxt.id()) < 0 && forbidden->value(nxt.id()) == 0) + { + medPoint2singNode->set(nxt.id(),-2); + e = getNextEdge(e.id(),nxt.id()); + prev = nxt; + nxt = getNextPoint(prev.id(),e.id()); + } + // Head direction of the section + math::Vector head_dir = edge2vec(e,prev); + Node n2 = nxt; + + if (forbidden->value(n1.id()) == 1 || forbidden->value(n2.id()) == 1) + continue; + + TCellID N1 = medPoint2singNode->value(n1.id()); + TCellID N2 = medPoint2singNode->value(n2.id()); + Edge newSection = m_topological_representation->newEdge(N1,N2); + // Directions of the section + tailDir->set(newSection.id(),tail_dir); + headDir->set(newSection.id(),head_dir); + // All end sections must be of type 1 + if (medPointType->value(n1.id()) == 1 || medPointType->value(n2.id()) == 1) + { + if (type == 0) + { + type = 1; + sectID = m_nb_medial_sections; + m_nb_medial_sections = m_nb_medial_sections + 1; + } + } + sectionType->set(newSection.id(),type); + section2sectionID->set(newSection.id(),sectID); + // Wings position + Node P1 = newSection.get()[0]; + math::Point S = getNextPoint(n_id,n.get()[1].id()).point() + (-1.)*n.point(); + math::Point R = touchingPoints->value(n_id)[0] + (-1.)*n.point(); + double orient = vec(S).dot(vec(R)); + if (type == 1) + { + if (orient >= 0) + wings->set(newSection.id(),-1); + else + wings->set(newSection.id(),1); + } + } + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::markRefinableSections() +{ + std::cout<<"> Marking refinable medial sections"<getVariable("medial_point_type"); + std::vector is_refinable(m_medial_section_type.size()); + for (int id = 0 ; id < m_medial_section_type.size() ; id++) + { + if (m_medial_section_type[id] != 2) + { + is_refinable[id] = 1; + continue; + } + Node n1 = m_medial_section_extremal_nodes[id][0]; + Node n2 = m_medial_section_extremal_nodes[id][1]; + if (medPointType->value(n1.id()) != 1 && medPointType->value(n2.id()) != 1) + { + is_refinable[id] = 1; + continue; + } + is_refinable[id] = 0; + } + m_is_refinable = is_refinable; +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::markForbiddenSingularPointsAndModifyBoundaryPoints() +{ + std::cout<<"> Marking forbidden singular points and modifying boundary points"<getVariable("is_a_forbidden_singular_point"); + auto medPointType = m_medax->getVariable("medial_point_type"); + auto boundaryTangentNodes = m_medax->getVariable,GMDS_NODE>("tangent_boundary_nodes"); + auto touchingPoints = m_medax->getVariable,GMDS_NODE>("touching_points"); + for (int id = 0 ; id < m_medial_section_type.size() ; id++) + { + if (m_medial_section_type[id] != 2) + { + continue; + } + Node n1 = m_medial_section_extremal_nodes[id][0]; + Node n2 = m_medial_section_extremal_nodes[id][1]; + Node ep,nm; + bool Continue = false; + if (medPointType->value(n1.id()) == 1) + { + forbidden->set(n1.id(),1); + ep = n1; + nm = n2; + Continue = true; + } + if (medPointType->value(n2.id()) == 1) + { + forbidden->set(n2.id(),1); + ep = n2; + nm = n1; + Continue = true; + } + if (!Continue) + continue; + // Modify the boundary points + std::vector old_bound_points = boundaryTangentNodes->value(nm.id()); + std::vector new_bound_points; + // Find the two boundary points closest to ep + double min_dist = 1e6; + for (auto n:old_bound_points) + { + if (n.point().distance(ep.point()) < min_dist) + { + n1 = n; + min_dist = n.point().distance(ep.point()); + } + } + min_dist = 1e6; + for (auto n:old_bound_points) + { + if (n.id() != n1.id() && n.point().distance(ep.point()) < min_dist) + { + n2 = n; + min_dist = n.point().distance(ep.point()); + } + } + bool added = false; + for (auto n:old_bound_points) + { + if (n.id() != n1.id() && n.id() != n2.id()) + { + new_bound_points.push_back(n); + } + else if (!added) + { + new_bound_points.push_back(boundaryTangentNodes->value(ep.id())[0]); + added = true; + } + } + boundaryTangentNodes->set(nm.id(),new_bound_points); + std::vector tp; + for (auto n:new_bound_points) + { + math::Point P = n.point(); + tp.push_back(P); + } + touchingPoints->set(nm.id(),tp); + } +} + +/*----------------------------------------------------------------------------*/ +void +MedaxBasedTMeshBuilder::optimizeMedaxColoring() +{ + std::cout<<"> Optimizing the coloring of the medial axis"<getVariable("section_type"); + for (auto n_id:m_topological_representation->nodes()) + { + Node n = m_topological_representation->get(n_id); + if (n.get().size() != 2) + continue; + Edge e1 = n.get()[0]; + Edge e2 = n.get()[1]; + if (sectionType->value(e1.id()) == 0 && sectionType->value(e2.id()) == 1) + { + Node n2 = getNextSingularNode(n,e2); + if (n2.get().size() == 3) + sectionType->set(e2.id(),0); + } + if (sectionType->value(e1.id()) == 1 && sectionType->value(e2.id()) == 0) + { + Node n2 = getNextSingularNode(n,e1); + if (n2.get().size() == 3) + sectionType->set(e1.id(),0); + } + } +} + +/*----------------------------------------------------------------------------*/ +void +MedaxBasedTMeshBuilder::setTopoRepEdgesColor() +{ + std::cout<<"> Setting color of the discretized medial axis"<getVariable("color"); + auto type = m_topological_representation->getVariable("section_type"); + auto FTN = m_topological_representation->getVariable("farTailNode"); + auto FHN = m_topological_representation->getVariable("farHeadNode"); + for (auto e_id:m_topological_representation->edges()) + { + Edge e = m_topological_representation->get(e_id); + bool end = false; + bool dangle = false; + for (auto n:e.get()) + { + if (n.get().size() == 1) + { + end = true; + } + if (FTN->value(e.id()) >=0 || FHN->value(e.id()) >=0) + { + dangle = true; + } + } + if (dangle) + color->set(e_id,3); + else if (end) + color->set(e_id,2); + else + color->set(e_id,type->value(e_id)); + } +} + +/*----------------------------------------------------------------------------*/ +void +MedaxBasedTMeshBuilder::writeTopoRep(std::basic_string AFileName) +{ + std::cout<<"> Writing the topological representation"<getVariable("wings_position"); + if (orientation(AN,AE) == wings->value(AE.id())) + return fabs(orientation(AN,AE)); + else + return 0; +} + +/*----------------------------------------------------------------------------*/ +int MedaxBasedTMeshBuilder::orientation(gmds::Node &AN, gmds::Edge &AE) +{ + if (AN.id() == AE.get()[0].id()) + return 1; + if (AN.id() == AE.get()[1].id()) + return -1; + return 0; +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::setTopoRepConnectivity() +{ + std::cout<<"> Setting topological representation connectivity"< Computing the distance to the end point for topological representation points"<getVariable("distance_to_end_point"); + for (auto n_id:m_topological_representation->nodes()) + dist->set(n_id,-1); + for (auto n_id:m_topological_representation->nodes()) + { + Node n = m_topological_representation->get(n_id); + if (n.get().size() == 1) + { + // We go upstream on the branch + Edge e = n.get()[0]; + Node prev = n; + Node nxt; + int d = 0; + dist->set(prev.id(),d); + d += 1; + if (e.get()[0].id() == n.id()) + nxt = e.get()[1]; + else + nxt = e.get()[0]; + while (nxt.get().size() == 2 && nxt.id() != n.id()) + { + prev = nxt; + dist->set(prev.id(),d); + d += 1; + for (auto edge:nxt.get()) + { + if (edge.id() != e.id()) + { + e = edge; + break; + } + } + if (e.get()[0].id() == prev.id()) + nxt = e.get()[1]; + else + nxt = e.get()[0]; + } + } + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::ensureType1ForTopoRepEndSections() +{ + auto type = m_topological_representation->getVariable("section_type"); + auto iae = m_medax->getVariable("is_an_extension"); + auto sn2mp = m_topological_representation->getVariable("sing_node_to_med_point"); + auto cos_med_angle = m_medax->getVariable("cos_medial_angle"); + auto wings_pos = m_topological_representation->getVariable("wings_position"); + auto dist = m_topological_representation->getVariable("distance_to_end_point"); + auto medPointType = m_medax->getVariable("medial_point_type"); + for (auto s_id:m_topological_representation->edges()) + { + Edge S = m_topological_representation->get(s_id); + bool is_extremal = false; + for (auto n:S.get()) + { + if (n.get().size() == 1) + { + is_extremal = true; + break; + } + } + if (is_extremal) + { + type->set(s_id,1); + int n1 = sn2mp->value(S.get()[0].id()); + int n2 = sn2mp->value(S.get()[1].id()); + if (iae->value(n1) == 1 || iae->value(n2) == 1 || (medPointType->value(n1) != 1 && medPointType->value(n1) != 1)) + { + int d1 = dist->value(S.get()[0].id()); + int d2 = dist->value(S.get()[1].id()); + if (d1 == -1) + wings_pos->set(s_id,-1); + else if (d2 == -1) + wings_pos->set(s_id,1); + else if (d1 > d2) + wings_pos->set(s_id,-1); + else + wings_pos->set(s_id,1); + } + } + else + { + int n1 = sn2mp->value(S.get()[0].id()); + int n2 = sn2mp->value(S.get()[1].id()); + if (iae->value(n1) == 1 && iae->value(n2) == 1) + { + if (cos_med_angle->value(n1) > -sqrt(2.)/2. && cos_med_angle->value(n2) > -sqrt(2.)/2.) + { + type->set(s_id,1); + } + } + + if (type->value(s_id) == 1) + { + int d1 = dist->value(S.get()[0].id()); + int d2 = dist->value(S.get()[1].id()); + if (d1 == -1) + wings_pos->set(s_id,-1); + else if (d2 == -1) + wings_pos->set(s_id,1); + else if (d1 > d2) + wings_pos->set(s_id,-1); + else + wings_pos->set(s_id,1); + } + } + } +} + +/*----------------------------------------------------------------------------*/ +Node MedaxBasedTMeshBuilder::getNextSingularNode(gmds::Node &AN, gmds::Edge &AE) +{ + if (AE.get()[0].id() == AN.id()) + return AE.get()[1]; + if (AE.get()[1].id() == AN.id()) + return AE.get()[0]; + std::cout<<"getNextSingularNode : error, the given Node does not belong to the given edge"<().size() != 2) + { + std::cout<<"getNextMedialSection : error, the given node doesn't have valence 2"<()[0].id() == AE.id()) + return AN.get()[1]; + if (AN.get()[1].id() == AE.id()) + return AN.get()[0]; + std::cout<<"getNextMedialSection : error, the given Node does not belong to the given edge"<get(0); + Node prev_node; + Edge current_edge = current_node.get()[0]; + TCellID initial_edge_id = current_edge.id(); + while (true) + { + std::cout<<"We are currently at node "<().size() == 2) + current_edge = getNextMedialSection(current_edge,current_node); + if (current_node.get().size() >= 3) + { + Edge nxt_edge; + double theta = 10.; + for (auto e:current_node.get()) + { + if (e.id() != current_edge.id()) + { + if (oriented_angle(edge2vec(current_edge,prev_node), edge2vec(e,current_node)) < theta) + { + nxt_edge = e; + theta = oriented_angle(edge2vec(current_edge,prev_node), edge2vec(e,current_node)); + } + } + } + current_edge = nxt_edge; + } + if (current_node.id() == 0 && current_edge.id() == initial_edge_id) + break; + } +} + +/*----------------------------------------------------------------------------*/ +std::vector MedaxBasedTMeshBuilder::shortestPathAlongBoundaryOrConstraints(Node &AN1, Node &AN2) +{ + auto constr = m_min_delaunay->getVariable("internal_constraint"); + auto visited = m_min_delaunay->newVariable("visited"); + std::vector path; + std::vector new_path; + std::vector shortestPath; + path.push_back(AN1); + visited->set(AN1.id(),1); + std::queue> toBeContinued; + toBeContinued.push(path); + bool finished = false; + while (!toBeContinued.empty() && !finished) + { + path = toBeContinued.front(); + toBeContinued.pop(); + Node last_node = path[path.size()-1]; + if (last_node.id() == AN2.id()) + { + shortestPath = path; + finished = true; + } + else + { + for (auto e:last_node.get()) + { + if (e.get().size() == 1 || constr->value(e.id()) == 1) + { + for (auto n:e.get()) + { + if (visited->value(n.id()) == 0) + { + new_path = path; + new_path.push_back(n); + visited->set(n.id(),1); + toBeContinued.push(new_path); + } + } + } + } + } + } + m_min_delaunay->deleteVariable(GMDS_NODE,visited); + return shortestPath; +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::markEdgesGeneratingTriangles() +{ + auto gt = m_topological_representation->getVariable("generates_a_triangle"); + auto boundaryTangentNodes = m_medax->getVariable,GMDS_NODE>("tangent_boundary_nodes"); + auto singNode2medPoint = m_topological_representation->getVariable("sing_node_to_med_point"); + for (auto e_id:m_topological_representation->edges()) + { + Edge e = m_topological_representation->get(e_id); + Node n1 = e.get()[0]; + Node n2 = e.get()[1]; + std::vector tbn1 = boundaryTangentNodes->value(singNode2medPoint->value(n1.id())); + std::vector tbn2 = boundaryTangentNodes->value(singNode2medPoint->value(n2.id())); + bool tri = false; + for (auto n:tbn1) + { + for (auto m:tbn2) + { + if (n.id() == m.id()) + tri = true; + } + } + if (tri) + gt->set(e_id,1); + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::markCornersOnMinDel() +{ + auto c = m_min_delaunay->getVariable("corner"); + auto constr = m_min_delaunay->getVariable("internal_constraint"); + auto del_nodes_constr = m_min_delaunay->getVariable("belong_to_an_internal_constraint"); + for (auto n_id:m_min_delaunay->nodes()) + { + Node n = m_min_delaunay->get(n_id); + if (del_nodes_constr->value(n_id) == 1) + { + if (!isInterior(n)) + { + c->set(n_id,1); + } + else + { + int nbConstrEdges = 0; + for (auto e:n.get()) + { + if (constr->value(e.id()) == 1) + nbConstrEdges += 1; + } + if (nbConstrEdges != 2) + c->set(n_id,1); + else + { + // Find the two constrainted edges containing n + Edge e1, e2; + for (auto e:n.get()) + { + if (constr->value(e.id()) == 1) + { + e1 = e; + break; + } + } + for (auto e:n.get()) + { + if (constr->value(e.id()) == 1 && e.id() != e1.id()) + { + e2 = e; + break; + } + } + double alpha = oriented_angle(edge2vec(e1,n),-edge2vec(e2,n)); + if (fabs(alpha) > 0.05) + c->set(n_id,1); + } + } + } + if (!isInterior(n)) + { + // Find the two boundary edges containing n + Edge e1, e2; + for (auto e:n.get()) + { + if (e.get().size() == 1) + { + e1 = e; + break; + } + } + for (auto e:n.get()) + { + if (e.get().size() == 1 && e.id() != e1.id()) + { + e2 = e; + break; + } + } + double alpha = oriented_angle(edge2vec(e1,n),-edge2vec(e2,n)); + if (fabs(alpha) > 0.05) + c->set(n_id,1); + } + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::buildTMeshNodesFromMinDelNodes() +{ + std::cout<<"> Building medial and boundary nodes of the medial axis based T-mesh"<getVariable("sing_node_to_med_point"); + auto boundaryTangentNodes = m_medax->getVariable,GMDS_NODE>("tangent_boundary_nodes"); + auto minTriNode2TMeshNode = m_min_delaunay->getVariable("minTriNode2TMeshNode"); + auto TMeshNode2MinTriNode = m_t_mesh->getVariable("TMeshNode2MinTriNode"); + auto sn2mn = m_topological_representation->getVariable("singuNode2MedNode"); + auto nodeType = m_topological_representation->getVariable("node_type"); + auto sn2bn = m_topological_representation->getVariable,GMDS_NODE>("singuNode2BoundNodes"); + auto del_nodes_constr = m_min_delaunay->getVariable("belong_to_an_internal_constraint"); + auto tmesh_nodes_constr = m_t_mesh->getVariable("belong_to_an_internal_constraint"); + auto c = m_min_delaunay->getVariable("corner"); + auto ctm = m_t_mesh->getVariable("corner"); + auto ip = m_t_mesh->getVariable("is_an_intersection_point"); + // Mark nodes of the minimal triangulation appearing in the T-mesh + auto appears = m_min_delaunay->newMark(); + for (auto n_id:m_topological_representation->nodes()) + { + + int med_point = singNode2medPoint->value(n_id); + std::vector tangent_nodes = boundaryTangentNodes->value(med_point); + for (auto n:tangent_nodes) + m_min_delaunay->mark(n.id(),appears); + + } + // Add these nodes to the T-mesh + for (auto n_id:m_min_delaunay->nodes()) + { + if (m_min_delaunay->isMarked(n_id,appears)) + { + Node n = m_min_delaunay->get(n_id); + Node new_node = m_t_mesh->newNode(n.point()); + minTriNode2TMeshNode->set(n_id,new_node.id()); + TMeshNode2MinTriNode->set(new_node.id(),n_id); + if (del_nodes_constr->value(n.id()) == 1) + tmesh_nodes_constr->set(new_node.id(),1); + //ctm->set(new_node.id(),c->value(n_id)); + } + else + { + minTriNode2TMeshNode->set(n_id,-1); + } + } + // Mark the corners + for (auto n_id:m_min_delaunay->nodes()) + { + if (c->value(n_id) == 1) + { + Node n = m_min_delaunay->get(n_id); + double min_dist = 1e6; + TCellID closest_node_id; + for (auto m_id:m_t_mesh->nodes()) + { + Node m = m_t_mesh->get(m_id); + if (m.point().distance(n.point()) < min_dist) + { + min_dist = m.point().distance(n.point()); + closest_node_id = m_id; + } + } + ctm->set(closest_node_id,1); + } + } + + // Add medial nodes to the T-mesh + for (auto n_id:m_topological_representation->nodes()) + { + if (nodeType->value(n_id) >= 2) + { + // Build medial node + Node singu_node = m_topological_representation->get(n_id); + Node newNode; + newNode = m_t_mesh->newNode(singu_node.point()); + sn2mn->set(n_id,newNode.id()); + if (singu_node.get().size() >= 3) + ip->set(newNode.id(),1); + // Update sn2bn + std::vector bound_nodes; + int med_point = singNode2medPoint->value(n_id); + std::vector tangent_nodes = boundaryTangentNodes->value(med_point); + for (auto n:tangent_nodes) + { + bound_nodes.push_back(minTriNode2TMeshNode->value(n.id())); + } + sn2bn->set(n_id,bound_nodes); + } + if (nodeType->value(n_id) == 1) + { + sn2mn->set(n_id,minTriNode2TMeshNode->value(boundaryTangentNodes->value(singNode2medPoint->value(n_id))[0].id())); + } + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::buildBlockDecompMedialAndBoundaryNodes() +{ + std::cout<<"> Building medial and boundary nodes of the medial axis based block decomposition"<getVariable("node_type"); + auto singNode2medPoint = m_topological_representation->getVariable("sing_node_to_med_point"); + auto touchingPoints = m_medax->getVariable,GMDS_NODE>("touching_points"); + auto boundaryTangentNodes = m_medax->getVariable,GMDS_NODE>("tangent_boundary_nodes"); + auto sn2mn = m_topological_representation->getVariable("singuNode2MedNode"); + auto sn2bn = m_topological_representation->getVariable,GMDS_NODE>("singuNode2BoundNodes"); + auto dualTriangles = m_medax->getVariable,GMDS_NODE>("dual_triangles"); + auto ip = m_t_mesh->getVariable("is_an_intersection_point"); + for (auto n_id:m_topological_representation->nodes()) + { + // // Build medial node + // Node singu_node = m_topological_representation->get(n_id); + // Node newNode; + // newNode = m_t_mesh->newNode(singu_node.point()); + // sn2mn->set(n_id,newNode.id()); + // if (nodeType->value(n_id) == 1) + // continue; + // // Build boundary nodes + // Node med_point = m_medax->get(singNode2medPoint->value(n_id)); + // std::vector newNodes; + // std::vector boundaryTangencyPoints = touchingPoints->value(med_point.id()); + + // if (nodeType->value(n_id) >= 2) + // { + // for (auto p:boundaryTangencyPoints) + // { + // newNode = m_t_mesh->newNode(p); + // newNodes.push_back(newNode.id()); + // } + // } + // sn2bn->set(n_id,newNodes); + + // Build medial node + Node singu_node = m_topological_representation->get(n_id); + Node newNode; + newNode = m_t_mesh->newNode(singu_node.point()); + if (singu_node.get().size() >= 3) + ip->set(newNode.id(),1); + sn2mn->set(n_id,newNode.id()); + if (nodeType->value(n_id) == 1) + continue; + // Build boundary nodes + Node med_point = m_medax->get(singNode2medPoint->value(n_id)); + std::vector newNodes; + std::vector tangentNodes = boundaryTangentNodes->value(med_point.id()); + + if (nodeType->value(n_id) >= 2) + { + for (auto p:tangentNodes) + { + newNode = m_t_mesh->newNode(p.point()); + newNodes.push_back(newNode.id()); + } + } + sn2bn->set(n_id,newNodes); + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::writeBlockDecomp(std::basic_string AFileName) +{ + std::cout<<"> Writing the medial axis based block decomposition"<getVariable("tailNode"); + auto HN = m_topological_representation->getVariable("headNode"); + auto LTBN = m_topological_representation->getVariable("leftTailBoundaryNode"); + auto RTBN = m_topological_representation->getVariable("rightTailBoundaryNode"); + auto LHBN = m_topological_representation->getVariable("leftHeadBoundaryNode"); + auto RHBN = m_topological_representation->getVariable("rightHeadBoundaryNode"); + auto sn2mn = m_topological_representation->getVariable("singuNode2MedNode"); + auto sn2bn = m_topological_representation->getVariable,GMDS_NODE>("singuNode2BoundNodes"); + auto nodeType = m_topological_representation->getVariable("node_type"); + auto tailDir = m_topological_representation->getVariable("tailDirection"); + auto headDir = m_topological_representation->getVariable("headDirection"); + auto FTN = m_topological_representation->getVariable("farTailNode"); + auto FHN = m_topological_representation->getVariable("farHeadNode"); + auto boundaryTangentNodes = m_medax->getVariable,GMDS_NODE>("tangent_boundary_nodes"); + auto singNode2medPoint = m_topological_representation->getVariable("sing_node_to_med_point"); + auto minTriNode2TMeshNode = m_min_delaunay->getVariable("minTriNode2TMeshNode"); + + for (auto n_id:m_topological_representation->nodes()) + { + Node n = m_topological_representation->get(n_id); + int medial_node = sn2mn->value(n_id); + std::vector boundary_nodes = sn2bn->value(n_id); + for (auto section:n.get()) + { + if (orientation(n,section) == 1) + { + TN->set(section.id(),medial_node); + if (nodeType->value(n_id) == 1) + { + LTBN->set(section.id(),-1); + RTBN->set(section.id(),-1); + FTN->set(section.id(),-1); + FHN->set(section.id(),-1); + } + if (nodeType->value(n_id) > 1) + { + if (boundaryTangentNodes->value(singNode2medPoint->value(n_id)).size() == 1) + { + int far_node_id = minTriNode2TMeshNode->value(boundaryTangentNodes->value(singNode2medPoint->value(n_id))[0].id()); + FTN->set(section.id(),far_node_id); + } + else + FTN->set(section.id(),-1); + // Find the left and right boundary points + double max_theta = -10.; + double min_theta = 10.; + int left_node; + int right_node; + bool left_found = false; + bool right_found = false; + for (auto ind:boundary_nodes) + { + Node boundary_node = m_t_mesh->get(ind); + math::Point R = boundary_node.point()+(-1.)*n.point(); + //double theta = oriented_angle(edge2vec(section,n),vec(R)); + //double theta = oriented_angle(tailDir->value(section.id()),vec(R)); + double theta = oriented_angle(edge2vec(section,n),vec(R)); + if (theta < 0. && theta > max_theta) + { + max_theta = theta; + right_node = ind; + right_found = true; + } + if (theta > 0. && theta < min_theta) + { + min_theta = theta; + left_node = ind; + left_found = true; + } + } + if (!left_found) + { + for (auto ind:boundary_nodes) + { + if (ind != right_node) + { + left_node = ind; + break; + } + } + } + if (!right_found) + { + for (auto ind:boundary_nodes) + { + if (ind != left_node) + { + right_node = ind; + break; + } + } + } + LTBN->set(section.id(),left_node); + RTBN->set(section.id(),right_node); + } + } + + else + { + HN->set(section.id(),medial_node); + if (nodeType->value(n_id) == 1) + { + LHBN->set(section.id(),-1); + RHBN->set(section.id(),-1); + FTN->set(section.id(),-1); + FHN->set(section.id(),-1); + } + if (nodeType->value(n_id) > 1) + { + if (boundaryTangentNodes->value(singNode2medPoint->value(n_id)).size() == 1) + { + int far_node_id = minTriNode2TMeshNode->value(boundaryTangentNodes->value(singNode2medPoint->value(n_id))[0].id()); + FHN->set(section.id(),far_node_id); + } + else + FHN->set(section.id(),-1); + // Find the left and right boundary points + double max_theta = -10.; + double min_theta = 10.; + int left_node; + int right_node; + bool left_found = false; + bool right_found = false; + for (auto ind:boundary_nodes) + { + Node boundary_node = m_t_mesh->get(ind); + math::Point R = boundary_node.point()+(-1.)*n.point(); + //double theta = oriented_angle(edge2vec(section,n),vec(R)); + // double theta = oriented_angle(-headDir->value(section.id()),vec(R)); + double theta = oriented_angle(edge2vec(section,n),vec(R)); + if (theta < 0. && theta > max_theta) + { + max_theta = theta; + left_node = ind; + left_found = true; + } + if (theta > 0. && theta < min_theta) + { + min_theta = theta; + right_node = ind; + right_found = true; + } + } + if (!left_found) + { + for (auto ind:boundary_nodes) + { + if (ind != right_node) + { + left_node = ind; + break; + } + } + } + if (!right_found) + { + for (auto ind:boundary_nodes) + { + if (ind != left_node) + { + right_node = ind; + break; + } + } + } + LHBN->set(section.id(),left_node); + RHBN->set(section.id(),right_node); + } + } + } + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::buildMiddleNodes() +{ + std::cout<<"> Building middle nodes of the block decomposition"<getVariable("leftHeadMiddleNode"); + auto RHMN = m_topological_representation->getVariable("rightHeadMiddleNode"); + auto LTMN = m_topological_representation->getVariable("leftTailMiddleNode"); + auto RTMN = m_topological_representation->getVariable("rightTailMiddleNode"); + //auto TN = m_topological_representation->getVariable("tailNode"); + //auto HN = m_topological_representation->getVariable("headNode"); + auto LTBN = m_topological_representation->getVariable("leftTailBoundaryNode"); + auto RTBN = m_topological_representation->getVariable("rightTailBoundaryNode"); + auto LHBN = m_topological_representation->getVariable("leftHeadBoundaryNode"); + auto RHBN = m_topological_representation->getVariable("rightHeadBoundaryNode"); + auto sectionType = m_topological_representation->getVariable("section_type"); + auto nodeType = m_topological_representation->getVariable("node_type"); + for (auto s_id:m_topological_representation->edges()) + { + LHMN->set(s_id,-1); + RHMN->set(s_id,-1); + LTMN->set(s_id,-1); + RTMN->set(s_id,-1); + } + for (auto s_id:m_topological_representation->edges()) + { + if (sectionType->value(s_id) == 1) + { + Edge section = m_topological_representation->get(s_id); + if (nodeType->value(section.get()[0].id()) > 1 && nodeType->value(section.get()[1].id()) > 1) + { + // Get the node where the middle points appear + Node n; + if (wings(section.get()[0],section) == 0) + n = section.get()[0]; + else + n = section.get()[1]; + // Get the neighbouring sections + //std::vector neighbours = neighbouringEdges(section,n); + std::vector neighbours = orderedNeigbourSections(section,n); + // Build the medial nodes + if (orientation(n,section) == 1) + { + Node ln = m_t_mesh->get(LTBN->value(s_id)); + Node rn = m_t_mesh->get(RTBN->value(s_id)); + Node newNode; + if (LTMN->value(s_id) < 0) + { + // Placement of the new node + math::Point dir = ln.point()+(-1.)*n.point(); + dir = (1./vec(dir).norm())*dir; + double l = (1./sqrt(2.))*section.length(); + if (l > vec((ln.point()+n.point())*(1./2.)+(-1.)*n.point()).norm()) + l = vec((ln.point()+n.point())*(1./2.)+(-1.)*n.point()).norm(); + //newNode = m_t_mesh->newNode((ln.point()+n.point())*(1./2.)); + newNode = m_t_mesh->newNode(n.point()+l*dir); + LTMN->set(s_id,newNode.id()); + // Update the neighbour middle node + if (neighbours.size() == 1) + { + Edge s = neighbours[0]; + if (orientation(n,s) == 1) + RTMN->set(s.id(),newNode.id()); + if (orientation(n,s) == -1) + LHMN->set(s.id(),newNode.id()); + } + if (neighbours.size() > 1) + { + Edge s = neighbours[1]; + if (orientation(n,s) == 1) + RTMN->set(s.id(),newNode.id()); + if (orientation(n,s) == -1) + LHMN->set(s.id(),newNode.id()); + } + } + if (RTMN->value(s_id) < 0) + { + // Placement of the new node + math::Point dir = rn.point()+(-1.)*n.point(); + dir = (1./vec(dir).norm())*dir; + double l = (1./sqrt(2.))*section.length(); + if (l > vec((rn.point()+n.point())*(1./2.)+(-1.)*n.point()).norm()) + l = vec((rn.point()+n.point())*(1./2.)+(-1.)*n.point()).norm(); + //newNode = m_t_mesh->newNode((rn.point()+n.point())*(1./2.)); + newNode = m_t_mesh->newNode(n.point()+l*dir); + RTMN->set(s_id,newNode.id()); + // Update the neighbour middle node + if (neighbours.size() == 1) + { + Edge s = neighbours[0]; + if (orientation(n,s) == 1) + LTMN->set(s.id(),newNode.id()); + if (orientation(n,s) == -1) + RHMN->set(s.id(),newNode.id()); + } + if (neighbours.size() > 1) + { + Edge s = neighbours[0]; + if (orientation(n,s) == 1) + LTMN->set(s.id(),newNode.id()); + if (orientation(n,s) == -1) + RHMN->set(s.id(),newNode.id()); + } + } + + } + else // if (orientation(n,section) == -1) + { + Node ln = m_t_mesh->get(LHBN->value(s_id)); + Node rn = m_t_mesh->get(RHBN->value(s_id)); + Node newNode; + if (LHMN->value(s_id) < 0) + { + // Placement of the new node + math::Point dir = ln.point()+(-1.)*n.point(); + dir = (1./vec(dir).norm())*dir; + double l = (1./sqrt(2.))*section.length(); + if (l > vec((ln.point()+n.point())*(1./2.)+(-1.)*n.point()).norm()) + l = vec((ln.point()+n.point())*(1./2.)+(-1.)*n.point()).norm(); + //newNode = m_t_mesh->newNode((ln.point()+n.point())*(1./2.)); + newNode = m_t_mesh->newNode(n.point()+l*dir); + LHMN->set(s_id,newNode.id()); + // Update the neighbour middle node + if (neighbours.size() == 1) + { + Edge s = neighbours[0]; + if (orientation(n,s) == 1) + LTMN->set(s.id(),newNode.id()); + if (orientation(n,s) == -1) + RHMN->set(s.id(),newNode.id()); + } + if (neighbours.size() > 1) + { + Edge s = neighbours[0]; + if (orientation(n,s) == 1) + LTMN->set(s.id(),newNode.id()); + if (orientation(n,s) == -1) + RHMN->set(s.id(),newNode.id()); + } + } + if (RHMN->value(s_id) < 0) + { + // Placement of the new node + math::Point dir = rn.point()+(-1.)*n.point(); + dir = (1./vec(dir).norm())*dir; + double l = (1./sqrt(2.))*section.length(); + if (l > vec((rn.point()+n.point())*(1./2.)+(-1.)*n.point()).norm()) + l = vec((rn.point()+n.point())*(1./2.)+(-1.)*n.point()).norm(); + //newNode = m_t_mesh->newNode((rn.point()+n.point())*(1./2.)); + newNode = m_t_mesh->newNode(n.point()+l*dir); + RHMN->set(s_id,newNode.id()); + // Update the neighbour middle node + if (neighbours.size() == 1) + { + Edge s = neighbours[0]; + if (orientation(n,s) == 1) + RTMN->set(s.id(),newNode.id()); + if (orientation(n,s) == -1) + LHMN->set(s.id(),newNode.id()); + } + if (neighbours.size() > 1) + { + Edge s = neighbours[1]; + if (orientation(n,s) == 1) + RTMN->set(s.id(),newNode.id()); + if (orientation(n,s) == -1) + LHMN->set(s.id(),newNode.id()); + } + } + } + } + } + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::buildBlocks() +{ + auto TN = m_topological_representation->getVariable("tailNode"); + auto HN = m_topological_representation->getVariable("headNode"); + auto LTBN = m_topological_representation->getVariable("leftTailBoundaryNode"); + auto RTBN = m_topological_representation->getVariable("rightTailBoundaryNode"); + auto LHBN = m_topological_representation->getVariable("leftHeadBoundaryNode"); + auto RHBN = m_topological_representation->getVariable("rightHeadBoundaryNode"); + auto LHMN = m_topological_representation->getVariable("leftHeadMiddleNode"); + auto RHMN = m_topological_representation->getVariable("rightHeadMiddleNode"); + auto LTMN = m_topological_representation->getVariable("leftTailMiddleNode"); + auto RTMN = m_topological_representation->getVariable("rightTailMiddleNode"); + auto FTN = m_topological_representation->getVariable("farTailNode"); + auto FHN = m_topological_representation->getVariable("farHeadNode"); + auto sectionType = m_topological_representation->getVariable("section_type"); + auto nodeType = m_topological_representation->getVariable("node_type"); + auto f2sid = m_t_mesh->getVariable("face2sectionID"); + auto f2st = m_t_mesh->getVariable("face2sectionType"); + auto section2sectionID = m_topological_representation->getVariable("section_to_section_id"); + auto t_junct = m_t_mesh->getVariable("is_a_T_junction"); + auto t_junct_list = m_t_mesh->getVariable,GMDS_FACE>("T_junctions"); + auto tri_ver = m_t_mesh->getVariable("is_a_triangle_vertex"); + auto tri = m_t_mesh->getVariable("is_a_triangle"); + auto minTriNode2TMeshNode = m_min_delaunay->getVariable("minTriNode2TMeshNode"); + auto TMeshNode2MinTriNode = m_t_mesh->getVariable("TMeshNode2MinTriNode"); + + int tn, hn, ltbn, rtbn, lhbn, rhbn, lhmn, rhmn, ltmn, rtmn, ftn, fhn; + Face new_face; + for (auto s_id:m_topological_representation->edges()) + { + if (sectionType->value(s_id) == 0) + { + tn = TN->value(s_id); + hn = HN->value(s_id); + ltbn = LTBN->value(s_id); + rtbn = RTBN->value(s_id); + lhbn = LHBN->value(s_id); + rhbn = RHBN->value(s_id); + lhmn = LHMN->value(s_id); + rhmn = RHMN->value(s_id); + ltmn = LTMN->value(s_id); + rtmn = RTMN->value(s_id); + + std::vector block1; + std::vector t_junctions; + block1.push_back(hn); + if (lhmn >= 0) + { + block1.push_back(lhmn); + t_junct->set(lhmn,1); + t_junctions.push_back(lhmn); + } + block1.push_back(lhbn); + Node n1 = m_min_delaunay->get(TMeshNode2MinTriNode->value(lhbn)); + Node n2 = m_min_delaunay->get(TMeshNode2MinTriNode->value(ltbn)); + std::vector inbetweenBoundNodes = shortestPathAlongBoundaryOrConstraints(n1,n2); + if (inbetweenBoundNodes.size() >= 3) + { + for (int i = 1; i < inbetweenBoundNodes.size()-1; i++) + { + if (minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()) >= 0) + { + block1.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + t_junct->set(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()),1); + t_junctions.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + } + } + } + block1.push_back(ltbn); + if (ltmn >= 0) + { + block1.push_back(ltmn); + t_junct->set(ltmn,1); + t_junctions.push_back(ltmn); + } + block1.push_back(tn); + new_face = m_t_mesh->newFace(block1); + f2sid->set(new_face.id(),section2sectionID->value(s_id)); + f2st->set(new_face.id(),0); + bool isATri = (lhbn == ltbn); + if (isATri) + { + tri->set(new_face.id(),1); + tri_ver->set(lhbn,1); + } + t_junct_list->set(new_face.id(),t_junctions); + t_junctions.clear(); + + std::vector block2; + block2.push_back(tn); + if (rtmn >= 0) + { + block2.push_back(rtmn); + t_junct->set(rtmn,1); + t_junctions.push_back(rtmn); + } + block2.push_back(rtbn); + n1 = m_min_delaunay->get(TMeshNode2MinTriNode->value(rtbn)); + n2 = m_min_delaunay->get(TMeshNode2MinTriNode->value(rhbn)); + inbetweenBoundNodes = shortestPathAlongBoundaryOrConstraints(n1,n2); + if (inbetweenBoundNodes.size() >= 3) + { + for (int i = 1; i < inbetweenBoundNodes.size()-1; i++) + { + if (minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()) >= 0) + { + block2.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + t_junct->set(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()),1); + t_junctions.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + } + } + } + block2.push_back(rhbn); + if (rhmn >= 0) + { + block2.push_back(rhmn); + t_junct->set(rhmn,1); + t_junctions.push_back(rhmn); + } + block2.push_back(hn); + new_face = m_t_mesh->newFace(block2); + f2sid->set(new_face.id(),section2sectionID->value(s_id)); + f2st->set(new_face.id(),0); + isATri = (rtbn == rhbn); + if (isATri) + { + tri->set(new_face.id(),1); + tri_ver->set(rtbn,1); + } + t_junct_list->set(new_face.id(),t_junctions); + t_junctions.clear(); + } + + if (sectionType->value(s_id) == 1) + { + tn = TN->value(s_id); + hn = HN->value(s_id); + ltbn = LTBN->value(s_id); + rtbn = RTBN->value(s_id); + lhbn = LHBN->value(s_id); + rhbn = RHBN->value(s_id); + lhmn = LHMN->value(s_id); + rhmn = RHMN->value(s_id); + ltmn = LTMN->value(s_id); + rtmn = RTMN->value(s_id); + ftn = FTN->value(s_id); + fhn = FHN->value(s_id); + + Edge section = m_topological_representation->get(s_id); + std::vector t_junctions; + + if (ftn >= 0) + { + std::vector block; + + block.push_back(tn); + block.push_back(rhmn); + block.push_back(hn); + block.push_back(lhmn); + new_face = m_t_mesh->newFace(block); + block.clear(); + f2sid->set(new_face.id(),section2sectionID->value(s_id)); + f2st->set(new_face.id(),1); + + block.push_back(tn); + block.push_back(lhmn); + block.push_back(lhbn); + Node n1 = m_min_delaunay->get(TMeshNode2MinTriNode->value(lhbn)); + Node n2 = m_min_delaunay->get(TMeshNode2MinTriNode->value(ftn)); + std::vector inbetweenBoundNodes = shortestPathAlongBoundaryOrConstraints(n1,n2); + if (inbetweenBoundNodes.size() >= 3) + { + for (int i = 1; i < inbetweenBoundNodes.size()-1; i++) + { + if (minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()) >= 0) + { + block.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + t_junct->set(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()),1); + t_junctions.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + } + } + } + block.push_back(ftn); + new_face = m_t_mesh->newFace(block); + block.clear(); + f2sid->set(new_face.id(),section2sectionID->value(s_id)); + f2st->set(new_face.id(),1); + t_junct_list->set(new_face.id(),t_junctions); + t_junctions.clear(); + + block.push_back(tn); + block.push_back(ftn); + n1 = m_min_delaunay->get(TMeshNode2MinTriNode->value(ftn)); + n2 = m_min_delaunay->get(TMeshNode2MinTriNode->value(rhbn)); + inbetweenBoundNodes = shortestPathAlongBoundaryOrConstraints(n1,n2); + if (inbetweenBoundNodes.size() >= 3) + { + for (int i = 1; i < inbetweenBoundNodes.size()-1; i++) + { + if (minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()) >= 0) + { + block.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + t_junct->set(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()),1); + t_junctions.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + } + } + } + block.push_back(rhbn); + block.push_back(rhmn); + new_face = m_t_mesh->newFace(block); + f2sid->set(new_face.id(),section2sectionID->value(s_id)); + f2st->set(new_face.id(),1); + t_junct_list->set(new_face.id(),t_junctions); + t_junctions.clear(); + } + + else if (fhn >= 0) + { + std::vector block; + + block.push_back(hn); + block.push_back(ltmn); + block.push_back(tn); + block.push_back(rtmn); + new_face = m_t_mesh->newFace(block); + block.clear(); + f2sid->set(new_face.id(),section2sectionID->value(s_id)); + f2st->set(new_face.id(),1); + + block.push_back(hn); + block.push_back(rtmn); + block.push_back(rtbn); + Node n1 = m_min_delaunay->get(TMeshNode2MinTriNode->value(rtbn)); + Node n2 = m_min_delaunay->get(TMeshNode2MinTriNode->value(fhn)); + std::vector inbetweenBoundNodes = shortestPathAlongBoundaryOrConstraints(n1,n2); + if (inbetweenBoundNodes.size() >= 3) + { + for (int i = 1; i < inbetweenBoundNodes.size()-1; i++) + { + if (minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()) >= 0) + { + block.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + t_junct->set(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()),1); + t_junctions.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + } + } + } + block.push_back(fhn); + new_face = m_t_mesh->newFace(block); + block.clear(); + f2sid->set(new_face.id(),section2sectionID->value(s_id)); + f2st->set(new_face.id(),1); + t_junct_list->set(new_face.id(),t_junctions); + t_junctions.clear(); + + block.push_back(hn); + block.push_back(fhn); + n1 = m_min_delaunay->get(TMeshNode2MinTriNode->value(fhn)); + n2 = m_min_delaunay->get(TMeshNode2MinTriNode->value(ltbn)); + inbetweenBoundNodes = shortestPathAlongBoundaryOrConstraints(n1,n2); + if (inbetweenBoundNodes.size() >= 3) + { + for (int i = 1; i < inbetweenBoundNodes.size()-1; i++) + { + if (minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()) >= 0) + { + block.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + t_junct->set(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()),1); + t_junctions.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + } + } + } + block.push_back(ltbn); + block.push_back(ltmn); + new_face = m_t_mesh->newFace(block); + f2sid->set(new_face.id(),section2sectionID->value(s_id)); + f2st->set(new_face.id(),1); + t_junct_list->set(new_face.id(),t_junctions); + t_junctions.clear(); + } + + else if (nodeType->value(section.get()[0].id()) == 1 || nodeType->value(section.get()[1].id()) == 1) + //if (nodeType->value(section.get()[0].id()) == 1 || nodeType->value(section.get()[1].id()) == 1) + { + std::vector block; + block.push_back(hn); + if (lhmn >= 0) + { + block.push_back(lhmn); + t_junct->set(lhmn,1); + t_junctions.push_back(lhmn); + } + if (lhbn == -1) + { + Node n1 = m_min_delaunay->get(TMeshNode2MinTriNode->value(hn)); + Node n2 = m_min_delaunay->get(TMeshNode2MinTriNode->value(ltbn)); + std::vector inbetweenBoundNodes = shortestPathAlongBoundaryOrConstraints(n1,n2); + if (inbetweenBoundNodes.size() >= 3) + { + for (int i = 1; i < inbetweenBoundNodes.size()-1; i++) + { + if (minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()) >= 0) + { + block.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + t_junct->set(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()),1); + t_junctions.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + } + } + } + } + block.push_back(lhbn+ltbn+1); + if (ltmn >= 0) + { + block.push_back(ltmn); + t_junct->set(ltmn,1); + t_junctions.push_back(ltmn); + } + if (ltbn == -1) + { + Node n1 = m_min_delaunay->get(TMeshNode2MinTriNode->value(lhbn)); + Node n2 = m_min_delaunay->get(TMeshNode2MinTriNode->value(tn)); + std::vector inbetweenBoundNodes = shortestPathAlongBoundaryOrConstraints(n1,n2); + if (inbetweenBoundNodes.size() >= 3) + { + for (int i = 1; i < inbetweenBoundNodes.size()-1; i++) + { + if (minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()) >= 0) + { + block.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + t_junct->set(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()),1); + t_junctions.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + } + } + } + } + block.push_back(tn); + if (rtmn >= 0) + { + block.push_back(rtmn); + t_junct->set(rtmn,1); + t_junctions.push_back(rtmn); + } + if (rtbn == -1) + { + Node n1 = m_min_delaunay->get(TMeshNode2MinTriNode->value(tn)); + Node n2 = m_min_delaunay->get(TMeshNode2MinTriNode->value(rhbn)); + std::vector inbetweenBoundNodes = shortestPathAlongBoundaryOrConstraints(n1,n2); + if (inbetweenBoundNodes.size() >= 3) + { + for (int i = 1; i < inbetweenBoundNodes.size()-1; i++) + { + if (minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()) >= 0) + { + block.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + t_junct->set(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()),1); + t_junctions.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + } + } + } + } + block.push_back(rhbn+rtbn+1); + if (rhmn >= 0) + { + block.push_back(rhmn); + t_junct->set(rhmn,1); + t_junctions.push_back(rhmn); + } + if (rhbn == -1) + { + Node n1 = m_min_delaunay->get(TMeshNode2MinTriNode->value(rtbn)); + Node n2 = m_min_delaunay->get(TMeshNode2MinTriNode->value(hn)); + std::vector inbetweenBoundNodes = shortestPathAlongBoundaryOrConstraints(n1,n2); + if (inbetweenBoundNodes.size() >= 3) + { + for (int i = 1; i < inbetweenBoundNodes.size()-1; i++) + { + if (minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()) >= 0) + { + block.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + t_junct->set(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()),1); + t_junctions.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + } + } + } + } + new_face = m_t_mesh->newFace(block); + f2sid->set(new_face.id(),section2sectionID->value(s_id)); + f2st->set(new_face.id(),1); + t_junct_list->set(new_face.id(),t_junctions); + t_junctions.clear(); + } + + else + { + if (wings(section.get()[0],section) == 1) + { + new_face = m_t_mesh->newQuad(hn,lhmn,tn,rhmn); + f2sid->set(new_face.id(),section2sectionID->value(s_id)); + f2st->set(new_face.id(),1); + + std::vector block; + block.push_back(tn); + block.push_back(lhmn); + block.push_back(lhbn); + Node n1 = m_min_delaunay->get(TMeshNode2MinTriNode->value(lhbn)); + Node n2 = m_min_delaunay->get(TMeshNode2MinTriNode->value(ltbn)); + std::vector inbetweenBoundNodes = shortestPathAlongBoundaryOrConstraints(n1,n2); + if (inbetweenBoundNodes.size() >= 3) + { + for (int i = 1; i < inbetweenBoundNodes.size()-1; i++) + { + if (minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()) >= 0) + { + block.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + t_junct->set(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()),1); + t_junctions.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + } + } + } + block.push_back(ltbn); + if (ltmn >= 0) + { + block.push_back(ltmn); + t_junct->set(ltmn,1); + t_junctions.push_back(ltmn); + } + new_face = m_t_mesh->newFace(block); + f2sid->set(new_face.id(),section2sectionID->value(s_id)); + f2st->set(new_face.id(),1); + bool isATri = (lhbn == ltbn); + if (isATri) + { + tri->set(new_face.id(),1); + tri_ver->set(lhbn,1); + } + t_junct_list->set(new_face.id(),t_junctions); + t_junctions.clear(); + block.clear(); + + block.push_back(tn); + if (rtmn >= 0) + { + block.push_back(rtmn); + t_junct->set(rtmn,1); + t_junctions.push_back(rtmn); + } + block.push_back(rtbn); + n1 = m_min_delaunay->get(TMeshNode2MinTriNode->value(rtbn)); + n2 = m_min_delaunay->get(TMeshNode2MinTriNode->value(rhbn)); + inbetweenBoundNodes = shortestPathAlongBoundaryOrConstraints(n1,n2); + if (inbetweenBoundNodes.size() >= 3) + { + for (int i = 1; i < inbetweenBoundNodes.size()-1; i++) + { + if (minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()) >= 0) + { + block.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + t_junct->set(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()),1); + t_junctions.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + } + } + } + block.push_back(rhbn); + block.push_back(rhmn); + new_face = m_t_mesh->newFace(block); + f2sid->set(new_face.id(),section2sectionID->value(s_id)); + f2st->set(new_face.id(),1); + isATri = (rtbn == rhbn); + if (isATri) + { + tri->set(new_face.id(),1); + tri_ver->set(rtbn,1); + } + t_junct_list->set(new_face.id(),t_junctions); + t_junctions.clear(); + } + else + { + new_face = m_t_mesh->newQuad(hn,ltmn,tn,rtmn); + f2sid->set(new_face.id(),section2sectionID->value(s_id)); + f2st->set(new_face.id(),1); + std::vector block; + block.push_back(hn); + if (lhmn >= 0) + { + block.push_back(lhmn); + t_junct->set(lhmn,1); + t_junctions.push_back(lhmn); + } + block.push_back(lhbn); + Node n1 = m_min_delaunay->get(TMeshNode2MinTriNode->value(lhbn)); + Node n2 = m_min_delaunay->get(TMeshNode2MinTriNode->value(ltbn)); + std::vector inbetweenBoundNodes = shortestPathAlongBoundaryOrConstraints(n1,n2); + if (inbetweenBoundNodes.size() >= 3) + { + for (int i = 1; i < inbetweenBoundNodes.size()-1; i++) + { + if (minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()) >= 0) + { + block.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + t_junct->set(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()),1); + t_junctions.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + } + } + } + block.push_back(ltbn); + block.push_back(ltmn); + new_face = m_t_mesh->newFace(block); + f2sid->set(new_face.id(),section2sectionID->value(s_id)); + f2st->set(new_face.id(),1); + bool isATri = (lhbn == ltbn); + if (isATri) + { + tri->set(new_face.id(),1); + tri_ver->set(lhbn,1); + } + t_junct_list->set(new_face.id(),t_junctions); + t_junctions.clear(); + block.clear(); + + block.push_back(hn); + block.push_back(rtmn); + block.push_back(rtbn); + n1 = m_min_delaunay->get(TMeshNode2MinTriNode->value(rtbn)); + n2 = m_min_delaunay->get(TMeshNode2MinTriNode->value(rhbn)); + inbetweenBoundNodes = shortestPathAlongBoundaryOrConstraints(n1,n2); + if (inbetweenBoundNodes.size() >= 3) + { + for (int i = 1; i < inbetweenBoundNodes.size()-1; i++) + { + if (minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()) >= 0) + { + block.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + t_junct->set(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id()),1); + t_junctions.push_back(minTriNode2TMeshNode->value(inbetweenBoundNodes[i].id())); + } + } + } + block.push_back(rhbn); + if (rhmn >= 0) + { + block.push_back(rhmn); + t_junct->set(rhmn,1); + t_junctions.push_back(rhmn); + } + new_face = m_t_mesh->newFace(block); + f2sid->set(new_face.id(),section2sectionID->value(s_id)); + f2st->set(new_face.id(),1); + isATri = (rtbn == rhbn); + if (isATri) + { + tri->set(new_face.id(),1); + tri_ver->set(rtbn,1); + } + t_junct_list->set(new_face.id(),t_junctions); + t_junctions.clear(); + // m_t_mesh->newQuad(hn,lhbn,ltbn,ltmn); + // m_t_mesh->newQuad(hn,rtmn,rtbn,rhbn); + } + } + } + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::transformDegenerateQuadsIntoTriangles() +{ + std::cout<<"> Transforming degenerate quads into triangles"<getVariable("is_a_triangle"); + auto tri_ver = m_t_mesh->getVariable("is_a_triangle_vertex"); + auto t_junct_list = m_t_mesh->getVariable,GMDS_FACE>("T_junctions"); + for (auto f_id:m_t_mesh->faces()) + { + if (tri->value(f_id) == 1) + { + Face f = m_t_mesh->get(f_id); + std::vector nodes; + bool double_vertex_added = false; + for (auto n:f.get()) + { + if (tri_ver->value(n.id()) == 0) + { + nodes.push_back(n.id()); + } + else + { + if (!double_vertex_added) + { + nodes.push_back(n.id()); + double_vertex_added = true; + } + } + } + Face new_face = m_t_mesh->newFace(nodes); + tri->set(new_face.id(),2); + t_junct_list->set(new_face.id(),t_junct_list->value(f_id)); + m_t_mesh->deleteFace(f_id); + } + } +} + +/*----------------------------------------------------------------------------*/ +std::vector MedaxBasedTMeshBuilder::sortEdgesFan(std::vector &AFan) +{ + // DOESN'T WORK + + + if (AFan.size() <= 1) + return AFan; + // Mark the edges of the fan + auto fan = m_t_mesh->newVariable("fan"); + auto neighbours = m_t_mesh->newVariable,GMDS_EDGE>("neighbours"); + for (auto e:AFan) + fan->set(e.id(),1); + // Find each edge's neighbours + std::vector neighb; + for (auto e:AFan) + { + neighb.clear(); + for (auto f:e.get()) + { + for (auto e2:f.get()) + { + if (fan->value(e2.id()) == 1 && e2.id() != e.id()) + neighb.push_back(e2); + } + } + std::cout<<"dnhdfhzs "<set(e.id(),neighb); + } + // Find the two extrem edges of the fan, if they exist (the fan can be a cycle) + bool isACycle = true; + Edge e1,e2; + for (auto e:AFan) + { + if (neighbours->value(e.id()).size() == 1) + { + e1 = e; + isACycle = false; + break; + } + } + for (auto e:AFan) + { + if (neighbours->value(e.id()).size() == 1 && e.id() != e1.id()) + { + e2 = e; + break; + } + } + + // Decide in which direction to go so that the fan is positively oriented + Edge nei_e1 = neighbours->value(e1.id())[0]; + Node n = getCommonNode(e1,nei_e1); + double theta = oriented_angle(edge2vec(e1,n),edge2vec(nei_e1,n)); + if (theta < 0.) + { + Edge e3 = e1; + e1 = e2; + e2 = e3; + } + + // Fill the fan from e1 to e2 + std::vector sorted_edges; + auto added = m_t_mesh->newVariable("added"); + Edge current = e1; + bool finished = false; + while (!finished) + { + sorted_edges.push_back(current); + added->set(current.id(),1); + bool found = false; + for (auto e:neighbours->value(current.id())) + { + if (added->value(e.id()) == 0) + { + current = e; + found = true; + break; + } + } + finished = !found; + } + + m_t_mesh->deleteVariable(GMDS_EDGE,fan); + m_t_mesh->deleteVariable(GMDS_EDGE,neighbours); + m_t_mesh->deleteVariable(GMDS_EDGE,added); + return sorted_edges; +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::transformTrianglesIntoQuads() +{ + std::cout<<"> Transforming triangles into non degenerate quads"<getVariable("is_a_triangle"); + auto tri_ver = m_t_mesh->getVariable("is_a_triangle_vertex"); + auto tri_edge = m_t_mesh->newVariable("belongs_to_triangle"); + auto tmesh_nodes_constr = m_t_mesh->getVariable("belong_to_an_internal_constraint"); + auto t_junct_list = m_t_mesh->getVariable,GMDS_FACE>("T_junctions"); + auto TMeshNode2MinTriNode = m_t_mesh->getVariable("TMeshNode2MinTriNode"); + auto fan_id = m_t_mesh->getVariable("fan_id"); + auto fan_angle = m_t_mesh->getVariable("fan_angle"); + // Initialize fan ids and angles + for (auto n_id:m_t_mesh->nodes()) + { + fan_id->set(n_id,-1); + fan_angle->set(n_id,10.); + } + int Fan_Id = 0; + // Mark with 1 edges belonging to a triangle + for (auto e_id:m_t_mesh->edges()) + { + Edge e = m_t_mesh->get(e_id); + for (auto f:e.get()) + { + if (tri->value(f.id()) == 2) + { + tri_edge->set(e_id,1); + break; + } + } + } + // For each triangle vertex, transform the triangles of its fan into quads + for (auto n_id:m_t_mesh->nodes()) + { + if (tri_ver->value(n_id) == 1) + { + Node n = m_t_mesh->get(n_id); + int NbNonTriEdges = 0; + for (auto e:n.get()) + { + if (tri_edge->value(e.id()) == 0) + { + NbNonTriEdges += 1; + } + } + if (NbNonTriEdges == 1) + { + fan_id->set(n_id,Fan_Id); + fan_angle->set(n_id,2*M_PI); + std::vector adj_edges = n.get(); + adj_edges = sortEdges(n,adj_edges); + //adj_edges = sortEdgesFan(adj_edges); + std::vector tri_edges; + int regular_edge_pos; + Edge regular_edge; + for (int i = 0; i < adj_edges.size(); i++) + { + if (tri_edge->value(adj_edges[i].id()) == 0) + { + regular_edge_pos = i; + regular_edge = adj_edges[i]; + break; + } + } + for (int i = 1; i < adj_edges.size(); i++) + { + tri_edges.push_back(adj_edges[(regular_edge_pos+i)%(adj_edges.size())]); + } + if (tri_edges.size() == 2) + throw GMDSException("transformTrianglesIntoQuads() : a constraint triangle fan has only one triangle"); + // Create the new nodes and quads + Node prev, nxt; + Face new_face; + for (int i = 0; i < tri_edges.size()-1; i++) + { + if (i == 0) + { + prev = n; + } + else + { + prev = nxt; + } + if (i < tri_edges.size()-2) + { + nxt = m_t_mesh->newNode(n.point()); + TMeshNode2MinTriNode->set(nxt.id(),TMeshNode2MinTriNode->value(n.id())); + if (tmesh_nodes_constr->value(n.id()) == 1) + tmesh_nodes_constr->set(nxt.id(),1); + fan_id->set(nxt.id(),Fan_Id); + fan_angle->set(nxt.id(),2*M_PI); + } + else + { + nxt = n; + } + Face f = getCommonFace(tri_edges[i],tri_edges[i+1]); + std::vector nodes; + for (auto n1:f.get()) + { + if (n1.id() != n_id) + nodes.push_back(n1.id()); + else + { + nodes.push_back(nxt.id()); + nodes.push_back(prev.id()); + } + } + new_face = m_t_mesh->newFace(nodes); + t_junct_list->set(new_face.id(),t_junct_list->value(f.id())); + m_t_mesh->deleteFace(f.id()); + tri->set(new_face.id(),3); + } + Fan_Id += 1; + } + if (NbNonTriEdges == 2) + { + std::vector adj_edges = n.get(); + adj_edges = sortEdges(n,adj_edges); + //adj_edges = sortEdgesFan(adj_edges); + std::vector tri_edges1; + std::vector tri_edges2; + int regular_edge_pos1,regular_edge_pos2; + Edge regular_edge1,regular_edge2; + for (int i = 0; i < adj_edges.size(); i++) + { + if (tri_edge->value(adj_edges[i].id()) == 0) + { + regular_edge_pos1 = i; + regular_edge1 = adj_edges[i]; + break; + } + } + for (int i = 0; i < adj_edges.size(); i++) + { + if (tri_edge->value(adj_edges[i].id()) == 0 && adj_edges[i].id() != regular_edge1.id()) + { + regular_edge_pos2 = i; + regular_edge2 = adj_edges[i]; + break; + } + } + // Build the two fans of edges + for (int i = 1; i < regular_edge_pos2 - regular_edge_pos1; i++) + { + tri_edges1.push_back(adj_edges[(regular_edge_pos1+i)%adj_edges.size()]); + } + for (int i = 1; i < regular_edge_pos1 - regular_edge_pos2 + adj_edges.size(); i++) + { + tri_edges2.push_back(adj_edges[(regular_edge_pos2+i)%adj_edges.size()]); + } + if (tri_edges1.empty() || tri_edges2.empty()) + { + // Update information about the fan + double angle = oriented_angle(edge2vec(regular_edge1,n),edge2vec(regular_edge2,n)); + angle = fabs(angle); + int fanId; + if (0. <= angle && angle < M_PI/4.) + //if (-M_PI/4. <= angle && angle < M_PI/4.) + { + angle = 2.*M_PI; + fanId = Fan_Id; + Fan_Id += 1; + } + else if (M_PI/4. <= angle && angle < 3.*M_PI/4.) + { + angle = 3.*M_PI/2.; + fanId = Fan_Id; + Fan_Id += 1; + } + else + { + angle = 10.; + fanId = -1; + } + fan_id->set(n_id,fanId); + fan_angle->set(n_id,angle); + + std::vector tri_edges; + Edge e1,e2; + if (tri_edges1.empty()) + { + tri_edges = tri_edges2; + e1 = regular_edge2; + e2 = regular_edge1; + } + else + { + tri_edges = tri_edges1; + e1 = regular_edge1; + e2 = regular_edge2; + } + + int NbNewPoints = tri_edges.size()-1; + math::Vector dir = edge2vec(e2,n); + std::vector new_points; + for (int i = 0; i < NbNewPoints; i++) + { + //Node new_node = m_t_mesh->newNode(n.point()+(double(i+1)/double(NbNewPoints+1))*dir); + Node new_node = m_t_mesh->newNode(n.point()); + new_points.push_back(new_node); + TMeshNode2MinTriNode->set(new_node.id(),TMeshNode2MinTriNode->value(n.id())); + if (tmesh_nodes_constr->value(n.id()) == 1) + tmesh_nodes_constr->set(new_node.id(),1); + fan_id->set(new_node.id(),fanId); + fan_angle->set(new_node.id(),angle); + } + for (int i = 0; i < tri_edges.size()-1; i++) + { + Face f = getCommonFace(tri_edges[i],tri_edges[i+1]); + std::vector nodes; + Node prev,nxt; + if (i == 0) + prev = n; + else + prev = new_points[i-1]; + nxt = new_points[i]; + for (auto n1:f.get()) + { + if (n1.id() != n_id) + nodes.push_back(n1.id()); + else + { + nodes.push_back(nxt.id()); + nodes.push_back(prev.id()); + } + } + Face new_face = m_t_mesh->newFace(nodes); + t_junct_list->set(new_face.id(),t_junct_list->value(f.id())); + m_t_mesh->deleteFace(f.id()); + tri->set(new_face.id(),3); + } + // Change the neigbouring face + Face f = getCommonFace(tri_edges[tri_edges.size()-1],e2); + std::vector nodes; + for (auto n1:f.get()) + { + if (n1.id() != n_id) + nodes.push_back(n1.id()); + else + { + nodes.push_back(new_points[new_points.size()-1].id()); + } + } + Face new_face = m_t_mesh->newFace(nodes); + t_junct_list->set(new_face.id(),t_junct_list->value(f.id())); + m_t_mesh->deleteFace(f.id()); + // Change the face behind + if (isInterior(n)) + { + f = getCommonFace(e1,e2); + nodes.clear(); + for (auto n1:f.get()) + { + if (n1.id() != n_id) + nodes.push_back(n1.id()); + else + { + nodes.push_back(n.id()); + for (int i = 0; i < new_points.size(); i++) + nodes.push_back(new_points[i].id()); + } + } + new_face = m_t_mesh->newFace(nodes); + std::vector tjs = t_junct_list->value(f.id()); + for (auto n1:new_points) + tjs.push_back(n1.id()); + t_junct_list->set(new_face.id(),tjs); + m_t_mesh->deleteFace(f.id()); + } + } + else + { + // If both fans are not empty + // Deal with the first fan + int NbNewPoints = tri_edges1.size()-1; + math::Vector dir = edge2vec(regular_edge2,n); + std::vector new_points; + for (int i = 0; i < NbNewPoints; i++) + { + //Node new_node = m_t_mesh->newNode(n.point()+(double(i+1)/double(NbNewPoints+1))*dir); + Node new_node = m_t_mesh->newNode(n.point()); + new_points.push_back(new_node); + TMeshNode2MinTriNode->set(new_node.id(),TMeshNode2MinTriNode->value(n.id())); + if (tmesh_nodes_constr->value(n.id()) == 1) + tmesh_nodes_constr->set(new_node.id(),1); + fan_id->set(new_node.id(),-1); + fan_angle->set(new_node.id(),10.); + } + for (int i = 0; i < tri_edges1.size()-1; i++) + { + Face f = getCommonFace(tri_edges1[i],tri_edges1[i+1]); + std::vector nodes; + Node prev,nxt; + if (i == 0) + prev = n; + else + prev = new_points[i-1]; + nxt = new_points[i]; + for (auto n1:f.get()) + { + if (n1.id() != n_id) + nodes.push_back(n1.id()); + else + { + nodes.push_back(nxt.id()); + nodes.push_back(prev.id()); + } + } + Face new_face = m_t_mesh->newFace(nodes); + t_junct_list->set(new_face.id(),t_junct_list->value(f.id())); + m_t_mesh->deleteFace(f.id()); + tri->set(new_face.id(),3); + } + // Change the neigbouring face + Face f = getCommonFace(tri_edges1[tri_edges1.size()-1],regular_edge2); + std::vector nodes; + for (auto n1:f.get()) + { + if (n1.id() != n_id) + nodes.push_back(n1.id()); + else + { + nodes.push_back(new_points[new_points.size()-1].id()); + } + } + Face new_face = m_t_mesh->newFace(nodes); + t_junct_list->set(new_face.id(),t_junct_list->value(f.id())); + m_t_mesh->deleteFace(f.id()); + // Change the face behind + if (isInterior(n)) + { + f = getCommonFace(tri_edges2[0],regular_edge2); + nodes.clear(); + for (auto n1:f.get()) + { + if (n1.id() != n_id) + nodes.push_back(n1.id()); + else + { + nodes.push_back(n.id()); + for (int i = 0; i < new_points.size(); i++) + nodes.push_back(new_points[i].id()); + } + } + new_face = m_t_mesh->newFace(nodes); + std::vector tjs = t_junct_list->value(f.id()); + for (auto n1:new_points) + tjs.push_back(n1.id()); + t_junct_list->set(new_face.id(),tjs); + m_t_mesh->deleteFace(f.id()); + } + // Deal with the second fan + NbNewPoints = tri_edges2.size()-1; + dir = edge2vec(regular_edge1,n); + new_points.clear(); + for (int i = 0; i < NbNewPoints; i++) + { + //Node new_node = m_t_mesh->newNode(n.point()+(double(i+1)/double(NbNewPoints+1))*dir); + Node new_node = m_t_mesh->newNode(n.point()); + new_points.push_back(new_node); + TMeshNode2MinTriNode->set(new_node.id(),TMeshNode2MinTriNode->value(n.id())); + if (tmesh_nodes_constr->value(n.id()) == 1) + tmesh_nodes_constr->set(new_node.id(),1); + fan_id->set(new_node.id(),-1); + fan_angle->set(new_node.id(),10.); + } + for (int i = 0; i < tri_edges2.size()-1; i++) + { + Face f = getCommonFace(tri_edges2[i],tri_edges2[i+1]); + std::vector nodes; + Node prev,nxt; + if (i == 0) + prev = n; + else + prev = new_points[i-1]; + nxt = new_points[i]; + for (auto n1:f.get()) + { + if (n1.id() != n_id) + nodes.push_back(n1.id()); + else + { + nodes.push_back(nxt.id()); + nodes.push_back(prev.id()); + } + } + Face new_face = m_t_mesh->newFace(nodes); + t_junct_list->set(new_face.id(),t_junct_list->value(f.id())); + m_t_mesh->deleteFace(f.id()); + tri->set(new_face.id(),3); + } + // Change the neigbouring face + f = getCommonFace(tri_edges2[tri_edges2.size()-1],regular_edge1); + nodes.clear(); + for (auto n1:f.get()) + { + if (n1.id() != n_id) + nodes.push_back(n1.id()); + else + { + nodes.push_back(new_points[new_points.size()-1].id()); + } + } + new_face = m_t_mesh->newFace(nodes); + t_junct_list->set(new_face.id(),t_junct_list->value(f.id())); + m_t_mesh->deleteFace(f.id()); + // Change the face behind + if (isInterior(n)) + { + f = getCommonFace(tri_edges1[0],regular_edge1); + nodes.clear(); + for (auto n1:f.get()) + { + if (n1.id() != n_id) + nodes.push_back(n1.id()); + else + { + nodes.push_back(n.id()); + for (int i = 0; i < new_points.size(); i++) + nodes.push_back(new_points[i].id()); + } + } + new_face = m_t_mesh->newFace(nodes); + std::vector tjs = t_junct_list->value(f.id()); + for (auto n1:new_points) + tjs.push_back(n1.id()); + t_junct_list->set(new_face.id(),tjs); + m_t_mesh->deleteFace(f.id()); + } + } + } + if (NbNonTriEdges == 3) + { + std::vector adj_edges = n.get(); + adj_edges = sortEdges(n,adj_edges); + //adj_edges = sortEdgesFan(adj_edges); + std::vector tri_edges; + int regular_edge_pos1,regular_edge_pos2,regular_edge_pos3; + Edge regular_edge1,regular_edge2,regular_edge3; + if (tri_edge->value(adj_edges[0].id()) == 0 && tri_edge->value(adj_edges[1].id()) == 1) + { + regular_edge1 = adj_edges[adj_edges.size()-2]; + regular_edge_pos1 = adj_edges.size()-2; + regular_edge2 = adj_edges[adj_edges.size()-1]; + regular_edge_pos2 = adj_edges.size()-1; + regular_edge3 = adj_edges[0]; + regular_edge_pos3 = 0; + } + else if (tri_edge->value(adj_edges[adj_edges.size()-1].id()) == 0 && tri_edge->value(adj_edges[adj_edges.size()-2].id()) == 1) + { + regular_edge1 = adj_edges[adj_edges.size()-1]; + regular_edge_pos1 = adj_edges.size()-1; + regular_edge2 = adj_edges[0]; + regular_edge_pos2 = 0; + regular_edge3 = adj_edges[1]; + regular_edge_pos3 = 1; + } + else + { + for (int i = 0; i < adj_edges.size(); i++) + { + if (tri_edge->value(adj_edges[i].id()) == 0) + { + regular_edge_pos1 = i; + regular_edge1 = adj_edges[i]; + break; + } + } + for (int i = 0; i < adj_edges.size(); i++) + { + if (tri_edge->value(adj_edges[i].id()) == 0 && adj_edges[i].id() != regular_edge1.id()) + { + regular_edge_pos2 = i; + regular_edge2 = adj_edges[i]; + break; + } + } + for (int i = 0; i < adj_edges.size(); i++) + { + if (tri_edge->value(adj_edges[i].id()) == 0 && adj_edges[i].id() != regular_edge1.id() && adj_edges[i].id() != regular_edge2.id()) + { + regular_edge_pos3 = i; + regular_edge3 = adj_edges[i]; + break; + } + } + } + // Build the fan of edges + if (regular_edge_pos3 > regular_edge_pos1) + { + for (int i = 1; i < adj_edges.size() + regular_edge_pos1 - regular_edge_pos3; i++) + { + tri_edges.push_back(adj_edges[(regular_edge_pos3+i)%adj_edges.size()]); + } + } + else + { + for (int i = 1; i < regular_edge_pos1 - regular_edge_pos3; i++) + { + tri_edges.push_back(adj_edges[(regular_edge_pos3+i)%adj_edges.size()]); + } + } + // Add the new verticies and transform the triangles into quads + int NbNewPoints = tri_edges.size()-1; + math::Vector dir = edge2vec(regular_edge1,n); + std::vector new_points; + for (int i = 0; i < NbNewPoints; i++) + { + //Node new_node = m_t_mesh->newNode(n.point()+(double(i+1)/double(NbNewPoints+1))*dir); + Node new_node = m_t_mesh->newNode(n.point()); + new_points.push_back(new_node); + TMeshNode2MinTriNode->set(new_node.id(),TMeshNode2MinTriNode->value(n.id())); + if (tmesh_nodes_constr->value(n.id()) == 1) + tmesh_nodes_constr->set(new_node.id(),1); + fan_id->set(new_node.id(),-1); + fan_angle->set(new_node.id(),10.); + } + for (int i = 0; i < tri_edges.size()-1; i++) + { + Face f = getCommonFace(tri_edges[i],tri_edges[i+1]); + std::vector nodes; + Node prev,nxt; + if (i == 0) + prev = n; + else + prev = new_points[i-1]; + nxt = new_points[i]; + for (auto n1:f.get()) + { + if (n1.id() != n_id) + nodes.push_back(n1.id()); + else + { + nodes.push_back(nxt.id()); + nodes.push_back(prev.id()); + } + } + Face new_face = m_t_mesh->newFace(nodes); + t_junct_list->set(new_face.id(),t_junct_list->value(f.id())); + m_t_mesh->deleteFace(f.id()); + tri->set(new_face.id(),3); + } + // Change the neigbouring face + Face f = getCommonFace(tri_edges[tri_edges.size()-1],regular_edge1); + std::vector nodes; + for (auto n1:f.get()) + { + if (n1.id() != n_id) + nodes.push_back(n1.id()); + else + { + nodes.push_back(new_points[new_points.size()-1].id()); + } + } + Face new_face = m_t_mesh->newFace(nodes); + t_junct_list->set(new_face.id(),t_junct_list->value(f.id())); + m_t_mesh->deleteFace(f.id()); + // Change the face behind + if (isInterior(n)) + { + f = getCommonFace(regular_edge1,regular_edge2); + nodes.clear(); + for (auto n1:f.get()) + { + if (n1.id() != n_id) + nodes.push_back(n1.id()); + else + { + nodes.push_back(n.id()); + for (int i = 0; i < new_points.size(); i++) + nodes.push_back(new_points[i].id()); + } + } + new_face = m_t_mesh->newFace(nodes); + std::vector tjs = t_junct_list->value(f.id()); + for (int i = 0; i < new_points.size(); i++) + tjs.push_back(new_points[i].id()); + t_junct_list->set(new_face.id(),tjs); + m_t_mesh->deleteFace(f.id()); + } + } + } + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::markTJunctions() +{ + std::cout<<"> Marking T-junctions on the T-mesh"<getVariable("is_a_T_junction"); + auto t_junct_list = m_t_mesh->getVariable,GMDS_FACE>("T_junctions"); + // Initialize at -1 + for (auto n_id:m_t_mesh->nodes()) + { + t_junct->set(n_id,-1); + } + for (auto f_id:m_t_mesh->faces()) + { + for (auto n_id:t_junct_list->value(f_id)) + t_junct->set(n_id,f_id); + } +} + +/*----------------------------------------------------------------------------*/ +std::vector MedaxBasedTMeshBuilder::sortedAdjacentEdges(gmds::Node &AN) +{ + std::vector adj_edges = AN.get(); + std::vector sorted_edges; + math::Vector X; + X.setX(1.); + X.setY(0.); + X.setZ(0.); + while (!adj_edges.empty()) + { + int min_pos; + double min_angle = 10.; + double angle; + for (int i = 0 ; i < adj_edges.size() ; i++) + { + Edge e = adj_edges[i]; + angle = oriented_angle(X,edge2vec(e,AN)); + if (angle < min_angle) + { + min_angle = angle; + min_pos = i; + } + } + sorted_edges.push_back(adj_edges[min_pos]); + adj_edges.erase(adj_edges.begin() + min_pos); + } + return sorted_edges; +} +/*----------------------------------------------------------------------------*/ +std::vector MedaxBasedTMeshBuilder::neighbouringEdges(gmds::Edge &AE, gmds::Node &AN) +{ + std::vector neighbours; + std::vector sorted_edges = sortedAdjacentEdges(AN); + + int pos = -1; + for (int i = 0; i < sorted_edges.size(); i++) + { + if (sorted_edges[i].id() == AE.id()) + { + pos = i; + break; + } + } + if (pos == -1) + { + std::cout<<"neighbouringEdges() : Warning, the given node doesn't belong to the given edge"< 0 && pos < sorted_edges.size() - 1) + { + neighbours.push_back(sorted_edges[pos-1]); + neighbours.push_back(sorted_edges[pos+1]); + return neighbours; + } + if (pos == 0) + { + neighbours.push_back(sorted_edges[sorted_edges.size() - 1]); + neighbours.push_back(sorted_edges[1]); + return neighbours; + } + if (pos == sorted_edges.size() - 1) + { + neighbours.push_back(sorted_edges[pos-1]); + neighbours.push_back(sorted_edges[0]); + return neighbours; + } + return neighbours; + +} + +/*----------------------------------------------------------------------------*/ +std::vector MedaxBasedTMeshBuilder::orderedNeigbourSections(Edge &ASection, Node &AN) +{ + auto tailDir = m_topological_representation->getVariable("tailDirection"); + auto headDir = m_topological_representation->getVariable("headDirection"); + + std::vector adj_edges = AN.get(); + std::vector angles; + math::Vector X; + X.setX(1.); + X.setY(0.); + X.setZ(0.); + for (auto s:adj_edges) + { + double angle; + if (orientation(AN,s) == 1) + angle = oriented_angle(X,tailDir->value(s.id())); + else + angle = oriented_angle(X,-headDir->value(s.id())); + angles.push_back(angle); + } + std::vector neighbours; + std::vector sorted_edges = order(adj_edges,angles); + + int pos = -1; + for (int i = 0; i < sorted_edges.size(); i++) + { + if (sorted_edges[i].id() == ASection.id()) + { + pos = i; + break; + } + } + if (pos == -1) + { + std::cout<<"neighbouringEdges() : Warning, the given node doesn't belong to the given edge"< 0 && pos < sorted_edges.size() - 1) + { + neighbours.push_back(sorted_edges[pos-1]); + neighbours.push_back(sorted_edges[pos+1]); + return neighbours; + } + if (pos == 0) + { + neighbours.push_back(sorted_edges[sorted_edges.size() - 1]); + neighbours.push_back(sorted_edges[1]); + return neighbours; + } + if (pos == sorted_edges.size() - 1) + { + neighbours.push_back(sorted_edges[pos-1]); + neighbours.push_back(sorted_edges[0]); + return neighbours; + } + + return neighbours; + +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::setBlockDecompConnectivity() +{ + std::cout<<"> Setting block decomposition connectivity"< Marking separating edges"<getVariable("face2sectionID"); + auto f2st = m_t_mesh->getVariable("face2sectionType"); + auto sb = m_t_mesh->getVariable("separates_blocks"); + for (auto e_id:m_t_mesh->edges()) + { + Edge e = m_t_mesh->get(e_id); + if (e.get().size() == 2) + { + Face f1 = e.get()[0]; + Face f2 = e.get()[1]; + if (f2sID->value(f1.id()) != f2sID->value(f2.id())) + sb->set(e_id,1); + else + { + if (f2st->value(f1.id()) == 0 && !touchesBoundary(e)) + sb->set(e_id,1); + } + } + } + // Unmark the superfluous marks + for (auto e_id:m_t_mesh->edges()) + { + Edge e = m_t_mesh->get(e_id); + if (e.get().size() == 2 && sb->value(e_id) == 1) + { + Face f1 = e.get()[0]; + Face f2 = e.get()[1]; + if (f2sID->value(f1.id()) != f2sID->value(f2.id()) && f2st->value(f1.id()) == 1 && f2st->value(f2.id()) == 1) + { + bool isACorner1, isACorner2; + int NbBoundaryEdges = 0; + for (auto e1:f1.get()) + { + if (e1.get().size() == 1) + NbBoundaryEdges += 1; + } + isACorner1 = (NbBoundaryEdges == 2); + NbBoundaryEdges = 0; + for (auto e1:f2.get()) + { + if (e1.get().size() == 1) + NbBoundaryEdges += 1; + } + isACorner2 = (NbBoundaryEdges == 2); + if (isACorner1 || isACorner2) + sb->set(e_id,0); + } + } + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::addBigTJunctions() +{ + std::cout<<"> Adding big T-junctions"<getVariable("separates_blocks"); + auto f2sID = m_t_mesh->getVariable("face2sectionID"); + for (auto n_id:m_t_mesh->nodes()) + { + Node n = m_t_mesh->get(n_id); + if (isInterior(n) && n.get().size() == 5) + { + for (auto e:n.get()) + { + if (sb->value(e.id()) == 0) + { + int sectID = f2sID->value(e.get()[0].id()); + Edge target; + double min_angle = 15.; + for (auto e_id2:m_t_mesh->edges()) + { + Edge e2 = m_t_mesh->get(e_id2); + if (e2.get().size() == 2) + { + if ((f2sID->value(e2.get()[0].id()) == sectID || f2sID->value(e2.get()[1].id()) == sectID) && f2sID->value(e2.get()[0].id()) != f2sID->value(e2.get()[1].id())) + { + math::Point C = (1./2.)*(e2.get()[0].point()+e2.get()[1].point()); + double alpha = oriented_angle(edge2vec(e,n),vec(C+(-1.)*n.point())); + if (fabs(alpha) < min_angle) + { + min_angle = fabs(alpha); + target = e2; + } + } + } + } + math::Point bigTJunction; + if ((n.point().distance(target.get()[0].point()) < n.point().distance(target.get()[1].point()) && isInterior(target.get()[0])) || !isInterior(target.get()[1])) + bigTJunction = target.get()[0].point(); + else + bigTJunction = target.get()[1].point(); + if (n.point().distance((1./2.)*(target.get()[0].point()+target.get()[1].point())) < n.point().distance(bigTJunction)) + bigTJunction = (1./2.)*(target.get()[0].point()+target.get()[1].point()); + Node new_node = m_t_mesh->newNode(bigTJunction); + Edge new_edge = m_t_mesh->newEdge(n.id(),new_node.id()); + sb->set(new_edge.id(),1); + } + } + } + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::ensureConnectivityThroughConstraints() +{ + std::cout<<"> Ensuring connectivity through constraints"<newMark(); + std::vector boundary_edges; + std::vector boundary_nodes; + for (auto e_id:m_t_mesh->edges()) + { + Edge e = m_t_mesh->get(e_id); + if (e.get().size() == 1 && e.length() > 1e-6) + { + boundary_edges.push_back(e); + for (auto n:e.get()) + { + if (!m_t_mesh->isMarked(n.id(),visited)) + { + boundary_nodes.push_back(n); + m_t_mesh->mark(n.id(),visited); + } + } + } + } + // Mofify faces on constraints + for (auto e:boundary_edges) + { + Face f = e.get()[0]; + std::vector adj_nodes = f.get(); + std::vector ids; + for (auto n:adj_nodes) + ids.push_back(n.id()); + for (auto n:boundary_nodes) + { + if (n.id()!=e.get()[0].id() && n.id()!=e.get()[1].id() && isOnSegment(n.point(),e.get()[0].point(),e.get()[1].point())) + { + ids = insertPoint(n,adj_nodes); + adj_nodes.clear(); + for (auto id:ids) + adj_nodes.push_back(m_t_mesh->get(id)); + } + } + m_t_mesh->newFace(ids); + m_t_mesh->deleteFace(f.id()); + m_t_mesh->deleteEdge(e.id()); + } + Face f = m_t_mesh->get(265); + std::cout<<"hfiehrt "<().size()< Identifying the connected components of the boundary of the T-mesh"<> components; + auto IDs = m_t_mesh->getVariable("boundary_connected_component_id"); + for (auto e_id:m_t_mesh->edges()) + IDs->set(e_id,-1); + int ID = 0; + for (auto e_id:m_t_mesh->edges()) + { + Edge e = m_t_mesh->get(e_id); + if (e.get().size() == 1 && IDs->value(e_id) == -1) + { + // Then we build a new connected component + std::vector component; + std::stack toAdd; + toAdd.push(e); + IDs->set(e_id,ID); + while (!toAdd.empty()) + { + e = toAdd.top(); + toAdd.pop(); + component.push_back(e); + for (auto n:e.get()) + { + for (auto e1:n.get()) + { + if (e1.get().size() == 1 && IDs->value(e1.id()) == -1) + { + toAdd.push(e1); + IDs->set(e1.id(),ID); + } + } + } + } + components.push_back(component); + ID += 1; + } + } + std::cout<<"NB boundary connected components : "< Building the final T-mesh"<getVariable("belong_to_an_internal_constraint"); + auto final_tmesh_nodes_constr = m_final_t_mesh->getVariable("belong_to_an_internal_constraint"); + auto old_tj = m_t_mesh->getVariable("is_a_T_junction"); + auto new_tj = m_final_t_mesh->getVariable("is_a_T-junction"); + auto TMeshNode2MinTriNode = m_t_mesh->getVariable("TMeshNode2MinTriNode"); + auto FinalTMeshNode2MinTriNode = m_final_t_mesh->getVariable("TMeshNode2MinTriNode"); + auto c1 = m_t_mesh->getVariable("corner"); + auto c2 = m_final_t_mesh->getVariable("corner"); + auto fan_id = m_t_mesh->getVariable("fan_id"); + auto fan_angle = m_t_mesh->getVariable("fan_angle"); + auto fan_id2 = m_final_t_mesh->getVariable("fan_id"); + auto fan_angle2 = m_final_t_mesh->getVariable("fan_angle"); + auto tri1 = m_t_mesh->getVariable("is_a_triangle"); + auto tri2 = m_final_t_mesh->getVariable("is_a_triangle"); + auto ip1 = m_t_mesh->getVariable("is_an_intersection_point"); + auto ip2 = m_final_t_mesh->getVariable("is_an_intersection_point"); + for (auto n_id:m_t_mesh->nodes()) + { + Node n = m_t_mesh->get(n_id); + Node new_node = m_final_t_mesh->newNode(n.point()); + FinalTMeshNode2MinTriNode->set(new_node.id(),TMeshNode2MinTriNode->value(n_id)); + if (tmesh_nodes_constr->value(n_id) == 1) + final_tmesh_nodes_constr->set(new_node.id(),1); + c2->set(new_node.id(),c1->value(n_id)); + fan_id2->set(new_node.id(),fan_id->value(n_id)); + fan_angle2->set(new_node.id(),fan_angle->value(n_id)); + ip2->set(new_node.id(),ip1->value(n_id)); + } + for (auto f_id:m_t_mesh->faces()) + { + Face f = m_t_mesh->get(f_id); + std::vector nodes; + for (auto n:f.get()) + { + nodes.push_back(n.id()); + } + Face new_face = m_final_t_mesh->newFace(nodes); + for (auto n:f.get()) + { + if (old_tj->value(n.id()) == -1) + new_tj->set(n.id(),-1); + if (old_tj->value(n.id()) == f.id()) + new_tj->set(n.id(),new_face.id()); + } + if (tri1->value(f_id) == 3) + tri2->set(new_face.id(),1); + } +} + +/*----------------------------------------------------------------------------*/ +void MedaxBasedTMeshBuilder::writeFinalTMesh(std::basic_string AFileName) +{ + std::cout<<"> Writing the final T-mesh"< Setting connectivity of the final T-mesh"< Marking internal constraints on the final T-mesh"<getVariable("belong_to_an_internal_constraint"); + auto int_constr = m_final_t_mesh->getVariable("internal_constraint"); + //auto tmn2mtn = m_t_mesh->getVariable("TMeshNode2MinTriNode"); + for (auto e_id:m_final_t_mesh->edges()) + { + Edge e = m_final_t_mesh->get(e_id); + Node n1 = e.get()[0]; + Node n2 = e.get()[1]; + if (nodes_constr->value(n1.id()) == 1 && nodes_constr->value(n2.id()) == 1) + int_constr->set(e.id(),1); + else if (nodes_constr->value(n1.id()) == 1 && isInterior(n1) && !isInterior(n2)) + int_constr->set(e.id(),1); + else if (nodes_constr->value(n2.id()) == 1 && isInterior(n2) && !isInterior(n1)) + int_constr->set(e.id(),1); + } +} +/*----------------------------------------------------------------------------*/ +} // end namespace medialaxis +/*----------------------------------------------------------------------------*/ +} // end namespace gmds +/*----------------------------------------------------------------------------*/ \ No newline at end of file diff --git a/modules/medialaxis/src/MedialAxis2D.cpp b/modules/medialaxis/src/MedialAxis2D.cpp index 60972e7e3..bc988f794 100644 --- a/modules/medialaxis/src/MedialAxis2D.cpp +++ b/modules/medialaxis/src/MedialAxis2D.cpp @@ -49,69 +49,29 @@ MedialAxis2D::MedialAxis2D(){ m_mesh_representation->newVariable("flux_residual_at_intersection_points"); // Boundary points at which each medial point touches the boundary m_mesh_representation->newVariable,GMDS_NODE>("touching_points"); + // List of triangles dual to the medial point + m_mesh_representation->newVariable,GMDS_NODE>("dual_triangles"); + // List of tengancy points of the medial points as nodes of the minimal triangulation + m_mesh_representation->newVariable,GMDS_NODE>("tangent_boundary_nodes"); // Medial point/group correspondence m_mesh_representation->newVariable("medialPoint2Group"); // List of boundary connected components connected by this medial point m_mesh_representation->newVariable,GMDS_NODE>("boundary_connected_components_ids"); - - // Topological representation - m_topological_representation = new Mesh(MeshModel(DIM3 | F | E | N | - F2N | E2N | N2E | N2F)); - // Type of the medial axis section: 0 if it is a quad edge // to the boundary, 1 if it is a quad diagonal, 2 if it is an edge orthogonal to the boundary - m_topological_representation->newVariable("section_type"); - // Type of the node: 0 if it is internal (there is an equation at this point), 1 if not (there is a border condition at this point) - m_topological_representation->newVariable("node_type"); - // ID of the section to which the point belongs - m_mesh_representation->newVariable("section_id"); - // Section/section ID correspondence - m_topological_representation->newVariable("section_to_section_id"); - // Correspondence medial point/node of the topological representation - m_mesh_representation->newVariable("med_point_to_sing_node"); - // Correspondence medial point/node of the topological representation - m_topological_representation->newVariable("sing_node_to_med_point"); - // Position of the wings on a section (1 if the wings point in the same direction as the oriented section, -1 if in the opposite direction, 0 if no wing) - m_topological_representation->newVariable("wings_position"); - // Position of each section in the vector of degrees of freedom - m_topological_representation->newVariable("section2matrix"); - // Quantization degrees of freedom graph representation - // Dof graph - m_dof_graph= new Mesh(MeshModel(DIM3 | E | N | - E2N | N2E)); - // Correspondence degree of freedom/vertex in the dof graph - m_topological_representation->newVariable("section2LeftQuadLength"); - m_topological_representation->newVariable("section2RightQuadLength"); - m_topological_representation->newVariable("section2QuadHeight"); - m_topological_representation->newVariable("section2LeftDiagoQuadLength"); - m_topological_representation->newVariable("section2RightDiagoQuadLength"); - // Solution to the quantization problem - m_dof_graph->newVariable("quantization_solution"); - // Topology of a medial axis based block decomposition - m_ma_block_decomposition = new Mesh(MeshModel(DIM3 | F | E | N | - F2N | E2N | N2E | N2F)); - // Going from topo rep singu node to its corresponding block decomp medial node - m_topological_representation->newVariable("singuNode2MedNode"); - // Going from topo rep singu node to its corresponding block decomp boundary nodes - m_topological_representation->newVariable,GMDS_NODE>("singuNode2BoundNodes"); - // Going from topo rep singu node to its corresponding block decomp middle nodes - m_topological_representation->newVariable,GMDS_NODE>("singuNode2MiddleNode"); - // Correspondence section/block decomposition nodes - m_topological_representation->newVariable("tailNode"); - m_topological_representation->newVariable("headNode"); - m_topological_representation->newVariable("leftTailBoundaryNode"); - m_topological_representation->newVariable("rightTailBoundaryNode"); - m_topological_representation->newVariable("leftHeadBoundaryNode"); - m_topological_representation->newVariable("rightHeadBoundaryNode"); - m_topological_representation->newVariable("leftHeadMiddleNode"); - m_topological_representation->newVariable("rightHeadMiddleNode"); - m_topological_representation->newVariable("leftTailMiddleNode"); - m_topological_representation->newVariable("rightTailMiddleNode"); + // Mark with 1 dangles (end points corresponding to boundary circle arcs) + m_mesh_representation->newVariable("is_a_dangle"); + // Mark with 1 medial points belonging to extensions (corresponding to boundary circle arcs) + m_mesh_representation->newVariable("is_an_extension"); } MedialAxis2D::~MedialAxis2D() { if(m_mesh_representation!= nullptr) delete m_mesh_representation; - if(m_topological_representation!= nullptr) - delete m_topological_representation; +} + +/*----------------------------------------------------------------------------*/ +Mesh MedialAxis2D::getMeshRepresentation() +{ + return *m_mesh_representation; } /*----------------------------------------------------------------------------*/ Node @@ -225,6 +185,7 @@ void MedialAxis2D::updateConnectivity() /*----------------------------------------------------------------------------*/ std::vector> MedialAxis2D::medialPointsGroups(const double &ATol) { + auto medPointType = m_mesh_representation->getVariable("medial_point_type"); // Medial point/group correspondence auto medialPoint2Group = m_mesh_representation->getVariable("medialPoint2Group"); // Associate to each node the value 1 if already visited @@ -252,6 +213,8 @@ std::vector> MedialAxis2D::medialPointsGroups(const double &AT medialPoint2Group->set(m.id(),groupID); for (auto e:m.get()) { + Node n1 = e.get()[0]; + Node n2 = e.get()[1]; if (e.length() < ATol) { for (auto m1:e.get()) @@ -283,8 +246,11 @@ int MedialAxis2D::medialPoint2Group(const gmds::Node &ANode) /*----------------------------------------------------------------------------*/ void MedialAxis2D::refine(const double& ATol) { + auto tangentNodes = m_mesh_representation->getVariable,GMDS_NODE>("tangent_boundary_nodes"); auto touchingPoints = m_mesh_representation->getVariable,GMDS_NODE>("touching_points"); auto medRadius = m_mesh_representation->getVariable("medial_radius"); + auto dualTriangles = m_mesh_representation->getVariable,GMDS_NODE>("dual_triangles"); + auto iae = m_mesh_representation->getVariable("is_an_extension"); std::cout<<"> Refining medial axis"<()[1]; math::Point direction = n2.point()+(-1.)*n1.point(); direction = direction*(1./vec(direction).norm()); - if ((e.length() > ATol) && ((touchingPoints->value(n1.id()).size() != 2) && (touchingPoints->value(n2.id()).size() != 2))) + if ((touchingPoints->value(n1.id()).size() != 2 && touchingPoints->value(n2.id()).size() != 2)) { - std::cout<<"Could not refine an edge because it is incident to no regular medial point (medial point type 2)."<value(n1.id()).size() == 1 && touchingPoints->value(n2.id()).size() == 1 && e.length() > ATol) + std::cout<<"Could not refine an edge because it is incident to two EPs."<value(n1.id()).size() == 1 && touchingPoints->value(n2.id()).size() > 2 || touchingPoints->value(n1.id()).size() > 2 && touchingPoints->value(n2.id()).size() == 1 && e.length() > ATol) + std::cout<<"Could not refine an edge because it is incident to an EP and an IP."<value(n1.id()).size() > 2 && touchingPoints->value(n2.id()).size() > 2) + { + math::Point M = (1./2.)*(n1.point()+n2.point()); + Node newPoint = newMedPoint(M); + // Update tangency points + double max_theta = -10.; + double min_theta = 10.; + double theta; + Node left_node; + Node right_node; + for (auto n:tangentNodes->value(n1.id())) + { + theta = oriented_angle(edge2vec(e,n1),vec(n.point()+(-1.)*n1.point())); + if (theta > 0. && theta < min_theta) + { + left_node = n; + min_theta = theta; + } + if (theta < 0. && theta > max_theta) + { + right_node = n; + max_theta = theta; + } + } + std::vector t; + std::vector p; + t.push_back(left_node); + t.push_back(right_node); + p.push_back(left_node.point()); + p.push_back(right_node.point()); + tangentNodes->set(newPoint.id(),t); + touchingPoints->set(newPoint.id(),p); + if (iae->value(n1.id()) == 1 && iae->value(n2.id()) == 1) + iae->set(newPoint.id(),1); + Edge newEdge1 = newMedEdge(n1.id(),newPoint.id()); + Edge newEdge2 = newMedEdge(newPoint.id(),n2.id()); + NbEdgesAdded += 2; + NbEdgesDeleted += 1; + NbPointsAdded += 1; + m_mesh_representation->deleteEdge(e); + } continue; } if (e.length() > ATol) @@ -328,23 +338,41 @@ void MedialAxis2D::refine(const double& ATol) math::Point P = n1.point() + double (i)*mean_len*direction; newPoint = newMedPoint(P); NbPointsAdded += 1; - // Updating the touching points and the medial radii of the new medial points + // Updating the touching points, the medial radii and the dual triangles of the new medial points if (i < NbPointsToAdd / 2) { if (touchingPoints->value(n1.id()).size() == 2) + { touchingPoints->set(newPoint.id(),touchingPoints->value(n1.id())); + dualTriangles->set(newPoint.id(),dualTriangles->value(n1.id())); + tangentNodes->set(newPoint.id(),tangentNodes->value(n1.id())); + } else + { touchingPoints->set(newPoint.id(),touchingPoints->value(n2.id())); + dualTriangles->set(newPoint.id(),dualTriangles->value(n2.id())); + tangentNodes->set(newPoint.id(),tangentNodes->value(n2.id())); + } medRadius->set(newPoint.id(),newPoint.point().distance(touchingPoints->value(n1.id())[0])); } else { if (touchingPoints->value(n2.id()).size() == 2) + { touchingPoints->set(newPoint.id(),touchingPoints->value(n2.id())); + dualTriangles->set(newPoint.id(),dualTriangles->value(n2.id())); + tangentNodes->set(newPoint.id(),tangentNodes->value(n2.id())); + } else + { touchingPoints->set(newPoint.id(),touchingPoints->value(n1.id())); + dualTriangles->set(newPoint.id(),dualTriangles->value(n1.id())); + tangentNodes->set(newPoint.id(),tangentNodes->value(n1.id())); + } medRadius->set(newPoint.id(),newPoint.point().distance(touchingPoints->value(n2.id())[0])); } + if (iae->value(n1.id()) == 1 && iae->value(n2.id()) == 1) + iae->set(newPoint.id(),1); newMedEdge(previousPoint.id(),newPoint.id()); NbEdgesAdded += 1; previousPoint = newPoint; @@ -647,6 +675,35 @@ void MedialAxis2D::setTouchingPoints(const gmds::TCellID &APointID, std::vector< touchingPoints->set(APointID,APoints); } +/*----------------------------------------------------------------------------*/ +void MedialAxis2D::setTangentNodes(const gmds::TCellID &APointID, std::vector ANodes) +{ + auto tbn = m_mesh_representation->getVariable,GMDS_NODE>("tangent_boundary_nodes"); + tbn->set(APointID,ANodes); +} + +/*----------------------------------------------------------------------------*/ +void MedialAxis2D::setDualTriangles(const gmds::TCellID &APointID, std::vector ATriangles) +{ + auto triangles = m_mesh_representation->getVariable,GMDS_NODE>("dual_triangles"); + triangles->set(APointID,ATriangles); +} + +/*----------------------------------------------------------------------------*/ +void MedialAxis2D::setDangle(const TCellID &APointID, bool ADangleValue) +{ + auto iad = m_mesh_representation->getVariable("is_a_dangle"); + if (ADangleValue) + iad->set(APointID,1); +} + +/*----------------------------------------------------------------------------*/ +void MedialAxis2D::markAsExtension(const TCellID &APointID) +{ + auto iae = m_mesh_representation->getVariable("is_an_extension"); + iae->set(APointID,1); +} + /*----------------------------------------------------------------------------*/ std::vector MedialAxis2D::getTouchingPoints(const gmds::TCellID &APointID) { @@ -654,6 +711,13 @@ std::vector MedialAxis2D::getTouchingPoints(const gmds::TCellID &AP return touchingPoints->value(APointID); } +/*----------------------------------------------------------------------------*/ +std::vector MedialAxis2D::getTangentNodes(const gmds::TCellID &APointID) +{ + auto tbn = m_mesh_representation->getVariable,GMDS_NODE>("tangent_boundary_nodes"); + return tbn->value(APointID); +} + /*----------------------------------------------------------------------------*/ void MedialAxis2D::setFluxThroughMedialRadii() { @@ -661,6 +725,8 @@ void MedialAxis2D::setFluxThroughMedialRadii() auto cosMedAngle = m_mesh_representation->getVariable("cos_medial_angle"); auto fluxThroughRadii = m_mesh_representation->getVariable("flux_through_medial_radii"); auto medPointType = m_mesh_representation->getVariable("medial_point_type"); + double epsilon = 0.15; // a small perturbation to avoid proliferation of singularities on branches where theta_M=pi/4 or 3pi/4 + //epsilon = 0.; for (auto n_id:m_mesh_representation->nodes()) { int type = medPointType->value(n_id); @@ -680,11 +746,11 @@ void MedialAxis2D::setFluxThroughMedialRadii() medAngle = acos(cosMedAngle->value(n_id)); } // Compute the flux - if (medAngle < M_PI/4.) + if (medAngle < M_PI/4.+epsilon) flux = -medAngle; else { - if (medAngle < 3.*M_PI/4.) + if (medAngle < 3.*M_PI/4.+epsilon) flux = M_PI/2.-medAngle; else flux = M_PI-medAngle; @@ -854,6 +920,36 @@ void MedialAxis2D::placeSingularities(const double& AMedRadiusFraction) std::cout<<"Total NB singularities : "< Removing singularity dipoles"< getVariable("singularity"); + // Remove dipoles + for (auto e_id:m_mesh_representation->edges()) + { + Edge e = m_mesh_representation->get(e_id); + Node n1 = e.get()[0]; + Node n2 = e.get()[1]; + if (sing->value(n1.id()) != 0 && sing->value(n1.id()) == -sing->value(n2.id())) + { + sing->set(n1.id(),0); + sing->set(n2.id(),0); + } + } + // Remove double singularities + for (auto e_id:m_mesh_representation->edges()) + { + Edge e = m_mesh_representation->get(e_id); + Node n1 = e.get()[0]; + Node n2 = e.get()[1]; + if (sing->value(n1.id()) != 0 && sing->value(n1.id()) == sing->value(n2.id())) + { + sing->set(n2.id(),0); + } + } +} + /*----------------------------------------------------------------------------*/ void MedialAxis2D::checkSingularities(const double &AOrthogonalityDefaultTol) { @@ -997,31 +1093,13 @@ MedialAxis2D::setBranchTypeOnPoints() // WARNING: this function is not optimized, its average complexity is in NbrPoints²/NbrBranches // Requires medial points type set auto branchType = m_mesh_representation->getVariable("point_branch_type"); - auto medPointType = m_mesh_representation->getVariable("medial_point_type"); + auto edgeBranchType = m_mesh_representation->getVariable("edge_branch_type"); for (auto n_id:m_mesh_representation->nodes()) { Node n = m_mesh_representation->get(n_id); std::vector adj_edges = n.get(); - int size = adj_edges.size(); - if (size == 3) - branchType->set(n.id(),2); - if (size == 2) - { - Edge e1 = adj_edges[0]; - Node extremPoint1 = getExtremPoint(n.id(),e1.id()); - Edge e2 = adj_edges[1]; - Node extremPoint2 = getExtremPoint(n.id(),e2.id()); - int mpt1 = medPointType->value(extremPoint1.id()); - int mpt2 = medPointType->value(extremPoint2.id()); - if ((mpt1 == 1)||(mpt2 == 1)) - branchType->set(n.id(),1); - else - branchType->set(n.id(),0); - } - if (size == 1) - { - branchType->set(n.id(),1); - } + if (adj_edges.size() > 0) + branchType->set(n_id,edgeBranchType->value(adj_edges[0].id())); } } @@ -1228,1384 +1306,6 @@ MedialAxis2D::write(std::basic_string AFileName) vtkWriter.setDataOptions(N| E| F); vtkWriter.write(AFileName); } - -/*----------------------------------------------------------------------------*/ -void MedialAxis2D::buildTopoRepNodes() -{ - std::cout<<"> Building nodes of the topological representation"< WARNING: if the medial axis has two neighbouring singular points (for example a singu and an IP), the topo rep is not valid"<getVariable("singularity"); - auto medPointType = m_mesh_representation->getVariable("medial_point_type"); - auto medPoint2singNode = m_mesh_representation->getVariable("med_point_to_sing_node"); - auto singNode2medPoint = m_topological_representation->getVariable("sing_node_to_med_point"); - auto nodeType = m_topological_representation->getVariable("node_type"); - - for (auto n_id:m_mesh_representation->nodes()) - { - if (medPointType->value(n_id) == 2) - { - if (singu->value(n_id) != 0) - { - Node n = m_mesh_representation->get(n_id); - Node newNode = m_topological_representation->newNode(n.point()); - medPoint2singNode->set(n_id,newNode.id()); - singNode2medPoint->set(newNode.id(),n.id()); - nodeType->set(newNode.id(),2); - } - else - medPoint2singNode->set(n_id,-1); - } - else - { - Node n = m_mesh_representation->get(n_id); - Node newNode = m_topological_representation->newNode(n.point()); - medPoint2singNode->set(n.id(),newNode.id()); - singNode2medPoint->set(newNode.id(),n.id()); - nodeType->set(newNode.id(),medPointType->value(n_id)); - } - } -} - -/*----------------------------------------------------------------------------*/ -void MedialAxis2D::setSectionID() -{ - std::cout<<"> Setting sections IDs"<getVariable("med_point_to_sing_node"); - auto sectionID = m_mesh_representation->getVariable("section_id"); - auto alreadyVisited = m_mesh_representation->newVariable("already_visited"); - int ID = 0; - for (auto n_id:m_mesh_representation->nodes()) - { - if ((medPoint2singNode->value(n_id) == -1) && (alreadyVisited->value(n_id) == 0)) - { - // We build a new section (= new edge of the topo rep) - - // Find the extreme nodes of the section - Node n = m_mesh_representation->get(n_id); - alreadyVisited->set(n_id,1); - sectionID->set(n.id(),ID); - Node prev, nxt; - Edge e; - // We go to the left - prev = n; - e = n.get()[0]; - nxt = getNextPoint(prev.id(),e.id()); - while (medPoint2singNode->value(nxt.id()) < 0) - { - alreadyVisited->set(nxt.id(),1); - sectionID->set(nxt.id(),ID); - e = getNextEdge(e.id(),nxt.id()); - prev = nxt; - nxt = getNextPoint(prev.id(),e.id()); - } - //Node n1 = nxt; - // We go to the right - prev = n; - e = n.get()[1]; - nxt = getNextPoint(prev.id(),e.id()); - while (medPoint2singNode->value(nxt.id()) < 0) - { - alreadyVisited->set(nxt.id(),1); - sectionID->set(nxt.id(),ID); - e = getNextEdge(e.id(),nxt.id()); - prev = nxt; - nxt = getNextPoint(prev.id(),e.id()); - } - //Node n2 = nxt; - ID += 1; - } - } - - std::cout<<"NB sections : "<deleteVariable(GMDS_NODE,alreadyVisited); -} - -/*----------------------------------------------------------------------------*/ -void MedialAxis2D::buildTopoRepEdges() -{ - std::cout<<"> Building edges of the topological representation"< WARNING: if the medial axis has only one section which is a cycle, the topo rep is not valid"<getVariable("cos_medial_angle"); - auto sectionType = m_topological_representation->getVariable("section_type"); - auto medPoint2singNode = m_mesh_representation->getVariable("med_point_to_sing_node"); - auto wings = m_topological_representation->getVariable("wings_position"); - auto touchingPoints = m_mesh_representation->getVariable,GMDS_NODE>("touching_points"); - auto section2sectionID = m_topological_representation->getVariable("section_to_section_id"); - auto sectionID = m_mesh_representation->getVariable("section_id"); - for (auto n_id:m_mesh_representation->nodes()) - { - if (medPoint2singNode->value(n_id) == -1) - { - // We build a new section (= new edge of the topo rep) - // Type of the section - int type; - if (cosMedAngle->value(n_id) < -sqrt(2.)/2.) - type = 0; - else - { - if (fabs(cosMedAngle->value(n_id)) < sqrt(2.)/2.) - type = 1; - else - type = 2; - } - - int sectID = sectionID->value(n_id); - - // Find the extreme nodes of the section - Node n = m_mesh_representation->get(n_id); - medPoint2singNode->set(n_id,-2); - Node prev, nxt; - Edge e; - // We go to the left - prev = n; - e = n.get()[0]; - nxt = getNextPoint(prev.id(),e.id()); - while (medPoint2singNode->value(nxt.id()) < 0) - { - medPoint2singNode->set(nxt.id(),-2); - e = getNextEdge(e.id(),nxt.id()); - prev = nxt; - nxt = getNextPoint(prev.id(),e.id()); - } - Node n1 = nxt; - // We go to the right - prev = n; - e = n.get()[1]; - nxt = getNextPoint(prev.id(),e.id()); - while (medPoint2singNode->value(nxt.id()) < 0) - { - medPoint2singNode->set(nxt.id(),-2); - e = getNextEdge(e.id(),nxt.id()); - prev = nxt; - nxt = getNextPoint(prev.id(),e.id()); - } - Node n2 = nxt; - TCellID N1 = medPoint2singNode->value(n1.id()); - TCellID N2 = medPoint2singNode->value(n2.id()); - Edge newSection = m_topological_representation->newEdge(N1,N2); - sectionType->set(newSection.id(),type); - section2sectionID->set(newSection.id(),sectID); - // Wings position - Node P1 = newSection.get()[0]; - math::Point S = getNextPoint(n_id,n.get()[1].id()).point() + (-1.)*n.point(); - math::Point R = touchingPoints->value(n_id)[0] + (-1.)*n.point(); - double orient = vec(S).dot(vec(R)); - if (type == 1) - { - if (orient >= 0) - wings->set(newSection.id(),-1); - else - wings->set(newSection.id(),1); - } - } - } -} - -/*----------------------------------------------------------------------------*/ -void -MedialAxis2D::writeTopoRep(std::basic_string AFileName) -{ - std::cout<<"> Writing the topological representation"<getVariable("wings_position"); - if (orientation(AN,AE) == wings->value(AE.id())) - return fabs(orientation(AN,AE)); - else - return 0; -} - -/*----------------------------------------------------------------------------*/ -int MedialAxis2D::orientation(gmds::Node &AN, gmds::Edge &AE) -{ - if (AN.id() == AE.get()[0].id()) - return 1; - if (AN.id() == AE.get()[1].id()) - return -1; - return 0; -} - -/*----------------------------------------------------------------------------*/ -void MedialAxis2D::setTopoRepConnectivity() -{ - std::cout<<"> Setting topological representation connectivity"<getVariable("section2matrix"); - auto type = m_topological_representation->getVariable("section_type"); - int N = 0; - for (auto e_id:m_topological_representation->edges()) - { - section2matrix->set(e_id,N); - if (type->value(e_id) == 0) - N += 3; - else - N += 4; - } - return N; -} - -/*----------------------------------------------------------------------------*/ -int MedialAxis2D::NbEquations() -{ - auto nodeType = m_topological_representation->getVariable("node_type"); - auto sectionType = m_topological_representation->getVariable("section_type"); - int N = 0; - for (auto n_id:m_topological_representation->nodes()) - { - N += nodeType->value(n_id); - if (nodeType->value(n_id) == 1) - { - Node n = m_topological_representation->get(n_id); - Edge e = n.get()[0]; - if (sectionType->value(e.id()) == 1) - N += wings(n,e); - if (sectionType->value(e.id()) == 2) - N += 1; - } - } - return N; -} - -/*----------------------------------------------------------------------------*/ -Eigen::MatrixXd MedialAxis2D::constraintMatrix() -{ - auto nodeType = m_topological_representation->getVariable("node_type"); - auto sectionType = m_topological_representation->getVariable("section_type"); - auto section2matrix = m_topological_representation->getVariable("section2matrix"); - auto singNode2medPoint = m_topological_representation->getVariable("sing_node_to_med_point"); - auto section2sectionID = m_topological_representation->getVariable("section_to_section_id"); - auto sectionID = m_mesh_representation->getVariable("section_id"); - int nbDoF = NbDoF(); - int nbEqu = NbEquations(); - Eigen::MatrixXd A; - A.resize(nbEqu,nbDoF); - A.setZero(); - - int row = 0; - int col; - - for (auto n_id:m_topological_representation->nodes()) - { - Node n = m_topological_representation->get(n_id); - - if (nodeType->value(n_id) == 1) - { - Edge S = n.get()[0]; - col = section2matrix->value(S.id()); - if (sectionType->value(S.id()) == 1) - { - if (wings(n,S) == 1) - { - A(row,col) = 1.; - A(row+1,col+1) = 1.; - row += 2; - } - else - { - A(row,col) = 1.; - A(row,col+1) = -1.; - row += 1; - } - } - else - std::cout<<"WARNING : there is a section of type 0 or 2 at an end point. This is not implemented yet"<value(n_id) == 2) - { - // First section - Edge S = n.get()[0]; - int orient = orientation(n,S); - orient = (orient + 1)/2; - col = section2matrix->value(S.id()); - if (sectionType->value(S.id()) == 0) - { - A(row,col + orient + 1) = 1.; - A(row + 1,col + 2 - orient) = 1.; - } - if (sectionType->value(S.id()) == 1) - { - A(row,col + orient) = 1.; - A(row + 1,col + 1 - orient) = 1.; - if (wings(n,S) == 0) - { - A(row,col + orient + 2) = 1.; - A(row + 1,col + 3 - orient) = 1.; - } - } - if (sectionType->value(S.id()) == 2) - std::cout<<"WARNING : there is a section of type 2. This is not implemented yet"<()[1]; - orient = orientation(n,S); - orient = (orient + 1)/2; - col = section2matrix->value(S.id()); - if (sectionType->value(S.id()) == 0) - { - A(row,col - orient + 2) = -1.; - A(row + 1,col + 1 + orient) = -1.; - } - if (sectionType->value(S.id()) == 1) - { - A(row,col + 1 - orient) = -1.; - A(row + 1,col + orient) = -1.; - if (wings(n,S) == 0) - { - A(row,col - orient + 3) = -1.; - A(row + 1,col + 2 + orient) = -1.; - } - } - if (sectionType->value(S.id()) == 2) - std::cout<<"WARNING : there is a section of type 2. This is not implemented yet"<value(n_id) >= 3) - { - // Order the sections - TCellID medPointID = singNode2medPoint->value(n_id); - Node medPoint = m_mesh_representation->get(medPointID); - std::vector adj_edges = medPoint.get(); - // Compute the angles - std::vector angles; - for (auto e:adj_edges) - { - Node n1,n2; - if (medPoint.id() == e.get()[0].id()) - { - n1 = e.get()[0]; - n2 = e.get()[1]; - } - else - { - n1 = e.get()[1]; - n2 = e.get()[0]; - } - math::Point E = n2.point() + (-1.)*n1.point(); - double cosTheta = E.X()/vec(E).norm(); - double sinTheta = E.Y()/vec(E).norm(); - double theta; - if (fabs(cosTheta-1)<10e-6) - theta = 0.; - else - { - if (fabs(cosTheta+1)<10e-6) - theta = M_PI; - else - theta = acos(cosTheta); - } - if (sinTheta < -10e-6) - theta = 2.*M_PI - theta; - angles.push_back(theta); - } - - // Sort the edges - std::vector sorted_edges; - int smallest_angle_index; - double min_angle; - for (int j = 1; j <= angles.size(); j ++) - { - // Find the jth smallest angle - min_angle = 10.; - for (auto i = 0; i < angles.size(); i ++) - { - if (min_angle > angles[i]) - { - min_angle = angles[i]; - smallest_angle_index = i; - } - } - angles[smallest_angle_index] = 20.; - sorted_edges.push_back(adj_edges[smallest_angle_index]); - } - - // Sorted sections - std::vector sorted_sections; - for (auto e:sorted_edges) - { - // Find the section to which this edge belongs - Node medPoint2; - if (e.get()[0].id() == medPoint.id()) - medPoint2 = e.get()[1]; - else - medPoint2 = e.get()[0]; - int sectID = sectionID->value(medPoint2.id()); - Edge section; - for (auto s:n.get()) - { - if (section2sectionID->value(s.id()) == sectID) - { - section = s; - break; - } - } - sorted_sections.push_back(section); - } - - // Now that the sections are sorted, write an equation for each pair of consecutive section - for (int i = 0; i < sorted_sections.size(); i ++) - { - Edge S1 = sorted_sections[i]; - Edge S2; - if (i < sorted_sections.size() - 1) - S2 = sorted_sections[i+1]; - else - S2 = sorted_sections[0]; - // First section - int orient = orientation(n,S1); - orient = (orient + 1)/2; - col = section2matrix->value(S1.id()); - if (sectionType->value(S1.id()) == 0) - A(row,col + 1 + orient) = 1.; - if (sectionType->value(S1.id()) == 1) - { - A(row,col + orient) = 1.; - if (wings(n,S1) == 0) - A(row,col + orient + 2) = 1.; - } - if (sectionType->value(S1.id()) == 2) - std::cout<<"Case section type 2 not implemented yet"<value(S2.id()); - if (sectionType->value(S2.id()) == 0) - A(row,col + 2 - orient) = -1.; - if (sectionType->value(S2.id()) == 1) - { - A(row,col + 1 - orient) = -1.; - if (wings(n,S2) == 0) - A(row,col - orient + 3) = -1.; - } - if (sectionType->value(S2.id()) == 2) - std::cout<<"Case section type 2 not implemented yet"<()[0].id() == AN.id()) - return AE.get()[1]; - if (AE.get()[1].id() == AN.id()) - return AE.get()[0]; - std::cout<<"getNextSingularNode : error, the given Node does not belong to the given edge"<().size() != 2) - { - std::cout<<"getNextMedialSection : error, the given node doesn't have valence 2"<()[0].id() == AE.id()) - return AN.get()[1]; - if (AN.get()[1].id() == AE.id()) - return AN.get()[0]; - std::cout<<"getNextMedialSection : error, the given Node does not belong to the given edge"<get(0); - Node prev_node; - Edge current_edge = current_node.get()[0]; - TCellID initial_edge_id = current_edge.id(); - while (true) - { - std::cout<<"We are currently at node "<().size() == 2) - current_edge = getNextMedialSection(current_edge,current_node); - if (current_node.get().size() >= 3) - { - Edge nxt_edge; - double theta = 10.; - for (auto e:current_node.get()) - { - if (e.id() != current_edge.id()) - { - if (oriented_angle(edge2vec(current_edge,prev_node), edge2vec(e,current_node)) < theta) - { - nxt_edge = e; - theta = oriented_angle(edge2vec(current_edge,prev_node), edge2vec(e,current_node)); - } - } - } - current_edge = nxt_edge; - } - if (current_node.id() == 0 && current_edge.id() == initial_edge_id) - break; - } -} - -/*----------------------------------------------------------------------------*/ -void MedialAxis2D::buildDofGraphNodes() -{ - std::cout<<"> Building the quantization degrees of freedom graph nodes"<getVariable("section2LeftQuadLength"); - auto RL = m_topological_representation->getVariable("section2RightQuadLength"); - auto H = m_topological_representation->getVariable("section2QuadHeight"); - auto DLL = m_topological_representation->getVariable("section2LeftDiagoQuadLength"); - auto DRL = m_topological_representation->getVariable("section2RightDiagoQuadLength"); - for (auto section_id:m_topological_representation->edges()) - { - LL->set(section_id,-1); - RL->set(section_id,-1); - H->set(section_id,-1); - DLL->set(section_id,-1); - DRL->set(section_id,-1); - } - - auto nodeType = m_topological_representation->getVariable("node_type"); - auto sectionType = m_topological_representation->getVariable("section_type"); - - Node newNode; - for (auto s_id:m_topological_representation->edges()) - { - // Create a node for each dof of the section, depending on its type - if (sectionType->value(s_id) == 0) - { - newNode = m_dof_graph->newNode(); - LL->set(s_id,newNode.id()); - newNode = m_dof_graph->newNode(); - RL->set(s_id,newNode.id()); - newNode = m_dof_graph->newNode(); - H->set(s_id,newNode.id()); - } - else - { - newNode = m_dof_graph->newNode(); - DLL->set(s_id,newNode.id()); - newNode = m_dof_graph->newNode(); - DRL->set(s_id,newNode.id()); - - Edge section = m_topological_representation->get(s_id); - Node n1 = section.get()[0]; - Node n2 = section.get()[1]; - if (nodeType->value(n1.id()) != 1 && nodeType->value(n2.id()) != 1) - { - newNode = m_dof_graph->newNode(); - LL->set(s_id,newNode.id()); - newNode = m_dof_graph->newNode(); - RL->set(s_id,newNode.id()); - } - } - } - std::cout<<"NB dof : "<getNbNodes()<getVariable("section2LeftQuadLength"); - auto RL = m_topological_representation->getVariable("section2RightQuadLength"); - - int dof1_id; - if (ASide1 == 0) - dof1_id = LL->value(ASection1.id()); - else - dof1_id = RL->value(ASection1.id()); - int dof2_id; - if (ASide2 == 0) - dof2_id = LL->value(ASection2.id()); - else - dof2_id = RL->value(ASection2.id()); - m_dof_graph->newEdge(dof1_id,dof2_id); -} - -/*----------------------------------------------------------------------------*/ -void MedialAxis2D::dofEdges11(gmds::Edge &ASection1, gmds::Edge &ASection2, int ASide1, int ASide2) -{ - auto LL = m_topological_representation->getVariable("section2LeftQuadLength"); - auto RL = m_topological_representation->getVariable("section2RightQuadLength"); - auto DLL = m_topological_representation->getVariable("section2LeftDiagoQuadLength"); - auto DRL = m_topological_representation->getVariable("section2RightDiagoQuadLength"); - - int dof11_id; - if (ASide1 == 0) - dof11_id = LL->value(ASection1.id()); - else - dof11_id = RL->value(ASection1.id()); - - int dof12_id; - if (ASide1 == 0) - dof12_id = DLL->value(ASection1.id()); - else - dof12_id = DRL->value(ASection1.id()); - - int dof21_id; - if (ASide2 == 0) - dof21_id = LL->value(ASection2.id()); - else - dof21_id = RL->value(ASection2.id()); - - int dof22_id; - if (ASide2 == 0) - dof22_id = DLL->value(ASection2.id()); - else - dof22_id = DRL->value(ASection2.id()); - - if (dof11_id >= 0 && dof21_id >= 0) - { - m_dof_graph->newEdge(dof11_id,dof21_id); - m_dof_graph->newEdge(dof12_id,dof22_id); - } - - if (dof11_id < 0 && dof21_id < 0) - { - m_dof_graph->newEdge(dof12_id,dof22_id); - } - - if (dof11_id < 0 && dof21_id >= 0) - { - m_dof_graph->newEdge(dof12_id,dof21_id); - m_dof_graph->newEdge(dof12_id,dof22_id); - } - - if (dof11_id >= 0 && dof21_id < 0) - { - m_dof_graph->newEdge(dof11_id,dof22_id); - m_dof_graph->newEdge(dof12_id,dof22_id); - } -} - -/*----------------------------------------------------------------------------*/ -void MedialAxis2D::dofEdges01(gmds::Edge &ASection1, gmds::Edge &ASection2, int ASide1, int ASide2) -{ - auto LL = m_topological_representation->getVariable("section2LeftQuadLength"); - auto RL = m_topological_representation->getVariable("section2RightQuadLength"); - auto DLL = m_topological_representation->getVariable("section2LeftDiagoQuadLength"); - auto DRL = m_topological_representation->getVariable("section2RightDiagoQuadLength"); - - int dof11_id; - if (ASide1 == 0) - dof11_id = LL->value(ASection1.id()); - else - dof11_id = RL->value(ASection1.id()); - - int dof21_id; - if (ASide2 == 0) - dof21_id = LL->value(ASection2.id()); - else - dof21_id = RL->value(ASection2.id()); - - int dof22_id; - if (ASide2 == 0) - dof22_id = DLL->value(ASection2.id()); - else - dof22_id = DRL->value(ASection2.id()); - - if (dof21_id >= 0) - m_dof_graph->newEdge(dof11_id,dof21_id); - Node n = getCommonNode(ASection1,ASection2); - if (wings(n,ASection2) == 0) - m_dof_graph->newEdge(dof11_id,dof22_id); -} - -/*----------------------------------------------------------------------------*/ -void MedialAxis2D::dofEdges10(gmds::Edge &ASection1, gmds::Edge &ASection2, int ASide1, int ASide2) -{ - auto LL = m_topological_representation->getVariable("section2LeftQuadLength"); - auto RL = m_topological_representation->getVariable("section2RightQuadLength"); - auto DLL = m_topological_representation->getVariable("section2LeftDiagoQuadLength"); - auto DRL = m_topological_representation->getVariable("section2RightDiagoQuadLength"); - - int dof11_id; - if (ASide1 == 0) - dof11_id = LL->value(ASection1.id()); - else - dof11_id = RL->value(ASection1.id()); - - int dof12_id; - if (ASide1 == 0) - dof12_id = DLL->value(ASection1.id()); - else - dof12_id = DRL->value(ASection1.id()); - - int dof21_id; - if (ASide2 == 0) - dof21_id = LL->value(ASection2.id()); - else - dof21_id = RL->value(ASection2.id()); - - if (dof11_id >= 0) - m_dof_graph->newEdge(dof11_id,dof21_id); - Node n = getCommonNode(ASection1,ASection2); - if (wings(n,ASection1) == 0) - m_dof_graph->newEdge(dof12_id,dof21_id); -} - -/*----------------------------------------------------------------------------*/ -void MedialAxis2D::dofEdges(gmds::Edge &ASection1, gmds::Edge &ASection2) -{ - auto sectionType = m_topological_representation->getVariable("section_type"); - - Node n = getCommonNode(ASection1,ASection2); - - if (sectionType->value(ASection1.id()) == 0 && sectionType->value(ASection2.id()) == 0) - { - if (orientation(n,ASection1) == 1 && orientation(n,ASection2) == 1) - { - dofEdge00(ASection1,ASection2,0,1); - } - if (orientation(n,ASection1) == 1 && orientation(n,ASection2) == -1) - { - dofEdge00(ASection1,ASection2,0,0); - } - if (orientation(n,ASection1) == -1 && orientation(n,ASection2) == 1) - { - dofEdge00(ASection1,ASection2,1,1); - } - if (orientation(n,ASection1) == -1 && orientation(n,ASection2) == -1) - { - dofEdge00(ASection1,ASection2,1,0); - } - } - - if (sectionType->value(ASection1.id()) == 0 && sectionType->value(ASection2.id()) == 1) - { - if (orientation(n,ASection1) == 1 && orientation(n,ASection2) == 1) - { - dofEdges01(ASection1,ASection2,0,1); - } - if (orientation(n,ASection1) == 1 && orientation(n,ASection2) == -1) - { - dofEdges01(ASection1,ASection2,0,0); - } - if (orientation(n,ASection1) == -1 && orientation(n,ASection2) == 1) - { - dofEdges01(ASection1,ASection2,1,1); - } - if (orientation(n,ASection1) == -1 && orientation(n,ASection2) == -1) - { - dofEdges01(ASection1,ASection2,1,0); - } - } - - if (sectionType->value(ASection1.id()) == 1 && sectionType->value(ASection2.id()) == 0) - { - if (orientation(n,ASection1) == 1 && orientation(n,ASection2) == 1) - { - dofEdges10(ASection1,ASection2,0,1); - } - if (orientation(n,ASection1) == 1 && orientation(n,ASection2) == -1) - { - dofEdges10(ASection1,ASection2,0,0); - } - if (orientation(n,ASection1) == -1 && orientation(n,ASection2) == 1) - { - dofEdges10(ASection1,ASection2,1,1); - } - if (orientation(n,ASection1) == -1 && orientation(n,ASection2) == -1) - { - dofEdges10(ASection1,ASection2,1,0); - } - } - - if (sectionType->value(ASection1.id()) == 1 && sectionType->value(ASection2.id()) == 1) - { - if (orientation(n,ASection1) == 1 && orientation(n,ASection2) == 1) - { - dofEdges11(ASection1,ASection2,0,1); - } - if (orientation(n,ASection1) == 1 && orientation(n,ASection2) == -1) - { - dofEdges11(ASection1,ASection2,0,0); - } - if (orientation(n,ASection1) == -1 && orientation(n,ASection2) == 1) - { - dofEdges11(ASection1,ASection2,1,1); - } - if (orientation(n,ASection1) == -1 && orientation(n,ASection2) == -1) - { - dofEdges11(ASection1,ASection2,1,0); - } - } -} - -/*----------------------------------------------------------------------------*/ -void MedialAxis2D::buildDofGraphEdges() -{ - std::cout<<"> Building the quantization degrees of freedom graph edges"<getVariable("node_type"); - for (auto n_id:m_topological_representation->nodes()) - { - Node n = m_topological_representation->get(n_id); - if (nodeType->value(n_id) == 2) - { - dofEdges(n.get()[0],n.get()[1]); - dofEdges(n.get()[1],n.get()[0]); - } - - if (nodeType->value(n_id) > 2) - { - std::vector adj_edges = n.get(); - std::vector sorted_edges = sortEdges(n,adj_edges); - for (int i = 0; i < sorted_edges.size()-1; i++) - dofEdges(sorted_edges[i],sorted_edges[i+1]); - dofEdges(sorted_edges[sorted_edges.size()-1],sorted_edges[0]); - } - } - - - // Display the edges - std::cout<<"Dof edges :"<edges()) - { - Edge e = m_dof_graph->get(e_id); - std::cout<<"("<()[0].id()<<","<()[1].id()<<")"< Setting quantization degrees of freedom graph connectivity"<getVariable("quantization_solution"); - //auto N = double(NbWells(m_dof_graph)); - for (auto n_id:m_dof_graph->nodes()) - { - Node n = m_dof_graph->get(n_id); - if (isASource(n)) - { - int NbWells = 0; - std::vector visited_dof; - visited_dof.push_back(n_id); - sol->set(n_id,1.); - std::queue front; - front.push(n); - while (!front.empty()) - { - Node n1 = front.back(); - front.pop(); - propagateValue(n1,*sol); - for (auto n2: getNextNodes(n1)) - { - visited_dof.push_back(n2.id()); - if (!isAWell(n2)) - front.push(n2); - else - NbWells += 1; - } - } - // Multiply the value by a power of 2 to have integers - for (auto id:visited_dof) - { - double value = sol->value(id); - if (NbWells > 1.) - value *= pow(2.,NbWells-1.); - sol->set(id,value); - } - } - } - - // Test - for (auto n_id:m_dof_graph->nodes()) - std::cout<<"Quantization solution at dof "<value(n_id)< Building medial and boundary nodes of the medial axis based block decomposition"<getVariable("node_type"); - auto singNode2medPoint = m_topological_representation->getVariable("sing_node_to_med_point"); - auto touchingPoints = m_mesh_representation->getVariable,GMDS_NODE>("touching_points"); - auto sn2mn = m_topological_representation->getVariable("singuNode2MedNode"); - auto sn2bn = m_topological_representation->getVariable,GMDS_NODE>("singuNode2BoundNodes"); - for (auto n_id:m_topological_representation->nodes()) - { - Node singu_node = m_topological_representation->get(n_id); - Node newNode; - newNode = m_ma_block_decomposition->newNode(singu_node.point()); - sn2mn->set(n_id,newNode.id()); - if (nodeType->value(n_id) == 1) - continue; - Node med_point = m_mesh_representation->get(singNode2medPoint->value(n_id)); - std::vector newNodes; - std::vector boundaryTangencyPoints = touchingPoints->value(med_point.id()); - if (nodeType->value(n_id) >= 2) - { - for (auto p:boundaryTangencyPoints) - { - newNode = m_ma_block_decomposition->newNode(p); - newNodes.push_back(newNode.id()); - } - } - sn2bn->set(n_id,newNodes); - } -} - -/*----------------------------------------------------------------------------*/ -void MedialAxis2D::writeBlockDecomp(std::basic_string AFileName) -{ - std::cout<<"> Writing the medial axis based block decomposition"<getVariable("tailNode"); - auto HN = m_topological_representation->getVariable("headNode"); - auto LTBN = m_topological_representation->getVariable("leftTailBoundaryNode"); - auto RTBN = m_topological_representation->getVariable("rightTailBoundaryNode"); - auto LHBN = m_topological_representation->getVariable("leftHeadBoundaryNode"); - auto RHBN = m_topological_representation->getVariable("rightHeadBoundaryNode"); - auto sn2mn = m_topological_representation->getVariable("singuNode2MedNode"); - auto sn2bn = m_topological_representation->getVariable,GMDS_NODE>("singuNode2BoundNodes"); - auto nodeType = m_topological_representation->getVariable("node_type"); - - for (auto n_id:m_topological_representation->nodes()) - { - Node n = m_topological_representation->get(n_id); - int medial_node = sn2mn->value(n_id); - std::vector boundary_nodes = sn2bn->value(n_id); - for (auto section:n.get()) - { - - if (orientation(n,section) == 1) - { - TN->set(section.id(),medial_node); - if (nodeType->value(n_id) == 1) - { - LTBN->set(section.id(),-1); - RTBN->set(section.id(),-1); - } - if (nodeType->value(n_id) > 1) - { - // Find the left and right boundary points - double max_theta = -10.; - double min_theta = 10.; - int left_node; - int right_node; - for (auto ind:boundary_nodes) - { - Node boundary_node = m_ma_block_decomposition->get(ind); - math::Point R = boundary_node.point()+(-1.)*n.point(); - double theta = oriented_angle(edge2vec(section,n),vec(R)); - if (theta < 0. && theta > max_theta) - { - max_theta = theta; - right_node = ind; - } - if (theta > 0. && theta < min_theta) - { - min_theta = theta; - left_node = ind; - } - } - LTBN->set(section.id(),left_node); - RTBN->set(section.id(),right_node); - } - } - - else - { - HN->set(section.id(),medial_node); - if (nodeType->value(n_id) == 1) - { - LHBN->set(section.id(),-1); - RHBN->set(section.id(),-1); - } - if (nodeType->value(n_id) > 1) - { - // Find the left and right boundary points - double max_theta = -10.; - double min_theta = 10.; - int left_node; - int right_node; - for (auto ind:boundary_nodes) - { - Node boundary_node = m_ma_block_decomposition->get(ind); - math::Point R = boundary_node.point()+(-1.)*n.point(); - double theta = oriented_angle(edge2vec(section,n),vec(R)); - if (theta < 0. && theta > max_theta) - { - max_theta = theta; - left_node = ind; - } - if (theta > 0. && theta < min_theta) - { - min_theta = theta; - right_node = ind; - } - } - LHBN->set(section.id(),left_node); - RHBN->set(section.id(),right_node); - } - } - } - } -} - -/*----------------------------------------------------------------------------*/ -void MedialAxis2D::buildMiddleNodes() -{ - auto LHMN = m_topological_representation->getVariable("leftHeadMiddleNode"); - auto RHMN = m_topological_representation->getVariable("rightHeadMiddleNode"); - auto LTMN = m_topological_representation->getVariable("leftTailMiddleNode"); - auto RTMN = m_topological_representation->getVariable("rightTailMiddleNode"); - //auto TN = m_topological_representation->getVariable("tailNode"); - //auto HN = m_topological_representation->getVariable("headNode"); - auto LTBN = m_topological_representation->getVariable("leftTailBoundaryNode"); - auto RTBN = m_topological_representation->getVariable("rightTailBoundaryNode"); - auto LHBN = m_topological_representation->getVariable("leftHeadBoundaryNode"); - auto RHBN = m_topological_representation->getVariable("rightHeadBoundaryNode"); - auto sectionType = m_topological_representation->getVariable("section_type"); - auto nodeType = m_topological_representation->getVariable("node_type"); - for (auto s_id:m_topological_representation->edges()) - { - LHMN->set(s_id,-1); - RHMN->set(s_id,-1); - LTMN->set(s_id,-1); - RTMN->set(s_id,-1); - } - for (auto s_id:m_topological_representation->edges()) - { - if (sectionType->value(s_id) == 1) - { - Edge section = m_topological_representation->get(s_id); - if (nodeType->value(section.get()[0].id()) > 1 && nodeType->value(section.get()[1].id()) > 1) - { - // Get the node where the middle points appear - Node n; - if (wings(section.get()[0],section) == 0) - n = section.get()[0]; - else - n = section.get()[1]; - // Get the neighbouring sections - std::vector neighbours = neighbouringEdges(section,n); - // Build the medial nodes - if (orientation(n,section) == 1) - { - Node ln = m_ma_block_decomposition->get(LTBN->value(s_id)); - Node rn = m_ma_block_decomposition->get(RTBN->value(s_id)); - Node newNode; - if (LTMN->value(s_id) < 0) - { - newNode = m_ma_block_decomposition->newNode((ln.point()+n.point())*(1./2.)); - LTMN->set(s_id,newNode.id()); - // Update the neighbour middle node - if (neighbours.size() == 1) - { - Edge s = neighbours[0]; - if (orientation(n,s) == 1) - RTMN->set(s.id(),newNode.id()); - if (orientation(n,s) == -1) - LHMN->set(s.id(),newNode.id()); - } - if (neighbours.size() > 1) - { - Edge s = neighbours[1]; - if (orientation(n,s) == 1) - RTMN->set(s.id(),newNode.id()); - if (orientation(n,s) == -1) - LHMN->set(s.id(),newNode.id()); - } - } - if (RTMN->value(s_id) < 0) - { - newNode = m_ma_block_decomposition->newNode((rn.point()+n.point())*(1./2.)); - RTMN->set(s_id,newNode.id()); - // Update the neighbour middle node - if (neighbours.size() == 1) - { - Edge s = neighbours[0]; - if (orientation(n,s) == 1) - LTMN->set(s.id(),newNode.id()); - if (orientation(n,s) == -1) - RHMN->set(s.id(),newNode.id()); - } - if (neighbours.size() > 1) - { - Edge s = neighbours[0]; - if (orientation(n,s) == 1) - LTMN->set(s.id(),newNode.id()); - if (orientation(n,s) == -1) - RHMN->set(s.id(),newNode.id()); - } - } - - } - else // if (orientation(n,section) == -1) - { - Node ln = m_ma_block_decomposition->get(LHBN->value(s_id)); - Node rn = m_ma_block_decomposition->get(RHBN->value(s_id)); - Node newNode; - if (LHMN->value(s_id) < 0) - { - newNode = m_ma_block_decomposition->newNode((ln.point()+n.point())*(1./2.)); - LHMN->set(s_id,newNode.id()); - // Update the neighbour middle node - if (neighbours.size() == 1) - { - Edge s = neighbours[0]; - if (orientation(n,s) == 1) - LTMN->set(s.id(),newNode.id()); - if (orientation(n,s) == -1) - RHMN->set(s.id(),newNode.id()); - } - if (neighbours.size() > 1) - { - Edge s = neighbours[0]; - if (orientation(n,s) == 1) - LTMN->set(s.id(),newNode.id()); - if (orientation(n,s) == -1) - RHMN->set(s.id(),newNode.id()); - } - } - if (RHMN->value(s_id) < 0) - { - newNode = m_ma_block_decomposition->newNode((rn.point()+n.point())*(1./2.)); - RHMN->set(s_id,newNode.id()); - // Update the neighbour middle node - if (neighbours.size() == 1) - { - Edge s = neighbours[0]; - if (orientation(n,s) == 1) - RTMN->set(s.id(),newNode.id()); - if (orientation(n,s) == -1) - LHMN->set(s.id(),newNode.id()); - } - if (neighbours.size() > 1) - { - Edge s = neighbours[1]; - if (orientation(n,s) == 1) - RTMN->set(s.id(),newNode.id()); - if (orientation(n,s) == -1) - LHMN->set(s.id(),newNode.id()); - } - } - } - } - } - } -} - -/*----------------------------------------------------------------------------*/ -void MedialAxis2D::buildBlocks() -{ - auto TN = m_topological_representation->getVariable("tailNode"); - auto HN = m_topological_representation->getVariable("headNode"); - auto LTBN = m_topological_representation->getVariable("leftTailBoundaryNode"); - auto RTBN = m_topological_representation->getVariable("rightTailBoundaryNode"); - auto LHBN = m_topological_representation->getVariable("leftHeadBoundaryNode"); - auto RHBN = m_topological_representation->getVariable("rightHeadBoundaryNode"); - auto LHMN = m_topological_representation->getVariable("leftHeadMiddleNode"); - auto RHMN = m_topological_representation->getVariable("rightHeadMiddleNode"); - auto LTMN = m_topological_representation->getVariable("leftTailMiddleNode"); - auto RTMN = m_topological_representation->getVariable("rightTailMiddleNode"); - auto sectionType = m_topological_representation->getVariable("section_type"); - auto nodeType = m_topological_representation->getVariable("node_type"); - - int tn, hn, ltbn, rtbn, lhbn, rhbn, lhmn, rhmn, ltmn, rtmn; - for (auto s_id:m_topological_representation->edges()) - { - if (sectionType->value(s_id) == 0) - { - tn = TN->value(s_id); - hn = HN->value(s_id); - ltbn = LTBN->value(s_id); - rtbn = RTBN->value(s_id); - lhbn = LHBN->value(s_id); - rhbn = RHBN->value(s_id); - lhmn = LHMN->value(s_id); - rhmn = RHMN->value(s_id); - ltmn = LTMN->value(s_id); - rtmn = RTMN->value(s_id); - - std::vector block1; - block1.push_back(hn); - if (lhmn >= 0) - block1.push_back(lhmn); - block1.push_back(lhbn); - block1.push_back(ltbn); - if (ltmn >= 0) - block1.push_back(ltmn); - block1.push_back(tn); - m_ma_block_decomposition->newFace(block1); - - std::vector block2; - block2.push_back(tn); - if (rtmn >= 0) - block2.push_back(rtmn); - block2.push_back(rtbn); - block2.push_back(rhbn); - if (rhmn >= 0) - block2.push_back(rhmn); - block2.push_back(hn); - m_ma_block_decomposition->newFace(block2); - } - - if (sectionType->value(s_id) == 1) - { - tn = TN->value(s_id); - hn = HN->value(s_id); - ltbn = LTBN->value(s_id); - rtbn = RTBN->value(s_id); - lhbn = LHBN->value(s_id); - rhbn = RHBN->value(s_id); - lhmn = LHMN->value(s_id); - rhmn = RHMN->value(s_id); - ltmn = LTMN->value(s_id); - rtmn = RTMN->value(s_id); - Edge section = m_topological_representation->get(s_id); - - if (nodeType->value(section.get()[0].id()) == 1 || nodeType->value(section.get()[1].id()) == 1) - { - std::vector block; - block.push_back(hn); - if (lhmn >= 0) - block.push_back(lhmn); - block.push_back(lhbn+ltbn+1); - if (ltmn >= 0) - block.push_back(ltmn); - block.push_back(tn); - if (rtmn >= 0) - block.push_back(rtmn); - block.push_back(rhbn+rtbn+1); - if (rhmn >= 0) - block.push_back(rhmn); - m_ma_block_decomposition->newFace(block); - } - - else - { - m_ma_block_decomposition->newQuad(hn,lhmn+ltmn+1,tn,rhmn+rtmn+1); - if (wings(section.get()[0],section) == 1) - { - m_ma_block_decomposition->newQuad(tn,lhmn,lhbn,ltbn); - m_ma_block_decomposition->newQuad(tn,rtbn,rhbn,rhmn); - } - else - { - m_ma_block_decomposition->newQuad(hn,lhbn,ltbn,ltmn); - m_ma_block_decomposition->newQuad(hn,rtmn,rtbn,rhbn); - } - } - } - } -} - -/*----------------------------------------------------------------------------*/ -std::vector MedialAxis2D::sortedAdjacentEdges(gmds::Node &AN) -{ - std::vector adj_edges = AN.get(); - std::vector sorted_edges; - math::Vector X; - X.setX(1.); - X.setY(0.); - X.setZ(0.); - while (!adj_edges.empty()) - { - int min_pos; - double min_angle = 10.; - double angle; - for (int i = 0 ; i < adj_edges.size() ; i++) - { - Edge e = adj_edges[i]; - angle = oriented_angle(X,edge2vec(e,AN)); - if (angle < min_angle) - { - min_angle = angle; - min_pos = i; - } - } - sorted_edges.push_back(adj_edges[min_pos]); - adj_edges.erase(adj_edges.begin() + min_pos); - } - return sorted_edges; -} -/*----------------------------------------------------------------------------*/ -std::vector MedialAxis2D::neighbouringEdges(gmds::Edge &AE, gmds::Node &AN) -{ - std::vector neighbours; - std::vector sorted_edges = sortedAdjacentEdges(AN); - - int pos = -1; - for (int i = 0; i < sorted_edges.size(); i++) - { - if (sorted_edges[i].id() == AE.id()) - { - pos = i; - break; - } - } - if (pos == -1) - { - std::cout<<"neighbouringEdges() : Warning, the given node doesn't belong to the given edge"< 0 && pos < sorted_edges.size() - 1) - { - neighbours.push_back(sorted_edges[pos-1]); - neighbours.push_back(sorted_edges[pos+1]); - return neighbours; - } - if (pos == 0) - { - neighbours.push_back(sorted_edges[sorted_edges.size() - 1]); - neighbours.push_back(sorted_edges[1]); - return neighbours; - } - if (pos == sorted_edges.size() - 1) - { - neighbours.push_back(sorted_edges[pos-1]); - neighbours.push_back(sorted_edges[0]); - return neighbours; - } - -} - - /*----------------------------------------------------------------------------*/ } // end namespace medialaxis /*----------------------------------------------------------------------------*/ diff --git a/modules/medialaxis/src/MedialAxis2DBuilder.cpp b/modules/medialaxis/src/MedialAxis2DBuilder.cpp index eb9f3f764..7088cb722 100644 --- a/modules/medialaxis/src/MedialAxis2DBuilder.cpp +++ b/modules/medialaxis/src/MedialAxis2DBuilder.cpp @@ -15,14 +15,19 @@ namespace gmds { /*----------------------------------------------------------------------------*/ namespace medialaxis { /*----------------------------------------------------------------------------*/ -MedialAxis2DBuilder::MedialAxis2DBuilder(Mesh &AMesh){ +MedialAxis2DBuilder::MedialAxis2DBuilder(Mesh &AMesh, std::vector ABoundEdgesIds){ m_mesh = &AMesh; + m_boundary_edges_ids = ABoundEdgesIds; // Delaunay triangle / medial point correspondence m_mesh->newVariable("triangle2MP"); // Delaunay internal edge / medial edge correspondence m_mesh->newVariable("intEdge2MedEdge"); // Edges classification between boundary (1) and internal (0) edges m_mesh->newVariable("edge_class"); + // Mark with 1 edges corresponding to internal constraints + m_mesh->newVariable("internal_constraint"); + // Mark with 1 nodes belonging to an internal constraint + m_mesh->newVariable("belong_to_an_internal_constraint"); // Edges classification between corner (1) and non corner (0) edges m_mesh->newVariable("corner"); // Faces classification (tangency = discrete number of tangency points of the circumcenter) @@ -194,6 +199,164 @@ void MedialAxis2DBuilder::setSideId() m_mesh->deleteVariable(GMDS_EDGE, alreadyVisited); } +/*----------------------------------------------------------------------------*/ +void MedialAxis2DBuilder::markIntConstraints() +{ + std::cout<<"> Marking interior constraints"<getVariable("internal_constraint"); + auto nodes_constr = m_mesh->getVariable("belong_to_an_internal_constraint"); + // // Mean edge length + // double mean_edge_length = 0.; + + // for (auto e_id:m_mesh->edges()) + // { + // Edge e = m_mesh->get(e_id); + // mean_edge_length += e.length(); + // // Check that it is the smallest edge of all its faces + // bool isSmall = true; + // for (auto f:e.get()) + // { + // for (auto e1:f.get()) + // { + // if (e1.length() < e.length()) + // isSmall = false; + // } + // } + // if (isInterior(e.get()[0]) && isInterior(e.get()[1]) && isSmall) + // constr->set(e_id,1); + // } + + // mean_edge_length = mean_edge_length/(double(m_mesh->getNbEdges())); + + // // Look for forgotten edges + // for (auto e_id:m_mesh->edges()) + // { + // if (constr->value(e_id) == 1) + // { + // Edge e = m_mesh->get(e_id); + // for (auto n:e.get()) + // { + // for (auto e1:n.get()) + // { + // if (e1.id() != e.id()) + // { + // double alpha = oriented_angle(edge2vec(e,n),-edge2vec(e1,n)); + // double r = fabs(e1.length()- e.length())/e.length(); + // if (fabs(alpha) < 1e-4 && r < 1 && constr->value(e1.id()) == 0) + // { + // // Then we continue the contraint in the direction given by e1 + // std::queue toAdd; + // toAdd.push(e1); + // constr->set(e1.id(),1); + // while (!toAdd.empty()) + // { + // Edge current = toAdd.front(); + // toAdd.pop(); + // for (auto n1:current.get()) + // { + // for (auto e2:n1.get()) + // { + // if (e2.id() != current.id()) + // { + // alpha = oriented_angle(edge2vec(current,n1),-edge2vec(e2,n1)); + // r = fabs(current.length()- e2.length())/current.length(); + // if (fabs(alpha) < 1e-4 && r < 1 && constr->value(e2.id()) == 0) + // { + // toAdd.push(e2); + // constr->set(e2.id(),1); + // } + // } + // } + // } + // } + // } + // } + // } + // } + // } + // } + // Mark constrained edges + for (auto e_id:m_boundary_edges_ids) + { + Edge e = m_mesh->get(e_id); + if (e.get().size() == 2) + constr->set(e_id,1); + } + // Mark nodes belonging to internal constraints + for (auto e_id:m_mesh->edges()) + { + if (constr->value(e_id) == 1) + { + Edge e = m_mesh->get(e_id); + for (auto n:e.get()) + nodes_constr->set(n.id(),1); + } + } + // for (auto n_id:m_mesh->nodes()) + // { + // Node n = m_mesh->get(n_id); + // if (isInterior(n)) + // nodes_constr->set(n_id,1); + // } + // // Look for more forgotten edges + // for (auto n_id:m_mesh->nodes()) + // { + // if (nodes_constr->value(n_id) == 1) + // { + // Node n = m_mesh->get(n_id); + // int NbConstrEdges = 0; + // for (auto e:n.get()) + // { + // if (constr->value(e.id()) == 1) + // NbConstrEdges += 1; + // } + // if (NbConstrEdges <= 1) + // { + // std::vector potential_neighbours; + // for (auto e:n.get()) + // { + // for (auto n1:e.get()) + // { + // if (n1.id() != n.id() && nodes_constr->value(n1.id()) == 1 && e.length() < mean_edge_length) + // { + // int NbConstrEdges1 = 0; + // for (auto e1:n1.get()) + // { + // if (constr->value(e1.id()) == 1) + // NbConstrEdges1 += 1; + // } + // if (NbConstrEdges1 <= 1) + // { + // potential_neighbours.push_back(n1); + // } + // } + // } + // } + // if (potential_neighbours.size() > 0) + // { + // // Find the closest potential neighbour + // double min_dist = 1e6; + // double dist; + // Node closest; + // for (auto n1:potential_neighbours) + // { + // dist = n1.point().distance(n.point()); + // if (dist < min_dist) + // { + // min_dist = dist; + // closest = n1; + // } + // } + // Edge e1 = getEdge(n,closest); + // constr->set(e1.id(),1); + // } + + // } + + // } + // } +} + /*----------------------------------------------------------------------------*/ void MedialAxis2DBuilder::setMedialRadiiOrthogonalityDefault(const double &ABoundaryCurvatureTol) { @@ -223,11 +386,13 @@ void MedialAxis2DBuilder::setMedialRadiiOrthogonalityDefault(const double &ABoun } } // Get the alone point of tri and its adjacent boundary edges + Node alone_point; std::vector adj_bound_edges; for (auto n1:tri.get()) { if (n1.id() != A.id() && n1.id() != B.id()) { + alone_point = n1; P2 = n1.point(); for (auto e:n1.get()) { @@ -241,8 +406,10 @@ void MedialAxis2DBuilder::setMedialRadiiOrthogonalityDefault(const double &ABoun } if (adj_bound_edges.size() != 2) break; - E2 = adj_bound_edges[0].get()[1].point() + (-1.)*adj_bound_edges[0].get()[0].point(); - E3 = adj_bound_edges[1].get()[1].point() + (-1.)*adj_bound_edges[1].get()[0].point(); + E2 = edge2vec(adj_bound_edges[0],alone_point).getPoint(); + E3 = edge2vec(adj_bound_edges[1],alone_point).getPoint(); + // E2 = adj_bound_edges[0].get()[1].point() + (-1.)*adj_bound_edges[0].get()[0].point(); + // E3 = adj_bound_edges[1].get()[1].point() + (-1.)*adj_bound_edges[1].get()[0].point(); // Normalized medial radii math::Point R1 = P1 + (-1.)*n.point(); R1 = R1*(1./vec(R1).norm()); @@ -263,7 +430,8 @@ void MedialAxis2DBuilder::setMedialRadiiOrthogonalityDefault(const double &ABoun max_ip = ip3; // If the medial point correspond to a concave corner of the geometry (ie E2 and E3 are not aligned) // then the tangency of the medial radius doesn't have sense - if (fabs(vec(E2).dot(vec(E3))-1) > ABoundaryCurvatureTol && fabs(vec(E2).dot(vec(E3))+1) > ABoundaryCurvatureTol) + //if (fabs(vec(E2).dot(vec(E3))-1) > ABoundaryCurvatureTol && fabs(vec(E2).dot(vec(E3))+1) > ABoundaryCurvatureTol) + if (fabs(vec(E2).dot(vec(E3))+1) > ABoundaryCurvatureTol) max_ip = 0.; m_voronoi_medax->setMedialRadiusOrthogonalityDefault(n_id,max_ip); } @@ -273,57 +441,88 @@ void MedialAxis2DBuilder::setMedialRadiiOrthogonalityDefault(const double &ABoun /*----------------------------------------------------------------------------*/ void MedialAxis2DBuilder::setTouchingPoints() { + std::cout<<"> Setting tangency points"<getVariable("triangle2MP"); + auto constr = m_mesh->getVariable("internal_constraint"); for (auto f_id:m_mesh->faces()) { Face face = m_mesh->get(f_id); TCellID medPoint = triangle2MP->value(face.id()); std::vector touchingPoints; + std::vector tangentNodes; std::vector adj_edges = face.get(); // Medial point type computation int type = 0; for (auto e:adj_edges) - type += e.get().size() -1; - // If it is an end point, it has one touching point, approximated by its triangle's barycenter + { + if (constr->value(e.id()) == 0) + { + type += e.get().size() -1; + } + } + // If it is an end point, it has one touching point if (type == 1) { - math::Point P1 = face.get()[0].point(); - math::Point P2 = face.get()[1].point(); - math::Point P3 = face.get()[2].point(); - math::Point P = (P1+P2+P3)*(1./3.); - touchingPoints.push_back(P); + // Find the 2 edges of the face which are either on the boundary, or on a constraint + Edge e1,e2; + for (auto e:face.get()) + { + if (e.get().size() == 1 || constr->value(e.id()) == 1) + { + e1 = e; + break; + } + } + for (auto e:face.get()) + { + if ((e.get().size() == 1 || constr->value(e.id()) == 1) && e.id() != e1.id()) + { + e2 = e; + break; + } + } + // Find the vertex of the face which is a corner, ie adjacent to only one triangle + Node corner = getCommonNode(e1,e2); + tangentNodes.push_back(corner); + touchingPoints.push_back(corner.point()); } // If it is an intersection point, all its triangle points are touching points if (type >= 3) { for (auto P:face.get()) + { + tangentNodes.push_back(P); touchingPoints.push_back(P.point()); + } } // If it is a regular point, it has two touching points, approximated by the barycenter of the two // neighbouring points and the remaining point of its triangle if (type == 2) { - // Find the only boundary edge of the triangle + // Find the only boundary or constraint edge of the triangle Edge e; for (auto edge:face.get()) { - if (edge.get().size() == 1) + if (edge.get().size() == 1 || constr->value(edge.id()) == 1) { e = edge; break; } } math::Point P1 = (e.get()[0].point()+e.get()[1].point())*(1./2.); + tangentNodes.push_back(e.get()[0]); touchingPoints.push_back(P1); - math::Point P2; + Node P2; for (auto n:face.get()) { if (n.id() != e.get()[0].id() && n.id() != e.get()[1].id()) - P2 = n.point(); + P2 = n; } - touchingPoints.push_back(P2); + tangentNodes.push_back(P2); + touchingPoints.push_back(P2.point()); } m_voronoi_medax->setTouchingPoints(medPoint,touchingPoints); + m_voronoi_medax->setTangentNodes(medPoint,tangentNodes); } } @@ -402,6 +601,7 @@ void MedialAxis2DBuilder::buildVoronoiMedialPointsAndEdges() // Delaunay triangle / medial point correspondence auto medial_point = m_mesh->getVariable("triangle2MP"); auto edge_correspondance = m_mesh->getVariable("intEdge2MedEdge"); + auto constr = m_mesh->getVariable("internal_constraint"); // Adding the medial points for (auto n_id:m_mesh->faces()){ Face f = m_mesh->get(n_id); @@ -411,20 +611,30 @@ void MedialAxis2DBuilder::buildVoronoiMedialPointsAndEdges() Node n = m_voronoi_medax->newMedPoint(MP); medial_point->set(f.id(), n.id()); m_voronoi_medax->setPrimalTriangleID(n.id(),f.id()); + m_voronoi_medax->setDualTriangles(n.id(),{f}); + // Test of the valdity of the circumcenter + // double d1 = MP.distance(nf[0].point()); + // double d2 = MP.distance(nf[1].point()); + // double d3 = MP.distance(nf[2].point()); + // if (fabs(d1-d2)+fabs(d1-d3)+fabs(d3-d2) > 1e-5) + // std::cout<<"buildVoronoiMedialPointsAndEdges() : WARNING, THE CIRCUMCENTER IS IN THE WRONG PLACE"<edges()) { - Edge e = m_mesh->get(e_id); - std::vector adj_faces = e.get(); - if (adj_faces.size() == 2) + if (constr->value(e_id) == 0) { - Face f1 = adj_faces[0]; - Face f2 = adj_faces[1]; - TCellID mp_id1 = medial_point->value(f1.id()); - TCellID mp_id2 = medial_point->value(f2.id()); - Edge me = m_voronoi_medax->newMedEdge(mp_id1, mp_id2); - edge_correspondance->set(e.id(),me.id()); + Edge e = m_mesh->get(e_id); + std::vector adj_faces = e.get(); + if (adj_faces.size() == 2) + { + Face f1 = adj_faces[0]; + Face f2 = adj_faces[1]; + TCellID mp_id1 = medial_point->value(f1.id()); + TCellID mp_id2 = medial_point->value(f2.id()); + Edge me = m_voronoi_medax->newMedEdge(mp_id1, mp_id2); + edge_correspondance->set(e.id(),me.id()); + } } } } @@ -434,6 +644,13 @@ void MedialAxis2DBuilder::buildVoronoiMedialAxis() { std::cout<<" "<setMedialEdgeType(); - + // Set the branches IDs on the edges m_voronoi_medax->setBranchIdOnEdges(); - + // Set the branches IDs on the points m_voronoi_medax->setBranchIdOnPoints(); - + // Set the branches type on the edges m_voronoi_medax->setBranchTypeOnEdges(); - + // Set the branches type on the points m_voronoi_medax->setBranchTypeOnPoints(); - + std::cout<<"NB medial points : "<getNbMedPoints()<minMedEdgeLength()<maxMedEdgeLength()<> AGroups) +std::vector> MedialAxis2DBuilder::boundaryArcs(std::vector ABoundNodes) { + auto constr = m_mesh->getVariable("internal_constraint"); + bool isACircle = true; + // Mark the nodes of the set + auto set = m_mesh->newVariable("is_part_of_the_set"); + for (auto n:ABoundNodes) + { + set->set(n.id(),1); + } + auto visited = m_mesh->newVariable("visited"); + std::vector> arcs; + std::vector arc; + for (auto n:ABoundNodes) + { + if (visited->value(n.id()) == 0) + { + int NbNeighbours = 0; + for (auto e:n.get()) + { + if (e.get().size() == 1 || constr->value(e.id()) == 1) + { + for (auto n1:e.get()) + { + if (set->value(n1.id()) == 1 && n1.id() != n.id()) + NbNeighbours += 1; + } + } + } + if (NbNeighbours == 1) + { + // if we are here, it means that the input arc is not a circle + isACircle = false; + // Then we build a new arc + arc.clear(); + std::queue toAdd; + toAdd.push(n); + visited->set(n.id(),1); + while (!toAdd.empty()) + { + Node n = toAdd.front(); + toAdd.pop(); + arc.push_back(n); + for (auto e:n.get()) + { + if (e.get().size() == 1 || constr->value(e.id()) == 1) + { + for (auto n1:e.get()) + { + if (set->value(n1.id()) == 1 && visited->value(n1.id()) == 0) + { + toAdd.push(n1); + visited->set(n1.id(),1); + } + } + } + } + } + arcs.push_back(arc); + } + } + + } + + if (isACircle) + { + std::vector ordered_nodes; + std::stack toAdd; + toAdd.push(ABoundNodes[0]); + visited->set(ABoundNodes[0].id(),1); + while (!toAdd.empty()) + { + Node n = toAdd.top(); + toAdd.pop(); + ordered_nodes.push_back(n); + for (auto e:n.get()) + { + if (e.get().size() == 1 || constr->value(e.id()) == 1) + { + for (auto n1:e.get()) + { + if (set->value(n1.id()) == 1 && visited->value(n1.id()) == 0) + { + toAdd.push(n1); + visited->set(n1.id(),1); + } + } + } + } + } + int half = ordered_nodes.size()/2; + int quarter = half/2; + arc.clear(); + for (int i = 0; i <= quarter; i++) + arc.push_back(ordered_nodes[i]); + arcs.push_back(arc); + arc.clear(); + for (int i = quarter; i <= half; i++) + arc.push_back(ordered_nodes[i]); + arcs.push_back(arc); + arc.clear(); + for (int i = half; i <= half+quarter; i++) + arc.push_back(ordered_nodes[i]); + arcs.push_back(arc); + arc.clear(); + for (int i = half+quarter; i < ordered_nodes.size(); i++) + arc.push_back(ordered_nodes[i]); + arc.push_back(ordered_nodes[0]); + arcs.push_back(arc); + } + + // Add the forgotten arcs + for (auto n:ABoundNodes) + { + if (visited->value(n.id()) == 0) + { + visited->set(n.id(),1); + arc.clear(); + arc.push_back(n); + arcs.push_back(arc); + } + } + + m_mesh->deleteVariable(GMDS_NODE,set); + m_mesh->deleteVariable(GMDS_NODE,visited); + + return arcs; +} + +/*----------------------------------------------------------------------------*/ +std::vector MedialAxis2DBuilder::boundaryArc(std::vector ABoundNodes) +{ + // Mark the nodes of the set + auto set = m_mesh->newVariable("is_part_of_the_set"); + for (auto n:ABoundNodes) + { + set->set(n.id(),1); + } + auto visited = m_mesh->newVariable("visited"); + // Find an extremal node of the group + Node extr; + for (auto n:ABoundNodes) + { + int NbNeighbours = 0; + for (auto e:n.get()) + { + if (e.get().size() == 1) + { + for (auto n1:e.get()) + { + if (set->value(n1.id()) == 1 && n1.id() != n.id()) + NbNeighbours += 1; + } + } + + } + if (NbNeighbours == 1) + { + extr = n; + break; + } + } + // Create the arc + std::vector arc; + std::queue toAdd; + toAdd.push(extr); + visited->set(extr.id(),1); + while (!toAdd.empty()) + { + Node n = toAdd.front(); + toAdd.pop(); + arc.push_back(n); + for (auto e:n.get()) + { + if (e.get().size() == 1) + { + for (auto n1:e.get()) + { + if (set->value(n1.id()) == 1 && visited->value(n1.id()) == 0) + { + toAdd.push(n1); + visited->set(n1.id(),1); + } + } + } + } + } + m_mesh->deleteVariable(GMDS_NODE,set); + m_mesh->deleteVariable(GMDS_NODE,visited); + + return arc; +} + +/*----------------------------------------------------------------------------*/ +void MedialAxis2DBuilder::unfoldMedax(Node AN, std::vector ABoundaryArc, bool AIsADangle) +{ + if (ABoundaryArc.size() < 3) + { + std::vector t = m_smoothed_medax->getTangentNodes(AN.id()); + bool already_here = false; + for (auto n:t) + { + if (n.id() == ABoundaryArc[0].id()) + { + already_here = true; + break; + } + } + if (!already_here) + t.push_back(ABoundaryArc[0]); + std::vector p; + for (auto n:t) + p.push_back(n.point()); + m_smoothed_medax->setTouchingPoints(AN.id(),p); + m_smoothed_medax->setTangentNodes(AN.id(),t); + m_smoothed_medax->setDangle(AN.id(),AIsADangle); + } + else + { + // Middle position + int N = ABoundaryArc.size()/2; + int NbNewNodes; + if (ABoundaryArc.size()%2 == 0) + NbNewNodes = N-1; + else + NbNewNodes = N; + // End node + Node end = ABoundaryArc[N]; + // Direction + math::Vector dir = vec(end.point()+(-1.)*AN.point()); + double r = dir.norm(); + dir = dir/r; + // Step + double step = (r*0.75)/double(NbNewNodes); + // Create the new nodes and edges + Node new_node, prev, boundNode1, boundNode2; + std::vector tangentNodes; + std::vector tangentPoints; + Edge new_edge; + prev = AN; + boundNode1 = ABoundaryArc[0]; + boundNode2 = ABoundaryArc[ABoundaryArc.size()-1]; + tangentNodes = m_smoothed_medax->getTangentNodes(prev.id()); + tangentPoints = m_smoothed_medax->getTouchingPoints(prev.id()); + bool added1 = false; + bool added2 = false; + for (auto tn:tangentNodes) + { + if (tn.id() == boundNode1.id()) + added1 = true; + if (tn.id() == boundNode2.id()) + added2 = true; + } + if (!added1) + { + tangentNodes.push_back(boundNode1); + tangentPoints.push_back(boundNode1.point()); + } + if (!added2) + { + tangentNodes.push_back(boundNode2); + tangentPoints.push_back(boundNode2.point()); + } + m_smoothed_medax->setTangentNodes(prev.id(),tangentNodes); + m_smoothed_medax->setTouchingPoints(prev.id(),tangentPoints); + m_smoothed_medax->setMedialRadius(prev.id(),prev.point().distance(boundNode1.point())); + for (int i = 1; i < NbNewNodes; i++) + { + new_node = m_smoothed_medax->newMedPoint(AN.point()+double(i)*step*dir); + tangentNodes.clear(); + tangentPoints.clear(); + boundNode1 = ABoundaryArc[i]; + boundNode2 = ABoundaryArc[ABoundaryArc.size()-i]; + tangentNodes.push_back(boundNode1); + tangentNodes.push_back(boundNode2); + tangentPoints.push_back(boundNode1.point()); + tangentPoints.push_back(boundNode2.point()); + m_smoothed_medax->setTangentNodes(new_node.id(),tangentNodes); + m_smoothed_medax->setTouchingPoints(new_node.id(),tangentPoints); + m_smoothed_medax->setMedialRadius(new_node.id(),new_node.point().distance(boundNode1.point())); + m_smoothed_medax->markAsExtension(new_node.id()); + new_edge = m_smoothed_medax->newMedEdge(prev.id(),new_node.id()); + prev = new_node; + } + // new_node = m_smoothed_medax->newMedPoint(end.point()); + new_node = m_smoothed_medax->newMedPoint(AN.point()+double(NbNewNodes)*step*dir); + tangentNodes.clear(); + tangentPoints.clear(); + boundNode1 = ABoundaryArc[N]; + tangentNodes.push_back(boundNode1); + tangentPoints.push_back(boundNode1.point()); + m_smoothed_medax->setTangentNodes(new_node.id(),tangentNodes); + m_smoothed_medax->setTouchingPoints(new_node.id(),tangentPoints); + // m_smoothed_medax->setMedialRadius(new_node.id(),0.); + m_smoothed_medax->setMedialRadius(new_node.id(),new_node.point().distance(end.point())); + m_smoothed_medax->setDangle(new_node.id(),AIsADangle); + if (NbNewNodes > 1) + m_smoothed_medax->markAsExtension(new_node.id()); + new_edge = m_smoothed_medax->newMedEdge(prev.id(),new_node.id()); + } +} + +/*----------------------------------------------------------------------------*/ +void MedialAxis2DBuilder::buildSmoothedMedaxFromVoronoi(const std::vector> AGroups) +{ std::cout<<" "<newMedPoint(AGroups[groupID][0].point()); + Node n0 = AGroups[groupID][0]; + for (auto n:AGroups[groupID]) + { + if (m_voronoi_medax->getMedialPointType(n.id()) > 2) + n0 = n; + } + Node newPoint = m_smoothed_medax->newMedPoint(n0.point()); group2NewMedPoint[groupID] = newPoint.id(); } + // Valence of the medial point associated to each group + std::vector valence(AGroups.size()); + for (int i = 0; i < valence.size(); i++) + valence[i] = 0; // Build the medial edges for (int groupID=0; groupIDnewMedEdge(group2NewMedPoint[groupID2],group2NewMedPoint[groupID]); + valence[groupID] = valence[groupID] + 1; + valence[groupID2] = valence[groupID2] + 1; } } - // Set the touching points, medial radii and medial radii orthogonality default + // Set the touching points, tangent nodes, medial radii and medial radii orthogonality default, potentially unfold medial points corresponding to circles centers for (int groupID=0; groupIDgetMedialPointType(n.id()) > 2) oldPoint = n.id(); + if (m_voronoi_medax->getMedialPointType(n.id()) == 1) + oldPoint = n.id(); + } + // Computing touching points + // Nb of intersection points and end points in the group + int NbIPs = 0; + int NbEPs = 0; + for (auto node:AGroups[groupID]) + { + if (m_voronoi_medax->getTouchingPoints(node.id()).size() > 2) + NbIPs += 1; + if (m_voronoi_medax->getTouchingPoints(node.id()).size() == 1) + NbEPs += 1; + } + if (NbEPs > 0) + { + // std::vector arc; + // for (auto n:AGroups[groupID]) + // { + // for (auto tangentNode:m_mesh->get(n.id()).get()) + // arc.push_back(tangentNode); + // } + // arc = boundaryArc(arc); + // unfoldMedax(m_smoothed_medax->getMedPoint(newPoint),arc); + bool isADangle = (NbEPs >= 2); + std::vector nodes; + for (auto n:AGroups[groupID]) + { + for (auto tangentNode:m_mesh->get(n.id()).get()) + nodes.push_back(tangentNode); + } + std::vector> arcs = boundaryArcs(nodes); + for (auto arc:arcs) + unfoldMedax(m_smoothed_medax->getMedPoint(newPoint),arc, isADangle); + } + if (NbIPs == 0 && NbEPs == 0) + { + m_smoothed_medax->setTouchingPoints(newPoint, m_voronoi_medax->getTouchingPoints(oldPoint)); + m_smoothed_medax->setTangentNodes(newPoint, m_voronoi_medax->getTangentNodes(oldPoint)); + } + if (NbIPs == 1 && NbEPs == 0) + { + for (auto node:AGroups[groupID]) + { + if (m_voronoi_medax->getTouchingPoints(node.id()).size() > 2) + { + oldPoint = node.id(); + break; + } + } + m_smoothed_medax->setTouchingPoints(newPoint, m_voronoi_medax->getTouchingPoints(oldPoint)); + m_smoothed_medax->setTangentNodes(newPoint, m_voronoi_medax->getTangentNodes(oldPoint)); + } + if (NbIPs >= 2 && NbEPs == 0) + { + std::vector intersection_points; + for (auto node:AGroups[groupID]) + { + if (m_voronoi_medax->getTouchingPoints(node.id()).size() > 2) + intersection_points.push_back(node.id()); + } + // std::vector merged_tangency_points; + // for (auto id:intersection_points) + // merged_tangency_points = merge(merged_tangency_points,m_voronoi_medax->getTouchingPoints(id)); + // m_smoothed_medax->setTouchingPoints(newPoint,merged_tangency_points); + std::vector merged_tangent_nodes; + for (auto id:intersection_points) + { + for (auto n:m_voronoi_medax->getTangentNodes(id)) + { + bool already_added = false; + for (auto n1:merged_tangent_nodes) + { + if (n1.id() == n.id()) + { + already_added = true; + break; + } + } + if (!already_added) + merged_tangent_nodes.push_back(n); + } + } + // Check if the number of tangency nodes is correct + if (merged_tangent_nodes.size() > valence[groupID]) + { + while (merged_tangent_nodes.size() > valence[groupID]) + { + // Remove one of the two closest nodes of the set + double min_dist = 1e6; + int min_pos; + for (int i = 0; i < merged_tangent_nodes.size(); i++) + { + for (int j = 0; j < merged_tangent_nodes.size(); j++) + { + if (i != j) + { + double dist = merged_tangent_nodes[i].point().distance(merged_tangent_nodes[j].point()); + if (dist < min_dist) + { + min_dist = dist; + min_pos = i; + } + } + } + } + merged_tangent_nodes.erase(merged_tangent_nodes.begin()+min_pos); + } + } + m_smoothed_medax->setTangentNodes(newPoint,merged_tangent_nodes); + std::vector merged_tangency_points; + for (auto n:merged_tangent_nodes) + merged_tangency_points.push_back(n.point()); + m_smoothed_medax->setTouchingPoints(newPoint,merged_tangency_points); + } + // Dual triangles of the new point + std::vector dual_tri; + for (auto node:AGroups[groupID]) + dual_tri.push_back(m_mesh->get(node.id())); + m_smoothed_medax->setDualTriangles(newPoint,dual_tri); + if (NbEPs == 0) + { + m_smoothed_medax->setMedialRadius(newPoint, m_voronoi_medax->getMedialRadius(oldPoint)); + m_smoothed_medax->setMedialRadiusOrthogonalityDefault(newPoint, m_voronoi_medax->getMedialRadiusOrthogonalityDefault(oldPoint)); } - // The touching points updated as below are not correct for new points that change type, for example passing from type 3 to type 4 - m_smoothed_medax->setTouchingPoints(newPoint, m_voronoi_medax->getTouchingPoints(oldPoint)); - m_smoothed_medax->setMedialRadius(newPoint, m_voronoi_medax->getMedialRadius(oldPoint)); - m_smoothed_medax->setMedialRadiusOrthogonalityDefault(newPoint, m_voronoi_medax->getMedialRadiusOrthogonalityDefault(oldPoint)); } // Refining the medial axis @@ -624,6 +1276,9 @@ void MedialAxis2DBuilder::placeSingularities() // Place singularities m_smoothed_medax->placeSingularities(5.0); + // Remove singularity dipoles + m_smoothed_medax->removeSingularityDipoles(); + // Check singularities m_smoothed_medax->checkSingularities(0.1); @@ -883,7 +1538,11 @@ MedialAxis2DBuilder::execute() buildVoronoiMedialAxis(); // Smoothed medial axis - std::vector> groups = m_voronoi_medax->medialPointsGroups(m_voronoi_medax->meanMedEdgeLength()/2.); + double tol = 1e-6; + double mean_edge_length = m_voronoi_medax->meanMedEdgeLength()/10.; + if (tol < mean_edge_length) + tol = mean_edge_length; + std::vector> groups = m_voronoi_medax->medialPointsGroups(tol); buildSmoothedMedaxFromVoronoi(groups); // Identify details on geometry using the (Voronoï) medial axis @@ -893,7 +1552,7 @@ MedialAxis2DBuilder::execute() placeSingularities(); // Connect the connected components of the boundary using the medial axis - connectBoundaryConnectedComponents(); + //connectBoundaryConnectedComponents(); return MedialAxis2DBuilder::SUCCESS; diff --git a/modules/medialaxis/src/MedialAxisMath.cpp b/modules/medialaxis/src/MedialAxisMath.cpp index 3e316070d..ce17e9e28 100644 --- a/modules/medialaxis/src/MedialAxisMath.cpp +++ b/modules/medialaxis/src/MedialAxisMath.cpp @@ -4,6 +4,8 @@ #include "gmds/medialaxis/MedialAxisMath.h" #include +#include +#include using namespace gmds; /*----------------------------------------------------------------------------*/ @@ -22,9 +24,9 @@ double maxRange(const math::Point& ACenter1, const double& ARadius1, const math: double oriented_angle(const math::Vector &AV1, const math::Vector &AV2) { double cos_theta = AV1.dot(AV2)/(AV1.norm()*AV2.norm()); - if (fabs(cos_theta-1.) < 10e-6) + if (fabs(cos_theta-1.) < 1e-10) return 0.; - if (fabs(cos_theta+1.) < 10e-6) + if (fabs(cos_theta+1.) < 1e-10) return M_PI; math::Vector AV3; AV3.setX(-AV1.Y()); @@ -72,24 +74,45 @@ std::vector sortEdges(Node &AN, std::vector &AV) double angle = oriented_angle(vec(X), edge2vec(e,AN)); angles.push_back(angle); } - std::vector sorted_edges; - double prev_min = -10.; - for (int i = 1; i <= AV.size(); i++) + + + + + // double prev_min = -10.; + // for (int i = 1; i <= AV.size(); i++) + // { + // // Find the ith min + // double min_angle = 10.; + // int min_pos; + // for (int j = 0; j < angles.size(); j++) + // { + // if (angles[j] < min_angle && angles[j] > prev_min) + // { + // min_angle = angles[j]; + // min_pos = j; + // } + // } + // prev_min = min_angle; + // sorted_edges.push_back(AV[min_pos]); + // } + std::vector edges = AV; + double min_angle; + int min_pos; + while (!edges.empty()) { - // Find the ith min - double min_angle = 10.; - int min_pos; - for (int j = 0; j < angles.size(); j++) + min_angle = 10.; + for (int j = 0; j < edges.size(); j++) { - if (angles[j] < min_angle && angles[j] > prev_min) + if (angles[j] < min_angle) { - min_angle = angles[j]; min_pos = j; + min_angle = angles[j]; } } - prev_min = min_angle; - sorted_edges.push_back(AV[min_pos]); + sorted_edges.push_back(edges[min_pos]); + edges.erase(edges.begin()+min_pos); + angles.erase(angles.begin()+min_pos); } return sorted_edges; } @@ -182,7 +205,8 @@ std::vector orientateEdges(Face &AF) } } angle = oriented_angle(edge2vec(e1,node), edge2vec(e2,node)); - if (fabs(fabs(angle)-M_PI/2.) < M_PI/4) + // if (fabs(fabs(angle)-M_PI/2.) < M_PI/4) + if (fabs(fabs(angle)-M_PI) > 1e-3 && fabs(angle) > 1e-3) { n0 = node; break; @@ -309,4 +333,247 @@ std::vector> groupsOfAlignedEdges(Face &AF) groups.push_back(newGroup); } return groups; +} + +/*----------------------------------------------------------------------------*/ +bool isOnSegment(math::Point AP0, math::Point AP1, math::Point AP2) +{ + math::Point U1 = AP1+(-1.)*AP0; + math::Point U2 = AP2+(-1.)*AP0; + if (vec(U1).norm() < 10e-6) + return true; + if (vec(U2).norm() < 10e-6) + return true; + double alpha = oriented_angle(vec(U1),vec(U2)); + return (fabs(alpha-M_PI)<10e-6 || fabs(alpha+M_PI)<10e-6); +} + +/*----------------------------------------------------------------------------*/ +std::vector insertPoint(Node AN, std::vector AV) +{ + std::vector new_list; + math::Point P1,P2; + bool added = false; + for (int i = 0; i < AV.size(); i++) + { + if (added) + new_list.push_back(AV[i].id()); + else + { + P2 = AV[i].point(); + if (i > 0) + P1 = AV[i-1].point(); + else + P1 = AV[AV.size()-1].point(); + if (isOnSegment(AN.point(),P1,P2)) + { + new_list.push_back(AN.id()); + added = true; + new_list.push_back(AV[i].id()); + } + else + new_list.push_back(AV[i].id()); + } + } + return new_list; +} + +/*----------------------------------------------------------------------------*/ +std::vector merge(std::vector AV1, std::vector AV2) +{ + std::vector merged; + for (auto point:AV1) + merged.push_back(point); + for (auto point:AV2) + { + bool already_seen = false; + for (auto point2:AV1) + { + if (point.distance(point2) < 10e-6) + { + already_seen = true; + break; + } + } + if (!already_seen) + merged.push_back(point); + } + return merged; +} + +/*----------------------------------------------------------------------------*/ +std::vector order(std::vector AVE, std::vector AVX) +{ + std::vector sorted_edges; + while (!AVX.empty()) + { + int min_pos; + double min_x = 10e6; + double x; + for (int i = 0 ; i < AVX.size() ; i++) + { + x = AVX[i]; + if (x < min_x) + { + min_x =x; + min_pos = i; + } + } + sorted_edges.push_back(AVE[min_pos]); + AVX.erase(AVX.begin() + min_pos); + AVE.erase(AVE.begin() + min_pos); + } + return sorted_edges; +} + +/*----------------------------------------------------------------------------*/ +Edge getEdge(Node &AN1, Node &AN2) +{ + Edge e; + for (auto e1:AN1.get()) + { + for (auto e2:AN2.get()) + { + if (e1.id() == e2.id()) + { + e = e1; + return e; + } + } + } + throw GMDSException("getEdge() : the two given nodes have no edge in common"); +} + +/*----------------------------------------------------------------------------*/ +bool isInterior(Node &AN) +{ + bool isInt = true; + for (auto e:AN.get()) + { + if (e.get().size() == 1) + { + isInt = false; + break; + } + } + return isInt; +} + +/*----------------------------------------------------------------------------*/ +double delimitedArea(std::vector AV) +{ + double x = 0.; + double y = 0.; + // Compute the barycenter of the points + for (auto e:AV) + { + for (auto n:e.get()) + { + x += 1./2.*n.X(); + y += 1./2.*n.Y(); + } + } + x = x/double(AV.size()); + y = y/double(AV.size()); + math::Point Bar(x,y,0.); + // Compute the area + double area = 0.; + for (auto e:AV) + { + math::Triangle T(e.get()[0].point(),e.get()[1].point(),Bar); + area += T.area(); + } + return area; +} + +/*----------------------------------------------------------------------------*/ +bool touchesBoundary(Edge &AE) +{ + return (!isInterior(AE.get()[0]) || !isInterior(AE.get()[1])); +} + +/*----------------------------------------------------------------------------*/ +Face getCommonFace(Edge &AE1, Edge &AE2) +{ + for (auto f1:AE1.get()) + { + for (auto f2:AE2.get()) + { + if (f1.id() == f2.id()) + { + return f1; + } + } + } + throw GMDSException("getCommonFace() : the two input edges have no face in common"); +} + +/*----------------------------------------------------------------------------*/ +math::Vector projection(math::Vector &AV1, math::Vector &AV2) +{ + return (AV1.dot(AV2)/AV1.norm()*(AV1/AV1.norm())); +} + +/*----------------------------------------------------------------------------*/ +std::vector shortestPathAlongBoundaryOrConstraints(Node &AN1, Node &AN2, Mesh &AMesh) +{ + auto constr = AMesh.getVariable("internal_constraint"); + auto visited = AMesh.newVariable("visited"); + std::vector path; + std::vector new_path; + std::vector shortestPath; + path.push_back(AN1); + visited->set(AN1.id(),1); + std::queue> toBeContinued; + toBeContinued.push(path); + bool finished = false; + + while (!toBeContinued.empty() && !finished) + { + path = toBeContinued.front(); + toBeContinued.pop(); + Node last_node = path[path.size()-1]; + if (last_node.id() == AN2.id()) + { + shortestPath = path; + finished = true; + } + else + { + for (auto e:last_node.get()) + { + if (e.get().size() == 1 || constr->value(e.id()) == 1) + { + for (auto n:e.get()) + { + if (visited->value(n.id()) == 0) + { + new_path = path; + new_path.push_back(n); + visited->set(n.id(),1); + toBeContinued.push(new_path); + } + } + } + } + } + } + + AMesh.deleteVariable(GMDS_NODE,visited); + return shortestPath; +} + +/*----------------------------------------------------------------------------*/ +Edge opp(Face AFace, Edge AEdge) +{ + for (auto e:AFace.get()) + { + if (e.get()[0].id() != AEdge.get()[0].id() && e.get()[0].id() != AEdge.get()[1].id()) + { + if (e.get()[1].id() != AEdge.get()[0].id() && e.get()[1].id() != AEdge.get()[1].id()) + return e; + } + } + Edge edge; + return edge; } \ No newline at end of file diff --git a/modules/medialaxis/src/MinDelaunayCleaner.cpp b/modules/medialaxis/src/MinDelaunayCleaner.cpp new file mode 100644 index 000000000..5b7cf89cc --- /dev/null +++ b/modules/medialaxis/src/MinDelaunayCleaner.cpp @@ -0,0 +1,296 @@ +#include "gmds/medialaxis/MinDelaunayCleaner.h" +#include +/*----------------------------------------------------------------------------*/ +namespace gmds { +/*----------------------------------------------------------------------------*/ +MinDelaunayCleaner::MinDelaunayCleaner(gmds::Mesh &AMesh) +{ + // Input minimal Delaunay trianglulation + m_mesh = &AMesh; + // Face type + m_mesh->newVariable("face_type"); + // Medial points + m_mesh->newVariable("medial_point"); + // Medial radii + m_mesh->newVariable("medial_radius"); + // Medial radii + m_mesh->newVariable("medial_branch_id"); + // Faces to delete are marked with 1 + m_mesh->newVariable("is_to_keep"); + // Small edges are marked with 1 + m_mesh->newVariable("is_small"); + + // Cleaned mesh + m_cleaned_mesh = new Mesh(MeshModel(DIM3 | E | N | F | + E2N | N2E | F2N | N2F | F2E | E2F)); + +} + +/*----------------------------------------------------------------------------*/ +void MinDelaunayCleaner::setFacesTypes() +{ + auto face_type = m_mesh->getVariable("face_type"); + for (auto f_id:m_mesh->faces()) + { + Face f = m_mesh->get(f_id); + int type = 0; + for (auto e:f.get()) + type += e.get().size()-1; + face_type->set(f_id,type); + } +} + +/*----------------------------------------------------------------------------*/ +void MinDelaunayCleaner::computeMedialPointsAndRadii() +{ + auto mp = m_mesh->getVariable("medial_point"); + auto mr = m_mesh->getVariable("medial_radius"); + for (auto f_id:m_mesh->faces()) + { + Face f = m_mesh->get(f_id); + math::Triangle T(f.get()[0].point(),f.get()[1].point(),f.get()[2].point()); + math::Point MP = T.getCircumcenter(); + double r = MP.distance(f.get()[0].point()); + mp->set(f_id,MP); + mr->set(f_id,r); + } +} + +/*----------------------------------------------------------------------------*/ +std::vector MinDelaunayCleaner::medialBranch(Face &AF) +{ + auto face_type = m_mesh->getVariable("face_type"); + std::vector medial_branch; + if (face_type->value(AF.id()) == 3) + return medial_branch; + std::queue toAdd; + Face current = AF; + toAdd.push(current); + auto visited = m_mesh->newVariable("visited"); + visited->set(current.id(),1); + while(!toAdd.empty()) + { + current = toAdd.front(); + toAdd.pop(); + medial_branch.push_back(current); + for (auto e:current.get()) + { + for (auto f:e.get()) + { + if (f.id() != current.id() && face_type->value(f.id()) != 3 && visited->value(f.id()) == 0) + { + toAdd.push(f); + visited->set(f.id(),1); + } + } + } + } + m_mesh->deleteVariable(GMDS_FACE,visited); + return medial_branch; +} + +/*----------------------------------------------------------------------------*/ +void MinDelaunayCleaner::setBranchID() +{ + auto id = m_mesh->getVariable("medial_branch_id"); + for (auto f_id:m_mesh->faces()) + id->set(f_id,-1); + int ID = 0; + for (auto f_id:m_mesh->faces()) + { + if (id->value(f_id) == -1) + { + Face f = m_mesh->get(f_id); + std::vector branch = medialBranch(f); + for (auto f1:branch) + id->set(f1.id(),ID); + ID += 1; + } + } +} + +/*----------------------------------------------------------------------------*/ +void MinDelaunayCleaner::markSmallEdges() +{ + auto is_small = m_mesh->getVariable("is_small"); + double mean_len = 0.; + for (auto e_id:m_mesh->edges()) + { + Edge e = m_mesh->get(e_id); + mean_len += e.length(); + } + mean_len = mean_len/double(m_mesh->getNbEdges()); + for (auto e_id:m_mesh->edges()) + { + Edge e = m_mesh->get(e_id); + // if (e.length() < mean_len/2.) + if (e.length() < mean_len/1.25) + is_small->set(e_id,1); + } +} + +/*----------------------------------------------------------------------------*/ +void MinDelaunayCleaner::markFacesToDelete() +{ + auto is_to_keep = m_mesh->getVariable("is_to_keep"); + auto face_type = m_mesh->getVariable("face_type"); + auto is_small = m_mesh->getVariable("is_small"); + // Mark big intersection triangles + auto big_itri = m_mesh->newVariable("is_a_big_itri"); + for (auto f_id:m_mesh->faces()) + { + if (face_type->value(f_id) == 3) + { + Face f = m_mesh->get(f_id); + bool isBig = true; + for (auto e:f.get()) + { + if (is_small->value(e.id()) == 1) + { + isBig = false; + break; + } + } + if (isBig) + big_itri->set(f_id,1); + } + } + // Check if there is at least one big intersection triangle. If not, artificially add one + bool valid = false; + for (auto f_id:m_mesh->faces()) + { + if (big_itri->value(f_id) == 1) + { + valid = true; + break; + } + } + if (!valid) + { + for (auto f_id:m_mesh->faces()) + { + big_itri->set(f_id,1); + break; + } + } + // Find the triangles to keep from the big intersection triangles + for (auto f_id:m_mesh->faces()) + { + if (big_itri->value(f_id) == 1) + { + Face f = m_mesh->get(f_id); + is_to_keep->set(f_id,1); + for (auto e:f.get()) + { + for (auto f1:e.get()) + { + if (f1.id() != f.id() && is_to_keep->value(f1.id()) == 0) + { + Face current_face = f1; + Edge current_edge = e; + is_to_keep->set(f1.id(),1); + while (big_itri->value(current_face.id()) == 0 && face_type->value(current_face.id()) != 1) + { + // We go through the biggest edge of current_face + Edge biggest_edge; + double l = 0; + for (auto e1:current_face.get()) + { + if (e1.id() != current_edge.id() && e1.length() > l && e1.get().size() == 2) + { + biggest_edge = e1; + l = e1.length(); + } + } + current_edge = biggest_edge; + for (auto f2:current_edge.get()) + { + if (f2.id() != current_face.id()) + { + current_face = f2; + break; + } + } + is_to_keep->set(current_face.id(),1); + } + } + } + } + } + } + + + m_mesh->deleteVariable(GMDS_FACE,big_itri); + +} + +/*----------------------------------------------------------------------------*/ +void MinDelaunayCleaner::buildCleanedMesh() +{ + auto is_to_keep = m_mesh->getVariable("is_to_keep"); + // Mark the nodes to keep + auto node_to_keep = m_mesh->newVariable("node_to_keep"); + for (auto f_id:m_mesh->faces()) + { + if (is_to_keep->value(f_id) == 1) + { + Face f = m_mesh->get(f_id); + for (auto n:f.get()) + node_to_keep->set(n.id(),1); + } + } + + // Build the new nodes + auto old2new = m_mesh->newVariable("old2new"); + for (auto n_id:m_mesh->nodes()) + { + if (node_to_keep->value(n_id) == 1) + { + Node n = m_mesh->get(n_id); + Node newNode = m_cleaned_mesh->newNode(n.point()); + old2new->set(n_id,newNode.id()); + } + } + + // Build the new faces + for (auto f_id:m_mesh->faces()) + { + if (is_to_keep->value(f_id) == 1) + { + Face f = m_mesh->get(f_id); + std::vector nodes; + for (auto n:f.get()) + nodes.push_back(old2new->value(n.id())); + m_cleaned_mesh->newFace(nodes); + } + } + m_mesh->deleteVariable(GMDS_NODE,node_to_keep); + m_mesh->deleteVariable(GMDS_NODE,old2new); +} + +/*----------------------------------------------------------------------------*/ +void MinDelaunayCleaner::setCleanedMeshConnectivity() +{ + MeshDoctor doc(m_cleaned_mesh); + doc.buildEdgesAndX2E(); + doc.updateUpwardConnectivity(); +} + +/*----------------------------------------------------------------------------*/ +Mesh MinDelaunayCleaner::getCleanedMesh() +{ + return *m_cleaned_mesh; +} + +/*----------------------------------------------------------------------------*/ +void MinDelaunayCleaner::writeCleanedMesh(std::string AFileName) +{ + IGMeshIOService ioService(m_cleaned_mesh); + VTKWriter vtkWriter(&ioService); + vtkWriter.setCellOptions(N| E| F); + vtkWriter.setDataOptions(N| E| F); + vtkWriter.write(AFileName); +} +/*----------------------------------------------------------------------------*/ +} // end namespace gmds +/*----------------------------------------------------------------------------*/ \ No newline at end of file diff --git a/modules/medialaxis/src/QuantizationGraph.cpp b/modules/medialaxis/src/QuantizationGraph.cpp index 66cb43f01..a0c13937d 100644 --- a/modules/medialaxis/src/QuantizationGraph.cpp +++ b/modules/medialaxis/src/QuantizationGraph.cpp @@ -5,6 +5,7 @@ #include "gmds/medialaxis/QuantizationGraph.h" #include "gmds/ig/MeshDoctor.h" #include +#include /*----------------------------------------------------------------------------*/ namespace gmds { /*----------------------------------------------------------------------------*/ @@ -12,12 +13,23 @@ QuantizationGraph::QuantizationGraph() { m_mesh_representation = new Mesh(MeshModel(DIM3 | E | N | E2N | N2E)); - // Mark visited nodes as 1 + // Mark vwith 1 visited nodes in the graph building process m_mesh_representation->newVariable("alreadyVisited"); - // Mark pushed nodes as 1 + // Mark with 1 pushed nodes in the graph building process m_mesh_representation->newVariable("alreadyPushed"); - // Quantization solution + // Mark with 1 graph edges connecting two half-edges belonging to the same quad + m_mesh_representation->newVariable("inQuad"); + // Quantization solution on nodes of the graph m_mesh_representation->newVariable("solution"); + // Geometrical length of the half edge that the node represents + m_mesh_representation->newVariable("geometrical_length"); + // Mark with 1 nodes where the quantization solution must be 0 + m_mesh_representation->newVariable("forbiden"); + // Quantization solution on edges of the graph (flux going through the edge) + m_mesh_representation->newVariable("flux"); + + // Max number of iterations + m_max_it = 10000; } /*----------------------------------------------------------------------------*/ @@ -72,6 +84,27 @@ int QuantizationGraph::alreadyPushed(gmds::TCellID AID) return visited->value(AID); } +/*----------------------------------------------------------------------------*/ +void QuantizationGraph::markAsInQuad(TCellID AID) +{ + auto inQuad = m_mesh_representation->getVariable("inQuad"); + inQuad->set(AID,1); +} + +/*----------------------------------------------------------------------------*/ +void QuantizationGraph::setGeometricalLength(TCellID AID, double ALength) +{ + auto gl = m_mesh_representation->getVariable("geometrical_length"); + gl->set(AID,ALength); +} + +/*----------------------------------------------------------------------------*/ +void QuantizationGraph::markAsZero(TCellID AID) +{ + auto forbiden = m_mesh_representation->getVariable("forbiden"); + forbiden->set(AID,1); +} + /*----------------------------------------------------------------------------*/ void QuantizationGraph::updateConnectivity() { @@ -95,6 +128,11 @@ std::vector QuantizationGraph::getLeavingEdges(gmds::Node &AN) /*----------------------------------------------------------------------------*/ std::vector QuantizationGraph::getEnteringEdges(gmds::Node &AN) { + // m2.newNode(1.,2.); + // m2.newNode(3.,2.); + // m2.newNode(4.,2.); + // m2.newNode(0.,3.); + std::vector entering_edges; for (auto e:AN.get()) { @@ -104,15 +142,56 @@ std::vector QuantizationGraph::getEnteringEdges(gmds::Node &AN) return entering_edges; } +/*----------------------------------------------------------------------------*/ +Edge QuantizationGraph::getIntEdge(gmds::Node &AN) +{ + auto in_quad = m_mesh_representation->getVariable("inQuad"); + Edge int_edge; + for (auto e:AN.get()) + { + if (in_quad->value(e.id()) == 1) + { + int_edge = e; + break; + } + } + return int_edge; +} + +/*----------------------------------------------------------------------------*/ +std::vector QuantizationGraph::getExtEdges(gmds::Node &AN) +{ + auto in_quad = m_mesh_representation->getVariable("inQuad"); + std::vector ext_edges; + for (auto e:AN.get()) + { + if (in_quad->value(e.id()) == 0) + ext_edges.push_back(e); + } + return ext_edges; +} + +/*----------------------------------------------------------------------------*/ +Node QuantizationGraph::getOtherNode(Node AN, Edge AE) +{ + if (AN.id() == AE.get()[0].id()) + return AE.get()[1]; + else + return AE.get()[0]; +} + /*----------------------------------------------------------------------------*/ void QuantizationGraph::propagateFromRoot(TCellID AID) { std::cout<<"> Propagating solution from root "<getVariable("solution"); + auto flux = m_mesh_representation->getVariable("flux"); + auto geo_len = m_mesh_representation->getVariable("geometrical_length"); + auto forbiden = m_mesh_representation->getVariable("forbiden"); Node root = m_mesh_representation->get(AID); - // Check if the given node is indeed a root - if (getEnteringEdges(root).empty()) + // Check if the given node is indeed a root, and if its length is 0 + if (getExtEdges(root).empty() && sol->value(AID) == 0 && forbiden->value(AID) == 0) { - auto sol = m_mesh_representation->getVariable("solution"); // Build all the paths from the root to a leaf or an already seen node std::vector> paths; std::vector path; @@ -122,189 +201,1089 @@ void QuantizationGraph::propagateFromRoot(TCellID AID) std::vector newAlreadySeen(m_mesh_representation->getNbNodes()); for (int i = 0; i < m_mesh_representation->getNbNodes(); i++) alreadySeen[i] = 0; + alreadySeen[root.id()] = 1; + int dir = 1; // Convention : when we continue the path trough an interior edges, we say we go in the positive direction. std::queue> path_to_continue; path_to_continue.push(path); std::queue> already_seen; already_seen.push(alreadySeen); - while (!path_to_continue.empty()) + std::queue direction; + direction.push(dir); + int it = 0; // Nb of iterations + while (!path_to_continue.empty() && paths.size() < 1000 && it < m_max_it) { + it += 1; path = path_to_continue.front(); path_to_continue.pop(); alreadySeen = already_seen.front(); already_seen.pop(); - // Continue the path - Node n1,n2,n3; - n1 = m_mesh_representation->get(path[path.size()-1]); - while(!getLeavingEdges(n1).empty() && alreadySeen[n1.id()] == 0) + dir = direction.front(); + direction.pop(); + Node last_node = m_mesh_representation->get(path[path.size()-1]); + if (dir == 1) { - alreadySeen[n1.id()] = 1; - for (auto e: getLeavingEdges(n1)) + Edge e = getIntEdge(last_node); + Node nxt = getOtherNode(last_node,e); + if (alreadySeen[nxt.id()] == 0) { - n2 = e.get()[1]; - if (alreadySeen[n2.id()] == 0) - { - path.push_back(n2.id()); - break; - } + // We continue the path + newPath = path; + newPath.push_back(nxt.id()); + path_to_continue.push(newPath); + newAlreadySeen = alreadySeen; + newAlreadySeen[nxt.id()] = 1; + already_seen.push(newAlreadySeen); + direction.push(-1); + } + } + if (dir == -1) + { + std::vector extEdges = getExtEdges(last_node); + if (extEdges.empty()) + { + // Then this path is over, we reached an other root + paths.push_back(path); } - for (auto e: getLeavingEdges(n1)) + else { - n3 = e.get()[1]; - if (n3.id() != n2.id() && alreadySeen[n3.id()] == 0) + for (auto e:extEdges) { - newPath = path; - newPath.erase(newPath.begin()+newPath.size()-1); - newPath.push_back(n3.id()); - newAlreadySeen = alreadySeen; - path_to_continue.push(newPath); - already_seen.push(newAlreadySeen); + Node nxt = getOtherNode(last_node,e); + if (alreadySeen[nxt.id()] == 0) + { + // We continue the path + newPath = path; + newPath.push_back(nxt.id()); + path_to_continue.push(newPath); + newAlreadySeen = alreadySeen; + newAlreadySeen[nxt.id()] = 1; + already_seen.push(newAlreadySeen); + direction.push(1); + } } } - n1 = n2; } - paths.push_back(path); } - // Update the solution - for (auto p:paths) + // Find which path to increase + int pos = -1; + double maxMinLength = 0.; + int maxNbZeros = 0; + for (int i = 0; i < paths.size(); i ++) + { + double ok = true; + int NbZeros = 0; + double minLength = 1e6; + for (auto id:paths[i]) + { + if (sol->value(id) == 0) + NbZeros += 1; + if (geo_len->value(id)/double(sol->value(id)) < minLength) + minLength = geo_len->value(id)/double(sol->value(id)); + if (forbiden->value(id) == 1) + ok = false; + } + if (NbZeros > maxNbZeros && ok) + { + pos = i; + maxNbZeros = NbZeros; + maxMinLength = minLength; + } + if (NbZeros == maxNbZeros && minLength > maxMinLength && ok) + { + pos = i; + maxMinLength = minLength; + } + } + // Increase the path with the highest number of zeros + if (pos >= 0) { - Node end = m_mesh_representation->get(p[p.size()-1]); - if (getLeavingEdges(end).empty()) + for (int i = 0; i < paths[pos].size(); i++) { - for (auto i:p) - sol->set(i,sol->value(i)+1); + int id = paths[pos][i]; + sol->set(id,sol->value(id)+1); + if (i < paths[pos].size()-1) + { + Node n1 = m_mesh_representation->get(id); + Node n2 = m_mesh_representation->get(paths[pos][i+1]); + Edge e = getCorrespondingEdge(n1,n2); + flux->set(e.id(),flux->value(e.id())+1); + } } } + // for (auto i:paths[pos]) + // { + // sol->set(i,sol->value(i)+1); + // } + // // Update the solution + // for (auto p:paths) + // { + // // Check if increasing this path is useful + // bool increase = false; + // for (auto i:p) + // { + // if (sol->value(i) == 0) + // { + // increase = true; + // break; + // } + // } + // if (increase) + // { + // for (auto i:p) + // { + // sol->set(i,sol->value(i)+1); + // } + // } + // } } } /*----------------------------------------------------------------------------*/ void QuantizationGraph::propagateFromRoots() { + //auto sol = m_mesh_representation->getVariable("solution"); for (auto n_id:m_mesh_representation->nodes()) { Node n = m_mesh_representation->get(n_id); - if (getEnteringEdges(n).empty()) + if (getExtEdges(n).empty()) + { + //increaseSolution(n_id); propagateFromRoot(n_id); + } + } +} + +/*----------------------------------------------------------------------------*/ +void QuantizationGraph::addOnCycles() +{ + auto sol = m_mesh_representation->getVariable("solution"); + auto forbiden = m_mesh_representation->getVariable("forbiden"); + for (auto n_id:m_mesh_representation->nodes()) + { + if (sol->value(n_id) == 0 && forbiden->value(n_id) == 0) + { + std::cout<<"> Adding 1 to the solution to every vertex of the cycle of "< cycle = shortestCycle(n_id); + // for (auto n:cycle) + // sol->set(n.id(),sol->value(n.id())+1); + increaseSolution(n_id); + } } } /*----------------------------------------------------------------------------*/ -void QuantizationGraph::addOnCycle(gmds::TCellID AID) +void QuantizationGraph::buildCompleteSolution() +{ + std::cout << " " << std::endl; + std::cout << "====== Building quantization solution =====" << std::endl; + propagateFromRoots(); + addOnCycles(); + std::cout << "===========================================" << std::endl; + std::cout << " " << std::endl; +} + +/*----------------------------------------------------------------------------*/ +void QuantizationGraph::displaySolution() { - std::cout<<"> Adding 1 to the solution to every vertex of the cycle of "<get(AID); auto sol = m_mesh_representation->getVariable("solution"); - // Build all the paths from the node to a leaf or an already seen node - std::vector> paths; - std::vector path; - std::vector newPath; - path.push_back(n.id()); - std::vector alreadySeen(m_mesh_representation->getNbNodes()); - std::vector newAlreadySeen(m_mesh_representation->getNbNodes()); + for (auto n_id:m_mesh_representation->nodes()) + std::cout<<"Length of half edge "<value(n_id)<getVariable("solution"); + return sol->value(AID); +} + +/*----------------------------------------------------------------------------*/ +int QuantizationGraph::fluxValue(gmds::TCellID AID) +{ + auto flux = m_mesh_representation->getVariable("flux"); + return flux->value(AID); +} + +/*----------------------------------------------------------------------------*/ +std::vector QuantizationGraph::shortestHalfPath(gmds::TCellID AID, int ADirection) +{ + auto forbiden = m_mesh_representation->getVariable("forbiden"); + // Get the node + Node n0 = m_mesh_representation->get(AID); + // Initialize queues and paths + std::queue> paths; + std::queue> alreadySeen; + std::queue direction; + std::vector path; + std::vector already_seen(m_mesh_representation->getNbNodes()); for (int i = 0; i < m_mesh_representation->getNbNodes(); i++) - alreadySeen[i] = 0; - std::queue> path_to_continue; - path_to_continue.push(path); - std::queue> already_seen; - already_seen.push(alreadySeen); - while (!path_to_continue.empty()) + already_seen[i] = 0; + path.push_back(n0); + already_seen[n0.id()] = 1; + paths.push(path); + alreadySeen.push(already_seen); + int dir = ADirection; + direction.push(dir); + bool Continue = true; + std::vector shortestPath; + std::vector leaving_edges; + Node front; + std::vector new_path; + std::vector new_already_seen(m_mesh_representation->getNbNodes()); + // Build the paths + while (Continue && !paths.empty()) { - path = path_to_continue.front(); - path_to_continue.pop(); - alreadySeen = already_seen.front(); - already_seen.pop(); - // Continue the path - Node n1,n2,n3; - n1 = m_mesh_representation->get(path[path.size()-1]); - while(!getLeavingEdges(n1).empty() && alreadySeen[n1.id()] == 0) + // Get the first path to be continued + path = paths.front(); + paths.pop(); + already_seen = alreadySeen.front(); + alreadySeen.pop(); + dir = direction.front(); + direction.pop(); + // Get the current last node of the path + front = path[path.size()-1]; + if (dir == 1) + { + Edge nxt_edge = getIntEdge(front); + Node n = getOtherNode(front,nxt_edge); + if (already_seen[n.id()] == 0 && forbiden->value(n.id()) == 0) + { + // We continue the path + new_path = path; + new_path.push_back(n); + paths.push(new_path); + new_already_seen = already_seen; + new_already_seen[n.id()] = 1; + alreadySeen.push(new_already_seen); + direction.push(-1); + } + } + else { - alreadySeen[n1.id()] = 1; - bool add = false; - for (auto e: getLeavingEdges(n1)) + leaving_edges = getExtEdges(front); + if (leaving_edges.empty()) { - n2 = e.get()[1]; - if (alreadySeen[n2.id()] == 0) + // It means we have reached a root + shortestPath = path; + Continue = false; + } + else + { + for (auto e:leaving_edges) { - path.push_back(n2.id()); - break; + Node n = getOtherNode(front,e); + if (already_seen[n.id()] == 0 && forbiden->value(n.id()) == 0) + { + // Then the path continues + new_path = path; + new_path.push_back(n); + paths.push(new_path); + new_already_seen = already_seen; + new_already_seen[n.id()] = 1; + alreadySeen.push(new_already_seen); + direction.push(1); + } } } - for (auto e: getLeavingEdges(n1)) + } + } + if (ADirection == -1) + std::reverse(shortestPath.begin(),shortestPath.end()); + return shortestPath; +} + +/*----------------------------------------------------------------------------*/ +std::vector QuantizationGraph::shortestCycle(TCellID AID) +{ + auto forbiden = m_mesh_representation->getVariable("forbiden"); + Node n0 = m_mesh_representation->get(AID); + std::queue> paths; + std::queue> alreadySeen; + std::queue direction; + std::vector path; + std::vector already_seen(m_mesh_representation->getNbNodes()); + for (int i = 0; i < m_mesh_representation->getNbNodes(); i++) + already_seen[i] = 0; + path.push_back(n0); + already_seen[n0.id()] = 1; + paths.push(path); + alreadySeen.push(already_seen); + direction.push(1); + int dir; + bool Continue = true; + std::vector shortestCycle; + std::vector leaving_edges; + Node front; + std::vector new_path; + std::vector new_already_seen(m_mesh_representation->getNbNodes()); + while (Continue && !paths.empty()) + { + path = paths.front(); + paths.pop(); + already_seen = alreadySeen.front(); + alreadySeen.pop(); + dir = direction.front(); + direction.pop(); + front = path[path.size()-1]; + if (dir == 1) + { + Edge nxt_edge = getIntEdge(front); + Node n = getOtherNode(front,nxt_edge); + if (n.id() == n0.id()) + { + Continue = false; + shortestCycle = path; + break; + } + else { - n3 = e.get()[1]; - if (n3.id() != n2.id() && alreadySeen[n3.id()] == 0) + if (already_seen[n.id()] == 0 && forbiden->value(n.id()) == 0) { - newPath = path; - newPath.erase(newPath.begin()+newPath.size()-1); - newPath.push_back(n3.id()); - newAlreadySeen = alreadySeen; - path_to_continue.push(newPath); - already_seen.push(newAlreadySeen); + // Then the path continues + new_path = path; + new_path.push_back(n); + paths.push(new_path); + new_already_seen = already_seen; + new_already_seen[n.id()] = 1; + alreadySeen.push(new_already_seen); + direction.push(-1); + } + } + } + else + { + leaving_edges = getExtEdges(front); + for (auto e:leaving_edges) + { + Node n = getOtherNode(front,e); + if (n.id() == n0.id()) + { + Continue = false; + shortestCycle = path; + break; + } + else + { + if (already_seen[n.id()] == 0 && forbiden->value(n.id()) == 0) + { + // Then the path continues + new_path = path; + new_path.push_back(n); + paths.push(new_path); + new_already_seen = already_seen; + new_already_seen[n.id()] = 1; + alreadySeen.push(new_already_seen); + direction.push(1); + } } } - n1 = n2; } - paths.push_back(path); } - // Update the solution - for (auto p:paths) + return shortestCycle; +} + +/*----------------------------------------------------------------------------*/ +std::vector QuantizationGraph::shortestElementaryPath(TCellID AID) +{ + std::vector shortestPath; + std::vector fromNode = shortestHalfPath(AID,1); + std::vector toNode = shortestHalfPath(AID,-1); + std::vector cycle = shortestCycle(AID); + if (fromNode.empty() || toNode.empty()) + shortestPath = cycle; + else + { + if ((fromNode.size()+toNode.size()-1 <= cycle.size()) || cycle.empty()) + { + shortestPath = toNode; + for (int i = 1; i < fromNode.size(); i++) + shortestPath.push_back(fromNode[i]); + } + else + shortestPath = cycle; + } + return shortestPath; +} + +/*----------------------------------------------------------------------------*/ +std::vector QuantizationGraph::problematicCycle(TCellID AID) +{ + Node n0 = m_mesh_representation->get(AID); + std::queue> paths; + std::queue> alreadySeen; + std::queue direction; + std::vector path; + std::vector already_seen(m_mesh_representation->getNbNodes()); + for (int i = 0; i < m_mesh_representation->getNbNodes(); i++) + already_seen[i] = 0; + path.push_back(n0); + already_seen[n0.id()] = 1; + paths.push(path); + paths.push(path); + alreadySeen.push(already_seen); + alreadySeen.push(already_seen); + direction.push(1); + direction.push(-1); + bool Continue = true; + std::vector problematicPath; + std::vector leaving_edges; + Node front,lastNode; + std::vector new_path; + std::vector new_already_seen(m_mesh_representation->getNbNodes()); + int dir; + while (Continue && !paths.empty()) { - Node end = m_mesh_representation->get(p[p.size()-1]); - bool cycle = false; - for (auto e: getLeavingEdges(end)) + path = paths.front(); + paths.pop(); + already_seen = alreadySeen.front(); + alreadySeen.pop(); + dir = direction.front(); + direction.pop(); + front = path[path.size()-1]; + leaving_edges.clear(); + if (dir == 1) + { + leaving_edges.push_back(getIntEdge(front)); + } + else + { + leaving_edges = getExtEdges(front); + } + for (auto e:leaving_edges) { - if (e.get()[1].id() == n.id()) + Node n = getOtherNode(front,e); + if (already_seen[n.id()] == 0) + { + // Then the path continues + new_path = path; + new_path.push_back(n); + paths.push(new_path); + new_already_seen = already_seen; + new_already_seen[n.id()] = 1; + alreadySeen.push(new_already_seen); + direction.push(-dir); + } + else { - cycle = true; + // Then we found a cycle, the path stops + Continue = false; + problematicPath = path; + lastNode = n; break; } + + } + } + // Extract the problematic cycle from the problematic path + std::vector problematicCycle; + bool add = false; + for (auto n:problematicPath) + { + if (n.id() == lastNode.id()) + add =true; + if (add) + problematicCycle.push_back(n); + } + return problematicCycle; +} + +/*----------------------------------------------------------------------------*/ +Edge QuantizationGraph::getCorrespondingEdge(Node AN1, Node AN2) +{ + Edge e; + for (auto e1:AN1.get()) + { + if (e1.get()[1].id() == AN2.id() || e1.get()[0].id() == AN2.id()) + { + e = e1; + break; + } + } + return e; +} + +/*----------------------------------------------------------------------------*/ +std::vector QuantizationGraph::middleQuadEdge(std::vector AV) +{ + auto inQuad = m_mesh_representation->getVariable("inQuad"); + int i = int(double(AV.size())/2.); + Node n1 = AV[i]; + Node n2; + if (i < AV.size()-1) + n2 = AV[i+1]; + else + n2 = AV[0]; + Edge e = getCorrespondingEdge(n1,n2); + if (inQuad->value(e.id())) + return {n1,n2}; + else + { + n2 = n1; + if (i > 0) + n1 = AV[i-1]; + else + n1 = AV[AV.size()-1]; + return {n1,n2}; + } +} + +/*----------------------------------------------------------------------------*/ +bool QuantizationGraph::increaseSolution(TCellID AID) +{ + std::cout<<" > Increasing solution at half edge "<getVariable("solution"); + auto flux = m_mesh_representation->getVariable("flux"); + //std::vector path = shortestElementaryPath(AID); + std::vector path = optimalElementaryPath(AID); + if (path.empty()) + return false; + for (int i = 0; i < path.size(); i++) + { + Node n1 = path[i]; + sol->set(n1.id(),sol->value(n1.id())+1); + if (i < path.size()-1) + { + Node n2 = path[i+1]; + Edge e = getCorrespondingEdge(n1,n2); + flux->set(e.id(),flux->value(e.id())+1); + } + else + { + // Check if the path is a cycle + Node n2 = path[0]; + bool areLinked = false; + for (auto e:n1.get()) + { + if (e.get()[0].id() == n2.id() || e.get()[1].id() == n2.id()) + { + areLinked = true; + break; + } + } + if (areLinked) + { + Edge e = getCorrespondingEdge(n1,n2); + flux->set(e.id(),flux->value(e.id())+1); + } } - if (cycle) + } + // for (auto n:path) + // sol->set(n.id(),sol->value(n.id())+1); + return true; +} + +/*----------------------------------------------------------------------------*/ +std::vector> QuantizationGraph::buildMinimalSolution() +{ + std::vector> problematicCouplesOfNodes; + std::cout<<"> Building minimal quantization solution"<getVariable("solution"); + // Ensure non zero conditions + for (auto n_id:m_non_zero_verticies) + { + if (sol->value(n_id) == 0) { - for (auto i:p) + success = increaseSolution(n_id); + if (!success) { - sol->set(i,sol->value(i)+1); + std::vector probCycle = problematicCycle(n_id); + std::vector probNodes = middleQuadEdge(probCycle); + problematicCouplesOfNodes.push_back(probNodes); + } + } + } + // Ensure condition "at least one non zero in each group" + for (auto group:m_non_zero_groups) + { + bool ok = false; + for (auto n_id:group) + { + if (sol->value(n_id) > 0) + ok = true; + } + if (!ok) + { + success = false; + for (auto n:group) + { + success = increaseSolution(n); + if (success) + break; + } + if (!success) + { + std::vector probCycle = problematicCycle(group[0]); + std::vector probNodes = middleQuadEdge(probCycle); + problematicCouplesOfNodes.push_back(probNodes); } } } + return problematicCouplesOfNodes; } /*----------------------------------------------------------------------------*/ -void QuantizationGraph::addOnCycles() +void QuantizationGraph::roughlyRepairSolution() { + std::cout<<"> Roughly repairing solution"<getVariable("solution"); - for (auto n_id:m_mesh_representation->nodes()) + auto inQuad = m_mesh_representation->getVariable("inQuad"); + for (auto e_id:m_mesh_representation->edges()) + { + if (inQuad->value(e_id) == 1) + { + Edge e = m_mesh_representation->get(e_id); + Node n1 = e.get()[0]; + Node n2 = e.get()[1]; + int l1 = sol->value(n1.id()); + int l2 = sol->value(n2.id()); + if (l1 != l2) + { + std::cout<<"... changing a length"<set(n2.id(),l1); + if (l2 < l1) + sol->set(n1.id(),l2); + } + } + } +} + +/*----------------------------------------------------------------------------*/ +std::vector> QuantizationGraph::checkSolutionValidity() +{ + std::vector> problematicCouplesOfNodes; + std::cout<<"> Checking validity of the quantization solution"<getVariable("solution"); + // Ensure non zero conditions + for (auto n_id:m_non_zero_verticies) { if (sol->value(n_id) == 0) - addOnCycle(n_id); + { + std::vector probCycle = problematicCycle(n_id); + std::vector probNodes = middleQuadEdge(probCycle); + problematicCouplesOfNodes.push_back(probNodes); + } } + // Ensure condition "at least one non zero in each group" + for (auto group:m_non_zero_groups) + { + bool ok = false; + for (auto n_id:group) + { + if (sol->value(n_id) > 0) + ok = true; + } + if (!ok) + { + std::vector probCycle = problematicCycle(group[0]); + std::vector probNodes = middleQuadEdge(probCycle); + problematicCouplesOfNodes.push_back(probNodes); + } + } + return problematicCouplesOfNodes; } /*----------------------------------------------------------------------------*/ -void QuantizationGraph::buildQuantizationSolution() +std::vector QuantizationGraph::optimalHalfPath(gmds::TCellID AID, int ADirection) { - std::cout << " " << std::endl; - std::cout << "====== Building quantization solution =====" << std::endl; - propagateFromRoots(); - addOnCycles(); - std::cout << "===========================================" << std::endl; - std::cout << " " << std::endl; + auto forbiden = m_mesh_representation->getVariable("forbiden"); + auto sol = m_mesh_representation->getVariable("solution"); + auto geo_len = m_mesh_representation->getVariable("geometrical_length"); + // Get the node + Node n0 = m_mesh_representation->get(AID); + // Initialize stack and paths + std::stack> paths; + std::stack> alreadySeen; + std::stack direction; + std::vector path; + std::vector already_seen(m_mesh_representation->getNbNodes()); + for (int i = 0; i < m_mesh_representation->getNbNodes(); i++) + already_seen[i] = 0; + path.push_back(n0); + already_seen[n0.id()] = 1; + paths.push(path); + alreadySeen.push(already_seen); + int dir = ADirection; + direction.push(dir); + bool Continue = true; + std::vector shortestPath; + std::vector leaving_edges; + Node front; + std::vector new_path; + std::vector new_already_seen(m_mesh_representation->getNbNodes()); + int it = 0; // Nb of iterations + // Build the paths + while (Continue && !paths.empty() && it < m_max_it) + { + it += 1; + // Get the first path to be continued + path = paths.top(); + paths.pop(); + already_seen = alreadySeen.top(); + alreadySeen.pop(); + dir = direction.top(); + direction.pop(); + // Get the current last node of the path + front = path[path.size()-1]; + if (dir == 1) + { + Edge nxt_edge = getIntEdge(front); + Node n = getOtherNode(front,nxt_edge); + if (already_seen[n.id()] == 0 && forbiden->value(n.id()) == 0) + { + // We continue the path + new_path = path; + new_path.push_back(n); + paths.push(new_path); + new_already_seen = already_seen; + new_already_seen[n.id()] = 1; + alreadySeen.push(new_already_seen); + direction.push(-1); + } + } + else + { + leaving_edges = getExtEdges(front); + if (leaving_edges.empty()) + { + // It means we have reached a root + shortestPath = path; + Continue = false; + } + else + { + // Order the nodes depending on the element size + std::vector ordered_nodes; + double max_size; + int max_pos; + Node node_to_add; + while (!leaving_edges.empty()) + { + max_size = -1.; + for (int i = 0; i < leaving_edges.size(); i++) + { + Node n = getOtherNode(front,leaving_edges[i]); + if (sol->value(n.id()) == 0) + { + node_to_add = n; + max_pos = i; + break; + } + else + { + double size = geo_len->value(n.id())/double(sol->value(n.id())); + if (size > max_size) + { + max_pos = i; + node_to_add = n; + max_size = size; + } + } + } + ordered_nodes.push_back(node_to_add); + leaving_edges.erase(leaving_edges.begin()+max_pos); + } + + std::reverse(ordered_nodes.begin(),ordered_nodes.end()); + + //for (auto e:leaving_edges) + for (auto n:ordered_nodes) + { + //Node n = getOtherNode(front,e); + if (already_seen[n.id()] == 0 && forbiden->value(n.id()) == 0) + { + // Then the path continues + new_path = path; + new_path.push_back(n); + paths.push(new_path); + new_already_seen = already_seen; + new_already_seen[n.id()] = 1; + alreadySeen.push(new_already_seen); + direction.push(1); + } + } + } + } + } + if (ADirection == -1) + std::reverse(shortestPath.begin(),shortestPath.end()); + return shortestPath; } /*----------------------------------------------------------------------------*/ -void QuantizationGraph::displaySolution() +std::vector QuantizationGraph::optimalPath(gmds::TCellID AID) +{ + std::vector p1 = optimalHalfPath(AID,-1); + std::vector p2 = optimalHalfPath(AID,1); + if (p1.empty()) + return p1; + if (p2.empty()) + return p2; + for (int i = 1; i < p2.size(); i++) + p1.push_back(p2[i]); + return p1; +} + +/*----------------------------------------------------------------------------*/ +std::vector QuantizationGraph::optimalCycle(TCellID AID) { + auto forbiden = m_mesh_representation->getVariable("forbiden"); auto sol = m_mesh_representation->getVariable("solution"); - for (auto n_id:m_mesh_representation->nodes()) - std::cout<<"Length of half edge "<value(n_id)<getVariable("geometrical_length"); + Node n0 = m_mesh_representation->get(AID); + std::stack> paths; + std::stack> alreadySeen; + std::stack direction; + std::vector path; + std::vector already_seen(m_mesh_representation->getNbNodes()); + for (int i = 0; i < m_mesh_representation->getNbNodes(); i++) + already_seen[i] = 0; + path.push_back(n0); + already_seen[n0.id()] = 1; + paths.push(path); + alreadySeen.push(already_seen); + direction.push(1); + int dir; + bool Continue = true; + std::vector shortestCycle; + std::vector leaving_edges; + Node front; + std::vector new_path; + std::vector new_already_seen(m_mesh_representation->getNbNodes()); + int it = 0; // Nb of iterations + while (Continue && !paths.empty() && it < m_max_it) + { + it += 1; + path = paths.top(); + paths.pop(); + already_seen = alreadySeen.top(); + alreadySeen.pop(); + dir = direction.top(); + direction.pop(); + front = path[path.size()-1]; + if (dir == 1) + { + Edge nxt_edge = getIntEdge(front); + Node n = getOtherNode(front,nxt_edge); + if (n.id() == n0.id()) + { + Continue = false; + shortestCycle = path; + break; + } + else + { + if (already_seen[n.id()] == 0 && forbiden->value(n.id()) == 0) + { + // Then the path continues + new_path = path; + new_path.push_back(n); + paths.push(new_path); + new_already_seen = already_seen; + new_already_seen[n.id()] = 1; + alreadySeen.push(new_already_seen); + direction.push(-1); + } + } + } + else + { + leaving_edges = getExtEdges(front); + + // Order the nodes depending on the element size + std::vector ordered_nodes; + double max_size; + int max_pos; + Node node_to_add; + while (!leaving_edges.empty()) + { + max_size = -1.; + for (int i = 0; i < leaving_edges.size(); i++) + { + Node n = getOtherNode(front,leaving_edges[i]); + if (sol->value(n.id()) == 0) + { + node_to_add = n; + max_pos = i; + break; + } + else + { + double size = geo_len->value(n.id())/double(sol->value(n.id())); + if (size > max_size) + { + max_pos = i; + node_to_add = n; + max_size = size; + } + } + } + ordered_nodes.push_back(node_to_add); + leaving_edges.erase(leaving_edges.begin()+max_pos); + } + std::reverse(ordered_nodes.begin(),ordered_nodes.end()); + + //for (auto e:leaving_edges) + for (auto n:ordered_nodes) + { + //Node n = getOtherNode(front,e); + if (n.id() == n0.id()) + { + Continue = false; + shortestCycle = path; + break; + } + else + { + if (already_seen[n.id()] == 0 && forbiden->value(n.id()) == 0) + { + // Then the path continues + new_path = path; + new_path.push_back(n); + paths.push(new_path); + new_already_seen = already_seen; + new_already_seen[n.id()] = 1; + alreadySeen.push(new_already_seen); + direction.push(1); + } + } + } + } + } + return shortestCycle; } /*----------------------------------------------------------------------------*/ -int QuantizationGraph::quantizationSolutionValue(gmds::TCellID AID) +std::vector QuantizationGraph::optimalElementaryPath(TCellID AID) { + std::vector optimalPath; + std::vector fromNode = optimalHalfPath(AID,1); + std::vector toNode = optimalHalfPath(AID,-1); + std::vector cycle = optimalCycle(AID); + if (fromNode.empty() || toNode.empty()) + optimalPath = cycle; + else + { + if ((fromNode.size()+toNode.size()-1 <= cycle.size()) || cycle.empty()) + { + optimalPath = toNode; + for (int i = 1; i < fromNode.size(); i++) + optimalPath.push_back(fromNode[i]); + } + else + optimalPath = cycle; + } + return optimalPath; +} + +/*----------------------------------------------------------------------------*/ +void QuantizationGraph::improveSolution(double AMeshSize) +{ + std::cout<<"> Improving the quantization solution"<getVariable("forbiden"); auto sol = m_mesh_representation->getVariable("solution"); - return sol->value(AID); + auto geo_len = m_mesh_representation->getVariable("geometrical_length"); + auto flux = m_mesh_representation->getVariable("flux"); + // for (auto n_id:m_mesh_representation->nodes()) + // { + // if (forbiden->value(n_id) == 0 && sol->value(n_id) > 0) + // { + // double size = geo_len->value(n_id)/double(sol->value(n_id)); + // if (size > 2.*AMeshSize) + // { + // int n = int(geo_len->value(n_id)/AMeshSize)-sol->value(n_id); + // std::vector cycle = optimalCycle(n_id); + // if (cycle.empty()) + // cycle = optimalPath(n_id); + // for (int i = 0; i < cycle.size(); i++) + // { + // Node n1 = cycle[i]; + // sol->set(n1.id(),sol->value(n1.id())+n); + // if (i < cycle.size()-1) + // { + // Node n2 = cycle[i+1]; + // Edge e = getCorrespondingEdge(n1,n2); + // flux->set(e.id(),flux->value(e.id())+n); + // } + // else + // { + // // Check if the path is a cycle + // Node n2 = cycle[0]; + // bool areLinked = false; + // for (auto e:n1.get()) + // { + // if (e.get()[0].id() == n2.id() || e.get()[1].id() == n2.id()) + // { + // areLinked = true; + // break; + // } + // } + // if (areLinked) + // { + // Edge e = getCorrespondingEdge(n1,n2); + // flux->set(e.id(),flux->value(e.id())+n); + // } + // } + // } + // } + // } + // } + bool Continue = true; + while (Continue) + { + Continue = false; + for (auto n_id:m_mesh_representation->nodes()) + { + if (forbiden->value(n_id) == 0 && sol->value(n_id) > 0) + { + double size = geo_len->value(n_id)/double(sol->value(n_id)); + if (size > 2.*AMeshSize) + { + int n = int(geo_len->value(n_id)/AMeshSize)-sol->value(n_id); + if (n >= 2) + Continue = true; + std::vector cycle = optimalCycle(n_id); + if (cycle.empty()) + cycle = optimalPath(n_id); + for (int i = 0; i < cycle.size(); i++) + { + Node n1 = cycle[i]; + sol->set(n1.id(),sol->value(n1.id())+1); + if (i < cycle.size()-1) + { + Node n2 = cycle[i+1]; + Edge e = getCorrespondingEdge(n1,n2); + flux->set(e.id(),flux->value(e.id())+1); + } + else + { + // Check if the path is a cycle + Node n2 = cycle[0]; + bool areLinked = false; + for (auto e:n1.get()) + { + if (e.get()[0].id() == n2.id() || e.get()[1].id() == n2.id()) + { + areLinked = true; + break; + } + } + if (areLinked) + { + Edge e = getCorrespondingEdge(n1,n2); + flux->set(e.id(),flux->value(e.id())+1); + } + } + } + } + } + } + } + } + /*----------------------------------------------------------------------------*/ } // end namespace gmds /*----------------------------------------------------------------------------*/ \ No newline at end of file diff --git a/modules/medialaxis/src/QuantizationSolver.cpp b/modules/medialaxis/src/QuantizationSolver.cpp index c23fa9344..fbb641577 100644 --- a/modules/medialaxis/src/QuantizationSolver.cpp +++ b/modules/medialaxis/src/QuantizationSolver.cpp @@ -7,23 +7,175 @@ /*----------------------------------------------------------------------------*/ namespace gmds { /*----------------------------------------------------------------------------*/ -QuantizationSolver::QuantizationSolver(gmds::Mesh &AMesh) +QuantizationSolver::QuantizationSolver(gmds::Mesh &AMesh, double AMeshSize) { // Non conformal quad mesh m_mesh = &AMesh; // Correspondence edge/half edge m_mesh->newVariable("edge2halfEdge1"); m_mesh->newVariable("edge2halfEdge2"); - // Nodes of the quantized mesh corresponding to each non-conformal face - m_mesh->newVariable>,GMDS_FACE>("face2quantizedNodes"); + // Correspondance edge/quantization graph edge + m_mesh->newVariable("edge2qgrapheEdge"); + // Length of the edge after quantization + m_mesh->newVariable("length"); + // Mark with 1 the nodes forming a T-junction + m_mesh->newVariable("T-junction"); + // Mark with 1 the nodes forming a T-junction (the previous variable is no longer necessary) + //m_mesh->newVariable("is_a_T-junction"); + + // Target mesh size + m_mesh_size = AMeshSize; // Quantization graph m_quantization_graph = new QuantizationGraph(); - // Quantized mesh - m_quantized_mesh = new Mesh(MeshModel(DIM3 | E | N | F | + // Mesh with a singularity dipole added + m_fixed_mesh = new Mesh(MeshModel(DIM3 | E | N | F | E2N | N2E | F2N | N2F | F2E | E2F)); + // Mark with 1 the faces affected by the dipole + m_mesh->newVariable("is_affected_by_dipole"); + +} +/*----------------------------------------------------------------------------*/ +void QuantizationSolver::findTJunctions() +{ + auto tj = m_mesh->getVariable("is_a_T-junction"); + // Initialize at -1 + for (auto n_id:m_mesh->nodes()) + tj->set(n_id,-1); + for (auto f_id:m_mesh->faces()) + { + Face f = m_mesh->get(f_id); + std::vector nodes = f.get(); + if (nodes.size() != 4) + { + // Then this face contains T-junctions + int NbTJ = nodes.size() - 4; + std::vector t_junctions; + // Find the NbTJ flatest angles + while (NbTJ > 0) + { + int min_pos; + double min_value = 100; + for (int i = 0; i < nodes.size(); i++) + { + Node n = nodes[i]; + // Find the two edges of f containing n + Edge e1,e2; + for (auto e:f.get()) + { + if (n.id() == e.get()[0].id() || n.id() == e.get()[1].id()) + { + e1 = e; + break; + } + } + for (auto e:f.get()) + { + if (e.id() != e1.id() && (n.id() == e.get()[0].id() || n.id() == e.get()[1].id())) + { + e2 = e; + break; + } + } + double angle = oriented_angle(edge2vec(e1,n),edge2vec(e2,n)); + double dist; + if (fabs(M_PI-angle) < fabs(M_PI+angle)) + dist = fabs(M_PI-angle); + else + dist = fabs(M_PI+angle); + if (dist < min_value) + { + min_pos = i; + min_value = dist; + } + } + t_junctions.push_back(nodes[min_pos]); + nodes.erase(nodes.begin() + min_pos); + NbTJ -= 1; + } + for (auto n:t_junctions) + tj->set(n.id(),f_id); + } + } +} + +/*----------------------------------------------------------------------------*/ +std::vector> QuantizationSolver::alignedEdgesGroups(Face &AF) +{ + auto tj = m_mesh->getVariable("is_a_T-junction"); + std::vector ordered_edges = orientateEdges(AF); + std::vector> edges_groups; + std::vector group; + // group of the first element + bool finished = false; + Edge e0 = ordered_edges[0]; + Edge current_edge = ordered_edges[0]; + group.push_back(current_edge); + ordered_edges.erase(ordered_edges.begin()); + Edge next_edge; + while (!finished) + { + next_edge = ordered_edges[ordered_edges.size()-1]; + Node n = getCommonNode(current_edge,next_edge); + if (tj->value(n.id()) == AF.id()) + { + group.push_back(next_edge); + ordered_edges.erase(ordered_edges.begin()+ordered_edges.size()-1); + current_edge = next_edge; + } + else + finished = true; + } + std::reverse(group.begin(),group.end()); + if (!ordered_edges.empty()) + { + finished = false; + current_edge = e0; + while (!finished) + { + next_edge = ordered_edges[0]; + Node n = getCommonNode(current_edge,next_edge); + if (tj->value(n.id()) == AF.id()) + { + group.push_back(next_edge); + ordered_edges.erase(ordered_edges.begin()); + current_edge = next_edge; + } + else + finished = true; + } + } + edges_groups.push_back(group); + // Group of the other elements + while (!ordered_edges.empty()) + { + group.clear(); + current_edge = ordered_edges[0]; + group.push_back(current_edge); + ordered_edges.erase(ordered_edges.begin()); + finished = false; + while (!finished) + { + next_edge = ordered_edges[0]; + Node n = getCommonNode(current_edge,next_edge); + if (tj->value(n.id()) == AF.id()) + { + group.push_back(next_edge); + ordered_edges.erase(ordered_edges.begin()); + current_edge = next_edge; + if (ordered_edges.size() == 0) + finished = true; + } + else + finished = true; + } + edges_groups.push_back(group); + } + + + return edges_groups; } /*----------------------------------------------------------------------------*/ @@ -44,9 +196,24 @@ QuantizationSolver::buildHalfEdges() for (auto f_id:m_mesh->faces()) { Face f = m_mesh->get(f_id); - std::vector> edges_groups = groupsOfAlignedEdges(f); + //std::vector> edges_groups = groupsOfAlignedEdges(f); + std::vector> edges_groups = alignedEdgesGroups(f); if (edges_groups.size() != 4) - throw GMDSException("buildHalfEdges() : non conformal quads must have 4 non conformal edges"); + { + std::cout<<"The face "<()) + // std::cout<<"test "< "< Building quantization graph nodes"<newNode(); + { + NonConformalHalfEdge he = m_half_edges[i]; + double len = he.firstNode().point().distance(he.endNode().point()); + Node n = m_quantization_graph->newNode(); + m_quantization_graph->setGeometricalLength(n.id(),len); + } } /*----------------------------------------------------------------------------*/ @@ -137,6 +330,7 @@ void QuantizationSolver::buildConnectedComponent(int AHalfEdgeID) { std::cout<<"> Building connected component of half edge "<getVariable("edge2qgrapheEdge"); std::queue front; front.push(AHalfEdgeID); int current; @@ -144,35 +338,48 @@ QuantizationSolver::buildConnectedComponent(int AHalfEdgeID) { current = front.front(); front.pop(); - m_quantization_graph->markAsVisited(current); - NonConformalHalfEdge e = m_half_edges[current]; - // Build forwards edges - for (auto opp:e.opposite()) + if (m_quantization_graph->alreadyVisited(current) == 0) { - if (m_quantization_graph->alreadyVisited(opp) == 0) + m_quantization_graph->markAsVisited(current); + NonConformalHalfEdge e = m_half_edges[current]; + // Build edges linking to opp + for (auto opp:e.opposite()) { - m_quantization_graph->newEdge(current,opp); - if (m_quantization_graph->alreadyPushed(oppositeInQuad(opp)) == 0) + if (m_quantization_graph->alreadyVisited(opp) == 0) { - front.push(oppositeInQuad(opp)); - m_quantization_graph->markAsPushed(oppositeInQuad(opp)); + Edge newGraphEdge = m_quantization_graph->newEdge(current,opp); + Edge commonEdge = getCommonEdge(current,opp); + e2qge->set(commonEdge.id(),newGraphEdge.id()); + if (m_quantization_graph->alreadyPushed(oppositeInQuad(opp)) == 0) + { + front.push(oppositeInQuad(opp)); + m_quantization_graph->markAsPushed(oppositeInQuad(opp)); + } } } - } - // Build backward edges - int nxt = oppositeInQuad(current); - NonConformalHalfEdge e2 = m_half_edges[nxt]; - m_quantization_graph->markAsVisited(nxt); - m_quantization_graph->newEdge(nxt,current); - for (auto opp:e2.opposite()) - { - if (m_quantization_graph->alreadyVisited(opp) == 0) + + int nxt = oppositeInQuad(current); + if (m_quantization_graph->alreadyVisited(nxt) == 0) { - m_quantization_graph->newEdge(opp,nxt); - if (m_quantization_graph->alreadyPushed(opp) == 0) + // Build edge linking to next(next) + NonConformalHalfEdge e2 = m_half_edges[nxt]; + m_quantization_graph->markAsVisited(nxt); + Edge newGraphEdge = m_quantization_graph->newEdge(nxt,current); + m_quantization_graph->markAsInQuad(newGraphEdge.id()); + // Build edges linking next(next) to opp(next(next)) + for (auto opp:e2.opposite()) { - front.push(opp); - m_quantization_graph->markAsPushed(opp); + if (m_quantization_graph->alreadyVisited(opp) == 0) + { + newGraphEdge = m_quantization_graph->newEdge(opp,nxt); + Edge commonEdge = getCommonEdge(opp,nxt); + e2qge->set(commonEdge.id(),newGraphEdge.id()); + if (m_quantization_graph->alreadyPushed(opp) == 0) + { + front.push(opp); + m_quantization_graph->markAsPushed(opp); + } + } } } } @@ -194,6 +401,7 @@ QuantizationSolver::buildQuantizationGraph() { std::cout<<" "<> QuantizationSolver::halfEdgesGroup(int AID, std::vector &AV) +void QuantizationSolver::setHalfEdgesLength() { - std::vector> groups; - std::vector group1; - std::vector group2; - NonConformalHalfEdge e = m_half_edges[AID]; - bool over = false; - while(!over) - { - group1.push_back(e.id()); - AV[e.id()] = 1; - for (auto opp:e.opposite()) + std::vector v(m_half_edges.size()); + for (int i = 0; i < m_half_edges.size(); i++) + v[i] = m_quantization_graph->quantizationSolutionValue(i); + m_half_edges_lengths = v; +} + +/*----------------------------------------------------------------------------*/ +void QuantizationSolver::setEdgesLength() +{ + auto l = m_mesh->getVariable("length"); + auto e2qge = m_mesh->getVariable("edge2qgrapheEdge"); + for (auto e_id:m_mesh->edges()) + l->set(e_id,m_quantization_graph->fluxValue(e2qge->value(e_id))); + for (auto he:m_half_edges) + { + if (he.opposite().empty()) { - if (AV[opp] == 0) + Edge e = he.edges()[0]; + l->set(e.id(),m_half_edges_lengths[he.id()]); + } + } +} + +/*----------------------------------------------------------------------------*/ +std::vector> QuantizationSolver::sides() +{ + auto e2he1 = m_mesh->getVariable("edge2halfEdge1"); + std::vector> sides; + auto alreadySeen = m_mesh->newMark(); + for (auto e_id:m_mesh->edges()) + { + if (!m_mesh->isMarked(e_id,alreadySeen)) + { + Edge e = m_mesh->get(e_id); + if (e.get().size() == 1) { - group2.push_back(opp); - AV[opp] = 1; + // Then it is a boundary edge, we build a new side + std::vector newSide; + std::queue front; + front.push(e); + m_mesh->mark(e_id,alreadySeen); + while (!front.empty()) + { + Edge e1 = front.front(); + front.pop(); + newSide.push_back(e2he1->value(e1.id())); + for (auto n:e1.get()) + { + for (auto e2:n.get()) + { + if (e2.get().size() == 1) + { + if (!m_mesh->isMarked(e2.id(),alreadySeen)) + { + double alpha = oriented_angle(edge2vec(e1,n),edge2vec(e2,n)); + if (fabs(alpha-M_PI) < 0.1 || fabs(alpha+M_PI) < 0.1) + { + front.push(e2); + m_mesh->mark(e2.id(),alreadySeen); + } + } + } + } + } + } + sides.push_back(newSide); } } - if (e.opposite().empty()) - over = true; - else + } + return sides; +} + +/*----------------------------------------------------------------------------*/ +void QuantizationSolver::markTJunctions() +{ + auto TJ = m_mesh->getVariable("T-junction"); + auto e2he1 = m_mesh->getVariable("edge2halfEdge1"); + auto e2he2 = m_mesh->getVariable("edge2halfEdge2"); + for (auto n_id:m_mesh->nodes()) + { + Node n = m_mesh->get(n_id); + bool isATJ = false; + for (auto e:n.get()) { - NonConformalHalfEdge e2 = m_half_edges[e.opposite()[e.opposite().size()-1]]; - if (e2.firstNode().id() == e.endNode().id()) - over = true; - else - e = m_half_edges[m_half_edges[m_half_edges[e.next()].opposite()[0]].next()]; + NonConformalHalfEdge he1 = m_half_edges[e2he1->value(e.id())]; + if (n.id() != he1.firstNode().id() && n.id() != he1.endNode().id()) + { + isATJ = true; + break; + } + if (e2he2->value(e.id()) >= 0) + { + he1 = m_half_edges[e2he2->value(e.id())]; + if (n.id() != he1.firstNode().id() && n.id() != he1.endNode().id()) + { + isATJ = true; + break; + } + } } + if (isATJ) + TJ->set(n_id,1); } - groups.push_back(group1); - groups.push_back(group2); - return groups; } /*----------------------------------------------------------------------------*/ -std::vector>> QuantizationSolver::halfEdgesGroups() +bool QuantizationSolver::isOnBoundary(Node AN) { - // Vector to mark the already seen half edges, initialized at 0 - std::vector V(m_half_edges.size()); - for (int i = 0; i < m_half_edges.size(); i++) - V[i] = 0; + bool boo = false; + for (auto e:AN.get()) + { + if (e.get().size() == 1) + { + boo = true; + break; + } + } + return boo; +} - // Build the groups - std::vector>> groups; - std::vector> group; +/*----------------------------------------------------------------------------*/ +std::vector QuantizationSolver::nonZeroHalfEdges() +{ + auto e2he1 = m_mesh->getVariable("edge2halfEdge1"); + auto e2he2 = m_mesh->getVariable("edge2halfEdge2"); + auto TJ = m_mesh->getVariable("T-junction"); + std::vector alreadyAdded(m_half_edges.size()); for (int i = 0; i < m_half_edges.size(); i++) + alreadyAdded[i] = 0; + std::vector nzhe; + for (auto n_id:m_mesh->nodes()) { - if (V[i] == 0) + Node n = m_mesh->get(n_id); + if (!isOnBoundary(n)) { - NonConformalHalfEdge e1 = m_half_edges[i]; - if (e1.opposite().empty()) + if (TJ->value(n_id) == 0) { - group = halfEdgesGroup(i,V); - groups.push_back(group); + std::vector adj_edges = n.get(); + if (adj_edges.size() != 4) + { + // Then it is an interior singularity + for (auto e:adj_edges) + { + int he = e2he1->value(e.id()); + if (alreadyAdded[he] == 0) + { + nzhe.push_back(he); + alreadyAdded[he] = 1; + } + he = e2he2->value(e.id()); + if (he >= 0) + { + if (alreadyAdded[he] == 0) + { + nzhe.push_back(he); + alreadyAdded[he] = 1; + } + } + + } + } } - else + } + else + { + std::vector adj_edges = n.get(); + if (adj_edges.size() != 3) { - NonConformalHalfEdge e2 = m_half_edges[e1.opposite()[0]]; - if (e1.firstNode().id() == e2.endNode().id()) + // Then it is a boundary singularity + for (auto e:adj_edges) { - group = halfEdgesGroup(i,V); - groups.push_back(group); + if (e.get().size() == 2) + { + int he = e2he1->value(e.id()); + if (alreadyAdded[he] == 0) + { + nzhe.push_back(he); + alreadyAdded[he] = 1; + } + he = e2he2->value(e.id()); + if (he >= 0) + { + if (alreadyAdded[he] == 0) + { + nzhe.push_back(he); + alreadyAdded[he] = 1; + } + } + } } } } } + return nzhe; +} -// // Test : display the groups -// for (auto g:groups) -// { -// std::cout<<"Group 1 : "; -// for (auto i:g[0]) -// std::cout<> QuantizationSolver::groupsOfConfundedNodes() +{ + std::cout<<"> Building groups of confunded nodes"<getVariable("groupId"); + auto e2he1 = m_mesh->getVariable("edge2halfEdge1"); + auto e2he2 = m_mesh->getVariable("edge2halfEdge2"); + std::vector> groups; + auto visited = m_mesh->newMark(); + int Id = 0; + for (auto n_id:m_mesh->nodes()) + { + if (!m_mesh->isMarked(n_id,visited)) + { + // Then we create a new group + std::vector newGroup; + std::queue toVisit; + Node n0 = m_mesh->get(n_id); + toVisit.push(n0); + m_mesh->mark(n_id,visited); + while (!toVisit.empty()) + { + Node n = toVisit.front(); + toVisit.pop(); + newGroup.push_back(n); + groupId->set(n.id(),Id); + for (auto e:n.get()) + { + int he_id = e2he1->value(e.id()); + if (m_half_edges_lengths[he_id] == 0) + { + NonConformalHalfEdge he = m_half_edges[he_id]; + for (auto n1:he.getOrderedNodes()) + { + if (!m_mesh->isMarked(n1.id(),visited)) + { + toVisit.push(n1); + m_mesh->mark(n1.id(),visited); + } + } + } + he_id = e2he2->value(e.id()); + if (he_id >= 0) + { + if (m_half_edges_lengths[he_id] == 0) + { + NonConformalHalfEdge he = m_half_edges[he_id]; + for (auto n1:he.getOrderedNodes()) + { + if (!m_mesh->isMarked(n1.id(),visited)) + { + toVisit.push(n1); + m_mesh->mark(n1.id(),visited); + } + } + } + } + } + } + groups.push_back(newGroup); + Id += 1; + } + } return groups; } /*----------------------------------------------------------------------------*/ -void QuantizationSolver::setHalfEdgesLength() +bool QuantizationSolver::isABoundaryCorner(Node AN) { - std::vector v(m_half_edges.size()); - for (int i = 0; i < m_half_edges.size(); i++) - v[i] = m_quantization_graph->quantizationSolutionValue(i); - m_half_edges_lengths = v; + if (!isOnBoundary(AN)) + return false; + else + { + // Find the two boundary edges adjacent at AN + Edge e1,e2; + for (auto e:AN.get()) + { + if (e.get().size() == 1) + { + e1 = e; + break; + } + } + for (auto e:AN.get()) + { + if (e.get().size() == 1 && e.id() != e1.id()) + { + e2 = e; + break; + } + } + double alpha = oriented_angle(edge2vec(e1,AN),edge2vec(e2,AN)); + if (fabs(alpha-M_PI) > 0.1 && fabs(alpha+M_PI) > 0.1) + return true; + else + return false; + } +} + +/*----------------------------------------------------------------------------*/ +void QuantizationSolver::addOldNodesOnFixedMesh() +{ + std::cout<<"> Adding existing nodes on the fixed mesh"<nodes()) + { + Node n = m_mesh->get(n_id); + m_fixed_mesh->newNode(n.point()); + } } /*----------------------------------------------------------------------------*/ -void QuantizationSolver::writeQuantizedMesh(std::basic_string AFileName) +void QuantizationSolver::writeFixedMesh(std::basic_string AFileName) { - std::cout<<"> Writing the quantized mesh"< Writing the fixed mesh"< AFileName) } /*----------------------------------------------------------------------------*/ -void QuantizationSolver::buildQuantizedMeshNodesOnEdges() +void QuantizationSolver::markAffectedFaces(int AId1, int AId2) { - std::cout<<"> Building nodes of the quantized mesh that lay on non-conformal edges"<> v(m_half_edges.size()); - m_half_edges_to_new_nodes = v; - // Add the nodes of the non-conformal mesh - for (auto n_id:m_mesh->nodes()) + auto var = m_mesh->getVariable("is_affected_by_dipole"); + NonConformalHalfEdge he1 = m_half_edges[AId1]; + NonConformalHalfEdge he2 = m_half_edges[he1.next()]; + NonConformalHalfEdge he3 = m_half_edges[AId2]; + NonConformalHalfEdge he4 = m_half_edges[he3.next()]; + Face f0 = he1.face(); + var->set(f0.id(),1); + for (auto e:he2.edges()) { - Node n = m_mesh->get(n_id); - m_quantized_mesh->newNode(n.point()); - } - // Build the new nodes - std::vector>> hegroups = halfEdgesGroups(); - for (auto g:hegroups) - { - // Get the groups of half edges on each side - std::vector g1 = g[0]; - std::vector g2 = g[1]; - // Compute the total length of the group - int L = 0; - for (auto i:g1) - L += m_half_edges_lengths[i]; - // If the half edges is on the boundary, ie g2 is empty - if (g2.empty()) + for (auto f:e.get()) { - NonConformalHalfEdge e = m_half_edges[g1[0]]; - std::vector newNodes; - newNodes.push_back(e.firstNode().id()); - math::Point E = e.endNode().point()+(-1.)*e.firstNode().point(); - math::Point P; - for (int l = 1; l < L; l++) - { - P = e.firstNode().point()+(double (l)/double(L))*E; - Node n = m_quantized_mesh->newNode(P); - newNodes.push_back(n.id()); - } - newNodes.push_back(e.endNode().id()); - m_half_edges_to_new_nodes[e.id()] = newNodes; + if (f.id() != f0.id()) + var->set(f.id(),1); } - else // g2 is not empty + } + for (auto e:he4.edges()) + { + for (auto f:e.get()) { - NonConformalHalfEdge e1 = m_half_edges[g1[0]]; - NonConformalHalfEdge e2 = m_half_edges[g2[0]]; - int i1 = 0; - int i2 = 0; - int L1 = 0; - int L2 = 0; - std::vector newNodes1; - std::vector newNodes2; - newNodes1.push_back(e1.firstNode().id()); - newNodes2.push_back(e2.endNode().id()); - for (int l = 1; l < L; l++) - { - if (l < L1+m_half_edges_lengths[e1.id()] && l < L2+m_half_edges_lengths[e2.id()]) - { - // Create the new node - math::Point dir1 = e1.endNode().point()+(-1.)*m_quantized_mesh->get(newNodes1[newNodes1.size()-1]).point(); - math::Point dir2 = e2.firstNode().point()+(-1.)*m_quantized_mesh->get(newNodes2[newNodes2.size()-1]).point(); - math::Point P; - if (vec(dir1).norm()/double(m_half_edges_lengths[e1.id()]) < vec(dir2).norm()/double(m_half_edges_lengths[e2.id()])) - P = m_quantized_mesh->get(newNodes1[newNodes1.size()-1]).point()+(1./double(m_half_edges_lengths[e1.id()]))*dir1; - else - P = m_quantized_mesh->get(newNodes2[newNodes2.size()-1]).point()+(1./double(m_half_edges_lengths[e2.id()]))*dir2; - Node n = m_quantized_mesh->newNode(P); - newNodes1.push_back(n.id()); - newNodes2.push_back(n.id()); - } - if (l == L1+m_half_edges_lengths[e1.id()] && l < L2+m_half_edges_lengths[e2.id()]) - { - newNodes1.push_back(e1.endNode().id()); - newNodes2.push_back(e1.endNode().id()); - L1 = L1 + m_half_edges_lengths[e1.id()]; - e1 = m_half_edges[g1[i1+1]]; - m_half_edges_to_new_nodes[g1[i1]] = newNodes1; - i1 += 1; - newNodes1.clear(); - newNodes1.push_back(e1.firstNode().id()); - } - if (l < L1+m_half_edges_lengths[e1.id()] && l == L2+m_half_edges_lengths[e2.id()]) - { - newNodes1.push_back(e2.firstNode().id()); - newNodes2.push_back(e2.firstNode().id()); - L2 = L2 + m_half_edges_lengths[e2.id()]; - e2 = m_half_edges[g2[i2+1]]; - std::reverse(newNodes2.begin(),newNodes2.end()); - m_half_edges_to_new_nodes[g2[i2]] = newNodes2; - i2 += 1; - newNodes2.clear(); - newNodes2.push_back(e2.endNode().id()); - } - } - newNodes1.push_back(m_half_edges[g1[g1.size()-1]].endNode().id()); - newNodes2.push_back(m_half_edges[g2[g2.size()-1]].firstNode().id()); - std::reverse(newNodes2.begin(),newNodes2.end()); - m_half_edges_to_new_nodes[g1[i1]] = newNodes1; - m_half_edges_to_new_nodes[g2[i2]] = newNodes2; + if (f.id() != f0.id()) + var->set(f.id(),1); } } } /*----------------------------------------------------------------------------*/ -void QuantizationSolver::buildQuantizedMeshInternalNodes() +void QuantizationSolver::addOldFacesOnFixedMesh() { - std::cout<<"> Building nodes of the quantized mesh that are internal to non-conformal faces"<getVariable>,GMDS_FACE>("face2quantizedNodes"); + std::cout<<"> Adding existing faces on the fixed mesh"<getVariable("is_affected_by_dipole"); for (auto f_id:m_mesh->faces()) { - // Get the half edges of the face - NonConformalHalfEdge e1 = m_half_edges[4*f_id]; - NonConformalHalfEdge e2 = m_half_edges[4*f_id+1]; - NonConformalHalfEdge e3 = m_half_edges[4*f_id+2]; - NonConformalHalfEdge e4 = m_half_edges[4*f_id+3]; - std::vector> nodesIDs(m_half_edges_lengths[e2.id()]+1); - std::vector row(m_half_edges_lengths[e1.id()]+1); - // first row - for (int i = 0; i < m_half_edges_lengths[e1.id()]+1; i++) - row[i] = m_half_edges_to_new_nodes[e1.id()][i]; - nodesIDs[0] = row; - // Last row - for (int i = 0; i < m_half_edges_lengths[e3.id()]+1; i++) - row[i] = m_half_edges_to_new_nodes[e3.id()][m_half_edges_lengths[e3.id()]-i]; - nodesIDs[m_half_edges_lengths[e2.id()]] = row; - for (int j = 1; j < m_half_edges_lengths[e2.id()]; j++) + if (var->value(f_id) == 0) { - row[0] = m_half_edges_to_new_nodes[e4.id()][m_half_edges_lengths[e4.id()]-j]; - row[m_half_edges_lengths[e1.id()]] = m_half_edges_to_new_nodes[e2.id()][j]; - for (int i = 1; i < m_half_edges_lengths[e1.id()]; i++) - { - math::Point P1 = m_quantized_mesh->get(m_half_edges_to_new_nodes[e1.id()][i]).point(); - math::Point P2 = m_quantized_mesh->get(m_half_edges_to_new_nodes[e3.id()][m_half_edges_lengths[e3.id()]-i]).point(); - math::Point P = P1 + (double(j)/double(m_half_edges_lengths[e2.id()]))*(P2+(-1.)*P1); - Node n = m_quantized_mesh->newNode(P); - row[i] = n.id(); - } - nodesIDs[j] = row; + Face f = m_mesh->get(f_id); + std::vector nodesIds; + for (auto n:f.get()) + nodesIds.push_back(n.id()); + m_fixed_mesh->newFace(nodesIds); } - f2qn->set(f_id,nodesIDs); } } /*----------------------------------------------------------------------------*/ -void QuantizationSolver::buildQuantizedMeshFaces() +void QuantizationSolver::buildOppFacesInFixedMesh(int AHalfEdgeId, Node AN1, Node AN2) { - std::cout<<"> Building faces of the quantized mesh"<getVariable>,GMDS_FACE>("face2quantizedNodes"); - for (auto f_id:m_mesh->faces()) + NonConformalHalfEdge he = m_half_edges[AHalfEdgeId]; + Face f = he.face(); + + // Build the faces containing AN1 and AN2 + int id1 = -1; + // Find the face where AN1 is + for (auto edge:he.edges()) + { + if (AN1.id() == edge.get()[0].id() || AN1.id() == edge.get()[1].id()) + break; + if (isOnSegment(AN1.point(),edge.get()[0].point(),edge.get()[1].point())) + { + for (auto f2:edge.get()) + { + if (f2.id() != f.id()) + { + id1 = f2.id(); + break; + } + } + } + } + + int id2 = -1; + // Find the face where AN2 is + for (auto edge:he.edges()) + { + if (AN2.id() == edge.get()[0].id() || AN2.id() == edge.get()[1].id()) + break; + if (isOnSegment(AN2.point(),edge.get()[0].point(),edge.get()[1].point())) + { + for (auto f2:edge.get()) + { + if (f2.id() != f.id()) + { + id2 = f2.id(); + break; + } + } + } + } + + // Now we build the eventual faces containing the new points + if (id1 >= 0 && id1 == id2) + { + Face f2 = m_mesh->get(id1); + std::vector new_face = insertPoint(AN1,f2.get()); + std::vector nodes; + for (auto id:new_face) + nodes.push_back(m_fixed_mesh->get(id)); + new_face = insertPoint(AN2,nodes); + m_fixed_mesh->newFace(new_face); + } + else { - std::vector> nodes = f2qn->value(f_id); - int I = nodes.size(); - int J = nodes[0].size(); - TCellID i1,i2,i3,i4; - for (int i = 0; i < I-1; i++) + if (id1 >= 0) + { + Face f2 = m_mesh->get(id1); + std::vector new_face = insertPoint(AN1,f2.get()); + m_fixed_mesh->newFace(new_face); + } + if (id2 >= 0) { - for (int j = 0; j < J-1; j++) + Face f2 = m_mesh->get(id2); + std::vector new_face = insertPoint(AN2,f2.get()); + m_fixed_mesh->newFace(new_face); + } + } + + // Build the other faces + for (auto edge:he.edges()) + { + for (auto f2:edge.get()) + { + if (f2.id() != f.id() && f2.id() != id1 &&f2.id() != id2) { - i1 = nodes[i][j]; - i2 = nodes[i+1][j]; - i3 = nodes[i+1][j+1]; - i4 = nodes[i][j+1]; - m_quantized_mesh->newFace({i1,i2,i3,i4}); + std::vector nodes; + for (auto n:f2.get()) + nodes.push_back(n.id()); + m_fixed_mesh->newFace(nodes); } } } } + +/*----------------------------------------------------------------------------*/ +void QuantizationSolver::buildFixedMesh(int AId1, int AId2) +{ + std::cout<<" "<get(he1.endNode().id()); + Node c2 = m_fixed_mesh->get(he2.endNode().id()); + Node c3 = m_fixed_mesh->get(he3.endNode().id()); + Node c4 = m_fixed_mesh->get(he4.endNode().id()); + + // Directions + math::Point d1 = c1.point()+(-1.)*c4.point(); + math::Point d2 = c2.point()+(-1.)*c1.point(); + math::Point d3 = c3.point()+(-1.)*c2.point(); + math::Point d4 = c4.point()+(-1.)*c3.point(); + + // Nodes on edges + // Find their distance to corner nodes + double h1,h2,h3,h4; + std::vector nodes_he2 = he2.getOrderedNodes(); + std::vector nodes_he4 = he4.getOrderedNodes(); + if (nodes_he2.size() == 2) + { + h1 = 1./5.; + h2 = 4./5.; + } + if (nodes_he2.size() >= 3) + { + if (1./5.+1./10. < nodes_he2[0].point().distance(nodes_he2[1].point())/vec(d2).norm()) + h1 = 1./5.; + else + h1 = 0.5*nodes_he2[0].point().distance(nodes_he2[1].point())/vec(d2).norm(); + if (4./5.-1./10. > nodes_he2[0].point().distance(nodes_he2[nodes_he2.size()-2].point())/vec(d2).norm()) + h2 = 4./5.; + else + h2 = 1.-0.5*nodes_he2[nodes_he2.size()-1].point().distance(nodes_he2[nodes_he2.size()-2].point())/vec(d2).norm(); + } + if (nodes_he4.size() == 2) + { + h3 = 1./5.; + h4 = 4./5.; + } + if (nodes_he4.size() >= 3) + { + if (1./5.+1./10. < nodes_he4[0].point().distance(nodes_he4[1].point())/vec(d4).norm()) + h3 = 1./5.; + else + h3 = 0.5*nodes_he4[0].point().distance(nodes_he4[1].point())/vec(d4).norm(); + if (4./5.-1./10. > nodes_he4[0].point().distance(nodes_he4[nodes_he4.size()-2].point())/vec(d4).norm()) + h4 = 4./5.; + else + h4 = 1.-0.5*nodes_he4[nodes_he4.size()-1].point().distance(nodes_he4[nodes_he4.size()-2].point())/vec(d4).norm(); + } + Node b1 = m_fixed_mesh->newNode(c1.point()+h1*d2); + Node b2 = m_fixed_mesh->newNode(c1.point()+h2*d2); + Node b3 = m_fixed_mesh->newNode(c3.point()+h3*d4); + Node b4 = m_fixed_mesh->newNode(c3.point()+h4*d4); + + // Build the faces opposite to he2 and he4 + buildOppFacesInFixedMesh(he2.id(),b1,b2); + buildOppFacesInFixedMesh(he4.id(),b3,b4); + + // Directions + math::Point dg = b4.point()+(-1.)*b1.point(); + math::Point dd = b3.point()+(-1.)*b2.point(); + + // Nodes on inner edges + Node s1 = m_fixed_mesh->newNode(b1.point()+(1./6.)*dg); + Node s4 = m_fixed_mesh->newNode(b2.point()+(1./6.)*dd); + Node s5 = m_fixed_mesh->newNode(b2.point()+(5./6.)*dd); + Node s8 = m_fixed_mesh->newNode(b1.point()+(5./6.)*dg); + Node s2 = m_fixed_mesh->newNode(s1.point()+(1./3.)*s4.point()+(-1./3.)*s1.point()); + Node s3 = m_fixed_mesh->newNode(s1.point()+(2./3.)*s4.point()+(-2./3.)*s1.point()); + Node s6 = m_fixed_mesh->newNode(s8.point()+(2./3.)*s5.point()+(-2./3.)*s8.point()); + Node s7 = m_fixed_mesh->newNode(s8.point()+(1./3.)*s5.point()+(-1./3.)*s8.point()); + + // Directions + math::Point d5 = s8.point()+(-1.)*s1.point(); + math::Point d7 = s7.point()+(-1.)*s2.point(); + math::Point d6 = (1./2.)*(d5+d7); + math::Point d8 = s6.point()+(-1.)*s3.point(); + math::Point d9 = s5.point()+(-1.)*s4.point(); + + // Inner nodes + Node i1 = m_fixed_mesh->newNode(s1.point()+(1./4.)*d5); + Node i2 = m_fixed_mesh->newNode(s2.point()+(1./4.)*d7); + Node i3 = m_fixed_mesh->newNode(s1.point()+(2./4.)*d5); + Node i4 = m_fixed_mesh->newNode((1./2.)*s1.point()+(1./2.)*s2.point()+(1./2.)*d6); + Node i5 = m_fixed_mesh->newNode(s3.point()+(1./2.)*d8); + Node i6 = m_fixed_mesh->newNode(s4.point()+(1./2.)*d9); + Node i7 = m_fixed_mesh->newNode(s1.point()+(3./4.)*d5); + Node i8 = m_fixed_mesh->newNode(s2.point()+(3./4.)*d7); + + // Create new faces + std::vector new_face; + for (auto node:he1.getOrderedNodes()) + new_face.push_back(node.id()); + new_face.push_back(b1.id()); + new_face.push_back(s1.id()); + new_face.push_back(i1.id()); + new_face.push_back(i3.id()); + new_face.push_back(i7.id()); + new_face.push_back(s8.id()); + new_face.push_back(b4.id()); + m_fixed_mesh->newFace(new_face); + + new_face.clear(); + for (auto node:he3.getOrderedNodes()) + new_face.push_back(node.id()); + new_face.push_back(b3.id()); + new_face.push_back(s5.id()); + new_face.push_back(i6.id()); + new_face.push_back(s4.id()); + new_face.push_back(b2.id()); + m_fixed_mesh->newFace(new_face); + + new_face.clear(); + new_face.push_back(b3.id()); + for (auto node:he4.getOrderedNodes()) + { + if (node.id() != c3.id() && node.id() != c4.id()) + new_face.push_back(node.id()); + } + new_face.push_back(b4.id()); + new_face.push_back(s8.id()); + new_face.push_back(s7.id()); + new_face.push_back(s6.id()); + new_face.push_back(s5.id()); + m_fixed_mesh->newFace(new_face); + + new_face.clear(); + new_face.push_back(b1.id()); + for (auto node:he2.getOrderedNodes()) + { + if (node.id() != c1.id() && node.id() != c2.id()) + new_face.push_back(node.id()); + } + new_face.push_back(b2.id()); + new_face.push_back(s4.id()); + new_face.push_back(s3.id()); + new_face.push_back(s2.id()); + new_face.push_back(s1.id()); + m_fixed_mesh->newFace(new_face); + + m_fixed_mesh->newFace({i7.id(),i8.id(),s7.id(),s8.id()}); + m_fixed_mesh->newFace({i3.id(),i4.id(),i8.id(),i7.id()}); + m_fixed_mesh->newFace({i1.id(),i2.id(),i4.id(),i3.id()}); + m_fixed_mesh->newFace({s1.id(),s2.id(),i2.id(),i1.id()}); + m_fixed_mesh->newFace({i8.id(),i5.id(),s6.id(),s7.id()}); + m_fixed_mesh->newFace({i2.id(),i5.id(),i8.id(),i4.id()}); + m_fixed_mesh->newFace({s2.id(),s3.id(),i5.id(),i2.id()}); + m_fixed_mesh->newFace({i5.id(),i6.id(),s5.id(),s6.id()}); + m_fixed_mesh->newFace({s3.id(),s4.id(),i6.id(),i5.id()}); + + std::cout<<"==============================="< Imposing degenerated half edges to be of length 0"<markAsZero(id); + std::cout<<"Imposing a half edge to 0"<> QuantizationSolver::buildCompleteSolution() +{ + std::cout<<" "<> sidesSet = sides(); + m_quantization_graph->nonZeroGroups(sidesSet); + // Set condition to separate singularities + markTJunctions(); + std::vector non_zeros = nonZeroHalfEdges(); + m_quantization_graph->nonZeroVerticies(non_zeros); + m_quantization_graph->updateConnectivity(); + //forceZeroEdgesToZeroLength(); + m_quantization_graph->buildCompleteSolution(); + //m_quantization_graph->roughlyRepairSolution(); + //m_quantization_graph->display(); // Display the graph + //m_quantization_graph->displaySolution(); // Display the quantization solution + std::vector> problematicCouples;// = m_quantization_graph->checkSolutionValidity(); + m_quantization_graph->improveSolution(m_mesh_size); + + std::cout<<"===================================="< +#include +/*----------------------------------------------------------------------------*/ +namespace gmds { +/*----------------------------------------------------------------------------*/ +TrianglesRemover::TrianglesRemover(gmds::Mesh &AMesh) +{ + // Non conformal quad mesh + m_degenerated_mesh = &AMesh; + // Degenerated mesh nodes/T-mesh nodes correspondance + m_degenerated_mesh->newVariable("degMeshNode2TMeshNode"); + // Node/group correspondance + m_degenerated_mesh->newVariable("degenerated_node_to_group"); + + // Triangle fan id + try {m_degenerated_mesh->getVariable("fan_id");} + catch (GMDSException& e){m_degenerated_mesh->newVariable("fan_id");} + // Triangle fans angle + try {m_degenerated_mesh->getVariable("fan_angle");} + catch (GMDSException& e){m_degenerated_mesh->newVariable("fan_angle");} + // Separatrices + try {m_degenerated_mesh->getVariable("belongs_to_a_separatrix");} + catch (GMDSException& e){m_degenerated_mesh->newVariable("belongs_to_a_separatrix");} + // Corners + try {m_degenerated_mesh->getVariable("corner");} + catch (GMDSException& e){m_degenerated_mesh->newVariable("corner");} + // Internal constraints + try {m_degenerated_mesh->getVariable("internal_constraint");} + catch (GMDSException& e){m_degenerated_mesh->newVariable("internal_constraint");} + try {m_degenerated_mesh->getVariable("belong_to_an_internal_constraint");} + catch (GMDSException& e){m_degenerated_mesh->newVariable("belong_to_an_internal_constraint");} + // Triangles + try {m_degenerated_mesh->getVariable("is_a_triangle");} + catch (GMDSException& e){m_degenerated_mesh->newVariable("is_a_triangle");} + + // T-mesh + m_t_mesh = new Mesh(MeshModel(DIM3 | E | N | F | + E2N | N2E | F2N | N2F | F2E | E2F)); + // T-junctions on the T-mesh + m_t_mesh->newVariable("is_a_T-junction"); + // Corners + m_t_mesh->newVariable("corner"); + // Internal constraints + m_t_mesh->newVariable("internal_constraint"); + m_t_mesh->newVariable("belong_to_an_internal_constraint"); + // Marking with 1 triangles created by remeshing the triangles fans + m_t_mesh->newVariable("is_an_unwanted_triangle"); + // Marking with 1 vertices of unwanted triangles + m_t_mesh->newVariable("is_an_unwanted_triangle_vertex"); + // Marking with 1 nodes created by removing the unwanted triangles + m_t_mesh->newVariable("is_a_new_node"); + + // Final T-mesh + m_final_t_mesh = new Mesh(MeshModel(DIM3 | E | N | F | + E2N | N2E | F2N | N2F | F2E | E2F)); + // T-junctions on the T-mesh + m_final_t_mesh->newVariable("is_a_T-junction"); + // Corners + m_final_t_mesh->newVariable("corner"); + // Internal constraints + m_final_t_mesh->newVariable("internal_constraint"); + m_final_t_mesh->newVariable("belong_to_an_internal_constraint"); + // Marking with 1 nodes created by removing the unwanted triangles + m_final_t_mesh->newVariable("is_a_new_node"); +} + +/*----------------------------------------------------------------------------*/ +void TrianglesRemover::writeDegeneratedMesh(std::basic_string AFileName) +{ + std::cout<<"> Writing the degenerated mesh"< AFileName) +{ + std::cout<<"> Writing the T-mesh"< TrianglesRemover::fan(int AFanId) +{ + auto fan_id = m_degenerated_mesh->getVariable("fan_id"); + auto separatrix = m_degenerated_mesh->getVariable("belongs_to_a_separatrix"); + auto tri = m_degenerated_mesh->getVariable("is_a_triangle"); + std::vector output_fan; + std::queue faces_to_add; + auto open = m_degenerated_mesh->newVariable("open"); + auto visited = m_degenerated_mesh->newVariable("visited"); + // Find the origin triangles of the fan + + for (auto f_id:m_degenerated_mesh->faces()) + { + if (tri->value(f_id) == 1) + { + Face f = m_degenerated_mesh->get(f_id); + int nb_vertices = 0; + Node n1,n2; + bool n1_found = false; + for (auto n:f.get()) + { + if (fan_id->value(n.id()) == AFanId) + { + nb_vertices += 1; + if (n1_found) + n2 = n; + else + { + n1 = n; + n1_found = true; + } + } + } + if (nb_vertices == 2) + { + faces_to_add.push(f); + visited->set(f_id,1); + Edge e1 = getEdge(n1,n2); + Edge e2 = opp(f,e1); + if (separatrix->value(e2.id()) == 0) + open->set(e2.id(),1); + } + } + } + + // Fill the fan + while (!faces_to_add.empty()) + { + Face f1 = faces_to_add.front(); + faces_to_add.pop(); + output_fan.push_back(f1); + for (auto e:f1.get()) + { + if (open->value(e.id()) == 1) + { + for (auto f2:e.get()) + { + if (visited->value(f2.id()) == 0) + { + faces_to_add.push(f2); + visited->set(f2.id(),1); + Edge e2 = opp(f2,e); + if (separatrix->value(e2.id()) == 0) + open->set(e2.id(),1); + } + } + } + } + } + + // // To visualize the fan + // auto bttf = m_degenerated_mesh->newVariable("belongs_to_the_fan"); + // for (auto f:output_fan) + // bttf->set(f.id(),1); + + m_degenerated_mesh->deleteVariable(GMDS_EDGE,open); + m_degenerated_mesh->deleteVariable(GMDS_FACE,visited); + return output_fan; +} + +/*----------------------------------------------------------------------------*/ +std::vector TrianglesRemover::outline(std::vector &AV) +{ + auto n2g = m_degenerated_mesh->getVariable("degenerated_node_to_group"); + // Mark the faces of the shape + auto bttf = m_degenerated_mesh->newVariable("belongs_to_the_fan"); + for (auto f:AV) + bttf->set(f.id(),1); + // Mark the edges of the outline + auto outline_edges = m_degenerated_mesh->newVariable("outline"); + for (auto f:AV) + { + for (auto e:f.get()) + { + if (e.get().size() == 1) + outline_edges->set(e.id(),1); + for (auto f2:e.get()) + { + if (bttf->value(f2.id()) == 0) + outline_edges->set(e.id(),1); + } + } + } + // Find the nodes of the outline + std::vector outline_nodes; + // Find a face and an edge at the border of the fan + Face f_start; + Edge e_start; + bool found = false; + for (auto f:AV) + { + for (auto e:f.get()) + { + if (outline_edges->value(e.id()) == 1) + { + if (n2g->value(e.get()[0].id()) != n2g->value(e.get()[1].id())) + { + f_start = f; + e_start = e; + found = true; + break; + } + + } + } + if (found) + break; + } + // // Orientate e_start with respect to f_start + // int pos1,pos2; + // for (int i = 0; i < 4; i++) + // { + // if (e_start.get()[0].id() == f_start.get()[i].id()) + // pos1 = i; + // if (e_start.get()[1].id() == f_start.get()[i].id()) + // pos2 = i; + // } + // Node prev,nxt; + // if ((pos1 < 3 && pos1 < pos2) || (pos1 == 3 && pos2 == 0)) + // { + // prev = e_start.get()[0]; + // nxt = e_start.get()[1]; + // } + // else + // { + // prev = e_start.get()[1]; + // nxt = e_start.get()[0]; + // } + Node prev = m_degenerated_nodes_groups[n2g->value(e_start.get()[0].id())][0]; + Node nxt = m_degenerated_nodes_groups[n2g->value(e_start.get()[1].id())][0]; + + outline_nodes.push_back(prev); + Node old_prev, candidate; + while (nxt.id() != outline_nodes[0].id()) + { + old_prev = prev; + prev = nxt; + outline_nodes.push_back(prev); + bool found = false; + for (auto n:m_degenerated_nodes_groups[n2g->value(prev.id())]) + { + for (auto e:n.get()) + { + if (n2g->value(e.get()[0].id()) == n2g->value(e.get()[1].id())) + continue; + if (n2g->value(e.get()[0].id()) == n2g->value(prev.id())) + candidate = m_degenerated_nodes_groups[n2g->value(e.get()[1].id())][0]; + else + candidate = m_degenerated_nodes_groups[n2g->value(e.get()[0].id())][0]; + if (outline_edges->value(e.id()) == 1 && candidate.id() != old_prev.id() && n2g->value(candidate.id()) != n2g->value(n.id())) + { + nxt = candidate; + found = true; + break; + } + } + if (found) + break; + } + // for (auto e:prev.get()) + // { + // if (e.get()[0].id() == prev.id()) + // candidate = e.get()[1]; + // else + // candidate = e.get()[0]; + // if (outline_edges->value(e.id()) == 1 && candidate.id() != old_prev.id()) + // { + // nxt = candidate; + // break; + // } + // } + } + + + // // To visualize the outline + // auto btto = m_degenerated_mesh->newVariable("belongs_to_the_outline"); + // for (auto n:outline_nodes) + // btto->set(n.id(),1); + + m_degenerated_mesh->deleteVariable(GMDS_FACE,bttf); + m_degenerated_mesh->deleteVariable(GMDS_EDGE,outline_edges); + + return outline_nodes; +} + +/*----------------------------------------------------------------------------*/ +void TrianglesRemover::buildTMesh() +{ + std::cout<<"> Building the T-mesh"<getVariable("fan_id"); + auto dn2tmn = m_degenerated_mesh->getVariable("degMeshNode2TMeshNode"); + auto fan_angle = m_degenerated_mesh->getVariable("fan_angle"); + auto tj = m_t_mesh->getVariable("is_a_T-junction"); + auto corner1 = m_degenerated_mesh->getVariable("corner"); + auto corner2 = m_t_mesh->getVariable("corner"); + auto constraints_nodes1 = m_degenerated_mesh->getVariable("belong_to_an_internal_constraint"); + auto constraints_nodes2 = m_t_mesh->getVariable("belong_to_an_internal_constraint"); + auto n2g = m_degenerated_mesh->getVariable("degenerated_node_to_group"); + auto iaut = m_t_mesh->getVariable("is_an_unwanted_triangle"); + auto iautv = m_t_mesh->getVariable("is_an_unwanted_triangle_vertex"); + auto of2tmf = m_degenerated_mesh->newVariable("oldFace2tmeshFace"); + // Find the number of fans + int nb_fans = -1; + for (auto n_id:m_degenerated_mesh->nodes()) + { + if (fan_id->value(n_id) > nb_fans) + nb_fans = fan_id->value(n_id); + } + nb_fans += 1; + // list of fans + std::vector> fans; + for (int i = 0; i < nb_fans; i++) + { + std::vector tri_fan = fan(i); + fans.push_back(tri_fan); + } + // Type of the fan (1 if there is 1 quad to put, 2 if 2 quads) + std::vector fan_type(fans.size()); + for (auto n_id:m_degenerated_mesh->nodes()) + { + if (fan_id->value(n_id) >= 0) + { + if (fabs(fan_angle->value(n_id)-3.*M_PI/2.) < 0.1) + fan_type[fan_id->value(n_id)] = 1; + else + fan_type[fan_id->value(n_id)] = 2; + } + } + // Mark the faces belonging to a fan + auto btaf = m_degenerated_mesh->newVariable("belongs_to_a_fan"); + for (auto tri_fan:fans) + { + for (auto f:tri_fan) + { + btaf->set(f.id(),1); + } + } + // Mark the fan vertices already added in the T-mesh + std::vector fan_vertex(nb_fans); + for (int i = 0; i < nb_fans; i++) + fan_vertex[i] = -1; + + // Build the nodes of the T-mesh + for (auto n_id:m_degenerated_mesh->nodes()) + { + int fanId = fan_id->value(n_id); + if (fanId >= 0) + { + if (fan_vertex[fanId] == -1) + { + Node new_node = m_t_mesh->newNode(m_degenerated_mesh->get(n_id).point()); + fan_vertex[fanId] = new_node.id(); + dn2tmn->set(n_id,new_node.id()); + tj->set(new_node.id(),-1); + corner2->set(new_node.id(),1); + bool is_on_constraint = false; + for (auto node:m_degenerated_nodes_groups[n2g->value(n_id)]) + { + if (constraints_nodes1->value(node.id()) == 1) + { + is_on_constraint = true; + break; + } + } + if (is_on_constraint) + constraints_nodes2->set(new_node.id(),1); + } + else + { + dn2tmn->set(n_id,fan_vertex[fanId]); + } + } + else + { + Node n = m_degenerated_mesh->get(n_id); + bool isInAFan = true; + for (auto f:n.get()) + { + if (btaf->value(f.id()) == 0) + { + isInAFan = false; + break; + } + } + if (!isInAFan) + { + Node new_node = m_t_mesh->newNode(m_degenerated_mesh->get(n_id).point()); + dn2tmn->set(n_id,new_node.id()); + tj->set(new_node.id(),-1); + corner2->set(new_node.id(),corner1->value(n_id)); + constraints_nodes2->set(new_node.id(),constraints_nodes1->value(n_id)); + } + else + { + dn2tmn->set(n_id,-1); + } + } + } + + // Building the faces of the T-mesh + for (auto f_id:m_degenerated_mesh->faces()) + { + if (btaf->value(f_id) == 0) + { + std::vector nodes_id; + Face f = m_degenerated_mesh->get(f_id); + TCellID tri_ver; + for (auto n:f.get()) + { + bool already_seen = false; + for (auto m_id:nodes_id) + { + if (m_id == dn2tmn->value(n.id())) + { + already_seen = true; + tri_ver = m_id; + break; + } + } + if (!already_seen) + nodes_id.push_back(dn2tmn->value(n.id())); + } + Face new_face = m_t_mesh->newFace(nodes_id); + if (nodes_id.size() == 3) + { + iaut->set(new_face.id(),1); + iautv->set(tri_ver,1); + } + of2tmf->set(f_id,new_face.id()); + } + else + { + of2tmf->set(f_id,-1); + } + } + for (int i = 0; i < fans.size(); i++) + { + std::vector tri_fan = fans[i]; + std::vector outline_nodes = outline(tri_fan); + if (fan_type[i] == 1) + { + std::vector> sub_outlines = subOutlines1(outline_nodes,tri_fan,i); + + if (sub_outlines[1].size() < 2 || sub_outlines[2].size() < 2) + { + // It means that the fan is only one edge wide + // Get the edge + Edge fan_width; + for (auto e1:sub_outlines[0][sub_outlines[0].size()-1].get()) + { + for (auto e2:sub_outlines[3][0].get()) + { + if (e1.id() == e2.id()) + { + fan_width = e1; + break; + } + } + } + // Get the face behind fan_width + Face face_behind; + for (auto f:fan_width.get()) + { + if (btaf->value(f.id()) == 0) + { + face_behind = f; + break; + } + } + // Create the new node + Node middle_node = m_t_mesh->newNode((1./2.)*(fan_width.get()[0].point()+fan_width.get()[1].point())); + // Create the face meshing the fan + std::vector nodes_id; + for (auto n:sub_outlines[0]) + { + nodes_id.push_back(dn2tmn->value(n.id())); + } + nodes_id.push_back(middle_node.id()); + for (int i = 0; i < sub_outlines[3].size()-1; i++) + nodes_id.push_back(dn2tmn->value(sub_outlines[3][i].id())); + Face new_face = m_t_mesh->newFace(nodes_id); + + // Update the T-junctions + for (int k = 1; k < sub_outlines[0].size()-1; k++) + { + tj->set(dn2tmn->value(sub_outlines[0][k].id()),new_face.id()); + } + for (int k = 1; k < sub_outlines[3].size()-1; k++) + { + tj->set(dn2tmn->value(sub_outlines[3][k].id()),new_face.id()); + } + + // Update the face behind + if (of2tmf->value(face_behind.id()) == -1) + throw GMDSException("BuildTMesh() : configuration not yet implemented"); + Face fb = m_t_mesh->get(of2tmf->value(face_behind.id())); + nodes_id.clear(); + bool added = false; + std::vector adj_nodes = fb.get(); + for (int i = 0; i < adj_nodes.size(); i++) + { + if (added) + nodes_id.push_back(adj_nodes[i].id()); + else + { + if (adj_nodes[i].id() == dn2tmn->value(fan_width.get()[0].id()) && adj_nodes[(i+1)%(adj_nodes.size())].id() == dn2tmn->value(fan_width.get()[1].id())) + { + nodes_id.push_back(adj_nodes[i].id()); + nodes_id.push_back(middle_node.id()); + added = true; + } + else if (adj_nodes[i].id() == dn2tmn->value(fan_width.get()[1].id()) && adj_nodes[(i+1)%(adj_nodes.size())].id() == dn2tmn->value(fan_width.get()[0].id())) + { + nodes_id.push_back(adj_nodes[i].id()); + nodes_id.push_back(middle_node.id()); + added = true; + } + else + nodes_id.push_back(adj_nodes[i].id()); + } + + } + new_face = m_t_mesh->newFace(nodes_id); + m_t_mesh->deleteFace(fb); + tj->set(middle_node.id(),new_face.id()); + } + + else + { + std::vector nodes_id; + for (auto n:outline_nodes) + { + nodes_id.push_back(dn2tmn->value(n.id())); + } + Face new_face = m_t_mesh->newFace(nodes_id); + + // Update the T-junctions + for (auto suboutline:sub_outlines) + { + for (int k = 1; k < suboutline.size()-1; k++) + { + tj->set(dn2tmn->value(suboutline[k].id()),new_face.id()); + } + } + } + + } + if (fan_type[i] == 2) + { + std::vector> sub_outlines = subOutlines2(outline_nodes,tri_fan,i); + + // for (auto sub:sub_outlines) + // { + // std::cout<<"TEST "< nodes_id; + for (int j = 0; j < sub_outlines[0].size(); j++) + { + nodes_id.push_back(dn2tmn->value(sub_outlines[0][j].id())); + } + for (int j = 1; j < sub_outlines[1].size(); j++) + { + nodes_id.push_back(dn2tmn->value(sub_outlines[1][j].id())); + } + for (int j = 1; j < sub_outlines[2].size(); j++) + { + nodes_id.push_back(dn2tmn->value(sub_outlines[2][j].id())); + } + Face new_face = m_t_mesh->newFace(nodes_id); + + // Update the T-junctions + for (int j = 0; j <= 2; j++) + { + for (int k = 1; k < sub_outlines[j].size()-1; k++) + { + tj->set(dn2tmn->value(sub_outlines[j][k].id()),new_face.id()); + } + } + + + nodes_id.clear(); + for (int j = 0; j < sub_outlines[3].size(); j++) + { + nodes_id.push_back(dn2tmn->value(sub_outlines[3][j].id())); + } + for (int j = 1; j < sub_outlines[4].size(); j++) + { + nodes_id.push_back(dn2tmn->value(sub_outlines[4][j].id())); + } + for (int j = 1; j < sub_outlines[5].size(); j++) + { + nodes_id.push_back(dn2tmn->value(sub_outlines[5][j].id())); + } + new_face = m_t_mesh->newFace(nodes_id); + + // Update the T-junctions + for (int j = 3; j <= 5; j++) + { + for (int k = 1; k < sub_outlines[j].size()-1; k++) + { + tj->set(dn2tmn->value(sub_outlines[j][k].id()),new_face.id()); + } + } + } + } + m_degenerated_mesh->deleteVariable(GMDS_FACE,btaf); + m_degenerated_mesh->deleteVariable(GMDS_FACE,of2tmf); +} + +/*----------------------------------------------------------------------------*/ +void TrianglesRemover::buildDegeneratedNodesGroups() +{ + std::cout<<"> Building groups of degenerated nodes"<getVariable("fan_id"); + auto n2g = m_degenerated_mesh->getVariable("degenerated_node_to_group"); + for (auto n_id:m_degenerated_mesh->nodes()) + n2g->set(n_id,-1); + std::vector> groups; + int group_id = 0; + for (auto n_id:m_degenerated_mesh->nodes()) + { + std::vector group; + if (fan_id->value(n_id) == -1) + { + Node n = m_degenerated_mesh->get(n_id); + group.push_back(n); + groups.push_back(group); + n2g->set(n_id,group_id); + group_id += 1; + } + else + { + if (n2g->value(n_id) < 0) + { + int fanId = fan_id->value(n_id); + for (auto m_id:m_degenerated_mesh->nodes()) + { + if (fan_id->value(m_id) == fanId) + { + Node n = m_degenerated_mesh->get(m_id); + group.push_back(n); + n2g->set(m_id,group_id); + } + } + groups.push_back(group); + group_id += 1; + } + } + } + + m_degenerated_nodes_groups = groups; +} + +/*----------------------------------------------------------------------------*/ +std::vector> TrianglesRemover::subOutlines1(std::vector AOutline, std::vector AFan, int AFanId) +{ + auto fan_id = m_degenerated_mesh->getVariable("fan_id"); + auto bttf = m_degenerated_mesh->newVariable("belongs_to_the_fan"); + auto separatrix = m_degenerated_mesh->getVariable("belongs_to_a_separatrix"); + for (auto f:AFan) + { + bttf->set(f.id(),1); + } + // Find the triangle vertex + int pos; + for (int i = 0; i < AOutline.size(); i++) + { + if (fan_id->value(AOutline[i].id()) == AFanId) + { + pos = i; + break; + } + } + std::vector> sub_outlines; + std::vector suboutline,suboutline2; + int i = pos; + bool Continue = true; + while (Continue) + { + suboutline.push_back(AOutline[i]); + i = i+1; + if (i == AOutline.size()) + i = 0; + int NbFanFaces = 0; + for (auto f:AOutline[i].get()) + { + if (bttf->value(f.id()) == 1) + { + NbFanFaces += 1; + } + } + if (NbFanFaces == 1) + { + Continue = false; + } + } + suboutline.push_back(AOutline[i]); + sub_outlines.push_back(suboutline); + + suboutline.clear(); + Continue = true; + while (Continue) + { + suboutline.push_back(AOutline[i]); + i = i+1; + if (i == AOutline.size()) + i = 0; + int NbFanFaces = 0; + for (auto f:AOutline[i].get()) + { + if (bttf->value(f.id()) == 1) + { + NbFanFaces += 1; + } + } + if (NbFanFaces == 1) + { + Continue = false; + } + } + suboutline.push_back(AOutline[i]); + int half = suboutline.size()/2; + + // Look for singularities + bool singu_found = false; + int singu_pos; + for (int j = 1; j < suboutline.size()-1; j++) + { + int NbNonSingu = 0; + for (auto e:suboutline[j].get()) + { + if (separatrix->value(e.id()) == 0) + { + NbNonSingu += 1; + } + } + if (NbNonSingu == 0) + { + singu_found = true; + singu_pos = j; + break; + } + } + + + if (singu_found) + { + for (int j = 0; j <= singu_pos; j++) + { + suboutline2.push_back(suboutline[j]); + } + sub_outlines.push_back(suboutline2); + suboutline2.clear(); + for (int j = singu_pos; j < suboutline.size(); j++) + { + suboutline2.push_back(suboutline[j]); + } + sub_outlines.push_back(suboutline2); + } + else + { + for (int j = 0; j <= half; j++) + { + suboutline2.push_back(suboutline[j]); + } + sub_outlines.push_back(suboutline2); + suboutline2.clear(); + for (int j = half; j < suboutline.size(); j++) + { + suboutline2.push_back(suboutline[j]); + } + sub_outlines.push_back(suboutline2); + } + + + suboutline.clear(); + Continue = true; + while (Continue) + { + suboutline.push_back(AOutline[i]); + i = i+1; + if (i == AOutline.size()) + i = 0; + if (i == pos) + { + Continue = false; + } + } + suboutline.push_back(AOutline[i]); + sub_outlines.push_back(suboutline); + + m_degenerated_mesh->deleteVariable(GMDS_FACE,bttf); + return sub_outlines; +} + +/*----------------------------------------------------------------------------*/ +std::vector> TrianglesRemover::subOutlines2(std::vector AOutline, std::vector AFan, int AFanId) +{ + auto fan_id = m_degenerated_mesh->getVariable("fan_id"); + auto bttf = m_degenerated_mesh->newVariable("belongs_to_the_fan"); + auto separatrix = m_degenerated_mesh->getVariable("belongs_to_a_separatrix"); + for (auto f:AFan) + { + bttf->set(f.id(),1); + } + // Find the triangle vertex + int pos; + for (int i = 0; i < AOutline.size(); i++) + { + if (fan_id->value(AOutline[i].id()) == AFanId) + { + pos = i; + break; + } + } + std::vector> sub_outlines; + std::vector suboutline,suboutline2; + int i = pos; + bool Continue = true; + while (Continue) + { + suboutline.push_back(AOutline[i]); + i = i+1; + if (i == AOutline.size()) + i = 0; + int NbFanFaces = 0; + for (auto f:AOutline[i].get()) + { + if (bttf->value(f.id()) == 1) + { + NbFanFaces += 1; + } + } + if (NbFanFaces == 1) + { + Continue = false; + } + } + suboutline.push_back(AOutline[i]); + sub_outlines.push_back(suboutline); + + suboutline.clear(); + Continue = true; + while (Continue) + { + suboutline.push_back(AOutline[i]); + i = i+1; + if (i == AOutline.size()) + i = 0; + int NbFanFaces = 0; + for (auto f:AOutline[i].get()) + { + if (bttf->value(f.id()) == 1) + { + NbFanFaces += 1; + } + } + if (NbFanFaces == 1) + { + Continue = false; + } + } + suboutline.push_back(AOutline[i]); + int quarter = suboutline.size()/4; + int half = suboutline.size()/2; + + // Look for singularities + bool singu_found = false; + int singu_pos; + for (int j = 1; j < half; j++) + { + int NbNonSingu = 0; + for (auto e:suboutline[j].get()) + { + if (separatrix->value(e.id()) == 0) + { + NbNonSingu += 1; + } + } + if (NbNonSingu == 0) + { + singu_found = true; + singu_pos = j; + break; + } + } + + if (singu_found) + { + for (int j = 0; j <= singu_pos; j++) + { + suboutline2.push_back(suboutline[j]); + } + sub_outlines.push_back(suboutline2); + + suboutline2.clear(); + for (int j = singu_pos; j <= half; j++) + { + suboutline2.push_back(suboutline[j]); + } + sub_outlines.push_back(suboutline2); + } + else + { + for (int j = 0; j <= quarter; j++) + { + suboutline2.push_back(suboutline[j]); + } + sub_outlines.push_back(suboutline2); + + suboutline2.clear(); + for (int j = quarter; j <= half; j++) + { + suboutline2.push_back(suboutline[j]); + } + sub_outlines.push_back(suboutline2); + } + + + // Look for singularities + singu_found = false; + for (int j = half+1; j < suboutline.size()-1; j++) + { + int NbNonSingu = 0; + for (auto e:suboutline[j].get()) + { + if (separatrix->value(e.id()) == 0) + { + NbNonSingu += 1; + } + } + if (NbNonSingu == 0) + { + singu_found = true; + singu_pos = j; + break; + } + } + + if (singu_found) + { + suboutline2.clear(); + for (int j = half; j <= singu_pos; j++) + { + suboutline2.push_back(suboutline[j]); + } + sub_outlines.push_back(suboutline2); + + suboutline2.clear(); + for (int j = singu_pos; j < suboutline.size(); j++) + { + suboutline2.push_back(suboutline[j]); + } + sub_outlines.push_back(suboutline2); + } + else + { + suboutline2.clear(); + for (int j = half; j <= half+quarter; j++) + { + suboutline2.push_back(suboutline[j]); + } + sub_outlines.push_back(suboutline2); + + suboutline2.clear(); + for (int j = half+quarter; j < suboutline.size(); j++) + { + suboutline2.push_back(suboutline[j]); + } + sub_outlines.push_back(suboutline2); + } + + + suboutline.clear(); + Continue = true; + while (Continue) + { + suboutline.push_back(AOutline[i]); + i = i+1; + if (i == AOutline.size()) + i = 0; + if (i == pos) + { + Continue = false; + } + } + suboutline.push_back(AOutline[i]); + sub_outlines.push_back(suboutline); + + m_degenerated_mesh->deleteVariable(GMDS_FACE,bttf); + return sub_outlines; +} + +/*----------------------------------------------------------------------------*/ +Mesh TrianglesRemover::getTMesh() +{ + return *m_t_mesh; +} + +/*----------------------------------------------------------------------------*/ +void TrianglesRemover::setTMeshConnectivity() +{ + std::cout<<"> Setting T-mesh connectivity"< Marking internal constraints on edges of the T-mesh"<getVariable("belong_to_an_internal_constraint"); + auto constraints_nodes2 = m_t_mesh->getVariable("belong_to_an_internal_constraint"); + auto constraints_edges1 = m_degenerated_mesh->getVariable("internal_constraint"); + auto constraints_edges2 = m_t_mesh->getVariable("internal_constraint"); + auto dmn2tmn = m_degenerated_mesh->getVariable("degMeshNode2TMeshNode"); + for (auto e_id:m_degenerated_mesh->edges()) + { + if (constraints_edges1->value(e_id) == 1) + { + Edge e = m_degenerated_mesh->get(e_id); + Node n1 = e.get()[0]; + Node n2 = e.get()[1]; + if (dmn2tmn->value(n1.id()) >= 0 && dmn2tmn->value(n2.id()) >= 0 && dmn2tmn->value(n1.id()) != dmn2tmn->value(n2.id())) + { + Node m1 = m_t_mesh->get(dmn2tmn->value(n1.id())); + Node m2 = m_t_mesh->get(dmn2tmn->value(n2.id())); + try + { + Edge e2 = getEdge(m1,m2); + constraints_edges2->set(e2.id(),1); + } + catch (GMDSException& e){} + } + } + } +} + +/*----------------------------------------------------------------------------*/ +void TrianglesRemover::transformUnwantedTrianglesIntoQuads() +{ + std::cout<<"> Transforming the unwanted triangles into quads"<getVariable("is_an_unwanted_triangle"); + auto iautv = m_t_mesh->getVariable("is_an_unwanted_triangle_vertex"); + auto constraints_nodes = m_t_mesh->getVariable("belong_to_an_internal_constraint"); + auto constraints_edges = m_t_mesh->getVariable("internal_constraint"); + auto tj = m_t_mesh->getVariable("is_a_T-junction"); + auto iann = m_t_mesh->getVariable("is_a_new_node"); + // Mark edges belonging to triangles and triangles vertices + auto tri_edge = m_t_mesh->newVariable("is_a_tri_edge"); + for (auto n_id:m_t_mesh->nodes()) + { + if (iautv->value(n_id) == 1) + { + Node vertex = m_t_mesh->get(n_id); + std::vector adj_edges = vertex.get(); + adj_edges = sortEdges(vertex,adj_edges); + for (auto e:adj_edges) + { + bool is_a_tri_edge = false; + for (auto f:e.get()) + { + if (iaut->value(f.id()) == 1) + { + is_a_tri_edge = true; + break; + } + } + if (is_a_tri_edge) + tri_edge->set(e.id(),1); + } + // Identify the fan of tri edges + std::vector edges_fan; + int start_pos; + for (int i = 0; i < adj_edges.size(); i++) + { + if (tri_edge->value(adj_edges[i].id()) == 0 && tri_edge->value(adj_edges[(i+1)%(adj_edges.size())].id()) == 1 && tri_edge->value(adj_edges[(i+2)%(adj_edges.size())].id()) == 1) + { + start_pos = i; + break; + } + } + edges_fan.push_back(adj_edges[start_pos]); + int incr = (start_pos+1)%(adj_edges.size()); + while (tri_edge->value(adj_edges[incr].id()) == 1) + { + edges_fan.push_back(adj_edges[incr]); + incr = (incr + 1)%(adj_edges.size()); + } + edges_fan.push_back(adj_edges[incr]); + // Check that the fan is well oriented + if (constraints_edges->value(edges_fan[1].id()) == 1) + std::reverse(edges_fan.begin(),edges_fan.end()); + + // Create the new points and quads + math::Vector dir = edge2vec(edges_fan[0],vertex); + std::vector new_nodes; + Node new_node; + math::Point P; + for (int i = 1; i < edges_fan.size()-2; i++) + { + P = vertex.point() + (double(i)/double(edges_fan.size()-2))*dir; + new_node = m_t_mesh->newNode(P); + new_nodes.push_back(new_node.id()); + } + + std::vector above_nodes; + Face neighbouring_face = getCommonFace(edges_fan[0],edges_fan[1]); + TCellID right_node,left_node,last_node_added; + if (edges_fan[0].get()[0].id() == vertex.id()) + right_node = edges_fan[0].get()[1].id(); + else + right_node = edges_fan[0].get()[0].id(); + if (edges_fan[1].get()[0].id() == vertex.id()) + left_node = edges_fan[1].get()[1].id(); + else + left_node = edges_fan[1].get()[0].id(); + for (auto n:neighbouring_face.get()) + { + if (n.id() != vertex.id() && n.id() != right_node && n.id() != left_node) + { + above_nodes.push_back(n.id()); + break; + } + } + above_nodes.push_back(left_node); + last_node_added = left_node; + for (int i = 1; i < edges_fan.size()-2; i++) + { + Face f = getCommonFace(edges_fan[i],edges_fan[i+1]); + for (auto n:f.get()) + { + if (n.id() != vertex.id() && n.id() != last_node_added) + { + above_nodes.push_back(n.id()); + last_node_added = n.id(); + break; + } + } + m_t_mesh->deleteFace(f); + } + m_t_mesh->deleteFace(neighbouring_face); + std::vector below_nodes; + below_nodes.push_back(vertex.id()); + for (auto m_id:new_nodes) + below_nodes.push_back(m_id); + below_nodes.push_back(right_node); + // Create the new quads + Face new_face; + std::vector new_face_ids; + for (int i = 0; i < below_nodes.size()-1; i++) + { + new_face_ids = {below_nodes[i],below_nodes[i+1],above_nodes[above_nodes.size()-2-i],above_nodes[above_nodes.size()-1-i]}; + new_face = m_t_mesh->newFace(new_face_ids); + } + // Change the behind face + Face behind_face; + for (auto f:edges_fan[0].get()) + { + if (f.id() != neighbouring_face.id()) + { + behind_face = f; + break; + } + } + new_face_ids.clear(); + std::vector adj_nodes = behind_face.get(); + bool already_added = false; + for (int i = 0; i < adj_nodes.size(); i++) + { + if (already_added) + new_face_ids.push_back(adj_nodes[i].id()); + else + { + if (adj_nodes[i].id() == vertex.id() && adj_nodes[(i+1)%adj_nodes.size()].id() == right_node) + { + new_face_ids.push_back(adj_nodes[i].id()); + for (auto id:new_nodes) + new_face_ids.push_back(id); + already_added = true; + } + else if (adj_nodes[(i+1)%adj_nodes.size()].id() == vertex.id() && adj_nodes[i].id() == right_node) + { + new_face_ids.push_back(adj_nodes[i].id()); + for (int j = 0; j < new_nodes.size(); j++) + new_face_ids.push_back(new_nodes[new_nodes.size()-1-j]); + already_added = true; + } + else + new_face_ids.push_back(adj_nodes[i].id()); + } + } + new_face = m_t_mesh->newFace(new_face_ids); + for (auto id:new_nodes) + tj->set(id,new_face.id()); + m_t_mesh->deleteFace(behind_face); + if (constraints_edges->value(edges_fan[0].id()) == 1) + { + for (auto id:new_nodes) + { + constraints_nodes->set(id,1); + iann->set(id,1); + } + } + } + } + + + m_t_mesh->deleteVariable(GMDS_EDGE,tri_edge); +} + +/*----------------------------------------------------------------------------*/ +void TrianglesRemover::buildFinalTMesh() +{ + std::cout<<"> Building the final T-mesh"<getVariable("is_a_T-junction"); + auto c1 = m_t_mesh->getVariable("corner"); + auto btic1 = m_t_mesh->getVariable("belong_to_an_internal_constraint"); + auto tj2 = m_final_t_mesh->getVariable("is_a_T-junction"); + auto c2 = m_final_t_mesh->getVariable("corner"); + auto btic2 = m_final_t_mesh->getVariable("belong_to_an_internal_constraint"); + auto of2nf = m_t_mesh->newVariable("oldFace2newFace"); + auto iann1 = m_t_mesh->getVariable("is_a_new_node"); + auto iann2 = m_final_t_mesh->getVariable("is_a_new_node"); + // Build nodes + Node n,new_node; + for (auto n_id:m_t_mesh->nodes()) + { + n = m_t_mesh->get(n_id); + new_node = m_final_t_mesh->newNode(n.point()); + c2->set(new_node.id(),c1->value(n_id)); + btic2->set(new_node.id(),btic1->value(n_id)); + iann2->set(new_node.id(),iann1->value(n_id)); + } + // Build faces + Face f,new_face; + for (auto f_id:m_t_mesh->faces()) + { + f = m_t_mesh->get(f_id); + std::vector ids; + for (auto n:f.get()) + ids.push_back(n.id()); + new_face = m_final_t_mesh->newFace(ids); + of2nf->set(f_id,new_face.id()); + } + // Update the T-junctions + for (auto n_id:m_final_t_mesh->nodes()) + { + if (tj1->value(n_id) == -1) + tj2->set(n_id,-1); + else + tj2->set(n_id,of2nf->value(tj1->value(n_id))); + } + + m_t_mesh->deleteVariable(GMDS_FACE,of2nf); +} + +/*----------------------------------------------------------------------------*/ +void TrianglesRemover::writeFinalTMesh(std::basic_string AFileName) +{ + std::cout<<"> Writing the final T-mesh"< Setting the final T-mesh connectivity"< Marking internal constraints on the edges of the final T-mesh"<getVariable("internal_constraint"); + auto btic1 = m_t_mesh->getVariable("belong_to_an_internal_constraint"); + auto ic2 = m_final_t_mesh->getVariable("internal_constraint"); + auto btic2 = m_final_t_mesh->getVariable("belong_to_an_internal_constraint"); + auto iann = m_final_t_mesh->getVariable("is_a_new_node"); + for (auto e_id:m_final_t_mesh->edges()) + { + Edge e1 = m_final_t_mesh->get(e_id); + Node n1 = m_t_mesh->get(e1.get()[0].id()); + Node n2 = m_t_mesh->get(e1.get()[1].id()); + try + { + Edge e2 = getEdge(n1,n2); + ic2->set(e1.id(),ic1->value(e2.id())); + } + catch (GMDSException& e){} + if (iann->value(e1.get()[0].id()) == 1 || iann->value(e1.get()[1].id()) == 1) + { + if (btic2->value(e1.get()[0].id()) == 1 && btic2->value(e1.get()[1].id()) == 1) + ic2->set(e1.id(),1); + } + + } +} +/*----------------------------------------------------------------------------*/ +} // end namespace gmds +/*----------------------------------------------------------------------------*/ \ No newline at end of file diff --git a/modules/medialaxis/src/main_block_decomp.cpp b/modules/medialaxis/src/main_block_decomp.cpp index f391c53e8..89c408db5 100644 --- a/modules/medialaxis/src/main_block_decomp.cpp +++ b/modules/medialaxis/src/main_block_decomp.cpp @@ -2,14 +2,23 @@ #include "gmds/io/IGMeshIOService.h" #include "gmds/io/VTKReader.h" #include "gmds/io/VTKWriter.h" +#include "gmds/io/MeshBWriter.h" #include "gmds/medialaxis/MedialAxis2D.h" #include "gmds/medialaxis/MedialAxis2DBuilder.h" #include "gmds/medialaxis/MedialAxis3D.h" #include "gmds/medialaxis/MedialAxis3DBuilder.h" #include "gmds/medialaxis/CrossField.h" #include "gmds/medialaxis/MedialAxisMath.h" +#include "gmds/medialaxis/MinDelaunayCleaner.h" +#include "gmds/medialaxis/MedaxBasedTMeshBuilder.h" +#include "gmds/medialaxis/QuantizationSolver.h" +#include "gmds/medialaxis/ConformalMeshBuilder.h" +#include "gmds/medialaxis/BlockStructureSimplifier.h" +#include "gmds/medialaxis/Conformalizer.h" +#include "gmds/medialaxis/TrianglesRemover.h" #include #include +#include #include using namespace gmds; using namespace math; @@ -24,13 +33,14 @@ int main(int argc, char* argv[]) //================================================================== std::string file_mesh, file_out, file_ma_out; int nb_iterations=0; - if (argc != 2) { + if (argc != 2) { // POULPE : 3 args std::cout << "Requires one parameters : \n"; std::cout << " - [IN ] minimal Delaunay mesh (.vtk) (to build the medial axis) \n"<> neighbours(m.getNbNodes()); + // for (auto e_id:me.edges()) + // { + // Edge e = me.get(e_id); + // int i1 = e.get()[0].id(); + // int i2 = e.get()[1].id(); + // if (i2 < i1) + // { + // int k = i1; + // i1 = i2; + // i2 = k; + // } + // std::vector vect = neighbours[i1]; + // bool already_here = false; + // for (auto i:vect) + // { + // if (i == i2) + // { + // already_here = true; + // break; + // } + // } + // if (!already_here) + // { + // vect.push_back(i2); + // neighbours[i1] = vect; + // m.newEdge(i1,i2); + // } + // } + + + + // Get the ids of the boundary edges + std::vector boundary_edges_ids; + for (auto e_id:m.edges()) + boundary_edges_ids.push_back(e_id); + MeshDoctor doc(&m); doc.buildEdgesAndX2E(); doc.updateUpwardConnectivity(); - std::cout << "NB nodes : " << m.getNbNodes() << std::endl; + + // MinDelaunayCleaner cleaner(m); + // cleaner.setFacesTypes(); + // cleaner.markSmallEdges(); + // cleaner.markFacesToDelete(); + // cleaner.buildCleanedMesh(); + // cleaner.setCleanedMeshConnectivity(); + + // Mesh cleaned_min_del = cleaner.getCleanedMesh(); + // m = cleaned_min_del; + + // Remove isolated points from m + for (auto n_id:m.nodes()) + { + Node n = m.get(n_id); + if (n.get().empty()) + m.deleteNode(n); + } + // Create a 2D medial axis - medialaxis::MedialAxis2DBuilder mb(m); + //medialaxis::MedialAxis2DBuilder mb(cleaned_min_del,boundary_edges_ids); + medialaxis::MedialAxis2DBuilder mb(m,boundary_edges_ids); auto st = mb.execute(); if (st == gmds::medialaxis::MedialAxis2DBuilder::SUCCESS) { @@ -72,54 +150,217 @@ int main(int argc, char* argv[]) medialaxis::MedialAxis2D* smoothed_ma = mb.getSmoothedMedialObject(); smoothed_ma->write("smoothed_medax.vtk"); - // Test topological representation - smoothed_ma->buildTopoRepNodes(); - smoothed_ma->setSectionID(); - smoothed_ma->buildTopoRepEdges(); - smoothed_ma->setTopoRepConnectivity(); - //smoothed_ma->browseTopoRep(); - //smoothed_ma->buildDofGraphNodes(); - //smoothed_ma->buildDofGraphEdges(); - //smoothed_ma->setDofGraphConnectivity(); - //smoothed_ma->buildQuantizationWithoutCycleSolution(); - smoothed_ma->buildBlockDecompMedialAndBoundaryNodes(); - smoothed_ma->buildSection2MedialAndBoundaryNodesAdjacency(); - smoothed_ma->buildMiddleNodes(); - smoothed_ma->buildBlocks(); - smoothed_ma->writeTopoRep("topo_rep.vtk"); - smoothed_ma->writeBlockDecomp("block_decomp.vtk"); - -// std::cout<<"Test Matrix : "<constraintMatrix(); -// int I = A.rows(); -// int J = A.cols(); -// for (int i = 0; i < I; i++) -// { -// for (int j = 0; j < J; j++) -// std::cout< fan0 = triRem.fan(4); + // for (auto f:fan0) + // std::cout<<"TEST "< outline = triRem.outline(fan0); + + triRem.buildTMesh(); + triRem.writeTMesh("post_process_t_mesh.vtk"); + triRem.setTMeshConnectivity(); + triRem.markInternalConstraintsOnEdges(); + triRem.transformUnwantedTrianglesIntoQuads(); + triRem.writeTMesh("post_process_t_mesh.vtk"); + triRem.buildFinalTMesh(); + triRem.setFinalTMeshConnectivity(); + triRem.markInternalConstraintsOnFinalEdges(); + triRem.writeFinalTMesh("post_process_final_t_mesh.vtk"); + Mesh t_mesh2 = triRem.getFinalTMesh(); + // Quantize the T-mesh + QuantizationSolver qs2(t_mesh2, mesh_size); + qs2.buildCompleteSolution(); + // We can build the quantized mesh + + qs2.setHalfEdgesLength(); + qs2.setEdgesLength(); + + triRem.writeTMesh("post_process_t_mesh.vtk"); + + // Build the conformal mesh + Conformalizer conf2(t_mesh2,qs2.halfEdges(),qs2.halfEdgesLengths()); + conf2.execute(); + Mesh conformal_mesh2 = conf2.getConformalMesh(); + + // Now we extract the block structure + BlockStructureSimplifier s2(conformal_mesh2); + s2.markSeparatrices(); + s2.setBlocksIDs(); + + conf2.projectOnBoundary(m); + conf2.writeConformalMesh("post_processed_quad_mesh_without_smoothing.vtk"); + for (int i = 0; i < 10; i++) + conf2.smooth(m); + + // Mesh quality + auto scaledJacobian_with_post_process = conformal_mesh2.newVariable("scaled_jacobian"); + for (auto f_id:conformal_mesh2.faces()) + { + Face f = conformal_mesh2.get(f_id); + double sj = f.computeScaledJacobian2D(); + scaledJacobian_with_post_process->set(f_id,sj); + } + + conf2.writeConformalMesh("post_processed_quad_mesh.vtk"); + } + + + + conf.writeConformalMesh("conformal_mesh_without_smoothing.vtk"); + conf.writeConformalMesh("contraintes_nous_not_smoothed.vtk"); + + clock_t t = clock(); + for (int i = 0; i < 3; i++) + conf.smooth(m); + t = clock()-t; + std::cout<<"Smoothing time (s) : "<("scaled_jacobian"); + for (auto f_id:conformal_mesh.faces()) + { + Face f = conformal_mesh.get(f_id); + double sj = f.computeScaledJacobian2D(); + scaledJacobian_without_post_process->set(f_id,sj); + } + + tmb.writeFinalTMesh("t_mesh.vtk"); + conf.writeConformalMesh("conformal_mesh.vtk"); + conf.writeConformalMesh("contraintes_nous_smoothed.vtk"); + + // //Test if it is 2-manifold + // std::cout<<"TEST "<(e_id); + // if (e.get().size() != 2 && e.get().size() != 1) + // std::cout<<"PROBLEM "< #include #include @@ -60,39 +63,65 @@ int main(int argc, char* argv[]) doc.buildEdgesAndX2E(); doc.updateUpwardConnectivity(); - // Test non conformal edges -// Face f = m.get(6); -// Edge e1 = m.get(9); -// Edge e2 = m.get(10); -// std::vector edges; -// edges.push_back(e1); -// edges.push_back(e2); -// NonConformalHalfEdge he(0,f,edges); -// std::vector half_edges; -// half_edges.push_back(he); -// std::vector> G = groupsOfAlignedEdges(f); -// for (auto g:G) -// { -// std::cout<<"Test "<updateConnectivity(); - g->buildQuantizationSolution(); - // g->display(); // Display the graph - // g->displaySolution(); // Display the quantization solution - // std::cout<<"Test "<> problematicCouples = qs.buildCompleteSolution(); + bool valid = true ;//problematicCouples.empty(); + if (valid) + { + // We can build the quantized mesh + + qs.setHalfEdgesLength(); + qs.setEdgesLength(); + + // Build the conformal mesh + Conformalizer conf(m,qs.halfEdges(),qs.halfEdgesLengths()); + conf.execute(); + Mesh conformal_mesh = conf.getConformalMesh(); + + // Now we simplify the quantized mesh, ie we try to minimize the number of blocks + BlockStructureSimplifier s(conformal_mesh); + // s.execute(); + // s.writeSimplifiedMesh("simplified_mesh.vtk"); + s.markSeparatrices(); + s.setBlocksIDs(); + + for (int i = 0; i < 100; i++) + conf.smooth(); + + conf.writeConformalMesh("conformal_mesh.vtk"); + } + else + { + //We need to add a singularity dipole + + qs.buildFixedMesh(problematicCouples[0][0].id(),problematicCouples[0][1].id()); + qs.writeFixedMesh("fixed_mesh.vtk"); + qs.setFixedMeshConnectivity(); + Mesh fixed_mesh = qs.getFixedMesh(); + + // Now we quantized the fixed mesh + + QuantizationSolver qs3(fixed_mesh); + qs3.buildCompleteSolution(); + + qs3.setHalfEdgesLength(); + + // Build the quantized mesh + ConformalMeshBuilder cmb(fixed_mesh,qs3.halfEdges(),qs3.halfEdgesLengths()); + cmb.execute(); + cmb.writeQuantizedMesh("quantized_mesh.vtk"); + + Mesh quantized_mesh = cmb.getQuantizedMesh(); + + + // Now we simplify the quantized mesh, ie we try to minimize the number of blocks + BlockStructureSimplifier s(quantized_mesh); + s.execute(); + s.writeSimplifiedMesh("simplified_mesh.vtk"); + } // Write the output file VTKWriter vtkWriter(&ioService); @@ -100,35 +129,43 @@ int main(int argc, char* argv[]) vtkWriter.setDataOptions(N| E| F); vtkWriter.write(file_out); -// // Create a test non-conformal mesh -// Mesh m2(MeshModel(DIM3 | F | E | N | -// F2N | F2E | -// E2F | E2N | N2E | N2F)); -// -// IGMeshIOService ioService2(&m2); -// m2.newNode(0.,0.); -// m2.newNode(2.,0.); -// m2.newNode(4.,0.); -// m2.newNode(0.,1.); -// m2.newNode(1.,1.); -// m2.newNode(2.,1.); -// m2.newNode(3.,1.); -// m2.newNode(4.,1.); -// m2.newNode(0.,2.); -// m2.newNode(1.,2.); -// m2.newNode(3.,2.); -// m2.newNode(4.,2.); -// m2.newNode(0.,3.); -// m2.newNode(4.,3.); -// m2.newFace({0,1,5,4,3}); -// m2.newFace({1,2,7,6,5}); -// m2.newFace({3,4,9,8}); -// m2.newFace({4,5,6,10,9}); -// m2.newFace({6,7,11,10}); -// m2.newFace({8,9,10,11,13,12}); -// VTKWriter vtkWriter2(&ioService2); -// vtkWriter2.setCellOptions(N| E| F); -// vtkWriter2.setDataOptions(N| E| F); -// vtkWriter2.write("blocks_decomp_test.vtk"); + + // // Create a test non-conformal mesh + // Mesh m2(MeshModel(DIM3 | F | E | N | + // F2N | F2E | + // E2F | E2N | N2E | N2F)); + + // IGMeshIOService ioService2(&m2); + + // m2.newNode(0.,0.); + // m2.newNode(12.,0.); + // m2.newNode(2.,1.); + // m2.newNode(4.,2.); + // m2.newNode(10.,2.); + // m2.newNode(0.,5.); + // m2.newNode(2.,5.); + // m2.newNode(4.,5.); + // m2.newNode(10.,7.); + // m2.newNode(12.,7.); + // m2.newNode(14.,7.); + // m2.newNode(4.,10.); + // m2.newNode(10.,10.); + // m2.newNode(12.,11.); + // m2.newNode(2.,12.); + // m2.newNode(14.,12.); + + // m2.newFace({0,1,4,3,2}); + // m2.newFace({0,2,6,5}); + // m2.newFace({2,3,7,6}); + // m2.newFace({4,1,9,8}); + // m2.newFace({6,7,11,14}); + // m2.newFace({8,9,13,12}); + // m2.newFace({9,10,15,13}); + // m2.newFace({11,12,13,15,14}); + + // VTKWriter vtkWriter2(&ioService2); + // vtkWriter2.setCellOptions(N| E| F); + // vtkWriter2.setDataOptions(N| E| F); + // vtkWriter2.write("blocks_decomp_test.vtk"); } \ No newline at end of file