blender/intern/boolop/intern/BOP_Face2Face.cpp
2011-02-25 10:32:33 +00:00

1248 lines
39 KiB
C++

/*
*
* $Id$
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Marc Freixas, Ken Hughes
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file boolop/intern/BOP_Face2Face.cpp
* \ingroup boolopintern
*/
#include "BOP_Face2Face.h"
#include "BOP_BBox.h"
// TAGS for segment classification in x-segment creation
// sA -> point of sA
// sB -> point of sB
// sX -> point of sA and SB
#define sA_sB 12
#define sB_sA 21
#define sX_sA 31
#define sA_sX 13
#define sX_sB 32
#define sB_sX 23
#define sX_sX 33
#define sA_sA_sB 112
#define sB_sB_sA 221
#define sB_sA_sA 211
#define sA_sB_sB 122
#define sA_sB_sA 121
#define sB_sA_sB 212
#define sA_sX_sB 132
#define sB_sX_sA 231
#define sX_sA_sB 312
#define sX_sB_sA 321
#define sA_sB_sX 123
#define sB_sA_sX 213
#define sA_sA_sB_sB 1122
#define sB_sB_sA_sA 2211
#define sA_sB_sA_sB 1212
#define sB_sA_sB_sA 2121
#define sA_sB_sB_sA 1221
#define sB_sA_sA_sB 2112
void BOP_intersectCoplanarFaces(BOP_Mesh* mesh,
BOP_Faces* facesB,
BOP_Face* faceA,
BOP_Face* faceB,
bool invert);
void BOP_intersectCoplanarFaces(BOP_Mesh* mesh,
BOP_Faces* facesB,
BOP_Face* faceB,
BOP_Segment sA,
MT_Plane3 planeA,
bool invert);
void BOP_intersectNonCoplanarFaces(BOP_Mesh* mesh,
BOP_Faces* facesA,
BOP_Faces* facesB,
BOP_Face* faceA,
BOP_Face* faceB);
void BOP_getPoints(BOP_Mesh* mesh,
BOP_Face* faceA,
BOP_Segment& sA,
MT_Plane3 planeB,
MT_Point3* points,
unsigned int* faces,
unsigned int& size,
unsigned int faceValue);
void BOP_mergeSort(MT_Point3 *points, unsigned int *face, unsigned int &size, bool &invertA, bool &invertB);
void BOP_createXS(BOP_Mesh* mesh,
BOP_Face* faceA,
BOP_Face* faceB,
BOP_Segment sA,
BOP_Segment sB,
bool invert,
BOP_Segment* segments);
void BOP_createXS(BOP_Mesh* mesh,
BOP_Face* faceA,
BOP_Face* faceB,
MT_Plane3 planeA,
MT_Plane3 planeB,
BOP_Segment sA,
BOP_Segment sB,
bool invert,
BOP_Segment* segments);
BOP_Index BOP_getVertexIndex(BOP_Mesh* mesh,
MT_Point3 point,
unsigned int cfgA,
unsigned int cfgB,
BOP_Index vA,
BOP_Index vB,
bool invert);
BOP_Index BOP_getVertexIndex(BOP_Mesh *mesh, MT_Point3 point, unsigned int cfg, BOP_Index v);
void triangulate(BOP_Mesh *mesh, BOP_Faces *faces, BOP_Face *face, BOP_Segment s);
BOP_Face *BOP_getOppositeFace(BOP_Mesh* mesh,
BOP_Faces* faces,
BOP_Face* face,
BOP_Edge* edge);
bool BOP_overlap(MT_Vector3 normal,
MT_Point3 p1,
MT_Point3 p2,
MT_Point3 p3,
MT_Point3 q1,
MT_Point3 q2,
MT_Point3 q3);
void BOP_mergeVertexs(BOP_Mesh *mesh, unsigned int firstFace);
/**
* Computes intersections between faces of both lists.
* @param mesh mesh that contains the faces, edges and vertices
* @param facesA set of faces from object A
* @param facesB set of faces from object B
*
* Two optimizations were added here:
* 1) keep the bounding box for a face once it's created; this is
* especially important for B faces, since they were being created and
* recreated over and over
* 2) associate a "split" index in the faceB vector with each A face; when
* an A face is split, we will not need to recheck any B faces have
* already been checked against that original A face
*/
void BOP_Face2Face(BOP_Mesh *mesh, BOP_Faces *facesA, BOP_Faces *facesB)
{
for(unsigned int idxFaceA=0;idxFaceA<facesA->size();idxFaceA++) {
BOP_Face *faceA = (*facesA)[idxFaceA];
MT_Plane3 planeA = faceA->getPlane();
MT_Point3 p1 = mesh->getVertex(faceA->getVertex(0))->getPoint();
MT_Point3 p2 = mesh->getVertex(faceA->getVertex(1))->getPoint();
MT_Point3 p3 = mesh->getVertex(faceA->getVertex(2))->getPoint();
/* get (or create) bounding box for face A */
if( faceA->getBBox() == NULL )
faceA->setBBox(p1,p2,p3);
BOP_BBox *boxA = faceA->getBBox();
/* start checking B faces with the previously stored split index */
for(unsigned int idxFaceB=faceA->getSplit();
idxFaceB<facesB->size() && (faceA->getTAG() != BROKEN) && (faceA->getTAG() != PHANTOM);) {
BOP_Face *faceB = (*facesB)[idxFaceB];
faceA->setSplit(idxFaceB);
if ((faceB->getTAG() != BROKEN) && (faceB->getTAG() != PHANTOM)) {
/* get (or create) bounding box for face B */
if( faceB->getBBox() == NULL )
faceB->setBBox(mesh->getVertex(faceB->getVertex(0))->getPoint(),
mesh->getVertex(faceB->getVertex(1))->getPoint(),
mesh->getVertex(faceB->getVertex(2))->getPoint());
BOP_BBox *boxB = faceB->getBBox();
if (boxA->intersect(*boxB)) {
MT_Plane3 planeB = faceB->getPlane();
if (BOP_containsPoint(planeB,p1) &&
BOP_containsPoint(planeB,p2) &&
BOP_containsPoint(planeB,p3)) {
if (BOP_orientation(planeB,planeA)>0) {
BOP_intersectCoplanarFaces(mesh,facesB,faceA,faceB,false);
}
}
else {
BOP_intersectNonCoplanarFaces(mesh,facesA,facesB,faceA,faceB);
}
}
}
idxFaceB++;
}
}
// Clean broken faces from facesA
BOP_IT_Faces it;
it = facesA->begin();
while (it != facesA->end()) {
BOP_Face *face = *it;
if (face->getTAG() == BROKEN) it = facesA->erase(it);
else it++;
}
/*
it = facesB->begin();
while (it != facesB->end()) {
BOP_Face *face = *it;
if (face->getTAG() == BROKEN) it = facesB->erase(it);
else it++;
}
*/
}
/**
* Computes intesections of coplanars faces from object A with faces from object B.
* @param mesh mesh that contains the faces, edges and vertices
* @param facesA set of faces from object A
* @param facesB set of faces from object B
*/
void BOP_sew(BOP_Mesh *mesh, BOP_Faces *facesA, BOP_Faces *facesB)
{
for(unsigned int idxFaceB = 0; idxFaceB < facesB->size(); idxFaceB++) {
BOP_Face *faceB = (*facesB)[idxFaceB];
MT_Plane3 planeB = faceB->getPlane();
MT_Point3 p1 = mesh->getVertex(faceB->getVertex(0))->getPoint();
MT_Point3 p2 = mesh->getVertex(faceB->getVertex(1))->getPoint();
MT_Point3 p3 = mesh->getVertex(faceB->getVertex(2))->getPoint();
for(unsigned int idxFaceA = 0;
idxFaceA < facesA->size() &&
faceB->getTAG() != BROKEN &&
faceB->getTAG() != PHANTOM;
idxFaceA++) {
BOP_Face *faceA = (*facesA)[idxFaceA];
if ((faceA->getTAG() != BROKEN)&&(faceA->getTAG() != PHANTOM)) {
MT_Plane3 planeA = faceA->getPlane();
if (BOP_containsPoint(planeA,p1) &&
BOP_containsPoint(planeA,p2) &&
BOP_containsPoint(planeA,p3)) {
if (BOP_orientation(planeA,planeB) > 0) {
BOP_intersectCoplanarFaces(mesh,facesA,faceB,faceA,true);
}
}
}
}
}
}
/**
* Triangulates faceB using edges of faceA that both are complanars.
* @param mesh mesh that contains the faces, edges and vertices
* @param facesB set of faces from object B
* @param faceA face from object A
* @param faceB face from object B
* @param invert indicates if faceA has priority over faceB
*/
void BOP_intersectCoplanarFaces(BOP_Mesh* mesh,
BOP_Faces* facesB,
BOP_Face* faceA,
BOP_Face* faceB,
bool invert)
{
unsigned int oldSize = facesB->size();
unsigned int originalFaceB = faceB->getOriginalFace();
MT_Point3 p1 = mesh->getVertex(faceA->getVertex(0))->getPoint();
MT_Point3 p2 = mesh->getVertex(faceA->getVertex(1))->getPoint();
MT_Point3 p3 = mesh->getVertex(faceA->getVertex(2))->getPoint();
MT_Vector3 normal(faceA->getPlane().x(),faceA->getPlane().y(),faceA->getPlane().z());
MT_Vector3 p1p2 = p2-p1;
MT_Plane3 plane1((p1p2.cross(normal).normalized()),p1);
BOP_Segment sA;
sA.m_cfg1 = BOP_Segment::createVertexCfg(1);
sA.m_v1 = faceA->getVertex(0);
sA.m_cfg2 = BOP_Segment::createVertexCfg(2);
sA.m_v2 = faceA->getVertex(1);
BOP_intersectCoplanarFaces(mesh,facesB,faceB,sA,plane1,invert);
MT_Vector3 p2p3 = p3-p2;
MT_Plane3 plane2((p2p3.cross(normal).normalized()),p2);
sA.m_cfg1 = BOP_Segment::createVertexCfg(2);
sA.m_v1 = faceA->getVertex(1);
sA.m_cfg2 = BOP_Segment::createVertexCfg(3);
sA.m_v2 = faceA->getVertex(2);
if (faceB->getTAG() == BROKEN) {
for(unsigned int idxFace = oldSize; idxFace < facesB->size(); idxFace++) {
BOP_Face *face = (*facesB)[idxFace];
if (face->getTAG() != BROKEN && originalFaceB == face->getOriginalFace())
BOP_intersectCoplanarFaces(mesh,facesB,face,sA,plane2,invert);
}
}
else {
BOP_intersectCoplanarFaces(mesh,facesB,faceB,sA,plane2,invert);
}
MT_Vector3 p3p1 = p1-p3;
MT_Plane3 plane3((p3p1.cross(normal).safe_normalized()),p3);
sA.m_cfg1 = BOP_Segment::createVertexCfg(3);
sA.m_v1 = faceA->getVertex(2);
sA.m_cfg2 = BOP_Segment::createVertexCfg(1);
sA.m_v2 = faceA->getVertex(0);
if (faceB->getTAG() == BROKEN) {
for(unsigned int idxFace = oldSize; idxFace < facesB->size(); idxFace++) {
BOP_Face *face = (*facesB)[idxFace];
if (face->getTAG() != BROKEN && originalFaceB == face->getOriginalFace())
BOP_intersectCoplanarFaces(mesh,facesB,face,sA,plane3,invert);
}
}
else {
BOP_intersectCoplanarFaces(mesh,facesB,faceB,sA,plane3,invert);
}
}
/**
* Triangulates faceB using segment sA and planeA.
* @param mesh mesh that contains the faces, edges and vertices
* @param facesB set of faces from object B
* @param faceB face from object B
* @param sA segment to intersect with faceB
* @param planeA plane to intersect with faceB
* @param invert indicates if sA has priority over faceB
*/
void BOP_intersectCoplanarFaces(BOP_Mesh* mesh,
BOP_Faces* facesB,
BOP_Face* faceB,
BOP_Segment sA,
MT_Plane3 planeA,
bool invert)
{
BOP_Segment sB = BOP_splitFace(planeA,mesh,faceB);
if (BOP_Segment::isDefined(sB.m_cfg1)) {
BOP_Segment xSegment[2];
BOP_createXS(mesh,NULL,faceB,planeA,MT_Plane3(),sA,sB,invert,xSegment);
if (BOP_Segment::isDefined(xSegment[1].m_cfg1)) {
unsigned int sizefaces = mesh->getNumFaces();
triangulate(mesh,facesB,faceB,xSegment[1]);
BOP_mergeVertexs(mesh,sizefaces);
}
}
}
/**
* Triangulates faceB using edges of faceA that both are not complanars.
* @param mesh mesh that contains the faces, edges and vertices
* @param facesB set of faces from object B
* @param faceA face from object A
* @param faceB face from object B
*/
void BOP_intersectNonCoplanarFaces(BOP_Mesh *mesh,
BOP_Faces *facesA,
BOP_Faces *facesB,
BOP_Face *faceA,
BOP_Face *faceB)
{
// Obtain segments of faces A and B from the intersection with their planes
BOP_Segment sA = BOP_splitFace(faceB->getPlane(),mesh,faceA);
BOP_Segment sB = BOP_splitFace(faceA->getPlane(),mesh,faceB);
if (BOP_Segment::isDefined(sA.m_cfg1) && BOP_Segment::isDefined(sB.m_cfg1)) {
// There is an intesection, build the X-segment
BOP_Segment xSegment[2];
BOP_createXS(mesh,faceA,faceB,sA,sB,false,xSegment);
unsigned int sizefaces = mesh->getNumFaces();
triangulate(mesh,facesA,faceA,xSegment[0]);
BOP_mergeVertexs(mesh,sizefaces);
sizefaces = mesh->getNumFaces();
triangulate(mesh,facesB,faceB,xSegment[1]);
BOP_mergeVertexs(mesh,sizefaces);
}
}
/**
* Tests if faces since firstFace have all vertexs non-coincident of colinear, otherwise repairs the mesh.
* @param mesh mesh that contains the faces, edges and vertices
* @param firstFace first face index to be tested
*/
void BOP_mergeVertexs(BOP_Mesh *mesh, unsigned int firstFace)
{
unsigned int numFaces = mesh->getNumFaces();
for(unsigned int idxFace = firstFace; idxFace < numFaces; idxFace++) {
BOP_Face *face = mesh->getFace(idxFace);
if ((face->getTAG() != BROKEN) && (face->getTAG() != PHANTOM)) {
MT_Point3 vertex1 = mesh->getVertex(face->getVertex(0))->getPoint();
MT_Point3 vertex2 = mesh->getVertex(face->getVertex(1))->getPoint();
MT_Point3 vertex3 = mesh->getVertex(face->getVertex(2))->getPoint();
if (BOP_collinear(vertex1,vertex2,vertex3)) // collinear triangle
face->setTAG(PHANTOM);
}
}
}
/**
* Obtains the points of the segment created from the intersection between faceA and planeB.
* @param mesh mesh that contains the faces, edges and vertices
* @param faceA intersected face
* @param sA segment of the intersection between faceA and planeB
* @param planeB intersected plane
* @param points array of points where the new points are saved
* @param faces array of relative face index to the points
* @param size size of arrays points and faces
* @param faceValue relative face index of new points
*/
void BOP_getPoints(BOP_Mesh* mesh,
BOP_Face* faceA,
BOP_Segment& sA,
MT_Plane3 planeB,
MT_Point3* points,
unsigned int* faces,
unsigned int& size,
unsigned int faceValue)
{
MT_Point3 p1,p2;
if (BOP_Segment::isDefined(sA.m_cfg1)) {
if (BOP_Segment::isEdge(sA.m_cfg1)) {
// the new point becomes of split faceA edge
p1 = BOP_splitEdge(planeB,mesh,faceA,BOP_Segment::getEdge(sA.m_cfg1));
}
else if (BOP_Segment::isVertex(sA.m_cfg1)) {
// the new point becomes of vertex faceA
p1 = mesh->getVertex(BOP_Segment::getVertex(sA.m_v1))->getPoint();
}
if (BOP_Segment::isDefined(sA.m_cfg2)) {
if (BOP_Segment::isEdge(sA.m_cfg2)) {
p2 = BOP_splitEdge(planeB,mesh,faceA,BOP_Segment::getEdge(sA.m_cfg2));
}
else if (BOP_Segment::isVertex(sA.m_cfg2)) {
p2 = mesh->getVertex(BOP_Segment::getVertex(sA.m_v2))->getPoint();
}
points[size] = p1;
points[size+1] = p2;
faces[size] = faceValue;
faces[size+1] = faceValue;
size += 2;
}
else {
points[size] = p1;
faces[size] = faceValue;
size++;
}
}
}
/**
* Sorts the colinear points and relative face indices.
* @param points array of points where the new points are saved
* @param faces array of relative face index to the points
* @param size size of arrays points and faces
* @param invertA indicates if points of same relative face had been exchanged
*/
void BOP_mergeSort(MT_Point3 *points, unsigned int *face, unsigned int &size, bool &invertA, bool &invertB) {
MT_Point3 sortedPoints[4];
unsigned int sortedFaces[4], position[4];
unsigned int i;
if (size == 2) {
// Trivial case, only test the merge ...
if (BOP_fuzzyZero(points[0].distance(points[1]))) {
face[0] = 3;
size--;
}
}
else {
// size is 3 or 4
// Get segment extreme points
MT_Scalar maxDistance = -1;
for(i=0;i<size-1;i++){
for(unsigned int j=i+1;j<size;j++){
MT_Scalar distance = points[i].distance(points[j]);
if (distance > maxDistance){
maxDistance = distance;
position[0] = i;
position[size-1] = j;
}
}
}
// Get segment inner points
position[1] = position[2] = size;
for(i=0;i<size;i++){
if ((i != position[0]) && (i != position[size-1])){
if (position[1] == size) position[1] = i;
else position[2] = i;
}
}
// Get inner points
if (position[2] < size) {
MT_Scalar d1 = points[position[1]].distance(points[position[0]]);
MT_Scalar d2 = points[position[2]].distance(points[position[0]]);
if (d1 > d2) {
unsigned int aux = position[1];
position[1] = position[2];
position[2] = aux;
}
}
// Sort data
for(i=0;i<size;i++) {
sortedPoints[i] = points[position[i]];
sortedFaces[i] = face[position[i]];
}
invertA = false;
invertB = false;
if (face[1] == 1) {
// invertAø?
for(i=0;i<size;i++) {
if (position[i] == 1) {
invertA = true;
break;
}
else if (position[i] == 0) break;
}
// invertBø?
if (size == 4) {
for(i=0;i<size;i++) {
if (position[i] == 3) {
invertB = true;
break;
}
else if (position[i] == 2) break;
}
}
}
else if (face[1] == 2) {
// invertBø?
for(i=0;i<size;i++) {
if (position[i] == 2) {
invertB = true;
break;
}
else if (position[i] == 1) break;
}
}
// Merge data
MT_Scalar d1 = sortedPoints[1].distance(sortedPoints[0]);
MT_Scalar d2 = sortedPoints[1].distance(sortedPoints[2]);
if (BOP_fuzzyZero(d1) && sortedFaces[1] != sortedFaces[0]) {
if (BOP_fuzzyZero(d2) && sortedFaces[1] != sortedFaces[2]) {
if (d1 < d2) {
// merge 0 and 1
sortedFaces[0] = 3;
for(i = 1; i<size-1;i++) {
sortedPoints[i] = sortedPoints[i+1];
sortedFaces[i] = sortedFaces[i+1];
}
size--;
if (size == 3) {
// merge 1 and 2 ???
d1 = sortedPoints[1].distance(sortedPoints[2]);
if (BOP_fuzzyZero(d1) && sortedFaces[1] != sortedFaces[2]) {
// merge!
sortedFaces[1] = 3;
size--;
}
}
}
else {
// merge 1 and 2
sortedFaces[1] = 3;
for(i = 2; i<size-1;i++) {
sortedPoints[i] = sortedPoints[i+1];
sortedFaces[i] = sortedFaces[i+1];
}
size--;
}
}
else {
// merge 0 and 1
sortedFaces[0] = 3;
for(i = 1; i<size-1;i++) {
sortedPoints[i] = sortedPoints[i+1];
sortedFaces[i] = sortedFaces[i+1];
}
size--;
if (size == 3) {
// merge 1 i 2 ???
d1 = sortedPoints[1].distance(sortedPoints[2]);
if (BOP_fuzzyZero(d1) && sortedFaces[1] != sortedFaces[2]) {
// merge!
sortedFaces[1] = 3;
size--;
}
}
}
}
else {
if (BOP_fuzzyZero(d2) && sortedFaces[1] != sortedFaces[2]) {
// merge 1 and 2
sortedFaces[1] = 3;
for(i = 2; i<size-1;i++) {
sortedPoints[i] = sortedPoints[i+1];
sortedFaces[i] = sortedFaces[i+1];
}
size--;
}
else if (size == 4) {
d1 = sortedPoints[2].distance(sortedPoints[3]);
if (BOP_fuzzyZero(d1) && sortedFaces[2] != sortedFaces[3]) {
// merge 2 and 3
sortedFaces[2] = 3;
size--;
}
}
}
// Merge initial points ...
for(i=0;i<size;i++) {
points[i] = sortedPoints[i];
face[i] = sortedFaces[i];
}
}
}
/**
* Computes the x-segment of two segments (the shared interval). The segments needs to have sA.m_cfg1 > 0 && sB.m_cfg1 > 0 .
* @param mesh mesh that contains the faces, edges and vertices
* @param faceA face of object A
* @param faceB face of object B
* @param sA segment of intersection between faceA and planeB
* @param sB segment of intersection between faceB and planeA
* @param invert indicates if faceA has priority over faceB
* @param segmemts array of the output x-segments
*/
void BOP_createXS(BOP_Mesh* mesh,
BOP_Face* faceA,
BOP_Face* faceB,
BOP_Segment sA,
BOP_Segment sB,
bool invert,
BOP_Segment* segments) {
BOP_createXS(mesh, faceA, faceB, faceA->getPlane(), faceB->getPlane(),
sA, sB, invert, segments);
}
/**
* Computes the x-segment of two segments (the shared interval). The segments needs to have sA.m_cfg1 > 0 && sB.m_cfg1 > 0 .
* @param mesh mesh that contains the faces, edges and vertices
* @param faceA face of object A
* @param faceB face of object B
* @param planeA plane of faceA
* @param planeB plane of faceB
* @param sA segment of intersection between faceA and planeB
* @param sB segment of intersection between faceB and planeA
* @param invert indicates if faceA has priority over faceB
* @param segmemts array of the output x-segments
*/
void BOP_createXS(BOP_Mesh* mesh,
BOP_Face* faceA,
BOP_Face* faceB,
MT_Plane3 planeA,
MT_Plane3 planeB,
BOP_Segment sA,
BOP_Segment sB,
bool invert,
BOP_Segment* segments)
{
MT_Point3 points[4]; // points of the segments
unsigned int face[4]; // relative face indexs (1 => faceA, 2 => faceB)
unsigned int size = 0; // size of points and relative face indexs
BOP_getPoints(mesh, faceA, sA, planeB, points, face, size, 1);
BOP_getPoints(mesh, faceB, sB, planeA, points, face, size, 2);
bool invertA = false;
bool invertB = false;
BOP_mergeSort(points,face,size,invertA,invertB);
if (invertA) sA.invert();
if (invertB) sB.invert();
// Compute the configuration label
unsigned int label = 0;
for(unsigned int i =0; i < size; i++) {
label = face[i]+label*10;
}
if (size == 1) {
// Two coincident points
segments[0].m_cfg1 = sA.m_cfg1;
segments[1].m_cfg1 = sB.m_cfg1;
segments[0].m_v1 = BOP_getVertexIndex(mesh, points[0], sA.m_cfg1, sB.m_cfg1,
sA.m_v1, sB.m_v1, invert);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = segments[1].m_cfg2 = BOP_Segment::createUndefinedCfg();
}
else if (size == 2) {
switch(label) {
// Two non-coincident points
case sA_sB:
case sB_sA:
segments[0].m_cfg1 =
segments[1].m_cfg1 =
segments[0].m_cfg2 =
segments[1].m_cfg2 = BOP_Segment::createUndefinedCfg();
break;
// Two coincident points and one non-coincident of sA
case sA_sX:
segments[0].m_cfg1 = sA.m_cfg2;
segments[1].m_cfg1 = sB.m_cfg1;
segments[0].m_v1 = BOP_getVertexIndex(mesh, points[1], sA.m_cfg2, sB.m_cfg1,
sA.m_v2, sB.m_v1, invert);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = BOP_Segment::createUndefinedCfg();
segments[1].m_cfg2 = BOP_Segment::createUndefinedCfg();
break;
case sX_sA:
segments[0].m_cfg1 = sA.m_cfg1;
segments[1].m_cfg1 = sB.m_cfg1;
segments[0].m_v1 = BOP_getVertexIndex(mesh, points[0], sA.m_cfg1, sB.m_cfg1,
sA.m_v1, sB.m_v1, invert);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = BOP_Segment::createUndefinedCfg();
segments[1].m_cfg2 = BOP_Segment::createUndefinedCfg();
break;
// Two coincident points and one non-coincident of sB
case sB_sX:
segments[0].m_cfg1 = sA.m_cfg1;
segments[1].m_cfg1 = sB.m_cfg2;
segments[0].m_v1 = BOP_getVertexIndex(mesh, points[1], sA.m_cfg1, sB.m_cfg2,
sA.m_v1, sB.m_v2, invert);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = BOP_Segment::createUndefinedCfg();
segments[1].m_cfg2 = BOP_Segment::createUndefinedCfg();
break;
case sX_sB:
segments[0].m_cfg1 = sA.m_cfg1;
segments[1].m_cfg1 = sB.m_cfg1;
segments[0].m_v1 = BOP_getVertexIndex(mesh, points[0], sA.m_cfg1, sB.m_cfg1,
sA.m_v1, sB.m_v1, invert);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = BOP_Segment::createUndefinedCfg();
segments[1].m_cfg2 = BOP_Segment::createUndefinedCfg();
break;
// coincident points 2-2
case sX_sX:
segments[0].m_cfg1 = sA.m_cfg1;
segments[1].m_cfg1 = sB.m_cfg1;
segments[0].m_v1 = BOP_getVertexIndex(mesh, points[0], sA.m_cfg1, sB.m_cfg1,
sA.m_v1, sB.m_v1, invert);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = sA.m_cfg2;
segments[1].m_cfg2 = sB.m_cfg2;
segments[0].m_v2 = BOP_getVertexIndex(mesh, points[1], sA.m_cfg2, sB.m_cfg2,
sA.m_v2, sB.m_v2, invert);
segments[1].m_v2 = segments[0].m_v2;
break;
default:
break;
}
}
else if (size == 3) {
switch(label) {
case sA_sA_sB:
case sB_sA_sA:
case sA_sB_sB:
case sB_sB_sA:
segments[0].m_cfg1 =
segments[1].m_cfg1 =
segments[0].m_cfg2 =
segments[1].m_cfg2 = BOP_Segment::createUndefinedCfg();
break;
case sA_sB_sA:
segments[1].m_v1 = BOP_getVertexIndex(mesh,points[1],sB.m_cfg1,sB.m_v1);
segments[1].m_cfg1 = sB.m_cfg1;
segments[1].m_cfg2 = BOP_Segment::createUndefinedCfg();
segments[0].m_cfg1 = sA.getConfig();
segments[0].m_cfg2 = BOP_Segment::createUndefinedCfg();
segments[0].m_v1 = segments[1].m_v1;
break;
case sB_sA_sB:
segments[0].m_v1 = BOP_getVertexIndex(mesh,points[1],sA.m_cfg1,sA.m_v1);
segments[0].m_cfg1 = sA.m_cfg1;
segments[0].m_cfg2 = BOP_Segment::createUndefinedCfg();
segments[1].m_cfg1 = sB.getConfig();
segments[1].m_cfg2 = BOP_Segment::createUndefinedCfg();
segments[1].m_v1 = segments[0].m_v1;
break;
case sA_sX_sB:
segments[0].m_cfg1 = sA.m_cfg2;
segments[1].m_cfg1 = sB.m_cfg1;
segments[0].m_v1 = BOP_getVertexIndex(mesh, points[1], sA.m_cfg2, sB.m_cfg1,
sA.m_v2, sB.m_v1, invert);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = BOP_Segment::createUndefinedCfg();
segments[1].m_cfg2 = BOP_Segment::createUndefinedCfg();
break;
case sB_sX_sA:
segments[0].m_cfg1 = sA.m_cfg1;
segments[1].m_cfg1 = sB.m_cfg2;
segments[0].m_v1 = BOP_getVertexIndex(mesh, points[1], sA.m_cfg1, sB.m_cfg2,
sA.m_v1, sB.m_v2, invert);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = BOP_Segment::createUndefinedCfg();
segments[1].m_cfg2 = BOP_Segment::createUndefinedCfg();
break;
case sX_sA_sB:
segments[0].m_cfg1 = sA.m_cfg1;
segments[1].m_cfg1 = sB.m_cfg1;
segments[0].m_v1 = BOP_getVertexIndex(mesh, points[0], sA.m_cfg1, sB.m_cfg1,
sA.m_v1, sB.m_v1, invert);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = sA.m_cfg2;
segments[1].m_cfg2 = sB.getConfig();
segments[0].m_v2 = BOP_getVertexIndex(mesh, points[1], sA.m_cfg2, sA.m_v2);
segments[1].m_v2 = segments[0].m_v2;
break;
case sX_sB_sA:
segments[0].m_cfg1 = sA.m_cfg1;
segments[1].m_cfg1 = sB.m_cfg1;
segments[0].m_v1 = BOP_getVertexIndex(mesh, points[0], sA.m_cfg1, sB.m_cfg1,
sA.m_v1, sB.m_v1, invert);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = sA.getConfig();
segments[1].m_cfg2 = sB.m_cfg2;
segments[0].m_v2 = BOP_getVertexIndex(mesh,points[1],sB.m_cfg2,sB.m_v2);
segments[1].m_v2 = segments[0].m_v2;
break;
case sA_sB_sX:
segments[0].m_cfg1 = sA.getConfig();
segments[1].m_cfg1 = sB.m_cfg1;
segments[0].m_v1 = BOP_getVertexIndex(mesh,points[1],sB.m_cfg1,sB.m_v1);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = sA.m_cfg2;
segments[1].m_cfg2 = sB.m_cfg2;
segments[0].m_v2 = BOP_getVertexIndex(mesh, points[2], sA.m_cfg2, sB.m_cfg2,
sA.m_v2, sB.m_v2, invert);
segments[1].m_v2 = segments[0].m_v2;
break;
case sB_sA_sX:
segments[0].m_cfg1 = sA.m_cfg1;
segments[1].m_cfg1 = sB.getConfig();
segments[0].m_v1 = BOP_getVertexIndex(mesh,points[1],sA.m_cfg1,sA.m_v1);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = sA.m_cfg2;
segments[1].m_cfg2 = sB.m_cfg2;
segments[0].m_v2 = BOP_getVertexIndex(mesh, points[2], sA.m_cfg2, sB.m_cfg2,
sA.m_v2, sB.m_v2, invert);
segments[1].m_v2 = segments[0].m_v2;
break;
default:
break;
}
}
else {
// 4!
switch(label) {
case sA_sA_sB_sB:
case sB_sB_sA_sA:
segments[0].m_cfg1 =
segments[1].m_cfg1 =
segments[0].m_cfg2 =
segments[1].m_cfg2 = BOP_Segment::createUndefinedCfg();
break;
case sA_sB_sA_sB:
segments[0].m_cfg1 = sA.getConfig();
segments[1].m_cfg1 = sB.m_cfg1;
segments[0].m_v1 = BOP_getVertexIndex(mesh,points[1],sB.m_cfg1,sB.m_v1);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = sA.m_cfg2;
segments[1].m_cfg2 = sB.getConfig();
segments[0].m_v2 = BOP_getVertexIndex(mesh,points[2],sA.m_cfg2,sA.m_v2);
segments[1].m_v2 = segments[0].m_v2;
break;
case sB_sA_sB_sA:
segments[0].m_cfg1 = sA.m_cfg1;
segments[1].m_cfg1 = sB.getConfig();
segments[0].m_v1 = BOP_getVertexIndex(mesh,points[1],sA.m_cfg1,sA.m_v1);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = sA.getConfig();
segments[1].m_cfg2 = sB.m_cfg2;
segments[0].m_v2 = BOP_getVertexIndex(mesh,points[2],sB.m_cfg2,sB.m_v2);
segments[1].m_v2 = segments[0].m_v2;
break;
case sA_sB_sB_sA:
segments[0].m_cfg1 = sA.getConfig();
segments[1].m_cfg1 = sB.m_cfg1;
segments[0].m_v1 = BOP_getVertexIndex(mesh,points[1],sB.m_cfg1,sB.m_v1);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = segments[0].m_cfg1;
segments[1].m_cfg2 = sB.m_cfg2;
segments[0].m_v2 = BOP_getVertexIndex(mesh,points[2],sB.m_cfg2,sB.m_v2);
segments[1].m_v2 = segments[0].m_v2;
break;
case sB_sA_sA_sB:
segments[0].m_cfg1 = sA.m_cfg1;
segments[1].m_cfg1 = sB.getConfig();
segments[0].m_v1 = BOP_getVertexIndex(mesh,points[1],sA.m_cfg1,sA.m_v1);
segments[1].m_v1 = segments[0].m_v1;
segments[0].m_cfg2 = sA.m_cfg2;
segments[1].m_cfg2 = segments[1].m_cfg1;
segments[0].m_v2 = BOP_getVertexIndex(mesh,points[2],sA.m_cfg2,sA.m_v2);
segments[1].m_v2 = segments[0].m_v2;
break;
default:
break;
}
}
segments[0].sort();
segments[1].sort();
}
/**
* Computes the vertex index of a point.
* @param mesh mesh that contains the faces, edges and vertices
* @param point input point
* @param cfgA configuration of point on faceA
* @param cfgB configuration of point on faceB
* @param vA vertex index of point on faceA
* @param vB vertex index of point on faceB
* @param invert indicates if vA has priority over vB
* @return final vertex index in the mesh
*/
BOP_Index BOP_getVertexIndex(BOP_Mesh* mesh,
MT_Point3 point,
unsigned int cfgA,
unsigned int cfgB,
BOP_Index vA,
BOP_Index vB,
bool invert)
{
if (BOP_Segment::isVertex(cfgA)) { // exists vertex index on A
if (BOP_Segment::isVertex(cfgB)) { // exists vertex index on B
// unify vertex indexs
if (invert)
return mesh->replaceVertexIndex(vA,vB);
else
return mesh->replaceVertexIndex(vB,vA);
}
else
return vA;
}
else {// does not exist vertex index on A
if (BOP_Segment::isVertex(cfgB)) // exists vertex index on B
return vB;
else {// does not exist vertex index on B
return mesh->addVertex(point);
}
}
}
/**
* Computes the vertex index of a point.
* @param mesh mesh that contains the faces, edges and vertices
* @param cfg configuration of point
* @param v vertex index of point
* @return final vertex index in the mesh
*/
BOP_Index BOP_getVertexIndex(BOP_Mesh *mesh, MT_Point3 point, unsigned int cfg, BOP_Index v)
{
if (BOP_Segment::isVertex(cfg)) // vertex existent
return v;
else {
return mesh->addVertex(point);
}
}
/******************************************************************************/
/*** TRIANGULATE ***/
/******************************************************************************/
/**
* Triangulates the input face according to the specified segment.
* @param mesh mesh that contains the faces, edges and vertices
* @param faces set of faces that contains the original face and the new triangulated faces
* @param face face to be triangulated
* @param s segment used to triangulate face
*/
void triangulate(BOP_Mesh *mesh, BOP_Faces *faces, BOP_Face *face, BOP_Segment s)
{
if (BOP_Segment::isUndefined(s.m_cfg1)) {
// Nothing to do
}
else if (BOP_Segment::isVertex(s.m_cfg1)) {
// VERTEX(v1) + VERTEX(v2) => nothing to do
}
else if (BOP_Segment::isEdge(s.m_cfg1)) {
if (BOP_Segment::isVertex(s.m_cfg2) || BOP_Segment::isUndefined(s.m_cfg2)) {
// EDGE(v1) + VERTEX(v2)
BOP_Edge *edge = mesh->getEdge(face,BOP_Segment::getEdge(s.m_cfg1));
BOP_triangulateA(mesh,faces,face,s.m_v1,BOP_Segment::getEdge(s.m_cfg1));
BOP_Face *opposite = BOP_getOppositeFace(mesh,faces,face,edge);
if (opposite != NULL) {
unsigned int e;
opposite->getEdgeIndex(edge->getVertex1(), edge->getVertex2(),e);
BOP_triangulateA(mesh, faces, opposite, s.m_v1, e);
}
}
else {
// EDGE(v1) + EDGE(v2)
if (BOP_Segment::getEdge(s.m_cfg1) == BOP_Segment::getEdge(s.m_cfg2)) {
// EDGE(v1) == EDGE(v2)
BOP_Edge *edge = mesh->getEdge(face,BOP_Segment::getEdge(s.m_cfg1));
BOP_triangulateD(mesh, faces, face, s.m_v1, s.m_v2,
BOP_Segment::getEdge(s.m_cfg1));
BOP_Face *opposite = BOP_getOppositeFace(mesh,faces,face,edge);
if (opposite != NULL) {
unsigned int e;
opposite->getEdgeIndex(edge->getVertex1(), edge->getVertex2(),e);
BOP_triangulateD(mesh, faces, opposite, s.m_v1, s.m_v2, e);
}
}
else { // EDGE(v1) != EDGE(v2)
BOP_Edge *edge1 = mesh->getEdge(face,BOP_Segment::getEdge(s.m_cfg1));
BOP_Edge *edge2 = mesh->getEdge(face,BOP_Segment::getEdge(s.m_cfg2));
BOP_triangulateE(mesh, faces, face, s.m_v1, s.m_v2,
BOP_Segment::getEdge(s.m_cfg1),
BOP_Segment::getEdge(s.m_cfg2));
BOP_Face *opposite = BOP_getOppositeFace(mesh,faces,face,edge1);
if (opposite != NULL) {
unsigned int e;
opposite->getEdgeIndex(edge1->getVertex1(), edge1->getVertex2(),e);
BOP_triangulateA(mesh, faces, opposite, s.m_v1, e);
}
opposite = BOP_getOppositeFace(mesh,faces,face,edge2);
if (opposite != NULL) {
unsigned int e;
opposite->getEdgeIndex(edge2->getVertex1(), edge2->getVertex2(),e);
BOP_triangulateA(mesh, faces, opposite, s.m_v2, e);
}
}
}
}
else if (BOP_Segment::isIn(s.m_cfg1)) {
if (BOP_Segment::isVertex(s.m_cfg2) || BOP_Segment::isUndefined(s.m_cfg2)) {
// IN(v1) + VERTEX(v2)
BOP_triangulateB(mesh,faces,face,s.m_v1);
}
else if (BOP_Segment::isEdge(s.m_cfg2)) {
// IN(v1) + EDGE(v2)
BOP_Edge *edge = mesh->getEdge(face,BOP_Segment::getEdge(s.m_cfg2));
BOP_triangulateF(mesh,faces,face,s.m_v1,s.m_v2,BOP_Segment::getEdge(s.m_cfg2));
BOP_Face *opposite = BOP_getOppositeFace(mesh,faces,face,edge);
if (opposite != NULL) {
unsigned int e;
opposite->getEdgeIndex(edge->getVertex1(), edge->getVertex2(),e);
BOP_triangulateA(mesh, faces, opposite, s.m_v2, e);
}
}
else // IN(v1) + IN(v2)
BOP_triangulateC(mesh,faces,face,s.m_v1,s.m_v2);
}
}
/**
* Returns if a face is in the set of faces.
* @param faces set of faces
* @param face face to be searched
* @return if the face is inside faces
*/
bool BOP_containsFace(BOP_Faces *faces, BOP_Face *face)
{
const BOP_IT_Faces facesEnd = faces->end();
for(BOP_IT_Faces it=faces->begin();it!=facesEnd;it++)
{
if (*it == face)
return true;
}
return false;
}
/**
* Returns the first face of faces that shares the input edge of face.
* @param mesh mesh that contains the faces, edges and vertices
* @param faces set of faces
* @param face input face
* @param edge face's edge
* @return first face that shares the edge of input face
*/
BOP_Face *BOP_getOppositeFace(BOP_Mesh* mesh,
BOP_Faces* faces,
BOP_Face* face,
BOP_Edge* edge)
{
if (edge == NULL)
return NULL;
BOP_Indexs auxfaces = edge->getFaces();
const BOP_IT_Indexs auxfacesEnd = auxfaces.end();
for(BOP_IT_Indexs it = auxfaces.begin(); it != auxfacesEnd; it++) {
BOP_Face *auxface = mesh->getFace(*it);
if ((auxface != face) && (auxface->getTAG()!=BROKEN) &&
BOP_containsFace(faces,auxface)) {
return auxface;
}
}
return NULL;
}
/******************************************************************************/
/*** OVERLAPPING ***/
/******************************************************************************/
/**
* Removes faces from facesB that are overlapped with anyone from facesA.
* @param mesh mesh that contains the faces, edges and vertices
* @param facesA set of faces from object A
* @param facesB set of faces from object B
*/
void BOP_removeOverlappedFaces(BOP_Mesh *mesh, BOP_Faces *facesA, BOP_Faces *facesB)
{
for(unsigned int i=0;i<facesA->size();i++) {
BOP_Face *faceI = (*facesA)[i];
if (faceI->getTAG()==BROKEN) continue;
bool overlapped = false;
MT_Point3 p1 = mesh->getVertex(faceI->getVertex(0))->getPoint();
MT_Point3 p2 = mesh->getVertex(faceI->getVertex(1))->getPoint();
MT_Point3 p3 = mesh->getVertex(faceI->getVertex(2))->getPoint();
for(unsigned int j=0;j<facesB->size();) {
BOP_Face *faceJ = (*facesB)[j];
if (faceJ->getTAG()!=BROKEN) {
MT_Plane3 planeJ = faceJ->getPlane();
if (BOP_containsPoint(planeJ,p1) && BOP_containsPoint(planeJ,p2)
&& BOP_containsPoint(planeJ,p3)) {
MT_Point3 q1 = mesh->getVertex(faceJ->getVertex(0))->getPoint();
MT_Point3 q2 = mesh->getVertex(faceJ->getVertex(1))->getPoint();
MT_Point3 q3 = mesh->getVertex(faceJ->getVertex(2))->getPoint();
if (BOP_overlap(MT_Vector3(planeJ.x(),planeJ.y(),planeJ.z()),
p1,p2,p3,q1,q2,q3)) {
facesB->erase(facesB->begin()+j,facesB->begin()+(j+1));
faceJ->setTAG(BROKEN);
overlapped = true;
}
else j++;
}
else j++;
}else j++;
}
if (overlapped) faceI->setTAG(OVERLAPPED);
}
}
/**
* Computes if triangle p1,p2,p3 is overlapped with triangle q1,q2,q3.
* @param normal normal of the triangle p1,p2,p3
* @param p1 point of first triangle
* @param p2 point of first triangle
* @param p3 point of first triangle
* @param q1 point of second triangle
* @param q2 point of second triangle
* @param q3 point of second triangle
* @return if there is overlapping between both triangles
*/
bool BOP_overlap(MT_Vector3 normal, MT_Point3 p1, MT_Point3 p2, MT_Point3 p3,
MT_Point3 q1, MT_Point3 q2, MT_Point3 q3)
{
MT_Vector3 p1p2 = p2-p1;
MT_Plane3 plane1(p1p2.cross(normal),p1);
MT_Vector3 p2p3 = p3-p2;
MT_Plane3 plane2(p2p3.cross(normal),p2);
MT_Vector3 p3p1 = p1-p3;
MT_Plane3 plane3(p3p1.cross(normal),p3);
BOP_TAG tag1 = BOP_createTAG(BOP_classify(q1,plane1));
BOP_TAG tag2 = BOP_createTAG(BOP_classify(q1,plane2));
BOP_TAG tag3 = BOP_createTAG(BOP_classify(q1,plane3));
BOP_TAG tagQ1 = BOP_createTAG(tag1,tag2,tag3);
if (tagQ1 == IN_IN_IN) return true;
tag1 = BOP_createTAG(BOP_classify(q2,plane1));
tag2 = BOP_createTAG(BOP_classify(q2,plane2));
tag3 = BOP_createTAG(BOP_classify(q2,plane3));
BOP_TAG tagQ2 = BOP_createTAG(tag1,tag2,tag3);
if (tagQ2 == IN_IN_IN) return true;
tag1 = BOP_createTAG(BOP_classify(q3,plane1));
tag2 = BOP_createTAG(BOP_classify(q3,plane2));
tag3 = BOP_createTAG(BOP_classify(q3,plane3));
BOP_TAG tagQ3 = BOP_createTAG(tag1,tag2,tag3);
if (tagQ3 == IN_IN_IN) return true;
if ((tagQ1 & OUT_OUT_OUT) == 0 && (tagQ2 & OUT_OUT_OUT) == 0 &&
(tagQ3 & OUT_OUT_OUT) == 0) return true;
else return false;
}