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