source: src/boundary.cpp@ d067d45

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

Merge branch 'MultipleMolecules'

Conflicts:

molecuilder/src/analyzer.cpp
molecuilder/src/atom.cpp
molecuilder/src/boundary.cpp
molecuilder/src/boundary.hpp
molecuilder/src/builder.cpp
molecuilder/src/config.cpp
molecuilder/src/datacreator.hpp
molecuilder/src/helpers.cpp
molecuilder/src/joiner.cpp
molecuilder/src/moleculelist.cpp
molecuilder/src/molecules.cpp
molecuilder/src/molecules.hpp
molecuilder/src/parser.cpp
molecuilder/src/parser.hpp
molecuilder/src/vector.cpp
molecuilder/src/verbose.cpp

merges:

compilation fixes:

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