Files
secondo/Algebras/Spatial3D/MultiObjectTriangleContainer.cpp
2026-01-23 17:03:45 +08:00

2611 lines
86 KiB
C++

/*
----
This file is part of SECONDO.
Copyright (C) 2004, University in Hagen, Department of Computer Science,
Database Systems for New Applications.
SECONDO 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.
SECONDO 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 SECONDO; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
----
01590 Fachpraktikum "Erweiterbare Datenbanksysteme"
WS 2014 / 2015
<our names here>
//paragraph [1] Title: [{\Large \bf \begin{center}] [\end{center}}]
//paragraph [10] Footnote: [{\footnote{] [}}]
//[TOC] [\tableofcontents]
[1] Implementation of a Spatial3D algebra
[TOC]
1 Includes and Defines
*/
#include "MultiObjectTriangleContainer.h"
#include "Spatial3D.h"
#include "AuxiliaryTypes.h"
#include "geometric_algorithm.h"
#include "geometric_algorithm_intersection_triangles.h"
#include "geometric_algorithm_intersection_line_plane.h"
#include<memory>
#include<limits>
using namespace spatial3d_geometric;
using namespace std;
const bool debug_volume = false;
const bool debug_splitting = false;
ostream& operator<< (ostream& os, MultiObjectTriangleContainer::Edge3d edge) {
return os << "(" << edge.startPoint << "->" << edge.endPoint << ")";
}
void MultiObjectTriangleContainer::test()
{
printStats();
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it(triangles_tree.entries());
size_t const * triangleIndex;
while( (triangleIndex = it->next()) != 0)
{
const TriangleData& data = triangles[*triangleIndex];
{
cerr << data.object_membership << " " << getTriangle(data) << endl;
}
}
}
void MultiObjectTriangleContainer::printStats() const
{
cerr << "OOOOOOOOOOXXXXXXX" << endl;
cerr << "Size of points vec: " << points.size() << endl;
cerr << "Size of triangle vec: " << triangles.size() << endl;
cerr << "Size of unused tri: " << unused_triangle_indices.size() << endl;
cerr << "Size of object 0: " << trianglesObject0.size() << endl;
cerr << "Size of points tree: " << points_tree.noObjects() << endl;
cerr << "Size of triangle tree: " << triangles_tree.noObjects() << endl;
}
//////////////////////////////////////////////////////////////////////////
// helper functions //////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
MultiObjectTriangleContainer::object_t getObjectBitmask(int object)
{
if (object < 0)
{
object = MultiObjectTriangleContainer::noExternalObjects - object;
}
return 1 << object;
}
TrianglePathDirection
operator!(TrianglePathDirection dir)
{
if (dir == LEAVE)
return ENTER;
else
return LEAVE;
}
//////////////////////////////////////////////////////////////////////////
// public interface //////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
MultiObjectTriangleContainer::MultiObjectTriangleContainer()
: points(), points_tree(4, 8), triangles(), triangles_tree(4,8),
unused_triangle_indices(), trianglesObject0()
{ }
int MultiObjectTriangleContainer::noTriangles() const
{
return triangles_tree.noObjects();
}
// Return true on success, false on error
bool MultiObjectTriangleContainer::addTriangle(const Triangle& triangle,
int object,
bool corrections)
{
if (debug_splitting) cerr << "addTriangle: " << triangle << endl;
assert(object > 0);
assert(object <= noExternalObjects);
size_t points[3];
points[0] = addPoint(triangle.getA());
points[1] = addPoint(triangle.getB());
points[2] = addPoint(triangle.getC());
try
{
bool result = addTriangle(points, object, corrections);
return result;
}
catch (NumericFailure f)
{
removeObject(0);
cerr << "Numeric problem, could not add triangle." << endl;
return false;
}
}
void MultiObjectTriangleContainer::removeObject(int object)
{
object_t object_bits = getObjectBitmask(object);
vector<size_t> trianglesToBeDeleted;
if (object == 0)
{
trianglesToBeDeleted = trianglesObject0;
}
else
{
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it(triangles_tree.entries());
size_t const * triangleIndex;
while( (triangleIndex = it->next()) != 0)
{
TriangleData& data = triangles[*triangleIndex];
data.object_membership &= ~object_bits;
if (data.object_membership == 0)
{
trianglesToBeDeleted.push_back(*triangleIndex);
}
}
}
for (vector<size_t>::iterator it = trianglesToBeDeleted.begin();
it != trianglesToBeDeleted.end(); ++it)
{
removeTriangle(*it);
}
}
void MultiObjectTriangleContainer::exportObject(int object,
vector<Triangle>& out) const
{
assert(object > 0);
assert(object <= noExternalObjects);
object_t object_bits = getObjectBitmask(object);
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it(triangles_tree.entries());
size_t const * triangleIndex;
while( (triangleIndex = it->next()) != 0)
{
const TriangleData& data = triangles[*triangleIndex];
if ((data.object_membership & object_bits) != 0)
{
// Triangle is member of the object ...
out.push_back(getTriangle(data, object));
}
}
}
// if corrections is enabled, will return true if we could repair the object
// if corrections is disabled, will return true
// iff provided object is correct without repair
bool MultiObjectTriangleContainer::checkVolume(int object, bool corrections)
{
if (debug_volume) cerr << "Entering checkVolume(object=" << object
<< ", corrections=" << corrections << ")\n";
std::vector<std::vector<size_t> > holes(0);
bool found_holes = MultiObjectTriangleContainer::getHoles (object, holes);
if (debug_volume) cerr << (found_holes?"found holes\n":"no holes\n");
bool filled_holes = MultiObjectTriangleContainer::fillHoles (object, holes);
if (debug_volume) cerr << (filled_holes ? "filled_holes OK\n"
: "filled_holes NOK... Error\n");
bool corrected_volume
= MultiObjectTriangleContainer::orientateAndCleanup (object);
if (debug_volume) cerr << (corrected_volume ? "corrected_volume\n"
: "no corrected_volume\n");
bool old_check_vol= checkVolume_closed(object, corrections);
if (debug_volume) cerr << (old_check_vol ? "old_check_vol OK\n"
: "NOK: old_check_vol\n");
bool we_repaired_something = found_holes || corrected_volume;
// corrections always "works", so just check if we had to repair anything
bool result = (corrections || !we_repaired_something) ;
result = result && filled_holes; // just learned: fill holes might fail...
if (debug_volume) cerr << "Leaving checkVolume(): result=" << result
<< " we_repaired_something=" << we_repaired_something
<< " corrections=" << corrections << endl;
return result;
}
bool MultiObjectTriangleContainer::orientateAndCleanup (int object) {
// wir gehen mal davon aus, dass die meisten Dreiecke zum objekt gehören...
// weil wir ja nur beim Import aufgerufen werden....
// hilfstruktur für status der Dreiecke initialisieren
// nicht alle dreiecke im triangles vector sind notwendig teil des object
// also erst mal alle auf OTHER und dann per tree-iterator die richtigen
// raussuchen
if (debug_volume) cerr << "entering orientateAndCleanup\n";
std::vector<TriangleStatus> tri_state (triangles.size(), OTHER);
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it(triangles_tree.entries());
size_t const * triangleIndex;
while( (triangleIndex = it->next()) != 0)
{
if (triangles[*triangleIndex].isMemberOfObject(object))
{
if (debug_volume) cerr << "U: " << *triangleIndex << endl;
tri_state[*triangleIndex] = UNKNOWN; // part of our object
} else {
if (debug_volume) cerr << "O: " << *triangleIndex << endl;
}
}
// now delete recursively all triangles with a lonely edge
for(size_t index = 0; index < tri_state.size(); ++index ) {
if (hasLonelyEdge(index, tri_state)) {
deletePatch(index, tri_state);
}
}
// get next correctly orientated "seed" triangle
size_t first_unknown = 0;
while (first_unknown < tri_state.size()) {
if (tri_state[first_unknown] == UNKNOWN) {
bool isOuter = false;
size_t seed_triangle = getOrientatedOuterOrInnerTriangle(
first_unknown, tri_state, object, isOuter);
if (debug_volume) cerr << "isOuter"<< isOuter << endl;
if (isOuter) {
createVolume(seed_triangle, tri_state, object);
} else {
// we might assume that the enclosing component has already been built
// so deleting everything connected to this triangle
// (except finally accepted ones) might also be OK.
// But to be sure, just delete the patch.
deletePatch(seed_triangle, tri_state);
}
} else {
++first_unknown; // we might have used a different seed before
}
}
// solange noch unbearbeitete dreiecke da
// äußeres dreieck ermitteln
// körperschluss probieren
// falls OK: Dreiecke als gut markieren & Nachbarn löschen
// falls NOK: startDreieck löschen
// Beim Donut: unterschiedliches Ergebnis abh. von start dreieck
/*
IntersectionPointResult intersection(const SimplePoint3d& p0,
const Vector3d& segmentVector,
const Triangle& triangle)
*/
//write out changes to container
bool we_changed_the_object = false;
for(size_t index = 0; index < tri_state.size(); ++index ) {
if (debug_volume) cerr << "tri_state[" << index << "]=";
switch (tri_state[index]) {
case DELETE:
if (debug_volume) cerr << "DELETE\n";
we_changed_the_object = true;
removeTriangleFromObject(object, index);
break;
case OK:
if (debug_volume) cerr << "OK\n";
if (triangles[index].getDirectionOfObject(object)) {
we_changed_the_object = true;
triangles[index].setDirectionOfObject(object, false);
}
break;
case FLIP:
if (debug_volume) cerr << "FLIP\n";
if (!triangles[index].getDirectionOfObject(object)) {
we_changed_the_object = true;
triangles[index].setDirectionOfObject(object, true);
}
break;
case UNKNOWN:
if (debug_volume) cerr << "UNKNOWN\n";
break;
case OTHER:
if (debug_volume) cerr << "OTHER\n";
break;
default:
if (debug_volume) cerr << "DEFAULT("<< tri_state[index] <<")\n";
}
}
if (debug_volume) cerr << "leaving orientateAndCleanup\n";
return we_changed_the_object;
}
/*
1.1 ~TriangleGetZInOrder~
Gets the Z values of a triangle in descending order.
*/
void MultiObjectTriangleContainer::TriangleGetZInOrder(
const Triangle& triangle,
double& z1, double& z2, double& z3)
{
double zTemp1 = triangle.getA().getZ();
double zTemp2 = triangle.getB().getZ();
double zTemp3 = triangle.getC().getZ();
z1 = max(zTemp1,max(zTemp2,zTemp3));
if(z1 == zTemp1){
z2 = max(zTemp2,zTemp3);
z3 = min(zTemp2,zTemp3);
}
if(z1 == zTemp2){
z2 = max(zTemp1,zTemp3);
z3 = min(zTemp1,zTemp3);
}
if(z1 == zTemp3){
z2 = max(zTemp1,zTemp2);
z3 = min(zTemp1,zTemp2);
}
}
/*
1.1 ~GetIndexOfHighestNotUsedTriangle~
Gets the triangle with the highest Z values, that was not considert before.
*/
size_t MultiObjectTriangleContainer::GetIndexOfHighestNotUsedTriangle(
std::vector<TriangleStatus>& tri_state){
double maxZ1;
double maxZ2;
double maxZ3;
double z1;
double z2;
double z3;
size_t maxIndex = 0;
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it(triangles_tree.entries());
size_t const * triangleIndex;
while( (triangleIndex = it->next()) != 0)
{
if((tri_state[*triangleIndex] != DELETE)
&& (tri_state[*triangleIndex] != OTHER)
&&(tri_state[*triangleIndex] != OK)
&&(tri_state[*triangleIndex] != FLIP)
&&(tri_state[*triangleIndex] != TMP_OK)
&&(tri_state[*triangleIndex] != TMP_FLIP)
&&(getTriangle(triangles[*triangleIndex]).
getNormalVector().getZ() != 0.0))
{
TriangleGetZInOrder(getTriangle(triangles[*triangleIndex]),z1,z2,z3);
if( maxIndex == 0 || z1 > maxZ1){
maxIndex = *triangleIndex;
maxZ1 = z1;
maxZ2 = z2;
maxZ3 = z3;
}
if(maxZ1 == z1 && maxZ2 < z2){
maxIndex = *triangleIndex;
maxZ2 = z2;
maxZ3 = z3;
}
if(maxZ1 == z1 && maxZ2 == z2 && maxZ3 < z3){
maxIndex = *triangleIndex;
maxZ3 = z3;
}
}
}
return maxIndex;
}
/*
1.1 ~IsOutside~
Checks whether a triangle is not inside a volume.
*/
bool MultiObjectTriangleContainer::IsOutside(Triangle& triangle,
std::vector<TriangleStatus>& tri_state){
size_t triangleIndex;
bool found = false;
int runNr = 1;
while(!found && runNr < 100)
{
runNr++;
int intersectionCount = 0;
SimplePoint3d p( (1.0 - 1.0/runNr) * ((1.0/runNr) * triangle.getA().getX()
+ (runNr - 1.0/runNr) * triangle.getB().getX())
+ (1.0/runNr) * triangle.getC().getX(),(1.0 - 1.0/runNr)
* ((1.0/runNr) * triangle.getA().getY()
+ (runNr - 1.0/runNr) * triangle.getB().getY())
+ (1.0/runNr) * triangle.getC().getY(),(1.0 - 1.0/runNr)
* ((1.0/runNr) * triangle.getA().getZ()
+ (runNr - 1.0/runNr) * triangle.getB().getZ())
+ (1.0/runNr) * triangle.getC().getZ());
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it(triangles_tree.entries());
bool nextNr = false;
while( (triangleIndex = *(it->next())) != 0 && !nextNr)
{
if((tri_state[triangleIndex] != DELETE)
&& (tri_state[triangleIndex] != OTHER)
&&(tri_state[triangleIndex] != TMP_OK)
&&(tri_state[triangleIndex] != TMP_FLIP))
{
Vector3d v(0.0,0.0,1.0);
const Triangle& t = getTriangle(triangles[triangleIndex]);
IntersectionPointResult intersectionType = intersection(p, v, t);
if (intersectionType.intersectionParameter > 0.00000001 &&
intersectionType.resultType == IntersectionPointResult::INNER)
{
intersectionCount++;
}else if(intersectionType.rayIntersects() &&
intersectionType.resultType == IntersectionPointResult::EDGE)
{
nextNr = true;
}
}
}
if(!nextNr){
if(intersectionCount % 2 == 0){
return true;
}
return false;
}
}
cerr << "this should never happen !!!" << endl;
return false;
}
/*
1.1 ~getOrientatedOuterOrInnerTriangle~
Gets a triangle that was not cosidert before,
and checks whether the triangle is not inside a volume
and has the right orientation.
*/
size_t MultiObjectTriangleContainer::getOrientatedOuterOrInnerTriangle(
size_t first_unknown,
std::vector<TriangleStatus>& tri_state,
int object,
bool& isOuter){
int first = GetIndexOfHighestNotUsedTriangle(tri_state);
Triangle triangle = getTriangle(triangles[first]);
if(triangle.getNormalVector().getZ() > 0.0){
tri_state[first] = TMP_OK;
}else{
tri_state[first] = TMP_FLIP;
}
isOuter = IsOutside(triangle,tri_state);
return first;
}
// triangle[index] is considered to have the correct orientation
// TODO: clarify if we are really completely outside
// ... we might just be on the surface and handle innner objects later on
// and to be outside of everything else.
void MultiObjectTriangleContainer::createVolume(
size_t seed_triangle, std::vector<TriangleStatus>& tri_state, int object) {
if (debug_volume) cerr << "cV: seed = " << seed_triangle << endl;
bool success = createVolumeRecursively(seed_triangle, tri_state, object);
if (success) {
for(size_t index = 0; index < tri_state.size(); ++index ) {
if (tri_state[index] == TMP_VISITED) {
// we've seen those triangles, but they are not part of the surface
// so they must be inside... Exterminate...
deletePatchRecursively(index, tri_state);
}
}
} else {
// if the surface does not form a valid object
// delete all connected triangles
// only deleting a patch may lead to different objects depending
// on the order of insertion:
// "donut with round piece of paper"
deleteAllConnectedTriangles(seed_triangle, tri_state);
}
makePermanent(tri_state);
}
// seed_triangle is correctly orientated.
// for every edge get the volume maximizing triangle
// then check if it can be used and recursively continue
// otherwise break and get back...
bool MultiObjectTriangleContainer::createVolumeRecursively(
size_t seed_triangle,
std::vector<TriangleStatus>& tri_state,
int object){
if (debug_volume) cerr << "cVR: seed = " << seed_triangle << endl;
TriangleData data = triangles[seed_triangle];
if (tri_state[seed_triangle] == TMP_FLIP) {
data = TriangleData(data.points[2], data.points[1], data.points[0]);
}
for (int c = 0; c < 3; ++c)
{
SortedEdge edge(data.points[c], data.points[(c + 1) % 3],
data.points[(c + 2) % 3], points);
vector<size_t> neighbour_triangles = getTrianglesForEdge (
data.points[c],
data.points[(c+1) % 3],
tri_state);
if (debug_volume)
{
cerr << "p1=" << data.points[c] << " p2=" << data.points[(c+1) % 3]
<< " tFE.size=" << neighbour_triangles.size() << endl;
}
vector<SortedTriangle> sorted_triangles;
for (vector<size_t>::iterator neighbourIndex
= neighbour_triangles.begin();
neighbourIndex != neighbour_triangles.end(); ++neighbourIndex)
{
const TriangleData neighbour = triangles[*neighbourIndex];
sorted_triangles.push_back(SortedTriangle(edge, *neighbourIndex,
neighbour, points));
switch (tri_state[*neighbourIndex]) {
case UNKNOWN:
tri_state[*neighbourIndex] = TMP_VISITED;
//fallthrough
default:
;
}
if (debug_volume)
{
cerr << "NIdx=" << *neighbourIndex
<< " ST.triangle=" << sorted_triangles.back().triangle
<< " tri_state=" << tri_state[*neighbourIndex]
<< endl;
}
}
std::sort(sorted_triangles.begin(), sorted_triangles.end(),
triangleSort);
//now we have sorted triangles...
//lets get the next one
vector<SortedTriangle>::iterator next_triangle;
for(vector<SortedTriangle>::iterator sortedNeighbour
= sorted_triangles.begin();
sortedNeighbour != sorted_triangles.end(); ++sortedNeighbour)
{
if (sortedNeighbour->triangle == seed_triangle) {
if (debug_volume) cerr << "seed->";
next_triangle = sortedNeighbour;
++next_triangle;
if (next_triangle == sorted_triangles.end()) {
next_triangle = sorted_triangles.begin();
}
}
if (debug_volume) cerr << "SN.triangle=" << sortedNeighbour->triangle
<< " phi=" << sortedNeighbour->phi <<endl;
}
size_t next_tri_idx = next_triangle->triangle;
if (debug_volume) cerr << "NT=" << next_tri_idx << endl;
//correctly orientated seed (container wise): TriangleData data
//correctly orienatated edge in direction of data: edge
//calculate target orientation for next_tri_idx
// smth like
bool next_tri_in_container_has_correct_orientation
= MultiObjectTriangleContainer::getTriangleEdgeDirection(
triangles[next_tri_idx],
edge.edgeP2, edge.edgeP1); // reverse points to have compatible edge
TriangleStatus next_tri_status
= next_tri_in_container_has_correct_orientation?TMP_OK:TMP_FLIP;
if (debug_volume) cerr << "tri_state[" << next_tri_idx << "]="
<< tri_state[next_tri_idx] << " ";
switch (tri_state[next_tri_idx]) {
case UNKNOWN:
case TMP_VISITED:
if (debug_volume) cerr << "UNKNOWN, TMP_VISITED\n";
// orientate and recursion
tri_state[next_tri_idx] = next_tri_status;
{
if (debug_volume)
{
cerr << "recursion for next_tri_idx=" << next_tri_idx << endl;
}
bool our_success
= createVolumeRecursively(next_tri_idx, tri_state, object);
if (debug_volume)
{
cerr << "recursion for next_tri_idx=" << next_tri_idx
<< " result=" << our_success << endl;
}
if (!our_success) {
return false;
}
}
// bail out if that didn't work
break;
case TMP_OK:
case TMP_FLIP:
if (debug_volume) cerr << "TMP_OK, TMP_FLIP\n";
if (tri_state[next_tri_idx] != next_tri_status) {
if (debug_volume) cerr << "incompatible edge found!!!!\n";
return false;
}
// check and bail out if incopatible
break;
default:
if (debug_volume) cerr << "DEFAULT\n";
// shouldn't get here TODO: check if that is really true...
}
}
return true;
}
void MultiObjectTriangleContainer::deleteAllConnectedTriangles(
size_t index, std::vector<TriangleStatus>& tri_state) {
if (debug_volume)
{
cerr << "dACT idx=" << index << " state=" << tri_state[index] << endl;
}
if (tri_state[index] == DELETE) { // shouldn't get here...
if (debug_volume) cerr << "already deleted: " << index << endl;
return;
}
if (tri_state[index] == OTHER) {
if (debug_volume) cerr << "part of other object: " << index << endl;
return;
}
if (tri_state[index] == TMP_DELETE) { // needed to end recursion!
// getTrianglesForEdge ignores DELETE triangles,
//so we might run into problems there...
if (debug_volume) cerr << "already tmp_deleted: " << index << endl;
return;
}
// We might want to exclude the permanent states (OK, FLIP), too...
tri_state[index] = TMP_DELETE;
TriangleData& data = triangles[index];
vector<size_t> neighbors;
for (int c = 0; c < 3; ++c)
{
vector<size_t> trisForEdge = getTrianglesForEdge (
data.points[c],
data.points[(c+1) % 3],
tri_state);
if (debug_volume)
{
cerr << "p1=" << data.points[c] << " p2=" << data.points[(c+1) % 3]
<< " tFE.size=" << trisForEdge.size() << endl;
}
for (size_t neigh_index=0;
neigh_index < trisForEdge.size();
++neigh_index) {
deleteAllConnectedTriangles(trisForEdge[neigh_index], tri_state);
}
}
}
void MultiObjectTriangleContainer::deletePatch(
size_t index, std::vector<TriangleStatus>& tri_state) {
if (debug_volume) cerr << "deletePatch(" << index << ")\n";
deletePatchRecursively(index, tri_state);
makePermanent(tri_state);
}
void MultiObjectTriangleContainer::makePermanent(
std::vector<TriangleStatus>& tri_state) {
for(size_t index = 0; index < tri_state.size(); ++index ) {
if (debug_volume) cerr << "mP: tri_state[" << index << "]=";
switch (tri_state[index]) {
case TMP_OK:
if (debug_volume) cerr << "TMP_OK\n";
tri_state[index] = OK;
break;
case TMP_FLIP:
if (debug_volume) cerr << "TMP_FLIP\n";
tri_state[index] = FLIP;
break;
case TMP_DELETE:
if (debug_volume) cerr << "TMP_DELETE\n";
tri_state[index] = DELETE;
break;
case TMP_VISITED:
if (debug_volume) cerr << "TMP_VISITED\n";
tri_state[index] = UNKNOWN;
break;
default:
if (debug_volume) cerr << "DEFAULT("<< tri_state[index] <<")\n";
}
}
}
void MultiObjectTriangleContainer::deletePatchRecursively(
size_t index, std::vector<TriangleStatus>& tri_state) {
if (debug_volume)
{
cerr << "dP idx=" << index << " state=" << tri_state[index] << endl;
}
if (tri_state[index] == DELETE) { // shouldn't get here...
if (debug_volume) cerr << "already deleted: " << index << endl;
return;
}
if (tri_state[index] == OTHER) {
if (debug_volume) cerr << "part of other object: " << index << endl;
return;
}
if (tri_state[index] == TMP_DELETE) { // needed to end recursion!
// getTrianglesForEdge ignores DELETE triangles,
//so we might run into problems there...
if (debug_volume) cerr << "already tmp_deleted: " << index << endl;
return;
}
// We might want to exclude the permanent states (OK, FLIP), too...
tri_state[index] = TMP_DELETE;
TriangleData& data = triangles[index];
vector<size_t> neighbors;
for (int c = 0; c < 3; ++c)
{
vector<size_t> trisForEdge = getTrianglesForEdge (
data.points[c],
data.points[(c+1) % 3],
tri_state);
if (debug_volume)
{
cerr << "p1=" << data.points[c] << " p2=" << data.points[(c+1) % 3]
<< " tFE.size=" << trisForEdge.size() << endl;
}
if (trisForEdge.size() == 2) { // just us and one neighbor
size_t neighbor = (trisForEdge[0]==index)?trisForEdge[1]:trisForEdge[0];
if (debug_volume) cerr << "neigh= " << neighbor << endl;
neighbors.push_back(neighbor);
}
}
for (size_t neigh_index=0; neigh_index < neighbors.size(); ++neigh_index) {
deletePatchRecursively(neighbors[neigh_index], tri_state);
}
}
bool MultiObjectTriangleContainer::hasLonelyEdge(
size_t index, std::vector<TriangleStatus>& tri_state) {
TriangleData& data = triangles[index];
for (int c = 0; c < 3; ++c)
{
vector<size_t> trisForEdge = getTrianglesForEdge (
data.points[c],
data.points[(c+1) % 3],
tri_state);
if (debug_volume)
{
cerr << "p1=" << data.points[c] << " p2=" << data.points[(c+1) % 3]
<< " tFE.size=" << trisForEdge.size() << endl;
}
if (trisForEdge.size() == 1) {
if (debug_volume) cerr << "foundLonelyEdge" << endl;
return true;
}
}
return false;
}
void MultiObjectTriangleContainer::removeTriangleFromObject(
int object, size_t triangle)
{
TriangleData& data = triangles[triangle];
data.setMembershipOfObject(object, false);
}
// extend orignial method to accomodate for deleted triangles in tri_state
vector<size_t>
MultiObjectTriangleContainer::getTrianglesForEdge(
size_t point1,
size_t point2,
vector<TriangleStatus>& tri_state) {
vector<size_t> result;
vector<size_t> allTrianglesForEdge = getTrianglesForEdge(
point1,
point2);
for (vector<size_t>::iterator it = allTrianglesForEdge.begin();
it != allTrianglesForEdge.end();
++it) {
TriangleStatus state = tri_state[*it];
if (state == DELETE || state == OTHER ) continue;
if (debug_volume) cerr << " " << *it << " ";
result.push_back(*it);
}
return result;
}
// first and second must be != last
void MultiObjectTriangleContainer::getPatchForHoleRecursion(
std::vector<size_t>::const_iterator first,
std::vector<size_t>::const_iterator last, // one after last
std::vector<std::vector<size_t> >& triangles) {
assert(first != last);
std::vector<size_t>::const_iterator second = first;
++second;
assert(second != last);
std::vector<size_t>::const_iterator third = second;
++third;
while (third != last) { //TODO:
//what if previous set of points was collinear when third becomes last??
// if truly collinear we alread inserted an edge which is overlapping
// these last points...
if (!spatial3d_geometric::collinear(points[*first],
points[*second], points[*third])) {
std::vector<size_t> triangle;
triangle.push_back(*first);
triangle.push_back(*second);
triangle.push_back(*third);
if (debug_volume) cerr << "added: " << points[*first] << ", "
<< points[*second] << ", " << points[*third] << endl;
triangles.push_back(triangle);
std::vector<size_t>::const_iterator next_last = third;
++next_last;
// if we have skipped some vertices, handle them now
MultiObjectTriangleContainer::getPatchForHoleRecursion(second,
next_last, triangles);
second = third;
} else {
if (debug_volume)
{
cerr << "collinear: " << points[*first] << ", "
<< points[*second] << ", " << points[*third] << endl;
}
}
++third;
}
}
// basically the 3 coins triangulation method adpated to 3D
// Idea: No need to consider self intersection of patching triangles.
// Just add to the container and let it do its magic.
// only thing to consider are colinear points, as they will not form a triangle
// if the first, second and third are colinear, we'll just advance third and
std::vector<std::vector<size_t> >
MultiObjectTriangleContainer::getPatchForHole(
int object,
const std::vector<size_t>& holes) {
std::vector<std::vector<size_t> > triangles(0);
if (debug_volume) cerr << "entering getPatchForHole()\n";
// initialize vectors
std::vector<size_t>::const_iterator last = holes.end();
std::vector<size_t>::const_iterator first = holes.begin();
if (first == last) {
if (debug_volume) cerr << "empty hole vector!!\n";
return triangles;
}
std::vector<size_t>::const_iterator second = first;
++second;
if (second == last) {
if (debug_volume) cerr << "only one point in hole vector!!\n";
return triangles;
}
std::vector<size_t>::const_iterator third = second;
++third;
if (third == last) {
if (debug_volume) cerr << "only two points in hole vector!!\n";
return triangles;
}
MultiObjectTriangleContainer::getPatchForHoleRecursion(first,last,triangles);
if (debug_volume) cerr << "leaving getPatchForHole()\n";
return triangles;
}
bool MultiObjectTriangleContainer::fillHoles (
int object, const std::vector<std::vector<size_t> >& holes) {
if (debug_volume) cerr << "entering fillHoles()\n";
for (std::vector<std::vector<size_t> >::const_iterator hole_it =
holes.begin();
hole_it != holes.end();
++hole_it) {
std::vector<std::vector<size_t> > patch =
MultiObjectTriangleContainer::getPatchForHole(object, *hole_it);
if (debug_volume) cerr << "--- Patch start ---\n";
if (patch.size() >0) {
for (std::vector<std::vector<size_t> >::iterator tri_it = patch.begin();
tri_it != patch.end();
++tri_it) {
std::vector<size_t> triangle = *tri_it;
if (debug_volume) {
cerr << triangle[0] << " " << triangle[1] << " " << triangle[2]<<endl;
cerr << points[triangle[0]] << " " << points[triangle[1]]
<< " "<< points[triangle[2]] << endl;
}
bool res = addTriangle(triangle.data(), object, true);
if (!res) {return false;} // failed to add triangle... bail out
}
}
if (debug_volume) cerr << "--- Patch end ---\n";
}
if (debug_volume) cerr << "leaving fillHoles()\n";
return true;
}
bool MultiObjectTriangleContainer::getHoles (
int object, std::vector<std::vector<size_t> >& result) {
assert(object > 0);
assert(object <= noExternalObjects);
std::multimap<size_t,size_t> edgeIndex;
std::vector<Edge3d> edges;
getLonelyEdges(object, edges, edgeIndex);
if (debug_volume) {
std::cerr << "found edges with one triangle:\n";
for (std::multimap<size_t,size_t>::iterator it=edgeIndex.begin();
it!=edgeIndex.end();
++it) {
cerr << "point " << (*it).first << " => edge " << (*it).second << ": "
<< edges[(*it).second] << endl;
}
}
formHolesFromEdges(object, edges, edgeIndex, result);
for (std::vector<std::vector<size_t> >::iterator hole_it = result.begin();
hole_it != result.end();
++hole_it) {
if (debug_volume) {
cerr << "----- hole start -----\n";
for (std::vector<size_t>::iterator point_it = hole_it->begin();
point_it != hole_it->end();
++point_it) {
cerr << *point_it << endl;
}
cerr << "------ hole end ------\n";
}
}
bool found_holes = (edges.size() > 0);
return found_holes;
}
// helper to find the lonely edges inside a given object.
// edges are directed as in the containing triangle.
// returns the egdes as pair of indices to the start/end points
// and a multimap to quickly find the edges by the index of
// a given start or end point
void MultiObjectTriangleContainer::getLonelyEdges (
int object,
std::vector<Edge3d>& edges,
std::multimap<size_t,size_t>& edgeIndex) {
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it(triangles_tree.entries());
size_t const * triangleIndex;
while( (triangleIndex = it->next()) != 0)
{
const TriangleData& data = triangles[*triangleIndex];
if (data.isMemberOfObject(object))
{
for (int c = 0; c < 3; ++c)
{
Edge3d edge;
edge.startPoint = data.points[c];
edge.endPoint = data.points[(c+1) % 3];
vector<size_t> allTrianglesForEdge = getTrianglesForEdge(
edge.startPoint,
edge.endPoint);
vector<size_t> trianglesForEdge;
for (vector<size_t>::iterator index = allTrianglesForEdge.begin();
index != allTrianglesForEdge.end();
++index)
{
if (triangles[*index].isMemberOfObject(object))
{
trianglesForEdge.push_back(*index);
}
}
//TODO: Check if edges with tri_count >2 need to be handled
// In that case a test for duplicate edges inside the map is required
if (trianglesForEdge.size() == 1)
{
size_t index = edges.size();
edges.push_back(edge);
edgeIndex.insert ( std::pair<size_t,size_t>(edge.startPoint,index) );
edgeIndex.insert ( std::pair<size_t,size_t>(edge.endPoint,index) );
}
}
}
}
}
// helper to create/find cycles in the provided edges
// the found cycles are returned as vector containing the index to the vertices
// where vertex[n] and vertex[n % vertex.size()] form an edge of the cycle.
// (two successive vertexes form an edge, and also the last and first one)
//
// This first (simple) implementation will only find cycles of edges whose pnts
// are only connected to exactly one other edge
// Or put another way: vertices of cycle A are disjoint from vertices of cycle B
// (for all cycles A, B)
//
// But in case we want to close loose ends, we need to navigate
// the surface of the correct object
void MultiObjectTriangleContainer::formHolesFromEdges(
int object,
std::vector<Edge3d>& edges,
std::multimap<size_t,size_t>& edgeIndex,
std::vector<std::vector<size_t> >& result) {
result.clear();
if (edges.size() == 0) return;
// try our best (so far): for every unvisited edge try to form a cycle...
// TODO: check if we need more states if we allow other types of holes
std::vector<bool> used(edges.size(), false);
size_t lowest_unused_edge = 0;
while (true) {
while (used[lowest_unused_edge]) {
++lowest_unused_edge;
if (lowest_unused_edge == edges.size()) {
return;
}
}
std::vector<size_t> newCycle;
size_t current_edge_index = lowest_unused_edge;
used[current_edge_index] = true;
Edge3d current_edge = edges[current_edge_index];
// we start the (potential) cycle in opposite direction of given edge
// that way (for correctly orietated egde triangles) the hole edges
// will have the same orientation as the edges for the required patch
//size_t current_point = current_edge.endPoint; // wrong ordre
size_t current_point = current_edge.startPoint;
newCycle.push_back(current_point);
while(true) {
int edge_count_for_current_point = 0;
size_t new_edge_index = 0;
for (std::multimap<size_t,size_t>::iterator it
= edgeIndex.lower_bound(current_point);
it != edgeIndex.upper_bound(current_point);
++it) {
++edge_count_for_current_point;
// we have found the incoming edge:
if (it->second == current_edge_index) continue;
new_edge_index = it->second;
used[new_edge_index] = true;
size_t other_point = (edges[new_edge_index].startPoint == current_point)
? edges[new_edge_index].endPoint : edges[new_edge_index].startPoint;
// push_back anyway, if we overwrite, we'll detect later (egde_count > 2)
newCycle.push_back(other_point);
}
if (edge_count_for_current_point !=2) {
// we only want a clean cycle! no "Abwzeigungen" allowed.
if (debug_volume) {
cerr << "detected " << edge_count_for_current_point
<< " edge(s) for point " << current_point << endl;
}
break;
}
if (newCycle.back() == newCycle.front()) {
// last = first element, front is valid, so we have a valid cycle
newCycle.pop_back();
result.push_back(newCycle);
break;
}
current_edge_index = new_edge_index;
current_point = newCycle.back();
}
}
}
// TODO: old method - used for reference only - check if can be removed
bool MultiObjectTriangleContainer::checkVolume_closed(int object,
bool corrections)
{
assert(object > 0);
assert(object <= noExternalObjects);
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it(triangles_tree.entries());
size_t const * triangleIndex;
while( (triangleIndex = it->next()) != 0)
{
const TriangleData& data = triangles[*triangleIndex];
if (data.isMemberOfObject(object))
{
for (int c = 0; c < 3; ++c)
{
size_t edgePoint1 = data.points[c];
size_t edgePoint2 = data.points[(c+1) % 3];
vector<size_t> allTrianglesForEdge = getTrianglesForEdge(edgePoint1,
edgePoint2);
vector<size_t> trianglesForEdge;
for (vector<size_t>::iterator index = allTrianglesForEdge.begin();
index != allTrianglesForEdge.end(); ++index)
{
if (triangles[*index].isMemberOfObject(object))
{
trianglesForEdge.push_back(*index);
}
}
// TODO: jede gerade Zahl erlauben und prüfen
if (trianglesForEdge.size() != 2)
{
if (debug_volume) {
cerr << "edgePoint1=" << edgePoint1
<< " edgePoint2=" << edgePoint2
<< " trianglesForEdge.size()=" << trianglesForEdge.size()
<< endl;
}
return false;
}
else
{
bool edge1 = getTriangleEdgeDirection(triangles[trianglesForEdge[0]],
edgePoint1,
edgePoint2);
bool edge2 = getTriangleEdgeDirection(triangles[trianglesForEdge[1]],
edgePoint1,
edgePoint2);
if (edge1 == edge2)
{
if (debug_volume) {
cerr << "same orientation for edgePoint1=" << edgePoint1
<< " edgePoint2=" << edgePoint2
<< " and triangles " << trianglesForEdge[0]
<< " " << trianglesForEdge[1] << endl;
}
return false;
}
}
}
}
}
return true;
}
// TODO: old method stub - not used anymore - remove
bool MultiObjectTriangleContainer::checkVolume_orientation(int object,
bool corrections)
{
return true;
}
// TODO: old method stub - not used anymore - remove
bool MultiObjectTriangleContainer::checkVolume_unconnected_inner_components(
int object,
bool corrections)
{
return true;
}
// TODO: old method stub - not used anymore - remove
bool MultiObjectTriangleContainer::checkVolume_connected_inner_components(
int object,
bool corrections)
{
return true;
}
//////////////////////////////////////////////////////////////////////////
// private methods ///////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
MultiObjectTriangleContainer::TriangleData::TriangleData(size_t p1,
size_t p2,
size_t p3)
{
points[0] = p1;
points[1] = p2;
points[2] = p3;
if (p1 == (size_t)-1)
{
cerr << "Invalid triangle XXX" << endl;
}
assert(p1 != p2);
assert(p2 != p3);
assert(p3 != p1);
object_membership = 0;
object_membership_direction = 0;
}
bool MultiObjectTriangleContainer::TriangleData::isMemberOfObject(int object)
const
{
return (object_membership & getObjectBitmask(object)) != 0;
}
void MultiObjectTriangleContainer::TriangleData::setMembershipOfObject(
int object, bool isMember)
{
if (isMember)
{
object_membership |= getObjectBitmask(object);
}
else
{
object_membership &= ~getObjectBitmask(object);
}
}
bool MultiObjectTriangleContainer::TriangleData::getDirectionOfObject(
int object) const
{
return (object_membership_direction & getObjectBitmask(object)) != 0;
}
void MultiObjectTriangleContainer::TriangleData::setDirectionOfObject(
int object, bool direction)
{
if (direction)
{
object_membership_direction |= getObjectBitmask(object);
}
else
{
object_membership_direction &= ~getObjectBitmask(object);
}
}
MultiObjectTriangleContainer::SortedEdge::SortedEdge(size_t _edgeP1,
size_t _edgeP2,
size_t xPoint,
const vector<SimplePoint3d>& points)
: edgeP1(_edgeP1), edgeP2(_edgeP2)
{
Vector3d edgeVector(points[edgeP1], points[edgeP2]);
projectionPlane = Plane3d(points[edgeP1],(1 / length(edgeVector))
* edgeVector);
projection = Projection2d(projectionPlane, points[edgeP1], points[xPoint]);
}
size_t MultiObjectTriangleContainer::SortedEdge::getThirdPoint(
const size_t trianglePoints[])
{
for (int c = 0; c < 3; ++c)
{
if (trianglePoints[c] != edgeP1 && trianglePoints[c] != edgeP2)
{
return trianglePoints[c];
}
}
assert(false);
return 0;
}
TrianglePathDirection MultiObjectTriangleContainer::SortedEdge::getDirection(
const size_t trianglePoints[])
{
for (int c = 0; c < 3; ++c)
{
if (trianglePoints[c] == edgeP1)
{
if (trianglePoints[(c + 1) % 3] == edgeP2)
return LEAVE;
else
return ENTER;
}
}
assert(false);
return LEAVE;
}
MultiObjectTriangleContainer::SortedTriangle::SortedTriangle(
SortedEdge _edge, size_t _triangle,
const TriangleData _triangleData,
const vector<SimplePoint3d>& points)
: edge(_edge), triangle(_triangle), triangleData(_triangleData)
{
thirdPoint = points[edge.getThirdPoint(triangleData.points)];
phi = getPolarAngle(edge.projection.project(thirdPoint));
direction = edge.getDirection(triangleData.points);
}
TrianglePathDirection
MultiObjectTriangleContainer::SortedTriangle::getDirectionForObject(int object)
{
object_t object_bits = getObjectBitmask(object);
if ((triangleData.object_membership_direction & object_bits) == 0)
return direction;
else
return !direction;
}
bool MultiObjectTriangleContainer::SortedTriangle::isInObject(int object)
{
object_t object_bits = getObjectBitmask(object);
return (triangleData.object_membership & object_bits) != 0;
}
bool MultiObjectTriangleContainer::addTriangle(size_t* points,
int object,
bool corrections)
{
// Add to object 0
TriangleData newTriangle(points[0], points[1], points[2]);
newTriangle.object_membership = getObjectBitmask(0);
addTriangle(newTriangle);
return moveObject0(object, corrections);
}
MultiObjectTriangleContainer::TriangleSearchResult
MultiObjectTriangleContainer::getAnIncompatibleTriangle(size_t triangle,
size_t& out_incompatibleTriangle,
TriangleIntersectionResult& out_intersection)
{
Rectangle<3> bbox = getTriangleBoundingBox(triangle);
bbox.Extend(0.0001);
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it(triangles_tree.find(bbox));
size_t const * existingTriangleIndex;
while( (existingTriangleIndex = it->next()) != 0)
{
if (*existingTriangleIndex == triangle)
continue;
const TriangleData& existingTriangleData =triangles[*existingTriangleIndex];
const TriangleData& newTriangle = triangles[triangle];
TriangleDataComparisonResult comp = compare(newTriangle,
existingTriangleData);
if (comp == SAME || comp == OPPOSITE)
{
out_incompatibleTriangle = *existingTriangleIndex;
return (comp == SAME) ? KNOWN_SAME : KNOWN_OPPOSITE;
}
TriangleIntersectionResult intersectionResult =
intersection(getTriangle(newTriangle),
getTriangle(existingTriangleData));
switch (intersectionResult.getIntersectionType())
{
case TriangleIntersectionResult::NO_INTERSECTION:
case TriangleIntersectionResult::POINT:
continue;
case TriangleIntersectionResult::AREA:
out_incompatibleTriangle = *existingTriangleIndex;
out_intersection = intersectionResult;
return INCOMPATIBLE;
case TriangleIntersectionResult::SEGMENT:
if (comp == SHARED_EDGE)
{
// This is legal!
continue;
}
else
{
out_incompatibleTriangle = *existingTriangleIndex;
out_intersection = intersectionResult;
return INCOMPATIBLE;
}
}
}
return NEW;
}
size_t MultiObjectTriangleContainer::addPoint(const SimplePoint3d& point)
{
/* First, compare coordinates with existing points. If there is a
* matching point, just return its index. Otherwise, we create a new
* point.
*/
Rectangle<3> bbox = point.BoundingBox();
bbox.Extend(0.0001);
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it(points_tree.find(bbox));
size_t const * index;
while( (index = it->next()) != 0)
{
SimplePoint3d p = points[*index];
if (almostEqual(point, p))
{
return *index;
}
}
int res = points.size();
points.push_back(point);
points_tree.insert(bbox, res);
return res;
}
size_t MultiObjectTriangleContainer::addTriangle(const TriangleData& triangle)
{
size_t newIndex;
if (unused_triangle_indices.size() > 0)
{
newIndex = unused_triangle_indices.top();
unused_triangle_indices.pop();
triangles[newIndex] = triangle;
}
else
{
newIndex = triangles.size();
triangles.push_back(triangle);
}
triangles_tree.insert(getTriangleBoundingBox(newIndex), newIndex);
if ((triangle.object_membership & 1) == 1)
{
trianglesObject0.push_back(newIndex);
}
return newIndex;
}
void MultiObjectTriangleContainer::removeTriangle(size_t triangle)
{
if ((triangles[triangle].object_membership & 1) == 1)
{
trianglesObject0.erase(std::remove(trianglesObject0.begin(),
trianglesObject0.end(),
triangle),
trianglesObject0.end());
}
triangles_tree.erase(getTriangleBoundingBox(triangle), triangle);
unused_triangle_indices.push(triangle);
}
void MultiObjectTriangleContainer::addExistingTriangleToObject(
TriangleData& triangleData,
int object,
bool oppositeDirection)
{
object_t bit = 1 << object;
triangleData.object_membership |= bit;
if (oppositeDirection)
{
triangleData.object_membership_direction |= bit;
}
else
{
triangleData.object_membership_direction &= ~bit;
}
}
Triangle MultiObjectTriangleContainer::getTriangle(const TriangleData& data,
int object) const
{
object_t object_bits = getObjectBitmask(object);
if ((data.object_membership_direction & object_bits) == 0)
{
// Direction of data
return Triangle(points[data.points[0]],
points[data.points[1]],
points[data.points[2]]);
}
else
{
// Opposite direction
return Triangle(points[data.points[2]],
points[data.points[1]],
points[data.points[0]]);
}
}
Triangle MultiObjectTriangleContainer::getTriangle(const TriangleData& data)
const
{
return Triangle(points[data.points[0]],
points[data.points[1]],
points[data.points[2]]);
}
bool MultiObjectTriangleContainer::getTriangleEdgeDirection(
const TriangleData& data,
size_t point1, size_t point2) const
{
for (int c = 0; c < 3; ++c)
{
if (data.points[c] == point1)
{
return data.points[(c + 1) % 3] == point2;
}
}
assert(false);
}
Rectangle<3> MultiObjectTriangleContainer::getSegmentBoundingBox(size_t point1,
size_t point2)
const
{
Rectangle<3> result = points[point1].BoundingBox();
result.Extend(points[point2].BoundingBox());
return result;
}
Rectangle<3> MultiObjectTriangleContainer::getTriangleBoundingBox(
size_t triangle) const
{
Rectangle<3> result = points[triangles[triangle].points[0]].BoundingBox();
result.Extend(points[triangles[triangle].points[1]].BoundingBox());
result.Extend(points[triangles[triangle].points[2]].BoundingBox());
return result;
}
// returns indices of points that share an edge with the argument
set<size_t>
MultiObjectTriangleContainer::getEdgesForPoint(size_t point) const
{
set<size_t> result;
Rectangle<3> bbox = points[point].BoundingBox();
bbox.Extend(0.0001);
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it(triangles_tree.find(bbox));
size_t const * existingTriangleIndex;
while( (existingTriangleIndex = it->next()) != 0)
{
const TriangleData& existingTriangleData =triangles[*existingTriangleIndex];
for(int c = 0; c < 3; ++c)
{
if (existingTriangleData.points[c] == point)
{
for (int d = 0; d < 3; ++d)
{
if (d != c)
{
result.insert(existingTriangleData.points[d]);
}
}
}
}
}
return result;
}
vector<size_t>
MultiObjectTriangleContainer::getTrianglesForPoint(size_t point) const
{
vector<size_t> result;
Rectangle<3> bbox = points[point].BoundingBox();
bbox.Extend(0.0001);
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it(triangles_tree.find(bbox));
size_t const * existingTriangleIndex;
while( (existingTriangleIndex = it->next()) != 0)
{
const TriangleData& existingTriangleData =triangles[*existingTriangleIndex];
for(int c = 0; c < 3; ++c)
{
if (existingTriangleData.points[c] == point)
{
result.push_back(*existingTriangleIndex);
break;
}
}
}
return result;
}
vector<size_t>
MultiObjectTriangleContainer::getTrianglesForEdge(size_t point1,
size_t point2) const
{
vector<size_t> result;
Rectangle<3> bbox = getSegmentBoundingBox(point1, point2);
bbox.Extend(0.0001);
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it(triangles_tree.find(bbox));
size_t const * existingTriangleIndex;
while( (existingTriangleIndex = it->next()) != 0)
{
const TriangleData& existingTriangleData =
triangles[*existingTriangleIndex];
int number_of_corners = 0;
for(int c = 0; c < 3; ++c)
{
size_t p = existingTriangleData.points[c];
if (p == point1 || p == point2)
{
++ number_of_corners;
}
}
if (number_of_corners == 2)
{
result.push_back(*existingTriangleIndex);
}
}
return result;
}
MultiObjectTriangleContainer::TriangleDataComparisonResult
MultiObjectTriangleContainer::compare(const TriangleData& t1,
const TriangleData& t2) const
{
size_t index_in_t2[3];
int number_of_corners = 0;
for (int c1 = 0; c1 < 3; ++c1)
{
for (int c2 = 0; c2 < 3; ++c2)
{
if (t1.points[c1] == t2.points[c2])
{
index_in_t2[c1] = c2;
++ number_of_corners;
}
}
}
switch(number_of_corners)
{
case 0:
return DIFFERENT;
case 1:
return SHARED_CORNER;
case 2:
return SHARED_EDGE;
case 3:
if ( index_in_t2[1] == (index_in_t2[0] + 1) % 3 )
return SAME;
else
return OPPOSITE;
default:
assert(false);
return DIFFERENT;
}
}
void MultiObjectTriangleContainer::splitEdge(size_t edgeP1, size_t edgeP2,
size_t splitP,
vector<vector<size_t> >& tracked_partitions)
{
vector<size_t> trianglesToBeSplit = getTrianglesForEdge(edgeP1, edgeP2);
for(size_t c = 0; c < trianglesToBeSplit.size(); ++c)
{
size_t triangleToBeSplitNow = trianglesToBeSplit[c];
TriangleData oldTriangleData = triangles[triangleToBeSplitNow];
removeTriangle(triangleToBeSplitNow);
vector<size_t> partitions_containing_this_triangle;
// Find out if this triangle belongs to a tracked partition
for (size_t p = 0; p < tracked_partitions.size(); ++p)
{
for (size_t t = 0; t < tracked_partitions[p].size(); ++t)
{
if (tracked_partitions[p][t] == triangleToBeSplitNow)
{
tracked_partitions[p].erase(tracked_partitions[p].begin() + t);
partitions_containing_this_triangle.push_back(p);
break;
}
}
}
// One point of the old triangle will still be in both of the new
// triangles. Find it.
int point_index_of_old_common_point = -1;
for (int d = 0; d < 3; ++d)
{
if (oldTriangleData.points[d] != edgeP1 &&
oldTriangleData.points[d] != edgeP2)
{
point_index_of_old_common_point = d;
}
}
// Now make two copies of the old triangle data, then replace one point
// with our new point. Do not replace the common point.
for (int n = 1; n <= 2; ++n)
{
TriangleData newTriangleData = oldTriangleData;
int index_to_replace = (point_index_of_old_common_point + n) % 3;
newTriangleData.points[index_to_replace] = splitP;
size_t newIndex = addTriangle(newTriangleData);
for (size_t p = 0; p < partitions_containing_this_triangle.size(); ++p)
{
tracked_partitions[partitions_containing_this_triangle[p]]
.push_back(newIndex);
}
}
}
}
void MultiObjectTriangleContainer::splitSurface(size_t triangle,
size_t surfacePoint,
vector<vector<size_t> >& tracked_partitions)
{
TriangleData oldTriangleData = triangles[triangle];
removeTriangle(triangle);
vector<size_t> partitions_containing_this_triangle;
// Find out if this triangle belongs to a tracked partition
for (size_t p = 0; p < tracked_partitions.size(); ++p)
{
for (size_t t = 0; t < tracked_partitions[p].size(); ++t)
{
if (tracked_partitions[p][t] == triangle)
{
tracked_partitions[p].erase(tracked_partitions[p].begin() + t);
partitions_containing_this_triangle.push_back(p);
break;
}
}
}
for (int c = 0; c < 3; ++c)
{
TriangleData newTriangleData = oldTriangleData;
newTriangleData.points[c] = surfacePoint;
size_t newIndex = addTriangle(newTriangleData);
for (size_t p = 0; p < partitions_containing_this_triangle.size(); ++p)
{
tracked_partitions[partitions_containing_this_triangle[p]]
.push_back(newIndex);
}
}
}
// Output vector both: orientation of t0
void MultiObjectTriangleContainer::cutTriangles2d(size_t t0, size_t t1,
vector<size_t>& o0, vector<size_t>& o1,
vector<size_t>& both)
{
vector<vector<size_t> > parts;
parts.push_back(vector<size_t>());
parts.push_back(vector<size_t>());
parts[0].push_back(t0);
parts[1].push_back(t1);
TriangleData originalTriangleData[2] = { triangles[t0], triangles[t1] };
// Iterate over triangles t whose edges we use to split the other triangle.
// Iterate over the edges e of that triangle.
for (size_t t = 0; t < 2; ++t) for (int e = 0; e < 3; ++e)
{
const size_t edgePoint1 = originalTriangleData[t].points[e];
const size_t edgePoint2 = originalTriangleData[t].points[(e + 1) % 3];
size_t parts_to_be_split = 1 - t;
for(;;)
{
bool split = false;
for (size_t c = 0; c < parts[parts_to_be_split].size(); ++c )
{
size_t triangle = parts[parts_to_be_split][c];
// Wir haben nun ein Dreieck sowie eine Strecke und müssen die
// Verträglichkeit beurteilen.
split = splitTriangleForSegment(triangle, edgePoint1, edgePoint2,
parts);
if (split)
break;
}
if (!split)
{
break;
}
}
}
if (debug_splitting)
{
cerr << "Parts 0: ";
for (size_t c = 0; c < parts[0].size(); ++c)
{
cerr << getTriangle(triangles[parts[0][c]]) << " ";
}
cerr << endl;
cerr << "Parts 1: ";
for (size_t c = 0; c < parts[1].size(); ++c)
{
cerr << getTriangle(triangles[parts[1][c]]) << " ";
}
cerr << endl;
}
// Nun sind alle Teile mit den Kanten des anderen Ursprungsdreiecks
// verträglich. Die Teile liegen nun komplett innerhalb oder komplett
// außerhalb des anderen Ursprungsdreiecks.
for (size_t c = 0; c < parts[0].size(); ++c)
{
if (isCompletelyInside(getTriangle(triangles[parts[0][c]]),
getTriangle(originalTriangleData[1])))
{
both.push_back(parts[0][c]);
}
else
{
o0.push_back(parts[0][c]);
}
}
for (size_t c = 0; c < parts[1].size(); ++c)
{
if (isCompletelyInside(getTriangle(triangles[parts[1][c]]),
getTriangle(originalTriangleData[0])))
{
// not needed (common parts are already found)
}
else
{
o1.push_back(parts[1][c]);
}
}
}
bool MultiObjectTriangleContainer::moveObject0(int toObject, bool corrections)
{
// In each iteration of this loop, one action is being made.
// Either integrating the top element of the object zero stack into
// the object, deleting it as a correction if it is not necessary,
// splitting the new object (increasing the number of objects on the stack),
// splitting existing triangles or both.
// Integrating a single triangle may need several iterations of this loop,
// but progress is guaranteed each time.
object_t toObject_bits = getObjectBitmask(toObject);
while(trianglesObject0.size() > 0)
{
if (debug_splitting)
{
cerr << "Next step." << endl;
// cerr << "Current triangles are: " << endl;
//test();
}
size_t newTriangleIndex = trianglesObject0.back();
size_t incompatibleTriangleIndex;
TriangleIntersectionResult intersectionResult;
TriangleSearchResult searchResult = getAnIncompatibleTriangle(
newTriangleIndex,
incompatibleTriangleIndex,
intersectionResult);
TriangleData& newTriangleData = triangles[newTriangleIndex];
Triangle newTriangle = getTriangle(newTriangleData);
if (debug_splitting) cerr << "new Triangle: "
<< getTriangle(newTriangleData) << endl;
switch(searchResult)
{
case NEW:
{
if (debug_splitting) cerr << "case NEW" << endl;
removeTriangle(newTriangleIndex);
newTriangleData.object_membership = getObjectBitmask(toObject);
newTriangleData.object_membership_direction = 0;
addTriangle(newTriangleData);
continue;
}
case KNOWN_SAME:
case KNOWN_OPPOSITE:
{
if (debug_splitting) cerr << "case KNOWN" << endl;
TriangleData& incompatibleData = triangles[incompatibleTriangleIndex];
bool twice_in_same_object =
(toObject_bits & incompatibleData.object_membership) != 0;
if (twice_in_same_object)
{
if (corrections)
{
// Just drop it
removeTriangle(newTriangleIndex);
continue;
}
else
{
// That is not allowed without corrections.
removeObject(0);
return false;
}
}
else
{
removeTriangle(newTriangleIndex);
bool direction = searchResult == KNOWN_OPPOSITE;
addExistingTriangleToObject(incompatibleData, toObject, direction);
continue;
}
}
case INCOMPATIBLE:
{
if (debug_splitting) cerr << "case INCOMPATIBLE" << endl;
TriangleData& incompatibleData = triangles[incompatibleTriangleIndex];
Triangle incompatibleTriangle = getTriangle(incompatibleData);
if (debug_splitting) cerr << "incompatible: "
<< getTriangle(incompatibleData) << endl;
if (debug_splitting) cerr << "intersection Type: "
<< intersectionResult.getIntersectionType()
<< endl;
if (!corrections)
{
if (debug_splitting) cerr << "Abort add: no corrections" << endl;
removeObject(0);
return false;
}
// Decide on action
if (intersectionResult.getIntersectionType() ==
TriangleIntersectionResult::AREA)
{
if (debug_splitting) cerr << "area intersection" << endl;
// Prüfen, ob gleiche oder gegensätzliche Orientierung
bool sameDirection = newTriangle.getNormalVector()
* incompatibleTriangle.getNormalVector() > 0;
vector<size_t> onlyNew, onlyExisting, both;
cutTriangles2d(incompatibleTriangleIndex, newTriangleIndex,
onlyExisting, onlyNew, both);
if (both.size() == 0)
{
cerr << "size only existing: " << onlyExisting.size() << endl
<< "size only new: " << onlyNew.size() << endl
<< "size both: " << both.size() << endl;
cerr << "---" << endl;
cerr << "triangles only existing: ";
for (int c = 0; c < onlyExisting.size(); ++c)
{
cerr << getTriangle(triangles[onlyExisting[c]]) << " ";
}
cerr << endl;
cerr << "triangles only new: ";
for (int c = 0; c < onlyNew.size(); ++c)
{
cerr << getTriangle(triangles[onlyNew[c]]) << " ";
}
cerr << endl;
cerr << "triangles both: ";
for (int c = 0; c < both.size(); ++c)
{
cerr << getTriangle(triangles[both[c]]) << " ";
}
cerr << endl;
numeric_fail();
}
// Both sind jetzt Dreiecke des bestehenden Objekts.
// Das kopieren wir in Objekt 0 und setzen die Orientierung
// entsprechend.
// Da Dreiecke in Objekt 0 in der nativen Orientierung erwartet
// werden, drehen wir sie bei Bedarf um.
for (int c = 0; c < both.size(); ++c)
{
if (sameDirection)
{
TriangleData copyForObject0(triangles[both[c]].points[0],
triangles[both[c]].points[1],
triangles[both[c]].points[2]);
copyForObject0.object_membership = getObjectBitmask(0);
addTriangle(copyForObject0);
}
else
{
TriangleData copyForObject0(triangles[both[c]].points[2],
triangles[both[c]].points[1],
triangles[both[c]].points[0]);
copyForObject0.object_membership = getObjectBitmask(0);
addTriangle(copyForObject0);
}
}
}
if (intersectionResult.getIntersectionType() ==
TriangleIntersectionResult::SEGMENT)
{
if (debug_splitting) cerr << "segment intersection" << endl;
vector<SimplePoint3d>& intersectionPoints
= intersectionResult.getIntersectionPoints();
size_t point1 = addPoint(intersectionPoints[0]);
size_t point2 = addPoint(intersectionPoints[1]);
// Try to split new triangle first
if (!isEdgeOf(point1, point2, newTriangleIndex))
{
vector<vector<size_t> > parts;
bool success = splitTriangleForSegment(newTriangleIndex,
point1, point2, parts);
if (!success)
{
cerr << "Fehler: 1" << endl
<< "new: " << newTriangle << "(normal: "
<< newTriangle.getPlane().getNormalVector() << ")" << endl
<< "incompatible: " << incompatibleTriangle << "(normal: "
<< incompatibleTriangle.getPlane().getNormalVector() << ")"
<< endl
<< "Segment: " << intersectionPoints[0] << "<->"
<< intersectionPoints[1] << endl;
numeric_fail();
}
continue;
}
else
{
vector<vector<size_t> > parts;
bool success = splitTriangleForSegment(incompatibleTriangleIndex,
point1, point2,
parts);
if (!success)
{
cerr << "Fehler: 2" << endl
<< "new: " << newTriangle << "(normal: "
<< newTriangle.getPlane().getNormalVector() << ")" << endl
<< "incompatible: " << incompatibleTriangle << "(normal: "
<< incompatibleTriangle.getPlane().getNormalVector() << ")"
<< endl
<< "Segment: " << intersectionPoints[0] << "<->"
<< intersectionPoints[1] << endl;
numeric_fail();
}
continue;
}
}
}
}
}
// Stack is empty if we left the loop!
return true;
}
bool MultiObjectTriangleContainer::isEdgeOf(size_t point1, size_t point2,
size_t triangle) const
{
bool point1Found = false;
bool point2Found = false;
for (int c = 0; c < 3; ++c)
{
if (triangles[triangle].points[c] == point1)
{
point1Found = true;
}
if (triangles[triangle].points[c] == point2)
{
point2Found = true;
}
}
return point1Found && point2Found;
}
bool MultiObjectTriangleContainer::splitTriangleForSegment(size_t triangleIndex,
size_t point1Index,
size_t point2Index,
vector<vector<size_t> >& tracked_partitions)
{
TriangleData& triangleData = triangles[triangleIndex];
Triangle triangle = getTriangle(triangleData);
// First job to do: reduce the segment to the subset of the segment that is
// inside the triangle. Done in 2D here, so transform everything to 2D.
const Transformation2d t(triangle.getPlane());
const SimplePoint2d p1_2d = t.transform(points[point1Index]);
const SimplePoint2d p2_2d = t.transform(points[point2Index]);
SimplePoint2d triangle2d[3] = { t.transform(triangle.getA()),
t.transform(triangle.getB()),
t.transform(triangle.getC()) };
if (intersection(p1_2d, p2_2d, triangle2d) != SEGMENT)
{
return false;
}
const SimplePoint2d p1_2d_in = firstPointInsideTriangle(p1_2d, p2_2d,
triangle2d[0],
triangle2d[1],
triangle2d[2]);
const SimplePoint2d p2_2d_in = firstPointInsideTriangle(p2_2d, p1_2d,
triangle2d[0],
triangle2d[1],
triangle2d[2]);
// Now, p1_2d_in and p2_2d_in are the part of the segment inside the triangle.
// Transform the results back into 3D space.
const SimplePoint3d p1_in = t.transform(p1_2d_in);
const SimplePoint3d p2_in = t.transform(p2_2d_in);
// If either point is inside the triangle (not on an edge), then do
// a surface split.
InsideResult insideResult1 = pointInsideTriangle(p1_in, triangle);
if (insideResult1 == INSIDE)
{
// Split surface for this point
splitSurface(triangleIndex, addPoint(p1_in), tracked_partitions);
return true;
}
InsideResult insideResult2 = pointInsideTriangle(p2_in, triangle);
if (insideResult2 == INSIDE)
{
// Split surface for this point
splitSurface(triangleIndex, addPoint(p2_in), tracked_partitions);
return true;
}
// No point is on the inside, so find a point of the segment that is on an
// edge. Split that edge.
size_t split_point_index;
SimplePoint2d const * splitPoint2d;
if (insideResult1 == EDGE)
{
split_point_index = addPoint(p1_in);
splitPoint2d = &p1_2d_in;
}
else if (insideResult2 == EDGE)
{
split_point_index = addPoint(p2_in);
splitPoint2d = &p2_2d_in;
}
else
{
return false;
}
// Split the edge of the triangle.
// First, find out which edge.
for (int c = 0; c < 3; ++c)
{
InsideResult r = pointInsideSegment(*splitPoint2d, triangle2d[c],
triangle2d[(c + 1) % 3]);
if (r == INSIDE)
{
// This is the edge we want to split.
splitEdge(triangleData.points[c],
triangleData.points[(c + 1) % 3],
split_point_index,
tracked_partitions);
return true;
}
}
// If we reach this code, the point we found on an edge turned out not to
// be on an edge. Should not happen.
return false;
}
void MultiObjectTriangleContainer::prepareSetOperationSurface(int in_object1,
int in_object2,
int out_object_commonSurface,
int out_object_only1,
int out_object_only2)
{
object_t in1 = getObjectBitmask(in_object1);
object_t in2 = getObjectBitmask(in_object2);
object_t in_common = in1 | in2;
object_t out_common = getObjectBitmask(out_object_commonSurface);
object_t out1 = getObjectBitmask(out_object_only1);
object_t out2 = getObjectBitmask(out_object_only2);
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it(triangles_tree.entries());
size_t const * triangleIndex;
while( (triangleIndex = it->next()) != 0)
{
TriangleData& data = triangles[*triangleIndex];
if ((data.object_membership & in_common) == in_common)
{
data.object_membership |= out_common;
}
else if ((data.object_membership & in1) != 0)
{
data.object_membership |= out1;
}
else if ((data.object_membership & in2) != 0)
{
data.object_membership |= out2;
}
}
}
// To prepare the six disjoint sets:
//
// The triangles in both sets are easy to find. For the other triangles,
// we have to find out whether these are on the inside of the other volume or
// on the outside. We do this by finding edges that are shared by both objects,
// which are the locations the surfaces intersect. For all triangles on these
// edges, we can follow a close path around the shared edge that intersects
// each triangle in turn. By keeping track which volume we are inside of, we
// know the result for these triangles. For triangles that do not share such
// an edge, we find out by their connection to a triangle we know about (which
// is just Tarjan's algorithm to find connected components in a graph).
// A working set for each output object other than the common objects is
// created. This set contains all triangles that have known output groups,
// but may have neighbours not visited yet.
// A triangle is marked as visited as soon as it is assigned its output group.
// Unless it is a member of both objects, it is added to the working set the
// same time. Once we look for its neighbours, it is removed from the set again.
void MultiObjectTriangleContainer::prepareSetOperationVolume(int in_object1,
int in_object2,
int out_object_commonSame,
int out_object_commonOpposite,
int out_object_only1_outside2,
int out_object_only2_outside1,
int out_object_only1_inside2,
int out_object_only2_inside1)
{
stack<size_t> working_set_out1_outside;
stack<size_t> working_set_out2_outside;
stack<size_t> working_set_out1_inside;
stack<size_t> working_set_out2_inside;
stack<size_t>* working_sets[4] = { &working_set_out1_outside,
&working_set_out2_outside,
&working_set_out1_inside,
&working_set_out2_inside };
int working_set_in_objects[4] = { in_object1, in_object2,
in_object1, in_object2 };
int working_set_out_objects[4] = { out_object_only1_outside2,
out_object_only2_outside1,
out_object_only1_inside2,
out_object_only2_inside1 };
vector<bool> visited(triangles.size(), false);
// Iterator over all triangles of object 1
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it(triangles_tree.entries());
size_t const * triangleIndex;
while( (triangleIndex = it->next()) != 0)
{
TriangleData& data = triangles[*triangleIndex];
if (data.isMemberOfObject(in_object1))
{
if (data.isMemberOfObject(in_object2))
{
// Triangle is part of both volumes.
bool direction_in1 = data.getDirectionOfObject(in_object1);
bool direction_in2 = data.getDirectionOfObject(in_object2);
if (direction_in1 == direction_in2)
{
data.setMembershipOfObject(out_object_commonSame, true);
data.setDirectionOfObject(out_object_commonSame, direction_in1);
}
else
{
data.setMembershipOfObject(out_object_commonOpposite, true);
data.setDirectionOfObject(out_object_commonOpposite, direction_in1);
}
visited[*triangleIndex] = true;
}
else
{
// Triangle is part of only in1. We have to find edges that are common
// to both objects, so search for neighbours of object in2.
for(int edge_n = 0; edge_n < 3; ++edge_n)
{
vector<size_t> neighbour_triangles
= getTrianglesForEdge(data.points[edge_n],
data.points[(edge_n+ 1) % 3]);
SortedEdge edge(data.points[edge_n], data.points[(edge_n + 1) % 3],
data.points[(edge_n + 2) % 3], points);
vector<SortedTriangle> sorted_triangles;
bool we_have_neighbour_of_object2 = false;
for (vector<size_t>::iterator neighbourIndex
= neighbour_triangles.begin();
neighbourIndex != neighbour_triangles.end(); ++neighbourIndex)
{
const TriangleData neighbour = triangles[*neighbourIndex];
if (neighbour.isMemberOfObject(in_object1) ||
neighbour.isMemberOfObject(in_object2))
{
sorted_triangles.push_back(SortedTriangle(edge, *neighbourIndex,
neighbour, points));
}
if (neighbour.isMemberOfObject(in_object2))
{
we_have_neighbour_of_object2 = true;
}
}
if (!we_have_neighbour_of_object2)
continue;
// We now know we have an edge shared by both objects.
std::sort(sorted_triangles.begin(), sorted_triangles.end(),
triangleSort);
bool inside1, inside2;
// Walk around the edge to see whether we are inside or outside
// the objects for the first triangle.
for(vector<SortedTriangle>::iterator sortedNeighbour
= sorted_triangles.begin();
sortedNeighbour != sorted_triangles.end(); ++sortedNeighbour)
{
bool isObj1 = sortedNeighbour->isInObject(in_object1);
bool isObj2 = sortedNeighbour->isInObject(in_object2);
if (isObj1)
{
// For the next triangle, we know whether we are inside or
// outside of object 1.
TrianglePathDirection direction
= sortedNeighbour->getDirectionForObject(in_object1);
switch (direction)
{
case ENTER:
inside1 = true;
break;
case LEAVE:
inside1 = false;
break;
}
}
if (isObj2)
{
// For the next triangle, we know whether we are inside or
// outside of object 2.
TrianglePathDirection direction
= sortedNeighbour->getDirectionForObject(in_object2);
switch (direction)
{
case ENTER:
inside2 = true;
break;
case LEAVE:
inside2 = false;
break;
}
}
}
// As we had a triangle of both objects on this edge, inside1 and
// inside2 now have the right values for the starting position.
// Walk around the edge a second time to put the triangles into
// the right working sets.
for(vector<SortedTriangle>::iterator sortedNeighbour
= sorted_triangles.begin();
sortedNeighbour != sorted_triangles.end(); ++sortedNeighbour)
{
// First, put the triangle into the right working set if
// - it only belongs to one of the objects and
// - it is not yet marked as visited
bool isObj1 = sortedNeighbour->isInObject(in_object1);
bool isObj2 = sortedNeighbour->isInObject(in_object2);
if (!visited[sortedNeighbour->triangle])
{
size_t triangleIndex = sortedNeighbour->triangle;
TriangleData& triangleData = triangles[triangleIndex];
object_t out_object = 0;
bool out_direction;
if (isObj1 && !isObj2)
{
out_direction = triangleData.getDirectionOfObject(in_object1);
if (inside2)
{
working_set_out1_inside.push(triangleIndex);
out_object = out_object_only1_inside2;
}
else
{
working_set_out1_outside.push(triangleIndex);
out_object = out_object_only1_outside2;
}
}
if (isObj2 && !isObj1)
{
out_direction = triangleData.getDirectionOfObject(in_object2);
if (inside1)
{
working_set_out2_inside.push(triangleIndex);
out_object = out_object_only2_inside1;
}
else
{
working_set_out2_outside.push(triangleIndex);
out_object = out_object_only2_outside1;
}
}
if (out_object != 0)
{
triangleData.setMembershipOfObject(out_object, true);
triangleData.setDirectionOfObject(out_object, out_direction);
visited[triangleIndex] = true;
}
}
if (isObj1)
{
// For the next triangle, we know whether we are inside or
// outside of object 1.
TrianglePathDirection direction
= sortedNeighbour->getDirectionForObject(in_object1);
switch (direction)
{
case ENTER:
inside1 = true;
break;
case LEAVE:
inside1 = false;
break;
}
}
if (isObj2)
{
// For the next triangle, we know whether we are inside or
// outside of object 2.
TrianglePathDirection direction
= sortedNeighbour->getDirectionForObject(in_object2);
switch (direction)
{
case ENTER:
inside2 = true;
break;
case LEAVE:
inside2 = false;
break;
}
}
}
}
}
}
}
// Now, all triangles that are a member of both volumes are already in their
// destination object. All triangles that share an edge with a triangle of
// the other object have been classified into the right working set. From
// here, we have to transitively follow all their neighbours of the same
// object and put them into the same working set.
for(int c = 0; c < 4; ++c)
{
stack<size_t>& working_set = *working_sets[c];
int working_set_in_object = working_set_in_objects[c];
int working_set_out_object = working_set_out_objects[c];
while(!working_set.empty())
{
size_t currentIndex = working_set.top();
working_set.pop();
const TriangleData& currentData = triangles[currentIndex];
// Find all neighbours of the current triangle
for (int edge = 0; edge < 3; ++edge)
{
vector<size_t> neighbour_triangles
= getTrianglesForEdge(currentData.points[edge],
currentData.points[(edge + 1) % 3]);
for (vector<size_t>::iterator neighbourIndex
= neighbour_triangles.begin();
neighbourIndex != neighbour_triangles.end(); ++neighbourIndex)
{
if (visited[*neighbourIndex])
continue;
TriangleData& neighbourData = triangles[*neighbourIndex];
if (neighbourData.isMemberOfObject(working_set_in_object))
{
// Neighbour triangle is not visited and part of the same object,
// so it is also a member of the same result set. Put it into
// the current working set.
working_set.push(*neighbourIndex);
neighbourData.setMembershipOfObject(working_set_out_object, true);
neighbourData.setDirectionOfObject(working_set_out_object,
neighbourData.getDirectionOfObject(working_set_in_object));
visited[*neighbourIndex] = true;
}
}
}
}
}
// Now, there are triangles left that are not visited.
auto_ptr<mmrtree::RtreeT<3, size_t>::iterator> it2(triangles_tree.entries());
while( (triangleIndex = it2->next()) != 0)
{
if (visited[*triangleIndex])
continue;
TriangleData& data = triangles[*triangleIndex];
if (data.isMemberOfObject(in_object1))
{
data.setMembershipOfObject(out_object_only1_outside2, true);
data.setDirectionOfObject(out_object_only1_outside2,
data.getDirectionOfObject(in_object1));
visited[*triangleIndex] = true;
}
if (data.isMemberOfObject(in_object2))
{
data.setMembershipOfObject(out_object_only2_outside1, true);
data.setDirectionOfObject(out_object_only2_outside1,
data.getDirectionOfObject(in_object2));
visited[*triangleIndex] = true;
}
}
}