1 /*
2 * (c) 2004 Iowa State University
3 * see the LICENSE file in the top level directory
4 */
5
6 /* MoleculeData.cpp
7
8 class MoleculeData organizes all information on the molecule for each window/file
9
10 Created from other files, BMB, 4/1998
11 */
12
13 #include "Globals.h"
14 #include "GlobalExceptions.h"
15 #include "MoleculeData.h"
16 #include "Frame.h"
17 #include "Internals.h"
18 #include "BasisSet.h"
19 #include "Math3D.h"
20 #include "InputData.h"
21 #include "VirtualSphere.h"
22 #include "Progress.h"
23 #include "Prefs.h"
24 #include "myFiles.h"
25 #include <string.h>
26 #include <new>
27 #include <map>
28 #if defined(WIN32)
29 #undef AddAtom
30 #endif
31
MoleculeData(MolDisplayWin * MolWin)32 MoleculeData::MoleculeData(MolDisplayWin *MolWin) {
33 this->MolWin = MolWin;
34 RotCoords = NULL;
35 cFrame = Frames = new Frame(MolWin);
36 CurrentFrame=1;
37 NumFrames = 1;
38 IntCoords = NULL;
39 Basis = NULL;
40 Description = NULL;
41 MaxAtoms = 0;
42 MaxSize=0.5;
43 WindowSize=3.0;
44 InitRotationMatrix(TotalRotation);
45 InputOptions = NULL;
46 DrawMode = 0;
47 DrawLabels = 0;
48 constrain_anno_id = -1;
49 }
~MoleculeData(void)50 MoleculeData::~MoleculeData(void) {
51 DeleteAllAnnotations();
52 while (Frames) {
53 cFrame = Frames->GetNextFrame();
54 delete Frames;
55 Frames = cFrame;
56 }
57 if (RotCoords) delete [] RotCoords;
58 if (Description) delete [] Description;
59 if (IntCoords) delete IntCoords;
60 if (Basis) delete Basis;
61 if (InputOptions) delete InputOptions;
62 }
SetupFrameMemory(long NumAtoms,long NumBonds)63 bool MoleculeData::SetupFrameMemory(long NumAtoms, long NumBonds) {
64 if (NumAtoms < cFrame->AtomAllocation) NumAtoms = cFrame->AtomAllocation;
65 if (NumBonds < cFrame->BondAllocation) NumBonds = cFrame->BondAllocation;
66 if ((NumAtoms == cFrame->AtomAllocation)&&
67 (NumBonds == cFrame->BondAllocation)) return true;//no need to resize
68 mpAtom * tempAtoms=NULL;
69 Bond * tempBonds=NULL;
70 try {
71 tempAtoms = new mpAtom[NumAtoms];
72 tempBonds = new Bond[NumBonds];
73 }
74 catch (std::bad_alloc) {
75 if (tempAtoms) delete [] tempAtoms;
76 if (tempBonds) delete [] tempBonds;
77 return false;
78 }
79 memcpy(tempAtoms, cFrame->Atoms, cFrame->NumAtoms*sizeof(mpAtom));
80 memcpy(tempBonds, cFrame->Bonds, cFrame->NumBonds*sizeof(Bond));
81 if (cFrame->Atoms) delete [] cFrame->Atoms; //delete old arrays, if any
82 if (cFrame->Bonds) delete [] cFrame->Bonds;
83 cFrame->Atoms = tempAtoms;
84 cFrame->Bonds = tempBonds;
85 cFrame->AtomAllocation = NumAtoms;
86 cFrame->BondAllocation = NumBonds;
87 if (NumAtoms > MaxAtoms) { //Need to extend the size of the zbuffer arrays
88 if (RotCoords) delete [] RotCoords;
89 try {
90 RotCoords = new CPoint3D[NumAtoms];
91 }
92 catch (std::bad_alloc) {
93 if (RotCoords) delete [] RotCoords;
94 RotCoords = new CPoint3D[MaxAtoms];
95 return false;
96 }
97 MaxAtoms = NumAtoms;
98 }
99 return true;
100 }
101 /*Call to position a frame in the frame list according to the xcoord (time or IRC value)
102 routine returns a new empty frame ptr in the frame list*/
LocateNewFrame(float XPosition)103 Frame * MoleculeData::LocateNewFrame(float XPosition)
104 { Frame * lNewFrame;
105
106 Frame * lFrame = cFrame;
107 if (XPosition < lFrame->time) {
108 while (XPosition < lFrame->time) {
109 if (lFrame->PreviousFrame)
110 lFrame = lFrame->PreviousFrame;
111 else
112 break;
113 }
114 if (lFrame->time < XPosition) lFrame = lFrame->NextFrame;
115 if ((lFrame->time-XPosition) < 1.0E-6) return NULL; /*duplicate point*/
116 lNewFrame = new Frame(MolWin);
117 if (lNewFrame == NULL) throw MemoryError();
118 lNewFrame->PreviousFrame = lFrame->PreviousFrame;
119 lFrame->PreviousFrame = lNewFrame;
120 lNewFrame->NextFrame = lFrame;
121 if (lNewFrame->PreviousFrame) lNewFrame->PreviousFrame->NextFrame = lNewFrame;
122 } else {
123 while (XPosition > lFrame->time) {
124 if (lFrame->NextFrame)
125 lFrame = lFrame->NextFrame;
126 else
127 break;
128 }
129 if (lFrame->time > XPosition) lFrame = lFrame->PreviousFrame;
130 if ((XPosition-lFrame->time) < 1.0E-6) return NULL; /*duplicate point*/
131 lNewFrame = new Frame(MolWin);
132 if (lNewFrame == NULL) throw MemoryError();
133 lNewFrame->NextFrame = lFrame->NextFrame;
134 lFrame->NextFrame = lNewFrame;
135 lNewFrame->PreviousFrame = lFrame;
136 if (lNewFrame->NextFrame) lNewFrame->NextFrame->PreviousFrame = lNewFrame;
137 }
138 if (lNewFrame->PreviousFrame == NULL) Frames = lNewFrame;
139 cFrame = lNewFrame;
140 lNewFrame->time = XPosition;
141 lFrame = Frames;
142 for (CurrentFrame=1; lFrame!=lNewFrame; CurrentFrame++)
143 lFrame = lFrame->NextFrame;
144 NumFrames ++;
145 return lNewFrame;
146 } /*LocateNewFrame*/
147
AddFrame(long NumAtoms,long NumBonds)148 Frame * MoleculeData::AddFrame(long NumAtoms, long NumBonds) {
149 Frame * temp = new Frame(MolWin);
150 if (!temp) throw MemoryError();
151 if (cFrame->NextFrame) {
152 cFrame->NextFrame->PreviousFrame = temp;
153 temp->NextFrame = cFrame->NextFrame;
154 }
155 temp->PreviousFrame = cFrame;
156 cFrame->NextFrame = temp;
157 cFrame = temp;
158
159 if (!SetupFrameMemory(NumAtoms, NumBonds)) throw MemoryError();
160 NumFrames++;
161 CurrentFrame++;
162 return temp;
163 }
164
DeleteFrame(void)165 void MoleculeData::DeleteFrame(void) {
166 if (!cFrame->NextFrame && !cFrame->PreviousFrame) return;
167 Frame * temp = cFrame;
168 if (cFrame->NextFrame) {
169 cFrame->NextFrame->PreviousFrame = cFrame->PreviousFrame;
170 if (cFrame->PreviousFrame)
171 cFrame->PreviousFrame->NextFrame = cFrame->NextFrame;
172 cFrame = cFrame->NextFrame;
173 } else {
174 cFrame->PreviousFrame->NextFrame=NULL;
175 cFrame = cFrame->PreviousFrame;
176 CurrentFrame--;
177 }
178 if (temp == Frames) Frames = temp->NextFrame;
179 delete temp;
180 NumFrames--;
181 }
GetRotationMatrix(Matrix4D copy)182 void MoleculeData::GetRotationMatrix(Matrix4D copy) {
183 CopyMatrix(TotalRotation, copy);
184 }
185
GetRotationMatrix() const186 const Matrix4D& MoleculeData::GetRotationMatrix() const {
187 return TotalRotation;
188 }
189
190 #include <iostream>
191 //routine calculates the cartesian center of the molecule and offsets the
192 //coordinate system of the window to match the moleculer center.
CenterModelWindow(void)193 void MoleculeData::CenterModelWindow(void) {
194 float XMin, XMax, YMin, YMax, ZMin, ZMax;
195 //first set the Min and Max to big and small values
196 XMin=YMin=ZMin=1.0e10;
197 XMax=YMax=ZMax=-1.0e10;
198
199 //Start at the beginning of the frame list so that the center for the entire
200 //frame list is generated.
201 Frame * lFrame = Frames;
202 //Now find the min and max in each direction
203 while (lFrame) {
204 for (long iatom=0; iatom<lFrame->NumAtoms; iatom++) {
205 XMin = MIN(XMin, lFrame->Atoms[iatom].Position.x);
206 XMax = MAX(XMax, lFrame->Atoms[iatom].Position.x);
207 YMin = MIN(YMin, lFrame->Atoms[iatom].Position.y);
208 YMax = MAX(YMax, lFrame->Atoms[iatom].Position.y);
209 ZMin = MIN(ZMin, lFrame->Atoms[iatom].Position.z);
210 ZMax = MAX(ZMax, lFrame->Atoms[iatom].Position.z);
211 }
212 lFrame = lFrame->GetNextFrame();
213 }
214 //The center is now just half of the min plus the max
215 Centroid.x = (XMax+XMin)*0.5f;
216 Centroid.y = (YMax+YMin)*0.5f;
217 Centroid.z = (ZMax+ZMin)*0.5f;
218 //Recompute the maximum width of the molecule
219 MaxSize = MAX((XMax-XMin), (YMax-YMin));
220 MaxSize = MAX(MaxSize, (ZMax-ZMin));
221 MolCentroid = Centroid;
222
223 } /*CenterModelWindow*/
224
ResetRotation(void)225 void MoleculeData::ResetRotation(void) {
226 if ((cFrame->NumAtoms > MaxAtoms)||(RotCoords==NULL)) {
227 if (RotCoords != NULL) {
228 delete [] RotCoords;
229 RotCoords = NULL;
230 }
231 RotCoords = new CPoint3D[cFrame->NumAtoms];
232 MaxAtoms = cFrame->NumAtoms;
233 }
234 for (long i=0; i<cFrame->NumAtoms; i++) {
235 Rotate3DPt(TotalRotation, (cFrame->Atoms[i].Position), &(RotCoords[i]));
236 }
237 //Now sort the Z Buffer
238 // SortzBuffer(RotCoords, zBuffer, cFrame->NumAtoms);
239 }
InitializeInternals(void)240 void MoleculeData::InitializeInternals(void) {
241 if (!IntCoords) IntCoords = new Internals;
242 }
243 // Stick the coordinates by moving the rotated coordinates to the file coordinates
244 // being careful to invalidate any orbital info
StickCoordinates(void)245 void MoleculeData::StickCoordinates(void) {
246 //Clean up the rotation matrix before making permenant changes
247 OrthogonalizeRotationMatrix(TotalRotation);
248 if (cFrame->Orbs.size() > 0) { // rotate any MO vectors and update the surface list
249 std::vector<OrbitalRec *>::const_iterator OrbSet = cFrame->Orbs.begin();
250 while (OrbSet != cFrame->Orbs.end()) {
251 (*OrbSet)->RotateVectors(TotalRotation, Basis, cFrame->NumAtoms);
252 OrbSet++;
253 }
254 Surface * lSurface = cFrame->SurfaceList;
255 while (lSurface) {
256 lSurface->RotateSurface(TotalRotation);
257 lSurface = lSurface->GetNextSurface();
258 }
259 }
260 // rotate any frequencies
261 if (cFrame->Vibs) {
262 std::vector<CPoint3D> & Modes = cFrame->Vibs->NormMode;
263 CPoint3D NMode;
264 for (long imode=0; imode < cFrame->Vibs->NumModes; imode++) {
265 long cmode = imode*cFrame->NumAtoms;
266 for (long iatom=0; iatom<cFrame->NumAtoms; iatom++) {
267 Rotate3DOffset(TotalRotation, Modes[iatom+cmode], &NMode);
268 Modes[iatom+cmode] = NMode;
269 }
270 }
271 }
272 long i;
273 for (i=0; i<cFrame->NumAtoms; i++) {
274 Rotate3DPt(TotalRotation, cFrame->Atoms[i].Position - Centroid,
275 &(RotCoords[i]));
276 }
277 for (i=0; i<cFrame->NumAtoms; i++) {
278 cFrame->Atoms[i].Position = RotCoords[i];
279 }
280 Centroid = CPoint3D(0.0f, 0.0f, 0.0f);
281 InitRotationMatrix(TotalRotation);
282 }
283
NewAtom(const mpAtom & atom,bool updateGlobal,long index,const CPoint3D * pos)284 void MoleculeData::NewAtom(const mpAtom& atom, bool updateGlobal, long index, const CPoint3D *pos) {
285 cFrame->AddAtom(atom, index, pos);
286
287 // Adjust annotations that connect higher-numbered atoms.
288 if (index >= 0 && index < cFrame->NumAtoms - 1) {
289 std::vector<Annotation *>::iterator anno;
290 for (anno = Annotations.begin(); anno != Annotations.end(); ++anno) {
291 (*anno)->adjustIds(index - 1, 1);
292 }
293 }
294
295 if (IntCoords) {
296 MOPacInternals * mInts = IntCoords->GetMOPacStyle();
297 if (mInts) mInts->AppendAtom(this);
298 }
299 if (updateGlobal) AtomAdded();
300 }
301
NewAtom(long AtomType,const CPoint3D & AtomPosition,bool updateGlobal,long index)302 void MoleculeData::NewAtom(long AtomType, const CPoint3D & AtomPosition, bool updateGlobal, long index) {
303 cFrame->AddAtom(AtomType, AtomPosition, index);
304
305 // Adjust annotations that connect higher-numbered atoms.
306 if (index >= 0 && index < cFrame->NumAtoms - 1) {
307 std::vector<Annotation *>::iterator anno;
308 for (anno = Annotations.begin(); anno != Annotations.end(); ++anno) {
309 (*anno)->adjustIds(index - 1, 1);
310 }
311 }
312
313 if (IntCoords) {
314 MOPacInternals * mInts = IntCoords->GetMOPacStyle();
315 if (mInts) mInts->AppendAtom(this);
316 }
317 if (updateGlobal) AtomAdded();
318 }
319
AtomAdded(void)320 void MoleculeData::AtomAdded(void) {
321 if (cFrame->AtomAllocation>MaxAtoms) {
322 if (RotCoords) delete [] RotCoords;
323 RotCoords = new CPoint3D[cFrame->AtomAllocation];
324 if (!RotCoords) throw MemoryError();
325 MaxAtoms = cFrame->AtomAllocation;
326 }
327 float XMin, XMax, YMin, YMax, ZMin, ZMax;
328 //first set the Min and Max to big and small values
329 XMin=YMin=ZMin=1.0E10;
330 XMax=YMax=ZMax=-1.0E10;
331
332 for (long iatom=0; iatom<cFrame->NumAtoms; iatom++) {
333 XMin = MIN(XMin, cFrame->Atoms[iatom].Position.x);
334 XMax = MAX(XMax, cFrame->Atoms[iatom].Position.x);
335 YMin = MIN(YMin, cFrame->Atoms[iatom].Position.y);
336 YMax = MAX(YMax, cFrame->Atoms[iatom].Position.y);
337 ZMin = MIN(ZMin, cFrame->Atoms[iatom].Position.z);
338 ZMax = MAX(ZMax, cFrame->Atoms[iatom].Position.z);
339 }
340 //Recompute the maximum width of the molecule
341 MaxSize = MAX(MaxSize, (XMax-XMin));
342 MaxSize = MAX(MaxSize, (YMax-YMin));
343 MaxSize = MAX(MaxSize, (ZMax-ZMin));
344
345 while (FMOFragmentIds.size() < cFrame->GetNumAtoms())
346 FMOFragmentIds.push_back(1); //initialize the fragment id for the new atoms
347
348 ResetRotation();
349 }
SetDescription(char * NewLabel)350 void MoleculeData::SetDescription(char * NewLabel) {
351 if (Description) delete [] Description;
352 long LineLength = strlen(NewLabel);
353 Description = new char[LineLength+1];
354 if (Description) strcpy(Description, NewLabel);
355 }
InvertMode(void)356 void MoleculeData::InvertMode(void) {
357 if (!cFrame->Vibs) return;
358
359 CPoint3D * Freq = &(cFrame->Vibs->NormMode[cFrame->NumAtoms*cFrame->Vibs->CurrentMode]);
360 for (long iatm=0; iatm<cFrame->NumAtoms; iatm++) {
361 Freq[iatm] *= -1.0;
362 }
363 } /*InvertMode*/
UnitConversion(bool AtoB)364 void MoleculeData::UnitConversion(bool AtoB) {
365 float factor;
366 //Choose the correct factor
367 if (AtoB) factor = kBohr2AngConversion;
368 else factor = kAng2BohrConversion;
369
370 Frame * lFrame = Frames;
371 //Now scale the permenant set of coordinates (for all frames)
372 while (lFrame) {
373 for (long iatm=0; iatm < lFrame->NumAtoms; iatm++) {
374 lFrame->Atoms[iatm].Position *= factor;
375 }
376 lFrame = lFrame->NextFrame;
377 }
378 TotalRotation[3][0] *= factor; //scale the origin offset so things stay centered
379 TotalRotation[3][1] *= factor;
380 TotalRotation[3][2] *= factor;
381 //Finally update the window scaling sizes so things are still drawn the same
382 MaxSize *= factor;
383 WindowSize *= factor;
384 } /*UnitConversion*/
385 //Rotate the molecule by 180 degrees about either the horizontal or vertical axis
386 //Pass in 0 to flip about the horizontal axis, 1 for vertical
FlipRotation(short theItem)387 void MoleculeData::FlipRotation(short theItem) {
388 wxPoint p(0,0), q(10,10), sphereCenter(10,10);
389 long sphereRadius=10;
390 Matrix4D rotationMatrix, tempcopyMatrix;
391
392 //180 degree rotation is desired, but if the two vectors are colinear then the
393 //rotation will not have a well defined axis so use two 90 degree rotations instead
394 if (theItem == 0) { //flipHitem
395 p.y = 10;
396 } else { //theItem == 1 (flipVitem)
397 p.x = 10;
398 }
399 VirtualSphereQD3D (p, q, sphereCenter, sphereRadius, rotationMatrix, TotalRotation);
400 float *m = (float *) rotationMatrix;
401 std::cout << "m[#](# in 0,16): " << std::endl
402 << m[0] << ", " << m[1] << ", " << m[2] << ", " << m[3] << ", " <<
403 std::endl
404 << m[4] << ", " << m[5] << ", " << m[6] << ", " << m[7] << ", " <<
405 std::endl
406 << m[8] << ", " << m[9] << ", " << m[10] << ", " << m[11] << ", " <<
407 std::endl
408 << m[12] << ", " << m[13] << ", " << m[14] << ", " << m[15] << std::endl;
409
410 /* Concatenate the new rotation with the current rotation */
411 //Mulitply twice since the rotate generated is 90 degrees and we want 180
412 MultiplyMatrix (rotationMatrix, TotalRotation, tempcopyMatrix);
413 CopyMatrix (tempcopyMatrix, TotalRotation);
414 MultiplyMatrix (rotationMatrix, TotalRotation, tempcopyMatrix);
415 CopyMatrix (tempcopyMatrix, TotalRotation);
416 } /*FlipRotation*/
417 // Sets the plane of the screen to that defined by the three points provided
SetScreenPlane(CPoint3D * Points)418 bool MoleculeData::SetScreenPlane(CPoint3D *Points) {
419 CPoint3D Vector1, Vector2, Vector3;
420
421 Vector1.x = Points[1].x - Points[0].x;
422 Vector1.y = Points[1].y - Points[0].y;
423 Vector1.z = Points[1].z - Points[0].z;
424 Vector2.x = Points[2].x - Points[0].x;
425 Vector2.y = Points[2].y - Points[0].y;
426 Vector2.z = Points[2].z - Points[0].z;
427
428 float length = Vector1.Magnitude();
429 if (length <= 0.0) return false; //2 points with the same coordinates
430 Vector1.x /= length;
431 Vector1.y /= length;
432 Vector1.z /= length;
433 length = Vector2.Magnitude();
434 if (length <= 0.0) return false;
435 Vector2.x /= length;
436 Vector2.y /= length;
437 Vector2.z /= length;
438 float V1DotV2 = DotProduct3D(&Vector2, &Vector1);
439 //Make sure the vectors are not parallel or anti-parallel
440 if (fabs(V1DotV2) >= 1.0) return false;
441 Vector2.x -= V1DotV2*Vector1.x;
442 Vector2.y -= V1DotV2*Vector1.y;
443 Vector2.z -= V1DotV2*Vector1.z;
444 Normalize3D(&Vector2);
445 CrossProduct3D (&Vector1, &Vector2, &Vector3);
446 Normalize3D(&Vector3);
447
448 TotalRotation[0][0] = Vector1.x;
449 TotalRotation[1][0] = Vector1.y;
450 TotalRotation[2][0] = Vector1.z;
451 TotalRotation[0][3] = 0.0;
452 TotalRotation[0][1] = Vector2.x;
453 TotalRotation[1][1] = Vector2.y;
454 TotalRotation[2][1] = Vector2.z;
455 TotalRotation[1][3] = 0.0;
456 TotalRotation[0][2] = Vector3.x;
457 TotalRotation[1][2] = Vector3.y;
458 TotalRotation[2][2] = Vector3.z;
459 TotalRotation[2][3] = TotalRotation[3][0] = TotalRotation[3][1] = TotalRotation[3][2] = 0.0;
460 TotalRotation[3][3] = 1.0;
461 //Rotation the first Point and use for the translation
462 /* Rotate3DPt(TotalRotation, Points[0], &Vector1); */
463 /* Centroid = Vector1 * -1.0f; */
464
465 ResetRotation();
466 return true;
467 }
468 //Linear Least Squares fit: minimize the sum of squares of the differences between each
469 //frame. This will "fix" a series of frames to give a nice, smooth animation.
LinearLeastSquaresFit(Progress * lProgress)470 void MoleculeData::LinearLeastSquaresFit(Progress * lProgress) {
471 long MinFrameNum=CurrentFrame;
472
473 Frame * lFrame = Frames;
474 Frame * l2Frame = lFrame->NextFrame;
475 lProgress->ChangeText("Minimizing frame motions...");
476
477 //First run through the frame list and make sure that each frame has the same number
478 //and order for the atoms. If not this routine will not work correctly!
479 while (l2Frame) {
480 if (lFrame->NumAtoms != l2Frame->NumAtoms) goto FrameIncorrect;
481 for (long iatom=0; iatom<lFrame->NumAtoms; iatom++) {
482 if (lFrame->Atoms[iatom].Type != l2Frame->Atoms[iatom].Type) goto FrameIncorrect;
483 }
484 l2Frame = l2Frame->NextFrame;
485 }
486 //Ok set of frames so get started fitting
487 l2Frame = lFrame->NextFrame;
488 Matrix4D FitMatrix, TempRotMat;
489 float SquaresValue, NewSquareValue, RotAngle, TransAmount;
490 long iOptAtoms;
491 //Save the current frame and rotation
492 Frame * SavedFrame;
493 SavedFrame = cFrame;
494 Matrix4D SavedRotation;
495 ::CopyMatrix(TotalRotation, SavedRotation);
496 while (l2Frame) {
497 InitRotationMatrix(FitMatrix); //zero out the fit matrix to start with...
498 //Now move the first atom of the second frame to the same position as the first frame.
499 //This is done by first moving it to the origin, then putting a translation in the rotation
500 //matrix to the desired location.
501 FitMatrix[3][0] = -l2Frame->Atoms[0].Position.x;
502 FitMatrix[3][1] = -l2Frame->Atoms[0].Position.y;
503 FitMatrix[3][2] = -l2Frame->Atoms[0].Position.z;
504 for (long iatom=0; iatom<lFrame->NumAtoms; iatom++) {
505 l2Frame->Atoms[iatom].Position.x += FitMatrix[3][0];
506 l2Frame->Atoms[iatom].Position.y += FitMatrix[3][1];
507 l2Frame->Atoms[iatom].Position.z += FitMatrix[3][2];
508 }
509 FitMatrix[3][0] = lFrame->Atoms[0].Position.x;
510 FitMatrix[3][1] = lFrame->Atoms[0].Position.y;
511 FitMatrix[3][2] = lFrame->Atoms[0].Position.z;
512 bool Done;
513 for (long ipass=0; ipass<4; ipass++) {
514 if (ipass<3) {
515 RotAngle = 10.0f; TransAmount=0.1f;
516 if (ipass == 0) iOptAtoms = MIN(2, lFrame->NumAtoms);
517 else if (ipass == 1) iOptAtoms = MIN(3, lFrame->NumAtoms);
518 else iOptAtoms = lFrame->NumAtoms;
519 for (int ii=0; ii<iOptAtoms; ii++)
520 Rotate3DPt(FitMatrix, l2Frame->Atoms[ii].Position, &(RotCoords[ii]));
521 SquaresValue = CalculateSquaresValue(iOptAtoms, lFrame->Atoms, RotCoords);
522 }
523 Done = false;
524 while (!Done) {
525 Done = true;
526 bool RotDone = false;
527 for (long jpass=0; jpass<2; jpass++) {
528 while (!RotDone) {
529 RotDone = true;
530 ::CopyMatrix (FitMatrix, TempRotMat);
531 for (long ii=0; ii<3; ii++) {
532 ApplyRotation(TempRotMat, ii, RotAngle);
533 for (int i=0; i<iOptAtoms; i++)
534 Rotate3DPt(TempRotMat, l2Frame->Atoms[i].Position, &(RotCoords[i]));
535 NewSquareValue = CalculateSquaresValue(iOptAtoms, lFrame->Atoms, RotCoords);
536 if (NewSquareValue<SquaresValue) {
537 RotDone = Done = false;
538 SquaresValue = NewSquareValue;
539 CopyMatrix (TempRotMat, FitMatrix);
540 } else {
541 ApplyRotation(TempRotMat, ii, -2.0f*RotAngle);
542 for (int i=0; i<iOptAtoms; i++)
543 Rotate3DPt(TempRotMat, l2Frame->Atoms[i].Position, &(RotCoords[i]));
544 NewSquareValue = CalculateSquaresValue(iOptAtoms, lFrame->Atoms, RotCoords);
545 if (NewSquareValue<SquaresValue) {
546 RotDone = Done = false;
547 SquaresValue = NewSquareValue;
548 CopyMatrix (TempRotMat, FitMatrix);
549 } else {
550 CopyMatrix (FitMatrix, TempRotMat);
551 }
552 }
553 }
554 //"clean up" the rotation matrix make the rotation part orthogonal and magnitude 1
555 OrthogonalizeRotationMatrix (FitMatrix);
556 }
557 RotAngle *= 0.1;
558 }
559 if (ipass > 2) { //Only rotate for the first two passes
560 bool TransDone = false;
561 while (!TransDone) {
562 TransDone = true;
563 for (long ii=0; ii<3; ii++) {
564 FitMatrix[3][ii] += TransAmount;
565 for (int i=0; i<iOptAtoms; i++)
566 Rotate3DPt(FitMatrix, l2Frame->Atoms[i].Position, &(RotCoords[i]));
567 NewSquareValue = CalculateSquaresValue(iOptAtoms, lFrame->Atoms, RotCoords);
568 if (NewSquareValue<SquaresValue) {
569 TransDone = Done = false;
570 SquaresValue = NewSquareValue;
571 } else {
572 FitMatrix[3][ii] -= 2.0*TransAmount;
573 for (int i=0; i<iOptAtoms; i++)
574 Rotate3DPt(FitMatrix, l2Frame->Atoms[i].Position, &(RotCoords[i]));
575 NewSquareValue = CalculateSquaresValue(iOptAtoms, lFrame->Atoms, RotCoords);
576 if (NewSquareValue<SquaresValue) {
577 TransDone = Done = false;
578 SquaresValue = NewSquareValue;
579 } else {
580 FitMatrix[3][ii] += TransAmount;
581 }
582 }
583 }
584 if (!lProgress->UpdateProgress(MinFrameNum/(NumFrames-CurrentFrame)))
585 goto CleanUpAndExit;
586 }
587 }
588 }
589 TransAmount *= 0.1;
590 }
591 //Done with this set of frames. Call StickCoordinates to copy over the coords, and
592 //update any MO vectors, surfaces, and normal mode information.
593 cFrame = l2Frame;
594 ::CopyMatrix(FitMatrix, TotalRotation);
595 StickCoordinates();
596
597 lFrame = l2Frame;
598 l2Frame = l2Frame->NextFrame;
599 MinFrameNum++;
600 }
601 CleanUpAndExit: //Clean up the RotCoords Array...
602 cFrame = SavedFrame;
603 ::CopyMatrix(SavedRotation, TotalRotation);
604 ResetRotation();
605 return; //Correct return
606 FrameIncorrect: //Incorrect frame list for this routine: throw up an error and exit
607 MessageAlert("Sorry each frame must have the same number and order of atoms in order to fit an animation path.");
608 } /*LinearLeastSquaresFit*/
OrbSurfacePossible(void) const609 bool MoleculeData::OrbSurfacePossible(void) const {
610 bool result = false;
611 if (Basis) result = true;
612 return result;
613 }
TotalDensityPossible(void) const614 bool MoleculeData::TotalDensityPossible(void) const {
615 bool result = false;
616 if (Basis) {
617 if (cFrame->Orbs.size() > 0) {//Currently TE density is based on eigenvectors only
618 std::vector<OrbitalRec *>::const_iterator OrbSet = cFrame->Orbs.begin();
619 while (OrbSet != cFrame->Orbs.end()) {
620 if ((*OrbSet)->TotalDensityPossible()) { //Is this a good enough test?
621 result = true;
622 break;
623 }
624 OrbSet++;
625 }
626 }
627 }
628 return result;
629 }
GradientVectorAvailable(void) const630 bool MoleculeData::GradientVectorAvailable(void) const {return cFrame->GradientVectorAvailable();};
MEPCalculationPossible(void) const631 bool MoleculeData::MEPCalculationPossible(void) const {
632 bool result = false;
633 if (Basis) {
634 result = Basis->AreNuclearChargesValid();
635 if (result) result = TotalDensityPossible();
636 }
637 return result;
638 }
639 //Read in a general basis set from a GAMESS log file
ParseGAMESSBasisSet(BufferFile * Buffer)640 void MoleculeData::ParseGAMESSBasisSet(BufferFile * Buffer) {
641
642 Basis = BasisSet::ParseGAMESSBasisSet(Buffer, cFrame->NumAtoms, cFrame->Atoms);
643 if (Basis) { //Setup the default nuclear charge array. This will be changed if
644 //ECP's are used.
645 long iatom;
646 for (iatom=0; iatom<cFrame->NumAtoms; iatom++) {
647 Basis->NuclearCharge[iatom] = cFrame->Atoms[iatom].GetNuclearCharge();
648 if (Basis->NuclearCharge[iatom] == 115) //dummy atom/no charge
649 Basis->NuclearCharge[iatom] = 0;
650 }
651 Basis->NuclearChargesAreValid(true);
652 }
653 } /*ParseGAMESSBasisSet*/
ParseFMOIds(BufferFile * Buffer,const long & AtomCount,const wxFileOffset & EndOfGroup)654 bool MoleculeData::ParseFMOIds(BufferFile * Buffer, const long & AtomCount, const wxFileOffset & EndOfGroup) {
655 if (Buffer->LocateKeyWord("INDAT", 5, EndOfGroup)) {
656 Buffer->LocateKeyWord("=", 1, EndOfGroup);
657 Buffer->BufferSkip(1);
658
659 //Prep the fragment mapping array so we can detect
660 if (FMOFragmentIds.size() > 0) {
661 //hmmm this array is already setup? must be an error?? Consider this as non-fatal
662 wxLogMessage(_("Starting to parse INDAT array, but already have existing data!"));
663 }
664 FMOFragmentIds.reserve(AtomCount);
665 for (long i=0; i<AtomCount; i++) FMOFragmentIds.push_back(0);
666
667 // read in the ids here
668 //This array comes in two forms, but either way it specifes how atoms are divided into fragments
669 long tempL;
670 if (!Buffer->ReadLongValue(tempL, EndOfGroup)) {
671 wxLogMessage(_("Error: Unable to locate the first integer for the INDAT array. Skipping Fragment Ids."));
672 return false;
673 }
674
675 if (tempL == 0) { //Style b, atoms grouped by fragment
676 InputOptions->FMO.UseFragmentINDAT(true);
677 for (long ifragment=0; ifragment<InputOptions->FMO.GetNumberFragments(); ifragment++) {
678 long lastatom=0;
679 do {
680 if (!Buffer->ReadLongValue(tempL, EndOfGroup)) {
681 wxString msg;
682 msg.Printf(_("Error: Unable to parse atom Id for fragment %d. Skipping the remainder of the array."),
683 ifragment+1);
684 wxLogMessage(msg);
685 return false;
686 }
687 if ((tempL > 0)&&(tempL<=AtomCount)) {
688 FMOFragmentIds[tempL-1] = ifragment+1;
689 lastatom = tempL;
690 } else if (tempL<0) {
691 if (lastatom==0) {
692 wxString msg;
693 msg.Printf(_("Error: Encountered %d not preceeded by a valid atom id. Skipping rest of INDAT."),
694 tempL);
695 wxLogMessage(msg);
696 return false;
697 }
698 tempL *= -1;
699 if ((tempL <= (lastatom))||(tempL > AtomCount)) {
700 wxString msg;
701 msg.Printf(_("Error: Encountered %d preceeded by %d. Skipping invalid data."),
702 -1*tempL, lastatom);
703 wxLogMessage(msg);
704 } else {
705 for (long jatom=lastatom; jatom < tempL; jatom++) {
706 FMOFragmentIds[jatom] = ifragment+1;
707 }
708 }
709 }
710 } while (tempL > 0);
711 }
712 } else { //Style a, array of fragment ids, one per atom
713 //read in each id, test it, and store it
714 InputOptions->FMO.UseFragmentINDAT(false);
715
716 for (long iatm=0; iatm<AtomCount; iatm++) {
717 if (iatm>0) { //skip the 1st pass since we already have it from above
718 if (!Buffer->ReadLongValue(tempL, EndOfGroup)) {
719 wxString msg;
720 msg.Printf(_("Error: Unable to parse fragment Id for atom %d. Skipping the remainder of the array."),
721 iatm+1);
722 wxLogMessage(msg);
723 return false;
724 }
725 }
726 if ((tempL < 1)||(tempL > InputOptions->FMO.GetNumberFragments())) {
727 wxString msg;
728 msg.Printf(_("Warning: Invalid fragment id assigned to atom %d, value %d."), iatm+1, tempL);
729 wxLogMessage(msg);
730 } else
731 FMOFragmentIds[iatm] = tempL;
732 }
733 }
734
735 // Now check the array to see if all atoms were assigned to a fragment
736 for (int i=0; i<FMOFragmentIds.size(); i++) {
737 if (FMOFragmentIds[i] < 1) {
738 wxString msg;
739 msg.Printf(_("Warning: Atom %d is not assigned to a fragment."), i);
740 wxLogMessage(msg);
741 }
742 }
743 }
744 return true;
745 }
746 //Parse a gaussian style zmatrix as would be formated in GAMESS input
ParseZMatrix(BufferFile * Buffer,const long & nAtoms,WinPrefs * Prefs)747 void MoleculeData::ParseZMatrix(BufferFile * Buffer, const long & nAtoms, WinPrefs * Prefs) {
748 if (!IntCoords) {
749 IntCoords = new Internals;
750 }
751 MOPacInternals * mInts = IntCoords->GetMOPacStyle();
752 if (!mInts) {
753 IntCoords->CreateMOPacInternals(3*nAtoms);
754 mInts = IntCoords->GetMOPacStyle();
755 }
756 float unitConversion = 1.0;
757 if (InputOptions && InputOptions->Data->GetUnits()) unitConversion = kBohr2AngConversion;
758 int iline=0;
759 long StartPos = Buffer->GetFilePos();
760 Buffer->LocateKeyWord("$END", 4);
761 long EndPos = Buffer->GetFilePos();
762 Buffer->SetFilePos(StartPos);
763 //First jump past the atoms and build a map of any string=value pairs (if any)
764 std::map<std::string, double> valueMap;
765 long bPos = Buffer->FindBlankLine();
766 if ((bPos > 0)&&(bPos < EndPos)) {
767 Buffer->SetFilePos(bPos);
768 Buffer->SkipnLines(1);
769 while (Buffer->GetFilePos() < EndPos) {
770 char token[kMaxLineLength], Line[kMaxLineLength];
771 double value;
772 Buffer->GetLine(Line);
773 char * last=NULL;
774 int foo=0; //Since VS lacks strtok_r we'll make do with this
775 while ((Line[foo] != '\0')&&(foo < kMaxLineLength)) {
776 if (Line[foo] == '=') {
777 Line[foo]='\0';
778 last = &(Line[foo+1]);
779 break;
780 }
781 foo++;
782 }
783 // if (strtok_r(Line, "=", &last) == NULL) break;
784 if (last == NULL) break;
785 int readCount = sscanf(Line, "%s", token);
786 readCount += sscanf(last, "%lf", &value);
787 if (readCount!=2) break;
788 std::pair<std::string, double> myVal(token, value);
789 std::pair<std::map<std::string, double>::iterator, bool> p = valueMap.insert(myVal);
790 if (! p.second) { //We have hit a duplicate value
791 MessageAlert("Duplicate keys detected in the value list");
792 }
793 }
794 Buffer->SetFilePos(StartPos);
795 }
796
797 while ((Buffer->GetFilePos() < EndPos)&&(iline<nAtoms)) {
798 CPoint3D pos = CPoint3D(0.0f, 0.0f, 0.0f); //This is just a placeholder
799 char token[kMaxLineLength], Line[kMaxLineLength], bondText[kMaxLineLength],
800 angleText[kMaxLineLength], dihedralText[kMaxLineLength];
801 float bondLength, bondAngle, bondDihedral;
802 long AtomType;
803 int con1, con2, con3;
804 Buffer->GetLine(Line);
805 int readCount = sscanf(Line, "%255s %d %255s %d %255s %d %255s", token, &con1, bondText, &con2, angleText, &con3,
806 dihedralText);
807 if (readCount < 1) break; //failed to parse anything??
808 AtomType = SetAtomType((unsigned char *) token);
809 if (AtomType < 0) break;
810 cFrame->AddAtom(AtomType, pos);
811 if (iline > 0) {
812 if (readCount < 2) break;
813 if (sscanf(bondText, "%f", &bondLength) != 1) {
814 //attempt to lookup the value in the map
815 char * s = bondText;
816 double flip = 1.0;
817 if (bondText[0] == '-') {
818 s = &(bondText[1]);
819 flip = -1.0;
820 }
821 std::map<std::string, double>::iterator p = valueMap.find(s);
822 if (p != valueMap.end()) {
823 bondLength = flip * (*p).second;
824 } else break; //missing key value
825 }
826 if (iline >= 2) {
827 if (readCount < 4) break;
828 if (sscanf(angleText, "%f", &bondAngle) != 1) {
829 //attempt to lookup the value in the map
830 char * s = angleText;
831 double flip = 1.0;
832 if (angleText[0] == '-') {
833 s = &(angleText[1]);
834 flip = -1.0;
835 }
836 std::map<std::string, double>::iterator p = valueMap.find(s);
837 if (p != valueMap.end()) {
838 bondAngle = flip * (*p).second;
839 } else break; //missing key value
840 }
841 if (iline >= 3) {
842 if (readCount < 6) break;
843 if (sscanf(dihedralText, "%f", &bondDihedral) != 1) {
844 //attempt to lookup the value in the map
845 char * s = dihedralText;
846 double flip = 1.0;
847 if (dihedralText[0] == '-') {
848 s = &(dihedralText[1]);
849 flip = -1.0;
850 }
851 std::map<std::string, double>::iterator p = valueMap.find(s);
852 if (p != valueMap.end()) {
853 bondDihedral = flip * (*p).second;
854 } else break; //missing key value
855 }
856 }
857 }
858 if (bondLength < 0.0) break;
859 con1--;
860 con2--;
861 con3--;
862 if (con1 >= iline) break;
863 mInts->AddInternalCoordinate(iline, con1, 0, bondLength*unitConversion);
864 if (iline > 1) {
865 if (con2 >= iline) break;
866 mInts->AddInternalCoordinate(iline, con2, 1, bondAngle);
867 if (iline > 2) {
868 if (con3 >= iline) break;
869 mInts->AddInternalCoordinate(iline, con3, 2, bondDihedral);
870 }
871 }
872 }
873 iline++;
874 }
875 //if we punted after the AddAtom call delete off the atom without internal coordinate information
876 if (iline > cFrame->NumAtoms) cFrame->DeleteAtom(iline-1);
877 //Now convert the set of internals into cartesians
878 mInts->InternalsToCartesians(this, Prefs, 0);
879 }
ParseGAMESSUKZMatrix(BufferFile * Buffer,WinPrefs * Prefs)880 void MoleculeData::ParseGAMESSUKZMatrix(BufferFile * Buffer, WinPrefs * Prefs) {
881 //In order to support concatinated sets of coords we will use a local set of internals if a preexisting
882 //set is present
883 //First determine the potential number of atoms
884 char Line[kMaxLineLength];
885 Buffer->GetLine(Line);
886 if (FindKeyWord(Line, "ZMAT", 4) < 0) return; //Not positioned at the start of the zmatrix block
887 float unitConversion = kBohr2AngConversion; //The default units are AU
888 if (FindKeyWord(Line, "ANGS", 4) > 0) unitConversion = 1.0;
889 long BlockStart = Buffer->GetFilePos();
890 if (! Buffer->LocateKeyWord("END", 3, -1)) return; //end of block marker not found
891 long EndPos = Buffer->GetFilePos();
892 Buffer->SetFilePos(BlockStart);
893 long LastAtomPos = EndPos;
894 if (Buffer->LocateKeyWord("VARIABLES", 9, EndPos)) {
895 LastAtomPos = Buffer->GetFilePos();
896 Buffer->SetFilePos(BlockStart);
897 }
898 if (Buffer->LocateKeyWord("CONSTANTS", 9, EndPos)) {
899 if (Buffer->GetFilePos() < LastAtomPos)
900 LastAtomPos = Buffer->GetFilePos();
901 Buffer->SetFilePos(BlockStart);
902 }
903 long atmCount = Buffer->GetNumLines(LastAtomPos);
904
905 bool internalsAreLocal=false;
906 if (!IntCoords) {
907 IntCoords = new Internals;
908 }
909 MOPacInternals * mInts = IntCoords->GetMOPacStyle();
910 if (!mInts) {
911 IntCoords->CreateMOPacInternals(3*atmCount);
912 mInts = IntCoords->GetMOPacStyle();
913 } else {
914 internalsAreLocal = true;
915 mInts = new MOPacInternals(3*atmCount);
916 }
917 //First jump past the atoms and build a map of any string=value pairs (if any)
918 std::map<std::string, double> valueMap;
919 if (Buffer->LocateKeyWord("VARIABLES", 9, EndPos)) {
920 Buffer->SkipnLines(1);
921 while (Buffer->GetFilePos() < EndPos) {
922 char token[kMaxLineLength];
923 double value;
924 Buffer->GetLine(Line);
925 int readCount = sscanf(Line, "%s %lf", token, &value);
926 if (readCount!=2) break;
927 std::pair<std::string, double> myVal(token, value);
928 /*std::pair<std::map<std::string, double>::iterator, bool> p = */ valueMap.insert(myVal);
929 if (! myVal.second) { //We have hit a duplicate value
930 MessageAlert("Duplicate keys detected in the value list");
931 }
932 }
933 Buffer->SetFilePos(BlockStart);
934 }
935 if (Buffer->LocateKeyWord("CONSTANTS", 9, EndPos)) {
936 Buffer->SkipnLines(1);
937 while (Buffer->GetFilePos() < EndPos) {
938 char token[kMaxLineLength];
939 double value;
940 Buffer->GetLine(Line);
941 int readCount = sscanf(Line, "%s %lf", token, &value);
942 if (readCount!=2) break;
943 std::pair<std::string, double> myVal(token, value);
944 /*std::pair<std::map<std::string, double>::iterator, bool> p = */ valueMap.insert(myVal);
945 if (! myVal.second) { //We have hit a duplicate value
946 MessageAlert("Duplicate keys detected in the value list");
947 }
948 }
949 Buffer->SetFilePos(BlockStart);
950 }
951
952 int iline=0;
953 while ((Buffer->GetFilePos() < LastAtomPos)&&(iline<atmCount)) {
954 CPoint3D pos = CPoint3D(0.0f, 0.0f, 0.0f); //This is just a placeholder
955 char token[kMaxLineLength], bondText[kMaxLineLength],
956 angleText[kMaxLineLength], dihedralText[kMaxLineLength];
957 float bondLength, bondAngle, bondDihedral;
958 long AtomType;
959 int con1, con2, con3;
960 Buffer->GetLine(Line);
961 int readCount = sscanf(Line, "%255s %d %255s %d %255s %d %255s", token, &con1, bondText, &con2, angleText, &con3,
962 dihedralText);
963 if (readCount < 1) break; //failed to parse anything??
964 AtomType = SetAtomType((unsigned char *) token);
965 if (AtomType < 0) break;
966 cFrame->AddAtom(AtomType, pos);
967 if (iline > 0) {
968 if (readCount < 2) break;
969 if (sscanf(bondText, "%f", &bondLength) != 1) {
970 //attempt to lookup the value in the map
971 char * s = bondText;
972 double flip = 1.0;
973 if (bondText[0] == '-') {
974 s = &(bondText[1]);
975 flip = -1.0;
976 }
977 std::map<std::string, double>::iterator p = valueMap.find(s);
978 if (p != valueMap.end()) {
979 bondLength = flip * (*p).second;
980 } else break; //missing key value
981 }
982 if (iline >= 2) {
983 if (readCount < 4) break;
984 if (sscanf(angleText, "%f", &bondAngle) != 1) {
985 //attempt to lookup the value in the map
986 char * s = angleText;
987 double flip = 1.0;
988 if (angleText[0] == '-') {
989 s = &(angleText[1]);
990 flip = -1.0;
991 }
992 std::map<std::string, double>::iterator p = valueMap.find(s);
993 if (p != valueMap.end()) {
994 bondAngle = flip * (*p).second;
995 } else break; //missing key value
996 }
997 if (iline >= 3) {
998 if (readCount < 6) break;
999 if (sscanf(dihedralText, "%f", &bondDihedral) != 1) {
1000 //attempt to lookup the value in the map
1001 char * s = dihedralText;
1002 double flip = 1.0;
1003 if (dihedralText[0] == '-') {
1004 s = &(dihedralText[1]);
1005 flip = -1.0;
1006 }
1007 std::map<std::string, double>::iterator p = valueMap.find(s);
1008 if (p != valueMap.end()) {
1009 bondDihedral = flip * (*p).second;
1010 } else break; //missing key value
1011 }
1012 }
1013 }
1014 if (bondLength < 0.0) break;
1015 con1--;
1016 con2--;
1017 con3--;
1018 if (con1 >= iline) break;
1019 mInts->AddInternalCoordinate(iline, con1, 0, bondLength);
1020 if (iline > 1) {
1021 if (con2 >= iline) break;
1022 mInts->AddInternalCoordinate(iline, con2, 1, bondAngle);
1023 if (iline > 2) {
1024 if (con3 >= iline) break;
1025 mInts->AddInternalCoordinate(iline, con3, 2, bondDihedral);
1026 }
1027 }
1028 }
1029 iline++;
1030 }
1031 //if we punted after the AddAtom call delete off the atom without internal coordinate information
1032 if (iline > cFrame->NumAtoms) cFrame->DeleteAtom(iline-1);
1033 //Now convert the set of internals into cartesians
1034 mInts->InternalsToCartesians(this, Prefs, 0);
1035 if (internalsAreLocal) delete mInts;
1036 Buffer->SetFilePos(EndPos);
1037 Buffer->SkipnLines(1);
1038 }
ParseMOPACZMatrix(BufferFile * Buffer,const long & nAtoms,WinPrefs * Prefs)1039 void MoleculeData::ParseMOPACZMatrix(BufferFile * Buffer, const long & nAtoms, WinPrefs * Prefs) {
1040 if (!IntCoords) {
1041 IntCoords = new Internals;
1042 }
1043 MOPacInternals * mInts = IntCoords->GetMOPacStyle();
1044 if (!mInts) {
1045 IntCoords->CreateMOPacInternals(3*nAtoms);
1046 mInts = IntCoords->GetMOPacStyle();
1047 }
1048 float unitConversion = 1.0;
1049 if (InputOptions && InputOptions->Data->GetUnits()) unitConversion = kBohr2AngConversion;
1050 int iline=0;
1051 long EndPos = Buffer->GetFileSize();
1052 while ((Buffer->GetFilePos() < EndPos)&&(iline<nAtoms)) {
1053 CPoint3D pos = CPoint3D(0.0f, 0.0f, 0.0f); //This is just a placeholder
1054 char token[kMaxLineLength], Line[kMaxLineLength];
1055 float bondLength, bondAngle, bondDihedral;
1056 long AtomType;
1057 int j1, j2, j3, con1, con2, con3;
1058 Buffer->GetLine(Line);
1059 int readCount = sscanf(Line, "%s %f %d %f %d %f %d %d %d %d", token, &bondLength, &j1, &bondAngle, &j2,
1060 &bondDihedral, &j3, &con1, &con2, &con3);
1061 if (readCount < 1) break; //failed to parse anything??
1062 AtomType = SetAtomType((unsigned char *) token);
1063 cFrame->AddAtom(AtomType, pos);
1064 if (iline > 0) {
1065 if (readCount < 2) break;
1066 if (iline == 1) { //the second atom will specify only the bond length
1067 con1 = 1;
1068 } else {
1069 if (iline == 2) { //For the third atom the connectivity is optional
1070 if ((readCount >= 5)&&(readCount <=7)) {
1071 con1 = 2;
1072 con2 = 1; //The default allows the connections to be assumed
1073 if (readCount >= 6) {
1074 con1 = (int) bondDihedral;
1075 con2 = j3;
1076 }
1077 } else break; //invalid line
1078 }
1079 }
1080 if (bondLength < 0.0) break;
1081 con1--;
1082 con2--;
1083 con3--;
1084 if (con1 >= iline) break;
1085 mInts->AddInternalCoordinate(iline, con1, 0, bondLength*unitConversion);
1086 if (iline > 1) {
1087 mInts->AddInternalCoordinate(iline, con2, 1, bondAngle);
1088 if (iline > 2)
1089 mInts->AddInternalCoordinate(iline, con3, 2, bondDihedral);
1090 }
1091 }
1092 iline++;
1093 }
1094 //if we punted after the AddAtom call delete off the atom without internal coordinate information
1095 if (iline > cFrame->NumAtoms) cFrame->DeleteAtom(iline-1);
1096 //Now convert the set of internals into cartesians
1097 mInts->InternalsToCartesians(this, Prefs, 0);
1098 }
GetModelCenter(CPoint3D * center)1099 void MoleculeData::GetModelCenter(CPoint3D * center) {
1100 *center = Centroid;
1101 }
SetModelCenter(CPoint3D * center)1102 void MoleculeData::SetModelCenter(CPoint3D * center) {
1103 Centroid = *center;
1104 }
GetModelRotation(float * Psi,float * Phi,float * Theta)1105 void MoleculeData::GetModelRotation(float * Psi, float * Phi, float * Theta) {
1106 MatrixToEulerAngles(TotalRotation, Psi, Phi, Theta);
1107 }
SetModelRotation(float Psi,float Phi,float Theta)1108 void MoleculeData::SetModelRotation(float Psi, float Phi, float Theta) {
1109 CPoint3D Center;
1110 GetModelCenter(&Center); //Retrieve off the old center
1111 EulerAnglesToMatrix(TotalRotation, Psi, Phi, Theta);
1112 SetModelCenter(&Center);
1113 }
RotateToPrincipleOrientation(WinPrefs * Prefs,double precision)1114 void MoleculeData::RotateToPrincipleOrientation(WinPrefs * Prefs, double precision) {
1115 if (!DeterminePrincipleOrientation(TotalRotation, Centroid, Prefs, precision)) {
1116 MessageAlert("Unable to determine the proper symmetry adapted rotation. This"
1117 " may mean your selected point group is incorrect.");
1118 }
1119 }
DeterminePointGroup(bool * pgFlags,WinPrefs * Prefs,double precision)1120 void MoleculeData::DeterminePointGroup(bool * pgFlags, WinPrefs * Prefs, double precision) {
1121 pgFlags[0] = true;
1122 for (int i=0; i<kNumSymmetryPointGroups; i++) pgFlags[i] = false;
1123 //order of the flags
1124 //c1, cs, ci, c2h-c8h (3-9), c2v-c8v (10-16), c2-c8 (17-23), s2-s8 (24-26),
1125 //D2d-d8d (27-33), d2h-d8h (34-40), d2-d8 (41-47), Td, Th, T, Oh, O
1126 int PrincAxisOrder = 1;
1127 Matrix4D temp;
1128 CPoint3D ptmp;
1129
1130 GAMESSPointGroup savedpg = GAMESS_C1;
1131 long savedpgOrder = 1;
1132 if (InputOptions) {
1133 savedpg = InputOptions->Data->GetPointGroup();
1134 savedpgOrder = InputOptions->Data->GetPointGroupOrder();
1135 } else {
1136 InputOptions = new InputData;
1137 }
1138 //First determine the rotation axis orders and store in Cn
1139 for (int i=2; i<=8; i++) {
1140 InputOptions->Data->SetPointGroup(GAMESS_CN);
1141 InputOptions->Data->SetPointGroupOrder(i);
1142 if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1143 pgFlags[17 - 2 + i] = true;
1144 PrincAxisOrder = i;
1145 InputOptions->Data->SetPointGroup(GAMESS_CNH);
1146 if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1147 pgFlags[3 - 2 + i] = true;
1148 }
1149 InputOptions->Data->SetPointGroup(GAMESS_CNV);
1150 if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1151 pgFlags[10 - 2 + i] = true;
1152 }
1153 InputOptions->Data->SetPointGroup(GAMESS_DN);
1154 if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1155 pgFlags[41 - 2 + i] = true;
1156 InputOptions->Data->SetPointGroup(GAMESS_DND);
1157 if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1158 pgFlags[27 - 2 + i] = true;
1159 }
1160 InputOptions->Data->SetPointGroup(GAMESS_DNH);
1161 if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1162 pgFlags[34 - 2 + i] = true;
1163 }
1164 }
1165 if (i < 5) {
1166 InputOptions->Data->SetPointGroup(GAMESS_S2N);
1167 InputOptions->Data->SetPointGroupOrder(i-1);
1168 if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1169 pgFlags[24 - 2 + i] = true;
1170 }
1171 }
1172 }
1173 }
1174 if (PrincAxisOrder > 1) {
1175 InputOptions->Data->SetPointGroup(GAMESS_T);
1176 if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1177 pgFlags[50] = true;
1178 InputOptions->Data->SetPointGroup(GAMESS_O);
1179 if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1180 pgFlags[52] = true;
1181 InputOptions->Data->SetPointGroup(GAMESS_OH);
1182 if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1183 pgFlags[51] = true;
1184 }
1185 }
1186 InputOptions->Data->SetPointGroup(GAMESS_TD);
1187 if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1188 pgFlags[48] = true;
1189 }
1190 InputOptions->Data->SetPointGroup(GAMESS_TH);
1191 if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1192 pgFlags[49] = true;
1193 }
1194 }
1195 }
1196 InputOptions->Data->SetPointGroup(GAMESS_CS);
1197 if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1198 pgFlags[1] = true;
1199 }
1200 InputOptions->Data->SetPointGroup(GAMESS_CI);
1201 if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1202 pgFlags[2] = true;
1203 }
1204 //restore the entry point group
1205 InputOptions->Data->SetPointGroup(savedpg);
1206 InputOptions->Data->SetPointGroupOrder(savedpgOrder);
1207 }
DeterminePrincipleOrientation(Matrix4D result,CPoint3D & translation,WinPrefs * Prefs,double precision) const1208 bool MoleculeData::DeterminePrincipleOrientation(Matrix4D result,
1209 CPoint3D& translation,
1210 WinPrefs * Prefs,
1211 double precision) const {
1212 //Setup the rotation matrix to present the molecule in the priciple orientation
1213
1214 InitRotationMatrix(result);
1215 //First compute and offset to the center of mass
1216 CPoint3D centerOfMass;
1217 CalculateCenterOfMass(cFrame->Atoms, cFrame->GetNumAtoms(),
1218 Prefs->GetAtomMassLoc(), ¢erOfMass);
1219 translation = centerOfMass;
1220 centerOfMass *= -1.0;
1221 CPoint3D rotatedCenterOfMass = centerOfMass;
1222
1223 //Compute the moment of interia tensor
1224 double xx=0.0, xy=0.0, yy=0.0, xz=0.0, yz=0.0, zz=0.0;
1225 for (int i=0; i<cFrame->GetNumAtoms(); i++) {
1226 double atmMass = Prefs->GetAtomMass(cFrame->Atoms[i].GetType()-1);
1227 double xc = cFrame->Atoms[i].Position.x + centerOfMass.x;
1228 double yc = cFrame->Atoms[i].Position.y + centerOfMass.y;
1229 double zc = cFrame->Atoms[i].Position.z + centerOfMass.z;
1230 xx += atmMass*yc*yc + atmMass*zc*zc;
1231 yy += atmMass*xc*xc + atmMass*zc*zc;
1232 zz += atmMass*xc*xc + atmMass*yc*yc;
1233 xy -= atmMass*xc*yc;
1234 xz -= atmMass*xc*zc;
1235 yz -= atmMass*yc*zc;
1236 }
1237 if ((fabs(xy)>1.0e-8)||(fabs(xz)>1.0e-8)||(fabs(yz)>1.0e-8)) {
1238 //Diagonalize the moment of interia tensor to yield the
1239 //rotation into the principle axis.
1240 double tri[6];
1241 tri[0] = xx;
1242 tri[1] = xy;
1243 tri[2] = yy;
1244 tri[3] = xz;
1245 tri[4] = yz;
1246 tri[5] = zz;
1247 double rot[9], eig[3];
1248 SymmetricJacobiDiagonalization(tri, rot, eig, 3, 3);
1249 for (int ii=0; ii<3; ii++) {
1250 for (int j=0; j<3; j++) {
1251 result[ii][j] = rot[3*ii + j];
1252 }
1253 }
1254 float det = DeterminantMatrix(result);
1255 //The determinate should be positive, multiple by -1 if not
1256 //I think this is to ensure the rotation is right-handed
1257 if (det < 0.0) {
1258 for (int ii=0; ii<3; ii++) {
1259 result[ii][0] *= -1.0;
1260 }
1261 }
1262 Rotate3DOffset(result,centerOfMass,&rotatedCenterOfMass);
1263 }
1264
1265 for (long i=0; i<cFrame->GetNumAtoms(); i++) {
1266 CPoint3D temp = (cFrame->Atoms[i].Position) + centerOfMass;
1267 Rotate3DPt(result, temp, &(RotCoords[i]));
1268 }
1269 //generate the moments of inertia
1270 xx = yy = zz = xy = xz = yz = 0.0;
1271 for (int i=0; i<cFrame->GetNumAtoms(); i++) {
1272 double atmMass = Prefs->GetAtomMass(cFrame->Atoms[i].GetType()-1);
1273 double xc = RotCoords[i].x;
1274 double yc = RotCoords[i].y;
1275 double zc = RotCoords[i].z;
1276 xx += atmMass*xc*xc;
1277 yy += atmMass*yc*yc;
1278 zz += atmMass*zc*zc;
1279 xy += atmMass*xc*yc;
1280 xz += atmMass*xc*zc;
1281 yz += atmMass*yc*zc;
1282 }
1283 if ((xx>0.1)&&(fabs(xx - yy)<1.0e-4)&&(fabs(zz-xx)>1.0e-4)) {
1284 //The x and y axis do not appear to be unique, Try to rotate an atom onto the x axis
1285 //loop through and look for atoms not on the z-axis. If there is already an atom on the
1286 //x or y axis we can skip as well. If the z axis is not unique we skip as well
1287 bool needRot = true;
1288 for (int i=0; i<cFrame->GetNumAtoms(); i++) {
1289 if (fabs(RotCoords[i].x)>1.0e-4) {
1290 if (fabs(RotCoords[i].y)<1.0e-4) {
1291 needRot = false; //Atom already on y-axis
1292 break;
1293 }
1294 } else if (fabs(RotCoords[i].y)>1.0e-4) {
1295 needRot = false; //Atom already on x-axis
1296 break;
1297 }
1298 }
1299 if (needRot) {
1300 //ok pick the first atom off of the z-axis
1301 for (int i=0; i<cFrame->GetNumAtoms(); i++) {
1302 if ((fabs(RotCoords[i].x)>1.0e-4)||(fabs(RotCoords[i].y)>1.0e-4)) {
1303 double theta = atan(RotCoords[i].y/RotCoords[i].x);
1304 double costh = cos(-theta);
1305 double sinth = sin(-theta);
1306 Matrix4D rotation;
1307 InitRotationMatrix(rotation);
1308 rotation[0][0] = costh;
1309 rotation[0][1] = sinth;
1310 rotation[1][0] = -sinth;
1311 rotation[1][1] = costh;
1312 Matrix4D temp;
1313 MultiplyMatrix(result,rotation,temp);
1314 CopyMatrix(temp,result);
1315 break;
1316 }
1317 }
1318 }
1319 }
1320
1321 //To complete the determination of the principle axis we must apply all of the symmetry
1322 //operators to each of the permutations of the axis. When we find a set that results in an
1323 //unchanged set of coordinates we are done.
1324 GAMESSPointGroup pg = GAMESS_C1;
1325 long pgOrder = 1;
1326 if (InputOptions) {
1327 pg = InputOptions->Data->GetPointGroup();
1328 pgOrder = InputOptions->Data->GetPointGroupOrder();
1329 }
1330 bool success = true;
1331 //If we are in C1 symmetry we are done.
1332 if (pg != GAMESS_C1) {
1333
1334 for (long i=0; i<cFrame->GetNumAtoms(); i++) {
1335 CPoint3D temp = (cFrame->Atoms[i].Position) + centerOfMass;
1336 Rotate3DPt(result, temp, &(RotCoords[i]));
1337 }
1338
1339 SymmetryOps symOps(pg, pgOrder);
1340 Matrix4D permuteMatrix;
1341 for (int pass=0; pass<6; pass++) {
1342 switch (pass) {
1343 case 0: //default axis
1344 InitRotationMatrix(permuteMatrix);
1345 break;
1346 case 1: //x, z, y
1347 permuteMatrix[1][1] = permuteMatrix[2][2] = 0.0;
1348 permuteMatrix[1][2] = -1.0;
1349 permuteMatrix[2][1] = 1.0;
1350 break;
1351 case 2: //y, x, z
1352 permuteMatrix[0][0] = permuteMatrix[1][2] = permuteMatrix[2][1] = 0.0;
1353 permuteMatrix[0][1] = -1.0;
1354 permuteMatrix[1][0] = permuteMatrix[2][2] = 1.0;
1355 break;
1356 case 3: //y, z, x
1357 permuteMatrix[0][1] = permuteMatrix[2][2] = 0.0;
1358 permuteMatrix[0][2] = permuteMatrix[2][1] = 1.0;
1359 break;
1360 case 4: //z, x, y
1361 permuteMatrix[0][2] = permuteMatrix[1][0] = permuteMatrix[2][1] = 0.0;
1362 permuteMatrix[0][2] = permuteMatrix[1][0] = permuteMatrix[2][1] = 1.0;
1363 break;
1364 case 5: //z, y, x
1365 permuteMatrix[1][0] = permuteMatrix[2][1] = 0.0;
1366 permuteMatrix[2][0] = -1.0;
1367 permuteMatrix[1][1] = 1.0;
1368 break;
1369 }
1370 success = true;
1371 for (int iOp=0; iOp<symOps.getOperationCount(); iOp++) {
1372 bool SymMatch = true;
1373 for (int atm=0; atm<cFrame->GetNumAtoms(); atm++) {
1374 CPoint3D test;
1375 Rotate3DPt(permuteMatrix, RotCoords[atm], &test);
1376 CPoint3D result;
1377 symOps.ApplyOperator(test, result, iOp);
1378 bool match = false;
1379 for (int testatom=0; testatom<cFrame->GetNumAtoms(); testatom++) {
1380 if (cFrame->Atoms[atm].GetType() != cFrame->Atoms[testatom].GetType())
1381 continue;
1382 CPoint3D testpt;
1383 Rotate3DPt(permuteMatrix, RotCoords[testatom], &testpt);
1384 CPoint3D offset = result - testpt;
1385 //test the difference in position. They should be quite close!
1386 if (offset.Magnitude() < precision) {
1387 match = true;
1388 break;
1389 }
1390 }
1391 if (!match) { //No matching atom so this permutation fails
1392 SymMatch = false;
1393 break;
1394 }
1395 }
1396 if (!SymMatch) {
1397 success = false;
1398 break;
1399 }
1400 }
1401 if (success) { //we are done
1402 //combine the permutation with the rotation matrix
1403 Matrix4D temp;
1404 MultiplyMatrix(result,permuteMatrix,temp);
1405 CopyMatrix(temp,result);
1406 Rotate3DOffset(result,centerOfMass,&rotatedCenterOfMass);
1407 break;
1408 }
1409 }
1410 }
1411 return success;
1412 }
GenerateSymmetryDependentAtoms(bool do_warnings)1413 void MoleculeData::GenerateSymmetryDependentAtoms(bool do_warnings) {
1414 // The input coordinates (in the current frame) must contain the symmetry
1415 // unique atoms in the proper symmetry adapted reference frame. This
1416 // routine will generate the symmetry dependent atoms.
1417 GAMESSPointGroup pg = GAMESS_C1;
1418 long pgOrder = 1;
1419 if (InputOptions) {
1420 pg = InputOptions->Data->GetPointGroup();
1421 pgOrder = InputOptions->Data->GetPointGroupOrder();
1422 }
1423 bool conflicts = false;
1424 bool closeAtoms = false;
1425 //If we are in C1 symmetry we are done.
1426 if ((pg != GAMESS_C1)&&(cFrame->GetNumAtoms()>0)) {
1427 SymmetryOps symOps(pg, pgOrder);
1428 //loop over the symmetry operations
1429 for (int atm=0; atm<cFrame->GetNumAtoms(); atm++) {
1430 for (int iOp=0; iOp<symOps.getOperationCount(); iOp++) {
1431 if (cFrame->Atoms[atm].IsSymmetryUnique()) {
1432 //Apply the operator to each atom
1433 CPoint3D result;
1434 symOps.ApplyOperator(cFrame->Atoms[atm].Position, result, iOp);
1435 bool match = false;
1436 for (int testatom=0; testatom<cFrame->GetNumAtoms(); testatom++) {
1437 //Now loop over the atoms to see if this generates a new atom
1438 //or if there are any conflicts
1439 CPoint3D offset = result - cFrame->Atoms[testatom].Position;
1440 float dist = offset.Magnitude();
1441 if (dist < 1.0e-3) { //this cutoff might need tuning
1442 //If we have a match the atoms had better be the same type
1443 if (cFrame->Atoms[atm].GetType() != cFrame->Atoms[testatom].GetType())
1444 conflicts = true;
1445 match = true;
1446 } else if (dist < 0.2) {
1447 //GAMESS would consider this an error
1448 closeAtoms = true;
1449 match = true;
1450 }
1451 }
1452 if (!match) {
1453 //In order to match the order of atoms that GAMESS generates we need
1454 //to insert the generated atom before the input one.
1455 /* NewAtom(cFrame->Atoms[atm].GetType(), result, atm); */
1456 NewAtom(cFrame->Atoms[atm], true, atm, &result);
1457 cFrame->Atoms[atm].IsSymmetryUnique(false);
1458 cFrame->SetAtomSelection(atm, false);
1459 atm++;
1460 }
1461 }
1462 }
1463 }
1464 }
1465
1466 if (do_warnings) {
1467 if (conflicts) {
1468 MessageAlert("Found conflicts during generation of symmetry dependent coordinates. Your starting coordinates are probably incorrect for the chosen symmetry point group.");
1469 }
1470 if (closeAtoms) {
1471 MessageAlert("Atoms closer than 0.2 Angstroms have been removed. Your coordinates may be incorrect for the chosen symmetry point group.");
1472 }
1473 }
1474 }
1475
GenerateSymmetryUniqueAtoms(double tolerance)1476 bool MoleculeData::GenerateSymmetryUniqueAtoms(double tolerance) {
1477 //On input the coordinates should contain the full set of atoms in the
1478 //correct orientation for the selected symmetry point group. The
1479 //coordinates will not be changed, just "marked" as symmetry unique or not.
1480 //If a symmetry operation generates a new atom an error is generated and
1481 //the operation fails. GAMESS likes the symmetry unique atom last. So if
1482 //an atom can be generated by an atom later in the list we consider it
1483 //symmetry dependent.
1484 GAMESSPointGroup pg = GAMESS_C1;
1485 long pgOrder = 1;
1486 if (InputOptions) {
1487 pg = InputOptions->Data->GetPointGroup();
1488 pgOrder = InputOptions->Data->GetPointGroupOrder();
1489 }
1490 //Clear the unique flags
1491 for (int i=0; i<cFrame->GetNumAtoms(); i++) {
1492 cFrame->Atoms[i].IsSymmetryUnique(false);
1493 }
1494 bool conflicts = false;
1495 bool closeAtoms = false;
1496 SymmetryOps symOps(pg, pgOrder);
1497 //loop over the symmetry operations
1498 for (int atm=0; atm<cFrame->GetNumAtoms(); atm++) {
1499 bool match = false;
1500 for (int iOp=0; iOp<symOps.getOperationCount(); iOp++) {
1501 //Apply the operator to each atom
1502 CPoint3D result;
1503 symOps.ApplyOperator(cFrame->Atoms[atm].Position, result, iOp);
1504 for (int testatom=atm+1; testatom<cFrame->GetNumAtoms(); testatom++) {
1505 //Now loop over the atoms to see if this generates a new atom
1506 //or if there are any conflicts
1507 CPoint3D offset = result - cFrame->Atoms[testatom].Position;
1508 float dist = offset.Magnitude();
1509 if (dist < tolerance) { //this cutoff might need tuning
1510 //If we have a match the atoms had better be the same type
1511 if (cFrame->Atoms[atm].GetType() != cFrame->Atoms[testatom].GetType())
1512 conflicts = true;
1513 match = true;
1514 break;
1515 } else if (dist < 0.2) {
1516 //GAMESS would consider this an error
1517 closeAtoms = true;
1518 match = true;
1519 break;
1520 }
1521 }
1522 if (match) {
1523 break;
1524 }
1525 }
1526 if (match == false) cFrame->Atoms[atm].IsSymmetryUnique(true);
1527 }
1528 // if (conflicts) {
1529 // MessageAlert("Found conflicts during generation of symmetry dependent coordinates. Your starting coordinates are probably incorrect for the chosen symmetry point group.");
1530 // }
1531 // if (closeAtoms) {
1532 // MessageAlert("Atoms closer than 0.2 Angstroms have been removed. Your coordinates may be incorrect for the chosen symmetry point group.");
1533 // }
1534 return (!conflicts);
1535 }
SymmetrizeCoordinates(bool selected_only)1536 void MoleculeData::SymmetrizeCoordinates(bool selected_only) {
1537 //The purpose of this routine is to remove the "slop" in the coordinates such that they
1538 //more tightly meet the selected point group.
1539 GAMESSPointGroup pg = GAMESS_C1;
1540 long pgOrder = 1;
1541 if (InputOptions) {
1542 pg = InputOptions->Data->GetPointGroup();
1543 pgOrder = InputOptions->Data->GetPointGroupOrder();
1544 }
1545 if (pg == GAMESS_C1) return; //there is nothing to be done for C1
1546 SymmetryOps symOps(pg, pgOrder);
1547 //My strategy is to loop over the symmetry unique atoms, apply each operator.
1548 //If the resulting operation generates an atom "close" to the original atom add it
1549 //to a vector sum. After all of the operators are applied divid the sum by the number of
1550 //images. Next pass through the symmetry dependent atoms and adjust their position to
1551 //match the newly generated coordinates
1552 for (int atm=0; atm<cFrame->GetNumAtoms(); atm++) {
1553 if (cFrame->Atoms[atm].IsSymmetryUnique() &&
1554 (!selected_only || cFrame->GetAtomSelection(atm))) {
1555 CPoint3D sum;
1556 int matchcount = 0;
1557 for (int iOp=0; iOp<symOps.getOperationCount(); iOp++) {
1558 CPoint3D result;
1559 //Apply the operator to each atom
1560 symOps.ApplyOperator(cFrame->Atoms[atm].Position, result, iOp);
1561 CPoint3D offset = result - cFrame->Atoms[atm].Position;
1562 float dist = offset.Magnitude();
1563 if (dist < 0.3) { //this cutoff might need tuning
1564 sum += result;
1565 matchcount++;
1566 }
1567 }
1568 if (matchcount > 1) {
1569 //update the symmetry unique coordinate
1570 cFrame->Atoms[atm].Position = sum / (float) matchcount;
1571 //and now update symmetry dependent atoms
1572 for (int iOp=0; iOp<symOps.getOperationCount(); iOp++) {
1573 CPoint3D result;
1574 //Apply the operator to each atom
1575 symOps.ApplyOperator(cFrame->Atoms[atm].Position, result, iOp);
1576 for (int testatom=0; testatom<cFrame->GetNumAtoms(); testatom++) {
1577 if (testatom == atm || cFrame->GetAtomType(testatom) != cFrame->GetAtomType(atm)) continue;
1578 //Now loop over the atoms to see if this generates a new atom
1579 //or if there are any conflicts
1580 CPoint3D offset = result - cFrame->Atoms[testatom].Position;
1581 float dist = offset.Magnitude();
1582 if (dist < 0.3) { //this cutoff might need tuning
1583 cFrame->Atoms[testatom].Position = result;
1584 }
1585 }
1586 }
1587 }
1588 }
1589 }
1590 }
CreateLLM(long NumPts,WinPrefs * Prefs)1591 void MoleculeData::CreateLLM(long NumPts, WinPrefs * Prefs) {
1592 Frame * NewFrame, * NewFrame2;
1593
1594 Frame * lFrame = cFrame;
1595 Frame * lEndFrame = lFrame->NextFrame;
1596 //saniety check
1597 if ((lFrame == NULL)||(lEndFrame == NULL)||(NumPts<1)) return;
1598 long NumAtoms = lFrame->NumAtoms;
1599
1600 CPoint3D * offset = new CPoint3D[NumAtoms];
1601 if (!offset) throw MemoryError();
1602 NumPts++;
1603 for (long i=0; i<NumAtoms; i++) {
1604 offset[i].x = (lEndFrame->Atoms[i].Position.x-lFrame->Atoms[i].Position.x)/NumPts;
1605 offset[i].y = (lEndFrame->Atoms[i].Position.y-lFrame->Atoms[i].Position.y)/NumPts;
1606 offset[i].z = (lEndFrame->Atoms[i].Position.z-lFrame->Atoms[i].Position.z)/NumPts;
1607 }
1608 NumPts--;
1609 NewFrame2 = lFrame;
1610 for (long counter=0; counter<NumPts; counter++) {
1611 NewFrame = AddFrame(NumAtoms, lFrame->NumBonds);
1612 for (long iatm=0; iatm<NumAtoms; iatm++) {
1613 NewFrame->Atoms[iatm].Type = lFrame->Atoms[iatm].Type;
1614 NewFrame->Atoms[iatm].Position.x = NewFrame2->Atoms[iatm].Position.x + offset[iatm].x;
1615 NewFrame->Atoms[iatm].Position.y = NewFrame2->Atoms[iatm].Position.y + offset[iatm].y;
1616 NewFrame->Atoms[iatm].Position.z = NewFrame2->Atoms[iatm].Position.z + offset[iatm].z;
1617 }
1618 NewFrame->NumAtoms = NumAtoms;
1619 if (Prefs->GetAutoBond()) {
1620 Progress lProg;
1621 NewFrame->SetBonds(Prefs, false, &lProg);
1622 }
1623 NewFrame2 = NewFrame;
1624 }
1625 delete [] offset;
1626 } /*CreateLLM*/
1627
1628 //CreateInternalLLM creates a linear least motion path based on the internal
1629 //coordinates of the two frames. In general this will result is a lower energy
1630 //path than a cartesian based LLM path, since it prevents bonds from being
1631 //stretched or squashed.
CreateInternalLLM(long NumPts,WinPrefs * Prefs)1632 void MoleculeData::CreateInternalLLM(long NumPts, WinPrefs * Prefs) {
1633 Frame * NewFrame, * NewFrame2;
1634
1635 Frame * lFrame = cFrame;
1636 Frame * lEndFrame = lFrame->NextFrame;
1637 //saniety check
1638 if ((lFrame == NULL)||(lEndFrame == NULL)||(NumPts<1)) return;
1639
1640 long NumAtoms = lFrame->NumAtoms;
1641 long SavedFrameNum = CurrentFrame;
1642
1643 if (NumAtoms <= 2) { //No point to using internals when there are less than 3 atoms!
1644 CreateLLM(NumPts, Prefs);
1645 return;
1646 }
1647 MOPacInternals * lInternals = IntCoords->GetMOPacStyle();
1648 if (!lInternals) return;
1649 float * OffsetValues = new float[3*NumAtoms];
1650 float * StartValues = new float[3*NumAtoms];
1651 if (!OffsetValues || !StartValues) throw MemoryError();
1652 Progress * lProg = new Progress;
1653 //Make sure the internals are up to date for the first frame
1654 lInternals->CartesiansToInternals(this);
1655 long i, part;
1656 for (i=1; i<NumAtoms; i++)
1657 for (part=0; part<3; part++)
1658 StartValues[3*i+part] = lInternals->GetValue(i, part);
1659 cFrame = lEndFrame;
1660 lInternals->CartesiansToInternals(this);
1661 for (i=0; i<9; i++) OffsetValues[i] = 0.0;
1662 NumPts++;
1663 for (i=1; i<NumAtoms; i++) {
1664 OffsetValues[3*i] = (lInternals->GetValue(i, 0) - StartValues[3*i])/NumPts;
1665 if (i>=2) {
1666 OffsetValues[3*i+1] = (lInternals->GetValue(i, 1) - StartValues[3*i+1])/NumPts;
1667 if (i>=3) { //dihedrals are tricky. We must take into account that they run from
1668 //180 to -180 and the shorest rotation might be through 180.
1669 OffsetValues[3*i+2] = lInternals->GetValue(i, 2) - StartValues[3*i+2];
1670 if (OffsetValues[3*i+2] > 180.0) OffsetValues[3*i+2] = OffsetValues[3*i+2] - 360.0;
1671 else if (OffsetValues[3*i+2] < -180.0) OffsetValues[3*i+2] = 360 + OffsetValues[3*i+2];
1672 OffsetValues[3*i+2] /= NumPts;
1673 }
1674 }
1675 }
1676 NumPts--;
1677 cFrame = lFrame;
1678 NewFrame2 = lFrame;
1679 for (long counter=0; counter<NumPts; counter++) {
1680 //add a new frame and copy the coordinates from the previous frame
1681 NewFrame = AddFrame(NumAtoms, lFrame->NumBonds);
1682 for (long iatm=0; iatm<NumAtoms; iatm++) {
1683 NewFrame->Atoms[iatm].Type = lFrame->Atoms[iatm].Type;
1684 NewFrame->Atoms[iatm].Position.x = NewFrame2->Atoms[iatm].Position.x;
1685 NewFrame->Atoms[iatm].Position.y = NewFrame2->Atoms[iatm].Position.y;
1686 NewFrame->Atoms[iatm].Position.z = NewFrame2->Atoms[iatm].Position.z;
1687 }
1688 NewFrame->NumAtoms = NumAtoms;
1689 //Now set the internals to their proper values
1690 cFrame = NewFrame;
1691 for (i=1; i<NumAtoms; i++) {
1692 StartValues[3*i] += OffsetValues[3*i];
1693 lInternals->SetValue(i, 0, StartValues[3*i]);
1694 if (i>=2) {
1695 StartValues[3*i+1] += OffsetValues[3*i+1];
1696 if (StartValues[3*i+1] < 0) StartValues[3*i+1] = 0.0;
1697 if (StartValues[3*i+1] > 180.0) StartValues[3*i+1] = 180.0;
1698 lInternals->SetValue(i, 1, StartValues[3*i+1]);
1699 if (i>=3) {
1700 StartValues[3*i+2] += OffsetValues[3*i+2];
1701 if (StartValues[3*i+2] < -180.0) StartValues[3*i+2] += 360.0;;
1702 if (StartValues[3*i+2] > 180.0) StartValues[3*i+2] -= 360.0;
1703 lInternals->SetValue(i, 2, StartValues[3*i+2]);
1704 }
1705 }
1706 }
1707 //Now convert the internals to cartesians so they are saved.
1708 lInternals->InternalsToCartesians(this, Prefs, 0);
1709
1710 if (Prefs->GetAutoBond())
1711 NewFrame->SetBonds(Prefs, false, lProg);
1712 NewFrame2 = NewFrame;
1713 }
1714 cFrame = lFrame; //reset the current frame pointer
1715 CurrentFrame = SavedFrameNum;
1716 delete [] OffsetValues;
1717 delete [] StartValues;
1718 if (lProg) {
1719 LinearLeastSquaresFit(lProg);
1720 delete lProg;
1721 }
1722 }
1723
AdvanceFrame(void)1724 void MoleculeData::AdvanceFrame(void) {
1725 if (cFrame->NextFrame) cFrame = cFrame->NextFrame;
1726 }
SetCurrentFrame(long FrameNum)1727 void MoleculeData::SetCurrentFrame(long FrameNum) {
1728 if ((FrameNum>NumFrames)||(FrameNum<1)) return;
1729 if (FrameNum < CurrentFrame) {
1730 cFrame = Frames;
1731 CurrentFrame = 1;
1732 }
1733 while (CurrentFrame < FrameNum) {
1734 cFrame = cFrame->NextFrame;
1735 CurrentFrame++;
1736 }
1737 }
SurfaceExportPossible(void)1738 bool MoleculeData::SurfaceExportPossible(void) {
1739 return cFrame->SurfaceExportPossible();
1740 }
ModeVisible(void) const1741 bool MoleculeData::ModeVisible(void) const {
1742 return ((cFrame->Vibs)&&GetDrawMode());
1743 }
GetNumBonds(void) const1744 long MoleculeData::GetNumBonds(void) const {
1745 return cFrame->GetNumBonds();
1746 }
DeleteAtom(long AtomNum,bool allFrames)1747 long MoleculeData::DeleteAtom(long AtomNum, bool allFrames) {
1748 long offset = AtomNum;
1749 int fragId = -1;
1750 if (cFrame->Atoms[AtomNum].IsEffectiveFragment()) fragId = cFrame->Atoms[AtomNum].GetFragmentNumber();
1751 if (allFrames) {
1752 Frame * lFrame = Frames;
1753 while (lFrame) {
1754 lFrame->DeleteAtom(AtomNum);
1755 lFrame = lFrame->NextFrame;
1756 }
1757 } else
1758 cFrame->DeleteAtom(AtomNum);
1759 if (IntCoords) {
1760 MOPacInternals * mInts = IntCoords->GetMOPacStyle();
1761 if (mInts) mInts->DeleteAtom(this, AtomNum);
1762 }
1763 std::vector<Annotation *>::iterator anno;
1764 anno = Annotations.begin();
1765 for (anno = Annotations.begin(); anno != Annotations.end(); ) {
1766 if ((*anno)->containsAtom(AtomNum)) {
1767 delete (*anno);
1768 anno = Annotations.erase(anno);
1769 } else {
1770 (*anno)->adjustIds(AtomNum, -1);
1771 anno++;
1772 }
1773 }
1774
1775 if (fragId > 0) { //recurse to delete the other atoms in the same fragment
1776 for (long i=0; i<cFrame->NumAtoms; i++) {
1777 if (cFrame->Atoms[i].IsEffectiveFragment() &&
1778 cFrame->Atoms[i].GetFragmentNumber() == fragId) {
1779 //turn off fragment bit to prevent multilevel recursion
1780 cFrame->Atoms[i].IsEffectiveFragment(false);
1781 DeleteAtom(i, allFrames);
1782 --i;
1783 }
1784 }
1785 //remove the fragment name from the name list
1786 std::vector<std::string>::iterator iter = FragmentNames.begin();
1787 for (int i=1; i<fragId; i++) ++iter;
1788 FragmentNames.erase(iter);
1789 offset = 0; //Have the caller rescan the whole list to be safe.
1790 }
1791 if (FMOFragmentIds.size() > AtomNum) FMOFragmentIds.erase(FMOFragmentIds.begin() + AtomNum);
1792
1793 ResetRotation();
1794 return offset;
1795 }
ReorderAtomList(long index1,long targetindex)1796 void MoleculeData::ReorderAtomList(long index1, long targetindex) {
1797 //Sanity check the move
1798 if ((index1 < 0)||(targetindex < 0)||(index1 >= cFrame->NumAtoms)||
1799 (targetindex >= cFrame->NumAtoms)||(index1 == targetindex)) return;
1800 //This should really remove basis set, orbitals, etc unless they are also updated
1801 //Move the atom
1802 mpAtom temp;
1803 if (targetindex > index1) {
1804 temp = cFrame->Atoms[index1];
1805 for (long i=index1; i<targetindex; i++) {
1806 cFrame->Atoms[i] = cFrame->Atoms[i+1];
1807 }
1808 cFrame->Atoms[targetindex] = temp;
1809 //update the bonds list
1810 for (long i=0; i<cFrame->NumBonds; i++) {
1811 if (cFrame->Bonds[i].Atom1 == index1)
1812 cFrame->Bonds[i].Atom1 = targetindex;
1813 else if ((cFrame->Bonds[i].Atom1 > index1)&&(cFrame->Bonds[i].Atom1 <= targetindex))
1814 cFrame->Bonds[i].Atom1 --;
1815 if (cFrame->Bonds[i].Atom2 == index1)
1816 cFrame->Bonds[i].Atom2 = targetindex;
1817 else if ((cFrame->Bonds[i].Atom2 > index1)&&(cFrame->Bonds[i].Atom2 <= targetindex))
1818 cFrame->Bonds[i].Atom2 --;
1819 }
1820 } else {
1821 temp = cFrame->Atoms[index1];
1822 for (long i=index1; i>targetindex; i--) {
1823 cFrame->Atoms[i] = cFrame->Atoms[i-1];
1824 }
1825 cFrame->Atoms[targetindex] = temp;
1826 //update the bonds list
1827 for (long i=0; i<cFrame->NumBonds; i++) {
1828 if (cFrame->Bonds[i].Atom1 == index1)
1829 cFrame->Bonds[i].Atom1 = targetindex;
1830 else if ((cFrame->Bonds[i].Atom1 < index1)&&(cFrame->Bonds[i].Atom1 >= targetindex))
1831 cFrame->Bonds[i].Atom1 ++;
1832 if (cFrame->Bonds[i].Atom2 == index1)
1833 cFrame->Bonds[i].Atom2 = targetindex;
1834 else if ((cFrame->Bonds[i].Atom2 < index1)&&(cFrame->Bonds[i].Atom2 >= targetindex))
1835 cFrame->Bonds[i].Atom2 ++;
1836 }
1837 }
1838 //update the internals list
1839 if (IntCoords) {
1840 IntCoords->ChangeAtomIndex(this, index1, targetindex);
1841 }
1842 }
ValidAtom(long AtomNum)1843 bool MoleculeData::ValidAtom(long AtomNum) {
1844 return ((AtomNum>=0)&&(AtomNum<cFrame->NumAtoms));
1845 }
GetAtomTypes(void)1846 AtomTypeList * MoleculeData::GetAtomTypes(void) {return cFrame->GetAtomTypes();}
GetNumBasisFunctions(void) const1847 long MoleculeData::GetNumBasisFunctions(void) const {
1848 return ((Basis!=NULL)?Basis->NumFuncs : 0);
1849 }
DeleteAllAnnotations(void)1850 void MoleculeData::DeleteAllAnnotations(void) {
1851 std::vector<Annotation *>::const_iterator anno;
1852 anno = Annotations.begin();
1853 for (anno = Annotations.begin(); anno != Annotations.end(); ) {
1854 delete (*anno);
1855 anno++;
1856 }
1857 Annotations.clear();
1858 }
1859
GetFragmentName(unsigned long index) const1860 const char * MoleculeData::GetFragmentName(unsigned long index) const {
1861 if (index < FragmentNames.size())
1862 return FragmentNames[index].c_str();
1863 else
1864 return "H2ORHF";
1865 }
1866
PruneUnusedFragments()1867 void MoleculeData::PruneUnusedFragments() {
1868
1869 bool found_instance;
1870 std::map<std::string, EFrag>::iterator frag;
1871 std::vector<std::string>::const_iterator fragname;
1872
1873 for (frag = efrags.begin(); frag != efrags.end(); /* BLANK */) {
1874 found_instance = false;
1875 for (fragname = FragmentNames.begin();
1876 !found_instance && fragname != FragmentNames.end();
1877 ++fragname) {
1878 if (frag->first.compare(*fragname) == 0) {
1879 found_instance = true;
1880 }
1881 }
1882 if (!found_instance) {
1883 efrags.erase(frag++);
1884 } else {
1885 ++frag;
1886 }
1887 }
1888
1889 }
1890
CreateFMOFragmentation(const int & NumMolecules,std::vector<long> & newFragmentation)1891 long MoleculeData::CreateFMOFragmentation(const int & NumMolecules, std::vector<long> & newFragmentation) {
1892 //Create an FMO fragment list by breaking up the system into non-bonded pieces (H-bonds are ignored)
1893
1894 long result = 0;
1895 //Start by initializing the vector with invalid fragment numbers
1896 newFragmentation.clear();
1897 newFragmentation.reserve(cFrame->GetNumAtoms());
1898 for (long i=0; i<cFrame->GetNumAtoms(); i++) {
1899 newFragmentation.push_back(-1);
1900 }
1901
1902 long NextId = 1;
1903 long atomNum = 0;
1904 while (atomNum < cFrame->GetNumAtoms()) {
1905 if (newFragmentation[atomNum] < 1) {
1906 newFragmentation[atomNum] = NextId;
1907 //starting a new fragment
1908 //a recursive function would be simpler to write and should normally be fine, but if somebody runs this
1909 //on a system with a large molecule that could be rather abusive on the stack
1910 //Scan through the bonds list and check both ends of the bond for the active fragment Id. If one
1911 //atom is tagged then the other needs to be. The process is done when a further pass through the list
1912 //does not produce any more tags. This seems a bit brute force and is horrible on memory accesses, but
1913 //it is probably fast enough for the very limited number of times it be executed.
1914 bool done=false;
1915 while (!done) {
1916 done = true;
1917 for (long ib=0; ib<cFrame->GetNumBonds(); ++ib) {
1918 if (newFragmentation[cFrame->Bonds[ib].Atom1] == NextId) {
1919 if (newFragmentation[cFrame->Bonds[ib].Atom2] != NextId) {
1920 newFragmentation[cFrame->Bonds[ib].Atom2] = NextId;
1921 done = false;
1922 }
1923 } else if (newFragmentation[cFrame->Bonds[ib].Atom2] == NextId) {
1924 if (newFragmentation[cFrame->Bonds[ib].Atom1] != NextId) {
1925 newFragmentation[cFrame->Bonds[ib].Atom1] = NextId;
1926 done = false;
1927 }
1928 }
1929 }
1930 }
1931 ++NextId;
1932 }
1933 ++atomNum;
1934 }
1935 result = NextId - 1;
1936 //Ok we should now have the non-bonded pieces separated. If the user wants more than one non-bonded piece in
1937 //each fragment coalesce them now.
1938 if (NumMolecules > 1) {
1939 --NextId; //this leaves NextId containing the starting number of fragments
1940 //I think it is probably best to combine fragments that are closest to each other.
1941 //It's probably best to consider the center of each fragment for the distance computation.
1942 std::vector<CPoint3D> fragCenters;
1943 fragCenters.reserve(NextId);
1944 for (long ifrag=0; ifrag<NextId; ++ifrag) {
1945 fragCenters.push_back(CPoint3D(0,0,0));
1946 long atmcount=0;
1947 for (long iatom=0; iatom<cFrame->GetNumAtoms(); ++iatom) {
1948 if (newFragmentation[iatom] == (ifrag+1)) {
1949 fragCenters[ifrag].x += cFrame->Atoms[iatom].Position.x;
1950 fragCenters[ifrag].y += cFrame->Atoms[iatom].Position.y;
1951 fragCenters[ifrag].z += cFrame->Atoms[iatom].Position.z;
1952 atmcount++;
1953 }
1954 }
1955 fragCenters[ifrag].x /= (float) atmcount;
1956 fragCenters[ifrag].y /= (float) atmcount;
1957 fragCenters[ifrag].z /= (float) atmcount;
1958 }
1959
1960 for (long ifrag=0; ifrag<NextId; ifrag++) {
1961 for (int i=1; i<NumMolecules; i++) {
1962 if ((ifrag+1) == NextId) break; //we've ran out of fragments, the last one will have to be short
1963 CPoint3D offset;
1964 offset.x = fragCenters[ifrag].x-fragCenters[ifrag+1].x;
1965 offset.y = fragCenters[ifrag].y-fragCenters[ifrag+1].y;
1966 offset.z = fragCenters[ifrag].z-fragCenters[ifrag+1].z;
1967 float distance = offset.x * offset.x + offset.y * offset.y + offset.z * offset.z;
1968
1969 long targetId = ifrag+1;
1970 for (long jfrag=ifrag+2; jfrag<NextId; jfrag++) {
1971 offset.x = fragCenters[ifrag].x-fragCenters[jfrag].x;
1972 offset.y = fragCenters[ifrag].y-fragCenters[jfrag].y;
1973 offset.z = fragCenters[ifrag].z-fragCenters[jfrag].z;
1974 float testDistance = offset.x * offset.x + offset.y * offset.y + offset.z * offset.z;
1975 if (testDistance < distance) {
1976 distance = testDistance;
1977 targetId = jfrag;
1978 }
1979 }
1980 //Since we are removing a fragment we need to pull down the Ids higher up
1981 //one thing I'm not doing is recomputing the fragment center so the next addition will
1982 //reference the original fragment only
1983 for (long atomi=0; atomi<cFrame->GetNumAtoms(); ++atomi) {
1984 if (newFragmentation[atomi] == (targetId+1)) newFragmentation[atomi] = (ifrag+1);
1985 if (newFragmentation[atomi] > (targetId+1)) newFragmentation[atomi]--;
1986 }
1987 fragCenters.erase(fragCenters.begin() + targetId);
1988 NextId--;
1989 }
1990 }
1991 result = NextId;
1992 }
1993 return result;
1994 }
GetFMOFragmentId(const long & AtomId) const1995 long MoleculeData::GetFMOFragmentId(const long & AtomId) const {
1996 if ((AtomId >= 0) && (AtomId < FMOFragmentIds.size())) {
1997 return FMOFragmentIds[AtomId];
1998 }
1999 return -1;
2000 }
SetFMOFragmentId(const long & AtomId,const long & FragId)2001 void MoleculeData::SetFMOFragmentId(const long & AtomId, const long & FragId) {
2002 if ((AtomId<0)||(AtomId>=cFrame->GetNumAtoms())) return; //invalid atom
2003 if ((FragId<1)||(FragId > InputOptions->FMO.GetNumberFragments())) return; //invalid fragment id
2004 //Is the fragment id array setup?
2005 while (FMOFragmentIds.size() < cFrame->GetNumAtoms()) {
2006 FMOFragmentIds.push_back(1); //simply initialize atoms to fragment 1
2007 }
2008 FMOFragmentIds[AtomId] = FragId;
2009 }
2010
ExportPOV(BufferFile * Buffer,WinPrefs * Prefs)2011 void MoleculeData::ExportPOV(BufferFile *Buffer, WinPrefs *Prefs) {
2012
2013 Frame *lFrame = GetCurrentFramePtr();
2014 mpAtom *lAtoms = lFrame->Atoms;
2015
2016 long NumAtoms = lFrame->NumAtoms;
2017 float AtomScale = Prefs->GetAtomScale();
2018 long curAtomType;
2019 RGBColor * AtomColor;
2020 float red, green, blue;
2021 char tmpStr[500];
2022
2023 Buffer->PutText("#include \"transforms.inc\"\n\n");
2024
2025 sprintf(tmpStr,
2026 "camera {\n"
2027 "\tlocation <0, 0, 0>\n"
2028 "\tsky <0, 1, 0>\n"
2029 "\tlook_at <0, 0, -1>\n"
2030 "}\n\n");
2031 Buffer->PutText(tmpStr);
2032
2033 sprintf(tmpStr,
2034 "light_source {\n"
2035 "\t<6, 6, 12>, rgb <1, 1, 1>\n"
2036 "}\n\n");
2037 Buffer->PutText(tmpStr);
2038
2039 sprintf(tmpStr,
2040 "light_source {\n"
2041 "\t<-6, 6, 12>, rgb <1, 1, 1>\n"
2042 "}\n\n");
2043 Buffer->PutText(tmpStr);
2044
2045 sprintf(tmpStr,
2046 "background {\n"
2047 "\trgb <1, 1, 1>\n"
2048 "}\n\n");
2049 Buffer->PutText(tmpStr);
2050
2051 Buffer->PutText("#declare AtomBondFinish = finish {specular 0.95 roughness 0.005}\n");
2052 Buffer->PutText("#declare SurfaceFinish = finish {specular 0.95 roughness 0.001}\n\n");
2053
2054 Buffer->PutText("union {\n");
2055 for (long iatom = 0; iatom < NumAtoms; iatom++) {
2056 if (lAtoms[iatom].GetInvisibility()) continue;
2057
2058 curAtomType = lAtoms[iatom].GetType() - 1;
2059 AtomColor = Prefs->GetAtomColorLoc(curAtomType);
2060 red = AtomColor->red / 65536.0;
2061 green = AtomColor->green / 65536.0;
2062 blue = AtomColor->blue / 65536.0;
2063
2064 float radius = AtomScale*Prefs->GetAtomSize(curAtomType);
2065 Buffer->PutText("sphere {\n");
2066 sprintf(tmpStr, "\t<%f, %f, %f>, %f\n",
2067 lAtoms[iatom].Position.x,
2068 lAtoms[iatom].Position.y,
2069 lAtoms[iatom].Position.z, radius);
2070 Buffer->PutText(tmpStr);
2071 sprintf(tmpStr, "\ttexture {\n"
2072 "\t\tpigment {color rgb <%f, %f, %f>}\n"
2073 "\t\tfinish {AtomBondFinish}\n"
2074 "\t}\n", red, green, blue);
2075 Buffer->PutText(tmpStr);
2076 Buffer->PutText("}\n\n");
2077 }
2078
2079 Bond *lBonds = lFrame->Bonds;
2080 long NumBonds = lFrame->NumBonds;
2081 double BondSize = Prefs->GetQD3DBondWidth();
2082 for (long ibond = 0; ibond < NumBonds; ibond++) {
2083 const mpAtom& atom1 = lAtoms[lBonds[ibond].Atom1];
2084 const mpAtom& atom2 = lAtoms[lBonds[ibond].Atom2];
2085
2086 CPoint3D offset = atom2.Position - atom1.Position;
2087 double length = offset.Magnitude();
2088
2089 double radius1 = AtomScale * Prefs->GetAtomSize(atom1.GetType() - 1);
2090 double radius2 = AtomScale * Prefs->GetAtomSize(atom2.GetType() - 1);
2091 double percent1 = radius1 / length;
2092 double percent2 = radius2 / length;
2093 double centerPercent = 0.5 + 0.5*(percent1-percent2);
2094 CPoint3D halfway = atom1.Position + offset * centerPercent;
2095
2096 AtomColor = Prefs->GetAtomColorLoc(atom1.GetType() - 1);
2097 red = AtomColor->red / 65536.0;
2098 green = AtomColor->green / 65536.0;
2099 blue = AtomColor->blue / 65536.0;
2100
2101 Buffer->PutText("cylinder {\n");
2102 sprintf(tmpStr, "\t<%f, %f, %f>, <%f, %f, %f>, %f\n",
2103 atom1.Position.x, atom1.Position.y, atom1.Position.z,
2104 halfway.x, halfway.y, halfway.z, BondSize);
2105 Buffer->PutText(tmpStr);
2106 sprintf(tmpStr,
2107 "\ttexture {\n"
2108 "\t\tpigment {color rgb <%f, %f, %f>}\n"
2109 "\t\tfinish {AtomBondFinish}\n"
2110 "\t}\n",
2111 red, green, blue);
2112 Buffer->PutText(tmpStr);
2113 Buffer->PutText("}\n\n");
2114
2115 AtomColor = Prefs->GetAtomColorLoc(atom2.GetType() - 1);
2116 red = AtomColor->red / 65536.0;
2117 green = AtomColor->green / 65536.0;
2118 blue = AtomColor->blue / 65536.0;
2119
2120 Buffer->PutText("cylinder {\n");
2121 sprintf(tmpStr, "\t<%f, %f, %f>, <%f, %f, %f> %f\n",
2122 atom2.Position.x, atom2.Position.y, atom2.Position.z,
2123 halfway.x, halfway.y, halfway.z, BondSize);
2124 Buffer->PutText(tmpStr);
2125 sprintf(tmpStr, "\ttexture {\n"
2126 "\t\tpigment {color rgb <%f, %f, %f>}\n"
2127 "\t\tfinish {AtomBondFinish}\n"
2128 "\t}\n", red, green, blue);
2129 Buffer->PutText(tmpStr);
2130 Buffer->PutText("}\n\n");
2131 }
2132
2133 // Export any surfaces.
2134 Surface *lSurface = lFrame->SurfaceList;
2135 while (lSurface) {
2136 Buffer->PutText("// ");
2137 Buffer->PutText(lSurface->GetLabel());
2138 Buffer->PutText("\n");
2139 lSurface->ExportPOV(this, Prefs, Buffer);
2140 lSurface = lSurface->GetNextSurface();
2141 }
2142
2143 // Now, transform scene to mimic current rotation and translation.
2144 float *m = (float *) TotalRotation;
2145 sprintf(tmpStr,
2146 "\n\tmatrix <%f, %f, %f,"
2147 "\n\t %f, %f, %f,"
2148 "\n\t %f, %f, %f,"
2149 "\n\t %f, %f, %f>\n",
2150 m[0], m[1], m[2], m[4], m[5], m[6],
2151 m[8], m[9], m[10], m[12], m[13], m[14]);
2152 Buffer->PutText(tmpStr);
2153 Buffer->PutText("\n\tscale <-1, 1, 1>\n");
2154 sprintf(tmpStr, "\n\ttranslate <0, 0, %f>\n", -WindowSize);
2155 Buffer->PutText(tmpStr);
2156 Buffer->PutText("}\n\n");
2157
2158 if (Prefs->ShowAtomicSymbolLabels()) {
2159 CPoint3D text_pos;
2160 wxString atomic_symbol;
2161 unsigned int i;
2162
2163 for (i = 0; i < kMaxAtomTypes; i++) {
2164 Prefs->GetAtomLabel(i, atomic_symbol);
2165 sprintf(tmpStr,
2166 "#declare Atom_%03d = "
2167 " text {"
2168 " ttf \"timrom.ttf\", \"%s\", 0.01, 0"
2169 " }\n\n",
2170 i, (const char *) atomic_symbol.mb_str(wxConvUTF8));
2171 Buffer->PutText(tmpStr);
2172 }
2173
2174 Buffer->PutText("union {\n");
2175 float radius;
2176 for (long iatom = 0; iatom < NumAtoms; iatom++) {
2177 if (lAtoms[iatom].GetInvisibility()) continue;
2178
2179 Rotate3DPt(TotalRotation, lAtoms[iatom].Position, &text_pos);
2180 curAtomType = lAtoms[iatom].GetType() - 1;
2181 radius = AtomScale * Prefs->GetAtomSize(curAtomType);
2182
2183 AtomColor = Prefs->GetAtomColorLoc(curAtomType);
2184 red = AtomColor->red / 65536.0;
2185 green = AtomColor->green / 65536.0;
2186 blue = AtomColor->blue / 65536.0;
2187
2188 sprintf(tmpStr,
2189 "object {\n"
2190 " Atom_%03ld\n"
2191 " Center_Trans(Atom_%03ld, x + y)\n"
2192 " scale <0.25, 0.25, 1.0>\n"
2193 " translate <%f, %f, %f>\n"
2194 " no_shadow\n"
2195 " pigment { color rgb <%f, %f, %f> }\n"
2196 "}\n\n", curAtomType, curAtomType,
2197 text_pos.x, text_pos.y, text_pos.z + radius,
2198 1.0f - red, 1.0f - green, 1.0f - blue);
2199 Buffer->PutText(tmpStr);
2200 }
2201 Buffer->PutText("\n\tscale <-1, 1, 1>\n");
2202 sprintf(tmpStr, "\n\ttranslate <0, 0, %f>\n", -WindowSize);
2203 Buffer->PutText(tmpStr);
2204 Buffer->PutText("}\n");
2205 }
2206
2207 }
2208