source: src/tesselationhelpers.cpp@ 8725ed

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

Fixes and naming of final Tecplot output file is now molecule name.

  • FIXES to builder.cpp:
    • case 'p' would not dissect the molecule into connected subgraphs
    • if done so, the BondGraph was not yet initialised
    • if done so, we need to check whether BondGraphFileName has been set
    • BondGraphFileName.empty() could not be used as a check, as was set to "" instead of "\n"
    • if (finally) done so, we have to remove the empty molecule that we parsed in
    • ... and then pick the newly added molecule for mol to point at
    • SaveConfig() did not set the merged molecule name correctly.
    • if empty config is given, the empty molecule now receives the ConfigFileName as name
  • FIXES to config.cpp
    • Load() did not set the name of the molecule
  • changes to tesselationhelper.cpp and tesselation.cpp
  • changes to PointCloud and molecule
    • new virtual function PointCloud::GetName() returns "unknown"
    • new function molecule::GetName() returns pointer to name of molecule
    • IsEmpty() and IsEnd() now return true by default
  • all .dat files in the "Tesselations" tests were changed accordingly (i.e. have now the name in the second line)
  • benzene was added as a new test for an absolutely flat molecule. So far, it fails horribly.
  • 13 of 17 tests run fine

Signed-off-by: Frederik Heber <heber@…>

  • Property mode set to 100644
