source: src/tesselationhelpers.cpp@ 3839e5

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 3839e5 was 4e10f5, checked in by Tillmann Crueger <crueger@…>, 14 years ago

Merge branch 'stable' into StructureRefactoring

Conflicts:

src/Actions/WorldAction/CenterOnEdgeAction.cpp
src/Actions/WorldAction/ChangeBoxAction.cpp
src/Actions/WorldAction/RepeatBoxAction.cpp
src/Actions/WorldAction/ScaleBoxAction.cpp
src/World.cpp
src/boundary.cpp

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