source: molecuilder/src/boundary.cpp@ 30212a8

TesselationWORKING
Last change on this file since 30212a8 was 30212a8, checked in by Frederik Heber <heber@…>, 16 years ago

FIX: CheckLineCriteriaforDegeneratedTriangle contains criteria when we may add a special (degenerated) triangle

  • new function CheckLineCriteriaforDegeneratedTriangle() that checks whether the special triangle closed at least two present edges and does not create two new edges for one being closed (which is the baseline), i.e. the total number of problems should remain the same or become smaller by degenerated triangles, never bigger
  • find_next_suitable_triangle() checks the above criteria when (existentTriangle == 1) and if false, does not add a triangle at all
  • some identing fixes
  • Property mode set to 100755
File size: 147.4 KB
Line 
1#include "boundary.hpp"
2#include "linkedcell.hpp"
3#include "molecules.hpp"
4#include <gsl/gsl_matrix.h>
5#include <gsl/gsl_linalg.h>
6#include <gsl/gsl_multimin.h>
7#include <gsl/gsl_permutation.h>
8
9#define DEBUG 1
10#define DoSingleStepOutput 1
11#define DoTecplotOutput 1
12#define DoRaster3DOutput 1
13#define DoVRMLOutput 1
14#define TecplotSuffix ".dat"
15#define Raster3DSuffix ".r3d"
16#define VRMLSUffix ".wrl"
17#define HULLEPSILON 1e-7
18
19// ======================================== Points on Boundary =================================
20
21BoundaryPointSet::BoundaryPointSet()
22{
23 LinesCount = 0;
24 Nr = -1;
25}
26;
27
28BoundaryPointSet::BoundaryPointSet(atom *Walker)
29{
30 node = Walker;
31 LinesCount = 0;
32 Nr = Walker->nr;
33}
34;
35
36BoundaryPointSet::~BoundaryPointSet()
37{
38 cout << Verbose(5) << "Erasing point nr. " << Nr << "." << endl;
39 if (!lines.empty())
40 cerr << "WARNING: Memory Leak! I " << *this << " am still connected to some lines." << endl;
41 node = NULL;
42}
43;
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
61ostream &
62operator <<(ostream &ost, BoundaryPointSet &a)
63{
64 ost << "[" << a.Nr << "|" << a.node->Name << "]";
65 return ost;
66}
67;
68
69// ======================================== Lines on Boundary =================================
70
71BoundaryLineSet::BoundaryLineSet()
72{
73 for (int i = 0; i < 2; i++)
74 endpoints[i] = NULL;
75 TrianglesCount = 0;
76 Nr = -1;
77}
78;
79
80BoundaryLineSet::BoundaryLineSet(class BoundaryPointSet *Point[2], int number)
81{
82 // set number
83 Nr = number;
84 // set endpoints in ascending order
85 SetEndpointsOrdered(endpoints, Point[0], Point[1]);
86 // add this line to the hash maps of both endpoints
87 Point[0]->AddLine(this); //Taken out, to check whether we can avoid unwanted double adding.
88 Point[1]->AddLine(this); //
89 // clear triangles list
90 TrianglesCount = 0;
91 cout << Verbose(5) << "New Line with endpoints " << *this << "." << endl;
92}
93;
94
95BoundaryLineSet::~BoundaryLineSet()
96{
97 int Numbers[2];
98 Numbers[0] = endpoints[1]->Nr;
99 Numbers[1] = endpoints[0]->Nr;
100 for (int i = 0; i < 2; i++) {
101 cout << Verbose(5) << "Erasing Line Nr. " << Nr << " in boundary point " << *endpoints[i] << "." << endl;
102 endpoints[i]->lines.erase(Numbers[i]);
103 if (endpoints[i]->lines.empty()) {
104 cout << Verbose(5) << *endpoints[i] << " has no more lines it's attached to, erasing." << endl;
105 if (endpoints[i] != NULL) {
106 delete(endpoints[i]);
107 endpoints[i] = NULL;
108 } else
109 cerr << "ERROR: Endpoint " << i << " has already been free'd." << endl;
110 } else
111 cout << Verbose(5) << *endpoints[i] << " has still lines it's attached to." << endl;
112 }
113 if (!triangles.empty())
114 cerr << "WARNING: Memory Leak! I " << *this << " am still connected to some triangles." << endl;
115}
116;
117
118void
119BoundaryLineSet::AddTriangle(class BoundaryTriangleSet *triangle)
120{
121 cout << Verbose(6) << "Add " << triangle->Nr << " to line " << *this << "."
122 << endl;
123 triangles.insert(TrianglePair(triangle->Nr, triangle));
124 TrianglesCount++;
125}
126;
127
128ostream &
129operator <<(ostream &ost, BoundaryLineSet &a)
130{
131 ost << "[" << a.Nr << "|" << a.endpoints[0]->node->Name << ","
132 << a.endpoints[1]->node->Name << "]";
133 return ost;
134}
135;
136
137// ======================================== Triangles on Boundary =================================
138
139
140BoundaryTriangleSet::BoundaryTriangleSet()
141{
142 for (int i = 0; i < 3; i++)
143 {
144 endpoints[i] = NULL;
145 lines[i] = NULL;
146 }
147 Nr = -1;
148}
149;
150
151BoundaryTriangleSet::BoundaryTriangleSet(class BoundaryLineSet *line[3], int number)
152{
153 // set number
154 Nr = number;
155 // set lines
156 cout << Verbose(5) << "New triangle " << Nr << ":" << endl;
157 for (int i = 0; i < 3; i++)
158 {
159 lines[i] = line[i];
160 lines[i]->AddTriangle(this);
161 }
162 // get ascending order of endpoints
163 map<int, class BoundaryPointSet *> OrderMap;
164 for (int i = 0; i < 3; i++)
165 // for all three lines
166 for (int j = 0; j < 2; j++)
167 { // for both endpoints
168 OrderMap.insert(pair<int, class BoundaryPointSet *> (
169 line[i]->endpoints[j]->Nr, line[i]->endpoints[j]));
170 // and we don't care whether insertion fails
171 }
172 // set endpoints
173 int Counter = 0;
174 cout << Verbose(6) << " with end points ";
175 for (map<int, class BoundaryPointSet *>::iterator runner = OrderMap.begin(); runner
176 != OrderMap.end(); runner++)
177 {
178 endpoints[Counter] = runner->second;
179 cout << " " << *endpoints[Counter];
180 Counter++;
181 }
182 if (Counter < 3)
183 {
184 cerr << "ERROR! We have a triangle with only two distinct endpoints!"
185 << endl;
186 //exit(1);
187 }
188 cout << "." << endl;
189}
190;
191
192BoundaryTriangleSet::~BoundaryTriangleSet()
193{
194 for (int i = 0; i < 3; i++) {
195 cout << Verbose(5) << "Erasing triangle Nr." << Nr << endl;
196 lines[i]->triangles.erase(Nr);
197 if (lines[i]->triangles.empty()) {
198 cout << Verbose(5) << *lines[i] << " is no more attached to any triangle, erasing." << endl;
199 if (lines[i] != NULL) {
200 delete (lines[i]);
201 lines[i] = NULL;
202 } else
203 cerr << "ERROR: This line " << i << " has already been free'd." << endl;
204 } else
205 cout << Verbose(5) << *lines[i] << " is still attached to a triangle." << endl;
206 }
207}
208;
209
210void
211BoundaryTriangleSet::GetNormalVector(Vector &OtherVector)
212{
213 // get normal vector
214 NormalVector.MakeNormalVector(&endpoints[0]->node->x, &endpoints[1]->node->x,
215 &endpoints[2]->node->x);
216
217 // make it always point inward (any offset vector onto plane projected onto normal vector suffices)
218 if (NormalVector.Projection(&OtherVector) > 0)
219 NormalVector.Scale(-1.);
220}
221;
222
223ostream &
224operator <<(ostream &ost, BoundaryTriangleSet &a)
225{
226 ost << "[" << a.Nr << "|" << a.endpoints[0]->node->Name << ","
227 << a.endpoints[1]->node->Name << "," << a.endpoints[2]->node->Name << "]";
228 return ost;
229}
230;
231
232
233// ============================ CandidateForTesselation =============================
234
235CandidateForTesselation::CandidateForTesselation(
236 atom *candidate, BoundaryLineSet* line, Vector OptCandidateCenter, Vector OtherOptCandidateCenter
237) {
238 point = candidate;
239 BaseLine = line;
240 OptCenter.CopyVector(&OptCandidateCenter);
241 OtherOptCenter.CopyVector(&OtherOptCandidateCenter);
242}
243
244CandidateForTesselation::~CandidateForTesselation() {
245 point = NULL;
246}
247
248// ========================================== F U N C T I O N S =================================
249
250/** Finds the endpoint two lines are sharing.
251 * \param *line1 first line
252 * \param *line2 second line
253 * \return point which is shared or NULL if none
254 */
255class BoundaryPointSet *
256GetCommonEndpoint(class BoundaryLineSet * line1, class BoundaryLineSet * line2)
257{
258 class BoundaryLineSet * lines[2] =
259 { line1, line2 };
260 class BoundaryPointSet *node = NULL;
261 map<int, class BoundaryPointSet *> OrderMap;
262 pair<map<int, class BoundaryPointSet *>::iterator, bool> OrderTest;
263 for (int i = 0; i < 2; i++)
264 // for both lines
265 for (int j = 0; j < 2; j++)
266 { // for both endpoints
267 OrderTest = OrderMap.insert(pair<int, class BoundaryPointSet *> (
268 lines[i]->endpoints[j]->Nr, lines[i]->endpoints[j]));
269 if (!OrderTest.second)
270 { // if insertion fails, we have common endpoint
271 node = OrderTest.first->second;
272 cout << Verbose(5) << "Common endpoint of lines " << *line1
273 << " and " << *line2 << " is: " << *node << "." << endl;
274 j = 2;
275 i = 2;
276 break;
277 }
278 }
279 return node;
280}
281;
282
283/** Determines the boundary points of a cluster.
284 * Does a projection per axis onto the orthogonal plane, transforms into spherical coordinates, sorts them by the angle
285 * and looks at triples: if the middle has less a distance than the allowed maximum height of the triangle formed by the plane's
286 * center and first and last point in the triple, it is thrown out.
287 * \param *out output stream for debugging
288 * \param *mol molecule structure representing the cluster
289 */
290Boundaries *
291GetBoundaryPoints(ofstream *out, molecule *mol)
292{
293 atom *Walker = NULL;
294 PointMap PointsOnBoundary;
295 LineMap LinesOnBoundary;
296 TriangleMap TrianglesOnBoundary;
297
298 *out << Verbose(1) << "Finding all boundary points." << endl;
299 Boundaries *BoundaryPoints = new Boundaries[NDIM]; // first is alpha, second is (r, nr)
300 BoundariesTestPair BoundaryTestPair;
301 Vector AxisVector, AngleReferenceVector, AngleReferenceNormalVector;
302 double radius, angle;
303 // 3a. Go through every axis
304 for (int axis = 0; axis < NDIM; axis++)
305 {
306 AxisVector.Zero();
307 AngleReferenceVector.Zero();
308 AngleReferenceNormalVector.Zero();
309 AxisVector.x[axis] = 1.;
310 AngleReferenceVector.x[(axis + 1) % NDIM] = 1.;
311 AngleReferenceNormalVector.x[(axis + 2) % NDIM] = 1.;
312 // *out << Verbose(1) << "Axisvector is ";
313 // AxisVector.Output(out);
314 // *out << " and AngleReferenceVector is ";
315 // AngleReferenceVector.Output(out);
316 // *out << "." << endl;
317 // *out << " and AngleReferenceNormalVector is ";
318 // AngleReferenceNormalVector.Output(out);
319 // *out << "." << endl;
320 // 3b. construct set of all points, transformed into cylindrical system and with left and right neighbours
321 Walker = mol->start;
322 while (Walker->next != mol->end)
323 {
324 Walker = Walker->next;
325 Vector ProjectedVector;
326 ProjectedVector.CopyVector(&Walker->x);
327 ProjectedVector.ProjectOntoPlane(&AxisVector);
328 // correct for negative side
329 //if (Projection(y) < 0)
330 //angle = 2.*M_PI - angle;
331 radius = ProjectedVector.Norm();
332 if (fabs(radius) > MYEPSILON)
333 angle = ProjectedVector.Angle(&AngleReferenceVector);
334 else
335 angle = 0.; // otherwise it's a vector in Axis Direction and unimportant for boundary issues
336
337 //*out << "Checking sign in quadrant : " << ProjectedVector.Projection(&AngleReferenceNormalVector) << "." << endl;
338 if (ProjectedVector.Projection(&AngleReferenceNormalVector) > 0)
339 {
340 angle = 2. * M_PI - angle;
341 }
342 //*out << Verbose(2) << "Inserting " << *Walker << ": (r, alpha) = (" << radius << "," << angle << "): ";
343 //ProjectedVector.Output(out);
344 //*out << endl;
345 BoundaryTestPair = BoundaryPoints[axis].insert(BoundariesPair(angle,
346 DistancePair (radius, Walker)));
347 if (BoundaryTestPair.second)
348 { // successfully inserted
349 }
350 else
351 { // same point exists, check first r, then distance of original vectors to center of gravity
352 *out << Verbose(2)
353 << "Encountered two vectors whose projection onto axis "
354 << axis << " is equal: " << endl;
355 *out << Verbose(2) << "Present vector: ";
356 BoundaryTestPair.first->second.second->x.Output(out);
357 *out << endl;
358 *out << Verbose(2) << "New vector: ";
359 Walker->x.Output(out);
360 *out << endl;
361 double tmp = ProjectedVector.Norm();
362 if (tmp > BoundaryTestPair.first->second.first)
363 {
364 BoundaryTestPair.first->second.first = tmp;
365 BoundaryTestPair.first->second.second = Walker;
366 *out << Verbose(2) << "Keeping new vector." << endl;
367 }
368 else if (tmp == BoundaryTestPair.first->second.first)
369 {
370 if (BoundaryTestPair.first->second.second->x.ScalarProduct(
371 &BoundaryTestPair.first->second.second->x)
372 < Walker->x.ScalarProduct(&Walker->x))
373 { // Norm() does a sqrt, which makes it a lot slower
374 BoundaryTestPair.first->second.second = Walker;
375 *out << Verbose(2) << "Keeping new vector." << endl;
376 }
377 else
378 {
379 *out << Verbose(2) << "Keeping present vector." << endl;
380 }
381 }
382 else
383 {
384 *out << Verbose(2) << "Keeping present vector." << endl;
385 }
386 }
387 }
388 // printing all inserted for debugging
389 // {
390 // *out << Verbose(2) << "Printing list of candidates for axis " << axis << " which we have inserted so far." << endl;
391 // int i=0;
392 // for(Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner != BoundaryPoints[axis].end(); runner++) {
393 // if (runner != BoundaryPoints[axis].begin())
394 // *out << ", " << i << ": " << *runner->second.second;
395 // else
396 // *out << i << ": " << *runner->second.second;
397 // i++;
398 // }
399 // *out << endl;
400 // }
401 // 3c. throw out points whose distance is less than the mean of left and right neighbours
402 bool flag = false;
403 do
404 { // do as long as we still throw one out per round
405 *out << Verbose(1)
406 << "Looking for candidates to kick out by convex condition ... "
407 << endl;
408 flag = false;
409 Boundaries::iterator left = BoundaryPoints[axis].end();
410 Boundaries::iterator right = BoundaryPoints[axis].end();
411 for (Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner
412 != BoundaryPoints[axis].end(); runner++)
413 {
414 // set neighbours correctly
415 if (runner == BoundaryPoints[axis].begin())
416 {
417 left = BoundaryPoints[axis].end();
418 }
419 else
420 {
421 left = runner;
422 }
423 left--;
424 right = runner;
425 right++;
426 if (right == BoundaryPoints[axis].end())
427 {
428 right = BoundaryPoints[axis].begin();
429 }
430 // check distance
431
432 // construct the vector of each side of the triangle on the projected plane (defined by normal vector AxisVector)
433 {
434 Vector SideA, SideB, SideC, SideH;
435 SideA.CopyVector(&left->second.second->x);
436 SideA.ProjectOntoPlane(&AxisVector);
437 // *out << "SideA: ";
438 // SideA.Output(out);
439 // *out << endl;
440
441 SideB.CopyVector(&right->second.second->x);
442 SideB.ProjectOntoPlane(&AxisVector);
443 // *out << "SideB: ";
444 // SideB.Output(out);
445 // *out << endl;
446
447 SideC.CopyVector(&left->second.second->x);
448 SideC.SubtractVector(&right->second.second->x);
449 SideC.ProjectOntoPlane(&AxisVector);
450 // *out << "SideC: ";
451 // SideC.Output(out);
452 // *out << endl;
453
454 SideH.CopyVector(&runner->second.second->x);
455 SideH.ProjectOntoPlane(&AxisVector);
456 // *out << "SideH: ";
457 // SideH.Output(out);
458 // *out << endl;
459
460 // calculate each length
461 double a = SideA.Norm();
462 //double b = SideB.Norm();
463 //double c = SideC.Norm();
464 double h = SideH.Norm();
465 // calculate the angles
466 double alpha = SideA.Angle(&SideH);
467 double beta = SideA.Angle(&SideC);
468 double gamma = SideB.Angle(&SideH);
469 double delta = SideC.Angle(&SideH);
470 double MinDistance = a * sin(beta) / (sin(delta)) * (((alpha
471 < M_PI / 2.) || (gamma < M_PI / 2.)) ? 1. : -1.);
472 // *out << Verbose(2) << " I calculated: a = " << a << ", h = " << h << ", beta(" << left->second.second->Name << "," << left->second.second->Name << "-" << right->second.second->Name << ") = " << beta << ", delta(" << left->second.second->Name << "," << runner->second.second->Name << ") = " << delta << ", Min = " << MinDistance << "." << endl;
473 //*out << Verbose(1) << "Checking CoG distance of runner " << *runner->second.second << " " << h << " against triangle's side length spanned by (" << *left->second.second << "," << *right->second.second << ") of " << MinDistance << "." << endl;
474 if ((fabs(h / fabs(h) - MinDistance / fabs(MinDistance))
475 < MYEPSILON) && (h < MinDistance))
476 {
477 // throw out point
478 //*out << Verbose(1) << "Throwing out " << *runner->second.second << "." << endl;
479 BoundaryPoints[axis].erase(runner);
480 flag = true;
481 }
482 }
483 }
484 }
485 while (flag);
486 }
487 return BoundaryPoints;
488}
489;
490
491/** Determines greatest diameters of a cluster defined by its convex envelope.
492 * Looks at lines parallel to one axis and where they intersect on the projected planes
493 * \param *out output stream for debugging
494 * \param *BoundaryPoints NDIM set of boundary points defining the convex envelope on each projected plane
495 * \param *mol molecule structure representing the cluster
496 * \param IsAngstroem whether we have angstroem or atomic units
497 * \return NDIM array of the diameters
498 */
499double *
500GetDiametersOfCluster(ofstream *out, Boundaries *BoundaryPtr, molecule *mol,
501 bool IsAngstroem)
502{
503 // get points on boundary of NULL was given as parameter
504 bool BoundaryFreeFlag = false;
505 Boundaries *BoundaryPoints = BoundaryPtr;
506 if (BoundaryPoints == NULL)
507 {
508 BoundaryFreeFlag = true;
509 BoundaryPoints = GetBoundaryPoints(out, mol);
510 }
511 else
512 {
513 *out << Verbose(1) << "Using given boundary points set." << endl;
514 }
515 // determine biggest "diameter" of cluster for each axis
516 Boundaries::iterator Neighbour, OtherNeighbour;
517 double *GreatestDiameter = new double[NDIM];
518 for (int i = 0; i < NDIM; i++)
519 GreatestDiameter[i] = 0.;
520 double OldComponent, tmp, w1, w2;
521 Vector DistanceVector, OtherVector;
522 int component, Othercomponent;
523 for (int axis = 0; axis < NDIM; axis++)
524 { // regard each projected plane
525 //*out << Verbose(1) << "Current axis is " << axis << "." << endl;
526 for (int j = 0; j < 2; j++)
527 { // and for both axis on the current plane
528 component = (axis + j + 1) % NDIM;
529 Othercomponent = (axis + 1 + ((j + 1) & 1)) % NDIM;
530 //*out << Verbose(1) << "Current component is " << component << ", Othercomponent is " << Othercomponent << "." << endl;
531 for (Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner
532 != BoundaryPoints[axis].end(); runner++)
533 {
534 //*out << Verbose(2) << "Current runner is " << *(runner->second.second) << "." << endl;
535 // seek for the neighbours pair where the Othercomponent sign flips
536 Neighbour = runner;
537 Neighbour++;
538 if (Neighbour == BoundaryPoints[axis].end()) // make it wrap around
539 Neighbour = BoundaryPoints[axis].begin();
540 DistanceVector.CopyVector(&runner->second.second->x);
541 DistanceVector.SubtractVector(&Neighbour->second.second->x);
542 do
543 { // seek for neighbour pair where it flips
544 OldComponent = DistanceVector.x[Othercomponent];
545 Neighbour++;
546 if (Neighbour == BoundaryPoints[axis].end()) // make it wrap around
547 Neighbour = BoundaryPoints[axis].begin();
548 DistanceVector.CopyVector(&runner->second.second->x);
549 DistanceVector.SubtractVector(&Neighbour->second.second->x);
550 //*out << Verbose(3) << "OldComponent is " << OldComponent << ", new one is " << DistanceVector.x[Othercomponent] << "." << endl;
551 }
552 while ((runner != Neighbour) && (fabs(OldComponent / fabs(
553 OldComponent) - DistanceVector.x[Othercomponent] / fabs(
554 DistanceVector.x[Othercomponent])) < MYEPSILON)); // as long as sign does not flip
555 if (runner != Neighbour)
556 {
557 OtherNeighbour = Neighbour;
558 if (OtherNeighbour == BoundaryPoints[axis].begin()) // make it wrap around
559 OtherNeighbour = BoundaryPoints[axis].end();
560 OtherNeighbour--;
561 //*out << Verbose(2) << "The pair, where the sign of OtherComponent flips, is: " << *(Neighbour->second.second) << " and " << *(OtherNeighbour->second.second) << "." << endl;
562 // now we have found the pair: Neighbour and OtherNeighbour
563 OtherVector.CopyVector(&runner->second.second->x);
564 OtherVector.SubtractVector(&OtherNeighbour->second.second->x);
565 //*out << Verbose(2) << "Distances to Neighbour and OtherNeighbour are " << DistanceVector.x[component] << " and " << OtherVector.x[component] << "." << endl;
566 //*out << Verbose(2) << "OtherComponents to Neighbour and OtherNeighbour are " << DistanceVector.x[Othercomponent] << " and " << OtherVector.x[Othercomponent] << "." << endl;
567 // do linear interpolation between points (is exact) to extract exact intersection between Neighbour and OtherNeighbour
568 w1 = fabs(OtherVector.x[Othercomponent]);
569 w2 = fabs(DistanceVector.x[Othercomponent]);
570 tmp = fabs((w1 * DistanceVector.x[component] + w2
571 * OtherVector.x[component]) / (w1 + w2));
572 // mark if it has greater diameter
573 //*out << Verbose(2) << "Comparing current greatest " << GreatestDiameter[component] << " to new " << tmp << "." << endl;
574 GreatestDiameter[component] = (GreatestDiameter[component]
575 > tmp) ? GreatestDiameter[component] : tmp;
576 } //else
577 //*out << Verbose(2) << "Saw no sign flip, probably top or bottom node." << endl;
578 }
579 }
580 }
581 *out << Verbose(0) << "RESULT: The biggest diameters are "
582 << GreatestDiameter[0] << " and " << GreatestDiameter[1] << " and "
583 << GreatestDiameter[2] << " " << (IsAngstroem ? "angstrom"
584 : "atomiclength") << "." << endl;
585
586 // free reference lists
587 if (BoundaryFreeFlag)
588 delete[] (BoundaryPoints);
589
590 return GreatestDiameter;
591}
592;
593
594/** Creates the objects in a VRML file.
595 * \param *out output stream for debugging
596 * \param *vrmlfile output stream for tecplot data
597 * \param *Tess Tesselation structure with constructed triangles
598 * \param *mol molecule structure with atom positions
599 */
600void write_vrml_file(ofstream *out, ofstream *vrmlfile, class Tesselation *Tess, class molecule *mol)
601{
602 atom *Walker = mol->start;
603 bond *Binder = mol->first;
604 int i;
605 Vector *center = mol->DetermineCenterOfAll(out);
606 if (vrmlfile != NULL) {
607 //cout << Verbose(1) << "Writing Raster3D file ... ";
608 *vrmlfile << "#VRML V2.0 utf8" << endl;
609 *vrmlfile << "#Created by molecuilder" << endl;
610 *vrmlfile << "#All atoms as spheres" << endl;
611 while (Walker->next != mol->end) {
612 Walker = Walker->next;
613 *vrmlfile << "Sphere {" << endl << " "; // 2 is sphere type
614 for (i=0;i<NDIM;i++)
615 *vrmlfile << Walker->x.x[i]+center->x[i] << " ";
616 *vrmlfile << "\t0.1\t1. 1. 1." << endl; // radius 0.05 and white as colour
617 }
618
619 *vrmlfile << "# All bonds as vertices" << endl;
620 while (Binder->next != mol->last) {
621 Binder = Binder->next;
622 *vrmlfile << "3" << endl << " "; // 2 is round-ended cylinder type
623 for (i=0;i<NDIM;i++)
624 *vrmlfile << Binder->leftatom->x.x[i]+center->x[i] << " ";
625 *vrmlfile << "\t0.03\t";
626 for (i=0;i<NDIM;i++)
627 *vrmlfile << Binder->rightatom->x.x[i]+center->x[i] << " ";
628 *vrmlfile << "\t0.03\t0. 0. 1." << endl; // radius 0.05 and blue as colour
629 }
630
631 *vrmlfile << "# All tesselation triangles" << endl;
632 for (TriangleMap::iterator TriangleRunner = Tess->TrianglesOnBoundary.begin(); TriangleRunner != Tess->TrianglesOnBoundary.end(); TriangleRunner++) {
633 *vrmlfile << "1" << endl << " "; // 1 is triangle type
634 for (i=0;i<3;i++) { // print each node
635 for (int j=0;j<NDIM;j++) // and for each node all NDIM coordinates
636 *vrmlfile << TriangleRunner->second->endpoints[i]->node->x.x[j]+center->x[j] << " ";
637 *vrmlfile << "\t";
638 }
639 *vrmlfile << "1. 0. 0." << endl; // red as colour
640 *vrmlfile << "18" << endl << " 0.5 0.5 0.5" << endl; // 18 is transparency type for previous object
641 }
642 } else {
643 cerr << "ERROR: Given vrmlfile is " << vrmlfile << "." << endl;
644 }
645 delete(center);
646};
647
648/** Creates the objects in a raster3d file (renderable with a header.r3d).
649 * \param *out output stream for debugging
650 * \param *rasterfile output stream for tecplot data
651 * \param *Tess Tesselation structure with constructed triangles
652 * \param *mol molecule structure with atom positions
653 */
654void write_raster3d_file(ofstream *out, ofstream *rasterfile, class Tesselation *Tess, class molecule *mol)
655{
656 atom *Walker = mol->start;
657 bond *Binder = mol->first;
658 int i;
659 Vector *center = mol->DetermineCenterOfAll(out);
660 if (rasterfile != NULL) {
661 //cout << Verbose(1) << "Writing Raster3D file ... ";
662 *rasterfile << "# Raster3D object description, created by MoleCuilder" << endl;
663 *rasterfile << "@header.r3d" << endl;
664 *rasterfile << "# All atoms as spheres" << endl;
665 while (Walker->next != mol->end) {
666 Walker = Walker->next;
667 *rasterfile << "2" << endl << " "; // 2 is sphere type
668 for (i=0;i<NDIM;i++)
669 *rasterfile << Walker->x.x[i]+center->x[i] << " ";
670 *rasterfile << "\t0.1\t1. 1. 1." << endl; // radius 0.05 and white as colour
671 }
672
673 *rasterfile << "# All bonds as vertices" << endl;
674 while (Binder->next != mol->last) {
675 Binder = Binder->next;
676 *rasterfile << "3" << endl << " "; // 2 is round-ended cylinder type
677 for (i=0;i<NDIM;i++)
678 *rasterfile << Binder->leftatom->x.x[i]+center->x[i] << " ";
679 *rasterfile << "\t0.03\t";
680 for (i=0;i<NDIM;i++)
681 *rasterfile << Binder->rightatom->x.x[i]+center->x[i] << " ";
682 *rasterfile << "\t0.03\t0. 0. 1." << endl; // radius 0.05 and blue as colour
683 }
684
685 *rasterfile << "# All tesselation triangles" << endl;
686 *rasterfile << "8\n 25. -1. 1. 1. 1. 0.0 0 0 0 2\n SOLID 1.0 0.0 0.0\n BACKFACE 0.3 0.3 1.0 0 0\n";
687 for (TriangleMap::iterator TriangleRunner = Tess->TrianglesOnBoundary.begin(); TriangleRunner != Tess->TrianglesOnBoundary.end(); TriangleRunner++) {
688 *rasterfile << "1" << endl << " "; // 1 is triangle type
689 for (i=0;i<3;i++) { // print each node
690 for (int j=0;j<NDIM;j++) // and for each node all NDIM coordinates
691 *rasterfile << TriangleRunner->second->endpoints[i]->node->x.x[j]+center->x[j] << " ";
692 *rasterfile << "\t";
693 }
694 *rasterfile << "1. 0. 0." << endl; // red as colour
695 //*rasterfile << "18" << endl << " 0.5 0.5 0.5" << endl; // 18 is transparency type for previous object
696 }
697 *rasterfile << "9\n terminating special property\n";
698 } else {
699 cerr << "ERROR: Given rasterfile is " << rasterfile << "." << endl;
700 }
701 delete(center);
702};
703
704/** This function creates the tecplot file, displaying the tesselation of the hull.
705 * \param *out output stream for debugging
706 * \param *tecplot output stream for tecplot data
707 * \param N arbitrary number to differentiate various zones in the tecplot format
708 */
709void
710write_tecplot_file(ofstream *out, ofstream *tecplot,
711 class Tesselation *TesselStruct, class molecule *mol, int N)
712{
713 if (tecplot != NULL)
714 {
715 *tecplot << "TITLE = \"3D CONVEX SHELL\"" << endl;
716 *tecplot << "VARIABLES = \"X\" \"Y\" \"Z\"" << endl;
717 *tecplot << "ZONE T=\"TRIANGLES" << N << "\", N="
718 << TesselStruct->PointsOnBoundaryCount << ", E="
719 << TesselStruct->TrianglesOnBoundaryCount
720 << ", DATAPACKING=POINT, ZONETYPE=FETRIANGLE" << endl;
721 int *LookupList = new int[mol->AtomCount];
722 for (int i = 0; i < mol->AtomCount; i++)
723 LookupList[i] = -1;
724
725 // print atom coordinates
726 *out << Verbose(2) << "The following triangles were created:";
727 int Counter = 1;
728 atom *Walker = NULL;
729 for (PointMap::iterator target = TesselStruct->PointsOnBoundary.begin(); target
730 != TesselStruct->PointsOnBoundary.end(); target++)
731 {
732 Walker = target->second->node;
733 LookupList[Walker->nr] = Counter++;
734 *tecplot << Walker->x.x[0] << " " << Walker->x.x[1] << " "
735 << Walker->x.x[2] << " " << endl;
736 }
737 *tecplot << endl;
738 // print connectivity
739 for (TriangleMap::iterator runner =
740 TesselStruct->TrianglesOnBoundary.begin(); runner
741 != TesselStruct->TrianglesOnBoundary.end(); runner++)
742 {
743 *out << " " << runner->second->endpoints[0]->node->Name << "<->"
744 << runner->second->endpoints[1]->node->Name << "<->"
745 << runner->second->endpoints[2]->node->Name;
746 *tecplot << LookupList[runner->second->endpoints[0]->node->nr] << " "
747 << LookupList[runner->second->endpoints[1]->node->nr] << " "
748 << LookupList[runner->second->endpoints[2]->node->nr] << endl;
749 }
750 delete[] (LookupList);
751 *out << endl;
752 }
753}
754
755/** Determines the volume of a cluster.
756 * Determines first the convex envelope, then tesselates it and calculates its volume.
757 * \param *out output stream for debugging
758 * \param *filename filename prefix for output of vertex data
759 * \param *configuration needed for path to store convex envelope file
760 * \param *BoundaryPoints NDIM set of boundary points on the projected plane per axis, on return if desired
761 * \param *mol molecule structure representing the cluster
762 * \return determined volume of the cluster in cubed config:GetIsAngstroem()
763 */
764double
765VolumeOfConvexEnvelope(ofstream *out, const char *filename, config *configuration,
766 Boundaries *BoundaryPtr, molecule *mol)
767{
768 bool IsAngstroem = configuration->GetIsAngstroem();
769 atom *Walker = NULL;
770 struct Tesselation *TesselStruct = new Tesselation;
771 bool BoundaryFreeFlag = false;
772 Boundaries *BoundaryPoints = BoundaryPtr;
773 double volume = 0.;
774 double PyramidVolume = 0.;
775 double G, h;
776 Vector x, y;
777 double a, b, c;
778
779 //Find_non_convex_border(out, tecplot, *TesselStruct, mol); // Is now called from command line.
780
781 // 1. calculate center of gravity
782 *out << endl;
783 Vector *CenterOfGravity = mol->DetermineCenterOfGravity(out);
784
785 // 2. translate all points into CoG
786 *out << Verbose(1) << "Translating system to Center of Gravity." << endl;
787 Walker = mol->start;
788 while (Walker->next != mol->end)
789 {
790 Walker = Walker->next;
791 Walker->x.Translate(CenterOfGravity);
792 }
793
794 // 3. Find all points on the boundary
795 if (BoundaryPoints == NULL)
796 {
797 BoundaryFreeFlag = true;
798 BoundaryPoints = GetBoundaryPoints(out, mol);
799 }
800 else
801 {
802 *out << Verbose(1) << "Using given boundary points set." << endl;
803 }
804
805 // 4. fill the boundary point list
806 for (int axis = 0; axis < NDIM; axis++)
807 for (Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner
808 != BoundaryPoints[axis].end(); runner++)
809 {
810 TesselStruct->AddPoint(runner->second.second);
811 }
812
813 *out << Verbose(2) << "I found " << TesselStruct->PointsOnBoundaryCount
814 << " points on the convex boundary." << endl;
815 // now we have the whole set of edge points in the BoundaryList
816
817 // listing for debugging
818 // *out << Verbose(1) << "Listing PointsOnBoundary:";
819 // for(PointMap::iterator runner = PointsOnBoundary.begin(); runner != PointsOnBoundary.end(); runner++) {
820 // *out << " " << *runner->second;
821 // }
822 // *out << endl;
823
824 // 5a. guess starting triangle
825 TesselStruct->GuessStartingTriangle(out);
826
827 // 5b. go through all lines, that are not yet part of two triangles (only of one so far)
828 TesselStruct->TesselateOnBoundary(out, configuration, mol);
829
830 *out << Verbose(2) << "I created " << TesselStruct->TrianglesOnBoundaryCount
831 << " triangles with " << TesselStruct->LinesOnBoundaryCount
832 << " lines and " << TesselStruct->PointsOnBoundaryCount << " points."
833 << endl;
834
835 // 6a. Every triangle forms a pyramid with the center of gravity as its peak, sum up the volumes
836 *out << Verbose(1)
837 << "Calculating the volume of the pyramids formed out of triangles and center of gravity."
838 << endl;
839 for (TriangleMap::iterator runner = TesselStruct->TrianglesOnBoundary.begin(); runner
840 != TesselStruct->TrianglesOnBoundary.end(); runner++)
841 { // go through every triangle, calculate volume of its pyramid with CoG as peak
842 x.CopyVector(&runner->second->endpoints[0]->node->x);
843 x.SubtractVector(&runner->second->endpoints[1]->node->x);
844 y.CopyVector(&runner->second->endpoints[0]->node->x);
845 y.SubtractVector(&runner->second->endpoints[2]->node->x);
846 a = sqrt(runner->second->endpoints[0]->node->x.DistanceSquared(
847 &runner->second->endpoints[1]->node->x));
848 b = sqrt(runner->second->endpoints[0]->node->x.DistanceSquared(
849 &runner->second->endpoints[2]->node->x));
850 c = sqrt(runner->second->endpoints[2]->node->x.DistanceSquared(
851 &runner->second->endpoints[1]->node->x));
852 G = sqrt(((a + b + c) * (a + b + c) - 2 * (a * a + b * b + c * c)) / 16.); // area of tesselated triangle
853 x.MakeNormalVector(&runner->second->endpoints[0]->node->x,
854 &runner->second->endpoints[1]->node->x,
855 &runner->second->endpoints[2]->node->x);
856 x.Scale(runner->second->endpoints[1]->node->x.Projection(&x));
857 h = x.Norm(); // distance of CoG to triangle
858 PyramidVolume = (1. / 3.) * G * h; // this formula holds for _all_ pyramids (independent of n-edge base or (not) centered peak)
859 *out << Verbose(2) << "Area of triangle is " << G << " "
860 << (IsAngstroem ? "angstrom" : "atomiclength") << "^2, height is "
861 << h << " and the volume is " << PyramidVolume << " "
862 << (IsAngstroem ? "angstrom" : "atomiclength") << "^3." << endl;
863 volume += PyramidVolume;
864 }
865 *out << Verbose(0) << "RESULT: The summed volume is " << setprecision(10)
866 << volume << " " << (IsAngstroem ? "angstrom" : "atomiclength") << "^3."
867 << endl;
868
869 // 7. translate all points back from CoG
870 *out << Verbose(1) << "Translating system back from Center of Gravity."
871 << endl;
872 CenterOfGravity->Scale(-1);
873 Walker = mol->start;
874 while (Walker->next != mol->end)
875 {
876 Walker = Walker->next;
877 Walker->x.Translate(CenterOfGravity);
878 }
879
880 // 8. Store triangles in tecplot file
881 string OutputName(filename);
882 OutputName.append(TecplotSuffix);
883 ofstream *tecplot = new ofstream(OutputName.c_str());
884 write_tecplot_file(out, tecplot, TesselStruct, mol, 0);
885 tecplot->close();
886 delete(tecplot);
887
888 // free reference lists
889 if (BoundaryFreeFlag)
890 delete[] (BoundaryPoints);
891
892 return volume;
893}
894;
895
896/** Creates multiples of the by \a *mol given cluster and suspends them in water with a given final density.
897 * We get cluster volume by VolumeOfConvexEnvelope() and its diameters by GetDiametersOfCluster()
898 * \param *out output stream for debugging
899 * \param *configuration needed for path to store convex envelope file
900 * \param *mol molecule structure representing the cluster
901 * \param ClusterVolume guesstimated cluster volume, if equal 0 we used VolumeOfConvexEnvelope() instead.
902 * \param celldensity desired average density in final cell
903 */
904void
905PrepareClustersinWater(ofstream *out, config *configuration, molecule *mol,
906 double ClusterVolume, double celldensity)
907{
908 // transform to PAS
909 mol->PrincipalAxisSystem(out, true);
910
911 // some preparations beforehand
912 bool IsAngstroem = configuration->GetIsAngstroem();
913 Boundaries *BoundaryPoints = GetBoundaryPoints(out, mol);
914 double clustervolume;
915 if (ClusterVolume == 0)
916 clustervolume = VolumeOfConvexEnvelope(out, NULL, configuration,
917 BoundaryPoints, mol);
918 else
919 clustervolume = ClusterVolume;
920 double *GreatestDiameter = GetDiametersOfCluster(out, BoundaryPoints, mol,
921 IsAngstroem);
922 Vector BoxLengths;
923 int repetition[NDIM] =
924 { 1, 1, 1 };
925 int TotalNoClusters = 1;
926 for (int i = 0; i < NDIM; i++)
927 TotalNoClusters *= repetition[i];
928
929 // sum up the atomic masses
930 double totalmass = 0.;
931 atom *Walker = mol->start;
932 while (Walker->next != mol->end)
933 {
934 Walker = Walker->next;
935 totalmass += Walker->type->mass;
936 }
937 *out << Verbose(0) << "RESULT: The summed mass is " << setprecision(10)
938 << totalmass << " atomicmassunit." << endl;
939
940 *out << Verbose(0) << "RESULT: The average density is " << setprecision(10)
941 << totalmass / clustervolume << " atomicmassunit/"
942 << (IsAngstroem ? "angstrom" : "atomiclength") << "^3." << endl;
943
944 // solve cubic polynomial
945 *out << Verbose(1) << "Solving equidistant suspension in water problem ..."
946 << endl;
947 double cellvolume;
948 if (IsAngstroem)
949 cellvolume = (TotalNoClusters * totalmass / SOLVENTDENSITY_A - (totalmass
950 / clustervolume)) / (celldensity - 1);
951 else
952 cellvolume = (TotalNoClusters * totalmass / SOLVENTDENSITY_a0 - (totalmass
953 / clustervolume)) / (celldensity - 1);
954 *out << Verbose(1) << "Cellvolume needed for a density of " << celldensity
955 << " g/cm^3 is " << cellvolume << " " << (IsAngstroem ? "angstrom"
956 : "atomiclength") << "^3." << endl;
957
958 double minimumvolume = TotalNoClusters * (GreatestDiameter[0]
959 * GreatestDiameter[1] * GreatestDiameter[2]);
960 *out << Verbose(1)
961 << "Minimum volume of the convex envelope contained in a rectangular box is "
962 << minimumvolume << " atomicmassunit/" << (IsAngstroem ? "angstrom"
963 : "atomiclength") << "^3." << endl;
964 if (minimumvolume > cellvolume)
965 {
966 cerr << Verbose(0)
967 << "ERROR: the containing box already has a greater volume than the envisaged cell volume!"
968 << endl;
969 cout << Verbose(0)
970 << "Setting Box dimensions to minimum possible, the greatest diameters."
971 << endl;
972 for (int i = 0; i < NDIM; i++)
973 BoxLengths.x[i] = GreatestDiameter[i];
974 mol->CenterEdge(out, &BoxLengths);
975 }
976 else
977 {
978 BoxLengths.x[0] = (repetition[0] * GreatestDiameter[0] + repetition[1]
979 * GreatestDiameter[1] + repetition[2] * GreatestDiameter[2]);
980 BoxLengths.x[1] = (repetition[0] * repetition[1] * GreatestDiameter[0]
981 * GreatestDiameter[1] + repetition[0] * repetition[2]
982 * GreatestDiameter[0] * GreatestDiameter[2] + repetition[1]
983 * repetition[2] * GreatestDiameter[1] * GreatestDiameter[2]);
984 BoxLengths.x[2] = minimumvolume - cellvolume;
985 double x0 = 0., x1 = 0., x2 = 0.;
986 if (gsl_poly_solve_cubic(BoxLengths.x[0], BoxLengths.x[1],
987 BoxLengths.x[2], &x0, &x1, &x2) == 1) // either 1 or 3 on return
988 *out << Verbose(0) << "RESULT: The resulting spacing is: " << x0
989 << " ." << endl;
990 else
991 {
992 *out << Verbose(0) << "RESULT: The resulting spacings are: " << x0
993 << " and " << x1 << " and " << x2 << " ." << endl;
994 x0 = x2; // sorted in ascending order
995 }
996
997 cellvolume = 1;
998 for (int i = 0; i < NDIM; i++)
999 {
1000 BoxLengths.x[i] = repetition[i] * (x0 + GreatestDiameter[i]);
1001 cellvolume *= BoxLengths.x[i];
1002 }
1003
1004 // set new box dimensions
1005 *out << Verbose(0) << "Translating to box with these boundaries." << endl;
1006 mol->CenterInBox((ofstream *) &cout, &BoxLengths);
1007 }
1008 // update Box of atoms by boundary
1009 mol->SetBoxDimension(&BoxLengths);
1010 *out << Verbose(0) << "RESULT: The resulting cell dimensions are: "
1011 << BoxLengths.x[0] << " and " << BoxLengths.x[1] << " and "
1012 << BoxLengths.x[2] << " with total volume of " << cellvolume << " "
1013 << (IsAngstroem ? "angstrom" : "atomiclength") << "^3." << endl;
1014}
1015;
1016
1017// =========================================================== class TESSELATION ===========================================
1018
1019/** Constructor of class Tesselation.
1020 */
1021Tesselation::Tesselation()
1022{
1023 PointsOnBoundaryCount = 0;
1024 LinesOnBoundaryCount = 0;
1025 TrianglesOnBoundaryCount = 0;
1026 TriangleFilesWritten = 0;
1027}
1028;
1029
1030/** Constructor of class Tesselation.
1031 * We have to free all points, lines and triangles.
1032 */
1033Tesselation::~Tesselation()
1034{
1035 cout << Verbose(1) << "Free'ing TesselStruct ... " << endl;
1036 for (TriangleMap::iterator runner = TrianglesOnBoundary.begin(); runner != TrianglesOnBoundary.end(); runner++) {
1037 if (runner->second != NULL) {
1038 delete (runner->second);
1039 runner->second = NULL;
1040 } else
1041 cerr << "ERROR: The triangle " << runner->first << " has already been free'd." << endl;
1042 }
1043}
1044;
1045
1046/** Gueses first starting triangle of the convex envelope.
1047 * We guess the starting triangle by taking the smallest distance between two points and looking for a fitting third.
1048 * \param *out output stream for debugging
1049 * \param PointsOnBoundary set of boundary points defining the convex envelope of the cluster
1050 */
1051void
1052Tesselation::GuessStartingTriangle(ofstream *out)
1053{
1054 // 4b. create a starting triangle
1055 // 4b1. create all distances
1056 DistanceMultiMap DistanceMMap;
1057 double distance, tmp;
1058 Vector PlaneVector, TrialVector;
1059 PointMap::iterator A, B, C; // three nodes of the first triangle
1060 A = PointsOnBoundary.begin(); // the first may be chosen arbitrarily
1061
1062 // with A chosen, take each pair B,C and sort
1063 if (A != PointsOnBoundary.end())
1064 {
1065 B = A;
1066 B++;
1067 for (; B != PointsOnBoundary.end(); B++)
1068 {
1069 C = B;
1070 C++;
1071 for (; C != PointsOnBoundary.end(); C++)
1072 {
1073 tmp = A->second->node->x.DistanceSquared(&B->second->node->x);
1074 distance = tmp * tmp;
1075 tmp = A->second->node->x.DistanceSquared(&C->second->node->x);
1076 distance += tmp * tmp;
1077 tmp = B->second->node->x.DistanceSquared(&C->second->node->x);
1078 distance += tmp * tmp;
1079 DistanceMMap.insert(DistanceMultiMapPair(distance, pair<
1080 PointMap::iterator, PointMap::iterator> (B, C)));
1081 }
1082 }
1083 }
1084 // // listing distances
1085 // *out << Verbose(1) << "Listing DistanceMMap:";
1086 // for(DistanceMultiMap::iterator runner = DistanceMMap.begin(); runner != DistanceMMap.end(); runner++) {
1087 // *out << " " << runner->first << "(" << *runner->second.first->second << ", " << *runner->second.second->second << ")";
1088 // }
1089 // *out << endl;
1090 // 4b2. pick three baselines forming a triangle
1091 // 1. we take from the smallest sum of squared distance as the base line BC (with peak A) onward as the triangle candidate
1092 DistanceMultiMap::iterator baseline = DistanceMMap.begin();
1093 for (; baseline != DistanceMMap.end(); baseline++)
1094 {
1095 // we take from the smallest sum of squared distance as the base line BC (with peak A) onward as the triangle candidate
1096 // 2. next, we have to check whether all points reside on only one side of the triangle
1097 // 3. construct plane vector
1098 PlaneVector.MakeNormalVector(&A->second->node->x,
1099 &baseline->second.first->second->node->x,
1100 &baseline->second.second->second->node->x);
1101 *out << Verbose(2) << "Plane vector of candidate triangle is ";
1102 PlaneVector.Output(out);
1103 *out << endl;
1104 // 4. loop over all points
1105 double sign = 0.;
1106 PointMap::iterator checker = PointsOnBoundary.begin();
1107 for (; checker != PointsOnBoundary.end(); checker++)
1108 {
1109 // (neglecting A,B,C)
1110 if ((checker == A) || (checker == baseline->second.first) || (checker
1111 == baseline->second.second))
1112 continue;
1113 // 4a. project onto plane vector
1114 TrialVector.CopyVector(&checker->second->node->x);
1115 TrialVector.SubtractVector(&A->second->node->x);
1116 distance = TrialVector.Projection(&PlaneVector);
1117 if (fabs(distance) < 1e-4) // we need to have a small epsilon around 0 which is still ok
1118 continue;
1119 *out << Verbose(3) << "Projection of " << checker->second->node->Name
1120 << " yields distance of " << distance << "." << endl;
1121 tmp = distance / fabs(distance);
1122 // 4b. Any have different sign to than before? (i.e. would lie outside convex hull with this starting triangle)
1123 if ((sign != 0) && (tmp != sign))
1124 {
1125 // 4c. If so, break 4. loop and continue with next candidate in 1. loop
1126 *out << Verbose(2) << "Current candidates: "
1127 << A->second->node->Name << ","
1128 << baseline->second.first->second->node->Name << ","
1129 << baseline->second.second->second->node->Name << " leave "
1130 << checker->second->node->Name << " outside the convex hull."
1131 << endl;
1132 break;
1133 }
1134 else
1135 { // note the sign for later
1136 *out << Verbose(2) << "Current candidates: "
1137 << A->second->node->Name << ","
1138 << baseline->second.first->second->node->Name << ","
1139 << baseline->second.second->second->node->Name << " leave "
1140 << checker->second->node->Name << " inside the convex hull."
1141 << endl;
1142 sign = tmp;
1143 }
1144 // 4d. Check whether the point is inside the triangle (check distance to each node
1145 tmp = checker->second->node->x.DistanceSquared(&A->second->node->x);
1146 int innerpoint = 0;
1147 if ((tmp < A->second->node->x.DistanceSquared(
1148 &baseline->second.first->second->node->x)) && (tmp
1149 < A->second->node->x.DistanceSquared(
1150 &baseline->second.second->second->node->x)))
1151 innerpoint++;
1152 tmp = checker->second->node->x.DistanceSquared(
1153 &baseline->second.first->second->node->x);
1154 if ((tmp < baseline->second.first->second->node->x.DistanceSquared(
1155 &A->second->node->x)) && (tmp
1156 < baseline->second.first->second->node->x.DistanceSquared(
1157 &baseline->second.second->second->node->x)))
1158 innerpoint++;
1159 tmp = checker->second->node->x.DistanceSquared(
1160 &baseline->second.second->second->node->x);
1161 if ((tmp < baseline->second.second->second->node->x.DistanceSquared(
1162 &baseline->second.first->second->node->x)) && (tmp
1163 < baseline->second.second->second->node->x.DistanceSquared(
1164 &A->second->node->x)))
1165 innerpoint++;
1166 // 4e. If so, break 4. loop and continue with next candidate in 1. loop
1167 if (innerpoint == 3)
1168 break;
1169 }
1170 // 5. come this far, all on same side? Then break 1. loop and construct triangle
1171 if (checker == PointsOnBoundary.end())
1172 {
1173 *out << "Looks like we have a candidate!" << endl;
1174 break;
1175 }
1176 }
1177 if (baseline != DistanceMMap.end())
1178 {
1179 BPS[0] = baseline->second.first->second;
1180 BPS[1] = baseline->second.second->second;
1181 BLS[0] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1182 BPS[0] = A->second;
1183 BPS[1] = baseline->second.second->second;
1184 BLS[1] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1185 BPS[0] = baseline->second.first->second;
1186 BPS[1] = A->second;
1187 BLS[2] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1188
1189 // 4b3. insert created triangle
1190 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
1191 TrianglesOnBoundary.insert(TrianglePair(TrianglesOnBoundaryCount, BTS));
1192 TrianglesOnBoundaryCount++;
1193 for (int i = 0; i < NDIM; i++)
1194 {
1195 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount, BTS->lines[i]));
1196 LinesOnBoundaryCount++;
1197 }
1198
1199 *out << Verbose(1) << "Starting triangle is " << *BTS << "." << endl;
1200 }
1201 else
1202 {
1203 *out << Verbose(1) << "No starting triangle found." << endl;
1204 exit(255);
1205 }
1206}
1207;
1208
1209/** Tesselates the convex envelope of a cluster from a single starting triangle.
1210 * The starting triangle is made out of three baselines. Each line in the final tesselated cluster may belong to at most
1211 * 2 triangles. Hence, we go through all current lines:
1212 * -# if the lines contains to only one triangle
1213 * -# We search all points in the boundary
1214 * -# if the triangle with the baseline and the current point has the smallest of angles (comparison between normal vectors
1215 * -# if the triangle is in forward direction of the baseline (at most 90 degrees angle between vector orthogonal to
1216 * baseline in triangle plane pointing out of the triangle and normal vector of new triangle)
1217 * -# then we have a new triangle, whose baselines we again add (or increase their TriangleCount)
1218 * \param *out output stream for debugging
1219 * \param *configuration for IsAngstroem
1220 * \param *mol the cluster as a molecule structure
1221 */
1222void
1223Tesselation::TesselateOnBoundary(ofstream *out, config *configuration,
1224 molecule *mol)
1225{
1226 bool flag;
1227 PointMap::iterator winner;
1228 class BoundaryPointSet *peak = NULL;
1229 double SmallestAngle, TempAngle;
1230 Vector NormalVector, VirtualNormalVector, CenterVector, TempVector,
1231 PropagationVector;
1232 LineMap::iterator LineChecker[2];
1233 do
1234 {
1235 flag = false;
1236 for (LineMap::iterator baseline = LinesOnBoundary.begin(); baseline
1237 != LinesOnBoundary.end(); baseline++)
1238 if (baseline->second->TrianglesCount == 1)
1239 {
1240 *out << Verbose(2) << "Current baseline is between "
1241 << *(baseline->second) << "." << endl;
1242 // 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)
1243 SmallestAngle = M_PI;
1244 BTS = baseline->second->triangles.begin()->second; // there is only one triangle so far
1245 // get peak point with respect to this base line's only triangle
1246 for (int i = 0; i < 3; i++)
1247 if ((BTS->endpoints[i] != baseline->second->endpoints[0])
1248 && (BTS->endpoints[i] != baseline->second->endpoints[1]))
1249 peak = BTS->endpoints[i];
1250 *out << Verbose(3) << " and has peak " << *peak << "." << endl;
1251 // normal vector of triangle
1252 BTS->GetNormalVector(NormalVector);
1253 *out << Verbose(4) << "NormalVector of base triangle is ";
1254 NormalVector.Output(out);
1255 *out << endl;
1256 // offset to center of triangle
1257 CenterVector.Zero();
1258 for (int i = 0; i < 3; i++)
1259 CenterVector.AddVector(&BTS->endpoints[i]->node->x);
1260 CenterVector.Scale(1. / 3.);
1261 *out << Verbose(4) << "CenterVector of base triangle is ";
1262 CenterVector.Output(out);
1263 *out << endl;
1264 // vector in propagation direction (out of triangle)
1265 // project center vector onto triangle plane (points from intersection plane-NormalVector to plane-CenterVector intersection)
1266 TempVector.CopyVector(&baseline->second->endpoints[0]->node->x);
1267 TempVector.SubtractVector(&baseline->second->endpoints[1]->node->x);
1268 PropagationVector.MakeNormalVector(&TempVector, &NormalVector);
1269 TempVector.CopyVector(&CenterVector);
1270 TempVector.SubtractVector(&baseline->second->endpoints[0]->node->x); // TempVector is vector on triangle plane pointing from one baseline egde towards center!
1271 //*out << Verbose(2) << "Projection of propagation onto temp: " << PropagationVector.Projection(&TempVector) << "." << endl;
1272 if (PropagationVector.Projection(&TempVector) > 0) // make sure normal propagation vector points outward from baseline
1273 PropagationVector.Scale(-1.);
1274 *out << Verbose(4) << "PropagationVector of base triangle is ";
1275 PropagationVector.Output(out);
1276 *out << endl;
1277 winner = PointsOnBoundary.end();
1278 for (PointMap::iterator target = PointsOnBoundary.begin(); target
1279 != PointsOnBoundary.end(); target++)
1280 if ((target->second != baseline->second->endpoints[0])
1281 && (target->second != baseline->second->endpoints[1]))
1282 { // don't take the same endpoints
1283 *out << Verbose(3) << "Target point is " << *(target->second)
1284 << ":";
1285 bool continueflag = true;
1286
1287 VirtualNormalVector.CopyVector(
1288 &baseline->second->endpoints[0]->node->x);
1289 VirtualNormalVector.AddVector(
1290 &baseline->second->endpoints[0]->node->x);
1291 VirtualNormalVector.Scale(-1. / 2.); // points now to center of base line
1292 VirtualNormalVector.AddVector(&target->second->node->x); // points from center of base line to target
1293 TempAngle = VirtualNormalVector.Angle(&PropagationVector);
1294 continueflag = continueflag && (TempAngle < (M_PI/2.)); // no bends bigger than Pi/2 (90 degrees)
1295 if (!continueflag)
1296 {
1297 *out << Verbose(4)
1298 << "Angle between propagation direction and base line to "
1299 << *(target->second) << " is " << TempAngle
1300 << ", bad direction!" << endl;
1301 continue;
1302 }
1303 else
1304 *out << Verbose(4)
1305 << "Angle between propagation direction and base line to "
1306 << *(target->second) << " is " << TempAngle
1307 << ", good direction!" << endl;
1308 LineChecker[0] = baseline->second->endpoints[0]->lines.find(
1309 target->first);
1310 LineChecker[1] = baseline->second->endpoints[1]->lines.find(
1311 target->first);
1312 // if (LineChecker[0] != baseline->second->endpoints[0]->lines.end())
1313 // *out << Verbose(4) << *(baseline->second->endpoints[0]) << " has line " << *(LineChecker[0]->second) << " to " << *(target->second) << " as endpoint with " << LineChecker[0]->second->TrianglesCount << " triangles." << endl;
1314 // else
1315 // *out << Verbose(4) << *(baseline->second->endpoints[0]) << " has no line to " << *(target->second) << " as endpoint." << endl;
1316 // if (LineChecker[1] != baseline->second->endpoints[1]->lines.end())
1317 // *out << Verbose(4) << *(baseline->second->endpoints[1]) << " has line " << *(LineChecker[1]->second) << " to " << *(target->second) << " as endpoint with " << LineChecker[1]->second->TrianglesCount << " triangles." << endl;
1318 // else
1319 // *out << Verbose(4) << *(baseline->second->endpoints[1]) << " has no line to " << *(target->second) << " as endpoint." << endl;
1320 // check first endpoint (if any connecting line goes to target or at least not more than 1)
1321 continueflag = continueflag && (((LineChecker[0]
1322 == baseline->second->endpoints[0]->lines.end())
1323 || (LineChecker[0]->second->TrianglesCount == 1)));
1324 if (!continueflag)
1325 {
1326 *out << Verbose(4) << *(baseline->second->endpoints[0])
1327 << " has line " << *(LineChecker[0]->second)
1328 << " to " << *(target->second)
1329 << " as endpoint with "
1330 << LineChecker[0]->second->TrianglesCount
1331 << " triangles." << endl;
1332 continue;
1333 }
1334 // check second endpoint (if any connecting line goes to target or at least not more than 1)
1335 continueflag = continueflag && (((LineChecker[1]
1336 == baseline->second->endpoints[1]->lines.end())
1337 || (LineChecker[1]->second->TrianglesCount == 1)));
1338 if (!continueflag)
1339 {
1340 *out << Verbose(4) << *(baseline->second->endpoints[1])
1341 << " has line " << *(LineChecker[1]->second)
1342 << " to " << *(target->second)
1343 << " as endpoint with "
1344 << LineChecker[1]->second->TrianglesCount
1345 << " triangles." << endl;
1346 continue;
1347 }
1348 // check whether the envisaged triangle does not already exist (if both lines exist and have same endpoint)
1349 continueflag = continueflag && (!(((LineChecker[0]
1350 != baseline->second->endpoints[0]->lines.end())
1351 && (LineChecker[1]
1352 != baseline->second->endpoints[1]->lines.end())
1353 && (GetCommonEndpoint(LineChecker[0]->second,
1354 LineChecker[1]->second) == peak))));
1355 if (!continueflag)
1356 {
1357 *out << Verbose(4) << "Current target is peak!" << endl;
1358 continue;
1359 }
1360 // in case NOT both were found
1361 if (continueflag)
1362 { // create virtually this triangle, get its normal vector, calculate angle
1363 flag = true;
1364 VirtualNormalVector.MakeNormalVector(
1365 &baseline->second->endpoints[0]->node->x,
1366 &baseline->second->endpoints[1]->node->x,
1367 &target->second->node->x);
1368 // make it always point inward
1369 if (baseline->second->endpoints[0]->node->x.Projection(
1370 &VirtualNormalVector) > 0)
1371 VirtualNormalVector.Scale(-1.);
1372 // calculate angle
1373 TempAngle = NormalVector.Angle(&VirtualNormalVector);
1374 *out << Verbose(4) << "NormalVector is ";
1375 VirtualNormalVector.Output(out);
1376 *out << " and the angle is " << TempAngle << "." << endl;
1377 if (SmallestAngle > TempAngle)
1378 { // set to new possible winner
1379 SmallestAngle = TempAngle;
1380 winner = target;
1381 }
1382 }
1383 }
1384 // 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
1385 if (winner != PointsOnBoundary.end())
1386 {
1387 *out << Verbose(2) << "Winning target point is "
1388 << *(winner->second) << " with angle " << SmallestAngle
1389 << "." << endl;
1390 // create the lins of not yet present
1391 BLS[0] = baseline->second;
1392 // 5c. add lines to the line set if those were new (not yet part of a triangle), delete lines that belong to two triangles)
1393 LineChecker[0] = baseline->second->endpoints[0]->lines.find(
1394 winner->first);
1395 LineChecker[1] = baseline->second->endpoints[1]->lines.find(
1396 winner->first);
1397 if (LineChecker[0]
1398 == baseline->second->endpoints[0]->lines.end())
1399 { // create
1400 BPS[0] = baseline->second->endpoints[0];
1401 BPS[1] = winner->second;
1402 BLS[1] = new class BoundaryLineSet(BPS,
1403 LinesOnBoundaryCount);
1404 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount,
1405 BLS[1]));
1406 LinesOnBoundaryCount++;
1407 }
1408 else
1409 BLS[1] = LineChecker[0]->second;
1410 if (LineChecker[1]
1411 == baseline->second->endpoints[1]->lines.end())
1412 { // create
1413 BPS[0] = baseline->second->endpoints[1];
1414 BPS[1] = winner->second;
1415 BLS[2] = new class BoundaryLineSet(BPS,
1416 LinesOnBoundaryCount);
1417 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount,
1418 BLS[2]));
1419 LinesOnBoundaryCount++;
1420 }
1421 else
1422 BLS[2] = LineChecker[1]->second;
1423 BTS = new class BoundaryTriangleSet(BLS,
1424 TrianglesOnBoundaryCount);
1425 TrianglesOnBoundary.insert(TrianglePair(
1426 TrianglesOnBoundaryCount, BTS));
1427 TrianglesOnBoundaryCount++;
1428 }
1429 else
1430 {
1431 *out << Verbose(1)
1432 << "I could not determine a winner for this baseline "
1433 << *(baseline->second) << "." << endl;
1434 }
1435
1436 // 5d. If the set of lines is not yet empty, go to 5. and continue
1437 }
1438 else
1439 *out << Verbose(2) << "Baseline candidate " << *(baseline->second)
1440 << " has a triangle count of "
1441 << baseline->second->TrianglesCount << "." << endl;
1442 }
1443 while (flag);
1444
1445}
1446;
1447
1448/** Adds an atom to the tesselation::PointsOnBoundary list.
1449 * \param *Walker atom to add
1450 */
1451void
1452Tesselation::AddPoint(atom *Walker)
1453{
1454 PointTestPair InsertUnique;
1455 BPS[0] = new class BoundaryPointSet(Walker);
1456 InsertUnique = PointsOnBoundary.insert(PointPair(Walker->nr, BPS[0]));
1457 if (InsertUnique.second) // if new point was not present before, increase counter
1458 PointsOnBoundaryCount++;
1459}
1460;
1461
1462/** Adds point to Tesselation::PointsOnBoundary if not yet present.
1463 * Tesselation::TPS is set to either this new BoundaryPointSet or to the existing one of not unique.
1464 * @param Candidate point to add
1465 * @param n index for this point in Tesselation::TPS array
1466 */
1467void
1468Tesselation::AddTrianglePoint(atom* Candidate, int n)
1469{
1470 PointTestPair InsertUnique;
1471 TPS[n] = new class BoundaryPointSet(Candidate);
1472 InsertUnique = PointsOnBoundary.insert(PointPair(Candidate->nr, TPS[n]));
1473 if (InsertUnique.second) // if new point was not present before, increase counter
1474 {
1475 PointsOnBoundaryCount++;
1476 }
1477 else
1478 {
1479 delete TPS[n];
1480 cout << Verbose(2) << "Atom " << *((InsertUnique.first)->second->node)
1481 << " gibt's schon in der PointMap." << endl;
1482 TPS[n] = (InsertUnique.first)->second;
1483 }
1484}
1485;
1486
1487/** Function tries to add line from current Points in BPS to BoundaryLineSet.
1488 * If successful it raises the line count and inserts the new line into the BLS,
1489 * if unsuccessful, it writes the line which had been present into the BLS, deleting the new constructed one.
1490 * @param *a first endpoint
1491 * @param *b second endpoint
1492 * @param n index of Tesselation::BLS giving the line with both endpoints
1493 */
1494void Tesselation::AddTriangleLine(class BoundaryPointSet *a, class BoundaryPointSet *b, int n) {
1495 bool insertNewLine = true;
1496
1497 if (a->lines.find(b->node->nr) != a->lines.end()) {
1498 LineMap::iterator FindLine;
1499 pair<LineMap::iterator,LineMap::iterator> FindPair;
1500 FindPair = a->lines.equal_range(b->node->nr);
1501
1502 for (FindLine = FindPair.first; FindLine != FindPair.second; ++FindLine) {
1503 // If there is a line with less than two attached triangles, we don't need a new line.
1504 if (FindLine->second->TrianglesCount < 2) {
1505 insertNewLine = false;
1506 cout << Verbose(2)
1507 << "Using existing line " << *FindLine->second << endl;
1508
1509 BPS[0] = FindLine->second->endpoints[0];
1510 BPS[1] = FindLine->second->endpoints[1];
1511 BLS[n] = FindLine->second;
1512
1513 break;
1514 }
1515 }
1516 }
1517
1518 if (insertNewLine) {
1519 AlwaysAddTriangleLine(a, b, n);
1520 }
1521}
1522;
1523
1524/**
1525 * Adds lines from each of the current points in the BPS to BoundaryLineSet.
1526 * Raises the line count and inserts the new line into the BLS.
1527 *
1528 * @param *a first endpoint
1529 * @param *b second endpoint
1530 * @param n index of Tesselation::BLS giving the line with both endpoints
1531 */
1532void Tesselation::AlwaysAddTriangleLine(class BoundaryPointSet *a, class BoundaryPointSet *b, int n)
1533{
1534 cout << Verbose(2)
1535 << "Adding line which has not been used before between "
1536 << *(a->node) << " and " << *(b->node) << "." << endl;
1537 BPS[0] = a;
1538 BPS[1] = b;
1539 BLS[n] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1540
1541 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount, BLS[n]));
1542 LinesOnBoundaryCount++;
1543};
1544
1545/** Function tries to add Triangle just created to Triangle and remarks if already existent (Failure of algorithm).
1546 * Furthermore it adds the triangle to all of its lines, in order to recognize those which are saturated later.
1547 */
1548void
1549Tesselation::AddTriangleToLines()
1550{
1551
1552 cout << Verbose(1) << "Adding triangle to its lines" << endl;
1553 int i = 0;
1554 TrianglesOnBoundary.insert(TrianglePair(TrianglesOnBoundaryCount, BTS));
1555 TrianglesOnBoundaryCount++;
1556
1557 /*
1558 * this is apparently done when constructing triangle
1559
1560 for (i=0; i<3; i++)
1561 {
1562 BLS[i]->AddTriangle(BTS);
1563 }
1564 */
1565}
1566;
1567
1568
1569double det_get(gsl_matrix *A, int inPlace) {
1570 /*
1571 inPlace = 1 => A is replaced with the LU decomposed copy.
1572 inPlace = 0 => A is retained, and a copy is used for LU.
1573 */
1574
1575 double det;
1576 int signum;
1577 gsl_permutation *p = gsl_permutation_alloc(A->size1);
1578 gsl_matrix *tmpA;
1579
1580 if (inPlace)
1581 tmpA = A;
1582 else {
1583 gsl_matrix *tmpA = gsl_matrix_alloc(A->size1, A->size2);
1584 gsl_matrix_memcpy(tmpA , A);
1585 }
1586
1587
1588 gsl_linalg_LU_decomp(tmpA , p , &signum);
1589 det = gsl_linalg_LU_det(tmpA , signum);
1590 gsl_permutation_free(p);
1591 if (! inPlace)
1592 gsl_matrix_free(tmpA);
1593
1594 return det;
1595};
1596
1597void get_sphere(Vector *center, Vector &a, Vector &b, Vector &c, double RADIUS)
1598{
1599 gsl_matrix *A = gsl_matrix_calloc(3,3);
1600 double m11, m12, m13, m14;
1601
1602 for(int i=0;i<3;i++) {
1603 gsl_matrix_set(A, i, 0, a.x[i]);
1604 gsl_matrix_set(A, i, 1, b.x[i]);
1605 gsl_matrix_set(A, i, 2, c.x[i]);
1606 }
1607 m11 = det_get(A, 1);
1608
1609 for(int i=0;i<3;i++) {
1610 gsl_matrix_set(A, i, 0, a.x[i]*a.x[i] + b.x[i]*b.x[i] + c.x[i]*c.x[i]);
1611 gsl_matrix_set(A, i, 1, b.x[i]);
1612 gsl_matrix_set(A, i, 2, c.x[i]);
1613 }
1614 m12 = det_get(A, 1);
1615
1616 for(int i=0;i<3;i++) {
1617 gsl_matrix_set(A, i, 0, a.x[i]*a.x[i] + b.x[i]*b.x[i] + c.x[i]*c.x[i]);
1618 gsl_matrix_set(A, i, 1, a.x[i]);
1619 gsl_matrix_set(A, i, 2, c.x[i]);
1620 }
1621 m13 = det_get(A, 1);
1622
1623 for(int i=0;i<3;i++) {
1624 gsl_matrix_set(A, i, 0, a.x[i]*a.x[i] + b.x[i]*b.x[i] + c.x[i]*c.x[i]);
1625 gsl_matrix_set(A, i, 1, a.x[i]);
1626 gsl_matrix_set(A, i, 2, b.x[i]);
1627 }
1628 m14 = det_get(A, 1);
1629
1630 if (fabs(m11) < MYEPSILON)
1631 cerr << "ERROR: three points are colinear." << endl;
1632
1633 center->x[0] = 0.5 * m12/ m11;
1634 center->x[1] = -0.5 * m13/ m11;
1635 center->x[2] = 0.5 * m14/ m11;
1636
1637 if (fabs(a.Distance(center) - RADIUS) > MYEPSILON)
1638 cerr << "ERROR: The given center is further way by " << fabs(a.Distance(center) - RADIUS) << " from a than RADIUS." << endl;
1639
1640 gsl_matrix_free(A);
1641};
1642
1643
1644
1645/**
1646 * Function returns center of sphere with RADIUS, which rests on points a, b, c
1647 * @param Center this vector will be used for return
1648 * @param a vector first point of triangle
1649 * @param b vector second point of triangle
1650 * @param c vector third point of triangle
1651 * @param *Umkreismittelpunkt new cneter point of circumference
1652 * @param Direction vector indicates up/down
1653 * @param AlternativeDirection vecotr, needed in case the triangles have 90 deg angle
1654 * @param Halfplaneindicator double indicates whether Direction is up or down
1655 * @param AlternativeIndicator doube indicates in case of orthogonal triangles which direction of AlternativeDirection is suitable
1656 * @param alpha double angle at a
1657 * @param beta double, angle at b
1658 * @param gamma, double, angle at c
1659 * @param Radius, double
1660 * @param Umkreisradius double radius of circumscribing circle
1661 */
1662void Get_center_of_sphere(Vector* Center, Vector a, Vector b, Vector c, Vector *NewUmkreismittelpunkt, Vector* Direction, Vector* AlternativeDirection,
1663 double HalfplaneIndicator, double AlternativeIndicator, double alpha, double beta, double gamma, double RADIUS, double Umkreisradius)
1664{
1665 Vector TempNormal, helper;
1666 double Restradius;
1667 Vector OtherCenter;
1668 cout << Verbose(3) << "Begin of Get_center_of_sphere.\n";
1669 Center->Zero();
1670 helper.CopyVector(&a);
1671 helper.Scale(sin(2.*alpha));
1672 Center->AddVector(&helper);
1673 helper.CopyVector(&b);
1674 helper.Scale(sin(2.*beta));
1675 Center->AddVector(&helper);
1676 helper.CopyVector(&c);
1677 helper.Scale(sin(2.*gamma));
1678 Center->AddVector(&helper);
1679 //*Center = a * sin(2.*alpha) + b * sin(2.*beta) + c * sin(2.*gamma) ;
1680 Center->Scale(1./(sin(2.*alpha) + sin(2.*beta) + sin(2.*gamma)));
1681 NewUmkreismittelpunkt->CopyVector(Center);
1682 cout << Verbose(4) << "Center of new circumference is " << *NewUmkreismittelpunkt << ".\n";
1683 // Here we calculated center of circumscribing circle, using barycentric coordinates
1684 cout << Verbose(4) << "Center of circumference is " << *Center << " in direction " << *Direction << ".\n";
1685
1686 TempNormal.CopyVector(&a);
1687 TempNormal.SubtractVector(&b);
1688 helper.CopyVector(&a);
1689 helper.SubtractVector(&c);
1690 TempNormal.VectorProduct(&helper);
1691 if (fabs(HalfplaneIndicator) < MYEPSILON)
1692 {
1693 if ((TempNormal.ScalarProduct(AlternativeDirection) <0 and AlternativeIndicator >0) or (TempNormal.ScalarProduct(AlternativeDirection) >0 and AlternativeIndicator <0))
1694 {
1695 TempNormal.Scale(-1);
1696 }
1697 }
1698 else
1699 {
1700 if (TempNormal.ScalarProduct(Direction)<0 && HalfplaneIndicator >0 || TempNormal.ScalarProduct(Direction)>0 && HalfplaneIndicator<0)
1701 {
1702 TempNormal.Scale(-1);
1703 }
1704 }
1705
1706 TempNormal.Normalize();
1707 Restradius = sqrt(RADIUS*RADIUS - Umkreisradius*Umkreisradius);
1708 cout << Verbose(4) << "Height of center of circumference to center of sphere is " << Restradius << ".\n";
1709 TempNormal.Scale(Restradius);
1710 cout << Verbose(4) << "Shift vector to sphere of circumference is " << TempNormal << ".\n";
1711
1712 Center->AddVector(&TempNormal);
1713 cout << Verbose(0) << "Center of sphere of circumference is " << *Center << ".\n";
1714 get_sphere(&OtherCenter, a, b, c, RADIUS);
1715 cout << Verbose(0) << "OtherCenter of sphere of circumference is " << OtherCenter << ".\n";
1716 cout << Verbose(3) << "End of Get_center_of_sphere.\n";
1717};
1718
1719
1720
1721/** This recursive function finds a third point, to form a triangle with two given ones.
1722 * Two atoms are fixed, a candidate is supplied, additionally two vectors for direction distinction, a Storage area to \
1723 * supply results to the calling function, the radius of the sphere which the triangle shall support and the molecule \
1724 * upon which we operate.
1725 * If the candidate is more fitting to support the sphere than the already stored atom is, then we write its general \
1726 * direction and angle into Storage.
1727 * We the determine the recursive level we have reached and if this is not on the threshold yet, call this function again, \
1728 * with all neighbours of the candidate.
1729 * @param a first point
1730 * @param b second point
1731 * *param c atom old third point of old triangle
1732 * @param Candidate base point along whose bonds to start looking from
1733 * @param Parent point to avoid during search as its wrong direction
1734 * @param RecursionLevel contains current recursion depth
1735 * @param Chord baseline vector of first and second point
1736 * @param direction1 second in plane vector (along with \a Chord) of the triangle the baseline belongs to
1737 * @param OldNormal normal of the triangle which the baseline belongs to
1738 * @param ReferencePoint Vector of center of circumscribing circle from which we look towards center of sphere
1739 * @param Opt_Candidate candidate reference to return
1740 * @param Storage array containing two angles of current Opt_Candidate
1741 * @param RADIUS radius of ball
1742 * @param mol molecule structure with atoms and bonds
1743 */
1744void Tesselation::Find_next_suitable_point_via_Angle_of_Sphere(atom* a, atom* b, atom* c, atom* Candidate, atom* Parent,
1745 int RecursionLevel, Vector *Chord, Vector *direction1, Vector *OldNormal, Vector ReferencePoint,
1746 atom*& Opt_Candidate, double *Storage, const double RADIUS, molecule* mol)
1747{
1748 cout << Verbose(2) << "Begin of Find_next_suitable_point_via_Angle_of_Sphere, recursion level " << RecursionLevel << ".\n";
1749 cout << Verbose(3) << "Candidate is "<< *Candidate << endl;
1750 cout << Verbose(4) << "Baseline vector is " << *Chord << "." << endl;
1751 cout << Verbose(4) << "ReferencePoint is " << ReferencePoint << "." << endl;
1752 cout << Verbose(4) << "Normal of base triangle is " << *OldNormal << "." << endl;
1753 cout << Verbose(4) << "Search direction is " << *direction1 << "." << endl;
1754 /* OldNormal is normal vector on the old triangle
1755 * direction1 is normal on the triangle line, from which we come, as well as on OldNormal.
1756 * Chord points from b to a!!!
1757 */
1758 Vector dif_a; //Vector from a to candidate
1759 Vector dif_b; //Vector from b to candidate
1760 Vector AngleCheck;
1761 Vector TempNormal, Umkreismittelpunkt;
1762 Vector Mittelpunkt;
1763
1764 double CurrentEpsilon = 0.1;
1765 double alpha, beta, gamma, SideA, SideB, SideC, sign, Umkreisradius, Restradius, Distance;
1766 double BallAngle, AlternativeSign;
1767 atom *Walker; // variable atom point
1768
1769 Vector NewUmkreismittelpunkt;
1770
1771 if (a != Candidate and b != Candidate and c != Candidate) {
1772 cout << Verbose(3) << "We have a unique candidate!" << endl;
1773 dif_a.CopyVector(&(a->x));
1774 dif_a.SubtractVector(&(Candidate->x));
1775 dif_b.CopyVector(&(b->x));
1776 dif_b.SubtractVector(&(Candidate->x));
1777 AngleCheck.CopyVector(&(Candidate->x));
1778 AngleCheck.SubtractVector(&(a->x));
1779 AngleCheck.ProjectOntoPlane(Chord);
1780
1781 SideA = dif_b.Norm();
1782 SideB = dif_a.Norm();
1783 SideC = Chord->Norm();
1784 //Chord->Scale(-1);
1785
1786 alpha = Chord->Angle(&dif_a);
1787 beta = M_PI - Chord->Angle(&dif_b);
1788 gamma = dif_a.Angle(&dif_b);
1789
1790 cout << Verbose(2) << "Base triangle has sides " << dif_a << ", " << dif_b << ", " << *Chord << " with angles " << alpha/M_PI*180. << ", " << beta/M_PI*180. << ", " << gamma/M_PI*180. << "." << endl;
1791
1792 if (fabs(M_PI - alpha - beta - gamma) > MYEPSILON) {
1793 cerr << Verbose(0) << "WARNING: sum of angles for base triangle " << (alpha + beta + gamma)/M_PI*180. << " != 180.\n";
1794 cout << Verbose(1) << "Base triangle has sides " << dif_a << ", " << dif_b << ", " << *Chord << " with angles " << alpha/M_PI*180. << ", " << beta/M_PI*180. << ", " << gamma/M_PI*180. << "." << endl;
1795 }
1796
1797 if ((M_PI*179./180. > alpha) && (M_PI*179./180. > beta) && (M_PI*179./180. > gamma)) {
1798 Umkreisradius = SideA / 2.0 / sin(alpha);
1799 //cout << Umkreisradius << endl;
1800 //cout << SideB / 2.0 / sin(beta) << endl;
1801 //cout << SideC / 2.0 / sin(gamma) << endl;
1802
1803 if (Umkreisradius < RADIUS) { //Checking whether ball will at least rest on points.
1804 cout << Verbose(3) << "Circle of circumference would fit: " << Umkreisradius << " < " << RADIUS << "." << endl;
1805 cout << Verbose(2) << "Candidate is "<< *Candidate << endl;
1806 sign = AngleCheck.ScalarProduct(direction1);
1807 if (fabs(sign)<MYEPSILON) {
1808 if (AngleCheck.ScalarProduct(OldNormal)<0) {
1809 sign =0;
1810 AlternativeSign=1;
1811 } else {
1812 sign =0;
1813 AlternativeSign=-1;
1814 }
1815 } else {
1816 sign /= fabs(sign);
1817 }
1818 if (sign >= 0) {
1819 cout << Verbose(3) << "Candidate is in search direction: " << sign << "." << endl;
1820
1821 Get_center_of_sphere(&Mittelpunkt, (a->x), (b->x), (Candidate->x), &NewUmkreismittelpunkt, OldNormal, direction1, sign, AlternativeSign, alpha, beta, gamma, RADIUS, Umkreisradius);
1822
1823 Mittelpunkt.SubtractVector(&ReferencePoint);
1824 cout << Verbose(3) << "Reference vector to sphere's center is " << Mittelpunkt << "." << endl;
1825
1826 BallAngle = Mittelpunkt.Angle(OldNormal);
1827 cout << Verbose(3) << "Angle between normal of base triangle and center of ball sphere is :" << BallAngle << "." << endl;
1828
1829 //cout << "direction1 is " << *direction1 << "." << endl;
1830 //cout << "Mittelpunkt is " << Mittelpunkt << "."<< endl;
1831
1832 //cout << Verbose(3) << "BallAngle is " << BallAngle << " Sign is " << sign << endl;
1833
1834 NewUmkreismittelpunkt.SubtractVector(&ReferencePoint);
1835
1836 if ((Mittelpunkt.ScalarProduct(direction1) >=0) || (fabs(NewUmkreismittelpunkt.Norm()) < MYEPSILON)) {
1837 if (Storage[0]< -1.5) { // first Candidate at all
1838 if (1) {//if (CheckPresenceOfTriangle((ofstream *)&cout,a,b,Candidate)) {
1839 cout << Verbose(2) << "First good candidate is " << *Candidate << " with ";
1840 Opt_Candidate = Candidate;
1841 Storage[0] = sign;
1842 Storage[1] = AlternativeSign;
1843 Storage[2] = BallAngle;
1844 cout << " angle " << Storage[2] << " and Up/Down "
1845 << Storage[0] << endl;
1846 } else
1847 cout << "Candidate " << *Candidate << " does not belong to a valid triangle." << endl;
1848 } else {
1849 if ( Storage[2] > BallAngle) {
1850 if (1) { //if (CheckPresenceOfTriangle((ofstream *)&cout,a,b,Candidate)) {
1851 cout << Verbose(2) << "Next better candidate is " << *Candidate << " with ";
1852 Opt_Candidate = Candidate;
1853 Storage[0] = sign;
1854 Storage[1] = AlternativeSign;
1855 Storage[2] = BallAngle;
1856 cout << " angle " << Storage[2] << " and Up/Down "
1857 << Storage[0] << endl;
1858 } else
1859 cout << "Candidate " << *Candidate << " does not belong to a valid triangle." << endl;
1860 } else {
1861 if (DEBUG) {
1862 cout << Verbose(3) << *Candidate << " looses against better candidate " << *Opt_Candidate << "." << endl;
1863 }
1864 }
1865 }
1866 } else {
1867 if (DEBUG) {
1868 cout << Verbose(3) << *Candidate << " refused due to Up/Down sign which is " << sign << endl;
1869 }
1870 }
1871 } else {
1872 if (DEBUG) {
1873 cout << Verbose(3) << *Candidate << " is not in search direction." << endl;
1874 }
1875 }
1876 } else {
1877 if (DEBUG) {
1878 cout << Verbose(3) << *Candidate << " would have circumference of " << Umkreisradius << " bigger than ball's radius " << RADIUS << "." << endl;
1879 }
1880 }
1881 } else {
1882 if (DEBUG) {
1883 cout << Verbose(0) << "Triangle consisting of " << *Candidate << ", " << *a << " and " << *b << " has an angle >150!" << endl;
1884 }
1885 }
1886 } else {
1887 if (DEBUG) {
1888 cout << Verbose(3) << *Candidate << " is either " << *a << " or " << *b << "." << endl;
1889 }
1890 }
1891
1892 if (RecursionLevel < 5) { // Seven is the recursion level threshold.
1893 for (int i = 0; i < mol->NumberOfBondsPerAtom[Candidate->nr]; i++) { // go through all bond
1894 Walker = mol->ListOfBondsPerAtom[Candidate->nr][i]->GetOtherAtom(Candidate);
1895 if (Walker == Parent) { // don't go back the same bond
1896 continue;
1897 } else {
1898 Find_next_suitable_point_via_Angle_of_Sphere(a, b, c, Walker, Candidate, RecursionLevel+1, Chord, direction1, OldNormal, ReferencePoint, Opt_Candidate, Storage, RADIUS, mol); //call function again
1899 }
1900 }
1901 }
1902 cout << Verbose(2) << "End of Find_next_suitable_point_via_Angle_of_Sphere, recursion level " << RecursionLevel << ".\n";
1903}
1904;
1905
1906
1907/** Constructs the center of the circumcircle defined by three points \a *a, \a *b and \a *c.
1908 * \param *Center new center on return
1909 * \param *a first point
1910 * \param *b second point
1911 * \param *c third point
1912 */
1913void GetCenterofCircumcircle(Vector *Center, Vector *a, Vector *b, Vector *c)
1914{
1915 Vector helper;
1916 double alpha, beta, gamma;
1917 Vector SideA, SideB, SideC;
1918 SideA.CopyVector(b);
1919 SideA.SubtractVector(c);
1920 SideB.CopyVector(c);
1921 SideB.SubtractVector(a);
1922 SideC.CopyVector(a);
1923 SideC.SubtractVector(b);
1924 alpha = M_PI - SideB.Angle(&SideC);
1925 beta = M_PI - SideC.Angle(&SideA);
1926 gamma = M_PI - SideA.Angle(&SideB);
1927 cout << Verbose(3) << "INFO: alpha = " << alpha/M_PI*180. << ", beta = " << beta/M_PI*180. << ", gamma = " << gamma/M_PI*180. << "." << endl;
1928 if (fabs(M_PI - alpha - beta - gamma) > HULLEPSILON)
1929 cerr << "Sum of angles " << (alpha+beta+gamma)/M_PI*180. << " > 180 degrees by " << fabs(M_PI - alpha - beta - gamma)/M_PI*180. << "!" << endl;
1930
1931 Center->Zero();
1932 helper.CopyVector(a);
1933 helper.Scale(sin(2.*alpha));
1934 Center->AddVector(&helper);
1935 helper.CopyVector(b);
1936 helper.Scale(sin(2.*beta));
1937 Center->AddVector(&helper);
1938 helper.CopyVector(c);
1939 helper.Scale(sin(2.*gamma));
1940 Center->AddVector(&helper);
1941 Center->Scale(1./(sin(2.*alpha) + sin(2.*beta) + sin(2.*gamma)));
1942};
1943
1944/** Returns the parameter "path length" for a given \a NewSphereCenter relative to \a OldSphereCenter on a circle on the plane \a CirclePlaneNormal with center \a CircleCenter and radius \a CircleRadius.
1945 * Test whether the \a NewSphereCenter is really on the given plane and in distance \a CircleRadius from \a CircleCenter.
1946 * It calculates the angle, making it unique on [0,2.*M_PI) by comparing to SearchDirection.
1947 * Also the new center is invalid if it the same as the old one and does not lie right above (\a NormalVector) the base line (\a CircleCenter).
1948 * \param CircleCenter Center of the parameter circle
1949 * \param CirclePlaneNormal normal vector to plane of the parameter circle
1950 * \param CircleRadius radius of the parameter circle
1951 * \param NewSphereCenter new center of a circumcircle
1952 * \param OldSphereCenter old center of a circumcircle, defining the zero "path length" on the parameter circle
1953 * \param NormalVector normal vector
1954 * \param SearchDirection search direction to make angle unique on return.
1955 * \return Angle between \a NewSphereCenter and \a OldSphereCenter relative to \a CircleCenter, 2.*M_PI if one test fails
1956 */
1957double GetPathLengthonCircumCircle(Vector &CircleCenter, Vector &CirclePlaneNormal, double CircleRadius, Vector &NewSphereCenter, Vector &OldSphereCenter, Vector &NormalVector, Vector &SearchDirection)
1958{
1959 Vector helper;
1960 double radius, alpha;
1961
1962 helper.CopyVector(&NewSphereCenter);
1963 // test whether new center is on the parameter circle's plane
1964 if (fabs(helper.ScalarProduct(&CirclePlaneNormal)) > HULLEPSILON) {
1965 cerr << "ERROR: Something's very wrong here: NewSphereCenter is not on the band's plane as desired by " <<fabs(helper.ScalarProduct(&CirclePlaneNormal)) << "!" << endl;
1966 helper.ProjectOntoPlane(&CirclePlaneNormal);
1967 }
1968 radius = helper.ScalarProduct(&helper);
1969 // test whether the new center vector has length of CircleRadius
1970 if (fabs(radius - CircleRadius) > HULLEPSILON)
1971 cerr << Verbose(1) << "ERROR: The projected center of the new sphere has radius " << radius << " instead of " << CircleRadius << "." << endl;
1972 alpha = helper.Angle(&OldSphereCenter);
1973 // make the angle unique by checking the halfplanes/search direction
1974 if (helper.ScalarProduct(&SearchDirection) < -HULLEPSILON) // acos is not unique on [0, 2.*M_PI), hence extra check to decide between two half intervals
1975 alpha = 2.*M_PI - alpha;
1976 cout << Verbose(2) << "INFO: RelativeNewSphereCenter is " << helper << ", RelativeOldSphereCenter is " << OldSphereCenter << " and resulting angle is " << alpha << "." << endl;
1977 radius = helper.Distance(&OldSphereCenter);
1978 helper.ProjectOntoPlane(&NormalVector);
1979 // check whether new center is somewhat away or at least right over the current baseline to prevent intersecting triangles
1980 if ((radius > HULLEPSILON) || (helper.Norm() < HULLEPSILON)) {
1981 cout << Verbose(2) << "INFO: Distance between old and new center is " << radius << " and between new center and baseline center is " << helper.Norm() << "." << endl;
1982 return alpha;
1983 } else {
1984 cout << Verbose(1) << "INFO: NewSphereCenter " << helper << " is too close to OldSphereCenter" << OldSphereCenter << "." << endl;
1985 return 2.*M_PI;
1986 }
1987};
1988
1989
1990/** This recursive function finds a third point, to form a triangle with two given ones.
1991 * The idea is as follows: A sphere with fixed radius is (almost) uniquely defined in space by three points
1992 * that sit on its boundary. Hence, when two points are given and we look for the (next) third point, then
1993 * the center of the sphere is still fixed up to a single parameter. The band of possible values
1994 * describes a circle in 3D-space. The old center of the sphere for the current base triangle gives
1995 * us the "null" on this circle, the new center of the candidate point will be some way along this
1996 * circle. The shorter the way the better is the candidate. Note that the direction is clearly given
1997 * by the normal vector of the base triangle that always points outwards by construction.
1998 * Hence, we construct a Center of this circle which sits right in the middle of the current base line.
1999 * We construct the normal vector that defines the plane this circle lies in, it is just in the
2000 * direction of the baseline. And finally, we need the radius of the circle, which is given by the rest
2001 * with respect to the length of the baseline and the sphere's fixed \a RADIUS.
2002 * Note that there is one difficulty: The circumcircle is uniquely defined, but for the circumsphere's center
2003 * there are two possibilities which becomes clear from the construction as seen below. Hence, we must check
2004 * both.
2005 * Note also that the acos() function is not unique on [0, 2.*M_PI). Hence, we need an additional check
2006 * to decide for one of the two possible angles. Therefore we need a SearchDirection and to make this check
2007 * sensible we need OldSphereCenter to be orthogonal to it. Either we construct SearchDirection orthogonal
2008 * right away, or -- what we do here -- we rotate the relative sphere centers such that this orthogonality
2009 * holds. Then, the normalized projection onto the SearchDirection is either +1 or -1 and thus states whether
2010 * the angle is uniquely in either (0,M_PI] or [M_PI, 2.*M_PI).
2011 * @param BaseTriangle BoundaryTriangleSet of the current base triangle with all three points
2012 * @param BaseLine BoundaryLineSet of BaseTriangle with the current base line
2013 * @param OptCandidate candidate reference on return
2014 * @param OptCandidateCenter candidate's sphere center on return
2015 * @param ShortestAngle the current path length on this circle band for the current Opt_Candidate
2016 * @param RADIUS radius of sphere
2017 * @param *LC LinkedCell structure with neighbouring atoms
2018 */
2019// void Find_next_suitable_point(class BoundaryTriangleSet *BaseTriangle, class BoundaryLineSet *BaseLine, atom*& OptCandidate, Vector *OptCandidateCenter, double *ShortestAngle, const double RADIUS, LinkedCell *LC)
2020// {
2021// atom *Walker = NULL;
2022// Vector CircleCenter; // center of the circle, i.e. of the band of sphere's centers
2023// Vector CirclePlaneNormal; // normal vector defining the plane this circle lives in
2024// Vector OldSphereCenter; // center of the sphere defined by the three points of BaseTriangle
2025// Vector NewSphereCenter; // center of the sphere defined by the two points of BaseLine and the one of Candidate, first possibility
2026// Vector OtherNewSphereCenter; // center of the sphere defined by the two points of BaseLine and the one of Candidate, second possibility
2027// Vector NewNormalVector; // normal vector of the Candidate's triangle
2028// Vector SearchDirection; // vector that points out of BaseTriangle and is orthonormal to its NormalVector (i.e. the desired direction for the best Candidate)
2029// Vector helper;
2030// LinkedAtoms *List = NULL;
2031// double CircleRadius; // radius of this circle
2032// double radius;
2033// double alpha, Otheralpha; // angles (i.e. parameter for the circle).
2034// double Nullalpha; // angle between OldSphereCenter and NormalVector of base triangle
2035// int N[NDIM], Nlower[NDIM], Nupper[NDIM];
2036// atom *Candidate = NULL;
2037//
2038// cout << Verbose(1) << "Begin of Find_next_suitable_point" << endl;
2039//
2040// cout << Verbose(2) << "INFO: NormalVector of BaseTriangle is " << BaseTriangle->NormalVector << "." << endl;
2041//
2042// // construct center of circle
2043// CircleCenter.CopyVector(&(BaseLine->endpoints[0]->node->x));
2044// CircleCenter.AddVector(&BaseLine->endpoints[1]->node->x);
2045// CircleCenter.Scale(0.5);
2046//
2047// // construct normal vector of circle
2048// CirclePlaneNormal.CopyVector(&BaseLine->endpoints[0]->node->x);
2049// CirclePlaneNormal.SubtractVector(&BaseLine->endpoints[1]->node->x);
2050//
2051// // calculate squared radius of circle
2052// radius = CirclePlaneNormal.ScalarProduct(&CirclePlaneNormal);
2053// if (radius/4. < RADIUS*RADIUS) {
2054// CircleRadius = RADIUS*RADIUS - radius/4.;
2055// CirclePlaneNormal.Normalize();
2056// cout << Verbose(2) << "INFO: CircleCenter is at " << CircleCenter << ", CirclePlaneNormal is " << CirclePlaneNormal << " with circle radius " << sqrt(CircleRadius) << "." << endl;
2057//
2058// // construct old center
2059// GetCenterofCircumcircle(&OldSphereCenter, &(BaseTriangle->endpoints[0]->node->x), &(BaseTriangle->endpoints[1]->node->x), &(BaseTriangle->endpoints[2]->node->x));
2060// helper.CopyVector(&BaseTriangle->NormalVector); // normal vector ensures that this is correct center of the two possible ones
2061// radius = BaseLine->endpoints[0]->node->x.DistanceSquared(&OldSphereCenter);
2062// helper.Scale(sqrt(RADIUS*RADIUS - radius));
2063// OldSphereCenter.AddVector(&helper);
2064// OldSphereCenter.SubtractVector(&CircleCenter);
2065// cout << Verbose(2) << "INFO: OldSphereCenter is at " << OldSphereCenter << "." << endl;
2066//
2067// // test whether old center is on the band's plane
2068// if (fabs(OldSphereCenter.ScalarProduct(&CirclePlaneNormal)) > HULLEPSILON) {
2069// cerr << "ERROR: Something's very wrong here: OldSphereCenter is not on the band's plane as desired by " << fabs(OldSphereCenter.ScalarProduct(&CirclePlaneNormal)) << "!" << endl;
2070// OldSphereCenter.ProjectOntoPlane(&CirclePlaneNormal);
2071// }
2072// radius = OldSphereCenter.ScalarProduct(&OldSphereCenter);
2073// if (fabs(radius - CircleRadius) < HULLEPSILON) {
2074//
2075// // construct SearchDirection
2076// SearchDirection.MakeNormalVector(&BaseTriangle->NormalVector, &CirclePlaneNormal);
2077// helper.CopyVector(&BaseLine->endpoints[0]->node->x);
2078// for(int i=0;i<3;i++) // just take next different endpoint
2079// if ((BaseTriangle->endpoints[i]->node != BaseLine->endpoints[0]->node) && (BaseTriangle->endpoints[i]->node != BaseLine->endpoints[1]->node)) {
2080// helper.SubtractVector(&BaseTriangle->endpoints[i]->node->x);
2081// }
2082// if (helper.ScalarProduct(&SearchDirection) < -HULLEPSILON) // ohoh, SearchDirection points inwards!
2083// SearchDirection.Scale(-1.);
2084// SearchDirection.ProjectOntoPlane(&OldSphereCenter);
2085// SearchDirection.Normalize();
2086// cout << Verbose(2) << "INFO: SearchDirection is " << SearchDirection << "." << endl;
2087// if (fabs(OldSphereCenter.ScalarProduct(&SearchDirection)) > HULLEPSILON) { // rotated the wrong way!
2088// cerr << "ERROR: SearchDirection and RelativeOldSphereCenter are still not orthogonal!" << endl;
2089// }
2090//
2091// if (LC->SetIndexToVector(&CircleCenter)) { // get cell for the starting atom
2092// for(int i=0;i<NDIM;i++) // store indices of this cell
2093// N[i] = LC->n[i];
2094// cout << Verbose(2) << "INFO: Center cell is " << N[0] << ", " << N[1] << ", " << N[2] << " with No. " << LC->index << "." << endl;
2095// } else {
2096// cerr << "ERROR: Vector " << CircleCenter << " is outside of LinkedCell's bounding box." << endl;
2097// return;
2098// }
2099// // then go through the current and all neighbouring cells and check the contained atoms for possible candidates
2100// cout << Verbose(2) << "LC Intervals:";
2101// for (int i=0;i<NDIM;i++) {
2102// Nlower[i] = ((N[i]-1) >= 0) ? N[i]-1 : 0;
2103// Nupper[i] = ((N[i]+1) < LC->N[i]) ? N[i]+1 : LC->N[i]-1;
2104// cout << " [" << Nlower[i] << "," << Nupper[i] << "] ";
2105// }
2106// cout << endl;
2107// for (LC->n[0] = Nlower[0]; LC->n[0] <= Nupper[0]; LC->n[0]++)
2108// for (LC->n[1] = Nlower[1]; LC->n[1] <= Nupper[1]; LC->n[1]++)
2109// for (LC->n[2] = Nlower[2]; LC->n[2] <= Nupper[2]; LC->n[2]++) {
2110// List = LC->GetCurrentCell();
2111// cout << Verbose(2) << "Current cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << " with No. " << LC->index << "." << endl;
2112// if (List != NULL) {
2113// for (LinkedAtoms::iterator Runner = List->begin(); Runner != List->end(); Runner++) {
2114// Candidate = (*Runner);
2115//
2116// // check for three unique points
2117// if ((Candidate != BaseTriangle->endpoints[0]->node) && (Candidate != BaseTriangle->endpoints[1]->node) && (Candidate != BaseTriangle->endpoints[2]->node)) {
2118// cout << Verbose(1) << "INFO: Current Candidate is " << *Candidate << " at " << Candidate->x << "." << endl;
2119//
2120// // construct both new centers
2121// GetCenterofCircumcircle(&NewSphereCenter, &(BaseLine->endpoints[0]->node->x), &(BaseLine->endpoints[1]->node->x), &(Candidate->x));
2122// OtherNewSphereCenter.CopyVector(&NewSphereCenter);
2123//
2124// if ((NewNormalVector.MakeNormalVector(&(BaseLine->endpoints[0]->node->x), &(BaseLine->endpoints[1]->node->x), &(Candidate->x))) && (fabs(NewNormalVector.ScalarProduct(&NewNormalVector)) > HULLEPSILON)) {
2125// helper.CopyVector(&NewNormalVector);
2126// cout << Verbose(2) << "INFO: NewNormalVector is " << NewNormalVector << "." << endl;
2127// radius = BaseLine->endpoints[0]->node->x.DistanceSquared(&NewSphereCenter);
2128// if (radius < RADIUS*RADIUS) {
2129// helper.Scale(sqrt(RADIUS*RADIUS - radius));
2130// cout << Verbose(3) << "INFO: Distance of NewCircleCenter to NewSphereCenter is " << helper.Norm() << "." << endl;
2131// NewSphereCenter.AddVector(&helper);
2132// NewSphereCenter.SubtractVector(&CircleCenter);
2133// cout << Verbose(2) << "INFO: NewSphereCenter is at " << NewSphereCenter << "." << endl;
2134//
2135// helper.Scale(-1.); // OtherNewSphereCenter is created by the same vector just in the other direction
2136// OtherNewSphereCenter.AddVector(&helper);
2137// OtherNewSphereCenter.SubtractVector(&CircleCenter);
2138// cout << Verbose(2) << "INFO: OtherNewSphereCenter is at " << OtherNewSphereCenter << "." << endl;
2139//
2140// // check both possible centers
2141// alpha = GetPathLengthonCircumCircle(CircleCenter, CirclePlaneNormal, CircleRadius, NewSphereCenter, OldSphereCenter, BaseTriangle->NormalVector, SearchDirection);
2142// Otheralpha = GetPathLengthonCircumCircle(CircleCenter, CirclePlaneNormal, CircleRadius, OtherNewSphereCenter, OldSphereCenter, BaseTriangle->NormalVector, SearchDirection);
2143// alpha = min(alpha, Otheralpha);
2144// if (*ShortestAngle > alpha) {
2145// OptCandidate = Candidate;
2146// *ShortestAngle = alpha;
2147// if (alpha != Otheralpha)
2148// OptCandidateCenter->CopyVector(&NewSphereCenter);
2149// else
2150// OptCandidateCenter->CopyVector(&OtherNewSphereCenter);
2151// cout << Verbose(1) << "We have found a better candidate: " << *OptCandidate << " with " << alpha << " and circumsphere's center at " << *OptCandidateCenter << "." << endl;
2152// } else {
2153// if (OptCandidate != NULL)
2154// cout << Verbose(1) << "REJECT: Old candidate: " << *OptCandidate << " is better than " << alpha << " with " << *ShortestAngle << "." << endl;
2155// else
2156// cout << Verbose(2) << "REJECT: Candidate " << *Candidate << " with " << alpha << " was rejected." << endl;
2157// }
2158//
2159// } else {
2160// cout << Verbose(1) << "REJECT: NewSphereCenter " << NewSphereCenter << " is too far away: " << radius << "." << endl;
2161// }
2162// } else {
2163// cout << Verbose(1) << "REJECT: Three points from " << *BaseLine << " and Candidate " << *Candidate << " are linear-dependent." << endl;
2164// }
2165// } else {
2166// cout << Verbose(1) << "REJECT: Base triangle " << *BaseTriangle << " contains Candidate " << *Candidate << "." << endl;
2167// }
2168// }
2169// }
2170// }
2171// } else {
2172// cerr << Verbose(1) << "ERROR: The projected center of the old sphere has radius " << radius << " instead of " << CircleRadius << "." << endl;
2173// }
2174// } else {
2175// cout << Verbose(1) << "Circumcircle for base line " << *BaseLine << " and base triangle " << *BaseTriangle << " is too big!" << endl;
2176// }
2177//
2178// cout << Verbose(1) << "End of Find_next_suitable_point" << endl;
2179// };
2180
2181
2182/** Checks whether the triangle consisting of the three atoms is already present.
2183 * Searches for the points in Tesselation::PointsOnBoundary and checks their
2184 * lines. If any of the three edges already has two triangles attached, false is
2185 * returned.
2186 * \param *out output stream for debugging
2187 * \param *Candidates endpoints of the triangle candidate
2188 * \return integer 0 if no triangle exists, 1 if one triangle exists, 2 if two
2189 * triangles exist which is the maximum for three points
2190 */
2191int Tesselation::CheckPresenceOfTriangle(ofstream *out, atom *Candidates[3]) {
2192 LineMap::iterator FindLine;
2193 PointMap::iterator FindPoint;
2194 TriangleMap::iterator FindTriangle;
2195 int adjacentTriangleCount = 0;
2196 class BoundaryPointSet *Points[3];
2197
2198 *out << Verbose(2) << "Begin of CheckPresenceOfTriangle" << endl;
2199 // builds a triangle point set (Points) of the end points
2200 for (int i = 0; i < 3; i++) {
2201 FindPoint = PointsOnBoundary.find(Candidates[i]->nr);
2202 if (FindPoint != PointsOnBoundary.end()) {
2203 Points[i] = FindPoint->second;
2204 } else {
2205 Points[i] = NULL;
2206 }
2207 }
2208
2209 // checks lines between the points in the Points for their adjacent triangles
2210 for (int i = 0; i < 3; i++) {
2211 if (Points[i] != NULL) {
2212 for (int j = i; j < 3; j++) {
2213 if (Points[j] != NULL) {
2214 FindLine = Points[i]->lines.find(Points[j]->node->nr);
2215 if (FindLine != Points[i]->lines.end()) {
2216 for (; FindLine->first == Points[j]->node->nr; FindLine++) {
2217 FindTriangle = FindLine->second->triangles.begin();
2218 for (; FindTriangle != FindLine->second->triangles.end(); FindTriangle++) {
2219 if ((
2220 (FindTriangle->second->endpoints[0] == Points[0])
2221 || (FindTriangle->second->endpoints[0] == Points[1])
2222 || (FindTriangle->second->endpoints[0] == Points[2])
2223 ) && (
2224 (FindTriangle->second->endpoints[1] == Points[0])
2225 || (FindTriangle->second->endpoints[1] == Points[1])
2226 || (FindTriangle->second->endpoints[1] == Points[2])
2227 ) && (
2228 (FindTriangle->second->endpoints[2] == Points[0])
2229 || (FindTriangle->second->endpoints[2] == Points[1])
2230 || (FindTriangle->second->endpoints[2] == Points[2])
2231 )
2232 ) {
2233 adjacentTriangleCount++;
2234 }
2235 }
2236 }
2237 // Only one of the triangle lines must be considered for the triangle count.
2238 *out << Verbose(2) << "Found " << adjacentTriangleCount << " adjacent triangles for the point set." << endl;
2239 return adjacentTriangleCount;
2240
2241 }
2242 }
2243 }
2244 }
2245 }
2246
2247 *out << Verbose(2) << "Found " << adjacentTriangleCount << " adjacent triangles for the point set." << endl;
2248 return adjacentTriangleCount;
2249};
2250
2251/** This recursive function finds a third point, to form a triangle with two given ones.
2252 * Note that this function is for the starting triangle.
2253 * The idea is as follows: A sphere with fixed radius is (almost) uniquely defined in space by three points
2254 * that sit on its boundary. Hence, when two points are given and we look for the (next) third point, then
2255 * the center of the sphere is still fixed up to a single parameter. The band of possible values
2256 * describes a circle in 3D-space. The old center of the sphere for the current base triangle gives
2257 * us the "null" on this circle, the new center of the candidate point will be some way along this
2258 * circle. The shorter the way the better is the candidate. Note that the direction is clearly given
2259 * by the normal vector of the base triangle that always points outwards by construction.
2260 * Hence, we construct a Center of this circle which sits right in the middle of the current base line.
2261 * We construct the normal vector that defines the plane this circle lies in, it is just in the
2262 * direction of the baseline. And finally, we need the radius of the circle, which is given by the rest
2263 * with respect to the length of the baseline and the sphere's fixed \a RADIUS.
2264 * Note that there is one difficulty: The circumcircle is uniquely defined, but for the circumsphere's center
2265 * there are two possibilities which becomes clear from the construction as seen below. Hence, we must check
2266 * both.
2267 * Note also that the acos() function is not unique on [0, 2.*M_PI). Hence, we need an additional check
2268 * to decide for one of the two possible angles. Therefore we need a SearchDirection and to make this check
2269 * sensible we need OldSphereCenter to be orthogonal to it. Either we construct SearchDirection orthogonal
2270 * right away, or -- what we do here -- we rotate the relative sphere centers such that this orthogonality
2271 * holds. Then, the normalized projection onto the SearchDirection is either +1 or -1 and thus states whether
2272 * the angle is uniquely in either (0,M_PI] or [M_PI, 2.*M_PI).
2273 * @param NormalVector normal direction of the base triangle (here the unit axis vector, \sa Find_starting_triangle())
2274 * @param SearchDirection general direction where to search for the next point, relative to center of BaseLine
2275 * @param OldSphereCenter center of sphere for base triangle, relative to center of BaseLine, giving null angle for the parameter circle
2276 * @param BaseLine BoundaryLineSet with the current base line
2277 * @param ThirdNode third atom to avoid in search
2278 * @param candidates list of equally good candidates to return
2279 * @param ShortestAngle the current path length on this circle band for the current Opt_Candidate
2280 * @param RADIUS radius of sphere
2281 * @param *LC LinkedCell structure with neighbouring atoms
2282 */
2283void Find_third_point_for_Tesselation(
2284 Vector NormalVector, Vector SearchDirection, Vector OldSphereCenter,
2285 class BoundaryLineSet *BaseLine, atom *ThirdNode, CandidateList* &candidates,
2286 double *ShortestAngle, const double RADIUS, LinkedCell *LC
2287) {
2288 atom *Walker = NULL;
2289 Vector CircleCenter; // center of the circle, i.e. of the band of sphere's centers
2290 Vector CirclePlaneNormal; // normal vector defining the plane this circle lives in
2291 Vector SphereCenter;
2292 Vector NewSphereCenter; // center of the sphere defined by the two points of BaseLine and the one of Candidate, first possibility
2293 Vector OtherNewSphereCenter; // center of the sphere defined by the two points of BaseLine and the one of Candidate, second possibility
2294 Vector NewNormalVector; // normal vector of the Candidate's triangle
2295 Vector helper, OptCandidateCenter, OtherOptCandidateCenter;
2296 LinkedAtoms *List = NULL;
2297 double CircleRadius; // radius of this circle
2298 double radius;
2299 double alpha, Otheralpha; // angles (i.e. parameter for the circle).
2300 double Nullalpha; // angle between OldSphereCenter and NormalVector of base triangle
2301 int N[NDIM], Nlower[NDIM], Nupper[NDIM];
2302 atom *Candidate = NULL;
2303 CandidateForTesselation *optCandidate;
2304
2305 cout << Verbose(1) << "Begin of Find_third_point_for_Tesselation" << endl;
2306
2307 cout << Verbose(2) << "INFO: NormalVector of BaseTriangle is " << NormalVector << "." << endl;
2308
2309 // construct center of circle
2310 CircleCenter.CopyVector(&(BaseLine->endpoints[0]->node->x));
2311 CircleCenter.AddVector(&BaseLine->endpoints[1]->node->x);
2312 CircleCenter.Scale(0.5);
2313
2314 // construct normal vector of circle
2315 CirclePlaneNormal.CopyVector(&BaseLine->endpoints[0]->node->x);
2316 CirclePlaneNormal.SubtractVector(&BaseLine->endpoints[1]->node->x);
2317
2318 // calculate squared radius atom *ThirdNode,f circle
2319 radius = CirclePlaneNormal.ScalarProduct(&CirclePlaneNormal);
2320 if (radius/4. < RADIUS*RADIUS) {
2321 CircleRadius = RADIUS*RADIUS - radius/4.;
2322 CirclePlaneNormal.Normalize();
2323 cout << Verbose(2) << "INFO: CircleCenter is at " << CircleCenter << ", CirclePlaneNormal is " << CirclePlaneNormal << " with circle radius " << sqrt(CircleRadius) << "." << endl;
2324
2325 // test whether old center is on the band's plane
2326 if (fabs(OldSphereCenter.ScalarProduct(&CirclePlaneNormal)) > HULLEPSILON) {
2327 cerr << "ERROR: Something's very wrong here: OldSphereCenter is not on the band's plane as desired by " << fabs(OldSphereCenter.ScalarProduct(&CirclePlaneNormal)) << "!" << endl;
2328 OldSphereCenter.ProjectOntoPlane(&CirclePlaneNormal);
2329 }
2330 radius = OldSphereCenter.ScalarProduct(&OldSphereCenter);
2331 if (fabs(radius - CircleRadius) < HULLEPSILON) {
2332
2333 // check SearchDirection
2334 cout << Verbose(2) << "INFO: SearchDirection is " << SearchDirection << "." << endl;
2335 if (fabs(OldSphereCenter.ScalarProduct(&SearchDirection)) > HULLEPSILON) { // rotated the wrong way!
2336 cerr << "ERROR: SearchDirection and RelativeOldSphereCenter are not orthogonal!" << endl;
2337 }
2338
2339 // get cell for the starting atom
2340 if (LC->SetIndexToVector(&CircleCenter)) {
2341 for(int i=0;i<NDIM;i++) // store indices of this cell
2342 N[i] = LC->n[i];
2343 cout << Verbose(2) << "INFO: Center cell is " << N[0] << ", " << N[1] << ", " << N[2] << " with No. " << LC->index << "." << endl;
2344 } else {
2345 cerr << "ERROR: Vector " << CircleCenter << " is outside of LinkedCell's bounding box." << endl;
2346 return;
2347 }
2348 // then go through the current and all neighbouring cells and check the contained atoms for possible candidates
2349 cout << Verbose(2) << "LC Intervals:";
2350 for (int i=0;i<NDIM;i++) {
2351 Nlower[i] = ((N[i]-1) >= 0) ? N[i]-1 : 0;
2352 Nupper[i] = ((N[i]+1) < LC->N[i]) ? N[i]+1 : LC->N[i]-1;
2353 cout << " [" << Nlower[i] << "," << Nupper[i] << "] ";
2354 }
2355 cout << endl;
2356 for (LC->n[0] = Nlower[0]; LC->n[0] <= Nupper[0]; LC->n[0]++)
2357 for (LC->n[1] = Nlower[1]; LC->n[1] <= Nupper[1]; LC->n[1]++)
2358 for (LC->n[2] = Nlower[2]; LC->n[2] <= Nupper[2]; LC->n[2]++) {
2359 List = LC->GetCurrentCell();
2360 //cout << Verbose(2) << "Current cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << " with No. " << LC->index << "." << endl;
2361 if (List != NULL) {
2362 for (LinkedAtoms::iterator Runner = List->begin(); Runner != List->end(); Runner++) {
2363 Candidate = (*Runner);
2364
2365 // check for three unique points
2366 cout << Verbose(1) << "INFO: Current Candidate is " << *Candidate << " at " << Candidate->x << "." << endl;
2367 if ((Candidate != BaseLine->endpoints[0]->node) && (Candidate != BaseLine->endpoints[1]->node) ){
2368
2369 // construct both new centers
2370 GetCenterofCircumcircle(&NewSphereCenter, &(BaseLine->endpoints[0]->node->x), &(BaseLine->endpoints[1]->node->x), &(Candidate->x));
2371 OtherNewSphereCenter.CopyVector(&NewSphereCenter);
2372
2373 if ((NewNormalVector.MakeNormalVector(&(BaseLine->endpoints[0]->node->x), &(BaseLine->endpoints[1]->node->x), &(Candidate->x)))
2374 && (fabs(NewNormalVector.ScalarProduct(&NewNormalVector)) > HULLEPSILON)
2375 ) {
2376 helper.CopyVector(&NewNormalVector);
2377 cout << Verbose(2) << "INFO: NewNormalVector is " << NewNormalVector << "." << endl;
2378 radius = BaseLine->endpoints[0]->node->x.DistanceSquared(&NewSphereCenter);
2379 if (radius < RADIUS*RADIUS) {
2380 helper.Scale(sqrt(RADIUS*RADIUS - radius));
2381 cout << Verbose(2) << "INFO: Distance of NewCircleCenter to NewSphereCenter is " << helper.Norm() << " with sphere radius " << RADIUS << "." << endl;
2382 NewSphereCenter.AddVector(&helper);
2383 NewSphereCenter.SubtractVector(&CircleCenter);
2384 cout << Verbose(2) << "INFO: NewSphereCenter is at " << NewSphereCenter << "." << endl;
2385
2386 // OtherNewSphereCenter is created by the same vector just in the other direction
2387 helper.Scale(-1.);
2388 OtherNewSphereCenter.AddVector(&helper);
2389 OtherNewSphereCenter.SubtractVector(&CircleCenter);
2390 cout << Verbose(2) << "INFO: OtherNewSphereCenter is at " << OtherNewSphereCenter << "." << endl;
2391
2392 alpha = GetPathLengthonCircumCircle(CircleCenter, CirclePlaneNormal, CircleRadius, NewSphereCenter, OldSphereCenter, NormalVector, SearchDirection);
2393 Otheralpha = GetPathLengthonCircumCircle(CircleCenter, CirclePlaneNormal, CircleRadius, OtherNewSphereCenter, OldSphereCenter, NormalVector, SearchDirection);
2394 alpha = min(alpha, Otheralpha);
2395 // if there is a better candidate, drop the current list and add the new candidate
2396 // otherwise ignore the new candidate and keep the list
2397 if (*ShortestAngle > (alpha - HULLEPSILON)) {
2398 optCandidate = new CandidateForTesselation(Candidate, BaseLine, OptCandidateCenter, OtherOptCandidateCenter);
2399 if (fabs(alpha - Otheralpha) > MYEPSILON) {
2400 optCandidate->OptCenter.CopyVector(&NewSphereCenter);
2401 optCandidate->OtherOptCenter.CopyVector(&OtherNewSphereCenter);
2402 } else {
2403 optCandidate->OptCenter.CopyVector(&OtherNewSphereCenter);
2404 optCandidate->OtherOptCenter.CopyVector(&NewSphereCenter);
2405 }
2406 // if there is an equal candidate, add it to the list without clearing the list
2407 if ((*ShortestAngle - HULLEPSILON) < alpha) {
2408 candidates->push_back(optCandidate);
2409 cout << Verbose(1) << "ACCEPT: We have found an equally good candidate: " << *(optCandidate->point) << " with "
2410 << alpha << " and circumsphere's center at " << optCandidate->OptCenter << "." << endl;
2411 } else {
2412 candidates->clear();
2413 candidates->push_back(optCandidate);
2414 cout << Verbose(1) << "ACCEPT: We have found a better candidate: " << *(optCandidate->point) << " with "
2415 << alpha << " and circumsphere's center at " << optCandidate->OptCenter << "." << endl;
2416 }
2417 *ShortestAngle = alpha;
2418 cout << Verbose(2) << "INFO: There are " << candidates->size() << " candidates in the list now." << endl;
2419 } else {
2420 if ((optCandidate != NULL) && (optCandidate->point != NULL))
2421 cout << Verbose(1) << "REJECT: Old candidate: " << *(optCandidate->point) << " is better than " << alpha << " with " << *ShortestAngle << "." << endl;
2422 else
2423 cout << Verbose(2) << "REJECT: Candidate " << *Candidate << " with " << alpha << " was rejected." << endl;
2424 }
2425
2426 } else {
2427 cout << Verbose(1) << "REJECT: NewSphereCenter " << NewSphereCenter << " is too far away: " << radius << "." << endl;
2428 }
2429 } else {
2430 cout << Verbose(1) << "REJECT: Three points from " << *BaseLine << " and Candidate " << *Candidate << " are linear-dependent." << endl;
2431 }
2432 } else {
2433 if (ThirdNode != NULL)
2434 cout << Verbose(1) << "REJECT: Base triangle " << *BaseLine << " and " << *ThirdNode << " contains Candidate " << *Candidate << "." << endl;
2435 else
2436 cout << Verbose(1) << "REJECT: Base triangle " << *BaseLine << " contains Candidate " << *Candidate << "." << endl;
2437 }
2438 }
2439 }
2440 }
2441 } else {
2442 cerr << Verbose(1) << "ERROR: The projected center of the old sphere has radius " << radius << " instead of " << CircleRadius << "." << endl;
2443 }
2444 } else {
2445 if (ThirdNode != NULL)
2446 cout << Verbose(1) << "Circumcircle for base line " << *BaseLine << " and third node " << *ThirdNode << " is too big!" << endl;
2447 else
2448 cout << Verbose(1) << "Circumcircle for base line " << *BaseLine << " is too big!" << endl;
2449 }
2450
2451 cout << Verbose(1) << "INFO: Sorting candidate list ..." << endl;
2452 if (candidates->size() > 1) {
2453 candidates->unique();
2454 candidates->sort(sortCandidates);
2455 }
2456
2457 cout << Verbose(1) << "End of Find_third_point_for_Tesselation" << endl;
2458};
2459
2460/**
2461 * Finds the preferable out of two third-point candidates with equal angles.
2462 *
2463 * @param Candidate - this and the second parameter are evaluated
2464 * @param OptCandidate - this and the second parameter are evaluated
2465 * @param current base line
2466 * @param third node of the base triangle
2467 * @param tesselation object
2468 *
2469 * @return true if Candidate should be taken, false if OptCandidate should be kept
2470 */
2471bool Choose_preferable_third_point(
2472 atom *Candidate, atom *OptCandidate, class BoundaryLineSet *BaseLine,
2473 atom *ThirdNode, Tesselation *Tess
2474) {
2475 bool takeNewCandidate;
2476
2477 ofstream *out = new ofstream();
2478 atom *Atoms[3];
2479 bool optCandidateAndBaseLineFormTriangle = (ThirdNode != NULL) && (OptCandidate == ThirdNode);
2480 bool candidateAndBaseLineFormTriangle = (ThirdNode != NULL) && (Candidate == ThirdNode);
2481 Atoms[0] = Candidate;
2482 Atoms[1] = OptCandidate;
2483 Atoms[2] = BaseLine->endpoints[0]->node;
2484 bool candidatesAndBaseLineNode0FormTriangle = (Tess->CheckPresenceOfTriangle(out, Atoms) > 0);
2485 Atoms[0] = Candidate;
2486 Atoms[1] = OptCandidate;
2487 Atoms[2] = BaseLine->endpoints[1]->node;
2488 bool candidatesAndBaseLineNode1FormTriangle = (Tess->CheckPresenceOfTriangle(out, Atoms) > 0);
2489 Vector halfBaseLine;
2490 halfBaseLine.CopyVector(&BaseLine->endpoints[0]->node->x);
2491 halfBaseLine.AddVector(&BaseLine->endpoints[1]->node->x);
2492 halfBaseLine.Scale(0.5);
2493
2494 if (optCandidateAndBaseLineFormTriangle) {
2495 takeNewCandidate = (!existsIntersection(Candidate->x, halfBaseLine, OptCandidate->x, BaseLine->endpoints[0]->node->x)
2496 && !existsIntersection(Candidate->x, halfBaseLine, OptCandidate->x, BaseLine->endpoints[1]->node->x));
2497 } else if (candidateAndBaseLineFormTriangle) {
2498 takeNewCandidate = (existsIntersection(OptCandidate->x, halfBaseLine, Candidate->x, BaseLine->endpoints[0]->node->x)
2499 || existsIntersection(OptCandidate->x, halfBaseLine, Candidate->x, BaseLine->endpoints[1]->node->x));
2500 } else if (candidatesAndBaseLineNode0FormTriangle) {
2501 takeNewCandidate = !existsIntersection(OptCandidate->x, BaseLine->endpoints[0]->node->x, Candidate->x, halfBaseLine);
2502 } else if (candidatesAndBaseLineNode1FormTriangle) {
2503 takeNewCandidate = !existsIntersection(OptCandidate->x, BaseLine->endpoints[1]->node->x, Candidate->x, halfBaseLine);
2504 } else {
2505 takeNewCandidate = (ThirdNode == NULL)
2506 || ((!existsIntersection(Candidate->x, halfBaseLine, ThirdNode->x, BaseLine->endpoints[0]->node->x)
2507 && !existsIntersection(Candidate->x, halfBaseLine, ThirdNode->x, BaseLine->endpoints[1]->node->x)));
2508 }
2509
2510 return takeNewCandidate;
2511};
2512
2513
2514struct Intersection {
2515 Vector x1;
2516 Vector x2;
2517 Vector x3;
2518 Vector x4;
2519};
2520
2521/**
2522 * Intersection calculation function.
2523 *
2524 * @param x to find the result for
2525 * @param function parameter
2526 */
2527double MinIntersectDistance(const gsl_vector * x, void *params) {
2528 double retval = 0;
2529 struct Intersection *I = (struct Intersection *)params;
2530 Vector intersection;
2531 Vector SideA,SideB,HeightA, HeightB;
2532 for (int i=0;i<NDIM;i++)
2533 intersection.x[i] = gsl_vector_get(x, i);
2534
2535 SideA.CopyVector(&(I->x1));
2536 SideA.SubtractVector(&I->x2);
2537 HeightA.CopyVector(&intersection);
2538 HeightA.SubtractVector(&I->x1);
2539 HeightA.ProjectOntoPlane(&SideA);
2540
2541 SideB.CopyVector(&I->x3);
2542 SideB.SubtractVector(&I->x4);
2543 HeightB.CopyVector(&intersection);
2544 HeightB.SubtractVector(&I->x3);
2545 HeightB.ProjectOntoPlane(&SideB);
2546
2547 retval = HeightA.ScalarProduct(&HeightA) + HeightB.ScalarProduct(&HeightB);
2548 //cout << Verbose(2) << "MinIntersectDistance called, result: " << retval << endl;
2549
2550 return retval;
2551};
2552
2553
2554/**
2555 * Calculates whether there is an intersection between two lines. The first line
2556 * always goes through point 1 and point 2 and the second line is given by the
2557 * connection between point 4 and point 5.
2558 *
2559 * @param point 1 of line 1
2560 * @param point 2 of line 1
2561 * @param point 1 of line 2
2562 * @param point 2 of line 2
2563 *
2564 * @return true if there is an intersection between the given lines, false otherwise
2565 */
2566bool existsIntersection(Vector point1, Vector point2, Vector point3, Vector point4) {
2567 bool result;
2568
2569 struct Intersection par;
2570 par.x1.CopyVector(&point1);
2571 par.x2.CopyVector(&point2);
2572 par.x3.CopyVector(&point3);
2573 par.x4.CopyVector(&point4);
2574
2575 const gsl_multimin_fminimizer_type *T = gsl_multimin_fminimizer_nmsimplex;
2576 gsl_multimin_fminimizer *s = NULL;
2577 gsl_vector *ss, *x;
2578 gsl_multimin_function minex_func;
2579
2580 size_t iter = 0;
2581 int status;
2582 double size;
2583
2584 /* Starting point */
2585 x = gsl_vector_alloc(NDIM);
2586 gsl_vector_set(x, 0, point1.x[0]);
2587 gsl_vector_set(x, 1, point1.x[1]);
2588 gsl_vector_set(x, 2, point1.x[2]);
2589
2590 /* Set initial step sizes to 1 */
2591 ss = gsl_vector_alloc(NDIM);
2592 gsl_vector_set_all(ss, 1.0);
2593
2594 /* Initialize method and iterate */
2595 minex_func.n = NDIM;
2596 minex_func.f = &MinIntersectDistance;
2597 minex_func.params = (void *)&par;
2598
2599 s = gsl_multimin_fminimizer_alloc(T, NDIM);
2600 gsl_multimin_fminimizer_set(s, &minex_func, x, ss);
2601
2602 do {
2603 iter++;
2604 status = gsl_multimin_fminimizer_iterate(s);
2605
2606 if (status) {
2607 break;
2608 }
2609
2610 size = gsl_multimin_fminimizer_size(s);
2611 status = gsl_multimin_test_size(size, 1e-2);
2612
2613 if (status == GSL_SUCCESS) {
2614 cout << Verbose(2) << "converged to minimum" << endl;
2615 }
2616 } while (status == GSL_CONTINUE && iter < 100);
2617
2618 // check whether intersection is in between or not
2619 Vector intersection, SideA, SideB, HeightA, HeightB;
2620 double t1, t2;
2621 for (int i = 0; i < NDIM; i++) {
2622 intersection.x[i] = gsl_vector_get(s->x, i);
2623 }
2624
2625 SideA.CopyVector(&par.x2);
2626 SideA.SubtractVector(&par.x1);
2627 HeightA.CopyVector(&intersection);
2628 HeightA.SubtractVector(&par.x1);
2629
2630 t1 = HeightA.Projection(&SideA)/SideA.ScalarProduct(&SideA);
2631
2632 SideB.CopyVector(&par.x4);
2633 SideB.SubtractVector(&par.x3);
2634 HeightB.CopyVector(&intersection);
2635 HeightB.SubtractVector(&par.x3);
2636
2637 t2 = HeightB.Projection(&SideB)/SideB.ScalarProduct(&SideB);
2638
2639 cout << Verbose(2) << "Intersection " << intersection << " is at "
2640 << t1 << " for (" << point1 << "," << point2 << ") and at "
2641 << t2 << " for (" << point3 << "," << point4 << "): ";
2642
2643 if (((t1 >= 0) && (t1 <= 1)) && ((t2 >= 0) && (t2 <= 1))) {
2644 cout << "true intersection." << endl;
2645 result = true;
2646 } else {
2647 cout << "intersection out of region of interest." << endl;
2648 result = false;
2649 }
2650
2651 // free minimizer stuff
2652 gsl_vector_free(x);
2653 gsl_vector_free(ss);
2654 gsl_multimin_fminimizer_free(s);
2655
2656 return result;
2657}
2658
2659/** Finds the second point of starting triangle.
2660 * \param *a first atom
2661 * \param *Candidate pointer to candidate atom on return
2662 * \param Oben vector indicating the outside
2663 * \param Opt_Candidate reference to recommended candidate on return
2664 * \param Storage[3] array storing angles and other candidate information
2665 * \param RADIUS radius of virtual sphere
2666 * \param *LC LinkedCell structure with neighbouring atoms
2667 */
2668void Find_second_point_for_Tesselation(atom* a, atom* Candidate, Vector Oben, atom*& Opt_Candidate, double Storage[3], double RADIUS, LinkedCell *LC)
2669{
2670 cout << Verbose(2) << "Begin of Find_second_point_for_Tesselation" << endl;
2671 int i;
2672 Vector AngleCheck;
2673 atom* Walker;
2674 double norm = -1., angle;
2675 LinkedAtoms *List = NULL;
2676 int N[NDIM], Nlower[NDIM], Nupper[NDIM];
2677
2678 if (LC->SetIndexToAtom(a)) { // get cell for the starting atom
2679 for(int i=0;i<NDIM;i++) // store indices of this cell
2680 N[i] = LC->n[i];
2681 } else {
2682 cerr << "ERROR: Atom " << *a << " is not found in cell " << LC->index << "." << endl;
2683 return;
2684 }
2685 // then go through the current and all neighbouring cells and check the contained atoms for possible candidates
2686 cout << Verbose(2) << "LC Intervals from [";
2687 for (int i=0;i<NDIM;i++) {
2688 cout << " " << N[i] << "<->" << LC->N[i];
2689 }
2690 cout << "] :";
2691 for (int i=0;i<NDIM;i++) {
2692 Nlower[i] = ((N[i]-1) >= 0) ? N[i]-1 : 0;
2693 Nupper[i] = ((N[i]+1) < LC->N[i]) ? N[i]+1 : LC->N[i]-1;
2694 cout << " [" << Nlower[i] << "," << Nupper[i] << "] ";
2695 }
2696 cout << endl;
2697
2698
2699 for (LC->n[0] = Nlower[0]; LC->n[0] <= Nupper[0]; LC->n[0]++)
2700 for (LC->n[1] = Nlower[1]; LC->n[1] <= Nupper[1]; LC->n[1]++)
2701 for (LC->n[2] = Nlower[2]; LC->n[2] <= Nupper[2]; LC->n[2]++) {
2702 List = LC->GetCurrentCell();
2703 cout << Verbose(2) << "Current cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << " with No. " << LC->index << "." << endl;
2704 if (List != NULL) {
2705 for (LinkedAtoms::iterator Runner = List->begin(); Runner != List->end(); Runner++) {
2706 Candidate = (*Runner);
2707 cout << Verbose(2) << "Current candidate is " << *Candidate << ": ";
2708 // check if we only have one unique point yet ...
2709 if (a != Candidate) {
2710 // Calculate center of the circle with radius RADIUS through points a and Candidate
2711 Vector OrthogonalizedOben, a_Candidate, Center;
2712 double distance, scaleFactor;
2713
2714 OrthogonalizedOben.CopyVector(&Oben);
2715 a_Candidate.CopyVector(&(a->x));
2716 a_Candidate.SubtractVector(&(Candidate->x));
2717 OrthogonalizedOben.ProjectOntoPlane(&a_Candidate);
2718 OrthogonalizedOben.Normalize();
2719 distance = 0.5 * a_Candidate.Norm();
2720 scaleFactor = sqrt(((RADIUS * RADIUS) - (distance * distance)));
2721 OrthogonalizedOben.Scale(scaleFactor);
2722
2723 Center.CopyVector(&(Candidate->x));
2724 Center.AddVector(&(a->x));
2725 Center.Scale(0.5);
2726 Center.AddVector(&OrthogonalizedOben);
2727
2728 AngleCheck.CopyVector(&Center);
2729 AngleCheck.SubtractVector(&(a->x));
2730 norm = a_Candidate.Norm();
2731 // second point shall have smallest angle with respect to Oben vector
2732 if (norm < RADIUS*2.) {
2733 angle = AngleCheck.Angle(&Oben);
2734 if (angle < Storage[0]) {
2735 //cout << Verbose(1) << "Old values of Storage: %lf %lf \n", Storage[0], Storage[1]);
2736 cout << "Is a better candidate with distance " << norm << " and angle " << angle << " to oben " << Oben << ".\n";
2737 Opt_Candidate = Candidate;
2738 Storage[0] = angle;
2739 //cout << Verbose(1) << "Changing something in Storage: %lf %lf. \n", Storage[0], Storage[2]);
2740 } else {
2741 cout << "Looses with angle " << angle << " to a better candidate " << *Opt_Candidate << endl;
2742 }
2743 } else {
2744 cout << "Refused due to Radius " << norm << endl;
2745 }
2746 } else {
2747 cout << " Candidate is equal to first endpoint " << *a << "." << endl;
2748 }
2749 }
2750 } else {
2751 cout << "Linked cell list is empty." << endl;
2752 }
2753 }
2754 cout << Verbose(2) << "End of Find_second_point_for_Tesselation" << endl;
2755};
2756
2757/** Finds the starting triangle for find_non_convex_border().
2758 * Looks at the outermost atom per axis, then Find_second_point_for_Tesselation()
2759 * for the second and Find_next_suitable_point_via_Angle_of_Sphere() for the third
2760 * point are called.
2761 * \param RADIUS radius of virtual rolling sphere
2762 * \param *LC LinkedCell structure with neighbouring atoms
2763 */
2764void Tesselation::Find_starting_triangle(ofstream *out, molecule *mol, const double RADIUS, LinkedCell *LC)
2765{
2766 cout << Verbose(1) << "Begin of Find_starting_triangle\n";
2767 int i = 0;
2768 LinkedAtoms *List = NULL;
2769 atom* Walker;
2770 atom* FirstPoint;
2771 atom* SecondPoint;
2772 atom* MaxAtom[NDIM];
2773 double max_coordinate[NDIM];
2774 Vector Oben;
2775 Vector helper;
2776 Vector Chord;
2777 Vector SearchDirection;
2778
2779 Oben.Zero();
2780
2781 for (i = 0; i < 3; i++) {
2782 MaxAtom[i] = NULL;
2783 max_coordinate[i] = -1;
2784 }
2785
2786 // 1. searching topmost atom with respect to each axis
2787 for (int i=0;i<NDIM;i++) { // each axis
2788 LC->n[i] = LC->N[i]-1; // current axis is topmost cell
2789 for (LC->n[(i+1)%NDIM]=0;LC->n[(i+1)%NDIM]<LC->N[(i+1)%NDIM];LC->n[(i+1)%NDIM]++)
2790 for (LC->n[(i+2)%NDIM]=0;LC->n[(i+2)%NDIM]<LC->N[(i+2)%NDIM];LC->n[(i+2)%NDIM]++) {
2791 List = LC->GetCurrentCell();
2792 //cout << Verbose(2) << "Current cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << " with No. " << LC->index << "." << endl;
2793 if (List != NULL) {
2794 for (LinkedAtoms::iterator Runner = List->begin();Runner != List->end();Runner++) {
2795 cout << Verbose(2) << "Current atom is " << *(*Runner) << "." << endl;
2796 if ((*Runner)->x.x[i] > max_coordinate[i]) {
2797 max_coordinate[i] = (*Runner)->x.x[i];
2798 MaxAtom[i] = (*Runner);
2799 }
2800 }
2801 } else {
2802 cerr << "ERROR: The current cell " << LC->n[0] << "," << LC->n[1] << "," << LC->n[2] << " is invalid!" << endl;
2803 }
2804 }
2805 }
2806
2807 cout << Verbose(2) << "Found maximum coordinates: ";
2808 for (int i=0;i<NDIM;i++)
2809 cout << i << ": " << *MaxAtom[i] << "\t";
2810 cout << endl;
2811 const int k = 1; // arbitrary choice
2812 Oben.x[k] = 1.;
2813 FirstPoint = MaxAtom[k];
2814 cout << Verbose(1) << "Coordinates of start atom " << *FirstPoint << " at " << FirstPoint->x << "." << endl;
2815
2816 double ShortestAngle;
2817 atom* Opt_Candidate = NULL;
2818 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.
2819
2820 Find_second_point_for_Tesselation(FirstPoint, NULL, Oben, Opt_Candidate, &ShortestAngle, RADIUS, LC); // we give same point as next candidate as its bonds are looked into in find_second_...
2821 SecondPoint = Opt_Candidate;
2822 cout << Verbose(1) << "Found second point is " << *SecondPoint << " at " << SecondPoint->x << ".\n";
2823
2824 helper.CopyVector(&(FirstPoint->x));
2825 helper.SubtractVector(&(SecondPoint->x));
2826 helper.Normalize();
2827 Oben.ProjectOntoPlane(&helper);
2828 Oben.Normalize();
2829 helper.VectorProduct(&Oben);
2830 ShortestAngle = 2.*M_PI; // This will indicate the quadrant.
2831
2832 Chord.CopyVector(&(FirstPoint->x)); // bring into calling function
2833 Chord.SubtractVector(&(SecondPoint->x));
2834 double radius = Chord.ScalarProduct(&Chord);
2835 double CircleRadius = sqrt(RADIUS*RADIUS - radius/4.);
2836 helper.CopyVector(&Oben);
2837 helper.Scale(CircleRadius);
2838 // Now, oben and helper are two orthonormalized vectors in the plane defined by Chord (not normalized)
2839
2840 cout << Verbose(2) << "Looking for third point candidates \n";
2841 // look in one direction of baseline for initial candidate
2842 CandidateList *Opt_Candidates = new CandidateList();
2843 SearchDirection.MakeNormalVector(&Chord, &Oben); // whether we look "left" first or "right" first is not important ...
2844
2845 // adding point 1 and point 2 and the line between them
2846 AddTrianglePoint(FirstPoint, 0);
2847 AddTrianglePoint(SecondPoint, 1);
2848 AddTriangleLine(TPS[0], TPS[1], 0);
2849
2850 cout << Verbose(1) << "Looking for third point candidates ...\n";
2851 cout << Verbose(2) << "INFO: OldSphereCenter is at " << helper << ".\n";
2852 Find_third_point_for_Tesselation(
2853 Oben, SearchDirection, helper, BLS[0], NULL, *&Opt_Candidates, &ShortestAngle, RADIUS, LC
2854 );
2855 cout << Verbose(1) << "Third Points are ";
2856 CandidateList::iterator it;
2857 for (it = Opt_Candidates->begin(); it != Opt_Candidates->end(); ++it) {
2858 cout << " " << *(*it)->point;
2859 }
2860 cout << endl;
2861
2862 for (it = Opt_Candidates->begin(); it != Opt_Candidates->end(); ++it) {
2863 // add third triangle point
2864 AddTrianglePoint((*it)->point, 2);
2865 // add the second and third line
2866 AddTriangleLine(TPS[1], TPS[2], 1);
2867 AddTriangleLine(TPS[0], TPS[2], 2);
2868 // ... and triangles to the Maps of the Tesselation class
2869 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
2870 AddTriangleToLines();
2871 // ... and calculate its normal vector (with correct orientation)
2872 (*it)->OptCenter.Scale(-1.);
2873 cout << Verbose(2) << "Anti-Oben is currently " << (*it)->OptCenter << "." << endl;
2874 BTS->GetNormalVector((*it)->OptCenter); // vector to compare with should point inwards
2875 cout << Verbose(0) << "==> Found starting triangle consists of " << *FirstPoint << ", " << *SecondPoint << " and "
2876 << *(*it)->point << " with normal vector " << BTS->NormalVector << ".\n";
2877
2878 // if we do not reach the end with the next step of iteration, we need to setup a new first line
2879 if (it != Opt_Candidates->end()--) {
2880 FirstPoint = (*it)->BaseLine->endpoints[0]->node;
2881 SecondPoint = (*it)->point;
2882 // adding point 1 and point 2 and the line between them
2883 AddTrianglePoint(FirstPoint, 0);
2884 AddTrianglePoint(SecondPoint, 1);
2885 AddTriangleLine(TPS[0], TPS[1], 0);
2886 }
2887 }
2888 cout << Verbose(2) << "Projection is " << BTS->NormalVector.Projection(&Oben) << "." << endl;
2889 cout << Verbose(1) << "End of Find_starting_triangle\n";
2890};
2891
2892/** Checks for a new special triangle whether one of its edges is already present with one one triangle connected.
2893 * This enforces that special triangles (i.e. degenerated ones) should at last close the open-edge frontier and not
2894 * make it bigger (i.e. closing one (the baseline) and opening two new ones).
2895 * \param TPS[3] nodes of the triangle
2896 * \return true - there is such a line (i.e. creation of degenerated triangle is valid), false - no such line (don't create)
2897 */
2898bool CheckLineCriteriaforDegeneratedTriangle(class BoundaryPointSet *nodes[3])
2899{
2900 bool result = false;
2901 int counter = 0;
2902
2903 // check all three points
2904 for (int i=0;i<3;i++)
2905 for (int j=i+1; j<3; j++) {
2906 if (nodes[i]->lines.find(nodes[j]->node->nr) != nodes[i]->lines.end()) { // there already is a line
2907 LineMap::iterator FindLine;
2908 pair<LineMap::iterator,LineMap::iterator> FindPair;
2909 FindPair = nodes[i]->lines.equal_range(nodes[j]->node->nr);
2910 for (FindLine = FindPair.first; FindLine != FindPair.second; ++FindLine) {
2911 // If there is a line with less than two attached triangles, we don't need a new line.
2912 if (FindLine->second->TrianglesCount < 2) {
2913 counter++;
2914 break; // increase counter only once per edge
2915 }
2916 }
2917 } else { // no line
2918 cout << Verbose(1) << "ERROR: The line between " << nodes[i] << " and " << nodes[j] << " is not yet present, hence no need for a degenerate triangle!" << endl;
2919 result = true;
2920 }
2921 }
2922 if (counter > 1) {
2923 cout << Verbose(2) << "INFO: Degenerate triangle is ok, there is only at most " << counter << " new line needed." << endl;
2924 result = true;
2925 }
2926 return result;
2927};
2928
2929
2930/** This function finds a triangle to a line, adjacent to an existing one.
2931 * @param out output stream for debugging
2932 * @param *mol molecule with Atom's and Bond's
2933 * @param Line current baseline to search from
2934 * @param T current triangle which \a Line is edge of
2935 * @param RADIUS radius of the rolling ball
2936 * @param N number of found triangles
2937 * @param *filename filename base for intermediate envelopes
2938 * @param *LC LinkedCell structure with neighbouring atoms
2939 */
2940bool Tesselation::Find_next_suitable_triangle(ofstream *out,
2941 molecule *mol, BoundaryLineSet &Line, BoundaryTriangleSet &T,
2942 const double& RADIUS, int N, const char *tempbasename, LinkedCell *LC)
2943{
2944 cout << Verbose(1) << "Begin of Find_next_suitable_triangle\n";
2945 ofstream *tempstream = NULL;
2946 char NumberName[255];
2947 double tmp;
2948 bool result = true;
2949 CandidateList *Opt_Candidates = new CandidateList();
2950
2951 Vector CircleCenter;
2952 Vector CirclePlaneNormal;
2953 Vector OldSphereCenter;
2954 Vector SearchDirection;
2955 Vector helper;
2956 atom *ThirdNode = NULL;
2957 LineMap::iterator testline;
2958 double ShortestAngle = 2.*M_PI; // This will indicate the quadrant.
2959 double radius, CircleRadius;
2960
2961 cout << Verbose(1) << "Current baseline is " << Line << " of triangle " << T << "." << endl;
2962 for (int i=0;i<3;i++)
2963 if ((T.endpoints[i]->node != Line.endpoints[0]->node) && (T.endpoints[i]->node != Line.endpoints[1]->node))
2964 ThirdNode = T.endpoints[i]->node;
2965
2966 // construct center of circle
2967 CircleCenter.CopyVector(&Line.endpoints[0]->node->x);
2968 CircleCenter.AddVector(&Line.endpoints[1]->node->x);
2969 CircleCenter.Scale(0.5);
2970
2971 // construct normal vector of circle
2972 CirclePlaneNormal.CopyVector(&Line.endpoints[0]->node->x);
2973 CirclePlaneNormal.SubtractVector(&Line.endpoints[1]->node->x);
2974
2975 // calculate squared radius of circle
2976 radius = CirclePlaneNormal.ScalarProduct(&CirclePlaneNormal);
2977 if (radius/4. < RADIUS*RADIUS) {
2978 CircleRadius = RADIUS*RADIUS - radius/4.;
2979 CirclePlaneNormal.Normalize();
2980 cout << Verbose(2) << "INFO: CircleCenter is at " << CircleCenter << ", CirclePlaneNormal is " << CirclePlaneNormal << " with circle radius " << sqrt(CircleRadius) << "." << endl;
2981
2982 // construct old center
2983 GetCenterofCircumcircle(&OldSphereCenter, &(T.endpoints[0]->node->x), &(T.endpoints[1]->node->x), &(T.endpoints[2]->node->x));
2984 helper.CopyVector(&T.NormalVector); // normal vector ensures that this is correct center of the two possible ones
2985 radius = Line.endpoints[0]->node->x.DistanceSquared(&OldSphereCenter);
2986 helper.Scale(sqrt(RADIUS*RADIUS - radius));
2987 OldSphereCenter.AddVector(&helper);
2988 OldSphereCenter.SubtractVector(&CircleCenter);
2989 cout << Verbose(2) << "INFO: OldSphereCenter is at " << OldSphereCenter << "." << endl;
2990
2991 // construct SearchDirection
2992 SearchDirection.MakeNormalVector(&T.NormalVector, &CirclePlaneNormal);
2993 helper.CopyVector(&Line.endpoints[0]->node->x);
2994 helper.SubtractVector(&ThirdNode->x);
2995 if (helper.ScalarProduct(&SearchDirection) < -HULLEPSILON)// ohoh, SearchDirection points inwards!
2996 SearchDirection.Scale(-1.);
2997 SearchDirection.ProjectOntoPlane(&OldSphereCenter);
2998 SearchDirection.Normalize();
2999 cout << Verbose(2) << "INFO: SearchDirection is " << SearchDirection << "." << endl;
3000 if (fabs(OldSphereCenter.ScalarProduct(&SearchDirection)) > HULLEPSILON) {
3001 // rotated the wrong way!
3002 cerr << "ERROR: SearchDirection and RelativeOldSphereCenter are still not orthogonal!" << endl;
3003 }
3004
3005 // add third point
3006 cout << Verbose(1) << "Looking for third point candidates for triangle ... " << endl;
3007 Find_third_point_for_Tesselation(
3008 T.NormalVector, SearchDirection, OldSphereCenter, &Line, ThirdNode, Opt_Candidates,
3009 &ShortestAngle, RADIUS, LC
3010 );
3011
3012 } else {
3013 cout << Verbose(1) << "Circumcircle for base line " << Line << " and base triangle " << T << " is too big!" << endl;
3014 }
3015
3016 if (Opt_Candidates->begin() == Opt_Candidates->end()) {
3017 cerr << "WARNING: Could not find a suitable candidate." << endl;
3018 return false;
3019 }
3020 cout << Verbose(1) << "Third Points are ";
3021 CandidateList::iterator it;
3022 for (it = Opt_Candidates->begin(); it != Opt_Candidates->end(); ++it) {
3023 cout << " " << *(*it)->point;
3024 }
3025 cout << endl;
3026
3027 BoundaryLineSet *BaseRay = &Line;
3028 for (it = Opt_Candidates->begin(); it != Opt_Candidates->end(); ++it) {
3029 cout << Verbose(1) << " Third point candidate is " << *(*it)->point
3030 << " with circumsphere's center at " << (*it)->OptCenter << "." << endl;
3031 cout << Verbose(1) << " Baseline is " << *BaseRay << endl;
3032
3033 // check whether all edges of the new triangle still have space for one more triangle (i.e. TriangleCount <2)
3034 atom *AtomCandidates[3];
3035 AtomCandidates[0] = (*it)->point;
3036 AtomCandidates[1] = BaseRay->endpoints[0]->node;
3037 AtomCandidates[2] = BaseRay->endpoints[1]->node;
3038 int existentTrianglesCount = CheckPresenceOfTriangle(out, AtomCandidates);
3039
3040 BTS = NULL;
3041 // If there is no triangle, add it regularly.
3042 if (existentTrianglesCount == 0) {
3043 AddTrianglePoint((*it)->point, 0);
3044 AddTrianglePoint(BaseRay->endpoints[0]->node, 1);
3045 AddTrianglePoint(BaseRay->endpoints[1]->node, 2);
3046
3047 AddTriangleLine(TPS[0], TPS[1], 0);
3048 AddTriangleLine(TPS[0], TPS[2], 1);
3049 AddTriangleLine(TPS[1], TPS[2], 2);
3050
3051 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
3052 AddTriangleToLines();
3053 (*it)->OptCenter.Scale(-1.);
3054 BTS->GetNormalVector((*it)->OptCenter);
3055 (*it)->OptCenter.Scale(-1.);
3056
3057 cout << "--> New triangle with " << *BTS << " and normal vector " << BTS->NormalVector
3058 << " for this triangle ... " << endl;
3059 cout << Verbose(1) << "We have "<< TrianglesOnBoundaryCount << " for line " << BaseRay << "." << endl;
3060 } else if (existentTrianglesCount == 1) { // If there is a planar region within the structure, we need this triangle a second time.
3061 AddTrianglePoint((*it)->point, 0);
3062 AddTrianglePoint(BaseRay->endpoints[0]->node, 1);
3063 AddTrianglePoint(BaseRay->endpoints[1]->node, 2);
3064
3065 // 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)
3066 // i.e. at least one of the three lines must be present with TriangleCount <= 1
3067 if (CheckLineCriteriaforDegeneratedTriangle(TPS)) {
3068 AddTriangleLine(TPS[0], TPS[1], 0);
3069 AddTriangleLine(TPS[0], TPS[2], 1);
3070 AddTriangleLine(TPS[1], TPS[2], 2);
3071
3072 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
3073 //TrianglesOnBoundary.insert(TrianglePair(TrianglesOnBoundaryCount, BTS));
3074 AddTriangleToLines();
3075
3076 (*it)->OtherOptCenter.Scale(-1.);
3077 BTS->GetNormalVector((*it)->OtherOptCenter);
3078 (*it)->OtherOptCenter.Scale(-1.);
3079
3080 cout << "--> WARNING: Special new triangle with " << *BTS << " and normal vector " << BTS->NormalVector
3081 << " for this triangle ... " << endl;
3082 cout << Verbose(1) << "We have "<< BaseRay->TrianglesCount << " for line " << BaseRay << "." << endl;
3083 } else {
3084 cout << Verbose(1) << "WARNING: This triangle consisting of ";
3085 cout << *(*it)->point << ", ";
3086 cout << *BaseRay->endpoints[0]->node << " and ";
3087 cout << *BaseRay->endpoints[1]->node << " ";
3088 cout << "exists and is not added, as it does not seem helpful!" << endl;
3089 result = false;
3090 }
3091 } else {
3092 cout << Verbose(1) << "This triangle consisting of ";
3093 cout << *(*it)->point << ", ";
3094 cout << *BaseRay->endpoints[0]->node << " and ";
3095 cout << *BaseRay->endpoints[1]->node << " ";
3096 cout << "is invalid!" << endl;
3097 result = false;
3098 }
3099
3100 if ((result) && (existentTrianglesCount < 2) && (DoSingleStepOutput && (TrianglesOnBoundaryCount % 1 == 0))) { // if we have a new triangle and want to output each new triangle configuration
3101 sprintf(NumberName, "-%04d-%s_%s_%s", TriangleFilesWritten, BTS->endpoints[0]->node->Name, BTS->endpoints[1]->node->Name, BTS->endpoints[2]->node->Name);
3102 if (DoTecplotOutput) {
3103 string NameofTempFile(tempbasename);
3104 NameofTempFile.append(NumberName);
3105 for(size_t npos = NameofTempFile.find_first_of(' '); npos != -1; npos = NameofTempFile.find(' ', npos))
3106 NameofTempFile.erase(npos, 1);
3107 NameofTempFile.append(TecplotSuffix);
3108 cout << Verbose(1) << "Writing temporary non convex hull to file " << NameofTempFile << ".\n";
3109 tempstream = new ofstream(NameofTempFile.c_str(), ios::trunc);
3110 write_tecplot_file(out, tempstream, this, mol, TriangleFilesWritten);
3111 tempstream->close();
3112 tempstream->flush();
3113 delete(tempstream);
3114 }
3115
3116 if (DoRaster3DOutput) {
3117 string NameofTempFile(tempbasename);
3118 NameofTempFile.append(NumberName);
3119 for(size_t npos = NameofTempFile.find_first_of(' '); npos != -1; npos = NameofTempFile.find(' ', npos))
3120 NameofTempFile.erase(npos, 1);
3121 NameofTempFile.append(Raster3DSuffix);
3122 cout << Verbose(1) << "Writing temporary non convex hull to file " << NameofTempFile << ".\n";
3123 tempstream = new ofstream(NameofTempFile.c_str(), ios::trunc);
3124 write_raster3d_file(out, tempstream, this, mol);
3125 // include the current position of the virtual sphere in the temporary raster3d file
3126 // make the circumsphere's center absolute again
3127 helper.CopyVector(&BaseRay->endpoints[0]->node->x);
3128 helper.AddVector(&BaseRay->endpoints[1]->node->x);
3129 helper.Scale(0.5);
3130 (*it)->OptCenter.AddVector(&helper);
3131 Vector *center = mol->DetermineCenterOfAll(out);
3132 (*it)->OptCenter.AddVector(center);
3133 delete(center);
3134 // and add to file plus translucency object
3135 *tempstream << "# current virtual sphere\n";
3136 *tempstream << "8\n 25.0 0.6 -1.0 -1.0 -1.0 0.2 0 0 0 0\n";
3137 *tempstream << "2\n " << (*it)->OptCenter.x[0] << " "
3138 << (*it)->OptCenter.x[1] << " " << (*it)->OptCenter.x[2]
3139 << "\t" << RADIUS << "\t1 0 0\n";
3140 *tempstream << "9\n terminating special property\n";
3141 tempstream->close();
3142 tempstream->flush();
3143 delete(tempstream);
3144 }
3145 if (DoTecplotOutput || DoRaster3DOutput)
3146 TriangleFilesWritten++;
3147 }
3148
3149 // set baseline to new ray from ref point (here endpoints[0]->node) to current candidate (here (*it)->point))
3150 BaseRay = BLS[0];
3151// LineMap::iterator LineIterator = Line.endpoints[0]->lines.find((*it)->point->nr);
3152// for (; LineIterator != Line.endpoints[0]->lines.end(); LineIterator++) {
3153// if ((*LineIterator->second).TrianglesCount != 2)
3154// break;
3155// }
3156// if (LineIterator == Line.endpoints[0]->lines.end())
3157// cout << Verbose(1) << "ERROR: I could not find a suitable line with less than two triangles connected!" << endl;
3158 }
3159
3160 cout << Verbose(1) << "End of Find_next_suitable_triangle\n";
3161 return result;
3162};
3163
3164/**
3165 * Sort function for the candidate list.
3166 */
3167bool sortCandidates(CandidateForTesselation* candidate1, CandidateForTesselation* candidate2) {
3168 Vector BaseLineVector, OrthogonalVector, helper;
3169 if (candidate1->BaseLine != candidate2->BaseLine) { // sanity check
3170 cout << Verbose(0) << "ERROR: sortCandidates was called for two different baselines: " << candidate1->BaseLine << " and " << candidate2->BaseLine << "." << endl;
3171 //return false;
3172 exit(1);
3173 }
3174 // create baseline vector
3175 BaseLineVector.CopyVector(&(candidate1->BaseLine->endpoints[1]->node->x));
3176 BaseLineVector.SubtractVector(&(candidate1->BaseLine->endpoints[0]->node->x));
3177 BaseLineVector.Normalize();
3178
3179 // 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!)
3180 helper.CopyVector(&(candidate1->BaseLine->endpoints[0]->node->x));
3181 helper.SubtractVector(&(candidate1->point->x));
3182 OrthogonalVector.CopyVector(&helper);
3183 helper.VectorProduct(&BaseLineVector);
3184 OrthogonalVector.SubtractVector(&helper);
3185 OrthogonalVector.Normalize();
3186
3187 // calculate both angles and correct with in-plane vector
3188 helper.CopyVector(&(candidate1->point->x));
3189 helper.SubtractVector(&(candidate1->BaseLine->endpoints[0]->node->x));
3190 double phi = BaseLineVector.Angle(&helper);
3191 if (OrthogonalVector.ScalarProduct(&helper) > 0) {
3192 phi = 2.*M_PI - phi;
3193 }
3194 helper.CopyVector(&(candidate2->point->x));
3195 helper.SubtractVector(&(candidate1->BaseLine->endpoints[0]->node->x));
3196 double psi = BaseLineVector.Angle(&helper);
3197 if (OrthogonalVector.ScalarProduct(&helper) > 0) {
3198 psi = 2.*M_PI - psi;
3199 }
3200
3201 cout << Verbose(2) << *candidate1->point << " has angle " << phi << endl;
3202 cout << Verbose(2) << *candidate2->point << " has angle " << psi << endl;
3203
3204 // return comparison
3205 return phi < psi;
3206}
3207
3208/** Tesselates the non convex boundary by rolling a virtual sphere along the surface of the molecule.
3209 * \param *out output stream for debugging
3210 * \param *mol molecule structure with Atom's and Bond's
3211 * \param *Tess Tesselation filled with points, lines and triangles on boundary on return
3212 * \param *filename filename prefix for output of vertex data
3213 * \para RADIUS radius of the virtual sphere
3214 */
3215void Find_non_convex_border(ofstream *out, molecule* mol, class Tesselation *Tess, class LinkedCell *LCList, const char *filename, const double RADIUS)
3216{
3217 int N = 0;
3218 bool freeTess = false;
3219 bool freeLC = false;
3220 *out << Verbose(1) << "Entering search for non convex hull. " << endl;
3221 if (Tess == NULL) {
3222 *out << Verbose(1) << "Allocating Tesselation struct ..." << endl;
3223 Tess = new Tesselation;
3224 freeTess = true;
3225 }
3226 LineMap::iterator baseline;
3227 LineMap::iterator testline;
3228 *out << Verbose(0) << "Begin of Find_non_convex_border\n";
3229 bool flag = false; // marks whether we went once through all baselines without finding any without two triangles
3230 bool failflag = false;
3231
3232 if (LCList == NULL) {
3233 LCList = new LinkedCell(mol, 2.*RADIUS);
3234 freeLC = true;
3235 }
3236
3237 Tess->Find_starting_triangle(out, mol, RADIUS, LCList);
3238
3239 baseline = Tess->LinesOnBoundary.begin();
3240 while ((baseline != Tess->LinesOnBoundary.end()) || (flag)) {
3241 if (baseline->second->TrianglesCount == 1) {
3242 failflag = Tess->Find_next_suitable_triangle(out, mol, *(baseline->second), *(((baseline->second->triangles.begin()))->second), RADIUS, N, filename, LCList); //the line is there, so there is a triangle, but only one.
3243 flag = flag || failflag;
3244 if (!failflag)
3245 cerr << "WARNING: Find_next_suitable_triangle failed." << endl;
3246
3247 // we inserted new lines, hence show list with connected triangles
3248 cout << Verbose(1) << "List of Baselines with connected triangles so far:" << endl;
3249 for (testline = Tess->LinesOnBoundary.begin(); testline != Tess->LinesOnBoundary.end(); testline++) {
3250 cout << Verbose(1) << *testline->second << "\t" << testline->second->TrianglesCount << endl;
3251 }
3252 } else {
3253 cout << Verbose(1) << "Line " << *baseline->second << " has " << baseline->second->TrianglesCount << " triangles adjacent" << endl;
3254 if (baseline->second->TrianglesCount != 2)
3255 cout << Verbose(1) << "ERROR: TESSELATION FINISHED WITH INVALID TRIANGLE COUNT!" << endl;
3256 }
3257
3258 N++;
3259 baseline++;
3260 if ((baseline == Tess->LinesOnBoundary.end()) && (flag)) {
3261 baseline = Tess->LinesOnBoundary.begin(); // restart if we reach end due to newly inserted lines
3262 flag = false;
3263 }
3264 }
3265 if (1) { //failflag) {
3266 *out << Verbose(1) << "Writing final tecplot file\n";
3267 if (DoTecplotOutput) {
3268 string OutputName(filename);
3269 OutputName.append(TecplotSuffix);
3270 ofstream *tecplot = new ofstream(OutputName.c_str());
3271 write_tecplot_file(out, tecplot, Tess, mol, -1);
3272 tecplot->close();
3273 delete(tecplot);
3274 }
3275 if (DoRaster3DOutput) {
3276 string OutputName(filename);
3277 OutputName.append(Raster3DSuffix);
3278 ofstream *raster = new ofstream(OutputName.c_str());
3279 write_raster3d_file(out, raster, Tess, mol);
3280 raster->close();
3281 delete(raster);
3282 }
3283 } else {
3284 cerr << "ERROR: Could definitively not find all necessary triangles!" << endl;
3285 }
3286 if (freeTess)
3287 delete(Tess);
3288 if (freeLC)
3289 delete(LCList);
3290 *out << Verbose(0) << "End of Find_non_convex_border\n";
3291};
3292
3293/** Finds a hole of sufficient size in \a this molecule to embed \a *srcmol into it.
3294 * \param *out output stream for debugging
3295 * \param *srcmol molecule to embed into
3296 * \return *Vector new center of \a *srcmol for embedding relative to \a this
3297 */
3298Vector* molecule::FindEmbeddingHole(ofstream *out, molecule *srcmol)
3299{
3300 Vector *Center = new Vector;
3301 Center->Zero();
3302 // calculate volume/shape of \a *srcmol
3303
3304 // find embedding holes
3305
3306 // if more than one, let user choose
3307
3308 // return embedding center
3309 return Center;
3310};
3311
Note: See TracBrowser for help on using the repository browser.