source: molecuilder/src/boundary.cpp@ 3f0c46

Last change on this file since 3f0c46 was 3f0c46, checked in by Frederik Heber <heber@…>, 17 years ago

Final touches for Create Clusters in water

repetition in CreateClustersinWater(), some stuff thrown out in VolumeOfConvexEnvelope (loop to throw out some more points, which does not happen if we take any appearing in at least one BoundaryPoints), some variable declarations shifted to the start of a function, BoundaryPoints are given to functions and if NULL is created by calling GetBoundaryPoints() and free'd subsequently.

  • Property mode set to 100644
File size: 44.9 KB
Line 
1#include "molecules.hpp"
2#include "boundary.hpp"
3
4// ======================================== Points on Boundary =================================
5
6BoundaryPointSet::BoundaryPointSet()
7{
8 LinesCount = 0;
9 Nr = -1;
10};
11
12BoundaryPointSet::BoundaryPointSet(atom *Walker)
13{
14 node = Walker;
15 LinesCount = 0;
16 Nr = Walker->nr;
17};
18
19BoundaryPointSet::~BoundaryPointSet()
20{
21 cout << Verbose(5) << "Erasing point nr. " << Nr << "." << endl;
22 node = NULL;
23};
24
25void BoundaryPointSet::AddLine(class BoundaryLineSet *line)
26{
27 cout << Verbose(6) << "Adding line " << *line << " to " << *this << "." << endl;
28 if (line->endpoints[0] == this) {
29 lines.insert ( LinePair( line->endpoints[1]->Nr, line) );
30 } else {
31 lines.insert ( LinePair( line->endpoints[0]->Nr, line) );
32 }
33 LinesCount++;
34};
35
36ostream & operator << (ostream &ost, BoundaryPointSet &a)
37{
38 ost << "[" << a.Nr << "|" << a.node->Name << "]";
39 return ost;
40};
41
42// ======================================== Lines on Boundary =================================
43
44BoundaryLineSet::BoundaryLineSet()
45{
46 for (int i=0;i<2;i++)
47 endpoints[i] = NULL;
48 TrianglesCount = 0;
49 Nr = -1;
50};
51
52BoundaryLineSet::BoundaryLineSet(class BoundaryPointSet *Point[2], int number)
53{
54 // set number
55 Nr = number;
56 // set endpoints in ascending order
57 SetEndpointsOrdered(endpoints, Point[0], Point[1]);
58 // add this line to the hash maps of both endpoints
59 Point[0]->AddLine(this);
60 Point[1]->AddLine(this);
61 // clear triangles list
62 TrianglesCount = 0;
63 cout << Verbose(5) << "New Line with endpoints " << *this << "." << endl;
64};
65
66BoundaryLineSet::~BoundaryLineSet()
67{
68 for (int i=0;i<2;i++) {
69 cout << Verbose(5) << "Erasing Line Nr. " << Nr << " in boundary point " << *endpoints[i] << "." << endl;
70 endpoints[i]->lines.erase(Nr);
71 LineMap::iterator tester = endpoints[i]->lines.begin();
72 tester++;
73 if (tester == endpoints[i]->lines.end()) {
74 cout << Verbose(5) << *endpoints[i] << " has no more lines it's attached to, erasing." << endl;
75 delete(endpoints[i]);
76 } else
77 cout << Verbose(5) << *endpoints[i] << " has still lines it's attached to." << endl;
78 }
79};
80
81void BoundaryLineSet::AddTriangle(class BoundaryTriangleSet *triangle)
82{
83 cout << Verbose(6) << "Add " << triangle->Nr << " to line " << *this << "." << endl;
84 triangles.insert ( TrianglePair( TrianglesCount, triangle) );
85 TrianglesCount++;
86};
87
88ostream & operator << (ostream &ost, BoundaryLineSet &a)
89{
90 ost << "[" << a.Nr << "|" << a.endpoints[0]->node->Name << "," << a.endpoints[1]->node->Name << "]";
91 return ost;
92};
93
94// ======================================== Triangles on Boundary =================================
95
96
97BoundaryTriangleSet::BoundaryTriangleSet()
98{
99 for (int i=0;i<3;i++) {
100 endpoints[i] = NULL;
101 lines[i] = NULL;
102 }
103 Nr = -1;
104};
105
106BoundaryTriangleSet::BoundaryTriangleSet(class BoundaryLineSet *line[3], int number)
107{
108 // set number
109 Nr = number;
110 // set lines
111 cout << Verbose(5) << "New triangle " << Nr << ":" << endl;
112 for (int i=0;i<3;i++) {
113 lines[i] = line[i];
114 lines[i]->AddTriangle(this);
115 }
116 // get ascending order of endpoints
117 map <int, class BoundaryPointSet * > OrderMap;
118 for(int i=0;i<3;i++) // for all three lines
119 for (int j=0;j<2;j++) { // for both endpoints
120 OrderMap.insert ( pair <int, class BoundaryPointSet * >( line[i]->endpoints[j]->Nr, line[i]->endpoints[j]) );
121 // and we don't care whether insertion fails
122 }
123 // set endpoints
124 int Counter = 0;
125 cout << Verbose(6) << " with end points ";
126 for (map <int, class BoundaryPointSet * >::iterator runner = OrderMap.begin(); runner != OrderMap.end(); runner++) {
127 endpoints[Counter] = runner->second;
128 cout << " " << *endpoints[Counter];
129 Counter++;
130 }
131 if (Counter < 3) {
132 cerr << "ERROR! We have a triangle with only two distinct endpoints!" << endl;
133 //exit(1);
134 }
135 cout << "." << endl;
136};
137
138BoundaryTriangleSet::~BoundaryTriangleSet()
139{
140 for (int i=0;i<3;i++) {
141 cout << Verbose(5) << "Erasing triangle Nr." << Nr << endl;
142 lines[i]->triangles.erase(Nr);
143 TriangleMap::iterator tester = lines[i]->triangles.begin();
144 tester++;
145 if (tester == lines[i]->triangles.end()) {
146 cout << Verbose(5) << *lines[i] << " is no more attached to any triangle, erasing." << endl;
147 delete(lines[i]);
148 } else
149 cout << Verbose(5) << *lines[i] << " is still attached to a triangle." << endl;
150 }
151};
152
153void BoundaryTriangleSet::GetNormalVector(vector &NormalVector)
154{
155 // get normal vector
156 NormalVector.MakeNormalVector(&endpoints[0]->node->x, &endpoints[1]->node->x, &endpoints[2]->node->x);
157
158 // make it always point inward (any offset vector onto plane projected onto normal vector suffices)
159 if (endpoints[0]->node->x.Projection(&NormalVector) > 0)
160 NormalVector.Scale(-1.);
161};
162
163ostream & operator << (ostream &ost, BoundaryTriangleSet &a)
164{
165 ost << "[" << a.Nr << "|" << a.endpoints[0]->node->Name << "," << a.endpoints[1]->node->Name << "," << a.endpoints[2]->node->Name << "]";
166 return ost;
167};
168
169// ========================================== F U N C T I O N S =================================
170
171/** Finds the endpoint two lines are sharing.
172 * \param *line1 first line
173 * \param *line2 second line
174 * \return point which is shared or NULL if none
175 */
176class BoundaryPointSet * GetCommonEndpoint(class BoundaryLineSet * line1, class BoundaryLineSet * line2)
177{
178 class BoundaryLineSet * lines[2] = {line1, line2};
179 class BoundaryPointSet *node = NULL;
180 map <int, class BoundaryPointSet * > OrderMap;
181 pair < map <int, class BoundaryPointSet * >::iterator, bool > OrderTest;
182 for(int i=0;i<2;i++) // for both lines
183 for (int j=0;j<2;j++) { // for both endpoints
184 OrderTest = OrderMap.insert ( pair <int, class BoundaryPointSet * >( lines[i]->endpoints[j]->Nr, lines[i]->endpoints[j]) );
185 if (!OrderTest.second) { // if insertion fails, we have common endpoint
186 node = OrderTest.first->second;
187 cout << Verbose(5) << "Common endpoint of lines " << *line1 << " and " << *line2 << " is: " << *node << "." << endl;
188 j=2;
189 i=2;
190 break;
191 }
192 }
193 return node;
194};
195
196/** Determines the boundary points of a cluster.
197 * Does a projection per axis onto the orthogonal plane, transforms into spherical coordinates, sorts them by the angle
198 * and looks at triples: if the middle has less a distance than the allowed maximum height of the triangle formed by the plane's
199 * center and first and last point in the triple, it is thrown out.
200 * \param *out output stream for debugging
201 * \param *mol molecule structure representing the cluster
202 */
203Boundaries * GetBoundaryPoints(ofstream *out, molecule *mol)
204{
205 atom *Walker = NULL;
206 PointMap PointsOnBoundary;
207 LineMap LinesOnBoundary;
208 TriangleMap TrianglesOnBoundary;
209
210 *out << Verbose(1) << "Finding all boundary points." << endl;
211 Boundaries *BoundaryPoints = new Boundaries [NDIM]; // first is alpha, second is (r, nr)
212 BoundariesTestPair BoundaryTestPair;
213 vector AxisVector, AngleReferenceVector, AngleReferenceNormalVector;
214 double radius, angle;
215 // 3a. Go through every axis
216 for (int axis=0; axis<NDIM; axis++) {
217 AxisVector.Zero();
218 AngleReferenceVector.Zero();
219 AngleReferenceNormalVector.Zero();
220 AxisVector.x[axis] = 1.;
221 AngleReferenceVector.x[(axis+1)%NDIM] = 1.;
222 AngleReferenceNormalVector.x[(axis+2)%NDIM] = 1.;
223 // *out << Verbose(1) << "Axisvector is ";
224 // AxisVector.Output(out);
225 // *out << " and AngleReferenceVector is ";
226 // AngleReferenceVector.Output(out);
227 // *out << "." << endl;
228 // *out << " and AngleReferenceNormalVector is ";
229 // AngleReferenceNormalVector.Output(out);
230 // *out << "." << endl;
231 // 3b. construct set of all points, transformed into cylindrical system and with left and right neighbours
232 Walker = mol->start;
233 while (Walker->next != mol->end) {
234 Walker = Walker->next;
235 vector ProjectedVector;
236 ProjectedVector.CopyVector(&Walker->x);
237 ProjectedVector.ProjectOntoPlane(&AxisVector);
238 // correct for negative side
239 //if (Projection(y) < 0)
240 //angle = 2.*M_PI - angle;
241 radius = ProjectedVector.Norm();
242 if (fabs(radius) > MYEPSILON)
243 angle = ProjectedVector.Angle(&AngleReferenceVector);
244 else
245 angle = 0.; // otherwise it's a vector in Axis Direction and unimportant for boundary issues
246
247 //*out << "Checking sign in quadrant : " << ProjectedVector.Projection(&AngleReferenceNormalVector) << "." << endl;
248 if (ProjectedVector.Projection(&AngleReferenceNormalVector) > 0) {
249 angle = 2.*M_PI - angle;
250 }
251 //*out << Verbose(2) << "Inserting " << *Walker << ": (r, alpha) = (" << radius << "," << angle << "): ";
252 //ProjectedVector.Output(out);
253 //*out << endl;
254 BoundaryTestPair = BoundaryPoints[axis].insert( BoundariesPair (angle, DistanceNrPair (radius, Walker) ) );
255 if (BoundaryTestPair.second) { // successfully inserted
256 } else { // same point exists, check first r, then distance of original vectors to center of gravity
257 *out << Verbose(2) << "Encountered two vectors whose projection onto axis " << axis << " is equal: " << endl;
258 *out << Verbose(2) << "Present vector: ";
259 BoundaryTestPair.first->second.second->x.Output(out);
260 *out << endl;
261 *out << Verbose(2) << "New vector: ";
262 Walker->x.Output(out);
263 *out << endl;
264 double tmp = ProjectedVector.Norm();
265 if (tmp > BoundaryTestPair.first->second.first) {
266 BoundaryTestPair.first->second.first = tmp;
267 BoundaryTestPair.first->second.second = Walker;
268 *out << Verbose(2) << "Keeping new vector." << endl;
269 } else if (tmp == BoundaryTestPair.first->second.first) {
270 if (BoundaryTestPair.first->second.second->x.ScalarProduct(&BoundaryTestPair.first->second.second->x) < Walker->x.ScalarProduct(&Walker->x)) { // Norm() does a sqrt, which makes it a lot slower
271 BoundaryTestPair.first->second.second = Walker;
272 *out << Verbose(2) << "Keeping new vector." << endl;
273 } else {
274 *out << Verbose(2) << "Keeping present vector." << endl;
275 }
276 } else {
277 *out << Verbose(2) << "Keeping present vector." << endl;
278 }
279 }
280 }
281 // printing all inserted for debugging
282 // {
283 // *out << Verbose(2) << "Printing list of candidates for axis " << axis << " which we have inserted so far." << endl;
284 // int i=0;
285 // for(Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner != BoundaryPoints[axis].end(); runner++) {
286 // if (runner != BoundaryPoints[axis].begin())
287 // *out << ", " << i << ": " << *runner->second.second;
288 // else
289 // *out << i << ": " << *runner->second.second;
290 // i++;
291 // }
292 // *out << endl;
293 // }
294 // 3c. throw out points whose distance is less than the mean of left and right neighbours
295 bool flag = false;
296 do { // do as long as we still throw one out per round
297 *out << Verbose(1) << "Looking for candidates to kick out by convex condition ... " << endl;
298 flag = false;
299 Boundaries::iterator left = BoundaryPoints[axis].end();
300 Boundaries::iterator right = BoundaryPoints[axis].end();
301 for(Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner != BoundaryPoints[axis].end(); runner++) {
302 // set neighbours correctly
303 if (runner == BoundaryPoints[axis].begin()) {
304 left = BoundaryPoints[axis].end();
305 } else {
306 left = runner;
307 }
308 left--;
309 right = runner;
310 right++;
311 if (right == BoundaryPoints[axis].end()) {
312 right = BoundaryPoints[axis].begin();
313 }
314 // check distance
315
316 // construct the vector of each side of the triangle on the projected plane (defined by normal vector AxisVector)
317 {
318 vector SideA, SideB, SideC, SideH;
319 SideA.CopyVector(&left->second.second->x);
320 SideA.ProjectOntoPlane(&AxisVector);
321 // *out << "SideA: ";
322 // SideA.Output(out);
323 // *out << endl;
324
325 SideB.CopyVector(&right->second.second->x);
326 SideB.ProjectOntoPlane(&AxisVector);
327 // *out << "SideB: ";
328 // SideB.Output(out);
329 // *out << endl;
330
331 SideC.CopyVector(&left->second.second->x);
332 SideC.SubtractVector(&right->second.second->x);
333 SideC.ProjectOntoPlane(&AxisVector);
334 // *out << "SideC: ";
335 // SideC.Output(out);
336 // *out << endl;
337
338 SideH.CopyVector(&runner->second.second->x);
339 SideH.ProjectOntoPlane(&AxisVector);
340 // *out << "SideH: ";
341 // SideH.Output(out);
342 // *out << endl;
343
344 // calculate each length
345 double a = SideA.Norm();
346 //double b = SideB.Norm();
347 //double c = SideC.Norm();
348 double h = SideH.Norm();
349 // calculate the angles
350 double alpha = SideA.Angle(&SideH);
351 double beta = SideA.Angle(&SideC);
352 double gamma = SideB.Angle(&SideH);
353 double delta = SideC.Angle(&SideH);
354 double MinDistance = a * sin(beta)/(sin(delta)) * (((alpha < M_PI/2.) || (gamma < M_PI/2.)) ? 1. : -1.);
355 // *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;
356 //*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;
357 if ((fabs(h/fabs(h) - MinDistance/fabs(MinDistance)) < MYEPSILON) && (h < MinDistance)) {
358 // throw out point
359 //*out << Verbose(1) << "Throwing out " << *runner->second.second << "." << endl;
360 BoundaryPoints[axis].erase(runner);
361 flag = true;
362 }
363 }
364 }
365 } while (flag);
366 }
367 return BoundaryPoints;
368};
369
370/** Determines greatest diameters of a cluster defined by its convex envelope.
371 * Looks at lines parallel to one axis and where they intersect on the projected planes
372 * \param *out output stream for debugging
373 * \param *BoundaryPoints NDIM set of boundary points defining the convex envelope on each projected plane
374 * \param *mol molecule structure representing the cluster
375 * \param IsAngstroem whether we have angstroem or atomic units
376 * \return NDIM array of the diameters
377 */
378double * GetDiametersOfCluster(ofstream *out, Boundaries *BoundaryPtr, molecule *mol, bool IsAngstroem)
379{
380 // get points on boundary of NULL was given as parameter
381 bool BoundaryFreeFlag = false;
382 Boundaries *BoundaryPoints = BoundaryPtr;
383 if (BoundaryPoints == NULL) {
384 BoundaryFreeFlag = true;
385 BoundaryPoints = GetBoundaryPoints(out, mol);
386 } else {
387 *out << Verbose(1) << "Using given boundary points set." << endl;
388 }
389
390 // determine biggest "diameter" of cluster for each axis
391 Boundaries::iterator Neighbour, OtherNeighbour;
392 double *GreatestDiameter = new double[NDIM];
393 for(int i=0;i<NDIM;i++)
394 GreatestDiameter[i] = 0.;
395 double OldComponent, tmp, w1, w2;
396 vector DistanceVector, OtherVector;
397 int component, Othercomponent;
398 for(int axis=0;axis<NDIM;axis++) { // regard each projected plane
399 //*out << Verbose(1) << "Current axis is " << axis << "." << endl;
400 for (int j=0;j<2;j++) { // and for both axis on the current plane
401 component = (axis+j+1)%NDIM;
402 Othercomponent = (axis+1+((j+1) & 1))%NDIM;
403 //*out << Verbose(1) << "Current component is " << component << ", Othercomponent is " << Othercomponent << "." << endl;
404 for(Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner != BoundaryPoints[axis].end(); runner++) {
405 //*out << Verbose(2) << "Current runner is " << *(runner->second.second) << "." << endl;
406 // seek for the neighbours pair where the Othercomponent sign flips
407 Neighbour = runner;
408 Neighbour++;
409 if (Neighbour == BoundaryPoints[axis].end()) // make it wrap around
410 Neighbour = BoundaryPoints[axis].begin();
411 DistanceVector.CopyVector(&runner->second.second->x);
412 DistanceVector.SubtractVector(&Neighbour->second.second->x);
413 do { // seek for neighbour pair where it flips
414 OldComponent = DistanceVector.x[Othercomponent];
415 Neighbour++;
416 if (Neighbour == BoundaryPoints[axis].end()) // make it wrap around
417 Neighbour = BoundaryPoints[axis].begin();
418 DistanceVector.CopyVector(&runner->second.second->x);
419 DistanceVector.SubtractVector(&Neighbour->second.second->x);
420 //*out << Verbose(3) << "OldComponent is " << OldComponent << ", new one is " << DistanceVector.x[Othercomponent] << "." << endl;
421 } while ((runner != Neighbour) && ( fabs( OldComponent/fabs(OldComponent) - DistanceVector.x[Othercomponent]/fabs(DistanceVector.x[Othercomponent]) ) < MYEPSILON)); // as long as sign does not flip
422 if (runner != Neighbour) {
423 OtherNeighbour = Neighbour;
424 if (OtherNeighbour == BoundaryPoints[axis].begin()) // make it wrap around
425 OtherNeighbour = BoundaryPoints[axis].end();
426 OtherNeighbour--;
427 //*out << Verbose(2) << "The pair, where the sign of OtherComponent flips, is: " << *(Neighbour->second.second) << " and " << *(OtherNeighbour->second.second) << "." << endl;
428 // now we have found the pair: Neighbour and OtherNeighbour
429 OtherVector.CopyVector(&runner->second.second->x);
430 OtherVector.SubtractVector(&OtherNeighbour->second.second->x);
431 //*out << Verbose(2) << "Distances to Neighbour and OtherNeighbour are " << DistanceVector.x[component] << " and " << OtherVector.x[component] << "." << endl;
432 //*out << Verbose(2) << "OtherComponents to Neighbour and OtherNeighbour are " << DistanceVector.x[Othercomponent] << " and " << OtherVector.x[Othercomponent] << "." << endl;
433 // do linear interpolation between points (is exact) to extract exact intersection between Neighbour and OtherNeighbour
434 w1 = fabs(OtherVector.x[Othercomponent]);
435 w2 = fabs(DistanceVector.x[Othercomponent]);
436 tmp = fabs((w1*DistanceVector.x[component] + w2*OtherVector.x[component])/(w1+w2));
437 // mark if it has greater diameter
438 //*out << Verbose(2) << "Comparing current greatest " << GreatestDiameter[component] << " to new " << tmp << "." << endl;
439 GreatestDiameter[component] = (GreatestDiameter[component] > tmp) ? GreatestDiameter[component] : tmp;
440 } //else
441 //*out << Verbose(2) << "Saw no sign flip, probably top or bottom node." << endl;
442 }
443 }
444 }
445 *out << Verbose(0) << "RESULT: The biggest diameters are " << GreatestDiameter[0] << " and " << GreatestDiameter[1] << " and " << GreatestDiameter[2] << " " << (IsAngstroem ? "angstrom" : "atomiclength") << "." << endl;
446
447 // free reference lists
448 if (BoundaryFreeFlag)
449 delete[](BoundaryPoints);
450
451 return GreatestDiameter;
452};
453
454
455/** Determines the volume of a cluster.
456 * Determines first the convex envelope, then tesselates it and calculates its volume.
457 * \param *out output stream for debugging
458 * \param *configuration needed for path to store convex envelope file
459 * \param *BoundaryPoints NDIM set of boundary points on the projected plane per axis, on return if desired
460 * \param *mol molecule structure representing the cluster
461 */
462double VolumeOfConvexEnvelope(ofstream *out, config *configuration, Boundaries *BoundaryPtr, molecule *mol)
463{
464 bool IsAngstroem = configuration->GetIsAngstroem();
465 atom *Walker = NULL;
466 struct Tesselation *TesselStruct = new Tesselation;
467 bool BoundaryFreeFlag = false;
468 Boundaries *BoundaryPoints = BoundaryPtr;
469 double volume = 0.;
470 double PyramidVolume = 0.;
471 double G,h;
472 vector x,y;
473 double a,b,c;
474
475 // 1. calculate center of gravity
476 *out << endl;
477 vector *CenterOfGravity = mol->DetermineCenterOfGravity(out);
478
479 // 2. translate all points into CoG
480 *out << Verbose(1) << "Translating system to Center of Gravity." << endl;
481 Walker = mol->start;
482 while (Walker->next != mol->end) {
483 Walker = Walker->next;
484 Walker->x.Translate(CenterOfGravity);
485 }
486
487 // 3. Find all points on the boundary
488 if (BoundaryPoints == NULL) {
489 BoundaryFreeFlag = true;
490 BoundaryPoints = GetBoundaryPoints(out, mol);
491 } else {
492 *out << Verbose(1) << "Using given boundary points set." << endl;
493 }
494
495 // 4. fill the boundary point list
496 for (int axis=0;axis<NDIM;axis++)
497 for(Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner != BoundaryPoints[axis].end(); runner++) {
498 TesselStruct->AddPoint(runner->second.second);
499 }
500
501 *out << Verbose(2) << "I found " << TesselStruct->PointsOnBoundaryCount << " points on the convex boundary." << endl;
502 // now we have the whole set of edge points in the BoundaryList
503
504
505 // listing for debugging
506// *out << Verbose(1) << "Listing PointsOnBoundary:";
507// for(PointMap::iterator runner = PointsOnBoundary.begin(); runner != PointsOnBoundary.end(); runner++) {
508// *out << " " << *runner->second;
509// }
510// *out << endl;
511
512 // 5a. guess starting triangle
513 TesselStruct->GuessStartingTriangle(out);
514
515 // 5b. go through all lines, that are not yet part of two triangles (only of one so far)
516 TesselStruct->TesselateOnBoundary(out, configuration, mol);
517
518 *out << Verbose(2) << "I created " << TesselStruct->TrianglesOnBoundaryCount << " triangles with " << TesselStruct->LinesOnBoundaryCount << " lines and " << TesselStruct->PointsOnBoundaryCount << " points." << endl;
519
520 // 6a. Every triangle forms a pyramid with the center of gravity as its peak, sum up the volumes
521 *out << Verbose(1) << "Calculating the volume of the pyramids formed out of triangles and center of gravity." << endl;
522 for (TriangleMap::iterator runner = TesselStruct->TrianglesOnBoundary.begin(); runner != TesselStruct->TrianglesOnBoundary.end(); runner++) { // go through every triangle, calculate volume of its pyramid with CoG as peak
523 x.CopyVector(&runner->second->endpoints[0]->node->x);
524 x.SubtractVector(&runner->second->endpoints[1]->node->x);
525 y.CopyVector(&runner->second->endpoints[0]->node->x);
526 y.SubtractVector(&runner->second->endpoints[2]->node->x);
527 a = sqrt(runner->second->endpoints[0]->node->x.Distance(&runner->second->endpoints[1]->node->x));
528 b = sqrt(runner->second->endpoints[0]->node->x.Distance(&runner->second->endpoints[2]->node->x));
529 c = sqrt(runner->second->endpoints[2]->node->x.Distance(&runner->second->endpoints[1]->node->x));
530 G = sqrt( ( (a*a+b*b+c*c)*(a*a+b*b+c*c) - 2*(a*a*a*a + b*b*b*b + c*c*c*c) )/16.); // area of tesselated triangle
531 x.MakeNormalVector(&runner->second->endpoints[0]->node->x, &runner->second->endpoints[1]->node->x, &runner->second->endpoints[2]->node->x);
532 x.Scale(runner->second->endpoints[1]->node->x.Projection(&x));
533 h = x.Norm(); // distance of CoG to triangle
534 PyramidVolume = (1./3.) * G * h; // this formula holds for _all_ pyramids (independent of n-edge base or (not) centered peak)
535 *out << Verbose(2) << "Area of triangle is " << G << " " << (IsAngstroem ? "angstrom" : "atomiclength") << "^2, height is " << h << " and the volume is " << PyramidVolume << " " << (IsAngstroem ? "angstrom" : "atomiclength") << "^3." << endl;
536 volume += PyramidVolume;
537 }
538 *out << Verbose(0) << "RESULT: The summed volume is " << setprecision(10) << volume << " " << (IsAngstroem ? "angstrom" : "atomiclength") << "^3." << endl;
539
540
541 // 7. translate all points back from CoG
542 *out << Verbose(1) << "Translating system back from Center of Gravity." << endl;
543 CenterOfGravity->Scale(-1);
544 Walker = mol->start;
545 while (Walker->next != mol->end) {
546 Walker = Walker->next;
547 Walker->x.Translate(CenterOfGravity);
548 }
549
550 // free reference lists
551 if (BoundaryFreeFlag)
552 delete[](BoundaryPoints);
553
554 return volume;
555};
556
557
558/** Creates multiples of the by \a *mol given cluster and suspends them in water with a given final density.
559 * We get cluster volume by VolumeOfConvexEnvelope() and its diameters by GetDiametersOfCluster()
560 * \param *out output stream for debugging
561 * \param *configuration needed for path to store convex envelope file
562 * \param *mol molecule structure representing the cluster
563 * \param celldensity desired average density in final cell
564 */
565void PrepareClustersinWater(ofstream *out, config *configuration, molecule *mol, double celldensity)
566{
567 // transform to PAS
568 mol->PrincipalAxisSystem(out, true);
569
570 // some preparations beforehand
571 bool IsAngstroem = configuration->GetIsAngstroem();
572 Boundaries *BoundaryPoints = GetBoundaryPoints(out, mol);
573 double clustervolume = VolumeOfConvexEnvelope(out, configuration, BoundaryPoints, mol);
574 double *GreatestDiameter = GetDiametersOfCluster(out, BoundaryPoints, mol, IsAngstroem);
575 vector BoxLengths;
576 int repetition[NDIM] = {1, 1, 1};
577 int TotalNoClusters = 1;
578 for (int i=0;i<NDIM;i++)
579 TotalNoClusters *= repetition[i];
580
581 // sum up the atomic masses
582 double totalmass = 0.;
583 atom *Walker = mol->start;
584 while (Walker->next != mol->end) {
585 Walker = Walker->next;
586 totalmass += Walker->type->mass;
587 }
588 *out << Verbose(0) << "RESULT: The summed mass is " << setprecision(10) << totalmass << " atomicmassunit." << endl;
589
590 *out << Verbose(0) << "RESULT: The average density is " << setprecision(10) << totalmass/clustervolume << " atomicmassunit/" << (IsAngstroem ? "angstrom" : "atomiclength") << "^3." << endl;
591
592 // solve cubic polynomial
593 *out << Verbose(1) << "Solving equidistant suspension in water problem ..." << endl;
594 double cellvolume;
595 if (IsAngstroem)
596 cellvolume = (TotalNoClusters*totalmass/SOLVENTDENSITY_A - (totalmass/clustervolume))/(celldensity-1);
597 else
598 cellvolume = (TotalNoClusters*totalmass/SOLVENTDENSITY_a0 - (totalmass/clustervolume))/(celldensity-1);
599 *out << Verbose(1) << "Cellvolume needed for a density of " << celldensity << " g/cm^3 is " << cellvolume << " " << (IsAngstroem ? "angstrom" : "atomiclength") << "^3." << endl;
600
601 double minimumvolume = TotalNoClusters*(GreatestDiameter[0]*GreatestDiameter[1]*GreatestDiameter[2]);
602 *out << Verbose(1) << "Minimum volume of the convex envelope contained in a rectangular box is " << minimumvolume << " atomicmassunit/" << (IsAngstroem ? "angstrom" : "atomiclength") << "^3." << endl;
603 if (minimumvolume > cellvolume)
604 cerr << Verbose(0) << "ERROR: the containing box already has a greater volume than the envisaged cell volume!" << endl;
605
606 BoxLengths.x[0] = (repetition[0]*GreatestDiameter[0] + repetition[1]*GreatestDiameter[1] + repetition[2]*GreatestDiameter[2]);
607 BoxLengths.x[1] = (repetition[0]*repetition[1]*GreatestDiameter[0]*GreatestDiameter[1]
608 + repetition[0]*repetition[2]*GreatestDiameter[0]*GreatestDiameter[2]
609 + repetition[1]*repetition[2]*GreatestDiameter[1]*GreatestDiameter[2]);
610 BoxLengths.x[2] = minimumvolume - cellvolume;
611 double x0 = 0.,x1 = 0.,x2 = 0.;
612 if (gsl_poly_solve_cubic(BoxLengths.x[0],BoxLengths.x[1],BoxLengths.x[2],&x0,&x1,&x2) == 1) // either 1 or 3 on return
613 *out << Verbose(0) << "RESULT: The resulting spacing is: " << x0 << " ." << endl;
614 else {
615 *out << Verbose(0) << "RESULT: The resulting spacings are: " << x0 << " and " << x1 << " and " << x2 << " ." << endl;
616 x0 = x2; // sorted in ascending order
617 }
618
619 cellvolume = 1;
620 for(int i=0;i<NDIM;i++) {
621 BoxLengths.x[i] = repetition[i] * (x0 + GreatestDiameter[i]);
622 cellvolume *= BoxLengths.x[i];
623 }
624 *out << Verbose(0) << "RESULT: The resulting cell dimensions are: " << BoxLengths.x[0] << " and " << BoxLengths.x[1] << " and " << BoxLengths.x[2] << " with total volume of " << cellvolume << " " << (IsAngstroem ? "angstrom" : "atomiclength") << "^3." << endl;
625
626 // set new box dimensions
627 *out << Verbose(0) << "Translating to box with these boundaries." << endl;
628 mol->CenterInBox((ofstream *)&cout, &BoxLengths);
629 // update Box of atoms by boundary
630 mol->SetBoxDimension(&BoxLengths);
631
632};
633
634
635// =========================================================== class TESSELATION ===========================================
636
637/** Constructor of class Tesselation.
638 */
639Tesselation::Tesselation()
640{
641 PointsOnBoundaryCount = 0;
642 LinesOnBoundaryCount = 0;
643 TrianglesOnBoundaryCount = 0;
644};
645
646/** Constructor of class Tesselation.
647 * We have to free all points, lines and triangles.
648 */
649Tesselation::~Tesselation()
650{
651 for (TriangleMap::iterator runner = TrianglesOnBoundary.begin(); runner != TrianglesOnBoundary.end(); runner++) {
652 delete(runner->second);
653 }
654};
655
656/** Gueses first starting triangle of the convex envelope.
657 * We guess the starting triangle by taking the smallest distance between two points and looking for a fitting third.
658 * \param *out output stream for debugging
659 * \param PointsOnBoundary set of boundary points defining the convex envelope of the cluster
660 */
661void Tesselation::GuessStartingTriangle(ofstream *out)
662{
663 // 4b. create a starting triangle
664 // 4b1. create all distances
665 DistanceMultiMap DistanceMMap;
666 double distance;
667 for (PointMap::iterator runner = PointsOnBoundary.begin(); runner != PointsOnBoundary.end(); runner++) {
668 for(PointMap::iterator sprinter = PointsOnBoundary.begin(); sprinter != PointsOnBoundary.end(); sprinter++) {
669 if (runner->first < sprinter->first) {
670 distance = runner->second->node->x.Distance(&sprinter->second->node->x);
671 DistanceMMap.insert( DistanceMultiMapPair(distance, pair<PointMap::iterator, PointMap::iterator>(runner,sprinter) ) );
672 }
673 }
674 }
675
676// // listing distances
677// *out << Verbose(1) << "Listing DistanceMMap:";
678// for(DistanceMultiMap::iterator runner = DistanceMMap.begin(); runner != DistanceMMap.end(); runner++) {
679// *out << " " << runner->first << "(" << *runner->second.first->second << ", " << *runner->second.second->second << ")";
680// }
681// *out << endl;
682
683 // 4b2. take three smallest distance that form a triangle
684 // we take the smallest distance as the base line
685 DistanceMultiMap::iterator baseline = DistanceMMap.begin();
686 BPS[0] = baseline->second.first->second;
687 BPS[1] = baseline->second.second->second;
688 BLS[0] = new class BoundaryLineSet(BPS , LinesOnBoundaryCount);
689
690 // take the second smallest as the second base line
691 DistanceMultiMap::iterator secondline = DistanceMMap.begin();
692 do {
693 secondline++;
694 } while (!(
695 (BPS[0] == secondline->second.first->second) && (BPS[1] != secondline->second.second->second) ||
696 (BPS[0] == secondline->second.second->second) && (BPS[1] != secondline->second.first->second) ||
697 (BPS[1] == secondline->second.first->second) && (BPS[0] != secondline->second.second->second) ||
698 (BPS[1] == secondline->second.second->second) && (BPS[0] != secondline->second.first->second)
699 ));
700 BPS[0] = secondline->second.first->second;
701 BPS[1] = secondline->second.second->second;
702 BLS[1] = new class BoundaryLineSet(BPS , LinesOnBoundaryCount);
703
704 // connection yields the third line (note: first and second endpoint are sorted!)
705 if (baseline->second.first->second == secondline->second.first->second) {
706 SetEndpointsOrdered(BPS, baseline->second.second->second, secondline->second.second->second);
707 } else if (baseline->second.first->second == secondline->second.second->second) {
708 SetEndpointsOrdered(BPS, baseline->second.second->second, secondline->second.first->second);
709 } else if (baseline->second.second->second == secondline->second.first->second) {
710 SetEndpointsOrdered(BPS, baseline->second.first->second, baseline->second.second->second);
711 } else if (baseline->second.second->second == secondline->second.second->second) {
712 SetEndpointsOrdered(BPS, baseline->second.first->second, baseline->second.first->second);
713 }
714 BLS[2] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
715
716 // 4b3. insert created triangle
717 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
718 TrianglesOnBoundary.insert( TrianglePair(TrianglesOnBoundaryCount, BTS) );
719 TrianglesOnBoundaryCount++;
720 for(int i=0;i<NDIM;i++) {
721 LinesOnBoundary.insert( LinePair(LinesOnBoundaryCount, BTS->lines[i]) );
722 LinesOnBoundaryCount++;
723 }
724
725 *out << Verbose(1) << "Starting triangle is " << *BTS << "." << endl;
726};
727
728
729/** Tesselates the convex envelope of a cluster from a single starting triangle.
730 * The starting triangle is made out of three baselines. Each line in the final tesselated cluster may belong to at most
731 * 2 triangles. Hence, we go through all current lines:
732 * -# if the lines contains to only one triangle
733 * -# We search all points in the boundary
734 * -# if the triangle with the baseline and the current point has the smallest of angles (comparison between normal vectors
735 * -# if the triangle is in forward direction of the baseline (at most 90 degrees angle between vector orthogonal to
736 * baseline in triangle plane pointing out of the triangle and normal vector of new triangle)
737 * -# then we have a new triangle, whose baselines we again add (or increase their TriangleCount)
738 * \param *out output stream for debugging
739 * \param *configuration for IsAngstroem
740 * \param *mol the cluster as a molecule structure
741 */
742void Tesselation::TesselateOnBoundary(ofstream *out, config *configuration, molecule *mol)
743{
744 bool flag;
745 PointMap::iterator winner;
746 class BoundaryPointSet *peak = NULL;
747 double SmallestAngle, TempAngle;
748 vector NormalVector, VirtualNormalVector, CenterVector, TempVector, PropagationVector;
749 LineMap::iterator LineChecker[2];
750 do {
751 flag = false;
752 for (LineMap::iterator baseline = LinesOnBoundary.begin(); baseline != LinesOnBoundary.end(); baseline++)
753 if (baseline->second->TrianglesCount == 1) {
754 *out << Verbose(2) << "Current baseline is between " << *(baseline->second) << "." << endl;
755 // 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)
756 SmallestAngle = M_PI;
757 BTS = baseline->second->triangles.begin()->second; // there is only one triangle so far
758 // get peak point with respect to this base line's only triangle
759 for(int i=0;i<3;i++)
760 if ((BTS->endpoints[i] != baseline->second->endpoints[0]) && (BTS->endpoints[i] != baseline->second->endpoints[1]))
761 peak = BTS->endpoints[i];
762 *out << Verbose(3) << " and has peak " << *peak << "." << endl;
763 // normal vector of triangle
764 BTS->GetNormalVector(NormalVector);
765 *out << Verbose(4) << "NormalVector of base triangle is ";
766 NormalVector.Output(out);
767 *out << endl;
768 // offset to center of triangle
769 CenterVector.Zero();
770 for(int i=0;i<3;i++)
771 CenterVector.AddVector(&BTS->endpoints[i]->node->x);
772 CenterVector.Scale(1./3.);
773 *out << Verbose(4) << "CenterVector of base triangle is ";
774 CenterVector.Output(out);
775 *out << endl;
776 // vector in propagation direction (out of triangle)
777 // project center vector onto triangle plane (points from intersection plane-NormalVector to plane-CenterVector intersection)
778 TempVector.CopyVector(&baseline->second->endpoints[0]->node->x);
779 TempVector.SubtractVector(&baseline->second->endpoints[1]->node->x);
780 PropagationVector.MakeNormalVector(&TempVector, &NormalVector);
781 TempVector.CopyVector(&CenterVector);
782 TempVector.SubtractVector(&baseline->second->endpoints[0]->node->x); // TempVector is vector on triangle plane pointing from one baseline egde towards center!
783 //*out << Verbose(2) << "Projection of propagation onto temp: " << PropagationVector.Projection(&TempVector) << "." << endl;
784 if (PropagationVector.Projection(&TempVector) > 0) // make sure normal propagation vector points outward from baseline
785 PropagationVector.Scale(-1.);
786 *out << Verbose(4) << "PropagationVector of base triangle is ";
787 PropagationVector.Output(out);
788 *out << endl;
789 winner = PointsOnBoundary.end();
790 for (PointMap::iterator target = PointsOnBoundary.begin(); target != PointsOnBoundary.end(); target++)
791 if ((target->second != baseline->second->endpoints[0]) && (target->second != baseline->second->endpoints[1])) { // don't take the same endpoints
792 *out << Verbose(3) << "Target point is " << *(target->second) << ":";
793 bool continueflag = true;
794
795 VirtualNormalVector.CopyVector(&baseline->second->endpoints[0]->node->x);
796 VirtualNormalVector.AddVector(&baseline->second->endpoints[0]->node->x);
797 VirtualNormalVector.Scale(-1./2.); // points now to center of base line
798 VirtualNormalVector.AddVector(&target->second->node->x); // points from center of base line to target
799 TempAngle = VirtualNormalVector.Angle(&PropagationVector);
800 continueflag = continueflag && (TempAngle < (M_PI/2.)); // no bends bigger than Pi/2 (90 degrees)
801 if (!continueflag) {
802 *out << Verbose(4) << "Angle between propagation direction and base line to " << *(target->second) << " is " << TempAngle << ", bad direction!" << endl;
803 continue;
804 } else
805 *out << Verbose(4) << "Angle between propagation direction and base line to " << *(target->second) << " is " << TempAngle << ", good direction!" << endl;
806 LineChecker[0] = baseline->second->endpoints[0]->lines.find(target->first);
807 LineChecker[1] = baseline->second->endpoints[1]->lines.find(target->first);
808 // if (LineChecker[0] != baseline->second->endpoints[0]->lines.end())
809 // *out << Verbose(4) << *(baseline->second->endpoints[0]) << " has line " << *(LineChecker[0]->second) << " to " << *(target->second) << " as endpoint with " << LineChecker[0]->second->TrianglesCount << " triangles." << endl;
810 // else
811 // *out << Verbose(4) << *(baseline->second->endpoints[0]) << " has no line to " << *(target->second) << " as endpoint." << endl;
812 // if (LineChecker[1] != baseline->second->endpoints[1]->lines.end())
813 // *out << Verbose(4) << *(baseline->second->endpoints[1]) << " has line " << *(LineChecker[1]->second) << " to " << *(target->second) << " as endpoint with " << LineChecker[1]->second->TrianglesCount << " triangles." << endl;
814 // else
815 // *out << Verbose(4) << *(baseline->second->endpoints[1]) << " has no line to " << *(target->second) << " as endpoint." << endl;
816 // check first endpoint (if any connecting line goes to target or at least not more than 1)
817 continueflag = continueflag && (( (LineChecker[0] == baseline->second->endpoints[0]->lines.end()) || (LineChecker[0]->second->TrianglesCount == 1)));
818 if (!continueflag) {
819 *out << Verbose(4) << *(baseline->second->endpoints[0]) << " has line " << *(LineChecker[0]->second) << " to " << *(target->second) << " as endpoint with " << LineChecker[0]->second->TrianglesCount << " triangles." << endl;
820 continue;
821 }
822 // check second endpoint (if any connecting line goes to target or at least not more than 1)
823 continueflag = continueflag && (( (LineChecker[1] == baseline->second->endpoints[1]->lines.end()) || (LineChecker[1]->second->TrianglesCount == 1)));
824 if (!continueflag) {
825 *out << Verbose(4) << *(baseline->second->endpoints[1]) << " has line " << *(LineChecker[1]->second) << " to " << *(target->second) << " as endpoint with " << LineChecker[1]->second->TrianglesCount << " triangles." << endl;
826 continue;
827 }
828 // check whether the envisaged triangle does not already exist (if both lines exist and have same endpoint)
829 continueflag = continueflag && (!(
830 ((LineChecker[0] != baseline->second->endpoints[0]->lines.end()) && (LineChecker[1] != baseline->second->endpoints[1]->lines.end())
831 && (GetCommonEndpoint(LineChecker[0]->second, LineChecker[1]->second) == peak))
832 ));
833 if (!continueflag) {
834 *out << Verbose(4) << "Current target is peak!" << endl;
835 continue;
836 }
837 // in case NOT both were found
838 if (continueflag) { // create virtually this triangle, get its normal vector, calculate angle
839 flag = true;
840 VirtualNormalVector.MakeNormalVector(&baseline->second->endpoints[0]->node->x, &baseline->second->endpoints[1]->node->x, &target->second->node->x);
841 // make it always point inward
842 if (baseline->second->endpoints[0]->node->x.Projection(&VirtualNormalVector) > 0)
843 VirtualNormalVector.Scale(-1.);
844 // calculate angle
845 TempAngle = NormalVector.Angle(&VirtualNormalVector);
846 *out << Verbose(4) << "NormalVector is ";
847 VirtualNormalVector.Output(out);
848 *out << " and the angle is " << TempAngle << "." << endl;
849 if (SmallestAngle > TempAngle) { // set to new possible winner
850 SmallestAngle = TempAngle;
851 winner = target;
852 }
853 }
854 }
855 // 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
856 if (winner != PointsOnBoundary.end()) {
857 *out << Verbose(2) << "Winning target point is " << *(winner->second) << " with angle " << SmallestAngle << "." << endl;
858 // create the lins of not yet present
859 BLS[0] = baseline->second;
860 // 5c. add lines to the line set if those were new (not yet part of a triangle), delete lines that belong to two triangles)
861 LineChecker[0] = baseline->second->endpoints[0]->lines.find(winner->first);
862 LineChecker[1] = baseline->second->endpoints[1]->lines.find(winner->first);
863 if (LineChecker[0] == baseline->second->endpoints[0]->lines.end()) { // create
864 BPS[0] = baseline->second->endpoints[0];
865 BPS[1] = winner->second;
866 BLS[1] = new class BoundaryLineSet(BPS , LinesOnBoundaryCount);
867 LinesOnBoundary.insert( LinePair(LinesOnBoundaryCount, BLS[1]) );
868 LinesOnBoundaryCount++;
869 } else
870 BLS[1] = LineChecker[0]->second;
871 if (LineChecker[1] == baseline->second->endpoints[1]->lines.end()) { // create
872 BPS[0] = baseline->second->endpoints[1];
873 BPS[1] = winner->second;
874 BLS[2] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
875 LinesOnBoundary.insert( LinePair(LinesOnBoundaryCount, BLS[2]) );
876 LinesOnBoundaryCount++;
877 } else
878 BLS[2] = LineChecker[1]->second;
879 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
880 TrianglesOnBoundary.insert( TrianglePair(TrianglesOnBoundaryCount, BTS) );
881 TrianglesOnBoundaryCount++;
882 } else {
883 *out << Verbose(1) << "I could not determine a winner for this baseline " << *(baseline->second) << "." << endl;
884 }
885
886 // 5d. If the set of lines is not yet empty, go to 5. and continue
887 } else
888 *out << Verbose(2) << "Baseline candidate " << *(baseline->second) << " has a triangle count of " << baseline->second->TrianglesCount << "." << endl;
889 } while (flag);
890
891 stringstream line;
892 line << configuration->configpath << "/" << CONVEXENVELOPE;
893 *out << Verbose(1) << "Storing convex envelope in tecplot data file " << line.str() << "." << endl;
894 ofstream output(line.str().c_str());
895 output << "TITLE = \"3D CONVEX SHELL\"" << endl;
896 output << "VARIABLES = \"X\" \"Y\" \"Z\"" << endl;
897 output << "ZONE T=\"TRIANGLES\", N=" << PointsOnBoundaryCount << ", E=" << TrianglesOnBoundaryCount << ", DATAPACKING=POINT, ZONETYPE=FETRIANGLE" << endl;
898 int *LookupList = new int[mol->AtomCount];
899 for (int i=0;i<mol->AtomCount;i++)
900 LookupList[i] = -1;
901
902 // print atom coordinates
903 *out << Verbose(2) << "The following triangles were created:";
904 int Counter = 1;
905 atom *Walker = NULL;
906 for (PointMap::iterator target = PointsOnBoundary.begin(); target != PointsOnBoundary.end(); target++) {
907 Walker = target->second->node;
908 LookupList[Walker->nr] = Counter++;
909 output << Walker->x.x[0] << " " << Walker->x.x[1] << " " << Walker->x.x[2] << " " << endl;
910 }
911 output << endl;
912 // print connectivity
913 for (TriangleMap::iterator runner = TrianglesOnBoundary.begin(); runner != TrianglesOnBoundary.end(); runner++) {
914 *out << " " << runner->second->endpoints[0]->node->Name << "<->" << runner->second->endpoints[1]->node->Name << "<->" << runner->second->endpoints[2]->node->Name;
915 output << LookupList[runner->second->endpoints[0]->node->nr] << " " << LookupList[runner->second->endpoints[1]->node->nr] << " " << LookupList[runner->second->endpoints[2]->node->nr] << endl;
916 }
917 output.close();
918 delete[](LookupList);
919 *out << endl;
920};
921
922/** Adds an atom to the tesselation::PointsOnBoundary list.
923 * \param *Walker atom to add
924 */
925void Tesselation::AddPoint(atom *Walker)
926{
927 BPS[0] = new class BoundaryPointSet(Walker);
928 PointsOnBoundary.insert( PointPair(Walker->nr, BPS[0]) );
929 PointsOnBoundaryCount++;
930};
Note: See TracBrowser for help on using the repository browser.