File size: 36.4 KB
Line 
1/*
2 * TesselationHelpers.cpp
3 *
4 * Created on: Aug 3, 2009
5 * Author: heber
6 */
7
8#include <fstream>
9
10#include "linkedcell.hpp"
11#include "log.hpp"
12#include "tesselation.hpp"
13#include "tesselationhelpers.hpp"
14#include "vector.hpp"
15#include "verbose.hpp"
16
17double DetGet(gsl_matrix * const A, const int inPlace) {
18 /*
19 inPlace = 1 => A is replaced with the LU decomposed copy.
20 inPlace = 0 => A is retained, and a copy is used for LU.
21 */
22
23 double det;
24 int signum;
25 gsl_permutation *p = gsl_permutation_alloc(A->size1);
26 gsl_matrix *tmpA;
27
28 if (inPlace)
29 tmpA = A;
30 else {
31 gsl_matrix *tmpA = gsl_matrix_alloc(A->size1, A->size2);
32 gsl_matrix_memcpy(tmpA , A);
33 }
34
35
36 gsl_linalg_LU_decomp(tmpA , p , &signum);
37 det = gsl_linalg_LU_det(tmpA , signum);
38 gsl_permutation_free(p);
39 if (! inPlace)
40 gsl_matrix_free(tmpA);
41
42 return det;
43};
44
45void GetSphere(Vector * const center, const Vector &a, const Vector &b, const Vector &c, const double RADIUS)
46{
47 gsl_matrix *A = gsl_matrix_calloc(3,3);
48 double m11, m12, m13, m14;
49
50 for(int i=0;i<3;i++) {
51 gsl_matrix_set(A, i, 0, a.x[i]);
52 gsl_matrix_set(A, i, 1, b.x[i]);
53 gsl_matrix_set(A, i, 2, c.x[i]);
54 }
55 m11 = DetGet(A, 1);
56
57 for(int i=0;i<3;i++) {
58 gsl_matrix_set(A, i, 0, a.x[i]*a.x[i] + b.x[i]*b.x[i] + c.x[i]*c.x[i]);
59 gsl_matrix_set(A, i, 1, b.x[i]);
60 gsl_matrix_set(A, i, 2, c.x[i]);
61 }
62 m12 = DetGet(A, 1);
63
64 for(int i=0;i<3;i++) {
65 gsl_matrix_set(A, i, 0, a.x[i]*a.x[i] + b.x[i]*b.x[i] + c.x[i]*c.x[i]);
66 gsl_matrix_set(A, i, 1, a.x[i]);
67 gsl_matrix_set(A, i, 2, c.x[i]);
68 }
69 m13 = DetGet(A, 1);
70
71 for(int i=0;i<3;i++) {
72 gsl_matrix_set(A, i, 0, a.x[i]*a.x[i] + b.x[i]*b.x[i] + c.x[i]*c.x[i]);
73 gsl_matrix_set(A, i, 1, a.x[i]);
74 gsl_matrix_set(A, i, 2, b.x[i]);
75 }
76 m14 = DetGet(A, 1);
77
78 if (fabs(m11) < MYEPSILON)
79 eLog() << Verbose(1) << "three points are colinear." << endl;
80
81 center->x[0] = 0.5 * m12/ m11;
82 center->x[1] = -0.5 * m13/ m11;
83 center->x[2] = 0.5 * m14/ m11;
84
85 if (fabs(a.Distance(center) - RADIUS) > MYEPSILON)
86 eLog() << Verbose(1) << "The given center is further way by " << fabs(a.Distance(center) - RADIUS) << " from a than RADIUS." << endl;
87
88 gsl_matrix_free(A);
89};
90
91
92
93/**
94 * Function returns center of sphere with RADIUS, which rests on points a, b, c
95 * @param Center this vector will be used for return
96 * @param a vector first point of triangle
97 * @param b vector second point of triangle
98 * @param c vector third point of triangle
99 * @param *Umkreismittelpunkt new center point of circumference
100 * @param Direction vector indicates up/down
101 * @param AlternativeDirection Vector, needed in case the triangles have 90 deg angle
102 * @param Halfplaneindicator double indicates whether Direction is up or down
103 * @param AlternativeIndicator double indicates in case of orthogonal triangles which direction of AlternativeDirection is suitable
104 * @param alpha double angle at a
105 * @param beta double, angle at b
106 * @param gamma, double, angle at c
107 * @param Radius, double
108 * @param Umkreisradius double radius of circumscribing circle
109 */
110void GetCenterOfSphere(Vector* const & Center, const Vector &a, const Vector &b, const Vector &c, Vector * const NewUmkreismittelpunkt, const Vector* const Direction, const Vector* const AlternativeDirection,
111 const double HalfplaneIndicator, const double AlternativeIndicator, const double alpha, const double beta, const double gamma, const double RADIUS, const double Umkreisradius)
112{
113 Vector TempNormal, helper;
114 double Restradius;
115 Vector OtherCenter;
116 Log() << Verbose(3) << "Begin of GetCenterOfSphere.\n";
117 Center->Zero();
118 helper.CopyVector(&a);
119 helper.Scale(sin(2.*alpha));
120 Center->AddVector(&helper);
121 helper.CopyVector(&b);
122 helper.Scale(sin(2.*beta));
123 Center->AddVector(&helper);
124 helper.CopyVector(&c);
125 helper.Scale(sin(2.*gamma));
126 Center->AddVector(&helper);
127 //*Center = a * sin(2.*alpha) + b * sin(2.*beta) + c * sin(2.*gamma) ;
128 Center->Scale(1./(sin(2.*alpha) + sin(2.*beta) + sin(2.*gamma)));
129 NewUmkreismittelpunkt->CopyVector(Center);
130 Log() << Verbose(4) << "Center of new circumference is " << *NewUmkreismittelpunkt << ".\n";
131 // Here we calculated center of circumscribing circle, using barycentric coordinates
132 Log() << Verbose(4) << "Center of circumference is " << *Center << " in direction " << *Direction << ".\n";
133
134 TempNormal.CopyVector(&a);
135 TempNormal.SubtractVector(&b);
136 helper.CopyVector(&a);
137 helper.SubtractVector(&c);
138 TempNormal.VectorProduct(&helper);
139 if (fabs(HalfplaneIndicator) < MYEPSILON)
140 {
141 if ((TempNormal.ScalarProduct(AlternativeDirection) <0 and AlternativeIndicator >0) or (TempNormal.ScalarProduct(AlternativeDirection) >0 and AlternativeIndicator <0))
142 {
143 TempNormal.Scale(-1);
144 }
145 }
146 else
147 {
148 if (TempNormal.ScalarProduct(Direction)<0 && HalfplaneIndicator >0 || TempNormal.ScalarProduct(Direction)>0 && HalfplaneIndicator<0)
149 {
150 TempNormal.Scale(-1);
151 }
152 }
153
154 TempNormal.Normalize();
155 Restradius = sqrt(RADIUS*RADIUS - Umkreisradius*Umkreisradius);
156 Log() << Verbose(4) << "Height of center of circumference to center of sphere is " << Restradius << ".\n";
157 TempNormal.Scale(Restradius);
158 Log() << Verbose(4) << "Shift vector to sphere of circumference is " << TempNormal << ".\n";
159
160 Center->AddVector(&TempNormal);
161 Log() << Verbose(0) << "Center of sphere of circumference is " << *Center << ".\n";
162 GetSphere(&OtherCenter, a, b, c, RADIUS);
163 Log() << Verbose(0) << "OtherCenter of sphere of circumference is " << OtherCenter << ".\n";
164 Log() << Verbose(3) << "End of GetCenterOfSphere.\n";
165};
166
167
168/** Constructs the center of the circumcircle defined by three points \a *a, \a *b and \a *c.
169 * \param *Center new center on return
170 * \param *a first point
171 * \param *b second point
172 * \param *c third point
173 */
174void GetCenterofCircumcircle(Vector * const Center, const Vector &a, const Vector &b, const Vector &c)
175{
176 Vector helper;
177 double alpha, beta, gamma;
178 Vector SideA, SideB, SideC;
179 SideA.CopyVector(b);
180 SideA.SubtractVector(&c);
181 SideB.CopyVector(c);
182 SideB.SubtractVector(&a);
183 SideC.CopyVector(a);
184 SideC.SubtractVector(&b);
185 alpha = M_PI - SideB.Angle(&SideC);
186 beta = M_PI - SideC.Angle(&SideA);
187 gamma = M_PI - SideA.Angle(&SideB);
188 //Log() << Verbose(3) << "INFO: alpha = " << alpha/M_PI*180. << ", beta = " << beta/M_PI*180. << ", gamma = " << gamma/M_PI*180. << "." << endl;
189 if (fabs(M_PI - alpha - beta - gamma) > HULLEPSILON) {
190 eLog() << Verbose(1) << "GetCenterofCircumcircle: Sum of angles " << (alpha+beta+gamma)/M_PI*180. << " > 180 degrees by " << fabs(M_PI - alpha - beta - gamma)/M_PI*180. << "!" << endl;
191 }
192
193 Center->Zero();
194 helper.CopyVector(a);
195 helper.Scale(sin(2.*alpha));
196 Center->AddVector(&helper);
197 helper.CopyVector(b);
198 helper.Scale(sin(2.*beta));
199 Center->AddVector(&helper);
200 helper.CopyVector(c);
201 helper.Scale(sin(2.*gamma));
202 Center->AddVector(&helper);
203 Center->Scale(1./(sin(2.*alpha) + sin(2.*beta) + sin(2.*gamma)));
204};
205
206/** 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.
207 * Test whether the \a NewSphereCenter is really on the given plane and in distance \a CircleRadius from \a CircleCenter.
208 * It calculates the angle, making it unique on [0,2.*M_PI) by comparing to SearchDirection.
209 * 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).
210 * \param CircleCenter Center of the parameter circle
211 * \param CirclePlaneNormal normal vector to plane of the parameter circle
212 * \param CircleRadius radius of the parameter circle
213 * \param NewSphereCenter new center of a circumcircle
214 * \param OldSphereCenter old center of a circumcircle, defining the zero "path length" on the parameter circle
215 * \param NormalVector normal vector
216 * \param SearchDirection search direction to make angle unique on return.
217 * \return Angle between \a NewSphereCenter and \a OldSphereCenter relative to \a CircleCenter, 2.*M_PI if one test fails
218 */
219double GetPathLengthonCircumCircle(const Vector &CircleCenter, const Vector &CirclePlaneNormal, const double CircleRadius, const Vector &NewSphereCenter, const Vector &OldSphereCenter, const Vector &NormalVector, const Vector &SearchDirection)
220{
221 Vector helper;
222 double radius, alpha;
223
224 helper.CopyVector(&NewSphereCenter);
225 // test whether new center is on the parameter circle's plane
226 if (fabs(helper.ScalarProduct(&CirclePlaneNormal)) > HULLEPSILON) {
227 eLog() << Verbose(1) << "Something's very wrong here: NewSphereCenter is not on the band's plane as desired by " <<fabs(helper.ScalarProduct(&CirclePlaneNormal)) << "!" << endl;
228 helper.ProjectOntoPlane(&CirclePlaneNormal);
229 }
230 radius = helper.ScalarProduct(&helper);
231 // test whether the new center vector has length of CircleRadius
232 if (fabs(radius - CircleRadius) > HULLEPSILON)
233 eLog() << Verbose(1) << "The projected center of the new sphere has radius " << radius << " instead of " << CircleRadius << "." << endl;
234 alpha = helper.Angle(&OldSphereCenter);
235 // make the angle unique by checking the halfplanes/search direction
236 if (helper.ScalarProduct(&SearchDirection) < -HULLEPSILON) // acos is not unique on [0, 2.*M_PI), hence extra check to decide between two half intervals
237 alpha = 2.*M_PI - alpha;
238 //Log() << Verbose(2) << "INFO: RelativeNewSphereCenter is " << helper << ", RelativeOldSphereCenter is " << OldSphereCenter << " and resulting angle is " << alpha << "." << endl;
239 radius = helper.Distance(&OldSphereCenter);
240 helper.ProjectOntoPlane(&NormalVector);
241 // check whether new center is somewhat away or at least right over the current baseline to prevent intersecting triangles
242 if ((radius > HULLEPSILON) || (helper.Norm() < HULLEPSILON)) {
243 //Log() << Verbose(2) << "INFO: Distance between old and new center is " << radius << " and between new center and baseline center is " << helper.Norm() << "." << endl;
244 return alpha;
245 } else {
246 //Log() << Verbose(1) << "INFO: NewSphereCenter " << helper << " is too close to OldSphereCenter" << OldSphereCenter << "." << endl;
247 return 2.*M_PI;
248 }
249};
250
251struct Intersection {
252 Vector x1;
253 Vector x2;
254 Vector x3;
255 Vector x4;
256};
257
258/**
259 * Intersection calculation function.
260 *
261 * @param x to find the result for
262 * @param function parameter
263 */
264double MinIntersectDistance(const gsl_vector * x, void *params)
265{
266 double retval = 0;
267 struct Intersection *I = (struct Intersection *)params;
268 Vector intersection;
269 Vector SideA,SideB,HeightA, HeightB;
270 for (int i=0;i<NDIM;i++)
271 intersection.x[i] = gsl_vector_get(x, i);
272
273 SideA.CopyVector(&(I->x1));
274 SideA.SubtractVector(&I->x2);
275 HeightA.CopyVector(&intersection);
276 HeightA.SubtractVector(&I->x1);
277 HeightA.ProjectOntoPlane(&SideA);
278
279 SideB.CopyVector(&I->x3);
280 SideB.SubtractVector(&I->x4);
281 HeightB.CopyVector(&intersection);
282 HeightB.SubtractVector(&I->x3);
283 HeightB.ProjectOntoPlane(&SideB);
284
285 retval = HeightA.ScalarProduct(&HeightA) + HeightB.ScalarProduct(&HeightB);
286 //Log() << Verbose(2) << "MinIntersectDistance called, result: " << retval << endl;
287
288 return retval;
289};
290
291
292/**
293 * Calculates whether there is an intersection between two lines. The first line
294 * always goes through point 1 and point 2 and the second line is given by the
295 * connection between point 4 and point 5.
296 *
297 * @param point 1 of line 1
298 * @param point 2 of line 1
299 * @param point 1 of line 2
300 * @param point 2 of line 2
301 *
302 * @return true if there is an intersection between the given lines, false otherwise
303 */
304bool existsIntersection(const Vector &point1, const Vector &point2, const Vector &point3, const Vector &point4)
305{
306 bool result;
307
308 struct Intersection par;
309 par.x1.CopyVector(&point1);
310 par.x2.CopyVector(&point2);
311 par.x3.CopyVector(&point3);
312 par.x4.CopyVector(&point4);
313
314 const gsl_multimin_fminimizer_type *T = gsl_multimin_fminimizer_nmsimplex;
315 gsl_multimin_fminimizer *s = NULL;
316 gsl_vector *ss, *x;
317 gsl_multimin_function minexFunction;
318
319 size_t iter = 0;
320 int status;
321 double size;
322
323 /* Starting point */
324 x = gsl_vector_alloc(NDIM);
325 gsl_vector_set(x, 0, point1.x[0]);
326 gsl_vector_set(x, 1, point1.x[1]);
327 gsl_vector_set(x, 2, point1.x[2]);
328
329 /* Set initial step sizes to 1 */
330 ss = gsl_vector_alloc(NDIM);
331 gsl_vector_set_all(ss, 1.0);
332
333 /* Initialize method and iterate */
334 minexFunction.n = NDIM;
335 minexFunction.f = &MinIntersectDistance;
336 minexFunction.params = (void *)&par;
337
338 s = gsl_multimin_fminimizer_alloc(T, NDIM);
339 gsl_multimin_fminimizer_set(s, &minexFunction, x, ss);
340
341 do {
342 iter++;
343 status = gsl_multimin_fminimizer_iterate(s);
344
345 if (status) {
346 break;
347 }
348
349 size = gsl_multimin_fminimizer_size(s);
350 status = gsl_multimin_test_size(size, 1e-2);
351
352 if (status == GSL_SUCCESS) {
353 Log() << Verbose(2) << "converged to minimum" << endl;
354 }
355 } while (status == GSL_CONTINUE && iter < 100);
356
357 // check whether intersection is in between or not
358 Vector intersection, SideA, SideB, HeightA, HeightB;
359 double t1, t2;
360 for (int i = 0; i < NDIM; i++) {
361 intersection.x[i] = gsl_vector_get(s->x, i);
362 }
363
364 SideA.CopyVector(&par.x2);
365 SideA.SubtractVector(&par.x1);
366 HeightA.CopyVector(&intersection);
367 HeightA.SubtractVector(&par.x1);
368
369 t1 = HeightA.ScalarProduct(&SideA)/SideA.ScalarProduct(&SideA);
370
371 SideB.CopyVector(&par.x4);
372 SideB.SubtractVector(&par.x3);
373 HeightB.CopyVector(&intersection);
374 HeightB.SubtractVector(&par.x3);
375
376 t2 = HeightB.ScalarProduct(&SideB)/SideB.ScalarProduct(&SideB);
377
378 Log() << Verbose(2) << "Intersection " << intersection << " is at "
379 << t1 << " for (" << point1 << "," << point2 << ") and at "
380 << t2 << " for (" << point3 << "," << point4 << "): ";
381
382 if (((t1 >= 0) && (t1 <= 1)) && ((t2 >= 0) && (t2 <= 1))) {
383 Log() << Verbose(0) << "true intersection." << endl;
384 result = true;
385 } else {
386 Log() << Verbose(0) << "intersection out of region of interest." << endl;
387 result = false;
388 }
389
390 // free minimizer stuff
391 gsl_vector_free(x);
392 gsl_vector_free(ss);
393 gsl_multimin_fminimizer_free(s);
394
395 return result;
396};
397
398/** Gets the angle between a point and a reference relative to the provided center.
399 * We have two shanks point and reference between which the angle is calculated
400 * and by scalar product with OrthogonalVector we decide the interval.
401 * @param point to calculate the angle for
402 * @param reference to which to calculate the angle
403 * @param OrthogonalVector points in direction of [pi,2pi] interval
404 *
405 * @return angle between point and reference
406 */
407double GetAngle(const Vector &point, const Vector &reference, const Vector &OrthogonalVector)
408{
409 if (reference.IsZero())
410 return M_PI;
411
412 // calculate both angles and correct with in-plane vector
413 if (point.IsZero())
414 return M_PI;
415 double phi = point.Angle(&reference);
416 if (OrthogonalVector.ScalarProduct(&point) > 0) {
417 phi = 2.*M_PI - phi;
418 }
419
420 Log() << Verbose(4) << "INFO: " << point << " has angle " << phi << " with respect to reference " << reference << "." << endl;
421
422 return phi;
423}
424
425
426/** Calculates the volume of a general tetraeder.
427 * \param *a first vector
428 * \param *a first vector
429 * \param *a first vector
430 * \param *a first vector
431 * \return \f$ \frac{1}{6} \cdot ((a-d) \times (a-c) \cdot (a-b)) \f$
432 */
433double CalculateVolumeofGeneralTetraeder(const Vector &a, const Vector &b, const Vector &c, const Vector &d)
434{
435 Vector Point, TetraederVector[3];
436 double volume;
437
438 TetraederVector[0].CopyVector(a);
439 TetraederVector[1].CopyVector(b);
440 TetraederVector[2].CopyVector(c);
441 for (int j=0;j<3;j++)
442 TetraederVector[j].SubtractVector(&d);
443 Point.CopyVector(&TetraederVector[0]);
444 Point.VectorProduct(&TetraederVector[1]);
445 volume = 1./6. * fabs(Point.ScalarProduct(&TetraederVector[2]));
446 return volume;
447};
448
449
450/** Checks for a new special triangle whether one of its edges is already present with one one triangle connected.
451 * This enforces that special triangles (i.e. degenerated ones) should at last close the open-edge frontier and not
452 * make it bigger (i.e. closing one (the baseline) and opening two new ones).
453 * \param TPS[3] nodes of the triangle
454 * \return true - there is such a line (i.e. creation of degenerated triangle is valid), false - no such line (don't create)
455 */
456bool CheckLineCriteriaForDegeneratedTriangle(const BoundaryPointSet * const nodes[3])
457{
458 bool result = false;
459 int counter = 0;
460
461 // check all three points
462 for (int i=0;i<3;i++)
463 for (int j=i+1; j<3; j++) {
464 if (nodes[i] == NULL) {
465 Log() << Verbose(1) << "Node nr. " << i << " is not yet present." << endl;
466 result = true;
467 } else if (nodes[i]->lines.find(nodes[j]->node->nr) != nodes[i]->lines.end()) { // there already is a line
468 LineMap::const_iterator FindLine;
469 pair<LineMap::const_iterator,LineMap::const_iterator> FindPair;
470 FindPair = nodes[i]->lines.equal_range(nodes[j]->node->nr);
471 for (FindLine = FindPair.first; FindLine != FindPair.second; ++FindLine) {
472 // If there is a line with less than two attached triangles, we don't need a new line.
473 if (FindLine->second->triangles.size() < 2) {
474 counter++;
475 break; // increase counter only once per edge
476 }
477 }
478 } else { // no line
479 Log() << Verbose(1) << "The line between " << *nodes[i] << " and " << *nodes[j] << " is not yet present, hence no need for a degenerate triangle." << endl;
480 result = true;
481 }
482 }
483 if ((!result) && (counter > 1)) {
484 Log() << Verbose(2) << "INFO: Degenerate triangle is ok, at least two, here " << counter << ", existing lines are used." << endl;
485 result = true;
486 }
487 return result;
488};
489
490
491/** Sort function for the candidate list.
492 */
493bool SortCandidates(const CandidateForTesselation* candidate1, const CandidateForTesselation* candidate2)
494{
495 Vector BaseLineVector, OrthogonalVector, helper;
496 if (candidate1->BaseLine != candidate2->BaseLine) { // sanity check
497 eLog() << Verbose(1) << "sortCandidates was called for two different baselines: " << candidate1->BaseLine << " and " << candidate2->BaseLine << "." << endl;
498 //return false;
499 exit(1);
500 }
501 // create baseline vector
502 BaseLineVector.CopyVector(candidate1->BaseLine->endpoints[1]->node->node);
503 BaseLineVector.SubtractVector(candidate1->BaseLine->endpoints[0]->node->node);
504 BaseLineVector.Normalize();
505
506 // 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!)
507 helper.CopyVector(candidate1->BaseLine->endpoints[0]->node->node);
508 helper.SubtractVector(candidate1->point->node);
509 OrthogonalVector.CopyVector(&helper);
510 helper.VectorProduct(&BaseLineVector);
511 OrthogonalVector.SubtractVector(&helper);
512 OrthogonalVector.Normalize();
513
514 // calculate both angles and correct with in-plane vector
515 helper.CopyVector(candidate1->point->node);
516 helper.SubtractVector(candidate1->BaseLine->endpoints[0]->node->node);
517 double phi = BaseLineVector.Angle(&helper);
518 if (OrthogonalVector.ScalarProduct(&helper) > 0) {
519 phi = 2.*M_PI - phi;
520 }
521 helper.CopyVector(candidate2->point->node);
522 helper.SubtractVector(candidate1->BaseLine->endpoints[0]->node->node);
523 double psi = BaseLineVector.Angle(&helper);
524 if (OrthogonalVector.ScalarProduct(&helper) > 0) {
525 psi = 2.*M_PI - psi;
526 }
527
528 Log() << Verbose(2) << *candidate1->point << " has angle " << phi << endl;
529 Log() << Verbose(2) << *candidate2->point << " has angle " << psi << endl;
530
531 // return comparison
532 return phi < psi;
533};
534
535/**
536 * Finds the point which is second closest to the provided one.
537 *
538 * @param Point to which to find the second closest other point
539 * @param linked cell structure
540 *
541 * @return point which is second closest to the provided one
542 */
543TesselPoint* FindSecondClosestPoint(const Vector* Point, const LinkedCell* const LC)
544{
545 TesselPoint* closestPoint = NULL;
546 TesselPoint* secondClosestPoint = NULL;
547 double distance = 1e16;
548 double secondDistance = 1e16;
549 Vector helper;
550 int N[NDIM], Nlower[NDIM], Nupper[NDIM];
551
552 LC->SetIndexToVector(Point); // ignore status as we calculate bounds below sensibly
553 for(int i=0;i<NDIM;i++) // store indices of this cell
554 N[i] = LC->n[i];
555 Log() << Verbose(2) << "INFO: Center cell is " << N[0] << ", " << N[1] << ", " << N[2] << " with No. " << LC->index << "." << endl;
556
557 LC->GetNeighbourBounds(Nlower, Nupper);
558 //Log() << Verbose(0) << endl;
559 for (LC->n[0] = Nlower[0]; LC->n[0] <= Nupper[0]; LC->n[0]++)
560 for (LC->n[1] = Nlower[1]; LC->n[1] <= Nupper[1]; LC->n[1]++)
561 for (LC->n[2] = Nlower[2]; LC->n[2] <= Nupper[2]; LC->n[2]++) {
562 const LinkedNodes *List = LC->GetCurrentCell();
563 //Log() << Verbose(3) << "The current cell " << LC->n[0] << "," << LC->n[1] << "," << LC->n[2] << endl;
564 if (List != NULL) {
565 for (LinkedNodes::const_iterator Runner = List->begin(); Runner != List->end(); Runner++) {
566 helper.CopyVector(Point);
567 helper.SubtractVector((*Runner)->node);
568 double currentNorm = helper. Norm();
569 if (currentNorm < distance) {
570 // remember second point
571 secondDistance = distance;
572 secondClosestPoint = closestPoint;
573 // mark down new closest point
574 distance = currentNorm;
575 closestPoint = (*Runner);
576 //Log() << Verbose(2) << "INFO: New Second Nearest Neighbour is " << *secondClosestPoint << "." << endl;
577 }
578 }
579 } else {
580 eLog() << Verbose(1) << "The current cell " << LC->n[0] << "," << LC->n[1] << ","
581 << LC->n[2] << " is invalid!" << endl;
582 }
583 }
584
585 return secondClosestPoint;
586};
587
588/**
589 * Finds the point which is closest to the provided one.
590 *
591 * @param Point to which to find the closest other point
592 * @param SecondPoint the second closest other point on return, NULL if none found
593 * @param linked cell structure
594 *
595 * @return point which is closest to the provided one, NULL if none found
596 */
597TesselPoint* FindClosestPoint(const Vector* Point, TesselPoint *&SecondPoint, const LinkedCell* const LC)
598{
599 TesselPoint* closestPoint = NULL;
600 SecondPoint = NULL;
601 double distance = 1e16;
602 double secondDistance = 1e16;
603 Vector helper;
604 int N[NDIM], Nlower[NDIM], Nupper[NDIM];
605
606 LC->SetIndexToVector(Point); // ignore status as we calculate bounds below sensibly
607 for(int i=0;i<NDIM;i++) // store indices of this cell
608 N[i] = LC->n[i];
609 Log() << Verbose(3) << "INFO: Center cell is " << N[0] << ", " << N[1] << ", " << N[2] << " with No. " << LC->index << "." << endl;
610
611 LC->GetNeighbourBounds(Nlower, Nupper);
612 //Log() << Verbose(0) << endl;
613 for (LC->n[0] = Nlower[0]; LC->n[0] <= Nupper[0]; LC->n[0]++)
614 for (LC->n[1] = Nlower[1]; LC->n[1] <= Nupper[1]; LC->n[1]++)
615 for (LC->n[2] = Nlower[2]; LC->n[2] <= Nupper[2]; LC->n[2]++) {
616 const LinkedNodes *List = LC->GetCurrentCell();
617 //Log() << Verbose(3) << "The current cell " << LC->n[0] << "," << LC->n[1] << "," << LC->n[2] << endl;
618 if (List != NULL) {
619 for (LinkedNodes::const_iterator Runner = List->begin(); Runner != List->end(); Runner++) {
620 helper.CopyVector(Point);
621 helper.SubtractVector((*Runner)->node);
622 double currentNorm = helper. Norm();
623 if (currentNorm < distance) {
624 secondDistance = distance;
625 SecondPoint = closestPoint;
626 distance = currentNorm;
627 closestPoint = (*Runner);
628 //Log() << Verbose(2) << "INFO: New Nearest Neighbour is " << *closestPoint << "." << endl;
629 } else if (currentNorm < secondDistance) {
630 secondDistance = currentNorm;
631 SecondPoint = (*Runner);
632 //Log() << Verbose(2) << "INFO: New Second Nearest Neighbour is " << *SecondPoint << "." << endl;
633 }
634 }
635 } else {
636 eLog() << Verbose(1) << "The current cell " << LC->n[0] << "," << LC->n[1] << ","
637 << LC->n[2] << " is invalid!" << endl;
638 }
639 }
640 // output
641 if (closestPoint != NULL) {
642 Log() << Verbose(2) << "Closest point is " << *closestPoint;
643 if (SecondPoint != NULL)
644 Log() << Verbose(0) << " and second closest is " << *SecondPoint;
645 Log() << Verbose(0) << "." << endl;
646 }
647 return closestPoint;
648};
649
650/** Returns the closest point on \a *Base with respect to \a *OtherBase.
651 * \param *out output stream for debugging
652 * \param *Base reference line
653 * \param *OtherBase other base line
654 * \return Vector on reference line that has closest distance
655 */
656Vector * GetClosestPointBetweenLine(const BoundaryLineSet * const Base, const BoundaryLineSet * const OtherBase)
657{
658 // construct the plane of the two baselines (i.e. take both their directional vectors)
659 Vector Normal;
660 Vector Baseline, OtherBaseline;
661 Baseline.CopyVector(Base->endpoints[1]->node->node);
662 Baseline.SubtractVector(Base->endpoints[0]->node->node);
663 OtherBaseline.CopyVector(OtherBase->endpoints[1]->node->node);
664 OtherBaseline.SubtractVector(OtherBase->endpoints[0]->node->node);
665 Normal.CopyVector(&Baseline);
666 Normal.VectorProduct(&OtherBaseline);
667 Normal.Normalize();
668 Log() << Verbose(4) << "First direction is " << Baseline << ", second direction is " << OtherBaseline << ", normal of intersection plane is " << Normal << "." << endl;
669
670 // project one offset point of OtherBase onto this plane (and add plane offset vector)
671 Vector NewOffset;
672 NewOffset.CopyVector(OtherBase->endpoints[0]->node->node);
673 NewOffset.SubtractVector(Base->endpoints[0]->node->node);
674 NewOffset.ProjectOntoPlane(&Normal);
675 NewOffset.AddVector(Base->endpoints[0]->node->node);
676 Vector NewDirection;
677 NewDirection.CopyVector(&NewOffset);
678 NewDirection.AddVector(&OtherBaseline);
679
680 // calculate the intersection between this projected baseline and Base
681 Vector *Intersection = new Vector;
682 Intersection->GetIntersectionOfTwoLinesOnPlane(Base->endpoints[0]->node->node, Base->endpoints[1]->node->node, &NewOffset, &NewDirection, &Normal);
683 Normal.CopyVector(Intersection);
684 Normal.SubtractVector(Base->endpoints[0]->node->node);
685 Log() << Verbose(3) << "Found closest point on " << *Base << " at " << *Intersection << ", factor in line is " << fabs(Normal.ScalarProduct(&Baseline)/Baseline.NormSquared()) << "." << endl;
686
687 return Intersection;
688};
689
690/** Returns the distance to the plane defined by \a *triangle
691 * \param *out output stream for debugging
692 * \param *x Vector to calculate distance to
693 * \param *triangle triangle defining plane
694 * \return distance between \a *x and plane defined by \a *triangle, -1 - if something went wrong
695 */
696double DistanceToTrianglePlane(const Vector *x, const BoundaryTriangleSet * const triangle)
697{
698 double distance = 0.;
699 if (x == NULL) {
700 return -1;
701 }
702 distance = x->DistanceToPlane(&triangle->NormalVector, triangle->endpoints[0]->node->node);
703 return distance;
704};
705
706/** Creates the objects in a VRML file.
707 * \param *out output stream for debugging
708 * \param *vrmlfile output stream for tecplot data
709 * \param *Tess Tesselation structure with constructed triangles
710 * \param *mol molecule structure with atom positions
711 */
712void WriteVrmlFile(ofstream * const vrmlfile, const Tesselation * const Tess, const PointCloud * const cloud)
713{
714 TesselPoint *Walker = NULL;
715 int i;
716 Vector *center = cloud->GetCenter();
717 if (vrmlfile != NULL) {
718 //Log() << Verbose(1) << "Writing Raster3D file ... ";
719 *vrmlfile << "#VRML V2.0 utf8" << endl;
720 *vrmlfile << "#Created by molecuilder" << endl;
721 *vrmlfile << "#All atoms as spheres" << endl;
722 cloud->GoToFirst();
723 while (!cloud->IsEnd()) {
724 Walker = cloud->GetPoint();
725 *vrmlfile << "Sphere {" << endl << " "; // 2 is sphere type
726 for (i=0;i<NDIM;i++)
727 *vrmlfile << Walker->node->x[i]-center->x[i] << " ";
728 *vrmlfile << "\t0.1\t1. 1. 1." << endl; // radius 0.05 and white as colour
729 cloud->GoToNext();
730 }
731
732 *vrmlfile << "# All tesselation triangles" << endl;
733 for (TriangleMap::const_iterator TriangleRunner = Tess->TrianglesOnBoundary.begin(); TriangleRunner != Tess->TrianglesOnBoundary.end(); TriangleRunner++) {
734 *vrmlfile << "1" << endl << " "; // 1 is triangle type
735 for (i=0;i<3;i++) { // print each node
736 for (int j=0;j<NDIM;j++) // and for each node all NDIM coordinates
737 *vrmlfile << TriangleRunner->second->endpoints[i]->node->node->x[j]-center->x[j] << " ";
738 *vrmlfile << "\t";
739 }
740 *vrmlfile << "1. 0. 0." << endl; // red as colour
741 *vrmlfile << "18" << endl << " 0.5 0.5 0.5" << endl; // 18 is transparency type for previous object
742 }
743 } else {
744 eLog() << Verbose(1) << "Given vrmlfile is " << vrmlfile << "." << endl;
745 }
746 delete(center);
747};
748
749/** Writes additionally the current sphere (i.e. the last triangle to file).
750 * \param *out output stream for debugging
751 * \param *rasterfile output stream for tecplot data
752 * \param *Tess Tesselation structure with constructed triangles
753 * \param *mol molecule structure with atom positions
754 */
755void IncludeSphereinRaster3D(ofstream * const rasterfile, const Tesselation * const Tess, const PointCloud * const cloud)
756{
757 Vector helper;
758
759 if (Tess->LastTriangle != NULL) {
760 // include the current position of the virtual sphere in the temporary raster3d file
761 Vector *center = cloud->GetCenter();
762 // make the circumsphere's center absolute again
763 helper.CopyVector(Tess->LastTriangle->endpoints[0]->node->node);
764 helper.AddVector(Tess->LastTriangle->endpoints[1]->node->node);
765 helper.AddVector(Tess->LastTriangle->endpoints[2]->node->node);
766 helper.Scale(1./3.);
767 helper.SubtractVector(center);
768 // and add to file plus translucency object
769 *rasterfile << "# current virtual sphere\n";
770 *rasterfile << "8\n 25.0 0.6 -1.0 -1.0 -1.0 0.2 0 0 0 0\n";
771 *rasterfile << "2\n " << helper.x[0] << " " << helper.x[1] << " " << helper.x[2] << "\t" << 5. << "\t1 0 0\n";
772 *rasterfile << "9\n terminating special property\n";
773 delete(center);
774 }
775};
776
777/** Creates the objects in a raster3d file (renderable with a header.r3d).
778 * \param *out output stream for debugging
779 * \param *rasterfile output stream for tecplot data
780 * \param *Tess Tesselation structure with constructed triangles
781 * \param *mol molecule structure with atom positions
782 */
783void WriteRaster3dFile(ofstream * const rasterfile, const Tesselation * const Tess, const PointCloud * const cloud)
784{
785 TesselPoint *Walker = NULL;
786 int i;
787 Vector *center = cloud->GetCenter();
788 if (rasterfile != NULL) {
789 //Log() << Verbose(1) << "Writing Raster3D file ... ";
790 *rasterfile << "# Raster3D object description, created by MoleCuilder" << endl;
791 *rasterfile << "@header.r3d" << endl;
792 *rasterfile << "# All atoms as spheres" << endl;
793 cloud->GoToFirst();
794 while (!cloud->IsEnd()) {
795 Walker = cloud->GetPoint();
796 *rasterfile << "2" << endl << " "; // 2 is sphere type
797 for (i=0;i<NDIM;i++)
798 *rasterfile << Walker->node->x[i]-center->x[i] << " ";
799 *rasterfile << "\t0.1\t1. 1. 1." << endl; // radius 0.05 and white as colour
800 cloud->GoToNext();
801 }
802
803 *rasterfile << "# All tesselation triangles" << endl;
804 *rasterfile << "8\n 25. -1. 1. 1. 1. 0.0 0 0 0 2\n SOLID 1.0 0.0 0.0\n BACKFACE 0.3 0.3 1.0 0 0\n";
805 for (TriangleMap::const_iterator TriangleRunner = Tess->TrianglesOnBoundary.begin(); TriangleRunner != Tess->TrianglesOnBoundary.end(); TriangleRunner++) {
806 *rasterfile << "1" << endl << " "; // 1 is triangle type
807 for (i=0;i<3;i++) { // print each node
808 for (int j=0;j<NDIM;j++) // and for each node all NDIM coordinates
809 *rasterfile << TriangleRunner->second->endpoints[i]->node->node->x[j]-center->x[j] << " ";
810 *rasterfile << "\t";
811 }
812 *rasterfile << "1. 0. 0." << endl; // red as colour
813 //*rasterfile << "18" << endl << " 0.5 0.5 0.5" << endl; // 18 is transparency type for previous object
814 }
815 *rasterfile << "9\n# terminating special property\n";
816 } else {
817 eLog() << Verbose(1) << "Given rasterfile is " << rasterfile << "." << endl;
818 }
819 IncludeSphereinRaster3D(rasterfile, Tess, cloud);
820 delete(center);
821};
822
823/** This function creates the tecplot file, displaying the tesselation of the hull.
824 * \param *out output stream for debugging
825 * \param *tecplot output stream for tecplot data
826 * \param N arbitrary number to differentiate various zones in the tecplot format
827 */
828void WriteTecplotFile(ofstream * const tecplot, const Tesselation * const TesselStruct, const PointCloud * const cloud, const int N)
829{
830 if ((tecplot != NULL) && (TesselStruct != NULL)) {
831 // write header
832 *tecplot << "TITLE = \"3D CONVEX SHELL\"" << endl;
833 *tecplot << "VARIABLES = \"X\" \"Y\" \"Z\" \"U\"" << endl;
834 *tecplot << "ZONE T=\"";
835 if (N < 0) {
836 *tecplot << cloud->GetName();
837 } else {
838 *tecplot << N << "-";
839 for (int i=0;i<3;i++)
840 *tecplot << (i==0 ? "" : "_") << TesselStruct->LastTriangle->endpoints[i]->node->Name;
841 }
842 *tecplot << "\", N=" << TesselStruct->PointsOnBoundary.size() << ", E=" << TesselStruct->TrianglesOnBoundary.size() << ", DATAPACKING=POINT, ZONETYPE=FETRIANGLE" << endl;
843 int i=0;
844 for (cloud->GoToFirst(); !cloud->IsEnd(); cloud->GoToNext(), i++);
845 int *LookupList = new int[i];
846 for (cloud->GoToFirst(), i=0; !cloud->IsEnd(); cloud->GoToNext(), i++)
847 LookupList[i] = -1;
848
849 // print atom coordinates
850 Log() << Verbose(2) << "The following triangles were created:";
851 int Counter = 1;
852 TesselPoint *Walker = NULL;
853 for (PointMap::const_iterator target = TesselStruct->PointsOnBoundary.begin(); target != TesselStruct->PointsOnBoundary.end(); target++) {
854 Walker = target->second->node;
855 LookupList[Walker->nr] = Counter++;
856 *tecplot << Walker->node->x[0] << " " << Walker->node->x[1] << " " << Walker->node->x[2] << " " << target->second->value << endl;
857 }
858 *tecplot << endl;
859 // print connectivity
860 for (TriangleMap::const_iterator runner = TesselStruct->TrianglesOnBoundary.begin(); runner != TesselStruct->TrianglesOnBoundary.end(); runner++) {
861 Log() << Verbose(0) << " " << runner->second->endpoints[0]->node->Name << "<->" << runner->second->endpoints[1]->node->Name << "<->" << runner->second->endpoints[2]->node->Name;
862 *tecplot << LookupList[runner->second->endpoints[0]->node->nr] << " " << LookupList[runner->second->endpoints[1]->node->nr] << " " << LookupList[runner->second->endpoints[2]->node->nr] << endl;
863 }
864 delete[] (LookupList);
865 Log() << Verbose(0) << endl;
866 }
867};
868
869/** Calculates the concavity for each of the BoundaryPointSet's in a Tesselation.
870 * Sets BoundaryPointSet::value equal to the number of connected lines that are not convex.
871 * \param *out output stream for debugging
872 * \param *TesselStruct pointer to Tesselation structure
873 */
874void CalculateConcavityPerBoundaryPoint(const Tesselation * const TesselStruct)
875{
876 class BoundaryPointSet *point = NULL;
877 class BoundaryLineSet *line = NULL;
878
879 //Log() << Verbose(2) << "Begin of CalculateConcavityPerBoundaryPoint" << endl;
880 // calculate remaining concavity
881 for (PointMap::const_iterator PointRunner = TesselStruct->PointsOnBoundary.begin(); PointRunner != TesselStruct->PointsOnBoundary.end(); PointRunner++) {
882 point = PointRunner->second;
883 Log() << Verbose(1) << "INFO: Current point is " << *point << "." << endl;
884 point->value = 0;
885 for (LineMap::iterator LineRunner = point->lines.begin(); LineRunner != point->lines.end(); LineRunner++) {
886 line = LineRunner->second;
887 //Log() << Verbose(2) << "INFO: Current line of point " << *point << " is " << *line << "." << endl;
888 if (!line->CheckConvexityCriterion())
889 point->value += 1;
890 }
891 }
892 //Log() << Verbose(2) << "End of CalculateConcavityPerBoundaryPoint" << endl;
893};
894
895
896/** Checks whether each BoundaryLineSet in the Tesselation has two triangles.
897 * \param *out output stream for debugging
898 * \param *TesselStruct
899 * \return true - all have exactly two triangles, false - some not, list is printed to screen
900 */
901bool CheckListOfBaselines(const Tesselation * const TesselStruct)
902{
903 LineMap::const_iterator testline;
904 bool result = false;
905 int counter = 0;
906
907 Log() << Verbose(1) << "Check: List of Baselines with not two connected triangles:" << endl;
908 for (testline = TesselStruct->LinesOnBoundary.begin(); testline != TesselStruct->LinesOnBoundary.end(); testline++) {
909 if (testline->second->triangles.size() != 2) {
910 Log() << Verbose(1) << *testline->second << "\t" << testline->second->triangles.size() << endl;
911 counter++;
912 }
913 }
914 if (counter == 0) {
915 Log() << Verbose(1) << "None." << endl;
916 result = true;
917 }
918 return result;
919}
920
Note: See TracBrowser for help on using the repository browser.