source: src/boundary.cpp@ 0dbddc

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 Candidate_v1.7.0 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 0dbddc was 0dbddc, checked in by Frederik Heber <heber@…>, 16 years ago

Merge branch 'ConcaveHull' of ssh://heber@192.168.194.2/home/metzler/workspace/espack into ConcaveHull

Conflicts:

molecuilder/src/atom.cpp
molecuilder/src/boundary.cpp
molecuilder/src/boundary.hpp
molecuilder/src/linkedcell.cpp
molecuilder/src/linkedcell.hpp
molecuilder/src/molecules.hpp
molecuilder/src/vector.hpp

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