source: src/boundary.cpp@ 6ac7ee

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Added_ParseSaveFragmentResults AddingActions_SaveParseParticleParameters Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_ParticleName_to_Atom Adding_StructOpt_integration_tests AtomFragments Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator CombiningParticlePotentialParsing Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_BoundInBox_CenterInBox_MoleculeActions Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_PopActions Fix_QtFragmentList_sorted_selection Fix_Restrictedkeyset_FragmentMolecule Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns Fix_fitting_potentials Fixes ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion FragmentAction_writes_AtomFragments FragmentMolecule_checks_bonddegrees GeometryObjects Gui_Fixes Gui_displays_atomic_force_velocity ImplicitCharges IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix MoreRobust_FragmentAutomation ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PdbParser_setsAtomName PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks Rewrite_FitPartialCharges RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching SaturateAtoms_singleDegree StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg Switchable_LogView ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps TremoloParser_setsAtomName Ubuntu_1604_changes stable
Last change on this file since 6ac7ee was 6ac7ee, checked in by Frederik Heber <heber@…>, 16 years ago

Merge branch 'ConcaveHull' of ../espack2 into ConcaveHull

Conflicts:

molecuilder/src/boundary.cpp
molecuilder/src/boundary.hpp
molecuilder/src/builder.cpp
molecuilder/src/linkedcell.cpp
molecuilder/src/linkedcell.hpp
molecuilder/src/vector.cpp
molecuilder/src/vector.hpp
util/src/NanoCreator.c

Basically, this resulted from a lot of conversions two from spaces to one tab, which is my standard indentation. The mess was caused by eclipse auto-indenting. And in espack2:ConcaveHull was the new stuff, so all from ConcaveHull was replaced in case of doubt.
Additionally, vector had ofstream << operator instead ostream << ...

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