source: molecuilder/src/tesselation.cpp@ d6b011

Last change on this file since d6b011 was d6b011, checked in by Saskia Metzler <metzler@…>, 16 years ago

Ticket 15: new function RemoveDegeneratedTriangles()
Added functions to find and remove surplus degenerated triangles.

  • Property mode set to 100644
File size: 129.8 KB
Line 
1/*
2 * tesselation.cpp
3 *
4 * Created on: Aug 3, 2009
5 * Author: heber
6 */
7
8#include "tesselation.hpp"
9
10// ======================================== Points on Boundary =================================
11
12/** Constructor of BoundaryPointSet.
13 */
14BoundaryPointSet::BoundaryPointSet()
15{
16 LinesCount = 0;
17 Nr = -1;
18};
19
20/** Constructor of BoundaryPointSet with Tesselpoint.
21 * \param *Walker TesselPoint this boundary point represents
22 */
23BoundaryPointSet::BoundaryPointSet(TesselPoint *Walker)
24{
25 node = Walker;
26 LinesCount = 0;
27 Nr = Walker->nr;
28};
29
30/** Destructor of BoundaryPointSet.
31 * Sets node to NULL to avoid removing the original, represented TesselPoint.
32 * \note When removing point from a class Tesselation, use RemoveTesselationPoint()
33 */
34BoundaryPointSet::~BoundaryPointSet()
35{
36 cout << Verbose(5) << "Erasing point nr. " << Nr << "." << endl;
37 if (!lines.empty())
38 cerr << "WARNING: Memory Leak! I " << *this << " am still connected to some lines." << endl;
39 node = NULL;
40};
41
42/** Add a line to the LineMap of this point.
43 * \param *line line to add
44 */
45void BoundaryPointSet::AddLine(class BoundaryLineSet *line)
46{
47 cout << Verbose(6) << "Adding " << *this << " to line " << *line << "."
48 << endl;
49 if (line->endpoints[0] == this)
50 {
51 lines.insert(LinePair(line->endpoints[1]->Nr, line));
52 }
53 else
54 {
55 lines.insert(LinePair(line->endpoints[0]->Nr, line));
56 }
57 LinesCount++;
58};
59
60/** output operator for BoundaryPointSet.
61 * \param &ost output stream
62 * \param &a boundary point
63 */
64ostream & operator <<(ostream &ost, BoundaryPointSet &a)
65{
66 ost << "[" << a.Nr << "|" << a.node->Name << "]";
67 return ost;
68}
69;
70
71// ======================================== Lines on Boundary =================================
72
73/** Constructor of BoundaryLineSet.
74 */
75BoundaryLineSet::BoundaryLineSet()
76{
77 for (int i = 0; i < 2; i++)
78 endpoints[i] = NULL;
79 Nr = -1;
80};
81
82/** Constructor of BoundaryLineSet with two endpoints.
83 * Adds line automatically to each endpoints' LineMap
84 * \param *Point[2] array of two boundary points
85 * \param number number of the list
86 */
87BoundaryLineSet::BoundaryLineSet(class BoundaryPointSet *Point[2], int number)
88{
89 // set number
90 Nr = number;
91 // set endpoints in ascending order
92 SetEndpointsOrdered(endpoints, Point[0], Point[1]);
93 // add this line to the hash maps of both endpoints
94 Point[0]->AddLine(this); //Taken out, to check whether we can avoid unwanted double adding.
95 Point[1]->AddLine(this); //
96 // clear triangles list
97 cout << Verbose(5) << "New Line with endpoints " << *this << "." << endl;
98};
99
100/** Destructor for BoundaryLineSet.
101 * Removes itself from each endpoints' LineMap, calling RemoveTrianglePoint() when point not connected anymore.
102 * \note When removing lines from a class Tesselation, use RemoveTesselationLine()
103 */
104BoundaryLineSet::~BoundaryLineSet()
105{
106 int Numbers[2];
107
108 // get other endpoint number of finding copies of same line
109 if (endpoints[1] != NULL)
110 Numbers[0] = endpoints[1]->Nr;
111 else
112 Numbers[0] = -1;
113 if (endpoints[0] != NULL)
114 Numbers[1] = endpoints[0]->Nr;
115 else
116 Numbers[1] = -1;
117
118 for (int i = 0; i < 2; i++) {
119 if (endpoints[i] != NULL) {
120 if (Numbers[i] != -1) { // as there may be multiple lines with same endpoints, we have to go through each and find in the endpoint's line list this line set
121 pair<LineMap::iterator, LineMap::iterator> erasor = endpoints[i]->lines.equal_range(Numbers[i]);
122 for (LineMap::iterator Runner = erasor.first; Runner != erasor.second; Runner++)
123 if ((*Runner).second == this) {
124 cout << Verbose(5) << "Removing Line Nr. " << Nr << " in boundary point " << *endpoints[i] << "." << endl;
125 endpoints[i]->lines.erase(Runner);
126 break;
127 }
128 } else { // there's just a single line left
129 if (endpoints[i]->lines.erase(Nr))
130 cout << Verbose(5) << "Removing Line Nr. " << Nr << " in boundary point " << *endpoints[i] << "." << endl;
131 }
132 if (endpoints[i]->lines.empty()) {
133 cout << Verbose(5) << *endpoints[i] << " has no more lines it's attached to, erasing." << endl;
134 if (endpoints[i] != NULL) {
135 delete(endpoints[i]);
136 endpoints[i] = NULL;
137 }
138 }
139 }
140 }
141 if (!triangles.empty())
142 cerr << "WARNING: Memory Leak! I " << *this << " am still connected to some triangles." << endl;
143};
144
145/** Add triangle to TriangleMap of this boundary line.
146 * \param *triangle to add
147 */
148void BoundaryLineSet::AddTriangle(class BoundaryTriangleSet *triangle)
149{
150 cout << Verbose(6) << "Add " << triangle->Nr << " to line " << *this << "."
151 << endl;
152 triangles.insert(TrianglePair(triangle->Nr, triangle));
153};
154
155/** Checks whether we have a common endpoint with given \a *line.
156 * \param *line other line to test
157 * \return true - common endpoint present, false - not connected
158 */
159bool BoundaryLineSet::IsConnectedTo(class BoundaryLineSet *line)
160{
161 if ((endpoints[0] == line->endpoints[0]) || (endpoints[1] == line->endpoints[0]) || (endpoints[0] == line->endpoints[1]) || (endpoints[1] == line->endpoints[1]))
162 return true;
163 else
164 return false;
165};
166
167/** Checks whether the adjacent triangles of a baseline are convex or not.
168 * We sum the two angles of each normal vector with a ficticious normnal vector from this baselinbe pointing outwards.
169 * If greater/equal M_PI than we are convex.
170 * \param *out output stream for debugging
171 * \return true - triangles are convex, false - concave or less than two triangles connected
172 */
173bool BoundaryLineSet::CheckConvexityCriterion(ofstream *out)
174{
175 Vector BaseLineCenter, BaseLineNormal, BaseLine, helper[2], NormalCheck;
176 // get the two triangles
177 if (triangles.size() != 2) {
178 *out << Verbose(1) << "ERROR: Baseline " << *this << " is connect to less than two triangles, Tesselation incomplete!" << endl;
179 return false;
180 }
181 // check normal vectors
182 // have a normal vector on the base line pointing outwards
183 *out << Verbose(3) << "INFO: " << *this << " has vectors at " << *(endpoints[0]->node->node) << " and at " << *(endpoints[1]->node->node) << "." << endl;
184 BaseLineCenter.CopyVector(endpoints[0]->node->node);
185 BaseLineCenter.AddVector(endpoints[1]->node->node);
186 BaseLineCenter.Scale(1./2.);
187 BaseLine.CopyVector(endpoints[0]->node->node);
188 BaseLine.SubtractVector(endpoints[1]->node->node);
189 *out << Verbose(3) << "INFO: Baseline is " << BaseLine << " and its center is at " << BaseLineCenter << "." << endl;
190
191 BaseLineNormal.Zero();
192 NormalCheck.Zero();
193 double sign = -1.;
194 int i=0;
195 class BoundaryPointSet *node = NULL;
196 for(TriangleMap::iterator runner = triangles.begin(); runner != triangles.end(); runner++) {
197 *out << Verbose(3) << "INFO: NormalVector of " << *(runner->second) << " is " << runner->second->NormalVector << "." << endl;
198 NormalCheck.AddVector(&runner->second->NormalVector);
199 NormalCheck.Scale(sign);
200 sign = -sign;
201 BaseLineNormal.SubtractVector(&runner->second->NormalVector); // we subtract as BaseLineNormal has to point inward in direction of [pi,2pi]
202 node = runner->second->GetThirdEndpoint(this);
203 if (node != NULL) {
204 *out << Verbose(3) << "INFO: Third node for triangle " << *(runner->second) << " is " << *node << " at " << *(node->node->node) << "." << endl;
205 helper[i].CopyVector(node->node->node);
206 helper[i].SubtractVector(&BaseLineCenter);
207 helper[i].MakeNormalVector(&BaseLine); // we want to compare the triangle's heights' angles!
208 *out << Verbose(4) << "INFO: Height vector with respect to baseline is " << helper[i] << "." << endl;
209 i++;
210 } else {
211 *out << Verbose(2) << "WARNING: I cannot find third node in triangle, something's wrong." << endl;
212 return true;
213 }
214 }
215 *out << Verbose(3) << "INFO: BaselineNormal is " << BaseLineNormal << "." << endl;
216 if (NormalCheck.NormSquared() < MYEPSILON) {
217 *out << Verbose(3) << "Normalvectors of both triangles are the same: convex." << endl;
218 return true;
219 }
220 double angle = GetAngle(helper[0], helper[1], BaseLineNormal);
221 if ((angle - M_PI) > -MYEPSILON)
222 return true;
223 else
224 return false;
225}
226
227/** Checks whether point is any of the two endpoints this line contains.
228 * \param *point point to test
229 * \return true - point is of the line, false - is not
230 */
231bool BoundaryLineSet::ContainsBoundaryPoint(class BoundaryPointSet *point)
232{
233 for(int i=0;i<2;i++)
234 if (point == endpoints[i])
235 return true;
236 return false;
237};
238
239/** Returns other endpoint of the line.
240 * \param *point other endpoint
241 * \return NULL - if endpoint not contained in BoundaryLineSet, or pointer to BoundaryPointSet otherwise
242 */
243class BoundaryPointSet *BoundaryLineSet::GetOtherEndpoint(class BoundaryPointSet *point)
244{
245 if (endpoints[0] == point)
246 return endpoints[1];
247 else if (endpoints[1] == point)
248 return endpoints[0];
249 else
250 return NULL;
251};
252
253/** output operator for BoundaryLineSet.
254 * \param &ost output stream
255 * \param &a boundary line
256 */
257ostream & operator <<(ostream &ost, BoundaryLineSet &a)
258{
259 ost << "[" << a.Nr << "|" << a.endpoints[0]->node->Name << "," << a.endpoints[1]->node->Name << "]";
260 return ost;
261};
262
263// ======================================== Triangles on Boundary =================================
264
265/** Constructor for BoundaryTriangleSet.
266 */
267BoundaryTriangleSet::BoundaryTriangleSet()
268{
269 for (int i = 0; i < 3; i++)
270 {
271 endpoints[i] = NULL;
272 lines[i] = NULL;
273 }
274 Nr = -1;
275};
276
277/** Constructor for BoundaryTriangleSet with three lines.
278 * \param *line[3] lines that make up the triangle
279 * \param number number of triangle
280 */
281BoundaryTriangleSet::BoundaryTriangleSet(class BoundaryLineSet *line[3], int number)
282{
283 // set number
284 Nr = number;
285 // set lines
286 cout << Verbose(5) << "New triangle " << Nr << ":" << endl;
287 for (int i = 0; i < 3; i++)
288 {
289 lines[i] = line[i];
290 lines[i]->AddTriangle(this);
291 }
292 // get ascending order of endpoints
293 map<int, class BoundaryPointSet *> OrderMap;
294 for (int i = 0; i < 3; i++)
295 // for all three lines
296 for (int j = 0; j < 2; j++)
297 { // for both endpoints
298 OrderMap.insert(pair<int, class BoundaryPointSet *> (
299 line[i]->endpoints[j]->Nr, line[i]->endpoints[j]));
300 // and we don't care whether insertion fails
301 }
302 // set endpoints
303 int Counter = 0;
304 cout << Verbose(6) << " with end points ";
305 for (map<int, class BoundaryPointSet *>::iterator runner = OrderMap.begin(); runner
306 != OrderMap.end(); runner++)
307 {
308 endpoints[Counter] = runner->second;
309 cout << " " << *endpoints[Counter];
310 Counter++;
311 }
312 if (Counter < 3)
313 {
314 cerr << "ERROR! We have a triangle with only two distinct endpoints!"
315 << endl;
316 //exit(1);
317 }
318 cout << "." << endl;
319};
320
321/** Destructor of BoundaryTriangleSet.
322 * Removes itself from each of its lines' LineMap and removes them if necessary.
323 * \note When removing triangles from a class Tesselation, use RemoveTesselationTriangle()
324 */
325BoundaryTriangleSet::~BoundaryTriangleSet()
326{
327 for (int i = 0; i < 3; i++) {
328 if (lines[i] != NULL) {
329 if (lines[i]->triangles.erase(Nr))
330 cout << Verbose(5) << "Triangle Nr." << Nr << " erased in line " << *lines[i] << "." << endl;
331 if (lines[i]->triangles.empty()) {
332 cout << Verbose(5) << *lines[i] << " is no more attached to any triangle, erasing." << endl;
333 delete (lines[i]);
334 lines[i] = NULL;
335 }
336 }
337 }
338 cout << Verbose(5) << "Erasing triangle Nr." << Nr << " itself." << endl;
339};
340
341/** Calculates the normal vector for this triangle.
342 * Is made unique by comparison with \a OtherVector to point in the other direction.
343 * \param &OtherVector direction vector to make normal vector unique.
344 */
345void BoundaryTriangleSet::GetNormalVector(Vector &OtherVector)
346{
347 // get normal vector
348 NormalVector.MakeNormalVector(endpoints[0]->node->node, endpoints[1]->node->node, endpoints[2]->node->node);
349
350 // make it always point inward (any offset vector onto plane projected onto normal vector suffices)
351 if (NormalVector.Projection(&OtherVector) > 0)
352 NormalVector.Scale(-1.);
353};
354
355/** Finds the point on the triangle \a *BTS the line defined by \a *MolCenter and \a *x crosses through.
356 * We call Vector::GetIntersectionWithPlane() to receive the intersection point with the plane
357 * This we test if it's really on the plane and whether it's inside the triangle on the plane or not.
358 * The latter is done as follows: if it's really outside, then for any endpoint of the triangle and it's opposite
359 * base line, the intersection between the line from endpoint to intersection and the base line will have a Vector::NormSquared()
360 * smaller than the first line.
361 * \param *out output stream for debugging
362 * \param *MolCenter offset vector of line
363 * \param *x second endpoint of line, minus \a *MolCenter is directional vector of line
364 * \param *Intersection intersection on plane on return
365 * \return true - \a *Intersection contains intersection on plane defined by triangle, false - zero vector if outside of triangle.
366 */
367bool BoundaryTriangleSet::GetIntersectionInsideTriangle(ofstream *out, Vector *MolCenter, Vector *x, Vector *Intersection)
368{
369 Vector CrossPoint;
370 Vector helper;
371
372 if (!Intersection->GetIntersectionWithPlane(out, &NormalVector, endpoints[0]->node->node, MolCenter, x)) {
373 *out << Verbose(1) << "Alas! Intersection with plane failed - at least numerically - the intersection is not on the plane!" << endl;
374 return false;
375 }
376
377 // Calculate cross point between one baseline and the line from the third endpoint to intersection
378 int i=0;
379 do {
380 if (CrossPoint.GetIntersectionOfTwoLinesOnPlane(out, endpoints[i%3]->node->node, endpoints[(i+1)%3]->node->node, endpoints[(i+2)%3]->node->node, Intersection, &NormalVector)) {
381 helper.CopyVector(endpoints[(i+1)%3]->node->node);
382 helper.SubtractVector(endpoints[i%3]->node->node);
383 } else
384 i++;
385 if (i>2)
386 break;
387 } while (CrossPoint.NormSquared() < MYEPSILON);
388 if (i==3) {
389 *out << Verbose(1) << "ERROR: Could not find any cross points, something's utterly wrong here!" << endl;
390 exit(255);
391 }
392 CrossPoint.SubtractVector(endpoints[i%3]->node->node);
393
394 // check whether intersection is inside or not by comparing length of intersection and length of cross point
395 if ((CrossPoint.NormSquared() - helper.NormSquared()) > -MYEPSILON) { // inside
396 return true;
397 } else { // outside!
398 Intersection->Zero();
399 return false;
400 }
401};
402
403/** Checks whether lines is any of the three boundary lines this triangle contains.
404 * \param *line line to test
405 * \return true - line is of the triangle, false - is not
406 */
407bool BoundaryTriangleSet::ContainsBoundaryLine(class BoundaryLineSet *line)
408{
409 for(int i=0;i<3;i++)
410 if (line == lines[i])
411 return true;
412 return false;
413};
414
415/** Checks whether point is any of the three endpoints this triangle contains.
416 * \param *point point to test
417 * \return true - point is of the triangle, false - is not
418 */
419bool BoundaryTriangleSet::ContainsBoundaryPoint(class BoundaryPointSet *point)
420{
421 for(int i=0;i<3;i++)
422 if (point == endpoints[i])
423 return true;
424 return false;
425};
426
427/** Checks whether three given \a *Points coincide with triangle's endpoints.
428 * \param *Points[3] pointer to BoundaryPointSet
429 * \return true - is the very triangle, false - is not
430 */
431bool BoundaryTriangleSet::IsPresentTupel(class BoundaryPointSet *Points[3])
432{
433 return (((endpoints[0] == Points[0])
434 || (endpoints[0] == Points[1])
435 || (endpoints[0] == Points[2])
436 ) && (
437 (endpoints[1] == Points[0])
438 || (endpoints[1] == Points[1])
439 || (endpoints[1] == Points[2])
440 ) && (
441 (endpoints[2] == Points[0])
442 || (endpoints[2] == Points[1])
443 || (endpoints[2] == Points[2])
444
445 ));
446};
447
448/** Returns the endpoint which is not contained in the given \a *line.
449 * \param *line baseline defining two endpoints
450 * \return pointer third endpoint or NULL if line does not belong to triangle.
451 */
452class BoundaryPointSet *BoundaryTriangleSet::GetThirdEndpoint(class BoundaryLineSet *line)
453{
454 // sanity check
455 if (!ContainsBoundaryLine(line))
456 return NULL;
457 for(int i=0;i<3;i++)
458 if (!line->ContainsBoundaryPoint(endpoints[i]))
459 return endpoints[i];
460 // actually, that' impossible :)
461 return NULL;
462};
463
464/** Calculates the center point of the triangle.
465 * Is third of the sum of all endpoints.
466 * \param *center central point on return.
467 */
468void BoundaryTriangleSet::GetCenter(Vector *center)
469{
470 center->Zero();
471 for(int i=0;i<3;i++)
472 center->AddVector(endpoints[i]->node->node);
473 center->Scale(1./3.);
474}
475
476/** output operator for BoundaryTriangleSet.
477 * \param &ost output stream
478 * \param &a boundary triangle
479 */
480ostream &operator <<(ostream &ost, BoundaryTriangleSet &a)
481{
482 ost << "[" << a.Nr << "|" << a.endpoints[0]->node->Name << ","
483 << a.endpoints[1]->node->Name << "," << a.endpoints[2]->node->Name << "]";
484 return ost;
485};
486
487// =========================================================== class TESSELPOINT ===========================================
488
489/** Constructor of class TesselPoint.
490 */
491TesselPoint::TesselPoint()
492{
493 node = NULL;
494 nr = -1;
495 Name = NULL;
496};
497
498/** Destructor for class TesselPoint.
499 */
500TesselPoint::~TesselPoint()
501{
502 Free((void **)&Name, "TesselPoint::~TesselPoint: *Name");
503};
504
505/** Prints LCNode to screen.
506 */
507ostream & operator << (ostream &ost, const TesselPoint &a)
508{
509 ost << "[" << (a.Name) << "|" << &a << "]";
510 return ost;
511};
512
513/** Prints LCNode to screen.
514 */
515ostream & TesselPoint::operator << (ostream &ost)
516{
517 ost << "[" << (Name) << "|" << this << "]";
518 return ost;
519};
520
521
522// =========================================================== class POINTCLOUD ============================================
523
524/** Constructor of class PointCloud.
525 */
526PointCloud::PointCloud()
527{
528
529};
530
531/** Destructor for class PointCloud.
532 */
533PointCloud::~PointCloud()
534{
535
536};
537
538// ============================ CandidateForTesselation =============================
539
540/** Constructor of class CandidateForTesselation.
541 */
542CandidateForTesselation::CandidateForTesselation(TesselPoint *candidate, BoundaryLineSet* line, Vector OptCandidateCenter, Vector OtherOptCandidateCenter) {
543 point = candidate;
544 BaseLine = line;
545 OptCenter.CopyVector(&OptCandidateCenter);
546 OtherOptCenter.CopyVector(&OtherOptCandidateCenter);
547};
548
549/** Destructor for class CandidateForTesselation.
550 */
551CandidateForTesselation::~CandidateForTesselation() {
552 point = NULL;
553 BaseLine = NULL;
554};
555
556// =========================================================== class TESSELATION ===========================================
557
558/** Constructor of class Tesselation.
559 */
560Tesselation::Tesselation()
561{
562 PointsOnBoundaryCount = 0;
563 LinesOnBoundaryCount = 0;
564 TrianglesOnBoundaryCount = 0;
565 InternalPointer = PointsOnBoundary.begin();
566}
567;
568
569/** Destructor of class Tesselation.
570 * We have to free all points, lines and triangles.
571 */
572Tesselation::~Tesselation()
573{
574 cout << Verbose(1) << "Free'ing TesselStruct ... " << endl;
575 for (TriangleMap::iterator runner = TrianglesOnBoundary.begin(); runner != TrianglesOnBoundary.end(); runner++) {
576 if (runner->second != NULL) {
577 delete (runner->second);
578 runner->second = NULL;
579 } else
580 cerr << "ERROR: The triangle " << runner->first << " has already been free'd." << endl;
581 }
582}
583;
584
585/** PointCloud implementation of GetCenter
586 * Uses PointsOnBoundary and STL stuff.
587 */
588Vector * Tesselation::GetCenter(ofstream *out)
589{
590 Vector *Center = new Vector(0.,0.,0.);
591 int num=0;
592 for (GoToFirst(); (!IsEnd()); GoToNext()) {
593 Center->AddVector(GetPoint()->node);
594 num++;
595 }
596 Center->Scale(1./num);
597 return Center;
598};
599
600/** PointCloud implementation of GoPoint
601 * Uses PointsOnBoundary and STL stuff.
602 */
603TesselPoint * Tesselation::GetPoint()
604{
605 return (InternalPointer->second->node);
606};
607
608/** PointCloud implementation of GetTerminalPoint.
609 * Uses PointsOnBoundary and STL stuff.
610 */
611TesselPoint * Tesselation::GetTerminalPoint()
612{
613 PointMap::iterator Runner = PointsOnBoundary.end();
614 Runner--;
615 return (Runner->second->node);
616};
617
618/** PointCloud implementation of GoToNext.
619 * Uses PointsOnBoundary and STL stuff.
620 */
621void Tesselation::GoToNext()
622{
623 if (InternalPointer != PointsOnBoundary.end())
624 InternalPointer++;
625};
626
627/** PointCloud implementation of GoToPrevious.
628 * Uses PointsOnBoundary and STL stuff.
629 */
630void Tesselation::GoToPrevious()
631{
632 if (InternalPointer != PointsOnBoundary.begin())
633 InternalPointer--;
634};
635
636/** PointCloud implementation of GoToFirst.
637 * Uses PointsOnBoundary and STL stuff.
638 */
639void Tesselation::GoToFirst()
640{
641 InternalPointer = PointsOnBoundary.begin();
642};
643
644/** PointCloud implementation of GoToLast.
645 * Uses PointsOnBoundary and STL stuff.
646 */
647void Tesselation::GoToLast()
648{
649 InternalPointer = PointsOnBoundary.end();
650 InternalPointer--;
651};
652
653/** PointCloud implementation of IsEmpty.
654 * Uses PointsOnBoundary and STL stuff.
655 */
656bool Tesselation::IsEmpty()
657{
658 return (PointsOnBoundary.empty());
659};
660
661/** PointCloud implementation of IsLast.
662 * Uses PointsOnBoundary and STL stuff.
663 */
664bool Tesselation::IsEnd()
665{
666 return (InternalPointer == PointsOnBoundary.end());
667};
668
669
670/** Gueses first starting triangle of the convex envelope.
671 * We guess the starting triangle by taking the smallest distance between two points and looking for a fitting third.
672 * \param *out output stream for debugging
673 * \param PointsOnBoundary set of boundary points defining the convex envelope of the cluster
674 */
675void
676Tesselation::GuessStartingTriangle(ofstream *out)
677{
678 // 4b. create a starting triangle
679 // 4b1. create all distances
680 DistanceMultiMap DistanceMMap;
681 double distance, tmp;
682 Vector PlaneVector, TrialVector;
683 PointMap::iterator A, B, C; // three nodes of the first triangle
684 A = PointsOnBoundary.begin(); // the first may be chosen arbitrarily
685
686 // with A chosen, take each pair B,C and sort
687 if (A != PointsOnBoundary.end())
688 {
689 B = A;
690 B++;
691 for (; B != PointsOnBoundary.end(); B++)
692 {
693 C = B;
694 C++;
695 for (; C != PointsOnBoundary.end(); C++)
696 {
697 tmp = A->second->node->node->DistanceSquared(B->second->node->node);
698 distance = tmp * tmp;
699 tmp = A->second->node->node->DistanceSquared(C->second->node->node);
700 distance += tmp * tmp;
701 tmp = B->second->node->node->DistanceSquared(C->second->node->node);
702 distance += tmp * tmp;
703 DistanceMMap.insert(DistanceMultiMapPair(distance, pair<PointMap::iterator, PointMap::iterator> (B, C)));
704 }
705 }
706 }
707 // // listing distances
708 // *out << Verbose(1) << "Listing DistanceMMap:";
709 // for(DistanceMultiMap::iterator runner = DistanceMMap.begin(); runner != DistanceMMap.end(); runner++) {
710 // *out << " " << runner->first << "(" << *runner->second.first->second << ", " << *runner->second.second->second << ")";
711 // }
712 // *out << endl;
713 // 4b2. pick three baselines forming a triangle
714 // 1. we take from the smallest sum of squared distance as the base line BC (with peak A) onward as the triangle candidate
715 DistanceMultiMap::iterator baseline = DistanceMMap.begin();
716 for (; baseline != DistanceMMap.end(); baseline++)
717 {
718 // we take from the smallest sum of squared distance as the base line BC (with peak A) onward as the triangle candidate
719 // 2. next, we have to check whether all points reside on only one side of the triangle
720 // 3. construct plane vector
721 PlaneVector.MakeNormalVector(A->second->node->node,
722 baseline->second.first->second->node->node,
723 baseline->second.second->second->node->node);
724 *out << Verbose(2) << "Plane vector of candidate triangle is ";
725 PlaneVector.Output(out);
726 *out << endl;
727 // 4. loop over all points
728 double sign = 0.;
729 PointMap::iterator checker = PointsOnBoundary.begin();
730 for (; checker != PointsOnBoundary.end(); checker++)
731 {
732 // (neglecting A,B,C)
733 if ((checker == A) || (checker == baseline->second.first) || (checker
734 == baseline->second.second))
735 continue;
736 // 4a. project onto plane vector
737 TrialVector.CopyVector(checker->second->node->node);
738 TrialVector.SubtractVector(A->second->node->node);
739 distance = TrialVector.Projection(&PlaneVector);
740 if (fabs(distance) < 1e-4) // we need to have a small epsilon around 0 which is still ok
741 continue;
742 *out << Verbose(3) << "Projection of " << checker->second->node->Name
743 << " yields distance of " << distance << "." << endl;
744 tmp = distance / fabs(distance);
745 // 4b. Any have different sign to than before? (i.e. would lie outside convex hull with this starting triangle)
746 if ((sign != 0) && (tmp != sign))
747 {
748 // 4c. If so, break 4. loop and continue with next candidate in 1. loop
749 *out << Verbose(2) << "Current candidates: "
750 << A->second->node->Name << ","
751 << baseline->second.first->second->node->Name << ","
752 << baseline->second.second->second->node->Name << " leaves "
753 << checker->second->node->Name << " outside the convex hull."
754 << endl;
755 break;
756 }
757 else
758 { // note the sign for later
759 *out << Verbose(2) << "Current candidates: "
760 << A->second->node->Name << ","
761 << baseline->second.first->second->node->Name << ","
762 << baseline->second.second->second->node->Name << " leave "
763 << checker->second->node->Name << " inside the convex hull."
764 << endl;
765 sign = tmp;
766 }
767 // 4d. Check whether the point is inside the triangle (check distance to each node
768 tmp = checker->second->node->node->DistanceSquared(A->second->node->node);
769 int innerpoint = 0;
770 if ((tmp < A->second->node->node->DistanceSquared(
771 baseline->second.first->second->node->node)) && (tmp
772 < A->second->node->node->DistanceSquared(
773 baseline->second.second->second->node->node)))
774 innerpoint++;
775 tmp = checker->second->node->node->DistanceSquared(
776 baseline->second.first->second->node->node);
777 if ((tmp < baseline->second.first->second->node->node->DistanceSquared(
778 A->second->node->node)) && (tmp
779 < baseline->second.first->second->node->node->DistanceSquared(
780 baseline->second.second->second->node->node)))
781 innerpoint++;
782 tmp = checker->second->node->node->DistanceSquared(
783 baseline->second.second->second->node->node);
784 if ((tmp < baseline->second.second->second->node->node->DistanceSquared(
785 baseline->second.first->second->node->node)) && (tmp
786 < baseline->second.second->second->node->node->DistanceSquared(
787 A->second->node->node)))
788 innerpoint++;
789 // 4e. If so, break 4. loop and continue with next candidate in 1. loop
790 if (innerpoint == 3)
791 break;
792 }
793 // 5. come this far, all on same side? Then break 1. loop and construct triangle
794 if (checker == PointsOnBoundary.end())
795 {
796 *out << "Looks like we have a candidate!" << endl;
797 break;
798 }
799 }
800 if (baseline != DistanceMMap.end())
801 {
802 BPS[0] = baseline->second.first->second;
803 BPS[1] = baseline->second.second->second;
804 BLS[0] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
805 BPS[0] = A->second;
806 BPS[1] = baseline->second.second->second;
807 BLS[1] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
808 BPS[0] = baseline->second.first->second;
809 BPS[1] = A->second;
810 BLS[2] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
811
812 // 4b3. insert created triangle
813 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
814 TrianglesOnBoundary.insert(TrianglePair(TrianglesOnBoundaryCount, BTS));
815 TrianglesOnBoundaryCount++;
816 for (int i = 0; i < NDIM; i++)
817 {
818 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount, BTS->lines[i]));
819 LinesOnBoundaryCount++;
820 }
821
822 *out << Verbose(1) << "Starting triangle is " << *BTS << "." << endl;
823 }
824 else
825 {
826 *out << Verbose(1) << "No starting triangle found." << endl;
827 exit(255);
828 }
829}
830;
831
832/** Tesselates the convex envelope of a cluster from a single starting triangle.
833 * The starting triangle is made out of three baselines. Each line in the final tesselated cluster may belong to at most
834 * 2 triangles. Hence, we go through all current lines:
835 * -# if the lines contains to only one triangle
836 * -# We search all points in the boundary
837 * -# if the triangle is in forward direction of the baseline (at most 90 degrees angle between vector orthogonal to
838 * baseline in triangle plane pointing out of the triangle and normal vector of new triangle)
839 * -# if the triangle with the baseline and the current point has the smallest of angles (comparison between normal vectors)
840 * -# then we have a new triangle, whose baselines we again add (or increase their TriangleCount)
841 * \param *out output stream for debugging
842 * \param *configuration for IsAngstroem
843 * \param *cloud cluster of points
844 */
845void Tesselation::TesselateOnBoundary(ofstream *out, PointCloud *cloud)
846{
847 bool flag;
848 PointMap::iterator winner;
849 class BoundaryPointSet *peak = NULL;
850 double SmallestAngle, TempAngle;
851 Vector NormalVector, VirtualNormalVector, CenterVector, TempVector, helper, PropagationVector, *Center = NULL;
852 LineMap::iterator LineChecker[2];
853
854 Center = cloud->GetCenter(out);
855 // create a first tesselation with the given BoundaryPoints
856 do {
857 flag = false;
858 for (LineMap::iterator baseline = LinesOnBoundary.begin(); baseline != LinesOnBoundary.end(); baseline++)
859 if (baseline->second->triangles.size() == 1) {
860 // 5a. go through each boundary point if not _both_ edges between either endpoint of the current line and this point exist (and belong to 2 triangles)
861 SmallestAngle = M_PI;
862
863 // get peak point with respect to this base line's only triangle
864 BTS = baseline->second->triangles.begin()->second; // there is only one triangle so far
865 *out << Verbose(2) << "Current baseline is between " << *(baseline->second) << "." << endl;
866 for (int i = 0; i < 3; i++)
867 if ((BTS->endpoints[i] != baseline->second->endpoints[0]) && (BTS->endpoints[i] != baseline->second->endpoints[1]))
868 peak = BTS->endpoints[i];
869 *out << Verbose(3) << " and has peak " << *peak << "." << endl;
870
871 // prepare some auxiliary vectors
872 Vector BaseLineCenter, BaseLine;
873 BaseLineCenter.CopyVector(baseline->second->endpoints[0]->node->node);
874 BaseLineCenter.AddVector(baseline->second->endpoints[1]->node->node);
875 BaseLineCenter.Scale(1. / 2.); // points now to center of base line
876 BaseLine.CopyVector(baseline->second->endpoints[0]->node->node);
877 BaseLine.SubtractVector(baseline->second->endpoints[1]->node->node);
878
879 // offset to center of triangle
880 CenterVector.Zero();
881 for (int i = 0; i < 3; i++)
882 CenterVector.AddVector(BTS->endpoints[i]->node->node);
883 CenterVector.Scale(1. / 3.);
884 *out << Verbose(4) << "CenterVector of base triangle is " << CenterVector << endl;
885
886 // normal vector of triangle
887 NormalVector.CopyVector(Center);
888 NormalVector.SubtractVector(&CenterVector);
889 BTS->GetNormalVector(NormalVector);
890 NormalVector.CopyVector(&BTS->NormalVector);
891 *out << Verbose(4) << "NormalVector of base triangle is " << NormalVector << endl;
892
893 // vector in propagation direction (out of triangle)
894 // project center vector onto triangle plane (points from intersection plane-NormalVector to plane-CenterVector intersection)
895 PropagationVector.MakeNormalVector(&BaseLine, &NormalVector);
896 TempVector.CopyVector(&CenterVector);
897 TempVector.SubtractVector(baseline->second->endpoints[0]->node->node); // TempVector is vector on triangle plane pointing from one baseline egde towards center!
898 //*out << Verbose(2) << "Projection of propagation onto temp: " << PropagationVector.Projection(&TempVector) << "." << endl;
899 if (PropagationVector.Projection(&TempVector) > 0) // make sure normal propagation vector points outward from baseline
900 PropagationVector.Scale(-1.);
901 *out << Verbose(4) << "PropagationVector of base triangle is " << PropagationVector << endl;
902 winner = PointsOnBoundary.end();
903
904 // loop over all points and calculate angle between normal vector of new and present triangle
905 for (PointMap::iterator target = PointsOnBoundary.begin(); target != PointsOnBoundary.end(); target++) {
906 if ((target->second != baseline->second->endpoints[0]) && (target->second != baseline->second->endpoints[1])) { // don't take the same endpoints
907 *out << Verbose(3) << "Target point is " << *(target->second) << ":" << endl;
908
909 // first check direction, so that triangles don't intersect
910 VirtualNormalVector.CopyVector(target->second->node->node);
911 VirtualNormalVector.SubtractVector(&BaseLineCenter); // points from center of base line to target
912 VirtualNormalVector.ProjectOntoPlane(&NormalVector);
913 TempAngle = VirtualNormalVector.Angle(&PropagationVector);
914 *out << Verbose(4) << "VirtualNormalVector is " << VirtualNormalVector << " and PropagationVector is " << PropagationVector << "." << endl;
915 if (TempAngle > (M_PI/2.)) { // no bends bigger than Pi/2 (90 degrees)
916 *out << Verbose(4) << "Angle on triangle plane between propagation direction and base line to " << *(target->second) << " is " << TempAngle << ", bad direction!" << endl;
917 continue;
918 } else
919 *out << Verbose(4) << "Angle on triangle plane between propagation direction and base line to " << *(target->second) << " is " << TempAngle << ", good direction!" << endl;
920
921 // check first and second endpoint (if any connecting line goes to target has at least not more than 1 triangle)
922 LineChecker[0] = baseline->second->endpoints[0]->lines.find(target->first);
923 LineChecker[1] = baseline->second->endpoints[1]->lines.find(target->first);
924 if (((LineChecker[0] != baseline->second->endpoints[0]->lines.end()) && (LineChecker[0]->second->triangles.size() == 2))) {
925 *out << Verbose(4) << *(baseline->second->endpoints[0]) << " has line " << *(LineChecker[0]->second) << " to " << *(target->second) << " as endpoint with " << LineChecker[0]->second->triangles.size() << " triangles." << endl;
926 continue;
927 }
928 if (((LineChecker[1] != baseline->second->endpoints[1]->lines.end()) && (LineChecker[1]->second->triangles.size() == 2))) {
929 *out << Verbose(4) << *(baseline->second->endpoints[1]) << " has line " << *(LineChecker[1]->second) << " to " << *(target->second) << " as endpoint with " << LineChecker[1]->second->triangles.size() << " triangles." << endl;
930 continue;
931 }
932
933 // check whether the envisaged triangle does not already exist (if both lines exist and have same endpoint)
934 if ((((LineChecker[0] != baseline->second->endpoints[0]->lines.end()) && (LineChecker[1] != baseline->second->endpoints[1]->lines.end()) && (GetCommonEndpoint(LineChecker[0]->second, LineChecker[1]->second) == peak)))) {
935 *out << Verbose(4) << "Current target is peak!" << endl;
936 continue;
937 }
938
939 // check for linear dependence
940 TempVector.CopyVector(baseline->second->endpoints[0]->node->node);
941 TempVector.SubtractVector(target->second->node->node);
942 helper.CopyVector(baseline->second->endpoints[1]->node->node);
943 helper.SubtractVector(target->second->node->node);
944 helper.ProjectOntoPlane(&TempVector);
945 if (fabs(helper.NormSquared()) < MYEPSILON) {
946 *out << Verbose(4) << "Chosen set of vectors is linear dependent." << endl;
947 continue;
948 }
949
950 // in case NOT both were found, create virtually this triangle, get its normal vector, calculate angle
951 flag = true;
952 VirtualNormalVector.MakeNormalVector(baseline->second->endpoints[0]->node->node, baseline->second->endpoints[1]->node->node, target->second->node->node);
953 TempVector.CopyVector(baseline->second->endpoints[0]->node->node);
954 TempVector.AddVector(baseline->second->endpoints[1]->node->node);
955 TempVector.AddVector(target->second->node->node);
956 TempVector.Scale(1./3.);
957 TempVector.SubtractVector(Center);
958 // make it always point outward
959 if (VirtualNormalVector.Projection(&TempVector) < 0)
960 VirtualNormalVector.Scale(-1.);
961 // calculate angle
962 TempAngle = NormalVector.Angle(&VirtualNormalVector);
963 *out << Verbose(4) << "NormalVector is " << VirtualNormalVector << " and the angle is " << TempAngle << "." << endl;
964 if ((SmallestAngle - TempAngle) > MYEPSILON) { // set to new possible winner
965 SmallestAngle = TempAngle;
966 winner = target;
967 *out << Verbose(4) << "New winner " << *winner->second->node << " due to smaller angle between normal vectors." << endl;
968 } else if (fabs(SmallestAngle - TempAngle) < MYEPSILON) { // check the angle to propagation, both possible targets are in one plane! (their normals have same angle)
969 // hence, check the angles to some normal direction from our base line but in this common plane of both targets...
970 helper.CopyVector(target->second->node->node);
971 helper.SubtractVector(&BaseLineCenter);
972 helper.ProjectOntoPlane(&BaseLine);
973 // ...the one with the smaller angle is the better candidate
974 TempVector.CopyVector(target->second->node->node);
975 TempVector.SubtractVector(&BaseLineCenter);
976 TempVector.ProjectOntoPlane(&VirtualNormalVector);
977 TempAngle = TempVector.Angle(&helper);
978 TempVector.CopyVector(winner->second->node->node);
979 TempVector.SubtractVector(&BaseLineCenter);
980 TempVector.ProjectOntoPlane(&VirtualNormalVector);
981 if (TempAngle < TempVector.Angle(&helper)) {
982 TempAngle = NormalVector.Angle(&VirtualNormalVector);
983 SmallestAngle = TempAngle;
984 winner = target;
985 *out << Verbose(4) << "New winner " << *winner->second->node << " due to smaller angle " << TempAngle << " to propagation direction." << endl;
986 } else
987 *out << Verbose(4) << "Keeping old winner " << *winner->second->node << " due to smaller angle to propagation direction." << endl;
988 } else
989 *out << Verbose(4) << "Keeping old winner " << *winner->second->node << " due to smaller angle between normal vectors." << endl;
990 }
991 } // end of loop over all boundary points
992
993 // 5b. The point of the above whose triangle has the greatest angle with the triangle the current line belongs to (it only belongs to one, remember!): New triangle
994 if (winner != PointsOnBoundary.end()) {
995 *out << Verbose(2) << "Winning target point is " << *(winner->second) << " with angle " << SmallestAngle << "." << endl;
996 // create the lins of not yet present
997 BLS[0] = baseline->second;
998 // 5c. add lines to the line set if those were new (not yet part of a triangle), delete lines that belong to two triangles)
999 LineChecker[0] = baseline->second->endpoints[0]->lines.find(winner->first);
1000 LineChecker[1] = baseline->second->endpoints[1]->lines.find(winner->first);
1001 if (LineChecker[0] == baseline->second->endpoints[0]->lines.end()) { // create
1002 BPS[0] = baseline->second->endpoints[0];
1003 BPS[1] = winner->second;
1004 BLS[1] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1005 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount, BLS[1]));
1006 LinesOnBoundaryCount++;
1007 } else
1008 BLS[1] = LineChecker[0]->second;
1009 if (LineChecker[1] == baseline->second->endpoints[1]->lines.end()) { // create
1010 BPS[0] = baseline->second->endpoints[1];
1011 BPS[1] = winner->second;
1012 BLS[2] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1013 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount, BLS[2]));
1014 LinesOnBoundaryCount++;
1015 } else
1016 BLS[2] = LineChecker[1]->second;
1017 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
1018 BTS->GetCenter(&helper);
1019 helper.SubtractVector(Center);
1020 helper.Scale(-1);
1021 BTS->GetNormalVector(helper);
1022 TrianglesOnBoundary.insert(TrianglePair(TrianglesOnBoundaryCount, BTS));
1023 TrianglesOnBoundaryCount++;
1024 } else {
1025 *out << Verbose(1) << "I could not determine a winner for this baseline " << *(baseline->second) << "." << endl;
1026 }
1027
1028 // 5d. If the set of lines is not yet empty, go to 5. and continue
1029 } else
1030 *out << Verbose(2) << "Baseline candidate " << *(baseline->second) << " has a triangle count of " << baseline->second->triangles.size() << "." << endl;
1031 } while (flag);
1032
1033 // exit
1034 delete(Center);
1035};
1036
1037/** Inserts all points outside of the tesselated surface into it by adding new triangles.
1038 * \param *out output stream for debugging
1039 * \param *cloud cluster of points
1040 * \param *LC LinkedCell structure to find nearest point quickly
1041 * \return true - all straddling points insert, false - something went wrong
1042 */
1043bool Tesselation::InsertStraddlingPoints(ofstream *out, PointCloud *cloud, LinkedCell *LC)
1044{
1045 Vector Intersection, Normal;
1046 TesselPoint *Walker = NULL;
1047 Vector *Center = cloud->GetCenter(out);
1048 list<BoundaryTriangleSet*> *triangles = NULL;
1049
1050 *out << Verbose(1) << "Begin of InsertStraddlingPoints" << endl;
1051
1052 cloud->GoToFirst();
1053 while (!cloud->IsEnd()) { // we only have to go once through all points, as boundary can become only bigger
1054 LinkedCell BoundaryPoints(this, 5.);
1055 Walker = cloud->GetPoint();
1056 *out << Verbose(2) << "Current point is " << *Walker << "." << endl;
1057 // get the next triangle
1058 triangles = FindClosestTrianglesToPoint(out, Walker->node, &BoundaryPoints);
1059 if (triangles == NULL) {
1060 *out << Verbose(1) << "No triangles found, probably a tesselation point itself." << endl;
1061 cloud->GoToNext();
1062 continue;
1063 } else {
1064 BTS = triangles->front();
1065 }
1066 *out << Verbose(2) << "Closest triangle is " << *BTS << "." << endl;
1067 // get the intersection point
1068 if (BTS->GetIntersectionInsideTriangle(out, Center, Walker->node, &Intersection)) {
1069 *out << Verbose(2) << "We have an intersection at " << Intersection << "." << endl;
1070 // we have the intersection, check whether in- or outside of boundary
1071 if ((Center->DistanceSquared(Walker->node) - Center->DistanceSquared(&Intersection)) < -MYEPSILON) {
1072 // inside, next!
1073 *out << Verbose(2) << *Walker << " is inside wrt triangle " << *BTS << "." << endl;
1074 } else {
1075 // outside!
1076 *out << Verbose(2) << *Walker << " is outside wrt triangle " << *BTS << "." << endl;
1077 class BoundaryLineSet *OldLines[3], *NewLines[3];
1078 class BoundaryPointSet *OldPoints[3], *NewPoint;
1079 // store the three old lines and old points
1080 for (int i=0;i<3;i++) {
1081 OldLines[i] = BTS->lines[i];
1082 OldPoints[i] = BTS->endpoints[i];
1083 }
1084 Normal.CopyVector(&BTS->NormalVector);
1085 // add Walker to boundary points
1086 *out << Verbose(2) << "Adding " << *Walker << " to BoundaryPoints." << endl;
1087 if (AddBoundaryPoint(Walker,0))
1088 NewPoint = BPS[0];
1089 else
1090 continue;
1091 // remove triangle
1092 *out << Verbose(2) << "Erasing triangle " << *BTS << "." << endl;
1093 TrianglesOnBoundary.erase(BTS->Nr);
1094 delete(BTS);
1095 // create three new boundary lines
1096 for (int i=0;i<3;i++) {
1097 BPS[0] = NewPoint;
1098 BPS[1] = OldPoints[i];
1099 NewLines[i] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1100 *out << Verbose(3) << "Creating new line " << *NewLines[i] << "." << endl;
1101 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount, NewLines[i])); // no need for check for unique insertion as BPS[0] is definitely a new one
1102 LinesOnBoundaryCount++;
1103 }
1104 // create three new triangle with new point
1105 for (int i=0;i<3;i++) { // find all baselines
1106 BLS[0] = OldLines[i];
1107 int n = 1;
1108 for (int j=0;j<3;j++) {
1109 if (NewLines[j]->IsConnectedTo(BLS[0])) {
1110 if (n>2) {
1111 *out << Verbose(1) << "ERROR: " << BLS[0] << " connects to all of the new lines?!" << endl;
1112 return false;
1113 } else
1114 BLS[n++] = NewLines[j];
1115 }
1116 }
1117 // create the triangle
1118 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
1119 Normal.Scale(-1.);
1120 BTS->GetNormalVector(Normal);
1121 Normal.Scale(-1.);
1122 *out << Verbose(2) << "Created new triangle " << *BTS << "." << endl;
1123 TrianglesOnBoundary.insert(TrianglePair(TrianglesOnBoundaryCount, BTS));
1124 TrianglesOnBoundaryCount++;
1125 }
1126 }
1127 } else { // something is wrong with FindClosestTriangleToPoint!
1128 *out << Verbose(1) << "ERROR: The closest triangle did not produce an intersection!" << endl;
1129 return false;
1130 }
1131 cloud->GoToNext();
1132 }
1133
1134 // exit
1135 delete(Center);
1136 *out << Verbose(1) << "End of InsertStraddlingPoints" << endl;
1137 return true;
1138};
1139
1140/** Adds a point to the tesselation::PointsOnBoundary list.
1141 * \param *Walker point to add
1142 * \param n TesselStruct::BPS index to put pointer into
1143 * \return true - new point was added, false - point already present
1144 */
1145bool
1146Tesselation::AddBoundaryPoint(TesselPoint *Walker, int n)
1147{
1148 PointTestPair InsertUnique;
1149 BPS[n] = new class BoundaryPointSet(Walker);
1150 InsertUnique = PointsOnBoundary.insert(PointPair(Walker->nr, BPS[n]));
1151 if (InsertUnique.second) { // if new point was not present before, increase counter
1152 PointsOnBoundaryCount++;
1153 return true;
1154 } else {
1155 delete(BPS[n]);
1156 BPS[n] = InsertUnique.first->second;
1157 return false;
1158 }
1159}
1160;
1161
1162/** Adds point to Tesselation::PointsOnBoundary if not yet present.
1163 * Tesselation::TPS is set to either this new BoundaryPointSet or to the existing one of not unique.
1164 * @param Candidate point to add
1165 * @param n index for this point in Tesselation::TPS array
1166 */
1167void
1168Tesselation::AddTesselationPoint(TesselPoint* Candidate, int n)
1169{
1170 PointTestPair InsertUnique;
1171 TPS[n] = new class BoundaryPointSet(Candidate);
1172 InsertUnique = PointsOnBoundary.insert(PointPair(Candidate->nr, TPS[n]));
1173 if (InsertUnique.second) { // if new point was not present before, increase counter
1174 PointsOnBoundaryCount++;
1175 } else {
1176 delete TPS[n];
1177 cout << Verbose(3) << "Node " << *((InsertUnique.first)->second->node) << " is already present in PointsOnBoundary." << endl;
1178 TPS[n] = (InsertUnique.first)->second;
1179 }
1180}
1181;
1182
1183/** Function tries to add line from current Points in BPS to BoundaryLineSet.
1184 * If successful it raises the line count and inserts the new line into the BLS,
1185 * if unsuccessful, it writes the line which had been present into the BLS, deleting the new constructed one.
1186 * @param *a first endpoint
1187 * @param *b second endpoint
1188 * @param n index of Tesselation::BLS giving the line with both endpoints
1189 */
1190void Tesselation::AddTesselationLine(class BoundaryPointSet *a, class BoundaryPointSet *b, int n) {
1191 bool insertNewLine = true;
1192
1193 if (a->lines.find(b->node->nr) != a->lines.end()) {
1194 LineMap::iterator FindLine;
1195 pair<LineMap::iterator,LineMap::iterator> FindPair;
1196 FindPair = a->lines.equal_range(b->node->nr);
1197
1198 for (FindLine = FindPair.first; FindLine != FindPair.second; ++FindLine) {
1199 // If there is a line with less than two attached triangles, we don't need a new line.
1200 if (FindLine->second->triangles.size() < 2) {
1201 insertNewLine = false;
1202 cout << Verbose(3) << "Using existing line " << *FindLine->second << endl;
1203
1204 BPS[0] = FindLine->second->endpoints[0];
1205 BPS[1] = FindLine->second->endpoints[1];
1206 BLS[n] = FindLine->second;
1207
1208 break;
1209 }
1210 }
1211 }
1212
1213 if (insertNewLine) {
1214 AlwaysAddTesselationTriangleLine(a, b, n);
1215 }
1216}
1217;
1218
1219/**
1220 * Adds lines from each of the current points in the BPS to BoundaryLineSet.
1221 * Raises the line count and inserts the new line into the BLS.
1222 *
1223 * @param *a first endpoint
1224 * @param *b second endpoint
1225 * @param n index of Tesselation::BLS giving the line with both endpoints
1226 */
1227void Tesselation::AlwaysAddTesselationTriangleLine(class BoundaryPointSet *a, class BoundaryPointSet *b, int n)
1228{
1229 cout << Verbose(3) << "Adding line between " << *(a->node) << " and " << *(b->node) << "." << endl;
1230 BPS[0] = a;
1231 BPS[1] = b;
1232 BLS[n] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount); // this also adds the line to the local maps
1233 // add line to global map
1234 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount, BLS[n]));
1235 // increase counter
1236 LinesOnBoundaryCount++;
1237};
1238
1239/** Function tries to add Triangle just created to Triangle and remarks if already existent (Failure of algorithm).
1240 * Furthermore it adds the triangle to all of its lines, in order to recognize those which are saturated later.
1241 */
1242void Tesselation::AddTesselationTriangle()
1243{
1244 cout << Verbose(1) << "Adding triangle to global TrianglesOnBoundary map." << endl;
1245
1246 // add triangle to global map
1247 TrianglesOnBoundary.insert(TrianglePair(TrianglesOnBoundaryCount, BTS));
1248 TrianglesOnBoundaryCount++;
1249
1250 // NOTE: add triangle to local maps is done in constructor of BoundaryTriangleSet
1251};
1252
1253/** Removes a triangle from the tesselation.
1254 * Removes itself from the TriangleMap's of its lines, calls for them RemoveTriangleLine() if they are no more connected.
1255 * Removes itself from memory.
1256 * \param *triangle to remove
1257 */
1258void Tesselation::RemoveTesselationTriangle(class BoundaryTriangleSet *triangle)
1259{
1260 if (triangle == NULL)
1261 return;
1262 for (int i = 0; i < 3; i++) {
1263 if (triangle->lines[i] != NULL) {
1264 cout << Verbose(5) << "Removing triangle Nr." << triangle->Nr << " in line " << *triangle->lines[i] << "." << endl;
1265 triangle->lines[i]->triangles.erase(triangle->Nr);
1266 if (triangle->lines[i]->triangles.empty()) {
1267 cout << Verbose(5) << *triangle->lines[i] << " is no more attached to any triangle, erasing." << endl;
1268 RemoveTesselationLine(triangle->lines[i]);
1269 triangle->lines[i] = NULL;
1270 } else
1271 cout << Verbose(5) << *triangle->lines[i] << " is still attached to another triangle." << endl;
1272 } else
1273 cerr << "ERROR: This line " << i << " has already been free'd." << endl;
1274 }
1275
1276 if (TrianglesOnBoundary.erase(triangle->Nr))
1277 cout << Verbose(5) << "Removing triangle Nr. " << triangle->Nr << "." << endl;
1278 delete(triangle);
1279};
1280
1281/** Removes a line from the tesselation.
1282 * Removes itself from each endpoints' LineMap, then removes itself from global LinesOnBoundary list and free's the line.
1283 * \param *line line to remove
1284 */
1285void Tesselation::RemoveTesselationLine(class BoundaryLineSet *line)
1286{
1287 int Numbers[2];
1288
1289 if (line == NULL)
1290 return;
1291 // get other endpoint number of finding copies of same line
1292 if (line->endpoints[1] != NULL)
1293 Numbers[0] = line->endpoints[1]->Nr;
1294 else
1295 Numbers[0] = -1;
1296 if (line->endpoints[0] != NULL)
1297 Numbers[1] = line->endpoints[0]->Nr;
1298 else
1299 Numbers[1] = -1;
1300
1301 for (int i = 0; i < 2; i++) {
1302 if (line->endpoints[i] != NULL) {
1303 if (Numbers[i] != -1) { // as there may be multiple lines with same endpoints, we have to go through each and find in the endpoint's line list this line set
1304 pair<LineMap::iterator, LineMap::iterator> erasor = line->endpoints[i]->lines.equal_range(Numbers[i]);
1305 for (LineMap::iterator Runner = erasor.first; Runner != erasor.second; Runner++)
1306 if ((*Runner).second == line) {
1307 cout << Verbose(5) << "Removing Line Nr. " << line->Nr << " in boundary point " << *line->endpoints[i] << "." << endl;
1308 line->endpoints[i]->lines.erase(Runner);
1309 break;
1310 }
1311 } else { // there's just a single line left
1312 if (line->endpoints[i]->lines.erase(line->Nr))
1313 cout << Verbose(5) << "Removing Line Nr. " << line->Nr << " in boundary point " << *line->endpoints[i] << "." << endl;
1314 }
1315 if (line->endpoints[i]->lines.empty()) {
1316 cout << Verbose(5) << *line->endpoints[i] << " has no more lines it's attached to, erasing." << endl;
1317 RemoveTesselationPoint(line->endpoints[i]);
1318 line->endpoints[i] = NULL;
1319 } else
1320 cout << Verbose(5) << *line->endpoints[i] << " has still lines it's attached to." << endl;
1321 } else
1322 cerr << "ERROR: Endpoint " << i << " has already been free'd." << endl;
1323 }
1324 if (!line->triangles.empty())
1325 cerr << "WARNING: Memory Leak! I " << *line << " am still connected to some triangles." << endl;
1326
1327 if (LinesOnBoundary.erase(line->Nr))
1328 cout << Verbose(5) << "Removing line Nr. " << line->Nr << "." << endl;
1329 delete(line);
1330};
1331
1332/** Removes a point from the tesselation.
1333 * Checks whether there are still lines connected, removes from global PointsOnBoundary list, then free's the point.
1334 * \note If a point should be removed, while keep the tesselated surface intact (i.e. closed), use RemovePointFromTesselatedSurface()
1335 * \param *point point to remove
1336 */
1337void Tesselation::RemoveTesselationPoint(class BoundaryPointSet *point)
1338{
1339 if (point == NULL)
1340 return;
1341 if (PointsOnBoundary.erase(point->Nr))
1342 cout << Verbose(5) << "Removing point Nr. " << point->Nr << "." << endl;
1343 delete(point);
1344};
1345
1346/** Checks whether the triangle consisting of the three points is already present.
1347 * Searches for the points in Tesselation::PointsOnBoundary and checks their
1348 * lines. If any of the three edges already has two triangles attached, false is
1349 * returned.
1350 * \param *out output stream for debugging
1351 * \param *Candidates endpoints of the triangle candidate
1352 * \return integer 0 if no triangle exists, 1 if one triangle exists, 2 if two
1353 * triangles exist which is the maximum for three points
1354 */
1355int Tesselation::CheckPresenceOfTriangle(ofstream *out, TesselPoint *Candidates[3]) {
1356 int adjacentTriangleCount = 0;
1357 class BoundaryPointSet *Points[3];
1358
1359 *out << Verbose(2) << "Begin of CheckPresenceOfTriangle" << endl;
1360 // builds a triangle point set (Points) of the end points
1361 for (int i = 0; i < 3; i++) {
1362 PointMap::iterator FindPoint = PointsOnBoundary.find(Candidates[i]->nr);
1363 if (FindPoint != PointsOnBoundary.end()) {
1364 Points[i] = FindPoint->second;
1365 } else {
1366 Points[i] = NULL;
1367 }
1368 }
1369
1370 // checks lines between the points in the Points for their adjacent triangles
1371 for (int i = 0; i < 3; i++) {
1372 if (Points[i] != NULL) {
1373 for (int j = i; j < 3; j++) {
1374 if (Points[j] != NULL) {
1375 LineMap::iterator FindLine = Points[i]->lines.find(Points[j]->node->nr);
1376 for (; (FindLine != Points[i]->lines.end()) && (FindLine->first == Points[j]->node->nr); FindLine++) {
1377 TriangleMap *triangles = &FindLine->second->triangles;
1378 *out << Verbose(3) << "Current line is " << FindLine->first << ": " << *(FindLine->second) << " with triangles " << triangles << "." << endl;
1379 for (TriangleMap::iterator FindTriangle = triangles->begin(); FindTriangle != triangles->end(); FindTriangle++) {
1380 if (FindTriangle->second->IsPresentTupel(Points)) {
1381 adjacentTriangleCount++;
1382 }
1383 }
1384 *out << Verbose(3) << "end." << endl;
1385 }
1386 // Only one of the triangle lines must be considered for the triangle count.
1387 *out << Verbose(2) << "Found " << adjacentTriangleCount << " adjacent triangles for the point set." << endl;
1388 return adjacentTriangleCount;
1389 }
1390 }
1391 }
1392 }
1393
1394 *out << Verbose(2) << "Found " << adjacentTriangleCount << " adjacent triangles for the point set." << endl;
1395 *out << Verbose(2) << "End of CheckPresenceOfTriangle" << endl;
1396 return adjacentTriangleCount;
1397};
1398
1399
1400/** Finds the starting triangle for FindNonConvexBorder().
1401 * Looks at the outermost point per axis, then FindSecondPointForTesselation()
1402 * for the second and FindNextSuitablePointViaAngleOfSphere() for the third
1403 * point are called.
1404 * \param *out output stream for debugging
1405 * \param RADIUS radius of virtual rolling sphere
1406 * \param *LC LinkedCell structure with neighbouring TesselPoint's
1407 */
1408void Tesselation::FindStartingTriangle(ofstream *out, const double RADIUS, LinkedCell *LC)
1409{
1410 cout << Verbose(1) << "Begin of FindStartingTriangle\n";
1411 int i = 0;
1412 LinkedNodes *List = NULL;
1413 TesselPoint* FirstPoint = NULL;
1414 TesselPoint* SecondPoint = NULL;
1415 TesselPoint* MaxPoint[NDIM];
1416 double maxCoordinate[NDIM];
1417 Vector Oben;
1418 Vector helper;
1419 Vector Chord;
1420 Vector SearchDirection;
1421
1422 Oben.Zero();
1423
1424 for (i = 0; i < 3; i++) {
1425 MaxPoint[i] = NULL;
1426 maxCoordinate[i] = -1;
1427 }
1428
1429 // 1. searching topmost point with respect to each axis
1430 for (int i=0;i<NDIM;i++) { // each axis
1431 LC->n[i] = LC->N[i]-1; // current axis is topmost cell
1432 for (LC->n[(i+1)%NDIM]=0;LC->n[(i+1)%NDIM]<LC->N[(i+1)%NDIM];LC->n[(i+1)%NDIM]++)
1433 for (LC->n[(i+2)%NDIM]=0;LC->n[(i+2)%NDIM]<LC->N[(i+2)%NDIM];LC->n[(i+2)%NDIM]++) {
1434 List = LC->GetCurrentCell();
1435 //cout << Verbose(2) << "Current cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << " with No. " << LC->index << "." << endl;
1436 if (List != NULL) {
1437 for (LinkedNodes::iterator Runner = List->begin();Runner != List->end();Runner++) {
1438 if ((*Runner)->node->x[i] > maxCoordinate[i]) {
1439 cout << Verbose(2) << "New maximal for axis " << i << " node is " << *(*Runner) << " at " << *(*Runner)->node << "." << endl;
1440 maxCoordinate[i] = (*Runner)->node->x[i];
1441 MaxPoint[i] = (*Runner);
1442 }
1443 }
1444 } else {
1445 cerr << "ERROR: The current cell " << LC->n[0] << "," << LC->n[1] << "," << LC->n[2] << " is invalid!" << endl;
1446 }
1447 }
1448 }
1449
1450 cout << Verbose(2) << "Found maximum coordinates: ";
1451 for (int i=0;i<NDIM;i++)
1452 cout << i << ": " << *MaxPoint[i] << "\t";
1453 cout << endl;
1454
1455 BTS = NULL;
1456 CandidateList *OptCandidates = new CandidateList();
1457 for (int k=0;k<NDIM;k++) {
1458 Oben.x[k] = 1.;
1459 FirstPoint = MaxPoint[k];
1460 cout << Verbose(1) << "Coordinates of start node at " << *FirstPoint->node << "." << endl;
1461
1462 double ShortestAngle;
1463 TesselPoint* OptCandidate = NULL;
1464 ShortestAngle = 999999.; // This will contain the angle, which will be always positive (when looking for second point), when looking for third point this will be the quadrant.
1465
1466 FindSecondPointForTesselation(FirstPoint, NULL, Oben, OptCandidate, &ShortestAngle, RADIUS, LC); // we give same point as next candidate as its bonds are looked into in find_second_...
1467 SecondPoint = OptCandidate;
1468 if (SecondPoint == NULL) // have we found a second point?
1469 continue;
1470 else
1471 cout << Verbose(1) << "Found second point is at " << *SecondPoint->node << ".\n";
1472
1473 helper.CopyVector(FirstPoint->node);
1474 helper.SubtractVector(SecondPoint->node);
1475 helper.Normalize();
1476 Oben.ProjectOntoPlane(&helper);
1477 Oben.Normalize();
1478 helper.VectorProduct(&Oben);
1479 ShortestAngle = 2.*M_PI; // This will indicate the quadrant.
1480
1481 Chord.CopyVector(FirstPoint->node); // bring into calling function
1482 Chord.SubtractVector(SecondPoint->node);
1483 double radius = Chord.ScalarProduct(&Chord);
1484 double CircleRadius = sqrt(RADIUS*RADIUS - radius/4.);
1485 helper.CopyVector(&Oben);
1486 helper.Scale(CircleRadius);
1487 // Now, oben and helper are two orthonormalized vectors in the plane defined by Chord (not normalized)
1488
1489 // look in one direction of baseline for initial candidate
1490 SearchDirection.MakeNormalVector(&Chord, &Oben); // whether we look "left" first or "right" first is not important ...
1491
1492 // adding point 1 and point 2 and add the line between them
1493 AddTesselationPoint(FirstPoint, 0);
1494 AddTesselationPoint(SecondPoint, 1);
1495 AddTesselationLine(TPS[0], TPS[1], 0);
1496
1497 //cout << Verbose(2) << "INFO: OldSphereCenter is at " << helper << ".\n";
1498 FindThirdPointForTesselation(
1499 Oben, SearchDirection, helper, BLS[0], NULL, *&OptCandidates, &ShortestAngle, RADIUS, LC
1500 );
1501 cout << Verbose(1) << "List of third Points is ";
1502 for (CandidateList::iterator it = OptCandidates->begin(); it != OptCandidates->end(); ++it) {
1503 cout << " " << *(*it)->point;
1504 }
1505 cout << endl;
1506
1507 for (CandidateList::iterator it = OptCandidates->begin(); it != OptCandidates->end(); ++it) {
1508 // add third triangle point
1509 AddTesselationPoint((*it)->point, 2);
1510 // add the second and third line
1511 AddTesselationLine(TPS[1], TPS[2], 1);
1512 AddTesselationLine(TPS[0], TPS[2], 2);
1513 // ... and triangles to the Maps of the Tesselation class
1514 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
1515 AddTesselationTriangle();
1516 // ... and calculate its normal vector (with correct orientation)
1517 (*it)->OptCenter.Scale(-1.);
1518 cout << Verbose(2) << "Anti-Oben is currently " << (*it)->OptCenter << "." << endl;
1519 BTS->GetNormalVector((*it)->OptCenter); // vector to compare with should point inwards
1520 cout << Verbose(0) << "==> Found starting triangle consists of " << *FirstPoint << ", " << *SecondPoint << " and "
1521 << *(*it)->point << " with normal vector " << BTS->NormalVector << ".\n";
1522
1523 // if we do not reach the end with the next step of iteration, we need to setup a new first line
1524 if (it != OptCandidates->end()--) {
1525 FirstPoint = (*it)->BaseLine->endpoints[0]->node;
1526 SecondPoint = (*it)->point;
1527 // adding point 1 and point 2 and the line between them
1528 AddTesselationPoint(FirstPoint, 0);
1529 AddTesselationPoint(SecondPoint, 1);
1530 AddTesselationLine(TPS[0], TPS[1], 0);
1531 }
1532 cout << Verbose(2) << "Projection is " << BTS->NormalVector.Projection(&Oben) << "." << endl;
1533 }
1534 if (BTS != NULL) // we have created one starting triangle
1535 break;
1536 else {
1537 // remove all candidates from the list and then the list itself
1538 class CandidateForTesselation *remover = NULL;
1539 for (CandidateList::iterator it = OptCandidates->begin(); it != OptCandidates->end(); ++it) {
1540 remover = *it;
1541 delete(remover);
1542 }
1543 OptCandidates->clear();
1544 }
1545 }
1546
1547 // remove all candidates from the list and then the list itself
1548 class CandidateForTesselation *remover = NULL;
1549 for (CandidateList::iterator it = OptCandidates->begin(); it != OptCandidates->end(); ++it) {
1550 remover = *it;
1551 delete(remover);
1552 }
1553 delete(OptCandidates);
1554 cout << Verbose(1) << "End of FindStartingTriangle\n";
1555};
1556
1557
1558/** This function finds a triangle to a line, adjacent to an existing one.
1559 * @param out output stream for debugging
1560 * @param Line current baseline to search from
1561 * @param T current triangle which \a Line is edge of
1562 * @param RADIUS radius of the rolling ball
1563 * @param N number of found triangles
1564 * @param *LC LinkedCell structure with neighbouring points
1565 */
1566bool Tesselation::FindNextSuitableTriangle(ofstream *out, BoundaryLineSet &Line, BoundaryTriangleSet &T, const double& RADIUS, int N, LinkedCell *LC)
1567{
1568 cout << Verbose(0) << "Begin of FindNextSuitableTriangle\n";
1569 bool result = true;
1570 CandidateList *OptCandidates = new CandidateList();
1571
1572 Vector CircleCenter;
1573 Vector CirclePlaneNormal;
1574 Vector OldSphereCenter;
1575 Vector SearchDirection;
1576 Vector helper;
1577 TesselPoint *ThirdNode = NULL;
1578 LineMap::iterator testline;
1579 double ShortestAngle = 2.*M_PI; // This will indicate the quadrant.
1580 double radius, CircleRadius;
1581
1582 cout << Verbose(1) << "Current baseline is " << Line << " of triangle " << T << "." << endl;
1583 for (int i=0;i<3;i++)
1584 if ((T.endpoints[i]->node != Line.endpoints[0]->node) && (T.endpoints[i]->node != Line.endpoints[1]->node))
1585 ThirdNode = T.endpoints[i]->node;
1586
1587 // construct center of circle
1588 CircleCenter.CopyVector(Line.endpoints[0]->node->node);
1589 CircleCenter.AddVector(Line.endpoints[1]->node->node);
1590 CircleCenter.Scale(0.5);
1591
1592 // construct normal vector of circle
1593 CirclePlaneNormal.CopyVector(Line.endpoints[0]->node->node);
1594 CirclePlaneNormal.SubtractVector(Line.endpoints[1]->node->node);
1595
1596 // calculate squared radius of circle
1597 radius = CirclePlaneNormal.ScalarProduct(&CirclePlaneNormal);
1598 if (radius/4. < RADIUS*RADIUS) {
1599 CircleRadius = RADIUS*RADIUS - radius/4.;
1600 CirclePlaneNormal.Normalize();
1601 cout << Verbose(2) << "INFO: CircleCenter is at " << CircleCenter << ", CirclePlaneNormal is " << CirclePlaneNormal << " with circle radius " << sqrt(CircleRadius) << "." << endl;
1602
1603 // construct old center
1604 GetCenterofCircumcircle(&OldSphereCenter, T.endpoints[0]->node->node, T.endpoints[1]->node->node, T.endpoints[2]->node->node);
1605 helper.CopyVector(&T.NormalVector); // normal vector ensures that this is correct center of the two possible ones
1606 radius = Line.endpoints[0]->node->node->DistanceSquared(&OldSphereCenter);
1607 helper.Scale(sqrt(RADIUS*RADIUS - radius));
1608 OldSphereCenter.AddVector(&helper);
1609 OldSphereCenter.SubtractVector(&CircleCenter);
1610 //cout << Verbose(2) << "INFO: OldSphereCenter is at " << OldSphereCenter << "." << endl;
1611
1612 // construct SearchDirection
1613 SearchDirection.MakeNormalVector(&T.NormalVector, &CirclePlaneNormal);
1614 helper.CopyVector(Line.endpoints[0]->node->node);
1615 helper.SubtractVector(ThirdNode->node);
1616 if (helper.ScalarProduct(&SearchDirection) < -HULLEPSILON)// ohoh, SearchDirection points inwards!
1617 SearchDirection.Scale(-1.);
1618 SearchDirection.ProjectOntoPlane(&OldSphereCenter);
1619 SearchDirection.Normalize();
1620 cout << Verbose(2) << "INFO: SearchDirection is " << SearchDirection << "." << endl;
1621 if (fabs(OldSphereCenter.ScalarProduct(&SearchDirection)) > HULLEPSILON) {
1622 // rotated the wrong way!
1623 cerr << "ERROR: SearchDirection and RelativeOldSphereCenter are still not orthogonal!" << endl;
1624 }
1625
1626 // add third point
1627 FindThirdPointForTesselation(
1628 T.NormalVector, SearchDirection, OldSphereCenter, &Line, ThirdNode, OptCandidates,
1629 &ShortestAngle, RADIUS, LC
1630 );
1631
1632 } else {
1633 cout << Verbose(1) << "Circumcircle for base line " << Line << " and base triangle " << T << " is too big!" << endl;
1634 }
1635
1636 if (OptCandidates->begin() == OptCandidates->end()) {
1637 cerr << "WARNING: Could not find a suitable candidate." << endl;
1638 return false;
1639 }
1640 cout << Verbose(1) << "Third Points are ";
1641 for (CandidateList::iterator it = OptCandidates->begin(); it != OptCandidates->end(); ++it) {
1642 cout << " " << *(*it)->point;
1643 }
1644 cout << endl;
1645
1646 BoundaryLineSet *BaseRay = &Line;
1647 for (CandidateList::iterator it = OptCandidates->begin(); it != OptCandidates->end(); ++it) {
1648 cout << Verbose(1) << " Third point candidate is " << *(*it)->point
1649 << " with circumsphere's center at " << (*it)->OptCenter << "." << endl;
1650 cout << Verbose(1) << " Baseline is " << *BaseRay << endl;
1651
1652 // check whether all edges of the new triangle still have space for one more triangle (i.e. TriangleCount <2)
1653 TesselPoint *PointCandidates[3];
1654 PointCandidates[0] = (*it)->point;
1655 PointCandidates[1] = BaseRay->endpoints[0]->node;
1656 PointCandidates[2] = BaseRay->endpoints[1]->node;
1657 int existentTrianglesCount = CheckPresenceOfTriangle(out, PointCandidates);
1658
1659 BTS = NULL;
1660 // If there is no triangle, add it regularly.
1661 if (existentTrianglesCount == 0) {
1662 AddTesselationPoint((*it)->point, 0);
1663 AddTesselationPoint(BaseRay->endpoints[0]->node, 1);
1664 AddTesselationPoint(BaseRay->endpoints[1]->node, 2);
1665
1666 if (CheckLineCriteriaForDegeneratedTriangle(TPS)) {
1667 AddTesselationLine(TPS[0], TPS[1], 0);
1668 AddTesselationLine(TPS[0], TPS[2], 1);
1669 AddTesselationLine(TPS[1], TPS[2], 2);
1670
1671 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
1672 AddTesselationTriangle();
1673 (*it)->OptCenter.Scale(-1.);
1674 BTS->GetNormalVector((*it)->OptCenter);
1675 (*it)->OptCenter.Scale(-1.);
1676
1677 cout << "--> New triangle with " << *BTS << " and normal vector " << BTS->NormalVector
1678 << " for this triangle ... " << endl;
1679 //cout << Verbose(1) << "We have "<< TrianglesOnBoundaryCount << " for line " << *BaseRay << "." << endl;
1680 } else {
1681 cout << Verbose(1) << "WARNING: This triangle consisting of ";
1682 cout << *(*it)->point << ", ";
1683 cout << *BaseRay->endpoints[0]->node << " and ";
1684 cout << *BaseRay->endpoints[1]->node << " ";
1685 cout << "exists and is not added, as it does not seem helpful!" << endl;
1686 result = false;
1687 }
1688 } else if (existentTrianglesCount == 1) { // If there is a planar region within the structure, we need this triangle a second time.
1689 AddTesselationPoint((*it)->point, 0);
1690 AddTesselationPoint(BaseRay->endpoints[0]->node, 1);
1691 AddTesselationPoint(BaseRay->endpoints[1]->node, 2);
1692
1693 // We demand that at most one new degenerate line is created and that this line also already exists (which has to be the case due to existentTrianglesCount == 1)
1694 // i.e. at least one of the three lines must be present with TriangleCount <= 1
1695 if (CheckLineCriteriaForDegeneratedTriangle(TPS)) {
1696 AddTesselationLine(TPS[0], TPS[1], 0);
1697 AddTesselationLine(TPS[0], TPS[2], 1);
1698 AddTesselationLine(TPS[1], TPS[2], 2);
1699
1700 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
1701 AddTesselationTriangle(); // add to global map
1702
1703 (*it)->OtherOptCenter.Scale(-1.);
1704 BTS->GetNormalVector((*it)->OtherOptCenter);
1705 (*it)->OtherOptCenter.Scale(-1.);
1706
1707 cout << "--> WARNING: Special new triangle with " << *BTS << " and normal vector " << BTS->NormalVector
1708 << " for this triangle ... " << endl;
1709 cout << Verbose(1) << "We have "<< BaseRay->triangles.size() << " for line " << BaseRay << "." << endl;
1710 } else {
1711 cout << Verbose(1) << "WARNING: This triangle consisting of ";
1712 cout << *(*it)->point << ", ";
1713 cout << *BaseRay->endpoints[0]->node << " and ";
1714 cout << *BaseRay->endpoints[1]->node << " ";
1715 cout << "exists and is not added, as it does not seem helpful!" << endl;
1716 result = false;
1717 }
1718 } else {
1719 cout << Verbose(1) << "This triangle consisting of ";
1720 cout << *(*it)->point << ", ";
1721 cout << *BaseRay->endpoints[0]->node << " and ";
1722 cout << *BaseRay->endpoints[1]->node << " ";
1723 cout << "is invalid!" << endl;
1724 result = false;
1725 }
1726
1727 // set baseline to new ray from ref point (here endpoints[0]->node) to current candidate (here (*it)->point))
1728 BaseRay = BLS[0];
1729 }
1730
1731 // remove all candidates from the list and then the list itself
1732 class CandidateForTesselation *remover = NULL;
1733 for (CandidateList::iterator it = OptCandidates->begin(); it != OptCandidates->end(); ++it) {
1734 remover = *it;
1735 delete(remover);
1736 }
1737 delete(OptCandidates);
1738 cout << Verbose(0) << "End of FindNextSuitableTriangle\n";
1739 return result;
1740};
1741
1742/** Checks whether the quadragon of the two triangles connect to \a *Base is convex.
1743 * We look whether the closest point on \a *Base with respect to the other baseline is outside
1744 * of the segment formed by both endpoints (concave) or not (convex).
1745 * \param *out output stream for debugging
1746 * \param *Base line to be flipped
1747 * \return NULL - concave, otherwise endpoint that makes it concave
1748 */
1749class BoundaryPointSet *Tesselation::IsConvexRectangle(ofstream *out, class BoundaryLineSet *Base)
1750{
1751 class BoundaryPointSet *Spot = NULL;
1752 class BoundaryLineSet *OtherBase;
1753 Vector *ClosestPoint[2];
1754
1755 int m=0;
1756 for(TriangleMap::iterator runner = Base->triangles.begin(); runner != Base->triangles.end(); runner++)
1757 for (int j=0;j<3;j++) // all of their endpoints and baselines
1758 if (!Base->ContainsBoundaryPoint(runner->second->endpoints[j])) // and neither of its endpoints
1759 BPS[m++] = runner->second->endpoints[j];
1760 OtherBase = new class BoundaryLineSet(BPS,-1);
1761
1762 *out << Verbose(3) << "INFO: Current base line is " << *Base << "." << endl;
1763 *out << Verbose(3) << "INFO: Other base line is " << *OtherBase << "." << endl;
1764
1765 // get the closest point on each line to the other line
1766 ClosestPoint[0] = GetClosestPointBetweenLine(out, Base, OtherBase);
1767 ClosestPoint[1] = GetClosestPointBetweenLine(out, OtherBase, Base);
1768
1769 // delete the temporary other base line
1770 delete(OtherBase);
1771
1772 // get the distance vector from Base line to OtherBase line
1773 Vector DistanceToIntersection, BaseLine;
1774 BaseLine.CopyVector(Base->endpoints[1]->node->node);
1775 BaseLine.SubtractVector(Base->endpoints[0]->node->node);
1776 DistanceToIntersection.CopyVector(ClosestPoint[0]);
1777 DistanceToIntersection.SubtractVector(Base->endpoints[0]->node->node);
1778 Spot = Base->endpoints[1];
1779 if (BaseLine.ScalarProduct(&DistanceToIntersection) < -MYEPSILON) {
1780 DistanceToIntersection.CopyVector(ClosestPoint[0]);
1781 DistanceToIntersection.SubtractVector(Base->endpoints[1]->node->node);
1782 Spot = Base->endpoints[0];
1783 if (BaseLine.ScalarProduct(&DistanceToIntersection) < -MYEPSILON) {
1784 *out << Verbose(3) << "ERROR: Something's very wrong here, both SKPs return negative?!" << endl;
1785 return NULL;
1786 }
1787 }
1788 if ((BaseLine.NormSquared() - DistanceToIntersection.NormSquared()) < -MYEPSILON) { // distance is smaller: concave
1789 *out << Verbose(3) << "INFO: Rectangle of triangles of base line " << *Base << " is concave: Base line squared length " << BaseLine.NormSquared() << " against Distance to intersection squared " << DistanceToIntersection.NormSquared() << "." << endl;
1790 return Spot;
1791 } else { // base line is longer: convex
1792 *out << Verbose(3) << "INFO: Rectangle of triangles of base line " << *Base << " is concave." << endl;
1793 return NULL;
1794 }
1795
1796};
1797
1798
1799/** For a given boundary line \a *Base and its two triangles, picks the central baseline that is "higher".
1800 * \param *out output stream for debugging
1801 * \param *Base line to be flipped
1802 * \return true - line was changed, false - same line as before
1803 */
1804bool Tesselation::PickFarthestofTwoBaselines(ofstream *out, class BoundaryLineSet *Base)
1805{
1806 class BoundaryLineSet *OtherBase;
1807 Vector *ClosestPoint[2];
1808
1809 int m=0;
1810 for(TriangleMap::iterator runner = Base->triangles.begin(); runner != Base->triangles.end(); runner++)
1811 for (int j=0;j<3;j++) // all of their endpoints and baselines
1812 if (!Base->ContainsBoundaryPoint(runner->second->endpoints[j])) // and neither of its endpoints
1813 BPS[m++] = runner->second->endpoints[j];
1814 OtherBase = new class BoundaryLineSet(BPS,-1);
1815
1816 *out << Verbose(3) << "INFO: Current base line is " << *Base << "." << endl;
1817 *out << Verbose(3) << "INFO: Other base line is " << *OtherBase << "." << endl;
1818
1819 // get the closest point on each line to the other line
1820 ClosestPoint[0] = GetClosestPointBetweenLine(out, Base, OtherBase);
1821 ClosestPoint[1] = GetClosestPointBetweenLine(out, OtherBase, Base);
1822
1823 // get the distance vector from Base line to OtherBase line
1824 Vector Distance;
1825 Distance.CopyVector(ClosestPoint[1]);
1826 Distance.SubtractVector(ClosestPoint[0]);
1827
1828 // delete the temporary other base line
1829 delete(OtherBase);
1830
1831 if (Distance.NormSquared() < MYEPSILON) { // check for intersection
1832 *out << Verbose(3) << "REJECT: Both lines have an intersection: Nothing to do." << endl;
1833 return false;
1834 } else { // check for sign against BaseLineNormal
1835 Vector BaseLineNormal;
1836 BaseLineNormal.Zero();
1837 if (Base->triangles.size() < 2) {
1838 *out << Verbose(2) << "ERROR: Less than two triangles are attached to this baseline!" << endl;
1839 return false;
1840 }
1841 for (TriangleMap::iterator runner = Base->triangles.begin(); runner != Base->triangles.end(); runner++) {
1842 *out << Verbose(4) << "INFO: Adding NormalVector " << runner->second->NormalVector << " of triangle " << *(runner->second) << "." << endl;
1843 BaseLineNormal.AddVector(&(runner->second->NormalVector));
1844 }
1845 BaseLineNormal.Scale(-1./2.); // has to point inside for BoundaryTriangleSet::GetNormalVector()
1846
1847 if (Distance.ScalarProduct(&BaseLineNormal) > MYEPSILON) { // Distance points outwards, hence OtherBase higher than Base -> flip
1848 *out << Verbose(2) << "ACCEPT: Other base line would be higher: Flipping baseline." << endl;
1849 FlipBaseline(out, Base);
1850 return true;
1851 } else { // Base higher than OtherBase -> do nothing
1852 *out << Verbose(2) << "REJECT: Base line is higher: Nothing to do." << endl;
1853 return false;
1854 }
1855 }
1856};
1857
1858/** Returns the closest point on \a *Base with respect to \a *OtherBase.
1859 * \param *out output stream for debugging
1860 * \param *Base reference line
1861 * \param *OtherBase other base line
1862 * \return Vector on reference line that has closest distance
1863 */
1864Vector * GetClosestPointBetweenLine(ofstream *out, class BoundaryLineSet *Base, class BoundaryLineSet *OtherBase)
1865{
1866 // construct the plane of the two baselines (i.e. take both their directional vectors)
1867 Vector Normal;
1868 Vector OtherBaseline;
1869 Normal.CopyVector(Base->endpoints[1]->node->node);
1870 Normal.SubtractVector(Base->endpoints[0]->node->node);
1871 OtherBaseline.CopyVector(OtherBase->endpoints[1]->node->node);
1872 OtherBaseline.SubtractVector(OtherBase->endpoints[0]->node->node);
1873 Normal.VectorProduct(&OtherBaseline);
1874 Normal.Normalize();
1875
1876 // project one offset point of OtherBase onto this plane
1877 Vector NewOffset;
1878 NewOffset.CopyVector(OtherBase->endpoints[0]->node->node);
1879 NewOffset.ProjectOntoPlane(&Normal);
1880 Vector NewDirection;
1881 NewDirection.CopyVector(OtherBase->endpoints[1]->node->node);
1882 NewDirection.ProjectOntoPlane(&Normal);
1883
1884 // calculate the intersection between this projected baseline and Base
1885 Vector *Intersection = new Vector;
1886 Intersection->GetIntersectionOfTwoLinesOnPlane(out, Base->endpoints[0]->node->node, Base->endpoints[1]->node->node, &NewOffset, &NewDirection, &Normal);
1887
1888 return Intersection;
1889};
1890
1891/** For a given baseline and its two connected triangles, flips the baseline.
1892 * I.e. we create the new baseline between the other two endpoints of these four
1893 * endpoints and reconstruct the two triangles accordingly.
1894 * \param *out output stream for debugging
1895 * \param *Base line to be flipped
1896 * \return true - flipping successful, false - something went awry
1897 */
1898bool Tesselation::FlipBaseline(ofstream *out, class BoundaryLineSet *Base)
1899{
1900 class BoundaryLineSet *OldLines[4], *NewLine;
1901 class BoundaryPointSet *OldPoints[2];
1902 Vector BaseLineNormal;
1903 int OldTriangleNrs[2], OldBaseLineNr;
1904 int i,m;
1905
1906 *out << Verbose(1) << "Begin of FlipBaseline" << endl;
1907
1908 // calculate NormalVector for later use
1909 BaseLineNormal.Zero();
1910 if (Base->triangles.size() < 2) {
1911 *out << Verbose(2) << "ERROR: Less than two triangles are attached to this baseline!" << endl;
1912 return false;
1913 }
1914 for (TriangleMap::iterator runner = Base->triangles.begin(); runner != Base->triangles.end(); runner++) {
1915 *out << Verbose(4) << "INFO: Adding NormalVector " << runner->second->NormalVector << " of triangle " << *(runner->second) << "." << endl;
1916 BaseLineNormal.AddVector(&(runner->second->NormalVector));
1917 }
1918 BaseLineNormal.Scale(-1./2.); // has to point inside for BoundaryTriangleSet::GetNormalVector()
1919
1920 // get the two triangles
1921 // gather four endpoints and four lines
1922 for (int j=0;j<4;j++)
1923 OldLines[j] = NULL;
1924 for (int j=0;j<2;j++)
1925 OldPoints[j] = NULL;
1926 i=0;
1927 m=0;
1928 *out << Verbose(3) << "The four old lines are: ";
1929 for(TriangleMap::iterator runner = Base->triangles.begin(); runner != Base->triangles.end(); runner++)
1930 for (int j=0;j<3;j++) // all of their endpoints and baselines
1931 if (runner->second->lines[j] != Base) { // pick not the central baseline
1932 OldLines[i++] = runner->second->lines[j];
1933 *out << *runner->second->lines[j] << "\t";
1934 }
1935 *out << endl;
1936 *out << Verbose(3) << "The two old points are: ";
1937 for(TriangleMap::iterator runner = Base->triangles.begin(); runner != Base->triangles.end(); runner++)
1938 for (int j=0;j<3;j++) // all of their endpoints and baselines
1939 if (!Base->ContainsBoundaryPoint(runner->second->endpoints[j])) { // and neither of its endpoints
1940 OldPoints[m++] = runner->second->endpoints[j];
1941 *out << *runner->second->endpoints[j] << "\t";
1942 }
1943 *out << endl;
1944
1945 // check whether everything is in place to create new lines and triangles
1946 if (i<4) {
1947 *out << Verbose(1) << "ERROR: We have not gathered enough baselines!" << endl;
1948 return false;
1949 }
1950 for (int j=0;j<4;j++)
1951 if (OldLines[j] == NULL) {
1952 *out << Verbose(1) << "ERROR: We have not gathered enough baselines!" << endl;
1953 return false;
1954 }
1955 for (int j=0;j<2;j++)
1956 if (OldPoints[j] == NULL) {
1957 *out << Verbose(1) << "ERROR: We have not gathered enough endpoints!" << endl;
1958 return false;
1959 }
1960
1961 // remove triangles and baseline removes itself
1962 *out << Verbose(3) << "INFO: Deleting baseline " << *Base << " from global list." << endl;
1963 OldBaseLineNr = Base->Nr;
1964 m=0;
1965 for(TriangleMap::iterator runner = Base->triangles.begin(); runner != Base->triangles.end(); runner++) {
1966 *out << Verbose(3) << "INFO: Deleting triangle " << *(runner->second) << "." << endl;
1967 OldTriangleNrs[m++] = runner->second->Nr;
1968 RemoveTesselationTriangle(runner->second);
1969 }
1970
1971 // construct new baseline (with same number as old one)
1972 BPS[0] = OldPoints[0];
1973 BPS[1] = OldPoints[1];
1974 NewLine = new class BoundaryLineSet(BPS, OldBaseLineNr);
1975 LinesOnBoundary.insert(LinePair(OldBaseLineNr, NewLine)); // no need for check for unique insertion as NewLine is definitely a new one
1976 *out << Verbose(3) << "INFO: Created new baseline " << *NewLine << "." << endl;
1977
1978 // construct new triangles with flipped baseline
1979 i=-1;
1980 if (OldLines[0]->IsConnectedTo(OldLines[2]))
1981 i=2;
1982 if (OldLines[0]->IsConnectedTo(OldLines[3]))
1983 i=3;
1984 if (i!=-1) {
1985 BLS[0] = OldLines[0];
1986 BLS[1] = OldLines[i];
1987 BLS[2] = NewLine;
1988 BTS = new class BoundaryTriangleSet(BLS, OldTriangleNrs[0]);
1989 BTS->GetNormalVector(BaseLineNormal);
1990 TrianglesOnBoundary.insert(TrianglePair(OldTriangleNrs[0], BTS));
1991 *out << Verbose(3) << "INFO: Created new triangle " << *BTS << "." << endl;
1992
1993 BLS[0] = (i==2 ? OldLines[3] : OldLines[2]);
1994 BLS[1] = OldLines[1];
1995 BLS[2] = NewLine;
1996 BTS = new class BoundaryTriangleSet(BLS, OldTriangleNrs[1]);
1997 BTS->GetNormalVector(BaseLineNormal);
1998 TrianglesOnBoundary.insert(TrianglePair(OldTriangleNrs[1], BTS));
1999 *out << Verbose(3) << "INFO: Created new triangle " << *BTS << "." << endl;
2000 } else {
2001 *out << Verbose(1) << "The four old lines do not connect, something's utterly wrong here!" << endl;
2002 return false;
2003 }
2004
2005 *out << Verbose(1) << "End of FlipBaseline" << endl;
2006 return true;
2007};
2008
2009
2010/** Finds the second point of starting triangle.
2011 * \param *a first node
2012 * \param *Candidate pointer to candidate node on return
2013 * \param Oben vector indicating the outside
2014 * \param OptCandidate reference to recommended candidate on return
2015 * \param Storage[3] array storing angles and other candidate information
2016 * \param RADIUS radius of virtual sphere
2017 * \param *LC LinkedCell structure with neighbouring points
2018 */
2019void Tesselation::FindSecondPointForTesselation(TesselPoint* a, TesselPoint* Candidate, Vector Oben, TesselPoint*& OptCandidate, double Storage[3], double RADIUS, LinkedCell *LC)
2020{
2021 cout << Verbose(2) << "Begin of FindSecondPointForTesselation" << endl;
2022 Vector AngleCheck;
2023 double norm = -1., angle;
2024 LinkedNodes *List = NULL;
2025 int N[NDIM], Nlower[NDIM], Nupper[NDIM];
2026
2027 if (LC->SetIndexToNode(a)) { // get cell for the starting point
2028 for(int i=0;i<NDIM;i++) // store indices of this cell
2029 N[i] = LC->n[i];
2030 } else {
2031 cerr << "ERROR: Point " << *a << " is not found in cell " << LC->index << "." << endl;
2032 return;
2033 }
2034 // then go through the current and all neighbouring cells and check the contained points for possible candidates
2035 cout << Verbose(3) << "LC Intervals from [";
2036 for (int i=0;i<NDIM;i++) {
2037 cout << " " << N[i] << "<->" << LC->N[i];
2038 }
2039 cout << "] :";
2040 for (int i=0;i<NDIM;i++) {
2041 Nlower[i] = ((N[i]-1) >= 0) ? N[i]-1 : 0;
2042 Nupper[i] = ((N[i]+1) < LC->N[i]) ? N[i]+1 : LC->N[i]-1;
2043 cout << " [" << Nlower[i] << "," << Nupper[i] << "] ";
2044 }
2045 cout << endl;
2046
2047
2048 for (LC->n[0] = Nlower[0]; LC->n[0] <= Nupper[0]; LC->n[0]++)
2049 for (LC->n[1] = Nlower[1]; LC->n[1] <= Nupper[1]; LC->n[1]++)
2050 for (LC->n[2] = Nlower[2]; LC->n[2] <= Nupper[2]; LC->n[2]++) {
2051 List = LC->GetCurrentCell();
2052 //cout << Verbose(2) << "Current cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << " with No. " << LC->index << "." << endl;
2053 if (List != NULL) {
2054 for (LinkedNodes::iterator Runner = List->begin(); Runner != List->end(); Runner++) {
2055 Candidate = (*Runner);
2056 // check if we only have one unique point yet ...
2057 if (a != Candidate) {
2058 // Calculate center of the circle with radius RADIUS through points a and Candidate
2059 Vector OrthogonalizedOben, aCandidate, Center;
2060 double distance, scaleFactor;
2061
2062 OrthogonalizedOben.CopyVector(&Oben);
2063 aCandidate.CopyVector(a->node);
2064 aCandidate.SubtractVector(Candidate->node);
2065 OrthogonalizedOben.ProjectOntoPlane(&aCandidate);
2066 OrthogonalizedOben.Normalize();
2067 distance = 0.5 * aCandidate.Norm();
2068 scaleFactor = sqrt(((RADIUS * RADIUS) - (distance * distance)));
2069 OrthogonalizedOben.Scale(scaleFactor);
2070
2071 Center.CopyVector(Candidate->node);
2072 Center.AddVector(a->node);
2073 Center.Scale(0.5);
2074 Center.AddVector(&OrthogonalizedOben);
2075
2076 AngleCheck.CopyVector(&Center);
2077 AngleCheck.SubtractVector(a->node);
2078 norm = aCandidate.Norm();
2079 // second point shall have smallest angle with respect to Oben vector
2080 if (norm < RADIUS*2.) {
2081 angle = AngleCheck.Angle(&Oben);
2082 if (angle < Storage[0]) {
2083 //cout << Verbose(3) << "Old values of Storage: %lf %lf \n", Storage[0], Storage[1]);
2084 cout << Verbose(3) << "Current candidate is " << *Candidate << ": Is a better candidate with distance " << norm << " and angle " << angle << " to oben " << Oben << ".\n";
2085 OptCandidate = Candidate;
2086 Storage[0] = angle;
2087 //cout << Verbose(3) << "Changing something in Storage: %lf %lf. \n", Storage[0], Storage[2]);
2088 } else {
2089 //cout << Verbose(3) << "Current candidate is " << *Candidate << ": Looses with angle " << angle << " to a better candidate " << *OptCandidate << endl;
2090 }
2091 } else {
2092 //cout << Verbose(3) << "Current candidate is " << *Candidate << ": Refused due to Radius " << norm << endl;
2093 }
2094 } else {
2095 //cout << Verbose(3) << "Current candidate is " << *Candidate << ": Candidate is equal to first endpoint." << *a << "." << endl;
2096 }
2097 }
2098 } else {
2099 cout << Verbose(3) << "Linked cell list is empty." << endl;
2100 }
2101 }
2102 cout << Verbose(2) << "End of FindSecondPointForTesselation" << endl;
2103};
2104
2105
2106/** This recursive function finds a third point, to form a triangle with two given ones.
2107 * Note that this function is for the starting triangle.
2108 * The idea is as follows: A sphere with fixed radius is (almost) uniquely defined in space by three points
2109 * that sit on its boundary. Hence, when two points are given and we look for the (next) third point, then
2110 * the center of the sphere is still fixed up to a single parameter. The band of possible values
2111 * describes a circle in 3D-space. The old center of the sphere for the current base triangle gives
2112 * us the "null" on this circle, the new center of the candidate point will be some way along this
2113 * circle. The shorter the way the better is the candidate. Note that the direction is clearly given
2114 * by the normal vector of the base triangle that always points outwards by construction.
2115 * Hence, we construct a Center of this circle which sits right in the middle of the current base line.
2116 * We construct the normal vector that defines the plane this circle lies in, it is just in the
2117 * direction of the baseline. And finally, we need the radius of the circle, which is given by the rest
2118 * with respect to the length of the baseline and the sphere's fixed \a RADIUS.
2119 * Note that there is one difficulty: The circumcircle is uniquely defined, but for the circumsphere's center
2120 * there are two possibilities which becomes clear from the construction as seen below. Hence, we must check
2121 * both.
2122 * Note also that the acos() function is not unique on [0, 2.*M_PI). Hence, we need an additional check
2123 * to decide for one of the two possible angles. Therefore we need a SearchDirection and to make this check
2124 * sensible we need OldSphereCenter to be orthogonal to it. Either we construct SearchDirection orthogonal
2125 * right away, or -- what we do here -- we rotate the relative sphere centers such that this orthogonality
2126 * holds. Then, the normalized projection onto the SearchDirection is either +1 or -1 and thus states whether
2127 * the angle is uniquely in either (0,M_PI] or [M_PI, 2.*M_PI).
2128 * @param NormalVector normal direction of the base triangle (here the unit axis vector, \sa FindStartingTriangle())
2129 * @param SearchDirection general direction where to search for the next point, relative to center of BaseLine
2130 * @param OldSphereCenter center of sphere for base triangle, relative to center of BaseLine, giving null angle for the parameter circle
2131 * @param BaseLine BoundaryLineSet with the current base line
2132 * @param ThirdNode third point to avoid in search
2133 * @param candidates list of equally good candidates to return
2134 * @param ShortestAngle the current path length on this circle band for the current OptCandidate
2135 * @param RADIUS radius of sphere
2136 * @param *LC LinkedCell structure with neighbouring points
2137 */
2138void Tesselation::FindThirdPointForTesselation(Vector NormalVector, Vector SearchDirection, Vector OldSphereCenter, class BoundaryLineSet *BaseLine, class TesselPoint *ThirdNode, CandidateList* &candidates, double *ShortestAngle, const double RADIUS, LinkedCell *LC)
2139{
2140 Vector CircleCenter; // center of the circle, i.e. of the band of sphere's centers
2141 Vector CirclePlaneNormal; // normal vector defining the plane this circle lives in
2142 Vector SphereCenter;
2143 Vector NewSphereCenter; // center of the sphere defined by the two points of BaseLine and the one of Candidate, first possibility
2144 Vector OtherNewSphereCenter; // center of the sphere defined by the two points of BaseLine and the one of Candidate, second possibility
2145 Vector NewNormalVector; // normal vector of the Candidate's triangle
2146 Vector helper, OptCandidateCenter, OtherOptCandidateCenter;
2147 LinkedNodes *List = NULL;
2148 double CircleRadius; // radius of this circle
2149 double radius;
2150 double alpha, Otheralpha; // angles (i.e. parameter for the circle).
2151 int N[NDIM], Nlower[NDIM], Nupper[NDIM];
2152 TesselPoint *Candidate = NULL;
2153 CandidateForTesselation *optCandidate = NULL;
2154
2155 cout << Verbose(1) << "Begin of FindThirdPointForTesselation" << endl;
2156
2157 //cout << Verbose(2) << "INFO: NormalVector of BaseTriangle is " << NormalVector << "." << endl;
2158
2159 // construct center of circle
2160 CircleCenter.CopyVector(BaseLine->endpoints[0]->node->node);
2161 CircleCenter.AddVector(BaseLine->endpoints[1]->node->node);
2162 CircleCenter.Scale(0.5);
2163
2164 // construct normal vector of circle
2165 CirclePlaneNormal.CopyVector(BaseLine->endpoints[0]->node->node);
2166 CirclePlaneNormal.SubtractVector(BaseLine->endpoints[1]->node->node);
2167
2168 // calculate squared radius TesselPoint *ThirdNode,f circle
2169 radius = CirclePlaneNormal.ScalarProduct(&CirclePlaneNormal);
2170 if (radius/4. < RADIUS*RADIUS) {
2171 CircleRadius = RADIUS*RADIUS - radius/4.;
2172 CirclePlaneNormal.Normalize();
2173 //cout << Verbose(2) << "INFO: CircleCenter is at " << CircleCenter << ", CirclePlaneNormal is " << CirclePlaneNormal << " with circle radius " << sqrt(CircleRadius) << "." << endl;
2174
2175 // test whether old center is on the band's plane
2176 if (fabs(OldSphereCenter.ScalarProduct(&CirclePlaneNormal)) > HULLEPSILON) {
2177 cerr << "ERROR: Something's very wrong here: OldSphereCenter is not on the band's plane as desired by " << fabs(OldSphereCenter.ScalarProduct(&CirclePlaneNormal)) << "!" << endl;
2178 OldSphereCenter.ProjectOntoPlane(&CirclePlaneNormal);
2179 }
2180 radius = OldSphereCenter.ScalarProduct(&OldSphereCenter);
2181 if (fabs(radius - CircleRadius) < HULLEPSILON) {
2182
2183 // check SearchDirection
2184 //cout << Verbose(2) << "INFO: SearchDirection is " << SearchDirection << "." << endl;
2185 if (fabs(OldSphereCenter.ScalarProduct(&SearchDirection)) > HULLEPSILON) { // rotated the wrong way!
2186 cerr << "ERROR: SearchDirection and RelativeOldSphereCenter are not orthogonal!" << endl;
2187 }
2188
2189 // get cell for the starting point
2190 if (LC->SetIndexToVector(&CircleCenter)) {
2191 for(int i=0;i<NDIM;i++) // store indices of this cell
2192 N[i] = LC->n[i];
2193 //cout << Verbose(2) << "INFO: Center cell is " << N[0] << ", " << N[1] << ", " << N[2] << " with No. " << LC->index << "." << endl;
2194 } else {
2195 cerr << "ERROR: Vector " << CircleCenter << " is outside of LinkedCell's bounding box." << endl;
2196 return;
2197 }
2198 // then go through the current and all neighbouring cells and check the contained points for possible candidates
2199 //cout << Verbose(2) << "LC Intervals:";
2200 for (int i=0;i<NDIM;i++) {
2201 Nlower[i] = ((N[i]-1) >= 0) ? N[i]-1 : 0;
2202 Nupper[i] = ((N[i]+1) < LC->N[i]) ? N[i]+1 : LC->N[i]-1;
2203 //cout << " [" << Nlower[i] << "," << Nupper[i] << "] ";
2204 }
2205 //cout << endl;
2206 for (LC->n[0] = Nlower[0]; LC->n[0] <= Nupper[0]; LC->n[0]++)
2207 for (LC->n[1] = Nlower[1]; LC->n[1] <= Nupper[1]; LC->n[1]++)
2208 for (LC->n[2] = Nlower[2]; LC->n[2] <= Nupper[2]; LC->n[2]++) {
2209 List = LC->GetCurrentCell();
2210 //cout << Verbose(2) << "Current cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << " with No. " << LC->index << "." << endl;
2211 if (List != NULL) {
2212 for (LinkedNodes::iterator Runner = List->begin(); Runner != List->end(); Runner++) {
2213 Candidate = (*Runner);
2214
2215 // check for three unique points
2216 //cout << Verbose(2) << "INFO: Current Candidate is " << *Candidate << " at " << Candidate->node << "." << endl;
2217 if ((Candidate != BaseLine->endpoints[0]->node) && (Candidate != BaseLine->endpoints[1]->node) ){
2218
2219 // construct both new centers
2220 GetCenterofCircumcircle(&NewSphereCenter, BaseLine->endpoints[0]->node->node, BaseLine->endpoints[1]->node->node, Candidate->node);
2221 OtherNewSphereCenter.CopyVector(&NewSphereCenter);
2222
2223 if ((NewNormalVector.MakeNormalVector(BaseLine->endpoints[0]->node->node, BaseLine->endpoints[1]->node->node, Candidate->node))
2224 && (fabs(NewNormalVector.ScalarProduct(&NewNormalVector)) > HULLEPSILON)
2225 ) {
2226 helper.CopyVector(&NewNormalVector);
2227 //cout << Verbose(2) << "INFO: NewNormalVector is " << NewNormalVector << "." << endl;
2228 radius = BaseLine->endpoints[0]->node->node->DistanceSquared(&NewSphereCenter);
2229 if (radius < RADIUS*RADIUS) {
2230 helper.Scale(sqrt(RADIUS*RADIUS - radius));
2231 //cout << Verbose(2) << "INFO: Distance of NewCircleCenter to NewSphereCenter is " << helper.Norm() << " with sphere radius " << RADIUS << "." << endl;
2232 NewSphereCenter.AddVector(&helper);
2233 NewSphereCenter.SubtractVector(&CircleCenter);
2234 //cout << Verbose(2) << "INFO: NewSphereCenter is at " << NewSphereCenter << "." << endl;
2235
2236 // OtherNewSphereCenter is created by the same vector just in the other direction
2237 helper.Scale(-1.);
2238 OtherNewSphereCenter.AddVector(&helper);
2239 OtherNewSphereCenter.SubtractVector(&CircleCenter);
2240 //cout << Verbose(2) << "INFO: OtherNewSphereCenter is at " << OtherNewSphereCenter << "." << endl;
2241
2242 alpha = GetPathLengthonCircumCircle(CircleCenter, CirclePlaneNormal, CircleRadius, NewSphereCenter, OldSphereCenter, NormalVector, SearchDirection);
2243 Otheralpha = GetPathLengthonCircumCircle(CircleCenter, CirclePlaneNormal, CircleRadius, OtherNewSphereCenter, OldSphereCenter, NormalVector, SearchDirection);
2244 alpha = min(alpha, Otheralpha);
2245 // if there is a better candidate, drop the current list and add the new candidate
2246 // otherwise ignore the new candidate and keep the list
2247 if (*ShortestAngle > (alpha - HULLEPSILON)) {
2248 optCandidate = new CandidateForTesselation(Candidate, BaseLine, OptCandidateCenter, OtherOptCandidateCenter);
2249 if (fabs(alpha - Otheralpha) > MYEPSILON) {
2250 optCandidate->OptCenter.CopyVector(&NewSphereCenter);
2251 optCandidate->OtherOptCenter.CopyVector(&OtherNewSphereCenter);
2252 } else {
2253 optCandidate->OptCenter.CopyVector(&OtherNewSphereCenter);
2254 optCandidate->OtherOptCenter.CopyVector(&NewSphereCenter);
2255 }
2256 // if there is an equal candidate, add it to the list without clearing the list
2257 if ((*ShortestAngle - HULLEPSILON) < alpha) {
2258 candidates->push_back(optCandidate);
2259 cout << Verbose(2) << "ACCEPT: We have found an equally good candidate: " << *(optCandidate->point) << " with "
2260 << alpha << " and circumsphere's center at " << optCandidate->OptCenter << "." << endl;
2261 } else {
2262 // remove all candidates from the list and then the list itself
2263 class CandidateForTesselation *remover = NULL;
2264 for (CandidateList::iterator it = candidates->begin(); it != candidates->end(); ++it) {
2265 remover = *it;
2266 delete(remover);
2267 }
2268 candidates->clear();
2269 candidates->push_back(optCandidate);
2270 cout << Verbose(2) << "ACCEPT: We have found a better candidate: " << *(optCandidate->point) << " with "
2271 << alpha << " and circumsphere's center at " << optCandidate->OptCenter << "." << endl;
2272 }
2273 *ShortestAngle = alpha;
2274 //cout << Verbose(2) << "INFO: There are " << candidates->size() << " candidates in the list now." << endl;
2275 } else {
2276 if ((optCandidate != NULL) && (optCandidate->point != NULL)) {
2277 //cout << Verbose(2) << "REJECT: Old candidate " << *(optCandidate->point) << " with " << *ShortestAngle << " is better than new one " << *Candidate << " with " << alpha << " ." << endl;
2278 } else {
2279 //cout << Verbose(2) << "REJECT: Candidate " << *Candidate << " with " << alpha << " was rejected." << endl;
2280 }
2281 }
2282
2283 } else {
2284 //cout << Verbose(2) << "REJECT: NewSphereCenter " << NewSphereCenter << " for " << *Candidate << " is too far away: " << radius << "." << endl;
2285 }
2286 } else {
2287 //cout << Verbose(2) << "REJECT: Three points from " << *BaseLine << " and Candidate " << *Candidate << " are linear-dependent." << endl;
2288 }
2289 } else {
2290 if (ThirdNode != NULL) {
2291 //cout << Verbose(2) << "REJECT: Base triangle " << *BaseLine << " and " << *ThirdNode << " contains Candidate " << *Candidate << "." << endl;
2292 } else {
2293 //cout << Verbose(2) << "REJECT: Base triangle " << *BaseLine << " contains Candidate " << *Candidate << "." << endl;
2294 }
2295 }
2296 }
2297 }
2298 }
2299 } else {
2300 cerr << Verbose(2) << "ERROR: The projected center of the old sphere has radius " << radius << " instead of " << CircleRadius << "." << endl;
2301 }
2302 } else {
2303 if (ThirdNode != NULL)
2304 cout << Verbose(2) << "Circumcircle for base line " << *BaseLine << " and third node " << *ThirdNode << " is too big!" << endl;
2305 else
2306 cout << Verbose(2) << "Circumcircle for base line " << *BaseLine << " is too big!" << endl;
2307 }
2308
2309 //cout << Verbose(2) << "INFO: Sorting candidate list ..." << endl;
2310 if (candidates->size() > 1) {
2311 candidates->unique();
2312 candidates->sort(SortCandidates);
2313 }
2314
2315 cout << Verbose(1) << "End of FindThirdPointForTesselation" << endl;
2316};
2317
2318/** Finds the endpoint two lines are sharing.
2319 * \param *line1 first line
2320 * \param *line2 second line
2321 * \return point which is shared or NULL if none
2322 */
2323class BoundaryPointSet *Tesselation::GetCommonEndpoint(class BoundaryLineSet * line1, class BoundaryLineSet * line2)
2324{
2325 class BoundaryLineSet * lines[2] =
2326 { line1, line2 };
2327 class BoundaryPointSet *node = NULL;
2328 map<int, class BoundaryPointSet *> OrderMap;
2329 pair<map<int, class BoundaryPointSet *>::iterator, bool> OrderTest;
2330 for (int i = 0; i < 2; i++)
2331 // for both lines
2332 for (int j = 0; j < 2; j++)
2333 { // for both endpoints
2334 OrderTest = OrderMap.insert(pair<int, class BoundaryPointSet *> (
2335 lines[i]->endpoints[j]->Nr, lines[i]->endpoints[j]));
2336 if (!OrderTest.second)
2337 { // if insertion fails, we have common endpoint
2338 node = OrderTest.first->second;
2339 cout << Verbose(5) << "Common endpoint of lines " << *line1
2340 << " and " << *line2 << " is: " << *node << "." << endl;
2341 j = 2;
2342 i = 2;
2343 break;
2344 }
2345 }
2346 return node;
2347};
2348
2349/** Finds the triangle that is closest to a given Vector \a *x.
2350 * \param *out output stream for debugging
2351 * \param *x Vector to look from
2352 * \return list of BoundaryTriangleSet of nearest triangles or NULL in degenerate case.
2353 */
2354list<BoundaryTriangleSet*> * Tesselation::FindClosestTrianglesToPoint(ofstream *out, Vector *x, LinkedCell* LC)
2355{
2356 TesselPoint *trianglePoints[3];
2357 TesselPoint *SecondPoint = NULL;
2358
2359 if (LinesOnBoundary.empty()) {
2360 *out << Verbose(0) << "Error: There is no tesselation structure to compare the point with, please create one first.";
2361 return NULL;
2362 }
2363
2364 trianglePoints[0] = FindClosestPoint(x, SecondPoint, LC);
2365
2366 // check whether closest point is "too close" :), then it's inside
2367 if (trianglePoints[0] == NULL) {
2368 *out << Verbose(1) << "Is the only point, no one else is closeby." << endl;
2369 return NULL;
2370 }
2371 if (trianglePoints[0]->node->DistanceSquared(x) < MYEPSILON) {
2372 *out << Verbose(1) << "Point is right on a tesselation point, no nearest triangle." << endl;
2373 return NULL;
2374 }
2375 list<TesselPoint*> *connectedPoints = GetCircleOfConnectedPoints(out, trianglePoints[0]);
2376 list<TesselPoint*> *connectedClosestPoints = GetNeighboursOnCircleOfConnectedPoints(out, connectedPoints, trianglePoints[0], x);
2377 delete(connectedPoints);
2378 trianglePoints[1] = connectedClosestPoints->front();
2379 trianglePoints[2] = connectedClosestPoints->back();
2380 for (int i=0;i<3;i++) {
2381 if (trianglePoints[i] == NULL) {
2382 *out << Verbose(1) << "IsInnerPoint encounters serious error, point " << i << " not found." << endl;
2383 }
2384 //*out << Verbose(1) << "List of possible points:" << endl;
2385 //*out << Verbose(2) << *trianglePoints[i] << endl;
2386 }
2387
2388 list<BoundaryTriangleSet*> *triangles = FindTriangles(trianglePoints);
2389
2390 delete(connectedClosestPoints);
2391
2392 if (triangles->empty()) {
2393 *out << Verbose(0) << "Error: There is no nearest triangle. Please check the tesselation structure.";
2394 return NULL;
2395 } else
2396 return triangles;
2397};
2398
2399/** Finds closest triangle to a point.
2400 * This basically just takes care of the degenerate case, which is not handled in FindClosestTrianglesToPoint().
2401 * \param *out output stream for debugging
2402 * \param *x Vector to look from
2403 * \return list of BoundaryTriangleSet of nearest triangles or NULL.
2404 */
2405class BoundaryTriangleSet * Tesselation::FindClosestTriangleToPoint(ofstream *out, Vector *x, LinkedCell* LC)
2406{
2407 class BoundaryTriangleSet *result = NULL;
2408 list<BoundaryTriangleSet*> *triangles = FindClosestTrianglesToPoint(out, x, LC);
2409
2410 if (triangles == NULL)
2411 return NULL;
2412
2413 if (x->ScalarProduct(&triangles->front()->NormalVector) < 0)
2414 result = triangles->back();
2415 else
2416 result = triangles->front();
2417
2418 delete(triangles);
2419 return result;
2420};
2421
2422/** Checks whether the provided Vector is within the tesselation structure.
2423 *
2424 * @param point of which to check the position
2425 * @param *LC LinkedCell structure
2426 *
2427 * @return true if the point is inside the tesselation structure, false otherwise
2428 */
2429bool Tesselation::IsInnerPoint(ofstream *out, Vector Point, LinkedCell* LC)
2430{
2431 class BoundaryTriangleSet *result = FindClosestTriangleToPoint(out, &Point, LC);
2432 if (result == NULL)
2433 return true;
2434 if (Point.ScalarProduct(&result->NormalVector) < 0)
2435 return true;
2436 else
2437 return false;
2438}
2439
2440/** Checks whether the provided TesselPoint is within the tesselation structure.
2441 *
2442 * @param *Point of which to check the position
2443 * @param *LC Linked Cell structure
2444 *
2445 * @return true if the point is inside the tesselation structure, false otherwise
2446 */
2447bool Tesselation::IsInnerPoint(ofstream *out, TesselPoint *Point, LinkedCell* LC)
2448{
2449 class BoundaryTriangleSet *result = FindClosestTriangleToPoint(out, Point->node, LC);
2450 if (result == NULL)
2451 return true;
2452 if (Point->node->ScalarProduct(&result->NormalVector) < 0)
2453 return true;
2454 else
2455 return false;
2456}
2457
2458/** Gets all points connected to the provided point by triangulation lines.
2459 *
2460 * @param *Point of which get all connected points
2461 *
2462 * @return list of the all points linked to the provided one
2463 */
2464list<TesselPoint*> * Tesselation::GetCircleOfConnectedPoints(ofstream *out, TesselPoint* Point)
2465{
2466 list<TesselPoint*> *connectedPoints = new list<TesselPoint*>;
2467 class BoundaryPointSet *ReferencePoint = NULL;
2468 TesselPoint* current;
2469 bool takePoint = false;
2470
2471 // find the respective boundary point
2472 PointMap::iterator PointRunner = PointsOnBoundary.find(Point->nr);
2473 if (PointRunner != PointsOnBoundary.end()) {
2474 ReferencePoint = PointRunner->second;
2475 } else {
2476 *out << Verbose(2) << "getCircleOfConnectedPoints() could not find the BoundaryPoint belonging to " << *Point << "." << endl;
2477 ReferencePoint = NULL;
2478 }
2479
2480 // little trick so that we look just through lines connect to the BoundaryPoint
2481 // OR fall-back to look through all lines if there is no such BoundaryPoint
2482 LineMap *Lines = &LinesOnBoundary;
2483 if (ReferencePoint != NULL)
2484 Lines = &(ReferencePoint->lines);
2485 LineMap::iterator findLines = Lines->begin();
2486 while (findLines != Lines->end()) {
2487 takePoint = false;
2488
2489 if (findLines->second->endpoints[0]->Nr == Point->nr) {
2490 takePoint = true;
2491 current = findLines->second->endpoints[1]->node;
2492 } else if (findLines->second->endpoints[1]->Nr == Point->nr) {
2493 takePoint = true;
2494 current = findLines->second->endpoints[0]->node;
2495 }
2496
2497 if (takePoint) {
2498 *out << Verbose(3) << "INFO: Endpoint " << *current << " of line " << *(findLines->second) << " is taken into the circle." << endl;
2499 connectedPoints->push_back(current);
2500 }
2501
2502 findLines++;
2503 }
2504
2505 if (connectedPoints->size() == 0) { // if have not found any points
2506 *out << Verbose(1) << "ERROR: We have not found any connected points to " << *Point<< "." << endl;
2507 return NULL;
2508 }
2509 return connectedPoints;
2510}
2511
2512/** Gets the two neighbouring points with respect to a reference line to the provided point.
2513 * Maps them down onto the plane designated by the axis \a *Point and \a *Reference. The center of all points
2514 * connected in the tesselation to \a *Point is mapped to spherical coordinates with the zero angle being given
2515 * by the mapped down \a *Reference. Hence, the biggest and the smallest angles are those of the two shanks of the
2516 * triangle we are looking for.
2517 *
2518 * @param *out output stream for debugging
2519 * @param *connectedPoints list of connected points to the central \a *Point
2520 * @param *Point of which get all connected points
2521 * @param *Reference Vector to be checked whether it is an inner point
2522 *
2523 * @return list of the two points linked to the provided one and closest to the point to be checked,
2524 */
2525list<TesselPoint*> * Tesselation::GetNeighboursOnCircleOfConnectedPoints(ofstream *out, list<TesselPoint*> *connectedPoints, TesselPoint* Point, Vector* Reference)
2526{
2527 map<double, TesselPoint*> anglesOfPoints;
2528 map<double, TesselPoint*>::iterator runner;
2529 ;
2530 Vector center, PlaneNormal, OrthogonalVector, helper, AngleZero;
2531
2532 if (connectedPoints->size() == 0) { // if have not found any points
2533 *out << Verbose(1) << "ERROR: We have not found any connected points to " << *Point<< "." << endl;
2534 return NULL;
2535 }
2536
2537 // calculate central point
2538 for (list<TesselPoint*>::iterator TesselRunner = connectedPoints->begin(); TesselRunner != connectedPoints->end(); TesselRunner++)
2539 center.AddVector((*TesselRunner)->node);
2540 //*out << "Summed vectors " << center << "; number of points " << connectedPoints.size()
2541 // << "; scale factor " << 1.0/connectedPoints.size();
2542 center.Scale(1.0/connectedPoints->size());
2543 *out << Verbose(4) << "INFO: Calculated center of all circle points is " << center << "." << endl;
2544
2545 // projection plane of the circle is at the closes Point and normal is pointing away from center of all circle points
2546 PlaneNormal.CopyVector(Point->node);
2547 PlaneNormal.SubtractVector(&center);
2548 PlaneNormal.Normalize();
2549 *out << Verbose(4) << "INFO: Calculated plane normal of circle is " << PlaneNormal << "." << endl;
2550
2551 // construct one orthogonal vector
2552 AngleZero.CopyVector(Reference);
2553 AngleZero.SubtractVector(Point->node);
2554 AngleZero.ProjectOntoPlane(&PlaneNormal);
2555 *out << Verbose(4) << "INFO: Reference vector on this plane representing angle 0 is " << AngleZero << "." << endl;
2556 OrthogonalVector.MakeNormalVector(&PlaneNormal, &AngleZero);
2557 *out << Verbose(4) << "INFO: OrthogonalVector on plane is " << OrthogonalVector << "." << endl;
2558
2559 // go through all connected points and calculate angle
2560 for (list<TesselPoint*>::iterator listRunner = connectedPoints->begin(); listRunner != connectedPoints->end(); listRunner++) {
2561 helper.CopyVector((*listRunner)->node);
2562 helper.SubtractVector(Point->node);
2563 helper.ProjectOntoPlane(&PlaneNormal);
2564 double angle = GetAngle(helper, AngleZero, OrthogonalVector);
2565 *out << Verbose(2) << "INFO: Calculated angle is " << angle << " for point " << **listRunner << "." << endl;
2566 anglesOfPoints.insert(pair<double, TesselPoint*>(angle, (*listRunner)));
2567 }
2568
2569 list<TesselPoint*> *result = new list<TesselPoint*>;
2570 runner = anglesOfPoints.begin();
2571 result->push_back(runner->second);
2572 runner = anglesOfPoints.end();
2573 runner--;
2574 result->push_back(runner->second);
2575
2576 *out << Verbose(2) << "List of closest points has " << result->size() << " elements, which are "
2577 << *(result->front()) << " and " << *(result->back()) << endl;
2578
2579 return result;
2580}
2581
2582/** Removes a boundary point from the envelope while keeping it closed.
2583 * We create new triangles and remove the old ones connected to the point.
2584 * \param *out output stream for debugging
2585 * \param *point point to be removed
2586 * \return volume added to the volume inside the tesselated surface by the removal
2587 */
2588double Tesselation::RemovePointFromTesselatedSurface(ofstream *out, class BoundaryPointSet *point) {
2589 class BoundaryLineSet *line = NULL;
2590 class BoundaryTriangleSet *triangle = NULL;
2591 Vector OldPoint, TetraederVector[3];
2592 double volume = 0;
2593 int *numbers = NULL;
2594 int count = 0;
2595 int i;
2596
2597 // copy old location for the volume
2598 OldPoint.CopyVector(point->node->node);
2599
2600 // get list of connected points
2601 if (point->lines.empty()) {
2602 *out << Verbose(1) << "ERROR: Cannot remove the point " << *point << ", it's connected to no lines!" << endl;
2603 return 0.;
2604 }
2605 list<TesselPoint*> *CircleofPoints = GetCircleOfConnectedPoints(out, point->node);
2606
2607 // remove all triangles
2608 for (LineMap::iterator LineRunner = point->lines.begin(); LineRunner != point->lines.end(); LineRunner++)
2609 count+=LineRunner->second->triangles.size();
2610 numbers = new int[count];
2611 i=0;
2612 for (LineMap::iterator LineRunner = point->lines.begin(); (point != NULL) && (LineRunner != point->lines.end()); LineRunner++) {
2613 line = LineRunner->second;
2614 for (TriangleMap::iterator TriangleRunner = line->triangles.begin(); TriangleRunner != line->triangles.end(); TriangleRunner++) {
2615 triangle = TriangleRunner->second;
2616 *out << Verbose(2) << "Erasing triangle " << *triangle << "." << endl;
2617 numbers[i++] = triangle->Nr;
2618 RemoveTesselationTriangle(triangle);
2619 triangle = NULL;
2620 }
2621 }
2622 *out << Verbose(1) << i << " triangles were removed." << endl;
2623
2624 // re-create all triangles by going through connected points list
2625 list<TesselPoint*>::iterator CircleRunner = CircleofPoints->begin();
2626 list<TesselPoint*>::iterator OtherCircleRunner = CircleofPoints->begin();
2627 class TesselPoint *CentralNode = *CircleRunner;
2628 // advance two with CircleRunner and one with OtherCircleRunner
2629 CircleRunner++;
2630 CircleRunner++;
2631 OtherCircleRunner++;
2632 i=0;
2633 cout << Verbose(2) << "INFO: CentralNode is " << *CentralNode << "." << endl;
2634 for (; (OtherCircleRunner != CircleofPoints->end()) && (CircleRunner != CircleofPoints->end()); (CircleRunner++), (OtherCircleRunner++)) {
2635 cout << Verbose(3) << "INFO: CircleRunner's node is " << **CircleRunner << "." << endl;
2636 cout << Verbose(3) << "INFO: OtherCircleRunner's node is " << **OtherCircleRunner << "." << endl;
2637 *out << Verbose(4) << "Adding new triangle points."<< endl;
2638 AddTesselationPoint(CentralNode, 0);
2639 AddTesselationPoint(*OtherCircleRunner, 1);
2640 AddTesselationPoint(*CircleRunner, 2);
2641 *out << Verbose(4) << "Adding new triangle lines."<< endl;
2642 AddTesselationLine(TPS[0], TPS[1], 0);
2643 AddTesselationLine(TPS[0], TPS[2], 1);
2644 AddTesselationLine(TPS[1], TPS[2], 2);
2645 BTS = new class BoundaryTriangleSet(BLS, numbers[i]);
2646 TrianglesOnBoundary.insert(TrianglePair(numbers[i], BTS));
2647 *out << Verbose(4) << "Created triangle " << *BTS << "." << endl;
2648 // calculate volume summand as a general tetraeder
2649 for (int j=0;j<3;j++) {
2650 TetraederVector[j].CopyVector(TPS[j]->node->node);
2651 TetraederVector[j].SubtractVector(&OldPoint);
2652 }
2653 OldPoint.CopyVector(&TetraederVector[0]);
2654 OldPoint.VectorProduct(&TetraederVector[1]);
2655 volume += 1./6. * fabs(OldPoint.ScalarProduct(&TetraederVector[2]));
2656 // advance number
2657 i++;
2658 if (i >= count)
2659 *out << Verbose(2) << "WARNING: Maximum of numbers reached!" << endl;
2660 }
2661 *out << Verbose(1) << i << " triangles were created." << endl;
2662
2663 delete[](numbers);
2664
2665 return volume;
2666};
2667
2668/** Checks for a new special triangle whether one of its edges is already present with one one triangle connected.
2669 * This enforces that special triangles (i.e. degenerated ones) should at last close the open-edge frontier and not
2670 * make it bigger (i.e. closing one (the baseline) and opening two new ones).
2671 * \param TPS[3] nodes of the triangle
2672 * \return true - there is such a line (i.e. creation of degenerated triangle is valid), false - no such line (don't create)
2673 */
2674bool CheckLineCriteriaForDegeneratedTriangle(class BoundaryPointSet *nodes[3])
2675{
2676 bool result = false;
2677 int counter = 0;
2678
2679 // check all three points
2680 for (int i=0;i<3;i++)
2681 for (int j=i+1; j<3; j++) {
2682 if (nodes[i]->lines.find(nodes[j]->node->nr) != nodes[i]->lines.end()) { // there already is a line
2683 LineMap::iterator FindLine;
2684 pair<LineMap::iterator,LineMap::iterator> FindPair;
2685 FindPair = nodes[i]->lines.equal_range(nodes[j]->node->nr);
2686 for (FindLine = FindPair.first; FindLine != FindPair.second; ++FindLine) {
2687 // If there is a line with less than two attached triangles, we don't need a new line.
2688 if (FindLine->second->triangles.size() < 2) {
2689 counter++;
2690 break; // increase counter only once per edge
2691 }
2692 }
2693 } else { // no line
2694 cout << Verbose(1) << "The line between " << nodes[i] << " and " << nodes[j] << " is not yet present, hence no need for a degenerate triangle." << endl;
2695 result = true;
2696 }
2697 }
2698 if (counter > 1) {
2699 cout << Verbose(2) << "INFO: Degenerate triangle is ok, at least two, here " << counter << ", existing lines are used." << endl;
2700 result = true;
2701 }
2702 return result;
2703};
2704
2705
2706/** Sort function for the candidate list.
2707 */
2708bool SortCandidates(CandidateForTesselation* candidate1, CandidateForTesselation* candidate2)
2709{
2710 Vector BaseLineVector, OrthogonalVector, helper;
2711 if (candidate1->BaseLine != candidate2->BaseLine) { // sanity check
2712 cout << Verbose(0) << "ERROR: sortCandidates was called for two different baselines: " << candidate1->BaseLine << " and " << candidate2->BaseLine << "." << endl;
2713 //return false;
2714 exit(1);
2715 }
2716 // create baseline vector
2717 BaseLineVector.CopyVector(candidate1->BaseLine->endpoints[1]->node->node);
2718 BaseLineVector.SubtractVector(candidate1->BaseLine->endpoints[0]->node->node);
2719 BaseLineVector.Normalize();
2720
2721 // create normal in-plane vector to cope with acos() non-uniqueness on [0,2pi] (note that is pointing in the "right" direction already, hence ">0" test!)
2722 helper.CopyVector(candidate1->BaseLine->endpoints[0]->node->node);
2723 helper.SubtractVector(candidate1->point->node);
2724 OrthogonalVector.CopyVector(&helper);
2725 helper.VectorProduct(&BaseLineVector);
2726 OrthogonalVector.SubtractVector(&helper);
2727 OrthogonalVector.Normalize();
2728
2729 // calculate both angles and correct with in-plane vector
2730 helper.CopyVector(candidate1->point->node);
2731 helper.SubtractVector(candidate1->BaseLine->endpoints[0]->node->node);
2732 double phi = BaseLineVector.Angle(&helper);
2733 if (OrthogonalVector.ScalarProduct(&helper) > 0) {
2734 phi = 2.*M_PI - phi;
2735 }
2736 helper.CopyVector(candidate2->point->node);
2737 helper.SubtractVector(candidate1->BaseLine->endpoints[0]->node->node);
2738 double psi = BaseLineVector.Angle(&helper);
2739 if (OrthogonalVector.ScalarProduct(&helper) > 0) {
2740 psi = 2.*M_PI - psi;
2741 }
2742
2743 cout << Verbose(2) << *candidate1->point << " has angle " << phi << endl;
2744 cout << Verbose(2) << *candidate2->point << " has angle " << psi << endl;
2745
2746 // return comparison
2747 return phi < psi;
2748};
2749
2750/**
2751 * Finds the point which is second closest to the provided one.
2752 *
2753 * @param Point to which to find the second closest other point
2754 * @param linked cell structure
2755 *
2756 * @return point which is second closest to the provided one
2757 */
2758TesselPoint* FindSecondClosestPoint(const Vector* Point, LinkedCell* LC)
2759{
2760 LinkedNodes *List = NULL;
2761 TesselPoint* closestPoint = NULL;
2762 TesselPoint* secondClosestPoint = NULL;
2763 double distance = 1e16;
2764 double secondDistance = 1e16;
2765 Vector helper;
2766 int N[NDIM], Nlower[NDIM], Nupper[NDIM];
2767
2768 LC->SetIndexToVector(Point); // ignore status as we calculate bounds below sensibly
2769 for(int i=0;i<NDIM;i++) // store indices of this cell
2770 N[i] = LC->n[i];
2771 cout << Verbose(2) << "INFO: Center cell is " << N[0] << ", " << N[1] << ", " << N[2] << " with No. " << LC->index << "." << endl;
2772
2773 LC->GetNeighbourBounds(Nlower, Nupper);
2774 //cout << endl;
2775 for (LC->n[0] = Nlower[0]; LC->n[0] <= Nupper[0]; LC->n[0]++)
2776 for (LC->n[1] = Nlower[1]; LC->n[1] <= Nupper[1]; LC->n[1]++)
2777 for (LC->n[2] = Nlower[2]; LC->n[2] <= Nupper[2]; LC->n[2]++) {
2778 List = LC->GetCurrentCell();
2779 cout << Verbose(3) << "The current cell " << LC->n[0] << "," << LC->n[1] << "," << LC->n[2] << endl;
2780 if (List != NULL) {
2781 for (LinkedNodes::iterator Runner = List->begin(); Runner != List->end(); Runner++) {
2782 helper.CopyVector(Point);
2783 helper.SubtractVector((*Runner)->node);
2784 double currentNorm = helper. Norm();
2785 if (currentNorm < distance) {
2786 // remember second point
2787 secondDistance = distance;
2788 secondClosestPoint = closestPoint;
2789 // mark down new closest point
2790 distance = currentNorm;
2791 closestPoint = (*Runner);
2792 cout << Verbose(2) << "INFO: New Nearest Neighbour is " << *closestPoint << "." << endl;
2793 }
2794 }
2795 } else {
2796 cerr << "ERROR: The current cell " << LC->n[0] << "," << LC->n[1] << ","
2797 << LC->n[2] << " is invalid!" << endl;
2798 }
2799 }
2800
2801 return secondClosestPoint;
2802};
2803
2804/**
2805 * Finds the point which is closest to the provided one.
2806 *
2807 * @param Point to which to find the closest other point
2808 * @param SecondPoint the second closest other point on return, NULL if none found
2809 * @param linked cell structure
2810 *
2811 * @return point which is closest to the provided one, NULL if none found
2812 */
2813TesselPoint* FindClosestPoint(const Vector* Point, TesselPoint *&SecondPoint, LinkedCell* LC)
2814{
2815 LinkedNodes *List = NULL;
2816 TesselPoint* closestPoint = NULL;
2817 SecondPoint = NULL;
2818 double distance = 1e16;
2819 double secondDistance = 1e16;
2820 Vector helper;
2821 int N[NDIM], Nlower[NDIM], Nupper[NDIM];
2822
2823 LC->SetIndexToVector(Point); // ignore status as we calculate bounds below sensibly
2824 for(int i=0;i<NDIM;i++) // store indices of this cell
2825 N[i] = LC->n[i];
2826 cout << Verbose(2) << "INFO: Center cell is " << N[0] << ", " << N[1] << ", " << N[2] << " with No. " << LC->index << "." << endl;
2827
2828 LC->GetNeighbourBounds(Nlower, Nupper);
2829 //cout << endl;
2830 for (LC->n[0] = Nlower[0]; LC->n[0] <= Nupper[0]; LC->n[0]++)
2831 for (LC->n[1] = Nlower[1]; LC->n[1] <= Nupper[1]; LC->n[1]++)
2832 for (LC->n[2] = Nlower[2]; LC->n[2] <= Nupper[2]; LC->n[2]++) {
2833 List = LC->GetCurrentCell();
2834 cout << Verbose(3) << "The current cell " << LC->n[0] << "," << LC->n[1] << "," << LC->n[2] << endl;
2835 if (List != NULL) {
2836 for (LinkedNodes::iterator Runner = List->begin(); Runner != List->end(); Runner++) {
2837 helper.CopyVector(Point);
2838 helper.SubtractVector((*Runner)->node);
2839 double currentNorm = helper. Norm();
2840 if (currentNorm < distance) {
2841 secondDistance = distance;
2842 SecondPoint = closestPoint;
2843 distance = currentNorm;
2844 closestPoint = (*Runner);
2845 cout << Verbose(2) << "INFO: New Nearest Neighbour is " << *closestPoint << "." << endl;
2846 } else if (currentNorm < secondDistance) {
2847 secondDistance = currentNorm;
2848 SecondPoint = (*Runner);
2849 cout << Verbose(2) << "INFO: New Second Nearest Neighbour is " << *SecondPoint << "." << endl;
2850 }
2851 }
2852 } else {
2853 cerr << "ERROR: The current cell " << LC->n[0] << "," << LC->n[1] << ","
2854 << LC->n[2] << " is invalid!" << endl;
2855 }
2856 }
2857
2858 return closestPoint;
2859};
2860
2861/**
2862 * Finds triangles belonging to the three provided points.
2863 *
2864 * @param *Points[3] list, is expected to contain three points
2865 *
2866 * @return triangles which belong to the provided points, will be empty if there are none,
2867 * will usually be one, in case of degeneration, there will be two
2868 */
2869list<BoundaryTriangleSet*> *Tesselation::FindTriangles(TesselPoint* Points[3])
2870{
2871 list<BoundaryTriangleSet*> *result = new list<BoundaryTriangleSet*>;
2872 LineMap::iterator FindLine;
2873 PointMap::iterator FindPoint;
2874 TriangleMap::iterator FindTriangle;
2875 class BoundaryPointSet *TrianglePoints[3];
2876
2877 for (int i = 0; i < 3; i++) {
2878 FindPoint = PointsOnBoundary.find(Points[i]->nr);
2879 if (FindPoint != PointsOnBoundary.end()) {
2880 TrianglePoints[i] = FindPoint->second;
2881 } else {
2882 TrianglePoints[i] = NULL;
2883 }
2884 }
2885
2886 // checks lines between the points in the Points for their adjacent triangles
2887 for (int i = 0; i < 3; i++) {
2888 if (TrianglePoints[i] != NULL) {
2889 for (int j = i; j < 3; j++) {
2890 if (TrianglePoints[j] != NULL) {
2891 FindLine = TrianglePoints[i]->lines.find(TrianglePoints[j]->node->nr);
2892 if (FindLine != TrianglePoints[i]->lines.end()) {
2893 for (; FindLine->first == TrianglePoints[j]->node->nr; FindLine++) {
2894 FindTriangle = FindLine->second->triangles.begin();
2895 for (; FindTriangle != FindLine->second->triangles.end(); FindTriangle++) {
2896 if ((
2897 (FindTriangle->second->endpoints[0] == TrianglePoints[0])
2898 || (FindTriangle->second->endpoints[0] == TrianglePoints[1])
2899 || (FindTriangle->second->endpoints[0] == TrianglePoints[2])
2900 ) && (
2901 (FindTriangle->second->endpoints[1] == TrianglePoints[0])
2902 || (FindTriangle->second->endpoints[1] == TrianglePoints[1])
2903 || (FindTriangle->second->endpoints[1] == TrianglePoints[2])
2904 ) && (
2905 (FindTriangle->second->endpoints[2] == TrianglePoints[0])
2906 || (FindTriangle->second->endpoints[2] == TrianglePoints[1])
2907 || (FindTriangle->second->endpoints[2] == TrianglePoints[2])
2908 )
2909 ) {
2910 result->push_back(FindTriangle->second);
2911 }
2912 }
2913 }
2914 // Is it sufficient to consider one of the triangle lines for this.
2915 return result;
2916
2917 }
2918 }
2919 }
2920 }
2921 }
2922
2923 return result;
2924}
2925
2926/**
2927 * Finds all degenerated triangles within the tesselation structure.
2928 *
2929 * @return map of keys of degenerated triangle pairs, each triangle occurs twice
2930 * in the list, once as key and once as value
2931 */
2932map<int, int> Tesselation::FindAllDegeneratedTriangles()
2933{
2934 map<int, int> DegeneratedTriangles;
2935
2936 // sanity check
2937 if (LinesOnBoundary.empty()) {
2938 cout << Verbose(1) << "Warning: FindAllDegeneratedTriangles() was called without any tesselation structure.";
2939 return DegeneratedTriangles;
2940 }
2941
2942 LineMap::iterator LineRunner1, LineRunner2;
2943
2944 for (LineRunner1 = LinesOnBoundary.begin(); LineRunner1 != LinesOnBoundary.end(); ++LineRunner1) {
2945 for (LineRunner2 = LinesOnBoundary.begin(); LineRunner2 != LinesOnBoundary.end(); ++LineRunner2) {
2946 if ((LineRunner1->second != LineRunner2->second)
2947 && (LineRunner1->second->endpoints[0] == LineRunner2->second->endpoints[0])
2948 && (LineRunner1->second->endpoints[1] == LineRunner2->second->endpoints[1])
2949 ) {
2950 TriangleMap::iterator TriangleRunner1 = LineRunner1->second->triangles.begin(),
2951 TriangleRunner2 = LineRunner2->second->triangles.begin();
2952
2953 for (; TriangleRunner1 != LineRunner1->second->triangles.end(); ++TriangleRunner1) {
2954 for (; TriangleRunner2 != LineRunner2->second->triangles.end(); ++TriangleRunner2) {
2955 if ((TriangleRunner1->second != TriangleRunner2->second)
2956 && (TriangleRunner1->second->endpoints[0] == TriangleRunner2->second->endpoints[0])
2957 && (TriangleRunner1->second->endpoints[1] == TriangleRunner2->second->endpoints[1])
2958 && (TriangleRunner1->second->endpoints[2] == TriangleRunner2->second->endpoints[2])
2959 ) {
2960 DegeneratedTriangles[TriangleRunner1->second->Nr] = TriangleRunner2->second->Nr;
2961 DegeneratedTriangles[TriangleRunner2->second->Nr] = TriangleRunner1->second->Nr;
2962 }
2963 }
2964 }
2965 }
2966 }
2967 }
2968
2969 cout << Verbose(1) << "FindAllDegeneratedTriangles() found " << DegeneratedTriangles.size() << " triangles." << endl;
2970 map<int,int>::iterator it;
2971 for (it = DegeneratedTriangles.begin(); it != DegeneratedTriangles.end(); it++)
2972 cout << Verbose(2) << (*it).first << " => " << (*it).second << endl;
2973
2974 return DegeneratedTriangles;
2975}
2976
2977/**
2978 * Purges degenerated triangles from the tesselation structure if they are not
2979 * necessary to keep a single point within the structure.
2980 */
2981void Tesselation::RemoveDegeneratedTriangles()
2982{
2983 map<int, int> DegeneratedTriangles = FindAllDegeneratedTriangles();
2984
2985 for (map<int, int>::iterator TriangleKeyRunner = DegeneratedTriangles.begin();
2986 TriangleKeyRunner != DegeneratedTriangles.end(); ++TriangleKeyRunner
2987 ) {
2988 BoundaryTriangleSet *triangle = TrianglesOnBoundary.find(TriangleKeyRunner->first)->second,
2989 *partnerTriangle = TrianglesOnBoundary.find(TriangleKeyRunner->second)->second;
2990
2991 bool trianglesShareLine = false;
2992 for (int i = 0; i < 3; ++i)
2993 for (int j = 0; j < 3; ++j)
2994 trianglesShareLine = trianglesShareLine || triangle->lines[i] == partnerTriangle->lines[j];
2995
2996 if (trianglesShareLine
2997 && (triangle->endpoints[1]->LinesCount > 2)
2998 && (triangle->endpoints[2]->LinesCount > 2)
2999 && (triangle->endpoints[0]->LinesCount > 2)
3000 ) {
3001 cout << Verbose(1) << "RemoveDegeneratedTriangles() removes triangle " << *triangle << "." << endl;
3002 RemoveTesselationTriangle(triangle);
3003 cout << Verbose(1) << "RemoveDegeneratedTriangles() removes triangle " << *partnerTriangle << "." << endl;
3004 RemoveTesselationTriangle(partnerTriangle);
3005 DegeneratedTriangles.erase(DegeneratedTriangles.find(partnerTriangle->Nr));
3006 } else {
3007 cout << Verbose(1) << "RemoveDegeneratedTriangles() does not remove triangle " << *triangle
3008 << " and its partner " << *partnerTriangle << " because it is essential for at"
3009 << " least one of the endpoints to be kept in the tesselation structure." << endl;
3010 }
3011 }
3012}
3013
3014/** Gets the angle between a point and a reference relative to the provided center.
3015 * We have two shanks point and reference between which the angle is calculated
3016 * and by scalar product with OrthogonalVector we decide the interval.
3017 * @param point to calculate the angle for
3018 * @param reference to which to calculate the angle
3019 * @param OrthogonalVector points in direction of [pi,2pi] interval
3020 *
3021 * @return angle between point and reference
3022 */
3023double GetAngle(const Vector &point, const Vector &reference, const Vector OrthogonalVector)
3024{
3025 if (reference.IsNull())
3026 return M_PI;
3027
3028 // calculate both angles and correct with in-plane vector
3029 if (point.IsNull())
3030 return M_PI;
3031 double phi = point.Angle(&reference);
3032 if (OrthogonalVector.ScalarProduct(&point) > 0) {
3033 phi = 2.*M_PI - phi;
3034 }
3035
3036 cout << Verbose(3) << "INFO: " << point << " has angle " << phi << " with respect to reference " << reference << "." << endl;
3037
3038 return phi;
3039}
3040
Note: See TracBrowser for help on using the repository browser.