Revision: 69035 http://sourceforge.net/p/brlcad/code/69035 Author: d_rossberg Date: 2016-10-12 14:32:21 +0000 (Wed, 12 Oct 2016) Log Message: ----------- patch https://sourceforge.net/p/brlcad/patches/448/ "Zippering gaps" from Rakshika Bagavathy implements the heal command for BoTs on mged: heal <bot primitive> <tolerance> the heal command has still issues and should be considered for testing purposes only, however, it should not interfere with the other BRL-CAD functionality, i.e. it should be safe to commit the patch
Modified Paths: -------------- brlcad/trunk/doc/html/manuals/mged/mged_cmd_index.html brlcad/trunk/include/analyze.h brlcad/trunk/include/ged/objects.h brlcad/trunk/src/libanalyze/CMakeLists.txt brlcad/trunk/src/libged/CMakeLists.txt brlcad/trunk/src/libtclcad/tclcad_obj.c brlcad/trunk/src/mged/setup.c Added Paths: ----------- brlcad/trunk/src/libanalyze/MeshHealing/ brlcad/trunk/src/libanalyze/MeshHealing/DCEL.h brlcad/trunk/src/libanalyze/MeshHealing/Geometry.cpp brlcad/trunk/src/libanalyze/MeshHealing/Geometry.h brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion.cpp brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion.h brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion_brlcad.cpp brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion_brlcad.h brlcad/trunk/src/libanalyze/MeshHealing/Stitch.cpp brlcad/trunk/src/libanalyze/MeshHealing/Stitch.h brlcad/trunk/src/libanalyze/MeshHealing/Zipper.cpp brlcad/trunk/src/libanalyze/MeshHealing/Zipper.h brlcad/trunk/src/libanalyze/heal_mesh.cpp brlcad/trunk/src/libged/heal.c Modified: brlcad/trunk/doc/html/manuals/mged/mged_cmd_index.html =================================================================== --- brlcad/trunk/doc/html/manuals/mged/mged_cmd_index.html 2016-10-12 11:46:13 UTC (rev 69034) +++ brlcad/trunk/doc/html/manuals/mged/mged_cmd_index.html 2016-10-12 14:32:21 UTC (rev 69035) @@ -194,6 +194,8 @@ <FONT SIZE=2><P></FONT><A HREF="#garbage_collect"><FONT SIZE=2>garbage_collect</FONT></A></TD> <TD WIDTH="20%" VALIGN="MIDDLE"> <FONT SIZE=2><P></FONT><A HREF="#gui"><FONT SIZE=2>gui</FONT></A></TD> +<TD WIDTH="20%" VALIGN="MIDDLE"> +<FONT SIZE=2><P></FONT><A HREF="#heal"><FONT SIZE=2>heal</FONT></A></TD> <TD WIDTH="22%" VALIGN="MIDDLE"> <FONT SIZE=2><P></FONT><A HREF="#help"><FONT SIZE=2>help</FONT></A></TD> <TD WIDTH="21%" VALIGN="MIDDLE"> @@ -1716,6 +1718,16 @@ <FONT SIZE=2><P><HR ALIGN="RIGHT"></P> </FONT><B><DL> +<DT><A NAME="heal"></A>heal</B> [<I>command</I>]</DT> +<DD><P>The "heal" command takes in the bot as input along with the tolerances. As of now, it takes the tolerance only for zippering. </P> + +<P>The healing essentially comprises only of zippering now, but more features will be added later on. The algorithm cannot support non-manifold meshes (those containing singular vertices or edges). The bots need not be oriented, the algorithm takes care of that. </P></DD> +<FONT SIZE=4><DT>Examples:</DT> +</FONT><TT><DD>mged></TT> <B>heal samplebot.s 47.3</DD> +</DL> + +<FONT SIZE=2><P><HR ALIGN="RIGHT"></P> +</FONT><B><DL> <DT><A NAME="help"></A>help</B> [<I>command</I>]</DT> <DD>The "help" command returns a list of available MGED commands along with a one-line usage message for each. If a command is supplied as an argument, the one-line usage message for that command is returned. The <A HREF="#helpdevel">helpdevel</A>, <A HREF="#helplib">helplib</A>, <A href="#questionmark">?</A>, <A HREF="#questionmarkdevel">?devel</A>, and <A HREF="#questionmarklib">?lib</A> commands provide additional information on available commands.</DD> <FONT SIZE=4><DT>Examples:</DT> Modified: brlcad/trunk/include/analyze.h =================================================================== --- brlcad/trunk/include/analyze.h 2016-10-12 11:46:13 UTC (rev 69034) +++ brlcad/trunk/include/analyze.h 2016-10-12 14:32:21 UTC (rev 69035) @@ -212,7 +212,10 @@ const char *pbrep, struct rt_gen_worker_vars *pbrep_rtvars, const char *curr_comb, struct bu_ptbl *candidates, void *curr_union_data, int ncpus); +ANALYZE_EXPORT void +analyze_heal_bot(struct rt_bot_internal *bot, double zipper_tol); + __END_DECLS #endif /* ANALYZE_H */ Modified: brlcad/trunk/include/ged/objects.h =================================================================== --- brlcad/trunk/include/ged/objects.h 2016-10-12 11:46:13 UTC (rev 69034) +++ brlcad/trunk/include/ged/objects.h 2016-10-12 14:32:21 UTC (rev 69035) @@ -215,6 +215,11 @@ GED_EXPORT extern int ged_group(struct ged *gedp, int argc, const char *argv[]); /** + * Heal command to heal the defects in bots + */ +GED_EXPORT extern int ged_heal(struct ged *gedp, int argc, const char *argv[]); + +/** * Set the "hidden" flag for the specified objects so they do not * appear in an "ls" command output */ Modified: brlcad/trunk/src/libanalyze/CMakeLists.txt =================================================================== --- brlcad/trunk/src/libanalyze/CMakeLists.txt 2016-10-12 11:46:13 UTC (rev 69034) +++ brlcad/trunk/src/libanalyze/CMakeLists.txt 2016-10-12 14:32:21 UTC (rev 69035) @@ -20,6 +20,12 @@ util.cpp volume.c voxels.c + MeshHealing/MeshConversion_brlcad.cpp + MeshHealing/MeshConversion.cpp + MeshHealing/Zipper.cpp + MeshHealing/Geometry.cpp + MeshHealing/Stitch.cpp + heal_mesh.cpp ) BRLCAD_ADDLIB(libanalyze "${LIBANALYZE_SOURCES}" "${TCL_LIBRARY};libbu;librt") Added: brlcad/trunk/src/libanalyze/MeshHealing/DCEL.h =================================================================== --- brlcad/trunk/src/libanalyze/MeshHealing/DCEL.h (rev 0) +++ brlcad/trunk/src/libanalyze/MeshHealing/DCEL.h 2016-10-12 14:32:21 UTC (rev 69035) @@ -0,0 +1,80 @@ +/* D C E L . H + * BRL-CAD + * + * Copyright (c) 2016 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + */ +/** @file DCEL.h + * + * Structure definitions of the vertex, edge, and face records for the DCEL + * + */ + +#ifndef SRC_LIBANALYZE_MESHHEALING_DCEL_H_ +#define SRC_LIBANALYZE_MESHHEALING_DCEL_H_ + +#include <utility> + +struct DCEL_Edge; + +/* DCEL_Vertex record of a doubly-connected DCEL_Edge list includes: + 1. DCEL_Vertex ID + 2. Coordinates + 3. An arbitrary incident DCEL_Edge - one with the particular DCEL_Vertex as its origin +*/ +struct DCEL_Vertex { + int vertex_id; + double coordinates[3]; + DCEL_Edge* incident_edge; +}; + +/* DCEL_Face record of a doubly-connected DCEL_Edge list includes: + 1. DCEL_Face ID, + 2. ID of any DCEL_Edge that can be used to traverse the DCEL_Face in counter-clockwise order + */ +struct DCEL_Face { + int face_id; + DCEL_Edge* start_edge; +}; + +/* The half-DCEL_Edge record of a doubly-connected DCEL_Edge list includes: + 1. DCEL_Edge ID + 2. ID of the origin DCEL_Vertex + 3. ID of the twin DCEL_Edge + 4. ID of the incident DCEL_Face - DCEL_Face to its left (since we orient the faces in counter-clockwise order) + 5. ID of the previous DCEL_Edge in the incident DCEL_Face + 6. ID of the next DCEL_Edge in the incident DCEL_Face + */ +struct DCEL_Edge { + std::pair<int, int> edge_id; + DCEL_Vertex* origin; + DCEL_Edge* twin; + DCEL_Face* incident_face; + DCEL_Edge* next; + DCEL_Edge* previous; +}; + +#endif /* SRC_LIBANALYZE_MESHHEALING_DCEL_H_ */ + +/* + * Local Variables: + * mode: C + * tab-width: 8 + * indent-tabs-mode: t + * c-file-style: "stroustrup" + * End: + * ex: shiftwidth=4 tabstop=8 + */ Property changes on: brlcad/trunk/src/libanalyze/MeshHealing/DCEL.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: brlcad/trunk/src/libanalyze/MeshHealing/Geometry.cpp =================================================================== --- brlcad/trunk/src/libanalyze/MeshHealing/Geometry.cpp (rev 0) +++ brlcad/trunk/src/libanalyze/MeshHealing/Geometry.cpp 2016-10-12 14:32:21 UTC (rev 69035) @@ -0,0 +1,267 @@ +/* G E O M E T R Y . C P P + * BRL-CAD + * + * Copyright (c) 2016 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + */ +/** @file Geometry.cpp + * + * Implementations of all the geometry functions required by the mesh healing module + * + */ + +#include "Geometry.h" + +#include <cmath> + +/* Calculates the determinant of a 3x3 matrix */ +double +calcDet(const double m[3][3]) +{ + double d; + + d = m[0][0] * (m[1][1]*m[2][2] - m[1][2] * m[2][1]); + d -= m[0][1] * (m[1][0]*m[2][2] - m[1][2] * m[2][0]); + d += m[0][2] * (m[1][0]*m[2][1] - m[1][1] * m[2][0]); + + return d; +} + +void +findOrthogonalProjection(const double line_point1[3], const double line_point2[3], const double point[3], double ortho[3]) +{ + /* Parameter in the line equation of AB */ + double t; + + double BA[3]; + double CA[3]; + + for (int i = 0; i < 3; ++i) { + BA[i] = line_point2[i] - line_point1[i]; + CA[i] = point[i] - line_point1[i]; + } + + /* Finding parameter t for a point D on line AB such that AB and DC are perpendicular to each other using the formula: + * t = dot(B-A, C-A)/dot(B-A, B-A) */ + double num = DOT_PROD(BA, CA); + double den = DOT_PROD(BA, BA); + + t = num / den; + + /* t=0 and t=1 correspond to the end points */ + if (t < 0) + t = 0; + else if (t > 1) + t = 1; + + for (int i = 0; i < 3; ++i) { + ortho[i] = line_point1[i] + t * BA[i]; + } +} + +bool +isOrthoProjPossible(const double line_point1[3], const double line_point2[3], const double point[3]) +{ + /* Parameter in the line equation of AB */ + double t; + + double BA[3]; + double CA[3]; + + for (int i = 0; i < 3; ++i) { + BA[i] = line_point2[i] - line_point1[i]; + CA[i] = point[i] - line_point1[i]; + } + + /* Finding parameter t for a point D on line AB such that AB and DC are perpendicular to each other using this formula: + * t = dot(B-A, C-A)/dot(B-A, B-A) + */ + double num = DOT_PROD(BA, CA); + double den = DOT_PROD(BA, BA); + + t = num / den; + + if (t > 0 && t < 1) + return true; + + return false; +} + +double +shortestDistBwPoints(const double A[3], const double B[3]) +{ + double dist = 0; + for (int i = 0; i < 3; i++) { + dist += (A[i] - B[i]) * (A[i] - B[i]); + } + dist = std::sqrt(dist); + + return dist; +} + +double +shortestDistToLine(const double line_point1[3], const double line_point2[3], const double point[3]) +{ + double dist; + double ortho[3]; + findOrthogonalProjection(line_point1, line_point2, point, ortho); + + dist = shortestDistBwPoints(point, ortho); + + return dist; +} + +bool +checkIfIntersectsInterior(const double line1_point1[3], const double line1_point2[3], const double line2_point1[3], const double line2_point2[3]) +{ + int o1, o2, o3, o4; + o1 = orientation(line1_point1, line1_point2, line2_point1); + o2 = orientation(line1_point1, line1_point2, line2_point2); + o3 = orientation(line2_point1, line2_point2, line1_point1); + o4 = orientation(line2_point1, line2_point2, line1_point2); + + if (o1 != o2 && o3 != o4 && o1 != COLL && o2 != COLL && o3 != COLL && o4 != COLL) + return true; + + return false; +} + +bool +checkIfCollinear(const double line1_point1[3], const double line1_point2[3], const double line2_point1[3], const double line2_point2[3]) +{ + int o1, o2, o3, o4; + o1 = orientation(line1_point1, line1_point2, line2_point1); + o2 = orientation(line1_point1, line1_point2, line2_point2); + o3 = orientation(line2_point1, line2_point2, line1_point1); + o4 = orientation(line2_point1, line2_point2, line1_point2); + + if(o1 == COLL && o2 == COLL && o3 == COLL && o4 == COLL) + return true; + return false; +} + +int +orientation(const double A[3], const double B[3], const double C[3]) +{ + double m[3][3]; + double det; + + for (int i = 0; i < 3; i++) { + m[0][i] = A[i]; + m[1][i] = B[i]; + m[2][i] = C[i]; + } + + if(NEAR_0(m[0][2]) && NEAR_0(m[1][2]) && NEAR_0(m[2][2])) { + m[0][2] = 1; + m[1][2] = 1; + m[2][2] = 1; + } + +/* + for (int i = 0; i < 3; i++) { + m[0][i] = 1; + m[1][i] = B[i] - A[i]; + m[2][i] = C[i] - A[i]; + } +*/ + det = calcDet(m); + + if(NEAR_0(det)) + return COLL; + else if (det > 0) + return CCW; + else + return CW; +} + +void +twoDimCoords(const double coordinates[3], double twod_coords[3]) +{ + twod_coords[0] = coordinates[0]; + twod_coords[1] = coordinates[1]; + twod_coords[2] = 0; +} + +bool +coplanar(const double A[3], const double B[3], const double C[3], const double D[3]) +{ + double m[4][4]; + + for(int i = 0; i < 3; i++) { + m[0][i] = A[i]; + m[1][i] = B[i]; + m[2][i] = C[i]; + m[3][i] = D[i]; + } + + for(int i = 0; i < 4; i++) { + m[i][3] = 1; + } + + return NEAR_0(calcDetFour(m)) ? true : false; +} + +double +perimeter(const double A[3], const double B[3], const double C[3]) +{ + double dist; + + dist = shortestDistBwPoints(A, B); + dist += shortestDistBwPoints(B, C); + dist += shortestDistBwPoints(C, A); + + return dist; +} + +double +calcDetFour(const double m[4][4]) +{ + double det = 0; + double temp[3][3]; + int col; + + for(int i = 0; i < 4; i++) { + for(int j = 1; j < 4; j++) { + col = 0; + + for(int k = 0; k < 4; k++) { + if(k == i) + continue; + + temp[j - 1][col] = m[j][k]; + col++; + } + } + + if(i % 2 == 0) + det += m[0][i]*calcDet(temp); + else + det -= m[0][i]*calcDet(temp); + } + + return det; +} + +/* + * Local Variables: + * mode: C + * tab-width: 8 + * indent-tabs-mode: t + * c-file-style: "stroustrup" + * End: + * ex: shiftwidth=4 tabstop=8 + */ Property changes on: brlcad/trunk/src/libanalyze/MeshHealing/Geometry.cpp ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: brlcad/trunk/src/libanalyze/MeshHealing/Geometry.h =================================================================== --- brlcad/trunk/src/libanalyze/MeshHealing/Geometry.h (rev 0) +++ brlcad/trunk/src/libanalyze/MeshHealing/Geometry.h 2016-10-12 14:32:21 UTC (rev 69035) @@ -0,0 +1,91 @@ +/* G E O M E T R Y . H + * BRL-CAD + * + * Copyright (c) 2016 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + */ +/** @file Geometry.h + * + * Definitions of the geometry functions required by the mesh healing module + * + */ + +#ifndef SRC_LIBANALYZE_MESHHEALING_GEOMETRY_H_ +#define SRC_LIBANALYZE_MESHHEALING_GEOMETRY_H_ + +#include <cstdlib> + +#define COLL 0 +#define CW 1 +#define CCW 2 +#define DOT_PROD(A, B) \ + A[0] * B[0] \ + + A[1] * B[1] \ + + A[2] * B[2] + +#define TOLERANCE (5.3e-11) +#define NEAR_0(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) + +/* Finds the orthogonal projection of a point C on a line segment formed by points A and B */ +void findOrthogonalProjection(const double line_point1[3], const double line_point2[3], const double point[3], double ortho[3]); + +/* Returns true if orthogonal projection of C on the line segment AB is possible, false otherwise. */ +bool isOrthoProjPossible(const double line_point1[3], const double line_point2[3], const double point[3]); + +/* Returns the shortest distance between the two points */ +double shortestDistBwPoints(const double A[3], const double B[3]); + +/* Returns the shortest distance between a point and a line segment */ +double shortestDistToLine(const double line_point1[3], const double line_point2[3], const double point[3]); + +/* Checks if line segment AB and CD intersect */ +bool checkIfIntersectsInterior(const double line1_point1[3], const double line1_point2[3], const double line2_point1[3], const double line2_point2[3]); + +/* Checks if the two lines are made up of collinear points */ +bool checkIfCollinear(const double line1_point1[3], const double line1_point2[3], const double line2_point1[3], const double line2_point2[3]); + +/* To check the orientation of the triangle formed by the three points P, Q, and R + * 0 - Collinear + * 1 - Clockwise + * 2 - Counter-clockwise*/ +int orientation(const double P[3], const double Q[3], const double R[3]); + +/* Calculates the determinant of the 3x3 matrix m */ +double calcDet(const double m[3][3]); + +/* Calculates the determinant of the 4x4 matrix m */ +double calcDetFour(const double m[4][4]); + +/* Checks if the four points are co-planar */ +bool coplanar(const double[3], const double[3], const double[3], const double[3]); + +/* Returns the 2D coordinates of the 3D point */ +void twoDimCoords(const double coordinates[3], double twod_coords[3]); + +/* Returns the perimeter of the triangle formed by the vertices A, B, and C */ +double perimeter(const double A[3], const double B[3], const double C[3]); + +#endif /* SRC_LIBANALYZE_MESHHEALING_GEOMETRY_H_ */ + +/* + * Local Variables: + * mode: C + * tab-width: 8 + * indent-tabs-mode: t + * c-file-style: "stroustrup" + * End: + * ex: shiftwidth=4 tabstop=8 + */ Property changes on: brlcad/trunk/src/libanalyze/MeshHealing/Geometry.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion.cpp =================================================================== --- brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion.cpp (rev 0) +++ brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion.cpp 2016-10-12 14:32:21 UTC (rev 69035) @@ -0,0 +1,879 @@ +/* M E S H C O N V E R S I O N . C P P + * BRL-CAD + * + * Copyright (c) 2016 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + */ +/** @file MeshConversion.cpp + * + * Main logic for the mesh healing portable module conversion + * + */ + +#include "MeshConversion.h" + +#include <cstddef> +#include <iterator> +#include <limits> + +#include "Geometry.h" + +/* Vertex record function */ + +void +PolygonalMesh::initIncidentEdge() +{ + unsigned int num_vertices = this->getNumVertices(); + + for (unsigned int i = 0; i < num_vertices; ++i) { + for (unsigned int j = 0; j < edgelist.size(); ++j) { + + if (vertexlist[i].vertex_id == edgelist[j].origin->vertex_id) { + vertexlist[i].incident_edge = &(edgelist[j]); + break; + } + } + } +} + +/* Face record function */ + +void +PolygonalMesh::initFaces() +{ + unsigned int num_faces = this->getNumFaces(); + DCEL_Face face; + for (unsigned int i = 0; i <= num_faces; i++) { + face.face_id = i; + face.start_edge = NULL; + facelist.push_back(face); + } +} + +/* Edge record functions */ + +void +PolygonalMesh::initTwinEdge() +{ + for (unsigned int i = 0; i < edgelist.size(); ++i) { + for (unsigned int j = 0; j < edgelist.size(); j++) { + if (edgelist[i].edge_id.first == edgelist[j].edge_id.second && \ + edgelist[j].edge_id.first == edgelist[i].edge_id.second) { + + edgelist[i].twin = &(edgelist[j]); + edgelist[j].twin = &(edgelist[i]); + break; + } + } + } +} + +/* Getters and setters */ + +DCEL_Vertex* +PolygonalMesh::getVertex(int ID) +{ + if (ID < (signed int)vertexlist.size()) + return &vertexlist[ID]; + return NULL; +} + +void +PolygonalMesh::setVertexCoordsInRecord(int ID, double coordinates[3]) +{ + if (ID < (signed int)vertexlist.size()) { + vertexlist[ID].coordinates[0] = coordinates[0]; + vertexlist[ID].coordinates[1] = coordinates[1]; + vertexlist[ID].coordinates[2] = coordinates[2]; + + setVertexCoords(ID); + } +} + +void +PolygonalMesh::setVertexRecord(DCEL_Vertex *vertex, DCEL_Vertex *vertex_to_be_replaced) +{ + for (unsigned int i = 0; i < edgelist.size(); ++i) { + /* Edges whose origin is the vertex to be replaced */ + if (edgelist[i].origin->vertex_id == vertex_to_be_replaced->vertex_id) { + edgelist[i].origin = getVertex(vertex->vertex_id); + edgelist[i].edge_id.first = vertex->vertex_id; + } + + /* Edges that end at the vertex to be replaced */ + if (edgelist[i].edge_id.second == vertex_to_be_replaced->vertex_id) { + edgelist[i].edge_id.second = vertex->vertex_id; + } + } + setVertex(vertex->vertex_id, vertex_to_be_replaced->vertex_id); +} + +void +PolygonalMesh::deleteVertexRecord(int ID) +{ + if ((unsigned)ID >= vertexlist.size()) + return; + + /* Setting the references for the origin of edges whose origin comes after the vertex being deleted */ + for (unsigned int i = 0; i < edgelist.size(); i++) { + if (edgelist[i].origin->vertex_id > ID) { + edgelist[i].origin = &vertexlist[edgelist[i].origin->vertex_id - 1]; + } + } + + /* Delete the vertex record with vertex id=ID. */ + + vertexlist.erase(vertexlist.begin() + ID); + setNumVertices(); + + for (unsigned int i = 0; i < edgelist.size(); i++) { + if (edgelist[i].edge_id.first > ID) + edgelist[i].edge_id.first--; + + if (edgelist[i].edge_id.second > ID) + edgelist[i].edge_id.second--; + } + + /* Reassigning vertex IDs to the vertices */ + for (unsigned int i = ID; i < vertexlist.size(); i++) { + vertexlist[i].vertex_id = i; + } +} + +void +PolygonalMesh::insertVertexOnEdge(DCEL_Vertex *vertex, DCEL_Edge *edge) +{ + /* edge11 and edge12 make up the edge passed as argument. edge21 and edge 22 make up the twin_edge */ + DCEL_Edge *edge11, *edge12, *edge21, *edge22; + + DCEL_Edge *twin_edge; + + edge11 = addEdge(std::make_pair(edge->edge_id.first, vertex->vertex_id), edge->origin, NULL, edge->incident_face, NULL, edge->previous); + + edge12 = addEdge(std::make_pair(vertex->vertex_id, edge->edge_id.second), vertex, NULL, edge->incident_face, edge->next, edge11); + + edge11->next = edge12; + + vertex->incident_edge = edge12; + + /* If the edge was the starting edge of its incident face, change it to either one of the two new edges */ + if (edge->incident_face->face_id != UNBOUNDED_FACE) { + if (edge->incident_face->start_edge->edge_id.first == edge->edge_id.first) + edge->incident_face->start_edge = edge11; + } + + twin_edge = edge->twin; + + edge21 = addEdge(std::make_pair(twin_edge->edge_id.first, vertex->vertex_id), twin_edge->origin, NULL, twin_edge->incident_face, NULL, twin_edge->previous); + + edge22 = addEdge(std::make_pair(vertex->vertex_id, twin_edge->edge_id.second), vertex, NULL, twin_edge->incident_face, twin_edge->next, edge21); + + edge21->next = edge22; + + + /* If the edge was the starting edge of its incident face, change it to either one of the two new edges */ + if (twin_edge->incident_face->face_id != UNBOUNDED_FACE) { + if (twin_edge->incident_face->start_edge->edge_id.first == twin_edge->edge_id.first) + twin_edge->incident_face->start_edge = edge21; + } + + edge11->twin = edge22; + edge12->twin = edge21; + edge21->twin = edge12; + edge22->twin = edge11; + + /* Delete the argument edge and its twin */ + int v1 = edge->edge_id.first; + int v2 = edge->edge_id.second; + int face = edge->incident_face->face_id; + int twin_face = edge->twin->incident_face->face_id; + deleteEdge(std::make_pair(v1, v2), face); + deleteEdge(std::make_pair(v2, v1), twin_face); + + /*checkAndSetTwins(vertex);*/ +} + +void +PolygonalMesh::deleteEdge(std::pair<int, int> ID, int face_id) +{ + int index = getEdgeIndex(ID, face_id); + + int e_id; + + for (unsigned int i = 0; i < vertexlist.size(); i++) { + e_id = getEdgeIndex(vertexlist[i].incident_edge->edge_id, vertexlist[i].incident_edge->incident_face->face_id); + if (e_id > index) { + vertexlist[i].incident_edge = &edgelist[e_id - 1]; + } + + else if (e_id == index) { + for (unsigned int j = 0; j < edgelist.size(); j++) { + if (vertexlist[i].vertex_id == edgelist[j].edge_id.first && j != (unsigned)index) { + vertexlist[i]. incident_edge = &edgelist[j]; + break; + } + } + } + } + + for (unsigned int i = 1; i < facelist.size(); i++) { + e_id = getEdgeIndex(facelist[i].start_edge->edge_id, i); + if (e_id > index) { + facelist[i].start_edge = &edgelist[e_id - 1]; + } + } + + for (unsigned int i =0; i < edgelist.size(); i++) { + e_id = getEdgeIndex(edgelist[i].twin->edge_id, edgelist[i].twin->incident_face->face_id); + if (e_id > index) { + edgelist[i].twin = &edgelist[e_id - 1]; + } + + e_id = getEdgeIndex(edgelist[i].next->edge_id, edgelist[i].next->incident_face->face_id); + if(e_id > index) { + edgelist[i].next = &edgelist[e_id - 1]; + } + + e_id = getEdgeIndex(edgelist[i].previous->edge_id, edgelist[i].previous->incident_face->face_id); + if ( e_id > index) { + edgelist[i].previous = &edgelist[e_id - 1]; + } + } + + if(index != -1) { + edgelist.erase(edgelist.begin() + index); + is_edge_checked.erase(is_edge_checked.begin() + index); + } +} + +void +PolygonalMesh::splitFace(int face_id, DCEL_Vertex *vertex_on_edge, DCEL_Vertex *opp_vertex) +{ + DCEL_Edge *start_edge1 = NULL, *start_edge2 = NULL; + DCEL_Edge *new_edge1, *new_edge2; + /* Indices of the newly added faces */ + int face1, face2; + + /* Find the two starting edges of the resultant faces */ + for (unsigned int i = 0; i < edgelist.size(); ++i) { + + if (edgelist[i].incident_face->face_id == face_id) { + + if (edgelist[i].edge_id.first == vertex_on_edge->vertex_id) + start_edge1 = &edgelist[i]; + else if (edgelist[i].edge_id.second== vertex_on_edge->vertex_id) + start_edge2 = &edgelist[i]; + } + + if (start_edge1 != NULL && start_edge2 != NULL) + break; + } + + start_edge1->next->previous = start_edge1; + start_edge2->previous->next = start_edge2; + + /* Create two half edges from the vertex passed as argument and the vertex opposite to the edge it is present on */ + new_edge1 = addEdge(std::make_pair(opp_vertex->vertex_id, vertex_on_edge->vertex_id), opp_vertex, NULL, NULL, start_edge1, start_edge1->next); + new_edge2 = addEdge(std::make_pair(vertex_on_edge->vertex_id, opp_vertex->vertex_id), vertex_on_edge, NULL, NULL, start_edge2->previous, start_edge2); + + new_edge1->twin = new_edge2; + new_edge2->twin = new_edge1; + + face1 = addFaceRecord(start_edge1); + face2 = addFaceRecord(start_edge2); + + start_edge1->incident_face = &facelist[face1]; + new_edge1->incident_face = &facelist[face1]; + start_edge1->next->incident_face = &facelist[face1]; + + start_edge2->incident_face = &facelist[face2]; + new_edge2->incident_face = &facelist[face2]; + start_edge2->previous->incident_face = &facelist[face2]; + + deleteFaceRecord(face_id); +} + +int +PolygonalMesh::addFaceRecord(DCEL_Edge *start_edge) +{ + int last_face_id = facelist.back().face_id; + DCEL_Face new_face; + new_face.face_id = last_face_id + 1; + new_face.start_edge = start_edge; + + facelist.push_back(new_face); + + /*addFace();*/ + + return last_face_id + 1; +} + +DCEL_Edge* +PolygonalMesh::addEdge(std::pair<int, int> edge_id, DCEL_Vertex *origin, DCEL_Edge *twin, DCEL_Face *inc_face, DCEL_Edge *next, DCEL_Edge *prev) +{ + DCEL_Edge *new_edge = new DCEL_Edge; + + new_edge->edge_id.first = edge_id.first; + new_edge->edge_id.second = edge_id.second; + + new_edge->origin = origin; + + new_edge->twin = twin; + if (twin != NULL) + twin->twin = new_edge; + + new_edge->incident_face = inc_face; + + new_edge->next = next; + if (next != NULL) + new_edge->next->previous = new_edge; + + new_edge->previous = prev; + if (prev != NULL) + new_edge->previous->next = new_edge; + + edgelist.push_back(*new_edge); + is_edge_checked.push_back(false); + + delete new_edge; + + if (twin != NULL) + twin->twin = &edgelist[edgelist.size() - 1]; + if (next != NULL) + next->previous = &edgelist[edgelist.size() - 1]; + if (prev != NULL) + prev->next = &edgelist[edgelist.size() - 1]; + + return &edgelist[edgelist.size() - 1]; +} + +void +PolygonalMesh::deleteFaceRecord(int face_id) +{ + /*deleteFace(face_id - 1);*/ + + /* Setting incident face references */ + for (unsigned int i = 0; i < edgelist.size(); i++) { + if (edgelist[i].incident_face->face_id > face_id) { + edgelist[i].incident_face = &facelist[edgelist[i].incident_face->face_id - 1]; + } + } + facelist.erase(facelist.begin() + face_id); + + /* Reassigning face IDs */ + for (unsigned int i = face_id; i < facelist.size(); i++) { + facelist[i].face_id = i; + } +} + +DCEL_Edge* +PolygonalMesh::getEdge(std::pair<int, int> ID) +{ + for (unsigned int i = 0; i < edgelist.size(); i++) { + if (edgelist[i].edge_id.first == ID.first && edgelist[i].edge_id.second == ID.second) + return &edgelist[i]; + } + return NULL; +} + +int +PolygonalMesh::addVertexRecord(double coordinates[3]) +{ + int last_vertex_id = vertexlist[vertexlist.size() - 1].vertex_id; + DCEL_Vertex new_vertex; + new_vertex.vertex_id = last_vertex_id + 1; + + new_vertex.coordinates[0] = coordinates[0]; + new_vertex.coordinates[1] = coordinates[1]; + new_vertex.coordinates[2] = coordinates[2]; + + vertexlist.push_back(new_vertex); + + addVertex(new_vertex.vertex_id); + + return last_vertex_id + 1; +} + +DCEL_Edge* +PolygonalMesh::findClosestEdge(DCEL_Vertex *vertex) +{ + double min_dist = std::numeric_limits<int>::max(); + double dist; + double ortho_proj[3]; + DCEL_Edge *edge = NULL; + DCEL_Vertex *closer_vertex; + bool is_eligible; + DCEL_Face *face; + + for (unsigned int i = 0; i < edgelist.size(); ++i) { + is_eligible = true; + /* The edge should have the unbounded face incident on it */ + if (edgelist[i].incident_face->face_id != UNBOUNDED_FACE) + continue; + + /* The edge should not be incident on the vertex itself */ + if (edgelist[i].edge_id.first == vertex->vertex_id || edgelist[i].edge_id.second == vertex->vertex_id) + continue; + + /* Line joining the vertex to the edge should not cross any bounded face */ + + /* The feature edge is an edge if an orthogonal projection is possible */ + if(isOrthoProjPossible(getVertex(edgelist[i].edge_id.first)->coordinates, \ + getVertex(edgelist[i].edge_id.second)->coordinates, vertex->coordinates)){ + /* If the face incident on this edge's twin contains the vertex, it will cross a bounded face */ + face = edgelist[i].twin->incident_face; + if (isVertexInFace(vertex->vertex_id, face->face_id)) + continue; + + findOrthogonalProjection(getVertex(edgelist[i].edge_id.first)->coordinates, \ + getVertex(edgelist[i].edge_id.second)->coordinates, vertex->coordinates, ortho_proj); + + /* Check if the line from the vertex to the edge intersects the face incident on the edge's twin, + * or any of the faces around the vertex + */ + if(!checkEligibleEdge(vertex, ortho_proj)) + continue; + if (doesLineIntersectFace(edgelist[i].twin->incident_face, vertex, ortho_proj)) + continue; + } + + /* The feature is a vertex */ + else { + + closer_vertex = findCloserVertex(&edgelist[i], vertex); + + /* If there exists an edge between the closer_vertex and vertex, make the current edge in-eligible */ + for (unsigned int j = 0; j < edgelist.size(); j++) { + if (edgelist[j].edge_id.first == vertex->vertex_id && edgelist[j].edge_id.second == closer_vertex->vertex_id) { + is_eligible = false; + break; + } + } + if(!is_eligible) + continue; + + /* Check if the line joining vertex to closer_vertex crosses any other bounded face. Precisely: + * 1. Any face around the vertex + * 2. Any face around the closer vertex + */ + if (!checkEligibleEdge(vertex, closer_vertex->coordinates)) + continue; + if(!checkEligibleEdge(closer_vertex, vertex->coordinates)) + continue; + + for (int j = 0; j < 3; j++) { + ortho_proj[j] = closer_vertex->coordinates[j]; + } + } + + /* The edge is now eligible to be the closest edge */ + dist = shortestDistBwPoints(vertex->coordinates, ortho_proj); + + if (dist < min_dist) { + min_dist = dist; + edge = &edgelist[i]; + } + } + + return edge; +} + +DCEL_Vertex* +PolygonalMesh::findCloserVertex(DCEL_Edge *edge, DCEL_Vertex *vertex) +{ + double dist1, dist2; + DCEL_Vertex *vertex1 = getVertex(edge->edge_id.first); + DCEL_Vertex *vertex2 = getVertex(edge->edge_id.second); + + dist1 = shortestDistBwPoints(vertex->coordinates, vertex1->coordinates); + dist2 = shortestDistBwPoints(vertex->coordinates, vertex2->coordinates); + + if(dist1 < dist2) + return vertex1; + return vertex2; +} + +DCEL_Face* +PolygonalMesh::getFace(int ID) +{ + return &facelist[ID]; +} + +bool +PolygonalMesh::isVertexInFace(int vertex_id, int face_id) +{ + DCEL_Edge *edge = (getFace(face_id))->start_edge; + DCEL_Edge *trav_edge = edge; + + if (face_id == UNBOUNDED_FACE) + return false; + + while (true) { + if (trav_edge->edge_id.first == vertex_id) + return true; + trav_edge = trav_edge->next; + if (trav_edge == edge) + break; + } + return false; +} + +bool +PolygonalMesh::isFreeEdge(DCEL_Edge* edge) +{ + if (edge->incident_face->face_id == UNBOUNDED_FACE) + return true; + + return false; +} + +int +PolygonalMesh::getEdgeIndex(std::pair<int, int> ID, int face_id) +{ + for (unsigned int i = 0; i < edgelist.size(); ++i) { + if (edgelist[i].edge_id.first == ID.first && edgelist[i].edge_id.second == ID.second && \ + edgelist[i].incident_face->face_id == face_id) { + return i; + } + } + return -1; +} + +int +PolygonalMesh::getNumEdges() +{ + return edgelist.size(); +} + +DCEL_Edge* +PolygonalMesh::findFreeEdgeChain() +{ + DCEL_Edge *edge, *trav_edge; + + for (unsigned int i = 0; i < edgelist.size(); ++i) { + if (!isFreeEdge(&edgelist[i]) || is_edge_checked[i]) + continue; + + edge = &edgelist[i]; + trav_edge = edge; + + while (isFreeEdge(trav_edge)) { + is_edge_checked[getEdgeIndex(trav_edge->edge_id, trav_edge->incident_face->face_id)] = true; + trav_edge = trav_edge->next; + if (trav_edge == edge) { + return trav_edge; + } + } + } + return NULL; +} + +bool +PolygonalMesh::doesLineIntersectFaceWithVertex(DCEL_Face *face, DCEL_Vertex *vertex, double other_vertex[3]) +{ + if(face->face_id == UNBOUNDED_FACE) + return false; + + DCEL_Edge *edge_to_be_checked = face->start_edge; + + /* Vertices of the end points of the opposite edge */ + DCEL_Vertex *v1, *v2; + + bool flag = false; + + for (int i = 0; i < VERTICES_PER_FACE; i++) { + if (vertex->vertex_id != edge_to_be_checked->edge_id.first && vertex->vertex_id != edge_to_be_checked->edge_id.second) { + flag = true; + break; + } + edge_to_be_checked = edge_to_be_checked->next; + } + + if(!flag) { + + return false; + } + + v1 = getVertex(edge_to_be_checked->edge_id.first); + v2 = getVertex(edge_to_be_checked->edge_id.second); + + if (checkIfIntersectsInterior(vertex->coordinates, other_vertex, v1->coordinates, v2->coordinates) && \ + coplanar(vertex->coordinates, other_vertex, v1->coordinates, v2->coordinates)) + { + return true; + } + + /* Check if it is collinear with the other two edges */ + edge_to_be_checked = edge_to_be_checked->next; + v1 = getVertex(edge_to_be_checked->edge_id.first); + v2 = getVertex(edge_to_be_checked->edge_id.second); + + if (checkIfCollinear(vertex->coordinates, other_vertex, v1->coordinates, v2->coordinates) && \ + coplanar(vertex->coordinates, other_vertex, v1->coordinates, v2->coordinates)) + { + return true; + } + + edge_to_be_checked = edge_to_be_checked->next; + + v1 = getVertex(edge_to_be_checked->edge_id.first); + v2 = getVertex(edge_to_be_checked->edge_id.second); + + bool result = checkIfCollinear(vertex->coordinates, other_vertex, v1->coordinates, v2->coordinates); + + result = result && coplanar(vertex->coordinates, other_vertex, v1->coordinates, v2->coordinates); + + return result; +} + +DCEL_Face* +PolygonalMesh::getNextFace(DCEL_Face *face, DCEL_Vertex *vertex) +{ + if(face->face_id == UNBOUNDED_FACE) { + for (unsigned int i = 0; i < edgelist.size(); i++) { + if(edgelist[i].edge_id.first == vertex->vertex_id && edgelist[i].incident_face->face_id == UNBOUNDED_FACE) + return edgelist[i].twin->incident_face; + } + } + DCEL_Edge *edge = face->start_edge; + DCEL_Edge *trav_edge = edge; + do { + if (trav_edge->edge_id.first == vertex->vertex_id) { + return trav_edge->twin->incident_face; + } + trav_edge = trav_edge->next; + } + while(trav_edge != edge); + return NULL; +} + +bool +PolygonalMesh::checkEligibleEdge(DCEL_Vertex *vertex, double closer_vertex[3]) +{ + DCEL_Face *face, *trav_face; + bool is_elgible = true; + face = vertex->incident_edge->incident_face; + trav_face = face; + + DCEL_Edge *edge; + do { + if (doesLineIntersectFaceWithVertex(trav_face, vertex, closer_vertex)) { + is_elgible = false; + break; + } + + /* Check if orientation of the face is changing - i.e is the face getting turned over */ + if (trav_face->face_id != UNBOUNDED_FACE) { + + edge = trav_face->start_edge; + while(edge->edge_id.first != vertex->vertex_id) { + edge = edge->next; + } + + if (orientation(closer_vertex, edge->next->origin->coordinates, edge->next->next->origin->coordinates) == CW) { + is_elgible = false; + break; + } + } + + trav_face = getNextFace(trav_face, vertex); + } + while(trav_face != face); + + return is_elgible; +} + +bool +PolygonalMesh::isEdgeOnChain(DCEL_Edge *edge_to_be_checked, DCEL_Edge *chain_edge) +{ + if(edge_to_be_checked == NULL) + return false; + DCEL_Edge *trav_edge = chain_edge; + do { + if(edge_to_be_checked == trav_edge) + return true; + trav_edge = trav_edge->next; + } + while(trav_edge != chain_edge); + return false; +} + +void +PolygonalMesh::squeezeOutEdges(DCEL_Edge* edge, DCEL_Edge *next_edge) +{ + if (edge->incident_face->face_id != UNBOUNDED_FACE) + return; + + edge->previous->next = next_edge->next; + next_edge->next->previous = edge->previous; + + /* Delete the edge records */ + int v1 = next_edge->edge_id.first, v2 = next_edge->edge_id.second, face = next_edge->incident_face->face_id; + + deleteEdge(std::make_pair(edge->edge_id.first, edge->edge_id.second), edge->incident_face->face_id); + deleteEdge(std::make_pair(v1, v2), face); + + /* If this edge is the incident edge of any vertex, change the reference to some other edge whose origin is this edge's origin */ + for (unsigned int i = 0; i < vertexlist.size(); i++) { + if (vertexlist[i].vertex_id != vertexlist[i].incident_edge->edge_id.first) { + for (unsigned int j = 0; j < edgelist.size(); j++) { + if (edgelist[j].edge_id.first == vertexlist[i].vertex_id && edgelist[j].incident_face->face_id != UNBOUNDED_FACE) { + vertexlist[i].incident_edge = &edgelist[j]; + break; + } + } + } + } +} + +bool +PolygonalMesh::doesLineIntersectFace(DCEL_Face* face, DCEL_Vertex* vertex, double ortho_proj[3]) +{ + if(face->face_id == UNBOUNDED_FACE) + return false; + + DCEL_Edge *edge_to_be_checked = face->start_edge; + DCEL_Edge *trav_edge = edge_to_be_checked; + + /* Vertices of the end points of the opposite edge */ + DCEL_Vertex *v1, *v2; + + do { + v1 = getVertex(trav_edge->edge_id.first); + v2 = getVertex(trav_edge->edge_id.second); + + if (checkIfIntersectsInterior(vertex->coordinates, ortho_proj, v1->coordinates, v2->coordinates) \ + && coplanar(vertex->coordinates, ortho_proj, v1->coordinates, v2->coordinates)) { + return true; + } + trav_edge = trav_edge->next; + } + while(trav_edge != edge_to_be_checked); + + return false; +} + +void +PolygonalMesh::checkAndSetTwins(DCEL_Vertex* vertex) +{ + DCEL_Edge *edge = vertex->incident_edge; + DCEL_Edge *trav_edge = edge; + DCEL_Edge *other_edge; + bool flag1 = false, flag2 = false; + std::pair<int, int> edge1_id, edge2_id; + + DCEL_Edge *edge1, *edge2; + DCEL_Edge *fedge1 = NULL, *fedge2 = NULL; + + do { + trav_edge = trav_edge->twin->next; + } + while (trav_edge->incident_face->face_id != UNBOUNDED_FACE && trav_edge != edge); + + if(trav_edge == edge && trav_edge->incident_face->face_id != UNBOUNDED_FACE) + return; + + edge1 = trav_edge; + edge1_id.first = edge1->edge_id.first; + edge1_id.second = edge1->edge_id.second; + + edge2 = edge1->previous; + edge2_id.first = edge2->edge_id.first; + edge2_id.second = edge2->edge_id.second; + + if (edge1->next->edge_id.second == edge1->edge_id.first) + flag1 = true; + + if (edge2->previous->edge_id.first == edge2->edge_id.second) + flag2 = true; + + /* Look for twins, for the twins of these edges */ + for (unsigned int i = 0; i < edgelist.size(); i++) { + + if (!flag1 && !flag2) { + + if (edgelist[i].incident_face->face_id == UNBOUNDED_FACE && edgelist[i].edge_id.first == vertex->vertex_id) { + if (fedge1 != NULL) { + fedge2 = &edgelist[i]; + + DCEL_Edge *temp = fedge1->previous; + + fedge1->previous = fedge2->previous; + fedge2->previous->next= fedge1; + + fedge2->previous = temp; + temp->next = fedge2; + + break; + } + + else { + fedge1 = &edgelist[i]; + } + } + } + + if (flag1) { + if (edgelist[i].edge_id.first == edge1->edge_id.first && edgelist[i].edge_id.second == edge1->edge_id.second &&\ + edgelist[i].incident_face->face_id != UNBOUNDED_FACE) { + + edge1->twin->twin = &edgelist[i]; + edgelist[i].twin = edge1->twin; + other_edge = edge1->next; + squeezeOutEdges(edge1, other_edge); + edge2 = &edgelist[getEdgeIndex(edge2_id, UNBOUNDED_FACE)]; + flag1 = false; + + if (!flag1 && !flag2) + break; + } + } + + if (flag2) { + if (edgelist[i].edge_id.first == edge2->edge_id.first && edgelist[i].edge_id.second == edge2->edge_id.second &&\ + edgelist[i].incident_face->face_id != UNBOUNDED_FACE) { + + edge2->twin->twin = &edgelist[i]; + edgelist[i].twin = edge2->twin; + other_edge = edge2->previous; + squeezeOutEdges(other_edge, edge2); + edge1 = &edgelist[getEdgeIndex(edge1_id, UNBOUNDED_FACE)]; + flag2 = false; + + if (!flag1 && !flag2) + break; + } + } + } +} + +DCEL_Edge* +PolygonalMesh::getEdgeWithIndex(int index) +{ + if ((unsigned)index < edgelist.size()) + return &edgelist[index]; + return NULL; +} + +/* + * Local Variables: + * mode: C + * tab-width: 8 + * indent-tabs-mode: t + * c-file-style: "stroustrup" + * End: + * ex: shiftwidth=4 tabstop=8 + */ Property changes on: brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion.cpp ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion.h =================================================================== --- brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion.h (rev 0) +++ brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion.h 2016-10-12 14:32:21 UTC (rev 69035) @@ -0,0 +1,180 @@ +/* M E S H C O N V E R S I O N . H + * BRL-CAD + * + * Copyright (c) 2016 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + */ +/** @file MeshConversion.h + * + * Definition of the PolygonalMesh class, and the utility methods for the mesh healing module + * + */ + +#ifndef SRC_LIBANALYZE_MESHHEALING_MESHCONVERSION_H_ +#define SRC_LIBANALYZE_MESHHEALING_MESHCONVERSION_H_ + +#include <utility> +#include <vector> + +#include "DCEL.h" + +#define VERTICES_PER_FACE 3 +#define UNBOUNDED_FACE 0 + + +class PolygonalMesh { + +protected: + + /* DCEL records */ + std::vector<DCEL_Vertex> vertexlist; + std::vector<DCEL_Face> facelist; + std::vector<DCEL_Edge> edgelist; + + /* Virtual functions to update the native structures */ + + /* DCEL_Vertex record functions */ + + /* Returns number of vertices */ + virtual int getNumVertices() = 0; + /* Sets the DCEL_Vertex IDs and coordinates for all the vertices */ + virtual void initVertices() = 0; + /* For every DCEL_Vertex, sets an arbitrary DCEL_Vertex incident on it */ + void initIncidentEdge(); + + /* DCEL_Face record functions + */ + + /* Returns number of faces */ + virtual int getNumFaces() = 0; + + /* Sets a DCEL_Face ID for each DCEL_Face */ + void initFaces(); + + /* Sets an arbitrary DCEL_Edge that is a prt of the DCEL_Face, so that that it can be traversed in counter-clockwise order */ + virtual void initStartEdge() = 0; + + /* DCEL_Edge record functions */ + + /* Sets DCEL_Edge ID and the origin of the DCEL_Edge */ + virtual void initEdges() = 0; + + /* Sets the DCEL_Face to the half-DCEL_Edge's left */ + virtual void initIncidentFace() = 0; + + /* Sets the twin DCEL_Edge of the half-DCEL_Edge */ + void initTwinEdge(); + + /* Sets the DCEL_Edge previous to this DCEL_Edge in the incident DCEL_Face */ + virtual void initPrevEdge() = 0; + + /* Sets the DCEL_Edge next to this DCEL_Edge in the incident DCEL_Face */ + virtual void initNextEdge() = 0; + + virtual void setNumVertices() = 0; + virtual void setNumFaces() = 0; + virtual void setVertexCoords(int ID) = 0; + virtual void setVertex(int v1, int v2) = 0; + virtual void deleteVertex(int ID) = 0; + virtual void deleteFace(int ID) = 0; + virtual void addFace() = 0; + virtual void addVertex(int ID) = 0; + + /* Test function to create bot from scratch + virtual void createBot();*/ + +public: + virtual void setVertices() = 0; + virtual void setFaces() = 0; + + std::vector<bool> is_edge_checked; + + /* Getters and setters */ + + /* Get vertex with vertex id=ID */ + DCEL_Vertex* getVertex(int ID); + /* Get edge with edge id=ID */ + DCEL_Edge* getEdge(std::pair<int, int> ID); + /* Returns index of the edge given it's ID pair */ + int getEdgeIndex(std::pair<int, int> ID, int face_id); + /* Get face with face id=ID */ + DCEL_Face* getFace(int ID); + /* Set the coordinates of the vertex with vertex id=ID */ + void setVertexCoordsInRecord(int ID, double coordinates[3]); + /* Replace all references of a vertex to be replaced with the other vertex */ + void setVertexRecord(DCEL_Vertex *vertex, DCEL_Vertex *vertex_to_be_replaced); + /* Deletes a vertex with vertex id=ID */ + void deleteVertexRecord(int ID); + /* Insert a vertex on the half edge */ + void insertVertexOnEdge(DCEL_Vertex *vertex, DCEL_Edge *edge); + /* Deletes edge with edge id=ID */ + void deleteEdge(std::pair<int, int> ID, int face_id); + /* Split the face through the line joining the given vertex (on an edge) to the opposite vertex */ + void splitFace(int face_id, DCEL_Vertex *vertex_on_edge, DCEL_Vertex *opp_vertex); + /* Adds new face and sets the starting edge and returns the ID */ + int addFaceRecord(DCEL_Edge *start_edge); + /* Adds an edge and sets the DCEL edge record attributes */ + DCEL_Edge* addEdge(std::pair<int, int> edge_id, DCEL_Vertex *origin, DCEL_Edge *twin, DCEL_Face *inc_face, DCEL_Edge *next, DCEL_Edge *prev); + /* Deletes face with given face_id */ + void deleteFaceRecord(int face_id); + /* Distance to an edge is the perpendicular distance if an orthogonal projection is possible, + * else it is the distance to the closer end point. + * Set the feature edge or the vertex accordingly. Set the other one to NULL. + * The edge if set, it is the half edge with the unbounded face. + * Line joining the vertex to the edge should not intersect any existing edge/vertex */ + DCEL_Edge* findClosestEdge(DCEL_Vertex *vertex); + /* Returns the end point of the edge closer to the vertex */ + DCEL_Vertex* findCloserVertex(DCEL_Edge *edge, DCEL_Vertex *vertex); + /*Adds new vertex with the given coordinates and returns ID*/ + int addVertexRecord(double coordinates[3]); + /* Checks if the vertex with the given ID is present in the face with the given ID) */ + bool isVertexInFace(int vertex_id, int face_id); + /* Checks if an edge is a free edge */ + bool isFreeEdge(DCEL_Edge *edge); + /* Returns the start and end edges of a free-edge chain */ + DCEL_Edge* findFreeEdgeChain(); + /* Returns number of half edges in the DCEL */ + int getNumEdges(); + /* Checks whether the edge opposite to vertex in face intersects the line from vertex to other_vertx */ + bool doesLineIntersectFaceWithVertex(DCEL_Face *face, DCEL_Vertex *vertex, double other_vertex[3]); + /* Checks whether line from vertex to the ortho_proj intersects the face */ + bool doesLineIntersectFace(DCEL_Face *face, DCEL_Vertex *vertex, double ortho_proj[3]); + /* Returns the face adjacent to 'face' around the vertex */ + DCEL_Face* getNextFace(DCEL_Face *face, DCEL_Vertex *vertex); + /* Checks whether the line from vertex to closer_vertex crosses any faces around vertex */ + bool checkEligibleEdge(DCEL_Vertex *vertex, double closer_vertex[3]); + /* Sets the boundary edge of the mesh, and hence the boundary of the mesh can be traversed through this one edge */ + void squeezeOutEdges(DCEL_Edge *edge, DCEL_Edge *next_edge); + /* Sets twin reference after vertex/edge contraction */ + void checkAndSetTwins(DCEL_Vertex *vertex); + /* Checking if one edge lies on the chain of another edge */ + bool isEdgeOnChain(DCEL_Edge *edge_to_be_checked, DCEL_Edge *chain_edge); + /* Returns edge in the given index in the edge list */ + DCEL_Edge* getEdgeWithIndex(int index); + +}; + +#endif /* SRC_LIBANALYZE_MESHHEALING_MESHCONVERSION_H_ */ + +/* + * Local Variables: + * mode: C + * tab-width: 8 + * indent-tabs-mode: t + * c-file-style: "stroustrup" + * End: + * ex: shiftwidth=4 tabstop=8 + */ Property changes on: brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion_brlcad.cpp =================================================================== --- brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion_brlcad.cpp (rev 0) +++ brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion_brlcad.cpp 2016-10-12 14:32:21 UTC (rev 69035) @@ -0,0 +1,460 @@ +/* M E S H C O N V E R S I O N _ B R L C A D . C P P + * BRL-CAD + * + * Copyright (c) 2016 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + */ +/** @file MeshConversion_brlcad.cpp + * + * Main logic for converting a bot to the PolygonalMesh object + * + */ + +#include "MeshConversion_brlcad.h" + +#include <bu/defines.h> +#include <bu/malloc.h> +#include <malloc.h> +#include <rt/geom.h> +#include <rt/primitives/bot.h> +#include <stddef.h> +#include <utility> +#include <vector> + +#include "DCEL.h" +#include "Geometry.h" + +BrlcadMesh::BrlcadMesh(rt_bot_internal *bot_mesh) +{ + bot = bot_mesh; + initVertices(); + initFaces(); + initEdges(); + initIncidentFace(); + initIncidentEdge(); + initStartEdge(); + initTwinEdge(); + initPrevEdge(); + //initNextEdge(); +} + +/* DCEL_Vertex record functions */ + +int +BrlcadMesh::getNumVertices() +{ + return bot->num_vertices; +} + +void +BrlcadMesh::initVertices() +{ + unsigned int num_vertices = this->getNumVertices(); + DCEL_Vertex vertex; + + for (unsigned int i = 0; i < num_vertices; i++) { + + vertex.vertex_id = i; + + vertex.coordinates[0] = bot->vertices[VERTICES_PER_FACE * i]; + vertex.coordinates[1] = bot->vertices[VERTICES_PER_FACE * i + 1]; + vertex.coordinates[2] = bot->vertices[VERTICES_PER_FACE * i + 2]; + vertexlist.push_back(vertex); + } +} + +/* DCEL_Face record functions + * Note: The unbounded DCEL_Face will have face_id = 0. Its start_edge will be set to NULL. + */ + +int +BrlcadMesh::getNumFaces() +{ + return bot->num_faces; +} + +void +BrlcadMesh::initStartEdge() +{ + int vertex1, vertex2, vertex3; + int v1, v2; + unsigned int num_faces = this->getNumFaces(); + facelist[UNBOUNDED_FACE].start_edge = NULL; + + for (unsigned int i = 0; i < num_faces; ++i) { + vertex1 = bot->faces[VERTICES_PER_FACE * i]; + vertex2 = bot->faces[VERTICES_PER_FACE * i + 1]; + vertex3 = bot->faces[VERTICES_PER_FACE * i + 2]; + + if (bot->orientation == RT_BOT_CCW) + vertex2 = bot->faces[VERTICES_PER_FACE * i + 1]; + else if (bot->orientation == RT_BOT_CW) + vertex2 = bot->faces[VERTICES_PER_FACE * i + 2]; + else { + /* Determine the CCW orientation and set the vertex2 accordingly */ + + /* If the orientation is clockwise, switch the second and third vertices */ + if (orientation(getVertex(vertex1)->coordinates, getVertex(vertex2)->coordinates, \ + getVertex(vertex3)->coordinates) != CCW) + vertex2 = vertex3; + } + + for (unsigned int j = 0; j < edgelist.size(); j++) { + v1 = edgelist[j].edge_id.first; + v2 = edgelist[j].edge_id.second; + + if (vertex1 == v1 && vertex2 == v2) { + /* i+1 due to the presence of the unbounded face record */ + facelist[i + 1].start_edge = &(edgelist[j]); + break; + } + } + } +} + +/* DCEL_Edge record functions */ + +void +BrlcadMesh::initEdges() +{ + unsigned int num_faces = this->getNumFaces(); + int v1, v2; + + /* To check if the half-edges are already present */ + bool flag; + + for (unsigned int i = 0; i < num_faces; ++i) { + + DCEL_Edge temp; + std::pair<int, int> id; + + for (unsigned int j = 0; j < VERTICES_PER_FACE; ++j){ + v1 = bot->faces[VERTICES_PER_FACE * i + j]; + v2 = bot->faces[VERTICES_PER_FACE * i + (j + 1) % VERTICES_PER_FACE]; + + flag = false; + + /* Checking if the DCEL_Edge from v1 to v2 is already present */ + for (unsigned int k = 0; k < edgelist.size(); k++) { + if (v1 == edgelist[k].edge_id.first && v2 == edgelist[k].edge_id.second) + flag = true; + + if (flag) + break; + } + + if (!flag) { + /* Setting the DCEL_Edge record for the half record from v1 to v2 */ + temp.origin = &(vertexlist[v1]); + temp.edge_id = std::make_pair(v1, v2); + edgelist.push_back(temp); + + /* Setting the DCEL_Edge record for the half record from v2 to v1 */ + temp.origin = &(vertexlist[v2]); + temp.edge_id = std::make_pair(v2, v1); + edgelist.push_back(temp); + } + } + } +} + +void +BrlcadMesh::initIncidentFace() +{ + + int vertex1, vertex2; + unsigned int num_faces = this->getNumFaces(); + int v1, v2, v3, temp; + bool face_set ; + + int *a = new int[2]; + int *b = new int[3]; + + for (unsigned int i = 0; i < edgelist.size(); ++i) { + face_set = false; + + vertex1 = edgelist[i].edge_id.first; + vertex2 = edgelist[i].edge_id.second; + + a[0] = vertex1; + a[1] = vertex2; + + for (unsigned int j = 0; j < num_faces; j++) { + v1 = bot->faces[VERTICES_PER_FACE * j]; + v2 = bot->faces[VERTICES_PER_FACE * j + 1]; + v3 = bot->faces[VERTICES_PER_FACE * j + 2]; + + if (bot->orientation == RT_BOT_CCW) { + v2 = bot->faces[VERTICES_PER_FACE * i + 1]; + v3 = bot->faces[VERTICES_PER_FACE * i + 2]; + } + else if (bot->orientation == RT_BOT_CW) { + v2 = bot->faces[VERTICES_PER_FACE * i + 2]; + v3 = bot->faces[VERTICES_PER_FACE * i + 1]; + } + else { + + /* If the orientation is clockwise, switch the second and third vertices */ + if(orientation(getVertex(v1)->coordinates, getVertex(v2)->coordinates, getVertex(v3)->coordinates) != CCW) { + temp = v2; + v2 = v3; + v3 = temp; + } + } + + b[0] = v1; + b[1] = v2; + b[2] = v3; + + /* Check if a (vertex1 and vertex2) is present in the same order as b (the triangle made of v1, v2, v3) */ + if (rt_bot_same_orientation(a, b)) { + edgelist[i].incident_face = &(facelist[j + 1]); + face_set = true; + break; + } + } + /* If no DCEL_Face has been set till here, set the unbounded DCEL_Face to be the incident DCEL_Face */ + if (!face_set) { + edgelist[i].incident_face = &(facelist[UNBOUNDED_FACE]); + } + } + delete[] a; + delete[] b; +} + +void +BrlcadMesh::initPrevEdge() +{ + int inc_face, third_vertex; + int vertex1, vertex2; + + for (unsigned int i = 0; i < edgelist.size(); i++) { + + /* inc_face has the face_id of the incident face for the current edge */ + inc_face = edgelist[i].incident_face->face_id; + + /* The vertex_id of the vertex list comprising the current half edge */ + vertex1 = edgelist[i].edge_id.first; + vertex2 = edgelist[i].edge_id.second; + + /* If the face is the unbounded face, check in the edge list for the vertex with vertex1 as its end vertex, + * and has the unbounded face incident on it. */ + if(inc_face == UNBOUNDED_FACE) { + for(unsigned int j = 0; j < edgelist.size(); j++) { + if(edgelist[j].edge_id.second == vertex1 && edgelist[j].incident_face->face_id == UNBOUNDED_FACE) { + edgelist[i].previous = &edgelist[j]; + edgelist[j].next = &edgelist[i]; + break; + } + } + } + + else { + for (unsigned int j = 0; j < VERTICES_PER_FACE; j++) { + + /*Finding the third vertex, that is one that is not vertex1 or vertex2 */ + third_vertex = bot->faces[VERTICES_PER_FACE * (inc_face - 1) + j]; + + if (third_vertex != vertex1 && third_vertex != vertex2) { + /* Finding the edge_id of the edge from third_vertex to vertex1 */ + + DCEL_Edge *edge = getEdge(std::make_pair(third_vertex, vertex1)); + edgelist[i].previous = edge; + edge->next = &edgelist[i]; + + break; + } + } + } + } +} + +void +BrlcadMesh::initNextEdge() +{ + + int inc_face, third_vertex; + int vertex1, vertex2; + + for (unsigned int i = 0; i < edgelist.size(); i++) { + + /* inc_face has the face_id of the incident face for the current edge */ + inc_face = edgelist[i].incident_face->face_id; + + /* The vertex_id of the vertices comprising the current half edge */ + vertex1 = edgelist[i].edge_id.first; + vertex2 = edgelist[i].edge_id.second; + + /* If the face is the unbounded face, check in the edge list for the vertex with vertex 1 as its end vertex, + * and has the unbounded face incident on it. */ + if(inc_face == UNBOUNDED_FACE) { + for(unsigned int j = 0; j < edgelist.size(); j++) { + if(edgelist[j].edge_id.first == vertex2 && edgelist[j].incident_face->face_id == UNBOUNDED_FACE) { + edgelist[i].next = &edgelist[j]; + break; + } + } + } else { + for (unsigned int j = 0; j < VERTICES_PER_FACE; j++) { + + /*Finding the third vertex, that is one that is not vertex1 or vertex2 */ + if (bot->faces[VERTICES_PER_FACE * (inc_face - 1) + j] != vertex1 && bot->faces[VERTICES_PER_FACE * (inc_face - 1) + j] != vertex2) { + third_vertex = bot->faces[VERTICES_PER_FACE * (inc_face - 1) + j]; + break; + } + } + + /* Finding the edge_id of the edge from vertex2 to third_vertex */ + edgelist[i].next = getEdge(std::make_pair(vertex2, third_vertex)); + } + } +} + +void +BrlcadMesh::setNumVertices() +{ + bot->num_vertices = vertexlist.size(); +} + +void +BrlcadMesh::setNumFaces() +{ + bot->num_faces = facelist.size(); +} + +void +BrlcadMesh::setVertices() +{ + bu_free(bot->vertices, "vertices"); + bot->vertices = (fastf_t*)bu_malloc(sizeof(fastf_t) * vertexlist.size() * 3, "vertices reallocation"); + bot->num_vertices = vertexlist.size(); + + for (unsigned int i = 0; i < vertexlist.size(); i++) { + for (int k = 0; k < 3; k++) + bot->vertices[3 * i + k] = vertexlist[i].coordinates[k]; + } +} + +void +BrlcadMesh::setFaces() +{ + DCEL_Edge *edge; + + bot->faces = (int*)realloc(bot->faces, 3* (facelist.size() - 1) * sizeof(int)); + bot->num_faces = facelist.size() - 1; + + /* Unbounded face is the first record, so skip */ + for (unsigned int i = 1; i < facelist.size(); ++i) { + + edge = facelist[i].start_edge; + for (int j = 0; j < VERTICES_PER_FACE; ++j) { + + bot->faces[VERTICES_PER_FACE * (i - 1) + j] = edge->edge_id.first; + edge = edge->next; + } + } +} + +void +BrlcadMesh::setVertexCoords(int ID) +{ + for (int i = 0; i < 3; i++) { + bot->vertices[3 * ID + i] = vertexlist[ID].coordinates[i]; + } +} + +void BrlcadMesh::setVertex(int vertex, int vertex_to_be_replaced) +{ + for (int i = 0; i < getNumFaces(); ++i) { + for (int j = 0; j < VERTICES_PER_FACE; ++j) { + if (bot->faces[VERTICES_PER_FACE * i + j] == vertex_to_be_replaced) + bot->faces[VERTICES_PER_FACE * i + j] = vertex; + } + } +} + +void +BrlcadMesh::deleteVertex(int ID) +{ + for (int i = ID ; i < getNumVertices() - 1; i++) { + for (int j = 0; j < 3; j++) { + bot->vertices[3 * i + j] = bot->vertices[3 * (i + 1) + j]; + } + } + + for (unsigned int i = 0; i < bot->num_faces; i++) { + for (int j = 0; j < VERTICES_PER_FACE; j++) { + if (bot->faces[3* i + j] > ID) { + bot->faces[3* i + j]--; + } + } + } + + bot->num_vertices -= 1; + /*bot->vertices = (fastf_t*)realloc(bot->vertices, 3 * getNumVertices() * sizeof(fastf_t));*/ +} + +void +BrlcadMesh::deleteFace(int ID) +{ + for (int i = ID ; i < (getNumFaces() - 1); i++) { + for (int j = 0; j < VERTICES_PER_FACE; j++) { + bot->faces[3 * i + j] = bot->faces[3 * (i + 1) + j]; + } + } + bot->num_faces -= 1; + + /*bot->faces = (int*)realloc(bot->faces, 3 * getNumFaces() * sizeof(int));*/ +} + +void +BrlcadMesh::addFace() +{ + int *new_faces = (int*)realloc(bot->faces, 3 * (getNumFaces() + 1) * sizeof(int)); + bot->faces = new_faces; + int ID = facelist.size() - 1; + DCEL_Edge *trav_edge = facelist[ID].start_edge; + + for (int i = 0; i < VERTICES_PER_FACE; i++) { + bot->faces[3 * (ID - 1) + i] = trav_edge->edge_id.first; + trav_edge = trav_edge->next; + } + + bot->num_faces += 1; +} + +void +BrlcadMesh::addVertex(int ID) +{ + fastf_t *new_vertices = (fastf_t*)realloc(bot->vertices, 3 * (getNumVertices() + 1) * sizeof(fastf_t)); + + bot->vertices = new_vertices; + + for (int i = 0; i < 3; i++) { + bot->vertices[3 * ID + i] = vertexlist[ID].coordinates[i]; + } + bot->num_vertices += 1; +} + +/* + * Local Variables: + * mode: C + * tab-width: 8 + * indent-tabs-mode: t + * c-file-style: "stroustrup" + * End: + * ex: shiftwidth=4 tabstop=8 + */ Property changes on: brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion_brlcad.cpp ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion_brlcad.h =================================================================== --- brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion_brlcad.h (rev 0) +++ brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion_brlcad.h 2016-10-12 14:32:21 UTC (rev 69035) @@ -0,0 +1,74 @@ +/* M E S H C O N V E R S I O N _ B R L C A D . H + * BRL-CAD + * + * Copyright (c) 2016 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + */ +/** @file MeshConversion_brlcad.h + * + * Definitions of the functions that let the PolygonalMesh object interact with the bot type + * + */ + +#ifndef SRC_LIBANALYZE_MESHHEALING_MESHCONVERSION_BRLCAD_H_ +#define SRC_LIBANALYZE_MESHHEALING_MESHCONVERSION_BRLCAD_H_ + +#include "MeshConversion.h" + +struct rt_bot_internal; + +class BrlcadMesh: public PolygonalMesh { + +public: + BrlcadMesh(rt_bot_internal *bot_mesh); + +private: + rt_bot_internal *bot; + + int getNumVertices(); + void initVertices(); + int getNumFaces(); + void initStartEdge(); + void initEdges(); + void initIncidentFace(); + void initPrevEdge(); + void initNextEdge(); + + void setNumVertices(); + void setNumFaces(); + void setVertices(); + void setFaces(); + void setVertexCoords(int ID); + void setVertex(int v1, int v2); + void deleteVertex(int ID); + void deleteFace(int ID); + void addFace(); + void addVertex(int ID); + + /* Test function + void createBot();*/ +}; + +#endif /* SRC_LIBANALYZE_MESHHEALING_MESHCONVERSION_BRLCAD_H_ */ +/* + * Local Variables: + * mode: C + * tab-width: 8 + * indent-tabs-mode: t + * c-file-style: "stroustrup" + * End: + * ex: shiftwidth=4 tabstop=8 + */ Property changes on: brlcad/trunk/src/libanalyze/MeshHealing/MeshConversion_brlcad.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: brlcad/trunk/src/libanalyze/MeshHealing/Stitch.cpp =================================================================== --- brlcad/trunk/src/libanalyze/MeshHealing/Stitch.cpp (rev 0) +++ brlcad/trunk/src/libanalyze/MeshHealing/Stitch.cpp 2016-10-12 14:32:21 UTC (rev 69035) @@ -0,0 +1,164 @@ +/* S T I T C H . C P P + * BRL-CAD + * + * Copyright (c) 2016 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + */ +/** @file Stitch.cpp + * + * Main logic for the stitching module in the mesh healing module + * + */ + +#include "Stitch.h" + +#include <stddef.h> +#include <algorithm> +#include <utility> + +#include "Geometry.h" + +void +stitchGaps(PolygonalMesh *polymesh, DCEL_Edge* start_A, DCEL_Edge* start_B, double tolerance) +{ + /* The edges that will be used to traverse both meshes, according to the advancing rule */ + DCEL_Edge *edge_A = start_A, *edge_B = start_B; + + DCEL_Vertex *first_A, *first_B; + DCEL_Vertex *next_A, *next_B; + int flag_A = 0, flag_B = 0; + + double perimeter_A, perimeter_B; + + do { + + first_A = polymesh->getVertex(edge_A->edge_id.first); + first_B = polymesh->getVertex(edge_B->edge_id.second); + + next_A = polymesh->getVertex(edge_A->edge_id.second); + next_B = polymesh->getVertex(edge_B->edge_id.first); + + perimeter_A = perimeter(first_A->coordinates, next_A->coordinates, first_B->coordinates); + perimeter_B = perimeter(first_B->coordinates, next_B->coordinates, first_A->coordinates); + + if (perimeter_A < perimeter_B) { + /* Add triangle comprising the vertices first_A, next_A, and first_B, and + * Advance the edge pointer on chain A + */ + addFace(polymesh, first_A, next_A, first_B, edge_A->previous, edge_A->next, edge_B->previous, edge_B, tolerance); + edge_A = edge_A->next; + if (edge_A == start_A) + flag_A = 1; + + } else { + /* Add triangle comprising the vertices first_B, next_B, and first_A, and + * Advance the edge pointer on chain B + */ + addFace(polymesh, next_B, first_B, first_A, edge_B->previous, edge_A->next, edge_A->previous, edge_A, tolerance); + edge_B = edge_B->previous; + if (edge_B == start_B) + flag_B = 1; + } + } + while (!(edge_A == start_A && flag_A) && !(edge_B == start_B && flag_B)); + + if (edge_A == start_A) { + + while (edge_B != start_B) { + /* Make triangles with the the ending vertex in the A chain + * And advance the edge pointer on the B chain till it reaches the end of the chain + */ + /*addFace(polymesh, next_B, first_B, edge_A->origin, edge_A->previous, );*/ + edge_B = edge_B->next; + } + } + + else if (edge_B == start_B) { + while (edge_A != start_A) { + /* Make triangles with the the ending vertex in the A chain + * And advance the edge pointer on the B chain till it reaches the end of the chain + */ + + edge_A = edge_A->previous; + } + } +} + +void +addFace(PolygonalMesh *polymesh, DCEL_Vertex* first_vertex_A, DCEL_Vertex* second_vertex_A, DCEL_Vertex* first_vertex_B, \ + DCEL_Edge *prev_A, DCEL_Edge *next_A, DCEL_Edge *prev_B, DCEL_Edge *next_B, double tolerance) +{ + if (std::max(shortestDistBwPoints(first_vertex_A->coordinates, first_vertex_B->coordinates), \ + shortestDistBwPoints(second_vertex_A->coordinates, first_vertex_B->coordinates)) > tolerance) + return; + + if (!polymesh->checkEligibleEdge(first_vertex_A, first_vertex_B->coordinates)) + return; + if (!polymesh->checkEligibleEdge(first_vertex_B, first_vertex_A->coordinates)) + return; + if (!polymesh->checkEligibleEdge(second_vertex_A, first_vertex_B->coordinates)) + return; + if (!polymesh->checkEligibleEdge(first_vertex_B, second_vertex_A->coordinates)) + return; + + DCEL_Edge *existing_edge = polymesh->getEdge(std::make_pair(first_vertex_A->vertex_id, second_vertex_A->vertex_id)); + + DCEL_Edge *b_edge_btoa = polymesh->getEdge(std::make_pair(first_vertex_B->vertex_id, first_vertex_A->vertex_id)); + DCEL_Edge *ub_edge_atob = NULL; + + /* Adding half-edges from the first vertex of chain A to the vertex on chain B, if they are not already present */ + if (b_edge_btoa == NULL) { + ub_edge_atob = polymesh->addEdge(std::make_pair(first_vertex_A->vertex_id, first_vertex_B->vertex_id), first_vertex_A, \ + NULL, polymesh->getFace(UNBOUNDED_FACE), prev_A, next_B); + b_edge_btoa = polymesh->addEdge(std::make_pair(first_vertex_B->vertex_id, first_vertex_A->vertex_id), first_vertex_B, \ + ub_edge_atob, NULL, existing_edge, NULL); + } + + DCEL_Edge *b_edge_atob = polymesh->getEdge(std::make_pair(second_vertex_A->vertex_id, first_vertex_B->vertex_id)); + DCEL_Edge *ub_edge_btoa = NULL; + + if (b_edge_atob == NULL) { + ub_edge_btoa = polymesh->addEdge(std::make_pair(first_vertex_B->vertex_id, second_vertex_A->vertex_id), first_vertex_B, \ + NULL, polymesh->getFace(UNBOUNDED_FACE), next_A, prev_B); + b_edge_atob = polymesh->addEdge(std::make_pair(second_vertex_A->vertex_id, first_vertex_B->vertex_id), second_vertex_A, \ + ub_edge_btoa, NULL, NULL, existing_edge); + } + if (ub_edge_atob != NULL) + ub_edge_atob->twin = b_edge_btoa; + + if (ub_edge_btoa != NULL) + ub_edge_btoa->twin = b_edge_atob; + + b_edge_atob->next = b_edge_btoa; + b_edge_btoa->previous = b_edge_atob; + + /* Add face record for the new face */ + int new_face = polymesh->addFaceRecord(b_edge_atob); + + b_edge_atob->incident_face = polymesh->getFace(new_face); + b_edge_btoa->incident_face = polymesh->getFace(new_face); + existing_edge->incident_face = polymesh->getFace(new_face); +} + +/* + * Local Variables: + * mode: C + * tab-width: 8 + * indent-tabs-mode: t + * c-file-style: "stroustrup" + * End: + * ex: shiftwidth=4 tabstop=8 + */ Property changes on: brlcad/trunk/src/libanalyze/MeshHealing/Stitch.cpp ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: brlcad/trunk/src/libanalyze/MeshHealing/Stitch.h =================================================================== --- brlcad/trunk/src/libanalyze/MeshHealing/Stitch.h (rev 0) +++ brlcad/trunk/src/libanalyze/MeshHealing/Stitch.h 2016-10-12 14:32:21 UTC (rev 69035) @@ -0,0 +1,57 @@ +/* S T I T C H . H + * BRL-CAD + * + * Copyright (c) 2016 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + */ +/** @file Stitch.h + * + * Definitions of the functions for the stitching module + * + */ + +#ifndef SRC_LIBANALYZE_MESHHEALING_STITCH_H_ +#define SRC_LIBANALYZE_MESHHEALING_STITCH_H_ + +#include "MeshConversion.h" + +/*#include "MeshConversion_brlcad.h"*/ + +/* Function to stitch gaps of chain start_A to end_A with start_B to end_B + * Advancing rule is that whichever chain has a triangle with the other other chain with the lesser perimeter, + * Will have it's edge pointer advanced + */ +void stitchGaps(PolygonalMesh *polymesh, DCEL_Edge *start_A, DCEL_Edge *start_B, double tolerance); + +/* Function to add a triangle comprising the vertices first_vertex_A and second_vertex_A from the chain A, and + * first_vertex_B from the chain B + * This function will add four half edges between the two mesh chains, and + * Set references for the existing edge records + */ +void addFace(PolygonalMesh *polymesh, DCEL_Vertex *first_vertex_A, DCEL_Vertex *second_vertex_A, DCEL_Vertex *first_vertex_B, \ + DCEL_Edge *prev_A, DCEL_Edge *next_A, DCEL_Edge *prev_B, DCEL_Edge *next_B, double tolerance); + +#endif /* SRC_LIBANALYZE_MESHHEALING_STITCH_H_ */ + +/* + * Local Variables: + * mode: C + * tab-width: 8 + * indent-tabs-mode: t + * c-file-style: "stroustrup" + * End: + * ex: shiftwidth=4 tabstop=8 + */ Property changes on: brlcad/trunk/src/libanalyze/MeshHealing/Stitch.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: brlcad/trunk/src/libanalyze/MeshHealing/Zipper.cpp =================================================================== --- brlcad/trunk/src/libanalyze/MeshHealing/Zipper.cpp (rev 0) +++ brlcad/trunk/src/libanalyze/MeshHealing/Zipper.cpp 2016-10-12 14:32:21 UTC (rev 69035) @@ -0,0 +1,359 @@ +/* Z I P P E R . C P P + * BRL-CAD + * + * Copyright (c) 2016 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + */ +/** @file Zipper.cpp + * + * Main logic for the zippering gaps module in the mesh healing module + * + */ + +#include "Zipper.h" + +#include <stddef.h> +#include <climits> +#include <utility> +#include <vector> + +#include "Geometry.h" + +void +calcFeaturePair(PolygonalMesh *polymesh, queue_element *node) +{ + DCEL_Edge *closest_edge; + DCEL_Vertex *vertex = node->vertex; + /* Vertices of the closest edge */ + DCEL_Vertex *v1, *v2; + + /* Get the closest edge */ + closest_edge = polymesh->findClosestEdge(vertex); + if(closest_edge == NULL) { + node->feature_edge = NULL; + node->feature_vertex = NULL; + node->dist = INT_MAX; + return; + } + v1 = (polymesh->getVertex(closest_edge->edge_id.first)); + v2 = (polymesh->getVertex(closest_edge->edge_id.second)); + + /* Check if orthogonal projection is possible, if yes set the feature_edge, else set the feature_vertex */ + if (isOrthoProjPossible(v1->coordinates, v2->coordinates, vertex->coordinates)) { + node->feature_edge = closest_edge; + node->dist = shortestDistToLine(v1->coordinates, v2->coordinates, vertex->coordinates); + node->feature_vertex = NULL; + } + + else { + /* Returns the closest end point */ + node->feature_vertex = polymesh->findCloserVertex(closest_edge, vertex); + node->dist = shortestDistBwPoints(vertex->coordinates, node->feature_vertex->coordinates); + node->feature_edge = NULL; + } +} + +std::priority_queue<queue_element, std::vector<queue_element>, compareDist> +initPriorityQueue(PolygonalMesh *polymesh, DCEL_Edge *start, double tolerance) +{ + queue_element node; + DCEL_Vertex *vertex; + DCEL_Edge *edge; + int v_id; + + /* Priority queue ordered based on the error measure */ + std::priority_queue<queue_element, std::vector<queue_element>, compareDist> PQ; + + if(start == NULL) + return PQ; + + /* Check if the gap is in the shape of a square. + * If yes pick a vertex and set it's feature vertex to the opposite vertex on the square. + */ + int no_of_sides = 0; + DCEL_Edge *trav_edge = start; + + do { + no_of_sides++; + trav_edge = trav_edge->next; + } + while (trav_edge != start); + + /* If it is a triangular hole, perform vertex contraction on the two closest vertices */ + if (no_of_sides == 3) { + + DCEL_Edge *temp = start, *feature; + int min_dist = INT_MAX; + + for(int i = 0; i < 3; i++) { + + if (isOrthoProjPossible(temp->origin->coordinates, temp->next->origin->coordinates, temp->previous->origin->coordinates)\ + && shortestDistToLine(temp->origin->coordinates, temp->next->origin->coordinates, temp->previous->origin->coordinates) < min_dist) { + feature = temp; + min_dist = shortestDistToLine(temp->origin->coordinates, temp->next->origin->coordinates, temp->previous->origin->coordinates); + } + temp = temp->next; + } + + node.vertex = feature->previous->origin; + node.feature_edge = feature; + node.feature_vertex = NULL; + node.dist = min_dist; + + while (!PQ.empty()) { + PQ.pop(); + } + + PQ.push(node); + + return PQ; + } + + edge = start; + do + { + /* Consider the origin v_id of each edge */ + v_id = edge->origin->vertex_id; + + vertex = polymesh->getVertex(v_id); + node.vertex = vertex; + calcFeaturePair(polymesh, &node); + + /* Push the queue element to the priority queue */ + if (node.dist < tolerance) + PQ.push(node); + + edge = edge->next; + } + while (edge != start); + + if (!PQ.empty()) { + + if (no_of_sides == 4) { + double vec1[3]; + double vec2[3]; + double vec3[3]; + + /* Check if there are two consecutive right angles */ + for (int i = 0; i < 3; i++) { + vec1[i] = start->origin->coordinates[i] - start->next->origin->coordinates[i]; + vec2[i] = start->next->origin->coordinates[i] - start->next->next->origin->coordinates[i]; + vec3[i] = start->next->next->origin->coordinates[i] - start->next->next->next->origin->coordinates[i]; + } + if (orientation(vec1, vec2, vec3) == CW) { + + return PQ; + } + + if (NEAR_0(DOT_PROD(vec1, vec2)) && NEAR_0(DOT_PROD(vec2, vec3))) { + node.vertex = start->origin; + node.feature_vertex = start->next->next->origin; + node.feature_edge = NULL; + node.dist = shortestDistBwPoints(node.vertex->coordinates, node.feature_vertex->coordinates); + + while (!PQ.empty()) { + PQ.pop(); + } + + PQ.push(node); + + return PQ; + } + } + } + + return PQ; +} + +void +zipperGaps(PolygonalMesh *polymesh, double tolerance, std::priority_queue<queue_element, std::vector<queue_element>, compareDist> PQ) +{ + double *A, *B, *C; + bool vertex_contract = false; + int deleted_vertex_id = INT_MAX; + + /*queue_element *curr_element = new queue_element;*/ + queue_element curr_element; + + /* Temporary priority queue */ + std::priority_queue<queue_element, std::vector<queue_element>, compareDist> temp_PQ; + + if (PQ.empty()) { + return; + } + + /*int count = 0;*/ + + while (!PQ.empty()) { + + curr_element = PQ.top(); + PQ.pop(); + /* Vertex has already been healed */ + if(NEAR_0(curr_element.dist)) + continue; + + /* Check if the error measure is within the tolerance for zippering. + * If not break out of the loop, since all the error measures after this one are not going to be lesser than this one + */ + if (curr_element.dist > tolerance) + break; + + /* If the feature is a vertex, move both these vertices to the midpoint of the line connecting the two vertices */ + if (curr_element.feature_vertex != NULL) { + vertex_contract = true; + + double coords[3]; + + /* Find the mid-point */ + coords[0] = (curr_element.vertex->coordinates[0] + curr_element.feature_vertex->coordinates[0]) / 2; + coords[1] = (curr_element.vertex->coordinates[1] + curr_element.feature_vertex->coordinates[1]) / 2; + coords[2] = (curr_element.vertex->coordinates[2] + curr_element.feature_vertex->coordinates[2]) / 2; + + /* Changing the coordinates of the vertex to the midpoint of the vertex and its feature pair */ + polymesh->setVertexCoordsInRecord(curr_element.vertex->vertex_id, coords); + + /* Replace all references of the feature vertex with that of the vertex on the free edge chain */ + polymesh->setVertexRecord(curr_element.vertex, curr_element.feature_vertex); + + /* Delete the feature vertex's record */ + deleted_vertex_id = curr_element.feature_vertex->vertex_id; + + polymesh->deleteVertexRecord(curr_element.feature_vertex->vertex_id); + + if (curr_element.vertex->vertex_id > curr_element.feature_vertex->vertex_id) { + polymesh->checkAndSetTwins(polymesh->getVertex(curr_element.vertex->vertex_id - 1)); + } else { + polymesh->checkAndSetTwins(polymesh->getVertex(curr_element.vertex->vertex_id)); + } + + } + + /* Else if the feature is an edge, project the vertex orthogonally onto the edge and split the edge and its incident face at the point */ + else if (curr_element.feature_edge != NULL) { + + double D[3]; + + vertex_contract = false; + + A = polymesh->getVertex(curr_element.feature_edge->edge_id.first)->coordinates; + B = polymesh->getVertex(curr_element.feature_edge->edge_id.second)->coordinates; + C = polymesh->getVertex(curr_element.vertex->vertex_id)->coordinates; + + findOrthogonalProjection(A, B, C, D); + + polymesh->setVertexCoordsInRecord(curr_element.vertex->vertex_id, D); + + std::pair<int, int> fe_id; + fe_id.first = curr_element.feature_edge->edge_id.first; + fe_id.second = curr_element.feature_edge->edge_id.second; + + int face_id = curr_element.feature_edge->twin->incident_face->face_id; + + /* Insert a vertex with coordinates D on the feature edge. */ + polymesh->insertVertexOnEdge(polymesh->getVertex(curr_element.vertex->vertex_id), curr_element.feature_edge); + + int index = polymesh->getEdgeIndex(std::make_pair(fe_id.second, curr_element.vertex->vertex_id), face_id); + + curr_element.feature_edge = polymesh->getEdgeWithIndex(index); + + /* Split the face incident on the half edge of the feature edge with a bounded face incident */ + polymesh->splitFace(curr_element.feature_edge->incident_face->face_id, \ + polymesh->getVertex(curr_element.vertex->vertex_id), curr_element.feature_edge->previous->origin); + + polymesh->checkAndSetTwins(curr_element.vertex); + } + + if (PQ.empty()) { + return; + } + + /* Testing here + count++; + if (count == 6) + break;*/ + + /* To account for: + * 1. Those vertices with invalid elements + * 2. Those vertices whose coordinates have been changed, and hence the error measure - recalculate for all measures + */ + queue_element *element = new queue_element; + do { + *element = PQ.top(); + PQ.pop(); + + /* To account for the vertex deletion in the vertex contraction process */ + if (vertex_contract && element->vertex->vertex_id == deleted_vertex_id) { + continue; + } + if(vertex_contract && element->vertex->vertex_id > deleted_vertex_id) { + element->vertex = polymesh->getVertex(element->vertex->vertex_id - 1); + } + + calcFeaturePair(polymesh, element); + + if (element->dist < tolerance) + temp_PQ.push(*element); + } + while (!PQ.empty()); + + delete element; + + while (!temp_PQ.empty()) { + PQ.push(temp_PQ.top()); + temp_PQ.pop(); + } + } +} + +int +isValidFeature(PolygonalMesh *polymesh, queue_element *node) +{ + if (node->feature_vertex != NULL) { + if (polymesh->getVertex(node->feature_vertex->vertex_id) == NULL) { + return 0; + } + return 1; + } + + else if(node->feature_edge != NULL){ + if (polymesh->getEdge(std::make_pair(node->feature_edge->edge_id.first, \ + node->feature_edge->edge_id.second)) == NULL) + return 0; + return 1; + } + return 1; +} + +bool +checkIfValidPQ(std::priority_queue<queue_element, std::vector<queue_element>, compareDist> PQ) +{ + while(!PQ.empty()) { + if (PQ.top().dist != INT_MAX) + return true; + PQ.pop(); + } + return false; +} + +/* + * Local Variables: + * mode: C + * tab-width: 8 + * indent-tabs-mode: t + * c-file-style: "stroustrup" + * End: + * ex: shiftwidth=4 tabstop=8 + */ Property changes on: brlcad/trunk/src/libanalyze/MeshHealing/Zipper.cpp ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: brlcad/trunk/src/libanalyze/MeshHealing/Zipper.h =================================================================== --- brlcad/trunk/src/libanalyze/MeshHealing/Zipper.h (rev 0) +++ brlcad/trunk/src/libanalyze/MeshHealing/Zipper.h 2016-10-12 14:32:21 UTC (rev 69035) @@ -0,0 +1,82 @@ +/* Z I P P E R . H + * BRL-CAD + * + * Copyright (c) 2016 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + */ +/** @file Zipper.h + * + * Definitions of the utility functions for the zippering module + * + */ + +#ifndef SRC_LIBANALYZE_MESHHEALING_ZIPPER_H_ +#define SRC_LIBANALYZE_MESHHEALING_ZIPPER_H_ + +#include <queue> +#include <vector> + +#include "MeshConversion.h" + +struct queue_element { + DCEL_Vertex *vertex; + + /* Either vertex feature or the edge feature will be set. + * Feature is the closest edge. + * If an orthogonal projection of the vertex on the edge is possible, the edge feature will be set. + * Else, the vertex feature will be set to the closer end point of the edge. + */ + DCEL_Vertex *feature_vertex; + DCEL_Edge *feature_edge; + + /* Distance between the vertex and the feature - the error measure*/ + double dist; +}; + +/* Operator to compare the error measures of two feature pairs */ +struct compareDist { + bool operator()(queue_element& lhs, queue_element& rhs) + { + return lhs.dist > rhs.dist; + } +}; + +/* Calculate error measures for the vertices on the free edge chain and push the queue_element variable to the priority queue */ +std::priority_queue<queue_element, std::vector<queue_element>, compareDist> initPriorityQueue(PolygonalMesh *polymesh, DCEL_Edge *start, double tol); + +/* Take the priority queue and zippers all those vertices whose error measures fall within the tolerance for zippering */ +void zipperGaps(PolygonalMesh *polymesh, double tolerance, std::priority_queue<queue_element, std::vector<queue_element>, compareDist> PQ); + +/* Check if the queue element has a valid feature */ +int isValidFeature(PolygonalMesh *polymesh, queue_element *node); + +/* Calculates the feature pair of a queue element */ +void calcFeaturePair(PolygonalMesh *polymesh, queue_element *node); + +/* Checks whether at least one node in the Priority queue has a valid feature pair */ +bool checkIfValidPQ(std::priority_queue<queue_element, std::vector<queue_element>, compareDist> PQ); + +#endif /* SRC_LIBANALYZE_MESHHEALING_ZIPPER_H_ */ + +/* + * Local Variables: + * mode: C + * tab-width: 8 + * indent-tabs-mode: t + * c-file-style: "stroustrup" + * End: + * ex: shiftwidth=4 tabstop=8 + */ Property changes on: brlcad/trunk/src/libanalyze/MeshHealing/Zipper.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: brlcad/trunk/src/libanalyze/heal_mesh.cpp =================================================================== --- brlcad/trunk/src/libanalyze/heal_mesh.cpp (rev 0) +++ brlcad/trunk/src/libanalyze/heal_mesh.cpp 2016-10-12 14:32:21 UTC (rev 69035) @@ -0,0 +1,93 @@ +/* H E A L _ M E S H . C P P + * BRL-CAD + * + * Copyright (c) 2016 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + */ +/** @file heal_mesh.cpp + * + * Interface for the heal command in the mesh healing module + * + */ + +#include "common.h" + +#include <stddef.h> +#include <queue> +#include <vector> + +#include "analyze.h" +#include "rt/geom.h" + +#include "MeshHealing/MeshConversion_brlcad.h" +#include "MeshHealing/Stitch.h" +#include "MeshHealing/Zipper.h" + + +void +analyze_heal_bot(struct rt_bot_internal *bot, double zipper_tol) +{ + PolygonalMesh *polymesh = new BrlcadMesh(bot); + + /* Zippering the gaps */ + + polymesh->is_edge_checked.assign(polymesh->getNumEdges(), false); + + DCEL_Edge *start; + do { + std::priority_queue<queue_element, std::vector<queue_element>, compareDist> PQ; + start = polymesh->findFreeEdgeChain(); + + PQ = initPriorityQueue(polymesh, start, zipper_tol); + zipperGaps(polymesh, zipper_tol, PQ); + /*break;*/ + } + while (start != NULL); + + /* For testing purposes - creating the bot from scratch */ + polymesh->setVertices(); + polymesh->setFaces(); + + + /*Stitching bigger defects + + polymesh->is_edge_checked.assign(polymesh->getNumEdges(), false); + + DCEL_Edge *closest_edge = NULL; + + while (start != NULL) { + start = polymesh->findFreeEdgeChain(); + if (start == NULL) + break; + + closest_edge = polymesh->findClosestEdge(start->origin); + + if (!polymesh->isEdgeOnChain(closest_edge, start)) { + stitchGaps(polymesh, start, closest_edge, stitch_tol); + } + }*/ +} + + +/* + * Local Variables: + * tab-width: 8 + * mode: C + * indent-tabs-mode: t + * c-file-style: "stroustrup" + * End: + * ex: shiftwidth=4 tabstop=8 + */ Property changes on: brlcad/trunk/src/libanalyze/heal_mesh.cpp ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Modified: brlcad/trunk/src/libged/CMakeLists.txt =================================================================== --- brlcad/trunk/src/libged/CMakeLists.txt 2016-10-12 11:46:13 UTC (rev 69034) +++ brlcad/trunk/src/libged/CMakeLists.txt 2016-10-12 14:32:21 UTC (rev 69035) @@ -118,6 +118,7 @@ grid2model_lu.c grid2view_lu.c group.c + heal.c hide.c how.c human.c Added: brlcad/trunk/src/libged/heal.c =================================================================== --- brlcad/trunk/src/libged/heal.c (rev 0) +++ brlcad/trunk/src/libged/heal.c 2016-10-12 14:32:21 UTC (rev 69035) @@ -0,0 +1,91 @@ +/* H E A L . C + * BRL-CAD + * + * Copyright (c) 2016 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + */ +/** @file heal.c + * + * Heal command for the mesh healing operations + * + */ + +#include "common.h" +#include "analyze.h" + +#include <stdlib.h> +#include <ctype.h> +#include <string.h> + +#include "bg/chull.h" +#include "rt/geom.h" +#include "wdb.h" +#include "./ged_private.h" + + +int +ged_heal(struct ged *gedp, int argc, const char *argv[]) +{ + struct directory *bot_dp; + struct rt_db_internal intern; + struct rt_bot_internal *bot; + const char *cmd = argv[0]; + const char *primitive = NULL; + + double zipper_tol = 0; + + GED_CHECK_DATABASE_OPEN(gedp, GED_ERROR); + GED_CHECK_READ_ONLY(gedp, GED_ERROR); + GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR); + + /* initialize result */ + bu_vls_trunc(gedp->ged_result_str, 0); + + primitive = argv[1]; + + if(argc > 2) + zipper_tol = atof(argv[2]); + + /* get bot */ + GED_DB_LOOKUP(gedp, bot_dp, primitive, LOOKUP_NOISY, GED_ERROR & GED_QUIET); + GED_DB_GET_INTERNAL(gedp, &intern, bot_dp, bn_mat_identity, &rt_uniresource, GED_ERROR); + + if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD || intern.idb_minor_type != DB5_MINORTYPE_BRLCAD_BOT) { + bu_vls_printf(gedp->ged_result_str, "%s: %s is not a BOT solid!", cmd, primitive); + return GED_ERROR; + } + + bot = (struct rt_bot_internal *)intern.idb_ptr; + RT_BOT_CK_MAGIC(bot); + + analyze_heal_bot(bot, zipper_tol); + rt_db_put_internal(bot_dp, gedp->ged_wdbp->dbip, &intern, &rt_uniresource); + + bu_vls_printf(gedp->ged_result_str, "Healed Mesh!"); + + return GED_OK; +} + + +/* + * Local Variables: + * mode: C + * tab-width: 8 + * indent-tabs-mode: t + * c-file-style: "stroustrup" + * End: + * ex: shiftwidth=4 tabstop=8 + */ Property changes on: brlcad/trunk/src/libged/heal.c ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Modified: brlcad/trunk/src/libtclcad/tclcad_obj.c =================================================================== --- brlcad/trunk/src/libtclcad/tclcad_obj.c 2016-10-12 11:46:13 UTC (rev 69034) +++ brlcad/trunk/src/libtclcad/tclcad_obj.c 2016-10-12 14:32:21 UTC (rev 69035) @@ -1119,6 +1119,7 @@ {"grid2model_lu", "x y", 4, to_view_func_less, ged_grid2model_lu}, {"grid2view_lu", "x y", 4, to_view_func_less, ged_grid2view_lu}, {"handle_expose", "vname count", TO_UNLIMITED, to_handle_expose, GED_FUNC_PTR_NULL}, + {"heal", (char *)0, TO_UNLIMITED, to_pass_through_func, ged_heal}, {"hide", (char *)0, TO_UNLIMITED, to_pass_through_func, ged_hide}, {"hide_view", "vname [0|1]", 3, to_hide_view, GED_FUNC_PTR_NULL}, {"how", (char *)0, TO_UNLIMITED, to_pass_through_func, ged_how}, Modified: brlcad/trunk/src/mged/setup.c =================================================================== --- brlcad/trunk/src/mged/setup.c 2016-10-12 11:46:13 UTC (rev 69034) +++ brlcad/trunk/src/mged/setup.c 2016-10-12 14:32:21 UTC (rev 69035) @@ -170,6 +170,7 @@ {"grid2model_lu", cmd_ged_plain_wrapper, ged_grid2model_lu}, {"grid2view_lu", cmd_ged_plain_wrapper, ged_grid2view_lu}, {"has_embedded_fb", cmd_has_embedded_fb, GED_FUNC_PTR_NULL}, + {"heal", cmd_ged_plain_wrapper, ged_heal}, {"hide", cmd_ged_plain_wrapper, ged_hide}, {"hist", cmd_hist, GED_FUNC_PTR_NULL}, {"history", f_history, GED_FUNC_PTR_NULL}, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, SlashDot.org! http://sdm.link/slashdot _______________________________________________ BRL-CAD Source Commits mailing list brlcad-commits@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/brlcad-commits