4 * Created by Toshi Nagata on 06/07/30.
5 * Copyright 2006-2008 Toshi Nagata. All rights reserved.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation version 2 of the License.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
17 /* On MinGW, #include of <GL/gl.h> should be before something in MolLib.h.
18 Therefore, we include MainView.h first separately and then include
19 the rest of MolLib.h. */
22 #include "Ruby_bind/Molby_extern.h"
24 #include "MD/MDCore.h"
25 #include "MD/MDGraphite.h"
32 #define biso2radius(r) ((r) > 0.5 ? sqrt((r) / 78.9568352087147) : 0.08)
34 /* pp is a pointer to Parameter record */
35 #define VDW_RADIUS(pp) (pp->vdw_radius == 0.0 ? pp->radius * 0.51 + 1.20 : pp->vdw_radius)
37 /* Invalid bond/angle/torsion value, used in internal cache */
38 const Double kInvalidValue = -10000000.0;
40 #pragma mark ==== OpenGL utility functions ===
42 static void __gluMultMatrixVecf(const GLdouble matrix[16], const GLdouble in[4], GLdouble out[4])
45 for (i = 0; i < 4; i++) {
46 out[i] = in[0] * matrix[0*4+i] + in[1] * matrix[1*4+i] + in[2] * matrix[2*4+i] + in[3] * matrix[3*4+i];
50 static int __gluInvertMatrixf(const GLdouble m[16], GLdouble invOut[16])
52 GLdouble inv[16], det;
55 inv[0] = m[5]*m[10]*m[15] - m[5]*m[11]*m[14] - m[9]*m[6]*m[15]
56 + m[9]*m[7]*m[14] + m[13]*m[6]*m[11] - m[13]*m[7]*m[10];
57 inv[4] = -m[4]*m[10]*m[15] + m[4]*m[11]*m[14] + m[8]*m[6]*m[15]
58 - m[8]*m[7]*m[14] - m[12]*m[6]*m[11] + m[12]*m[7]*m[10];
59 inv[8] = m[4]*m[9]*m[15] - m[4]*m[11]*m[13] - m[8]*m[5]*m[15]
60 + m[8]*m[7]*m[13] + m[12]*m[5]*m[11] - m[12]*m[7]*m[9];
61 inv[12] = -m[4]*m[9]*m[14] + m[4]*m[10]*m[13] + m[8]*m[5]*m[14]
62 - m[8]*m[6]*m[13] - m[12]*m[5]*m[10] + m[12]*m[6]*m[9];
63 inv[1] = -m[1]*m[10]*m[15] + m[1]*m[11]*m[14] + m[9]*m[2]*m[15]
64 - m[9]*m[3]*m[14] - m[13]*m[2]*m[11] + m[13]*m[3]*m[10];
65 inv[5] = m[0]*m[10]*m[15] - m[0]*m[11]*m[14] - m[8]*m[2]*m[15]
66 + m[8]*m[3]*m[14] + m[12]*m[2]*m[11] - m[12]*m[3]*m[10];
67 inv[9] = -m[0]*m[9]*m[15] + m[0]*m[11]*m[13] + m[8]*m[1]*m[15]
68 - m[8]*m[3]*m[13] - m[12]*m[1]*m[11] + m[12]*m[3]*m[9];
69 inv[13] = m[0]*m[9]*m[14] - m[0]*m[10]*m[13] - m[8]*m[1]*m[14]
70 + m[8]*m[2]*m[13] + m[12]*m[1]*m[10] - m[12]*m[2]*m[9];
71 inv[2] = m[1]*m[6]*m[15] - m[1]*m[7]*m[14] - m[5]*m[2]*m[15]
72 + m[5]*m[3]*m[14] + m[13]*m[2]*m[7] - m[13]*m[3]*m[6];
73 inv[6] = -m[0]*m[6]*m[15] + m[0]*m[7]*m[14] + m[4]*m[2]*m[15]
74 - m[4]*m[3]*m[14] - m[12]*m[2]*m[7] + m[12]*m[3]*m[6];
75 inv[10] = m[0]*m[5]*m[15] - m[0]*m[7]*m[13] - m[4]*m[1]*m[15]
76 + m[4]*m[3]*m[13] + m[12]*m[1]*m[7] - m[12]*m[3]*m[5];
77 inv[14] = -m[0]*m[5]*m[14] + m[0]*m[6]*m[13] + m[4]*m[1]*m[14]
78 - m[4]*m[2]*m[13] - m[12]*m[1]*m[6] + m[12]*m[2]*m[5];
79 inv[3] = -m[1]*m[6]*m[11] + m[1]*m[7]*m[10] + m[5]*m[2]*m[11]
80 - m[5]*m[3]*m[10] - m[9]*m[2]*m[7] + m[9]*m[3]*m[6];
81 inv[7] = m[0]*m[6]*m[11] - m[0]*m[7]*m[10] - m[4]*m[2]*m[11]
82 + m[4]*m[3]*m[10] + m[8]*m[2]*m[7] - m[8]*m[3]*m[6];
83 inv[11] = -m[0]*m[5]*m[11] + m[0]*m[7]*m[9] + m[4]*m[1]*m[11]
84 - m[4]*m[3]*m[9] - m[8]*m[1]*m[7] + m[8]*m[3]*m[5];
85 inv[15] = m[0]*m[5]*m[10] - m[0]*m[6]*m[9] - m[4]*m[1]*m[10]
86 + m[4]*m[2]*m[9] + m[8]*m[1]*m[6] - m[8]*m[2]*m[5];
88 det = m[0]*inv[0] + m[1]*inv[4] + m[2]*inv[8] + m[3]*inv[12];
94 for (i = 0; i < 16; i++)
95 invOut[i] = inv[i] * det;
100 static void __gluMultMatricesf(const GLdouble a[16], const GLdouble b[16],
104 for (i = 0; i < 4; i++) {
105 for (j = 0; j < 4; j++) {
106 r[i*4+j] = a[i*4+0]*b[0*4+j] + a[i*4+1]*b[1*4+j] + a[i*4+2]*b[2*4+j] + a[i*4+3]*b[3*4+j];
112 myGluProject(GLdouble objx, GLdouble objy, GLdouble objz, const GLdouble modelMatrix[16], const GLdouble projMatrix[16], const GLint viewport[4], GLdouble *winx, GLdouble *winy, GLdouble *winz)
121 __gluMultMatrixVecf(modelMatrix, in, out);
122 __gluMultMatrixVecf(projMatrix, out, in);
128 /* Map x, y and z to range 0-1 */
129 in[0] = in[0] * 0.5 + 0.5;
130 in[1] = in[1] * 0.5 + 0.5;
131 in[2] = in[2] * 0.5 + 0.5;
132 /* Map x,y to viewport */
133 in[0] = in[0] * viewport[2] + viewport[0];
134 in[1] = in[1] * viewport[3] + viewport[1];
142 myGluUnProject(GLdouble winx, GLdouble winy, GLdouble winz, const GLdouble modelMatrix[16], const GLdouble projMatrix[16], const GLint viewport[4], GLdouble *objx, GLdouble *objy, GLdouble *objz)
144 GLdouble finalMatrix[16];
148 __gluMultMatricesf(modelMatrix, projMatrix, finalMatrix);
149 if (!__gluInvertMatrixf(finalMatrix, finalMatrix))
157 /* Map x and y from window coordinates */
158 in[0] = (in[0] - viewport[0]) / viewport[2];
159 in[1] = (in[1] - viewport[1]) / viewport[3];
161 /* Map to range -1 to 1 */
162 in[0] = in[0] * 2 - 1;
163 in[1] = in[1] * 2 - 1;
164 in[2] = in[2] * 2 - 1;
166 __gluMultMatrixVecf(finalMatrix, in, out);
167 if (out[3] == 0.0) return(GL_FALSE);
178 myGluPerspective(GLdouble fovy_degree, GLdouble aspect, GLdouble zNear, GLdouble zFar) {
180 GLdouble fovy_rad = fovy_degree * (3.14159265358979 / 180.0);
181 GLdouble f = 1.0 / tan(fovy_rad / (GLdouble)2.0);
192 m[10] = (zFar + zNear) / (zNear - zFar);
196 m[14] = (2 * zFar * zNear) / (zNear - zFar);
198 glMatrixMode(GL_PROJECTION);
202 #pragma mark ==== MainView public methods ====
205 MainView_setViewObject(MainView *mview, void *ref)
207 static GLdouble sIdentity[16] = {
208 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1
211 if (mview->ref == NULL) {
212 /* Initialize GUI-only members */
213 mview->mode = kTrackballRotateMode;
214 mview->tempAtoms[0] = mview->tempAtoms[1] = -1;
215 memmove(mview->modelview_matrix, sIdentity, sizeof(sIdentity));
216 memmove(mview->projection_matrix, sIdentity, sizeof(sIdentity));
217 mview->tableCache = IntGroupNew();
218 mview->tableSelection = IntGroupNew();
220 mview->ref = ref; /* No retain */
221 IntGroupClear(mview->tableCache);
222 IntGroupClear(mview->tableSelection);
223 if (mview->ref != NULL)
224 MoleculeCallback_notifyModification(mview->mol, 0);
229 MainView_isAtomHidden(MainView *mview, int index)
231 if (index < 0 || index >= mview->mol->natoms)
233 else if (mview->visibleFlags == NULL)
235 else return (mview->visibleFlags[index] == 0);
236 /* Atom *ap = ATOM_AT_INDEX(mview->mol->atoms, index);
237 if (ap->exflags & kAtomHiddenFlag)
239 if (!mview->showHydrogens && ap->atomicNumber == 1)
241 if (!mview->showDummyAtoms && ap->atomicNumber == 0)
247 MainView_refreshCachedInfo(MainView *mview)
250 int i, j, n1, n2, n3, n4;
254 if (mview == NULL || (mol = mview->mol) == NULL)
257 /* Rebuild internal caches */
260 if (mview->visibleFlags == NULL)
261 mview->visibleFlags = (Byte *)malloc(mol->natoms);
263 mview->visibleFlags = (Byte *)realloc(mview->visibleFlags, mol->natoms);
264 memset(mview->visibleFlags, 0, mol->natoms);
265 mview->countHidden = 0;
266 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
267 if (ap->exflags & kAtomHiddenFlag) {
268 mview->countHidden++;
271 if (!mview->showHydrogens && ap->atomicNumber == 1)
273 if (!mview->showDummyAtoms && ap->atomicNumber == 0)
275 mview->visibleFlags[i] = 1;
278 /* Selection (only temporary used within this function) */
280 IntGroup *sel = MoleculeGetSelection(mol);
281 if (sel != NULL && IntGroupGetCount(sel) > 0) {
282 for (i = 0; (n1 = IntGroupGetStartPoint(sel, i)) >= 0; i++) {
283 n2 = IntGroupGetEndPoint(sel, i);
284 if (n2 > mol->natoms)
287 mview->visibleFlags[n1++] |= 2;
293 if (mview->tableCache == NULL)
294 mview->tableCache = IntGroupNew();
295 if (mview->tableSelection == NULL)
296 mview->tableSelection = IntGroupNew();
297 IntGroupClear(mview->tableCache);
298 IntGroupClear(mview->tableSelection);
300 if (mview->tableIndex == kMainViewAtomTableIndex ||
301 mview->tableIndex == kMainViewXtalCoordTableIndex) { /* Atoms */
302 for (i = j = 0; i < mol->natoms; i++) {
303 f1 = mview->visibleFlags[i];
305 IntGroupAdd(mview->tableCache, i, 1);
307 IntGroupAdd(mview->tableSelection, j, 1);
311 } else if (mview->tableIndex == kMainViewBondTableIndex) { /* Bonds */
312 for (i = j = 0; i < mol->nbonds; i++) {
313 n1 = mol->bonds[i * 2];
314 n2 = mol->bonds[i * 2 + 1];
315 f1 = mview->visibleFlags[n1];
316 f2 = mview->visibleFlags[n2];
317 if ((f1 & 1) != 0 && (f2 & 1) != 0) {
318 IntGroupAdd(mview->tableCache, i, 1);
319 if ((f1 & 2) != 0 && (f2 & 2) != 0)
320 IntGroupAdd(mview->tableSelection, j, 1);
324 } else if (mview->tableIndex == kMainViewAngleTableIndex) { /* Angles */
325 for (i = j = 0; i < mol->nangles; i++) {
326 n1 = mol->angles[i * 3];
327 n2 = mol->angles[i * 3 + 1];
328 n3 = mol->angles[i * 3 + 2];
329 f1 = mview->visibleFlags[n1];
330 f2 = mview->visibleFlags[n2];
331 f3 = mview->visibleFlags[n3];
332 if ((f1 & 1) != 0 && (f2 & 1) != 0 && (f3 & 1) != 0) {
333 IntGroupAdd(mview->tableCache, i, 1);
334 if ((f1 & 2) != 0 && (f2 & 2) != 0 && (f3 & 2) != 0)
335 IntGroupAdd(mview->tableSelection, j, 1);
339 } else if (mview->tableIndex == kMainViewDihedralTableIndex) { /* Dihedrals */
340 for (i = j = 0; i < mol->ndihedrals; i++) {
341 n1 = mol->dihedrals[i * 4];
342 n2 = mol->dihedrals[i * 4 + 1];
343 n3 = mol->dihedrals[i * 4 + 2];
344 n4 = mol->dihedrals[i * 4 + 3];
345 f1 = mview->visibleFlags[n1];
346 f2 = mview->visibleFlags[n2];
347 f3 = mview->visibleFlags[n3];
348 f4 = mview->visibleFlags[n4];
349 if ((f1 & 1) != 0 && (f2 & 1) != 0 && (f3 & 1) != 0 && (f4 & 1) != 0) {
350 IntGroupAdd(mview->tableCache, i, 1);
351 if ((f1 & 2) != 0 && (f2 & 2) != 0 && (f3 & 2) != 0 && (f4 & 2) != 0)
352 IntGroupAdd(mview->tableSelection, j, 1);
356 } else if (mview->tableIndex == kMainViewImproperTableIndex) { /* Impropers */
357 for (i = j = 0; i < mol->nimpropers; i++) {
358 n1 = mol->impropers[i * 4];
359 n2 = mol->impropers[i * 4 + 1];
360 n3 = mol->impropers[i * 4 + 2];
361 n4 = mol->impropers[i * 4 + 3];
362 f1 = mview->visibleFlags[n1];
363 f2 = mview->visibleFlags[n2];
364 f3 = mview->visibleFlags[n3];
365 f4 = mview->visibleFlags[n4];
366 if ((f1 & 1) != 0 && (f2 & 1) != 0 && (f3 & 1) != 0 && (f4 & 1) != 0) {
367 IntGroupAdd(mview->tableCache, i, 1);
368 if ((f1 & 2) != 0 && (f2 & 2) != 0 && (f3 & 2) != 0 && (f4 & 2) != 0)
369 IntGroupAdd(mview->tableSelection, j, 1);
374 /* Cache is left empty (not used) */
377 // } else if (mview->tableIndex == kMainViewParameterTableIndex || /* Parameter infos */
378 // mview->tableIndex == kMainViewUnitCellTableIndex) { /* Unit cell infos */
379 // /* Do nothing (tableCache will not be used) */
380 // } else if (mview->tableIndex == kMainViewMOTableIndex) { /* MO infos */
381 // /* Really no need to cache info, but create it anyway to simplify code */
382 // if (mol->bset != NULL && mol->bset->ncomps > 0)
383 // IntGroupAdd(mview->tableCache, 0, mol->bset->ncomps);
386 /* Clear internal selection flag */
387 for (i = 0; i < mol->natoms; i++) {
388 mview->visibleFlags[i] &= ~2;
391 /* Store the tableIndex value */
392 mview->cachedTableIndex = mview->tableIndex;
395 #pragma mark ====== 2D/3D transform operations ======
398 MainView_resizeToFit(MainView *mview)
404 if (mview == NULL || mview->mol == NULL)
406 if (mview->mol->natoms == 0) {
407 TrackballReset(mview->track);
410 MoleculeCenterOfMass(mview->mol, &p, NULL);
411 f[0] = -p.x / mview->dimension;
412 f[1] = -p.y / mview->dimension;
413 f[2] = -p.z / mview->dimension;
414 TrackballSetTranslate(mview->track, f);
417 r0: the longest distance from the center of mass
418 r0 > dimension: scale = -log(r0/dimension)/log(10) (negative)
419 r0 < dimension: scale = -log(atan2(r0, dimension*cot(15deg))*180deg/pi*2/30deg)/log(10) (positive)
425 double r0 = 0.0, r1, scale;
426 for (i = 0, ap = mview->mol->atoms; i < mview->mol->natoms; i++, ap = ATOM_NEXT(ap)) {
433 r0 /= mview->dimension;
437 scale = -log(atan2(r0, kCot15Deg) * kRad2Deg * 2 / 30.0) / kLog10;
439 scale = -log(r0) / kLog10;
440 TrackballSetScale(mview->track, scale);
443 MainViewCallback_setNeedsDisplay(mview, 1);
447 MainView_convertScreenPositionToObjectPosition(MainView *mview, const GLfloat *screenPos, double *objectPos)
450 GLint viewport[4], n;
452 GLdouble posX, posY, posZ;
453 float scale = mview->view_scale;
456 MainViewCallback_frame(mview, rect);
457 viewport[0] = viewport[1] = 0;
458 viewport[2] = (GLint)(rect[2] - rect[0]) * scale;
459 viewport[3] = (GLint)(rect[3] - rect[1]) * scale;
460 MainViewCallback_lockFocus(mview);
461 if (screenPos[2] >= 0.0)
464 glReadPixels(screenPos[0] * scale, screenPos[1] * scale, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
465 myGluUnProject(screenPos[0] * scale, screenPos[1] * scale, winZ, mview->modelview_matrix, mview->projection_matrix, viewport, &posX, &posY, &posZ);
467 MainViewCallback_unlockFocus(mview);
471 if (n != GL_NO_ERROR || winZ == 1.0)
478 MainView_convertObjectPositionToScreenPosition(MainView *mview, const double *objectPos, GLfloat *screenPos)
482 GLdouble objX, objY, objZ;
483 float scale = mview->view_scale;
486 MainViewCallback_frame(mview, rect);
487 viewport[0] = viewport[1] = 0;
488 viewport[2] = (GLint)(rect[2] - rect[0]) * scale;
489 viewport[3] = (GLint)(rect[3] - rect[1]) * scale;
490 myGluProject(objectPos[0], objectPos[1], objectPos[2], mview->modelview_matrix, mview->projection_matrix, viewport, &objX, &objY, &objZ);
491 if (glGetError() == GL_NO_ERROR) {
492 screenPos[0] = objX / scale;
493 screenPos[1] = objY / scale;
495 /* fprintf(stderr, "object(%.3f,%.3f,%.3f) screen(%.3f,%.3f,%.3f)\n", objectPos[0], objectPos[1], objectPos[2], screenPos[0], screenPos[1], screenPos[2]); */
501 MainView_findObjectAtPoint(MainView *mview, const float *mousePos, int *outIndex1, int *outIndex2, int mouseCheck, int ignoreExpandedAtoms)
504 double op[3], oq[3], pqlen, pqlen2;
505 Vector pq, pa, v1, r1, r2;
506 int i, natoms, nbonds;
509 const ElementPar *ep;
515 if (mview == NULL || mview->mol == NULL) {
516 *outIndex1 = *outIndex2 = -1;
521 screenPos[0] = mousePos[0];
522 screenPos[1] = mousePos[1];
524 if (MainView_convertScreenPositionToObjectPosition(mview, screenPos, op) == 0)
525 return 0; /* Nothing is here */
527 /* PQ is the part of the eyesight line in the visible area */
529 MainView_convertScreenPositionToObjectPosition(mview, screenPos, op);
531 MainView_convertScreenPositionToObjectPosition(mview, screenPos, oq);
532 pq.x = oq[0] - op[0];
533 pq.y = oq[1] - op[1];
534 pq.z = oq[2] - op[2];
535 pqlen2 = VecLength2(pq);
536 pqlen = sqrt(pqlen2);
537 natoms = mol->natoms;
540 for (i = 0; i < natoms; i++) {
542 double pq1len2, pq1len;
543 if (mouseCheck && i % 50 == 0 && MainViewCallback_mouseCheck(mview))
544 return 0; /* If mouse event is detected return immediately */
545 /* Examine if an atom is visible or not */
546 /* The distance of the atom center (A) from line PQ: */
547 /* d = |VecCross(PA,PQ)|/|PQ| */
548 /* z = VecDot(PA,PQ)/|PQ|^2 - sqrt(r^2 - d^2)/|PQ| */
549 ap = ATOM_AT_INDEX(mol->atoms, i);
552 if (MainView_isAtomHidden(mview, i))
554 if (ignoreExpandedAtoms && SYMOP_ALIVE(ap->symop))
557 /* if (mol->is_xtal_coord)
558 TransformVec(&r1, mol->cell->tr, &r1); */
562 if (mview->showEllipsoids && ap->aniso != NULL) {
563 /* Convert to ellipsoid principal axes */
565 Aniso *anp = ap->aniso;
566 MatrixInvert(m1, anp->pmat);
567 MatrixVec(&pq1, m1, &pq);
568 MatrixVec(&pa1, m1, &pa);
569 r = mview->probabilityScale;
570 pq1len2 = VecLength2(pq1);
571 pq1len = sqrt(pq1len2);
573 if (mview->showEllipsoids) {
574 r = biso2radius(ap->tempFactor) * mview->probabilityScale;
576 ep = &(gElementParameters[ap->atomicNumber]);
579 r = VDW_RADIUS(ep) * mview->atomRadius;
586 VecCross(v1, pa1, pq1);
587 d2 = VecLength2(v1) / pq1len2;
589 continue; /* Not visible */
590 z = VecDot(pa1, pq1) / pq1len2 - sqrt(r * r - d2) / pq1len;
591 if (z < 0.0 || z > 1.0)
592 continue; /* Out of viewing volume */
598 nbonds = mol->nbonds;
599 for (i = 0; i < nbonds; i++) {
600 Vector vx, vy, vz, vv, vp;
601 Double wb, wa, t, wx, blen;
602 if (mouseCheck && i % 50 == 0 && MainViewCallback_mouseCheck(mview))
603 return 0; /* If mouse event is detected return immediately */
604 /* Examine if a bond is visible or not */
605 ip = &(mol->bonds[i * 2]);
606 ap = ATOM_AT_INDEX(mol->atoms, ip[0]);
607 bp = ATOM_AT_INDEX(mol->atoms, ip[1]);
608 if (MainView_isAtomHidden(mview, ip[0]) || MainView_isAtomHidden(mview, ip[1]))
610 if (ignoreExpandedAtoms && SYMOP_ALIVE(ap->symop) && SYMOP_ALIVE(bp->symop))
612 /* vx/vy/vz is a orthonormal base in which AB parallels the x-axis
613 and AP in in the xy plane */
614 /* vp and vv is AP and PQ in that coordinate system */
617 /* if (mol->is_xtal_coord) {
618 TransformVec(&r1, mol->cell->tr, &r1);
619 TransformVec(&r2, mol->cell->tr, &r2);
625 blen = sqrt(VecLength2(vx));
631 VecCross(vz, vx, v1);
632 if (NormalizeVec(&vz, &vz))
634 VecCross(vy, vz, vx);
635 vp.x = VecDot(v1, vx);
636 vp.y = VecDot(v1, vy);
637 vp.z = VecDot(v1, vz);
638 vv.x = VecDot(pq, vx);
639 vv.y = VecDot(pq, vy);
640 vv.z = VecDot(pq, vz);
641 /* The bond surface is y^2 + z^2 = r^2, 0 <= x <= 1 */
642 /* The eyesight line is (x,y,z) = vp + t * vv, 0 <= t <= 1 */
643 /* The crossing point: t = (-vv.y*vp.y - sqrt((vv.y*vp.y)^2 - (vv.y^2+vv.z^2)(vp.y^2-r^2)))/(vv.y^2+vv.z^2) */
644 /* (Note that vp.z = 0 by definition) */
645 r = mview->bondRadius;
647 wa = vv.y * vv.y + vv.z * vv.z;
648 d2 = wb * wb - wa * (vp.y * vp.y - r * r);
650 continue; /* Not visible */
651 t = (-wb - sqrt(d2)) / wa;
653 continue; /* Out of visible volume */
654 wx = vp.x + t * vv.x;
655 if (wx < 0 || wx > blen)
656 continue; /* Outside of bond */
665 return (n1 >= 0 || n2 >= 0);
669 MainView_screenCenterPointOfAtom(MainView *mview, int index, float *outScreenPos)
672 const ElementPar *dp;
677 if (mview == NULL || mview->mol == NULL || index < 0 || index >= mview->mol->natoms)
679 ap = ATOM_AT_INDEX(mview->mol->atoms, index);
681 /* Camera position in object coordinates */
684 /* The atom position (in Cartesian) */
686 /* if (mview->mol->is_xtal_coord)
687 TransformVec(&v, mview->mol->cell->tr, &v); */
689 /* The vector from atom center to camera */
692 /* Get the surface point of the ellipsoid/sphere along the camera vector */
693 if (mview->showEllipsoids) {
695 Aniso *anp = ap->aniso;
698 MatrixInvert(m1, anp->pmat);
699 /* Convert the 'screen plane' vectors to ellipsoid principal axes */
701 VecCross(vx, mview->lookto, vy);
702 MatrixVec(&vx, m1, &vx);
703 MatrixVec(&vy, m1, &vy);
704 /* Touching point of the 'screen plane' to the ellipsoid */
705 VecCross(vz, vx, vy);
706 w = mview->probabilityScale / VecLength(vz) * 1.1;
708 MatrixVec(&vz, anp->pmat, &vz);
709 /* The crossing point of the camera vector with the touching plane */
710 w = fabs(VecDot(pv, vz) / VecLength2(pv));
713 w = mview->probabilityScale * biso2radius(ap->tempFactor) / VecLength(pv) * 1.1;
717 dp = &(gElementParameters[ap->atomicNumber]);
718 rad = VDW_RADIUS(dp) * mview->atomRadius;
719 w = rad / VecLength(pv) * 1.1;
724 /* Transform to screen coordinate */
728 return MainView_convertObjectPositionToScreenPosition(mview, p, outScreenPos);
732 MainView_getCamera(MainView *mview, Vector *outCamera, Vector *outLookAt, Vector *outUp)
737 *outCamera = mview->camera;
738 *outLookAt = mview->lookat;
743 #pragma mark ====== Draw model ======
748 static GLfloat pos[] = {0.0f, 0.0f, 1.0f, 0.0f};
749 glEnable(GL_LIGHTING);
751 /* On Mac OS 10.6, redefining the light position seems to be necessary
752 every time lighting is made enabled */
753 glMatrixMode(GL_MODELVIEW);
756 glLightfv(GL_LIGHT0, GL_POSITION, pos);
762 MainView_initializeOpenGL(void)
764 static GLfloat ambient[] = {0.6, 0.6, 0.6, 1.0}; // Some white ambient light.
765 static GLfloat diffuse[] = {1.0, 1.0, 1.0, 1.0}; // A white light.
767 glEnable(GL_DEPTH_TEST);
768 glEnable(GL_LIGHTING);
770 // Set the ambient light
771 glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
773 // Set the light and switch it on
774 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
779 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
782 /* Get orthogonal unit vectors */
784 getOrthogonalVectors(const GLfloat *ap, GLfloat *bp, GLfloat *cp)
787 ra = sqrt(ap[0] * ap[0] + ap[1] * ap[1] + ap[2] * ap[2]);
791 if (fabs(ap[0]) < fabs(ap[1])) {
792 if (fabs(ap[0]) < fabs(ap[2])) {
802 if (fabs(ap[1]) < fabs(ap[2])) {
812 rb = 1 / sqrt(bp[0] * bp[0] + bp[1] * bp[1] + bp[2] * bp[2]);
816 cp[0] = ra * (ap[1] * bp[2] - ap[2] * bp[1]);
817 cp[1] = ra * (ap[2] * bp[0] - ap[0] * bp[2]);
818 cp[2] = ra * (ap[0] * bp[1] - ap[1] * bp[0]);
819 /* printf("a = (%f, %f, %f) b = (%f, %f, %f) c = (%f, %f, %f)\n",
820 ap[0], ap[1], ap[2], bp[0], bp[1], bp[2], cp[0], cp[1], cp[2]); */
824 static GLfloat sSinCache[81];
825 static int sSinCacheSect = 0;
828 setSinCache(int sect)
835 if (n != sSinCacheSect) {
837 for (i = 0; i <= m * 5; i++)
838 sSinCache[i] = sin(3.14159265358979 * 2 / n * i);
844 drawCylinder(const GLfloat *a, const GLfloat *b, GLfloat r, int sect, int closed)
849 GLfloat d[3], v[3], w[3];
850 n = setSinCache(sect);
858 if (getOrthogonalVectors(d, v, w) == 0)
860 glBegin(GL_QUAD_STRIP);
861 for (i = 0; i <= n; i++) {
862 nx = v[0] * c[i] + w[0] * s[i];
863 ny = v[1] * c[i] + w[1] * s[i];
864 nz = v[2] * c[i] + w[2] * s[i];
865 glNormal3f(nx, ny, nz);
866 glVertex3f(a[0] + r * nx, a[1] + r * ny, a[2] + r * nz);
867 glVertex3f(b[0] + r * nx, b[1] + r * ny, b[2] + r * nz);
871 glBegin(GL_TRIANGLE_FAN);
872 glNormal3f(-d[0], -d[1], -d[2]);
873 for (i = 0; i <= n; i++) {
874 nx = v[0] * c[i] + w[0] * s[i];
875 ny = v[1] * c[i] + w[1] * s[i];
876 nz = v[2] * c[i] + w[2] * s[i];
877 glVertex3f(a[0] + r * nx, a[1] + r * ny, a[2] + r * nz);
880 glBegin(GL_TRIANGLE_FAN);
881 glNormal3f(d[0], d[1], d[2]);
882 for (i = 0; i <= n; i++) {
883 nx = v[0] * c[i] + w[0] * s[i];
884 ny = v[1] * c[i] + w[1] * s[i];
885 nz = v[2] * c[i] + w[2] * s[i];
886 glVertex3f(b[0] + r * nx, b[1] + r * ny, b[2] + r * nz);
893 drawCone(const GLfloat *a, const GLfloat *b, GLfloat r, int sect, int closed)
897 GLfloat d[3], v[3], w[3];
899 n = setSinCache(sect);
907 if (getOrthogonalVectors(d, v, w) == 0)
909 glBegin(GL_TRIANGLE_FAN);
913 NormalizeVec(&nv, &nv);
914 glNormal3f(nv.x, nv.y, nv.z);
915 glVertex3f(b[0], b[1], b[2]);
916 for (i = 0; i <= n; i++) {
917 nv.x = v[0] * c[i] + w[0] * s[i];
918 nv.y = v[1] * c[i] + w[1] * s[i];
919 nv.z = v[2] * c[i] + w[2] * s[i];
920 glNormal3f(nv.x, nv.y, nv.z);
921 p1.x = a[0] + r * nv.x;
922 p1.y = a[1] + r * nv.y;
923 p1.z = a[2] + r * nv.z;
924 glNormal3f(nv.x, nv.y, nv.z);
925 glVertex3f(p1.x, p1.y, p1.z);
929 glBegin(GL_TRIANGLE_FAN);
930 glNormal3f(d[0], d[1], d[2]);
931 for (i = 0; i <= n; i++) {
932 p1.x = a[0] + r * (v[0] * c[i] + w[0] * s[i]);
933 p1.y = a[1] + r * (v[1] * c[i] + w[1] * s[i]);
934 p1.z = a[2] + r * (v[2] * c[i] + w[2] * s[i]);
935 glVertex3f(p1.x, p1.y, p1.z);
942 drawSphere(const GLfloat *p, GLfloat r, int sect)
946 n = setSinCache(sect);
951 for (i = 0; i <= n; i++) {
952 glBegin(GL_QUAD_STRIP);
953 for (j = 1; j <= n / 2 - 1; j++) {
954 glNormal3f(s[j] * c[i], s[j] * s[i], c[j]);
955 glVertex3f(r * s[j] * c[i] + p[0], r * s[j] * s[i] + p[1], r * c[j] + p[2]);
956 glNormal3f(s[j] * c[i+1], s[j] * s[i+1], c[j]);
957 glVertex3f(r * s[j] * c[i+1] + p[0], r * s[j] * s[i+1] + p[1], r * c[j] + p[2]);
961 glBegin(GL_TRIANGLE_FAN);
963 glVertex3f(p[0], p[1], r + p[2]);
964 for (i = n; i >= 0; i--) {
965 glNormal3f(s[1] * c[i], s[1] * s[i], c[1]);
966 glVertex3f(r * s[1] * c[i] + p[0], r * s[1] * s[i] + p[1], r * c[1] + p[2]);
969 glBegin(GL_TRIANGLE_FAN);
970 glNormal3f(0, 0, -1);
971 glVertex3f(p[0], p[1], -r + p[2]);
972 for (i = 0; i <= n; i++) {
973 glNormal3f(s[1] * c[i], s[1] * s[i], -c[1]);
974 glVertex3f(r * s[1] * c[i] + p[0], r * s[1] * s[i] + p[1], -r * c[1] + p[2]);
980 drawEllipsoid(const GLfloat *p, const GLfloat *v1, const GLfloat *v2, const GLfloat *v3, int sect)
983 static const GLfloat origin[3] = {0, 0, 0};
984 mat[0] = v1[0]; mat[1] = v1[1]; mat[2] = v1[2]; mat[3] = 0;
985 mat[4] = v2[0]; mat[5] = v2[1]; mat[6] = v2[2]; mat[7] = 0;
986 mat[8] = v3[0]; mat[9] = v3[1]; mat[10] = v3[2]; mat[11] = 0;
987 mat[12] = p[0]; mat[13] = p[1]; mat[14] = p[2]; mat[15] = 1;
988 glMatrixMode(GL_MODELVIEW);
991 glEnable(GL_NORMALIZE);
992 // glutSolidSphere(1, sect, sect); /* Is this faster than my code? */
993 drawSphere(origin, 1, sect);
994 glDisable(GL_NORMALIZE);
999 temporarySelection(MainView *mview, int flags, int clickCount, int ignoreExpandedAtoms)
1005 natoms = mview->mol->natoms;
1006 selectFlags = (char *)calloc(sizeof(char), natoms);
1007 if (selectFlags == NULL)
1009 if (clickCount > 0) {
1011 if (MainView_findObjectAtPoint(mview, mview->dragStartPos, &n1, &n2, 0, 0)) {
1012 if (n1 >= 0 && n1 < natoms)
1013 selectFlags[n1] = 1;
1014 if (n2 >= 0 && n2 < natoms)
1015 selectFlags[n2] = 1;
1018 if (mview->dragStartPos[0] < mview->dragEndPos[0]) {
1019 rect[0] = mview->dragStartPos[0];
1020 rect[2] = mview->dragEndPos[0];
1022 rect[0] = mview->dragEndPos[0];
1023 rect[2] = mview->dragStartPos[0];
1025 if (mview->dragStartPos[1] < mview->dragEndPos[1]) {
1026 rect[1] = mview->dragStartPos[1];
1027 rect[3] = mview->dragEndPos[1];
1029 rect[1] = mview->dragEndPos[1];
1030 rect[3] = mview->dragStartPos[1];
1032 for (i = 0; i < natoms; i++) {
1033 ap = ATOM_AT_INDEX(mview->mol->atoms, i);
1036 if (MainView_isAtomHidden(mview, i))
1038 if (ignoreExpandedAtoms && SYMOP_ALIVE(ap->symop))
1040 if (mview->draggingMode == kMainViewSelectingRegion) {
1041 /* Check if this atom is within the selection rectangle */
1042 double objectPos[3];
1043 GLfloat screenPos[3];
1046 /* if (mview->mol->is_xtal_coord)
1047 TransformVec(&r1, mview->mol->cell->tr, &r1); */
1048 objectPos[0] = r1.x;
1049 objectPos[1] = r1.y;
1050 objectPos[2] = r1.z;
1051 if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos) && screenPos[0] >= rect[0] && screenPos[0] <= rect[2] && screenPos[1] >= rect[1] && screenPos[1] <= rect[3])
1053 else selectFlags[i] = 0;
1057 if (flags & kShiftKeyMask) {
1058 for (i = 0; i < natoms; i++)
1059 selectFlags[i] ^= (MoleculeIsAtomSelected(mview->mol, i) != 0);
1065 drawGraphite(MainView *mview)
1067 static GLfloat sDarkCyanColor[] = {0, 0.75, 0.75, 1};
1069 MDGraphiteArena *graphite;
1070 Vector xaxis, yaxis, zaxis, origin;
1072 int i, j, i0, i1, j0, j1, ir;
1073 Double x, dx, y, dy, xx, yy;
1075 if ((arena = mview->mol->arena) != NULL && (graphite = arena->graphite) != NULL) {
1076 graphite_get_axes(graphite, &origin, &xaxis, &yaxis, &zaxis, &R);
1078 origin.x = origin.y = origin.z = 0.0;
1079 xaxis.x = yaxis.y = zaxis.z = 1.0;
1080 xaxis.y = xaxis.z = yaxis.x = yaxis.z = zaxis.x = zaxis.y = 0.0;
1083 i0 = -(mview->showGraphite / 2) - 1;
1084 i1 = i0 + mview->showGraphite + 1;
1085 j0 = -(mview->showGraphite / 2);
1086 j1 = j0 + mview->showGraphite;
1088 dy = 0.866025403784439 * R;
1089 glDisable(GL_LIGHTING);
1090 glColor3fv(sDarkCyanColor);
1091 for (i = i0; i <= i1; i++) {
1092 for (j = j0; j <= j1; j++) {
1094 ir = (i % 2 == 0 ? 0 : 1);
1096 y = (2 * j + ir) * dy;
1099 p[0] = xaxis.x * xx + yaxis.x * y + origin.x;
1100 p[1] = xaxis.y * xx + yaxis.y * y + origin.y;
1101 p[2] = xaxis.z * xx + yaxis.z * y + origin.z;
1103 p[3] = xaxis.x * xx + yaxis.x * yy + origin.x;
1104 p[4] = xaxis.y * xx + yaxis.y * yy + origin.y;
1105 p[5] = xaxis.z * xx + yaxis.z * yy + origin.z;
1107 p[6] = xaxis.x * xx + yaxis.x * yy + origin.x;
1108 p[7] = xaxis.y * xx + yaxis.y * yy + origin.y;
1109 p[8] = xaxis.z * xx + yaxis.z * yy + origin.z;
1111 p[9] = xaxis.x * xx + yaxis.x * y + origin.x;
1112 p[10] = xaxis.y * xx + yaxis.y * y + origin.y;
1113 p[11] = xaxis.z * xx + yaxis.z * y + origin.z;
1117 if ((ir == 0 && j == j0) || (ir == 1 && j == j1))
1119 } else if (i == i1) {
1121 if ((ir == 0 && j == j0) || (ir == 1 && j == j1))
1123 } else if (j == j1) {
1126 } else if (i == i0 + 1) {
1128 } else if (i == i1 - 1) {
1151 static GLfloat sRedColor[] = {1, 0, 0, 1};
1154 drawAtom(MainView *mview, int i1, int selected, const Vector *dragOffset, const Vector *periodicOffset)
1157 const ElementPar *dp;
1166 Transform *trp = NULL;
1167 int natoms = mview->mol->natoms;
1169 /* Extra 2 atoms for the bond being newly created */
1170 if (mview->draggingMode != kMainViewCreatingBond)
1172 if (mview->tempAtoms[i1 - natoms] >= 0)
1173 return; /* Already drawn */
1176 r1 = mview->tempAtomPos[i1 - natoms];
1179 ap = ATOM_AT_INDEX(mview->mol->atoms, i1);
1182 an1 = ap->atomicNumber;
1184 strncpy(label, ap->aname, 4);
1186 if (SYMOP_ALIVE(ap->symop))
1189 if (!mview->showHydrogens && an1 == 1)
1191 if (!mview->showDummyAtoms && an1 == 0)
1193 if (!mview->showExpandedAtoms && expanded)
1195 if (ap != NULL && (ap->exflags & kAtomHiddenFlag))
1197 dp = &(gElementParameters[an1]);
1201 memcpy(rgba, sRedColor, sizeof(rgba));
1203 rgba[0] = dp->red / 65535.0;
1204 rgba[1] = dp->green / 65535.0;
1205 rgba[2] = dp->blue / 65535.0;
1208 if (expanded || periodicOffset != NULL) {
1213 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
1214 if (periodicOffset != NULL)
1215 VecInc(r1, *periodicOffset);
1216 p[0] = r1.x; p[1] = r1.y; p[2] = r1.z;
1217 if (mview->draggingMode == kMainViewDraggingSelectedAtoms && selected) {
1218 p[0] += dragOffset->x;
1219 p[1] += dragOffset->y;
1220 p[2] += dragOffset->z;
1222 rad = VDW_RADIUS(dp) * mview->atomRadius;
1223 if (mview->showEllipsoids) {
1224 if (ap != NULL && ap->aniso != NULL) {
1229 MatrixMul(pmat2, mview->mol->cell->rtr, ap->aniso->pmat);
1230 MatrixMul(pmat2, *((Mat33 *)trp), pmat2);
1231 MatrixMul(pmat2, mview->mol->cell->tr, pmat2);
1232 for (i = 0; i < 9; i++)
1233 elip[i] = pmat2[i] * mview->probabilityScale;
1235 for (i = 0; i < 9; i++)
1236 elip[i] = ap->aniso->pmat[i] * mview->probabilityScale;
1238 drawEllipsoid(p, elip, elip+3, elip+6, mview->atomResolution * 3 / 2); /* Use higher resolution than spheres */
1241 // Recalculate radius from temperature factor
1242 rad = biso2radius(ap->tempFactor);
1243 rad *= mview->probabilityScale;
1245 drawSphere(p, rad, mview->atomResolution);
1248 drawSphere(p, rad, mview->atomResolution);
1253 if (MainView_convertObjectPositionToScreenPosition(mview, pp, p + 3)) {
1254 /* fprintf(stderr, "atom %d: {%f, %f, %f}\n", i1, p[3], p[4], p[5]); */
1256 fp[0] = p[3]; fp[1] = p[4]; fp[2] = p[5];
1257 MainViewCallback_drawLabel(mview, fp, label);
1262 drawBond(MainView *mview, int i1, int i2, int selected, int selected2, int draft, const Vector *dragOffset, const Vector *periodicOffset, int isAnchorBond)
1264 const ElementPar *dp;
1272 float rad_mul = 1.0;
1273 float alpha_mul = 1.0;
1274 int natoms = mview->mol->natoms;
1275 expanded[0] = expanded[1] = 0;
1276 anchor[0] = anchor[1] = 0;
1278 for (i = 0; i < 2; i++) {
1280 in = (i == 0 ? i1 : i2);
1281 if (in >= natoms && in < natoms + 2) {
1282 if (mview->tempAtoms[in - natoms] >= 0) {
1283 ap = ATOM_AT_INDEX(mview->mol->atoms, mview->tempAtoms[in - natoms]);
1284 an[i] = ap->atomicNumber;
1288 r[i] = mview->tempAtomPos[in - natoms];
1292 ap = ATOM_AT_INDEX(mview->mol->atoms, in);
1293 an[i] = ap->atomicNumber;
1295 if (SYMOP_ALIVE(ap->symop))
1297 if (ap->anchor != NULL)
1300 if (!mview->showHydrogens && an[i] == 1)
1302 if (!mview->showDummyAtoms && an[i] == 0)
1304 if (!mview->showExpandedAtoms && expanded[i])
1306 if (ap != NULL && (ap->exflags & kAtomHiddenFlag))
1310 if (periodicOffset != NULL) {
1311 VecInc(r[0], *periodicOffset);
1312 VecInc(r[1], *periodicOffset);
1315 if (anchor[0] + anchor[1] == 2)
1317 if (anchor[0] + anchor[1] == 1)
1318 rad_mul = (isAnchorBond ? 0.3 : 0.6);
1320 dp = &(gElementParameters[an[0]]);
1323 if (selected && selected2) {
1324 memcpy(rgba, sRedColor, sizeof(rgba));
1326 rgba[0] = dp->red / 65535.0;
1327 rgba[1] = dp->green / 65535.0;
1328 rgba[2] = dp->blue / 65535.0;
1331 rgba[3] *= alpha_mul;
1332 if (expanded[0] || periodicOffset != NULL) {
1337 if (mview->draggingMode == kMainViewDraggingSelectedAtoms) {
1339 VecInc(r[0], *dragOffset);
1341 VecInc(r[1], *dragOffset);
1343 p[0] = r[0].x; p[1] = r[0].y; p[2] = r[0].z;
1344 p[3] = (r[1].x + p[0]) * 0.5;
1345 p[4] = (r[1].y + p[1]) * 0.5;
1346 p[5] = (r[1].z + p[2]) * 0.5;
1348 glColor3f(rgba[0], rgba[1], rgba[2]);
1350 glVertex3f(p[0], p[1], p[2]);
1351 glVertex3f(p[3], p[4], p[5]);
1354 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
1355 drawCylinder(p, p + 3, mview->bondRadius * rad_mul, mview->bondResolution, 0);
1357 dp = &(gElementParameters[an[1]]);
1360 if (!selected || !selected2) {
1361 rgba[0] = dp->red / 65535.0;
1362 rgba[1] = dp->green / 65535.0;
1363 rgba[2] = dp->blue / 65535.0;
1366 rgba[3] *= alpha_mul;
1367 if (expanded[1] || periodicOffset != NULL) {
1372 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
1373 p[0] = r[1].x; p[1] = r[1].y; p[2] = r[1].z;
1375 glColor3f(rgba[0], rgba[1], rgba[2]);
1377 glVertex3f(p[0], p[1], p[2]);
1378 glVertex3f(p[3], p[4], p[5]);
1381 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
1382 drawCylinder(p, p + 3, mview->bondRadius * rad_mul, 8, 0);
1386 /* Calculate drag offset during moving the selection. */
1388 calcDragOffset(MainView *mview, Vector *outVector)
1391 if (mview->draggingMode == kMainViewDraggingSelectedAtoms
1392 && MainView_convertScreenPositionToObjectPosition(mview, mview->dragStartPos, p)
1393 && MainView_convertScreenPositionToObjectPosition(mview, mview->dragEndPos, p + 3)) {
1394 outVector->x = p[3] - p[0];
1395 outVector->y = p[4] - p[1];
1396 outVector->z = p[5] - p[2];
1398 outVector->x = outVector->y = outVector->z = 0;
1404 drawSurface(MainView *mview)
1409 if (mview->mol == NULL || mview->mol->mcube == NULL || mview->mol->mcube->hidden != 0)
1411 mc = mview->mol->mcube;
1412 for (sn = 0; sn <= 1; sn++) {
1413 if (mc->c[sn].ntriangles == 0)
1415 for (i = 0; i < 4; i++)
1416 rgba[i] = mc->c[sn].rgba[i];
1417 k = (sn == 0 ? -1 : 1);
1418 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
1419 glBegin(GL_TRIANGLES);
1420 for (i = 0; mc->c[sn].triangles[i] >= 0; i++) {
1421 MCubePoint *mcp = mc->c[sn].cubepoints + mc->c[sn].triangles[i];
1422 glNormal3f(mcp->grad[0] * k, mcp->grad[1] * k, mcp->grad[2] * k);
1423 glVertex3f(mcp->pos[0], mcp->pos[1], mcp->pos[2]);
1430 drawModel(MainView *mview)
1433 int i, natoms, nbonds;
1434 int amin, amax, bmin, bmax, cmin, cmax, da, db, dc;
1436 Double atomRadius, bondRadius;
1437 Vector periodicOffset;
1439 int selected, selected2;
1442 int draft = mview->lineMode;
1443 /* static Double gray[] = {0.8, 0.8, 0.8, 1}; */
1445 atomRadius = mview->atomRadius;
1446 bondRadius = mview->bondRadius;
1448 natoms = mol->natoms;
1449 /* if (natoms == 0) {
1453 if (mview->draggingMode == kMainViewSelectingRegion)
1454 selectFlags = temporarySelection(mview, mview->modifierFlags, 0, 0);
1455 else selectFlags = NULL;
1457 if (mview->draggingMode == kMainViewDraggingSelectedAtoms)
1458 calcDragOffset(mview, &mview->dragOffset);
1459 else mview->dragOffset.x = mview->dragOffset.y = mview->dragOffset.z = 0;
1461 if (mview->showGraphite > 0 && mview->showGraphiteFlag) {
1462 drawGraphite(mview);
1465 amin = amax = bmin = bmax = cmin = cmax = 0;
1466 if (mview->showExpandedAtoms && mol->cell != NULL) {
1467 if (mol->cell->flags[0] && mview->showPeriodicImageFlag) {
1468 amin = mview->showPeriodicImage[0];
1469 amax = mview->showPeriodicImage[1];
1471 if (mol->cell->flags[1] && mview->showPeriodicImageFlag) {
1472 bmin = mview->showPeriodicImage[2];
1473 bmax = mview->showPeriodicImage[3];
1475 if (mol->cell->flags[2] && mview->showPeriodicImageFlag) {
1476 cmin = mview->showPeriodicImage[4];
1477 cmax = mview->showPeriodicImage[5];
1479 axes = mol->cell->axes;
1485 for (da = amin; da <= amax; da++) {
1486 for (db = bmin; db <= bmax; db++) {
1487 for (dc = cmin; dc <= cmax; dc++) {
1488 original = (da == 0 && db == 0 && dc == 0);
1490 VecScale(periodicOffset, axes[0], da);
1491 VecScaleInc(periodicOffset, axes[1], db);
1492 VecScaleInc(periodicOffset, axes[2], dc);
1494 for (i = 0, ap = mview->mol->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
1495 if (mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
1496 /* Mouse event is detected */
1500 if (mview->draggingMode == kMainViewCreatingBond && (i == mview->tempAtoms[0] || i == mview->tempAtoms[1] || i >= natoms))
1501 selected = 1; /* extra atoms */
1502 else if (selectFlags != NULL)
1503 selected = selectFlags[i];
1505 selected = MoleculeIsAtomSelected(mview->mol, i);
1506 drawAtom(mview, i, selected, &mview->dragOffset, (original ? NULL : &periodicOffset));
1507 if (ap->anchor != NULL) {
1508 /* Draw anchor bonds */
1510 Int *cp = AtomConnectData(&ap->anchor->connect);
1511 for (j = 0; j < ap->anchor->connect.count; j++) {
1513 if (selectFlags != NULL)
1514 selected2 = selectFlags[k];
1516 selected2 = MoleculeIsAtomSelected(mview->mol, k);
1517 drawBond(mview, i, k, selected, selected2, draft, &mview->dragOffset, (original ? NULL : &periodicOffset), 1);
1526 drawAtom(mview, natoms, 1, &mview->dragOffset, NULL);
1527 drawAtom(mview, natoms + 1, 1, &mview->dragOffset, NULL);
1529 /* Expanded atoms */
1530 /* if (mview->showExpandedAtoms) {
1531 for (i = 0; i < mview->mol->nexatoms; i++) {
1532 if (mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
1533 // Mouse event is detected
1538 drawAtom(mview, -i-1, (selectFlags == NULL ? 0 : selectFlags[i]), &dragOffset, (original ? NULL : &periodicOffset));
1547 nbonds = mol->nbonds;
1549 glDisable(GL_LIGHTING);
1550 for (da = amin; da <= amax; da++) {
1551 for (db = bmin; db <= bmax; db++) {
1552 for (dc = cmin; dc <= cmax; dc++) {
1553 original = (da == 0 && db == 0 && dc == 0);
1555 VecScale(periodicOffset, axes[0], da);
1556 VecScaleInc(periodicOffset, axes[1], db);
1557 VecScaleInc(periodicOffset, axes[2], dc);
1560 for (i = 0; i < nbonds; i++) {
1562 if (draft == 0 && mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
1563 /* Mouse event is detected */
1565 glDisable(GL_LIGHTING);
1568 n1 = mview->mol->bonds[i * 2];
1569 n2 = mview->mol->bonds[i * 2 + 1];
1570 if (selectFlags == NULL) {
1571 selected = MoleculeIsAtomSelected(mview->mol, n1);
1572 selected2 = MoleculeIsAtomSelected(mview->mol, n2);
1574 selected = selectFlags[n1];
1575 selected2 = selectFlags[n2];
1577 drawBond(mview, n1, n2, selected, selected2, draft, &mview->dragOffset, (original ? NULL : &periodicOffset), 0);
1581 if (original && mview->draggingMode == kMainViewCreatingBond) {
1582 drawBond(mview, natoms, natoms + 1, 1, 1, draft, &mview->dragOffset, NULL, 0);
1585 /* Expanded bonds */
1586 /* for (i = 0; i < mview->mol->nexbonds; i++) {
1588 if (draft == 0 && mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
1589 // Mouse event is detected
1591 glDisable(GL_LIGHTING);
1594 n1 = mview->mol->exbonds[i * 2];
1595 n2 = mview->mol->exbonds[i * 2 + 1];
1600 if (selectFlags == NULL) {
1601 selected = MoleculeIsAtomSelected(mview->mol, n1);
1602 selected2 = MoleculeIsAtomSelected(mview->mol, n2);
1604 selected = selectFlags[n1];
1605 selected2 = selectFlags[n2];
1607 drawBond(mview, mview->mol->exbonds[i * 2], mview->mol->exbonds[i * 2 + 1], selected, selected2, draft, &dragOffset, (original ? NULL : &periodicOffset));
1616 if (selectFlags != NULL)
1621 drawGraphics(MainView *mview)
1625 for (i = 0; i < mview->ngraphics; i++) {
1626 g = &mview->graphics[i];
1627 if (g->visible == 0)
1629 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, g->rgba);
1631 case kMainViewGraphicLine:
1632 glDisable(GL_LIGHTING);
1633 glColor4fv(g->rgba);
1634 glBegin(GL_LINE_STRIP);
1635 for (j = 0; j < g->npoints; j++) {
1636 if (g->points[j * 3] >= kInvalidFloat)
1638 glVertex3fv(&g->points[j * 3]);
1643 case kMainViewGraphicPoly: {
1644 /* Vector v0, v1, v2, v3; */
1645 glBegin(GL_TRIANGLE_FAN);
1646 /* v1.x = g->points[0] - g->points[g->npoints - 3];
1647 v1.y = g->points[1] - g->points[g->npoints - 2];
1648 v1.z = g->points[2] - g->points[g->npoints - 1];
1650 for (j = 0; j < g->npoints; j++) {
1651 /* v2.x = g->points[j * 3 + 3] - g->points[j * 3];
1652 v2.y = g->points[j * 3 + 4] - g->points[j * 3 + 1];
1653 v2.z = g->points[j * 3 + 5] - g->points[j * 3 + 2];
1654 VecCross(v3, v1, v2);
1655 if (NormalizeVec(&v3, &v3) == 0)
1656 glNormal3f(v3.x, v3.y, v3.z); */
1657 glNormal3fv(&g->normals[j * 3]);
1658 glVertex3fv(&g->points[j * 3]);
1662 /* VecCross(v3, v1, v0);
1663 if (NormalizeVec(&v3, &v3) == 0)
1664 glNormal3f(v3.x, v3.y, v3.z); */
1665 glNormal3fv(g->normals);
1666 glVertex3fv(g->points);
1671 case kMainViewGraphicCylinder:
1672 drawCylinder(g->points, g->points + 3, g->points[6], 15, g->closed);
1674 case kMainViewGraphicCone:
1675 drawCone(g->points, g->points + 3, g->points[6], 15, g->closed);
1677 case kMainViewGraphicEllipsoid:
1678 drawEllipsoid(g->points, g->points + 3, g->points + 6, g->points + 9, 8);
1685 drawUnitCell(MainView *mview)
1687 GLfloat a[3], b[3], c[3], ab[3], bc[3], ca[3], abc[3];
1690 if (!mview->showUnitCell || (cp = mview->mol->cell) == NULL)
1692 origin[0] = cp->origin.x;
1693 origin[1] = cp->origin.y;
1694 origin[2] = cp->origin.z;
1695 a[0] = cp->axes[0].x + origin[0];
1696 a[1] = cp->axes[0].y + origin[1];
1697 a[2] = cp->axes[0].z + origin[2];
1698 b[0] = cp->axes[1].x + origin[0];
1699 b[1] = cp->axes[1].y + origin[1];
1700 b[2] = cp->axes[1].z + origin[2];
1701 c[0] = cp->axes[2].x + origin[0];
1702 c[1] = cp->axes[2].y + origin[1];
1703 c[2] = cp->axes[2].z + origin[2];
1705 ab[0] = a[0] + b[0]; ab[1] = a[1] + b[1]; ab[2] = a[2] + b[2];
1706 bc[0] = b[0] + c[0]; bc[1] = b[1] + c[1]; bc[2] = b[2] + c[2];
1707 ca[0] = c[0] + a[0]; ca[1] = c[1] + a[1]; ca[2] = c[2] + a[2];
1708 abc[0] = a[0] + bc[0]; abc[1] = a[1] + bc[1]; abc[2] = a[2] + bc[2];
1710 glDisable(GL_LIGHTING);
1711 glColor3f(0.75, 0.2, 0.0);
1713 glVertex3fv(origin);
1715 glVertex3fv(origin);
1717 glVertex3fv(origin);
1742 /* For debugging surface view */
1744 drawCubeBoundary(MainView *mview)
1749 if ((mc = mview->mol->mcube) == NULL || mc->showbox == 0)
1754 px = ox + mc->dx * mc->nx;
1755 py = oy + mc->dy * mc->ny;
1756 pz = oz + mc->dz * mc->nz;
1757 glDisable(GL_LIGHTING);
1758 glColor3f(0.0, 0.5, 0.75);
1760 glVertex3f(ox, oy, oz);
1761 glVertex3f(px, oy, oz);
1762 glVertex3f(ox, oy, oz);
1763 glVertex3f(ox, py, oz);
1764 glVertex3f(ox, oy, oz);
1765 glVertex3f(ox, oy, pz);
1766 glVertex3f(px, oy, oz);
1767 glVertex3f(px, py, oz);
1768 glVertex3f(px, oy, oz);
1769 glVertex3f(px, oy, pz);
1770 glVertex3f(ox, py, oz);
1771 glVertex3f(px, py, oz);
1772 glVertex3f(ox, py, oz);
1773 glVertex3f(ox, py, pz);
1774 glVertex3f(ox, oy, pz);
1775 glVertex3f(px, oy, pz);
1776 glVertex3f(ox, oy, pz);
1777 glVertex3f(ox, py, pz);
1778 glVertex3f(px, py, oz);
1779 glVertex3f(px, py, pz);
1780 glVertex3f(px, oy, pz);
1781 glVertex3f(px, py, pz);
1782 glVertex3f(ox, py, pz);
1783 glVertex3f(px, py, pz);
1790 drawRotationCenter(MainView *mview)
1792 GLfloat ps[3], pe[3], col[4];
1793 double fd[2]; /* Fovy and distance */
1794 double tr[3]; /* Translation */
1796 if (mview == NULL || !mview->showRotationCenter)
1798 TrackballGetTranslate(mview->track, tr);
1799 TrackballGetPerspective(mview->track, fd);
1800 tr[0] *= -mview->dimension;
1801 tr[1] *= -mview->dimension;
1802 tr[2] *= -mview->dimension;
1803 r = fd[1] * mview->dimension * tan(fd[0] * 0.5 * kDeg2Rad) * 0.1;
1808 col[0] = col[1] = col[2] = 0.5; col[3] = 1.0;
1809 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
1810 drawSphere(ps, rr, 8);
1815 col[0] = 1.0; col[1] = col[2] = 0.0;
1816 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
1817 drawSphere(ps, rr, 8);
1818 drawSphere(pe, rr, 8);
1819 drawCylinder(ps, pe, rr, 8, 0);
1824 col[1] = 1.0; col[0] = col[2] = 0.0;
1825 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
1826 drawSphere(ps, rr, 8);
1827 drawSphere(pe, rr, 8);
1828 drawCylinder(ps, pe, rr, 8, 0);
1833 col[2] = 1.0; col[0] = col[1] = 0.0;
1834 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
1835 drawSphere(ps, rr, 8);
1836 drawSphere(pe, rr, 8);
1837 drawCylinder(ps, pe, rr, 8, 0);
1841 compareLabelByDepth(const void *ap, const void *bp)
1843 Double dz = ((const LabelRecord *)bp)->pos.z - ((const LabelRecord *)ap)->pos.z;
1852 drawLabels(MainView *mview)
1854 /* Transform *trp; */
1859 if (mview->nlabels == 0)
1862 /* mview->sortedLabels = (LabelRecord **)calloc(sizeof(LabelRecord *), mview->nlabels);
1863 if (mview->sortedLabels == NULL)
1866 /* if (mview->mol->is_xtal_coord)
1867 trp = &(mview->mol->cell->tr);
1870 /* Get the screen coordinates of the labels */
1872 for (i = 0; i < mview->mol->natoms; i++) {
1873 float scrp[3], f[3];
1874 ap = ATOM_AT_INDEX(mview->mol->atoms, i);
1875 if (ap->exflags & kAtomHiddenFlag)
1877 if (!mview->showHydrogens && ap->atomicNumber == 1)
1879 if (!mview->showDummyAtoms && ap->atomicNumber == 0)
1881 if (ap->labelid <= 0 || ap->labelid > mview->nlabels)
1883 lp = mview->labels + (ap->labelid - 1);
1884 MainView_screenCenterPointOfAtom(mview, lp->idx1, scrp);
1885 /* if (lp->idx2 >= 0) {
1889 TransformVec(&r1, *trp, &r1);
1890 bp = ATOM_AT_INDEX(mview->mol->atoms, lp->idx2);
1891 if (bp->exflags & kAtomHiddenFlag)
1895 TransformVec(&r2, *trp, &r2);
1896 r1.x = (r1.x + r2.x) * 0.5;
1897 r1.y = (r1.y + r2.y) * 0.5;
1898 r1.z = (r1.z + r2.z) * 0.5;
1902 MainView_convertObjectPositionToScreenPosition(mview, objp, scrp);
1904 lp->pos.x = scrp[0];
1905 lp->pos.y = scrp[1];
1906 lp->pos.z = scrp[2];
1907 MainViewCallback_labelSize(lp->label, f);
1908 f[0] = floor(lp->pos.x - f[0] * 0.5);
1909 f[1] = -floor(lp->pos.y + f[1] * 0.5);
1911 /* fprintf(stderr, "label position (%d) = {%f, %f, %f}\n", i, f[0], f[1], f[2]); */
1912 MainViewCallback_drawLabelAtPoint(lp->label, f);
1913 /* mview->sortedLabels[nlabels++] = lp;
1914 if (nlabels >= mview->nlabels)
1918 /* // Sort labels by z coordinates (descending order)
1919 qsort(mview->sortedLabels, nlabels, sizeof(LabelRecord *), compareLabelByDepth);
1922 for (i = 0; i < nlabels; i++) {
1923 LabelRecord *lp = mview->sortedLabels[i];
1925 MainViewCallback_labelSize(lp->label, f);
1926 f[0] = floor(lp->pos.x - f[0] * 0.5);
1927 f[1] = -floor(lp->pos.y + f[1] * 0.5);
1929 MainViewCallback_drawLabelAtPoint(lp->label, f);
1935 MainView_drawModel(MainView *mview)
1937 double w[4], dimension, distance;
1938 float frame[4], width, height, scale;
1947 if (!mview->isInitialized) {
1948 MainView_initializeOpenGL();
1949 mview->view_scale = MainViewCallback_getContentScaleFactor(mview);
1950 mview->isInitialized = 1;
1953 dimension = mview->dimension;
1955 if (mview->offline_scale == 0.0)
1956 scale = mview->view_scale;
1958 scale = mview->offline_scale;
1960 MainViewCallback_frame(mview, frame);
1961 width = frame[2] - frame[0];
1962 height = frame[3] - frame[1];
1964 if (mview->offline_width > 0)
1965 width = mview->offline_width;
1966 if (mview->offline_height > 0)
1967 height = mview->offline_height;
1972 glViewport(0, 0, width, height);
1974 /* Clear the buffer */
1975 glClearColor(mview->background_color[0], mview->background_color[1], mview->background_color[2], mview->background_color[3]);
1976 glClear(GL_COLOR_BUFFER_BIT |
1977 GL_DEPTH_BUFFER_BIT);
1979 if (mview->mol == NULL)
1982 /* Set up the projection */
1983 glMatrixMode(GL_PROJECTION);
1985 TrackballGetPerspective(mview->track, w);
1986 distance = w[1] * dimension;
1987 mview->perspective_vector[0] = w[0];
1988 mview->perspective_vector[1] = width / height;
1989 mview->perspective_vector[2] = dimension;
1990 mview->perspective_vector[3] = distance + 200.0 * dimension;
1991 myGluPerspective(mview->perspective_vector[0], mview->perspective_vector[1], mview->perspective_vector[2], mview->perspective_vector[3]);
1993 /* Set up the model view */
1994 glMatrixMode(GL_MODELVIEW);
1996 glTranslatef(0.0, 0.0, -distance);
1997 TrackballGetRotate(mview->track, w);
1998 glRotatef(w[0], w[1], w[2], w[3]);
1999 TrackballGetTranslate(mview->track, w);
2003 glTranslatef(w[0], w[1], w[2]);
2004 mview->lookat.x = -w[0];
2005 mview->lookat.y = -w[1];
2006 mview->lookat.z = -w[2];
2008 MainViewCallback_clearLabels(mview);
2011 drawUnitCell(mview);
2012 drawCubeBoundary(mview);
2013 drawRotationCenter(mview);
2014 drawGraphics(mview);
2016 /* Get important matrices and vectors */
2017 glGetDoublev(GL_MODELVIEW_MATRIX, mview->modelview_matrix);
2018 glGetDoublev(GL_PROJECTION_MATRIX, mview->projection_matrix);
2019 pp = mview->modelview_matrix;
2020 mtr[0] = pp[0]; mtr[1] = pp[1]; mtr[2] = pp[2];
2021 mtr[3] = pp[4]; mtr[4] = pp[5]; mtr[5] = pp[6];
2022 mtr[6] = pp[8]; mtr[7] = pp[9]; mtr[8] = pp[10];
2023 mtr[9] = pp[12]; mtr[10] = pp[13]; mtr[11] = pp[14];
2024 TransformInvert(mtr, mtr);
2025 mview->camera.x = mtr[9]; mview->camera.y = mtr[10]; mview->camera.z = mtr[11];
2026 mview->lookto.x = mtr[6]; mview->lookto.y = mtr[7]; mview->lookto.z = mtr[8];
2027 mview->up.x = mtr[3]; mview->up.y = mtr[4]; mview->up.z = mtr[5];
2030 glDisable(GL_LIGHTING);
2031 // glDisable (GL_DEPTH_TEST);
2032 // glEnable (GL_BLEND);
2033 // glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
2035 glEnable (GL_TEXTURE_RECTANGLE_EXT);
2037 glMatrixMode (GL_PROJECTION);
2039 glMatrixMode (GL_MODELVIEW);
2041 glOrtho(0, width, 0, -height, 0.0, -1.0); /* non-flipped view */
2044 glDisable (GL_TEXTURE_RECTANGLE_EXT);
2046 // glDisable(GL_BLEND);
2047 // glEnable(GL_DEPTH_TEST);
2050 if (mview->draggingMode == kMainViewSelectingRegion) {
2051 /* Draw selection rectangle */
2052 glDisable(GL_LIGHTING);
2053 glDisable(GL_DEPTH_TEST);
2054 glMatrixMode(GL_MODELVIEW);
2056 glMatrixMode(GL_PROJECTION);
2058 glOrtho(0, width, 0, height, -1.0, 1.0);
2059 glColor3f(1.0, 1.0, 0.0);
2060 glBegin(GL_LINE_STRIP);
2061 glVertex2f(mview->dragStartPos[0] * scale, mview->dragStartPos[1] * scale);
2062 glVertex2f(mview->dragStartPos[0] * scale, mview->dragEndPos[1] * scale);
2063 glVertex2f(mview->dragEndPos[0] * scale, mview->dragEndPos[1] * scale);
2064 glVertex2f(mview->dragEndPos[0] * scale, mview->dragStartPos[1] * scale);
2065 glVertex2f(mview->dragStartPos[0] * scale, mview->dragStartPos[1] * scale);
2067 glEnable(GL_DEPTH_TEST);
2075 #pragma mark ====== Labels ======
2078 MainView_attachLabelToAtom(MainView *mview, int index)
2083 /* const ElementPar *dp; */
2084 static float foreColor[] = {1, 1, 0, 1};
2085 static float backColor[] = {1, 1, 1, 0};
2086 ap = ATOM_AT_INDEX(mview->mol->atoms, index);
2087 if (ap->resSeq == 0)
2088 snprintf(buf, sizeof buf, "%-.4s", ap->aname);
2090 snprintf(buf, sizeof buf, "%d:%-.4s", ap->resSeq, ap->aname);
2091 for (p = buf; *p; p++) {
2097 /* dp = &(gBuiltinParameters->atomPars[ap->atomicNumber]);
2098 foreColor[0] = 1.0 - dp->r;
2099 foreColor[1] = 1.0 - dp->g;
2100 foreColor[2] = 1.0 - dp->b; */
2101 rec.label = MainViewCallback_newLabel(mview, buf, 14, foreColor, backColor);
2103 rec.idx2 = kInvalidIndex;
2104 rec.labelid = mview->nlabels + 1;
2105 ap->labelid = rec.labelid;
2106 AssignArray(&mview->labels, &mview->nlabels, sizeof(LabelRecord), mview->nlabels, &rec);
2110 MainView_detachLabelFromAtom(MainView *mview, int index)
2113 if (index >= 0 && index < mview->mol->natoms) {
2114 ap = ATOM_AT_INDEX(mview->mol->atoms, index);
2115 if (ap->labelid > 0 && ap->labelid <= mview->nlabels) {
2116 mview->labels[ap->labelid - 1].idx1 = kInvalidIndex;
2123 MainView_purgeUnusedLabels(MainView *mview)
2128 if (mview == NULL || mview->nlabels == 0 || mview->mol->natoms == 0)
2130 tempid = (Int *)calloc(sizeof(Int), mview->nlabels);
2134 /* Mark the labels in use */
2135 for (i = 0, ap = mview->mol->atoms; i < mview->mol->natoms; i++, ap = ATOM_NEXT(ap)) {
2136 if (ap->labelid > 0 && ap->labelid <= mview->nlabels)
2137 tempid[ap->labelid - 1] = 1;
2140 /* Release the labels not in use, and assign new IDs */
2142 for (i = 0; i < mview->nlabels; i++) {
2143 if (tempid[i] == 0) {
2144 MainViewCallback_releaseLabel(mview->labels[i].label);
2145 mview->labels[i].label = NULL;
2147 mview->labels[i].labelid = tempid[i] = n++;
2151 /* Purge the unused entries */
2152 for (i = mview->nlabels - 1; i >= 0; i--) {
2153 if (tempid[i] == 0) {
2154 memmove(mview->labels + i + 1, mview->labels + i, (mview->nlabels - 1 - i) * sizeof(LabelRecord));
2158 if (mview->nlabels == 0) {
2159 free(mview->labels);
2160 mview->labels = NULL;
2164 for (i = 0, ap = mview->mol->atoms; i < mview->mol->natoms; i++, ap = ATOM_NEXT(ap)) {
2165 if (ap->labelid > 0 && ap->labelid <= mview->nlabels) {
2166 ap->labelid = tempid[ap->labelid - 1];
2173 #pragma mark ====== Mode ======
2176 MainView_setMode(MainView *mview, int mode)
2183 MainView_getMode(const MainView *mview)
2190 #pragma mark ====== Mouse operations ======
2193 mousePosToTrackballPos(MainView *mview, const float *mousePos, float *pos)
2195 float frame[4], width, height, radius;
2196 NULL_CHECK(mview, "mousePosToTrackballPos");
2197 MainViewCallback_frame(mview, frame);
2198 width = frame[2] - frame[0];
2199 height = frame[3] - frame[1];
2200 radius = (width > height ? height * 0.5 : width * 0.5);
2201 pos[0] = (mousePos[0] - frame[0] - width * 0.5) / radius;
2202 pos[1] = (mousePos[1] - frame[1] - height * 0.5) / radius;
2206 showAtomsInInfoText(MainView *mview, int n1, int n2)
2210 MoleculeGetAtomName(mview->mol, n1, buf, sizeof buf - 1);
2212 int nn = strlen(buf);
2214 MoleculeGetAtomName(mview->mol, n2, buf + nn, sizeof buf - nn);
2216 MainViewCallback_drawInfoText(mview, buf);
2217 } else MainViewCallback_drawInfoText(mview, NULL);
2221 MainView_mouseDown(MainView *mview, const float *mousePos, int flags)
2226 double objectPos[3];
2229 mview->dragStartPos[0] = mousePos[0];
2230 mview->dragStartPos[1] = mousePos[1];
2231 mview->dragStartPos[2] = 0.5;
2232 found = MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 0, 0);
2233 mview->clickedAtoms[0] = n1;
2234 mview->clickedAtoms[1] = n2;
2236 /* Estimate the screen z-coordinate of the mouse position */
2238 ap = ATOM_AT_INDEX(mview->mol->atoms, n1);
2240 /* if (mview->mol->is_xtal_coord)
2241 TransformVec(&r1, mview->mol->cell->tr, &r1); */
2242 objectPos[0] = r1.x;
2243 objectPos[1] = r1.y;
2244 objectPos[2] = r1.z;
2245 if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos)) {
2247 ap = ATOM_AT_INDEX(mview->mol->atoms, n2);
2249 /* if (mview->mol->is_xtal_coord)
2250 TransformVec(&r2, mview->mol->cell->tr, &r2); */
2251 objectPos[0] = r2.x;
2252 objectPos[1] = r2.y;
2253 objectPos[2] = r2.z;
2254 if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, fp)) {
2256 r1.x = fp[0] - screenPos[0];
2257 r1.y = fp[1] - screenPos[1];
2258 r1.z = fp[2] - screenPos[2];
2259 NormalizeVec(&r1, &r1);
2260 r2.x = mousePos[0] - screenPos[0];
2261 r2.y = mousePos[1] - screenPos[1];
2264 VecScale(r2, r1, w);
2265 screenPos[2] += r2.z;
2269 mview->dragStartPos[2] = screenPos[2];
2271 /* Set the 'depth' value of the molecule center (i.e. trackball->translate * dimension)
2272 as the z-coordinate of the drag start position */
2273 TrackballGetTranslate(mview->track, p);
2274 objectPos[0] = -mview->dimension * p[0];
2275 objectPos[1] = -mview->dimension * p[1];
2276 objectPos[2] = -mview->dimension * p[2];
2277 if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos))
2278 mview->dragStartPos[2] = screenPos[2];
2281 mview->isDragging = 0;
2283 switch (mview->mode) {
2284 case kTrackballRotateMode:
2285 case kTrackballTranslateMode:
2286 case kTrackballScaleMode:
2288 mousePosToTrackballPos(mview, mousePos, fp);
2289 TrackballStartDragging(mview->track, fp, mview->mode);
2290 mview->draggingMode = kMainViewMovingTrackball;
2293 /* No 'break' intentional; drop to next case */
2295 case kTrackballSelectionMode: {
2296 /* Select or move around */
2298 if (flags & kShiftKeyMask) {
2299 /* Shift-key pressed: toggle selection */
2300 MoleculeToggleSelectionOfAtom(mview->mol, n1);
2302 MoleculeToggleSelectionOfAtom(mview->mol, n2);
2305 if (!MoleculeIsAtomSelected(mview->mol, n1)) {
2306 /* If this atom is not selected, select this atom and unselect others */
2307 MoleculeSelectAtom(mview->mol, n1, 0);
2310 if (!MoleculeIsBondSelected(mview->mol, n1, n2)) {
2311 /* If this bond is not selected, select this bond and unselect others */
2312 MoleculeSelectAtom(mview->mol, n1, 0);
2313 MoleculeSelectAtom(mview->mol, n2, 1);
2317 if (MoleculeIsAtomSelected(mview->mol, n1))
2318 mview->draggingMode = kMainViewDraggingSelectedAtoms;
2319 else mview->draggingMode = 0;
2320 } else mview->draggingMode = kMainViewSelectingRegion;
2321 mview->dragEndPos[0] = mousePos[0];
2322 mview->dragEndPos[1] = mousePos[1];
2323 mview->dragEndPos[2] = mview->dragStartPos[2];
2324 mview->modifierFlags = flags;
2327 case kTrackballCreateMode: {
2328 if (md_is_running(mview->mol->arena)) {
2329 MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
2330 mview->draggingMode = 0;
2333 /* Draw a new bond */
2334 MoleculeSetSelection(mview->mol, NULL);
2335 if (found && n2 < 0) {
2336 /* An atom under mouse: create a new atom and a bond */
2337 ap = ATOM_AT_INDEX(mview->mol->atoms, n1);
2338 mview->tempAtoms[0] = n1;
2339 mview->tempAtomPos[0] = ap->r;
2341 /* No atom under mouse: create two new atoms and a bond */
2342 mview->tempAtoms[0] = -1;
2343 screenPos[0] = mousePos[0];
2344 screenPos[1] = mousePos[1];
2345 screenPos[2] = mview->dragStartPos[2];
2346 if (MainView_convertScreenPositionToObjectPosition(mview, screenPos, objectPos) == 0) {
2347 mview->tempAtoms[0] = -1; /* Cannot create */
2348 mview->draggingMode = 0;
2350 mview->tempAtomPos[0].x = objectPos[0];
2351 mview->tempAtomPos[0].y = objectPos[1];
2352 mview->tempAtomPos[0].z = objectPos[2];
2353 /* if (mview->mol->is_xtal_coord)
2354 TransformVec(&mview->tempAtomPos[0], mview->mol->cell->rtr, &mview->tempAtomPos[0]); */
2357 mview->tempAtoms[1] = -1;
2358 mview->tempAtomPos[1] = mview->tempAtomPos[0];
2359 mview->draggingMode = kMainViewCreatingBond;
2363 mview->draggingMode = 0;
2367 showAtomsInInfoText(mview, n1, n2);
2369 showAtomsInInfoText(mview, -1, -1);
2370 /* MainViewCallback_setNeedsDisplay(mview, 1); */
2374 MainView_mouseDragged(MainView *mview, const float *mousePos, int flags)
2377 if (mview->isDragging == 0) {
2378 if (fabsf(mousePos[0] - mview->dragStartPos[0]) >= 3 || fabsf(mousePos[1] - mview->dragStartPos[1]) >= 3)
2379 mview->isDragging = 1;
2382 mousePosToTrackballPos(mview, mousePos, p);
2383 switch (mview->draggingMode) {
2384 case kMainViewMovingTrackball:
2385 TrackballDrag(mview->track, p);
2387 case kMainViewSelectingRegion:
2388 case kMainViewDraggingSelectedAtoms:
2389 mview->dragEndPos[0] = mousePos[0];
2390 mview->dragEndPos[1] = mousePos[1];
2391 mview->dragEndPos[2] = mview->dragStartPos[2];
2392 mview->modifierFlags = flags;
2393 MainViewCallback_display(mview);
2395 case kMainViewCreatingBond: {
2397 if (MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 0, 0) && n2 < 0) {
2398 /* An atom under mouse */
2399 Atom *ap = ATOM_AT_INDEX(mview->mol->atoms, n1);
2400 /* printf("n1=%d, n2=%d\n", n1, n2); */
2401 mview->tempAtoms[1] = n1;
2402 mview->tempAtomPos[1] = ap->r;
2405 double objectPos[3];
2407 mview->tempAtoms[1] = -1;
2408 /* Convert the position of temporary atom 0 to screen position */
2409 r1 = mview->tempAtomPos[0];
2410 /* if (mview->mol->is_xtal_coord)
2411 TransformVec(&r1, mview->mol->cell->tr, &r1); */
2412 objectPos[0] = r1.x;
2413 objectPos[1] = r1.y;
2414 objectPos[2] = r1.z;
2415 if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos) == 0)
2416 break; /* Do nothing */
2417 /* Convert the mouse position to object position, while using the same Z depth calculated from the temporary atom 0 position */
2418 screenPos[0] = mousePos[0];
2419 screenPos[1] = mousePos[1];
2420 if (MainView_convertScreenPositionToObjectPosition(mview, screenPos, objectPos) == 0)
2421 break; /* Do nothing */
2422 r1.x = objectPos[0];
2423 r1.y = objectPos[1];
2424 r1.z = objectPos[2];
2425 /* if (mview->mol->is_xtal_coord)
2426 TransformVec(&r1, mview->mol->cell->rtr, &r1); */
2427 mview->tempAtomPos[1] = r1;
2429 if (mview->tempAtoms[0] < 0)
2430 showAtomsInInfoText(mview, mview->tempAtoms[1], -1);
2432 showAtomsInInfoText(mview, mview->tempAtoms[0], mview->tempAtoms[1]);
2433 MainViewCallback_display(mview);
2439 MainViewCallback_display(mview);
2443 MainView_mouseMoved(MainView *mview, const float *mousePos, int flags)
2446 if (mview->isDragging)
2448 found = MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 1, 0);
2450 showAtomsInInfoText(mview, n1, n2);
2452 showAtomsInInfoText(mview, -1, -1);
2456 sCreateNewAtom(Molecule *mol, Vector pos)
2461 memset(&a, 0, sizeof(Atom));
2463 for (i = 0; i < 1000; i++) {
2464 snprintf(name, sizeof name, "C%03d", i);
2465 for (j = 0; j < mol->natoms; j++) {
2466 if (strncmp(ATOM_AT_INDEX(mol->atoms, j)->aname, name, 4) == 0)
2469 if (j == mol->natoms)
2472 strncpy(a.aname, name, 4);
2474 strcpy(a.element, "C");
2475 a.type = AtomTypeEncodeToUInt("c3");
2476 a.weight = WeightForAtomicNumber(a.atomicNumber);
2478 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, -1, &i) == 0)
2479 return mol->natoms - 1;
2481 /* return MoleculeAddAtom(mol, &a); */
2485 MainView_mouseUp(MainView *mview, const float *mousePos, int flags, int clickCount)
2490 mousePosToTrackballPos(mview, mousePos, p);
2492 if (clickCount == 2 && mview->isDragging == 0) {
2493 /* Create a new molecular fragment */
2494 int n1, n2, status, found;
2496 found = MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 0, 0);
2497 status = MyAppCallback_getGlobalSettingsWithType("global.entered_formula", 's', &p);
2499 strncpy(buf, p, sizeof buf - 1);
2500 buf[sizeof buf - 1] = 0;
2504 if (MyAppCallback_getTextWithPrompt("Enter formula (e.g. CH2OCH3)", buf, sizeof buf) == 0)
2506 MyAppCallback_setGlobalSettingsWithType("global.entered_formula", 's', buf);
2507 MolActionCreateAndPerform(mview->mol, SCRIPT_ACTION("si"), "cmd_fuse_or_dock", buf, n1);
2511 if (mview->mode == kTrackballEraseMode) {
2513 found = MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 0, 0);
2514 if (found && n1 == mview->clickedAtoms[0] && n2 == mview->clickedAtoms[1]) {
2516 /* IntGroup *sel, *newsel;
2517 sel = MoleculeGetSelection(mview->mol);
2519 newsel = MoleculeModifySelectionByRemovingAtoms(mview->mol, sel, IntGroupNewWithPoints(n1, 1, -1));
2520 if (!IntGroupIsEqual(sel, newsel))
2521 MolActionCreateAndPerform(mview->mol, gMolActionSetSelection, newsel);
2523 IntGroupRelease(newsel);
2525 MolActionCreateAndPerform(mview->mol, gMolActionDeleteAnAtom, (Int)n1);
2528 Int bn = MoleculeLookupBond(mview->mol, n1, n2);
2530 IntGroup *ig = IntGroupNewWithPoints(bn, 1, -1);
2531 MolActionCreateAndPerform(mview->mol, gMolActionDeleteBonds, ig);
2532 IntGroupRelease(ig);
2534 fprintf(stderr, "Internal error %s:%d: bond to delete is not found", __FILE__, __LINE__);
2541 switch (mview->draggingMode) {
2542 case kMainViewMovingTrackball:
2543 TrackballEndDragging(mview->track, p);
2545 case kMainViewDraggingSelectedAtoms: {
2547 if (mview->isDragging) {
2548 IntGroup *dupSelection = IntGroupNewFromIntGroup(mview->mol->selection);
2549 calcDragOffset(mview, &offset);
2550 /* if (mview->mol->is_xtal_coord)
2551 TransformVec(&offset, mview->mol->cell->rtr, &offset); */
2552 MolActionCreateAndPerform(mview->mol, gMolActionTranslateAtoms, &offset, dupSelection);
2553 IntGroupRelease(dupSelection);
2557 case kMainViewSelectingRegion: {
2558 char *selectFlags = temporarySelection(mview, mview->modifierFlags, (mview->isDragging ? 0 : 1), 0);
2559 if (selectFlags != NULL) {
2560 IntGroup *ig = IntGroupNew();
2563 natoms = mview->mol->natoms;
2564 for (i = 0; i < natoms; i++) {
2566 IntGroupAdd(ig, i, 1);
2568 MoleculeSetSelection(mview->mol, ig);
2569 /* printf("current selection = {");
2570 for (i = j = 0; i < natoms; i++) {
2571 if (selectFlags[i]) {
2572 printf("%s%.4s", (j == 0 ? "" : " "), mview->mol->atoms[i].name);
2577 IntGroupRelease(ig);
2583 case kMainViewCreatingBond: {
2586 if (mview->tempAtoms[0] >= 0 && mview->tempAtoms[1] >= 0) {
2587 n1 = mview->tempAtoms[0];
2588 n2 = mview->tempAtoms[1];
2590 if (mview->tempAtoms[0] < 0) {
2591 /* Create the first atom */
2592 n1 = sCreateNewAtom(mview->mol, mview->tempAtomPos[0]);
2593 } else n1 = mview->tempAtoms[0];
2594 if (mview->tempAtoms[1] < 0) {
2595 /* Create the second atom, if not too close */
2596 Vector dr = ATOM_AT_INDEX(mview->mol->atoms, n1)->r;
2597 VecDec(dr, mview->tempAtomPos[1]);
2598 /* if (mview->mol->is_xtal_coord)
2599 TransformVec(&dr, mview->mol->cell->tr, &dr); */
2600 if (VecLength2(dr) > 0.01)
2601 n2 = sCreateNewAtom(mview->mol, mview->tempAtomPos[1]);
2603 } else n2 = mview->tempAtoms[1];
2605 if (n1 >= 0 && n2 >= 0 && n1 != n2) {
2608 b[2] = kInvalidIndex;
2609 MolActionCreateAndPerform(mview->mol, gMolActionAddBonds, 2, b, NULL);
2610 /* MoleculeAddBonds(mview->mol, b, NULL); */
2616 mview->draggingMode = 0;
2617 mview->isDragging = 0;
2618 MainViewCallback_setNeedsDisplay(mview, 1);
2619 MainViewCallback_setKeyboardFocus(mview);
2623 MainView_rotateBySlider(MainView *mview, float angle, int mode, int mouseStatus, int modifierFlags)
2627 if (mouseStatus == 1) { /* mouseDown */
2629 if (mode == kSliderRotateBondMode) {
2630 if (MoleculeIsFragmentRotatable(mview->mol, mview->mol->selection, &n1, &n2, &(mview->rotateFragment))) {
2631 mview->rotateCenter = ATOM_AT_INDEX(mview->mol->atoms, n1)->r;
2632 mview->rotateAxis = ATOM_AT_INDEX(mview->mol->atoms, n2)->r;
2633 VecDec(mview->rotateAxis, mview->rotateCenter);
2634 if (VecLength2(mview->rotateAxis) < 1e-10)
2636 NormalizeVec(&(mview->rotateAxis), &(mview->rotateAxis));
2637 if (modifierFlags & kAltKeyMask) {
2638 /* Option key: reverse the fragment */
2639 IntGroupRelease(mview->rotateFragment);
2640 mview->rotateFragment = MoleculeFragmentExcludingAtoms(mview->mol, n2, 1, &n1);
2641 VecScaleSelf(mview->rotateAxis, -1);
2644 } else if ((modifierFlags & kAltKeyMask) != 0) {
2645 /* Rotate selection */
2647 if (mview->mol->selection == NULL)
2649 mview->rotateFragment = IntGroupNewFromIntGroup(mview->mol->selection);
2650 TrackballGetTranslate(mview->track, tr);
2651 mview->rotateCenter.x = -mview->dimension * tr[0];
2652 mview->rotateCenter.y = -mview->dimension * tr[1];
2653 mview->rotateCenter.z = -mview->dimension * tr[2];
2654 if (mode == kSliderRotateXMode) {
2655 VecCross(mview->rotateAxis, mview->up, mview->lookto);
2657 mview->rotateAxis = mview->up;
2658 VecScaleSelf(mview->rotateAxis, -1);
2661 /* Rotate along x/y axis (no coordinate transform) */
2662 TrackballStartDragging(mview->track, NULL, kTrackballRotateMode);
2663 mview->rotateFragment = NULL; /* This is probably not necessary */
2665 if (mview->rotateFragment != NULL) {
2666 /* Save the original position */
2667 n1 = IntGroupGetCount(mview->rotateFragment);
2668 mview->rotateFragmentOldPos = (Vector *)calloc(sizeof(Vector), n1);
2669 if (mview->rotateFragmentOldPos == NULL) {
2670 IntGroupRelease(mview->rotateFragment);
2671 mview->rotateFragment = NULL;
2674 for (i = 0; i < n1; i++) {
2675 n2 = IntGroupGetNthPoint(mview->rotateFragment, i);
2676 mview->rotateFragmentOldPos[i] = (ATOM_AT_INDEX(mview->mol->atoms, n2))->r;
2680 } else { /* mouseDragged or mouseUp */
2682 if (mview->rotateFragment != NULL) {
2684 /* Restore original positions */
2685 n1 = IntGroupGetCount(mview->rotateFragment);
2686 for (i = 0; i < n1; i++) {
2687 n2 = IntGroupGetNthPoint(mview->rotateFragment, i);
2688 (ATOM_AT_INDEX(mview->mol->atoms, n2))->r = mview->rotateFragmentOldPos[i];
2691 if (mouseStatus == 2) { /* dragged */
2692 MoleculeRotate(mview->mol, &(mview->rotateAxis), angle, &(mview->rotateCenter), mview->rotateFragment);
2693 } else { /* mouse up */
2694 IntGroup *ig = IntGroupNewFromIntGroup(mview->rotateFragment);
2695 MolActionCreateAndPerform(mview->mol, gMolActionRotateAtoms, &(mview->rotateAxis), (double)angle, &(mview->rotateCenter), ig);
2696 IntGroupRelease(ig);
2697 IntGroupRelease(mview->rotateFragment);
2698 mview->rotateFragment = NULL;
2699 free(mview->rotateFragmentOldPos);
2700 mview->rotateFragmentOldPos = NULL;
2705 double cs = cos(angle / 2);
2706 double sn = sin(angle / 2);
2707 if (mouseStatus == 0) { /* mouseUp */
2708 TrackballEndDragging(mview->track, NULL);
2709 } else { /* mouseDragged */
2711 if (mode == kSliderRotateXMode) {
2712 /* Rotation along x axis */
2714 quat[2] = quat[3] = 0;
2717 quat[1] = quat[3] = 0;
2719 TrackballSetTemporaryRotation(mview->track, quat);
2724 MainViewCallback_setNeedsDisplay(mview, 1);
2725 MainViewCallback_setKeyboardFocus(mview);
2728 #pragma mark ====== Menu Commands ======
2731 MainView_selectAll(MainView *mview)
2734 if (mview == NULL || mview->mol == NULL)
2739 IntGroupAdd(ig, 0, mview->mol->natoms);
2740 MoleculeSetSelection(mview->mol, ig);
2741 IntGroupRelease(ig);
2742 MainViewCallback_setNeedsDisplay(mview, 1);
2746 MainView_selectFragment(MainView *mview)
2749 if (mview == NULL || mview->mol == NULL)
2751 ig = MoleculeFragmentWithAtomGroups(mview->mol, MoleculeGetSelection(mview->mol), NULL);
2754 MoleculeSetSelection(mview->mol, ig);
2755 IntGroupRelease(ig);
2756 MainViewCallback_setNeedsDisplay(mview, 1);
2760 MainView_selectReverse(MainView *mview)
2763 if (mview == NULL || mview->mol == NULL)
2765 ig = IntGroupNewFromIntGroup(MoleculeGetSelection(mview->mol));
2766 IntGroupReverse(ig, 0, mview->mol->natoms);
2767 MoleculeSetSelection(mview->mol, ig);
2768 IntGroupRelease(ig);
2769 MainViewCallback_setNeedsDisplay(mview, 1);
2773 MainView_centerSelection(MainView *mview)
2778 if (mview == NULL || mview->mol == NULL)
2780 ig = MoleculeGetSelection(mview->mol);
2781 MoleculeCenterOfMass(mview->mol, &c, ig);
2782 tr[0] = -c.x / mview->dimension;
2783 tr[1] = -c.y / mview->dimension;
2784 tr[2] = -c.z / mview->dimension;
2785 TrackballSetTranslate(mview->track, tr);
2786 MainViewCallback_setNeedsDisplay(mview, 1);
2789 #pragma mark ====== Pasteboard Support ======
2792 MainView_copy(MainView *mview)
2794 Molecule *mol, *mol2;
2796 Int len, result, time;
2798 if (mview == NULL || (mol = mview->mol) == NULL || (sel = MoleculeGetSelection(mol)) == NULL)
2800 if (MoleculeExtract(mol, &mol2, MoleculeGetSelection(mol), 1) != 0
2802 || (p = MoleculeSerialize(mol2, &len, &time)) == NULL)
2804 result = MoleculeCallback_writeToPasteboard(gMoleculePasteboardType, p, len);
2808 MoleculeRelease(mol2);
2809 mview->pasteTimeStamp = time;
2810 mview->pasteCount = 0;
2815 MainView_delete(MainView *mview)
2818 Molecule *mol = mview->mol;
2819 if (md_is_running(mview->mol->arena)) {
2820 MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
2823 result = MolActionCreateAndPerform(mol, gMolActionUnmergeMolecule, MoleculeGetSelection(mol));
2825 result = MolActionCreateAndPerform(mol, gMolActionSetSelection, NULL);
2830 MainView_cut(MainView *mview)
2833 if (md_is_running(mview->mol->arena)) {
2834 MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
2837 result = MainView_copy(mview);
2839 result = MainView_delete(mview);
2844 MainView_isPastable(MainView *mview)
2846 if (MoleculeCallback_isDataInPasteboard(gMoleculePasteboardType))
2852 MainView_paste(MainView *mview)
2855 Int len, result, time;
2859 if (md_is_running(mview->mol->arena)) {
2860 MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
2863 if (!MainView_isPastable(mview))
2865 if (MoleculeCallback_readFromPasteboard(gMoleculePasteboardType, &p, &len) != 0)
2867 if ((mol2 = MoleculeDeserialize(p, len, &time)) == NULL) {
2873 if (time == mview->pasteTimeStamp) {
2874 /* Offset the pasted fragment by something */
2876 mview->pasteCount++;
2877 v.x = v.y = v.z = mview->pasteCount * 0.5 + sin((double)mview->pasteCount) * 0.1; /* sin(..) is to avoid accidental coincidence */
2878 MoleculeTranslate(mol2, &v, NULL);
2880 mview->pasteTimeStamp = time;
2881 mview->pasteCount = 0;
2884 sel = MoleculeGetSelection(mview->mol);
2885 if (sel == NULL || IntGroupGetCount(sel) == 0) {
2886 /* Remove dummy atoms from mol2 */
2889 sel = IntGroupNew();
2890 for (i = 0, ap = mol2->atoms; i < mol2->natoms; i++, ap = ATOM_NEXT(ap)) {
2891 if (ap->atomicNumber == 0 && ap->aname[0] == '_') {
2893 IntGroupAdd(sel, i, 1);
2896 if (IntGroupGetCount(sel) > 0)
2897 MoleculeUnmerge(mol2, NULL, sel, 0, NULL, NULL, 0);
2898 IntGroupRelease(sel);
2901 result = MolActionCreateAndPerform(mview->mol, SCRIPT_ACTION("M"), "dock_fragment", mol2);
2902 MoleculeRelease(mol2);
2907 sMainView_AppendParameterToSerialBuffer(void **pb_ptr, Int *pb_len, Int parType, Int count, const UnionPar *up)
2909 int len = sizeof(Int) * 2 + sizeof(UnionPar) * count;
2912 *pb_ptr = malloc(len + 1);
2913 if (*pb_ptr == NULL)
2915 p = (char *)(*pb_ptr);
2918 *pb_ptr = realloc(*pb_ptr, *pb_len + len);
2919 if (*pb_ptr == NULL)
2921 p = (char *)(*pb_ptr) + *pb_len - 1; /* *pb_len includes the end mark */
2927 *((Int *)p) = count;
2929 memmove(p, up, sizeof(UnionPar) * count);
2930 p += sizeof(UnionPar) * count;
2931 *p = 0; /* End mark */
2936 MainView_pasteParameters(MainView *mview)
2941 if (mview == NULL || mview->mol == NULL)
2943 if (mview->mol->par == NULL)
2944 mview->mol->par = ParameterNew();
2945 if (!MoleculeCallback_isDataInPasteboard(gParameterPasteboardType))
2947 if (MoleculeCallback_readFromPasteboard(gParameterPasteboardType, (void **)&p, &len) != 0)
2950 newsel = IntGroupNew();
2956 if (parType < kFirstParType || parType > kLastParType)
2959 count = *((Int *)p);
2961 up = (UnionPar *)calloc(sizeof(UnionPar), count);
2962 memmove(up, p, sizeof(UnionPar) * count);
2964 /* The global parameters become local when pasted */
2965 for (i = 0; i < count; i++) {
2966 if (up[i].bond.src > 0)
2970 c = ParameterGetCountForType(mview->mol->par, parType);
2971 ig = IntGroupNewWithPoints(c, count, -1);
2972 MolActionCreateAndPerform(mview->mol, gMolActionAddParameters, parType, ig, count, up);
2974 IntGroupRelease(ig);
2975 p += sizeof(UnionPar) * count;
2976 c = ParameterTableGetRowFromTypeAndIndex(mview->mol->par, parType, c);
2977 IntGroupAdd(newsel, c, count);
2981 /* Select newly pasted parameters */
2982 MainViewCallback_setTableSelection(mview, newsel);
2983 IntGroupRelease(newsel);
2985 /* Request MD rebuild */
2986 mview->mol->needsMDRebuild = 1;
2988 /* Suppress clear of parameter table selection */
2989 mview->mol->parameterTableSelectionNeedsClear = 0;
2991 MoleculeCallback_notifyModification(mview->mol, 0);
2995 /* flags: 1, delete; 2, copy; 3, cut */
2997 MainView_copyOrCutParameters(MainView *mview, int flags)
2999 IntGroup *ig = MainViewCallback_getTableSelection(mview);
3001 int i, n, idx, type = -1, t1;
3002 Parameter *par = mview->mol->par;
3003 void *pb_ptr = NULL;
3005 if (ig == NULL || (i = IntGroupGetCount(ig)) == 0)
3010 n = IntGroupGetNthPoint(ig, i);
3012 idx = ParameterTableGetItemIndex(par, n, &t1);
3018 /* Process Parameters for the last group */
3019 if (type >= kFirstParType && ig2 != NULL && (n = IntGroupGetCount(ig2)) > 0) {
3020 UnionPar *up = (UnionPar *)calloc(sizeof(UnionPar), n);
3023 if (ParameterDelete(par, type, up, ig2) < 0)
3025 act = MolActionNew(gMolActionAddParameters, type, ig2, n, up);
3026 MolActionCallback_registerUndo(mview->mol, act);
3027 MolActionRelease(act);
3029 if (ParameterCopy(par, type, up, ig2) < 0)
3033 if (sMainView_AppendParameterToSerialBuffer(&pb_ptr, &pb_len, type, n, up) != 0)
3037 IntGroupRelease(ig2);
3046 ig2 = IntGroupNew();
3047 IntGroupAdd(ig2, idx, 1);
3052 IntGroupRelease(ig2);
3053 IntGroupRelease(ig);
3056 n = MoleculeCallback_writeToPasteboard(gParameterPasteboardType, pb_ptr, pb_len);
3061 /* Clear selection */
3062 MainViewCallback_setTableSelection(mview, NULL);
3063 /* Request MD rebuild */
3064 mview->mol->needsMDRebuild = 1;
3066 MoleculeCallback_notifyModification(mview->mol, 0);
3070 #pragma mark ====== Table View (also for gBuiltinParameters) ======
3072 /* As a special case, the table view functions will handle gBuiltinParameters when mview == NULL. */
3074 typedef struct ColumnInfoRecord {
3080 static ColumnInfoRecord sAtomColumns[] = {
3081 {"atom", 4, 0}, {"name", 4, 1}, {"type", 4, 1}, {"element", 4, 1}, {"residue", 6, 1},
3082 {"x", 6, 1}, {"y", 6, 1}, {"z", 6, 1}, {"charge", 6, 1}, {NULL}
3084 static ColumnInfoRecord sBondColumns[] = {
3085 {"atoms", 9, 0}, {"names", 9, 0}, {"type", 9, 0}, {"length", 8, 0},
3086 {"par type", 8, 0}, {"k", 8, 0}, {"r0", 8, 0}, {NULL}
3088 static ColumnInfoRecord sAngleColumns[] = {
3089 {"atoms", 12, 0}, {"names", 12, 0}, {"type", 12, 0}, {"angle", 8, 0},
3090 {"par type", 8, 0}, {"k", 8, 0}, {"a0", 8, 0}, {NULL}
3092 static ColumnInfoRecord sDihedralColumns[] = {
3093 {"atoms", 15, 0}, {"names", 15, 0}, {"type", 15, 0}, {"dihedral", 8, 0},
3094 {"par type", 8, 0}, {"k", 8, 0}, {"period", 4, 0}, {"phi0", 8, 0}, {NULL}
3096 static ColumnInfoRecord sImproperColumns[] = {
3097 {"atoms", 15, 0}, {"names", 15, 0}, {"type", 15, 0}, {"improper", 8, 0},
3098 {"par type", 8, 0}, {"k", 8, 0}, {"period", 4, 0}, {"phi0", 8, 0}, {NULL}
3100 static ColumnInfoRecord sParameterColumns[] = {
3101 {"class", 5, 0}, {"type", 9, 0}, {"", 6, 0}, {"", 6, 0}, {"", 6, 0},
3102 {"", 6, 0}, {"", 6, 0}, {"", 6, 0}, {"src", 8, 0}, {"comment", 25, 0}, {NULL}
3104 static ColumnInfoRecord sUnitCellColumns[] = {
3105 {"name", 6, 0}, {"values", 9, 1}, {"", 9, 1}, {"", 9, 1}, {NULL}
3107 static ColumnInfoRecord sXtalCoordColumns[] = {
3108 {"atom", 4, 0}, {"name", 4, 1}, {"element", 4, 1},
3109 {"frx", 6, 1}, {"fry", 6, 1}, {"frz", 6, 1},
3110 {"occ", 6, 1}, {"temp", 6, 1}, {NULL}
3112 static ColumnInfoRecord sMOEnergyColumns[] = {
3113 {"MO", 5, 0}, {"alpha energy", 12, 0}, {"beta energy", 12, 0}, {NULL}
3115 static ColumnInfoRecord *sColumnInfo[] = {
3116 sAtomColumns, sBondColumns, sAngleColumns, sDihedralColumns,
3117 sImproperColumns, NULL, sParameterColumns, NULL,
3118 sUnitCellColumns, sXtalCoordColumns, NULL, sMOEnergyColumns
3120 static char *sTableTitles[] = {
3121 "atoms", "bonds", "angles", "dihedrals", "impropers", "--------",
3122 "MM/MD pars", "--------", "unit cell", "xtal coords", "--------", "MO energy"
3126 MainView_tableTitleForIndex(MainView *mview, int idx, char *buf, int bufsize)
3129 idx = kMainViewParameterTableIndex;
3130 if (idx < 0 || idx >= sizeof(sColumnInfo) / sizeof(sColumnInfo[0])) {
3134 snprintf(buf, bufsize, "%s", sTableTitles[idx]);
3138 MainView_createColumnsForTableAtIndex(MainView *mview, int idx)
3143 idx = kMainViewParameterTableIndex;
3144 if (idx < 0 || idx >= sizeof(sColumnInfo) / sizeof(sColumnInfo[0]))
3145 return 0; /* Invalid index */
3146 if (sColumnInfo[idx] == NULL)
3147 return 0; /* Invalid index */
3149 /* Remove all existing columns */
3150 while (MainViewCallback_removeTableColumnAtIndex(mview, 0) > 0);
3153 mview->tableIndex = idx;
3155 /* Create columns */
3156 for (i = 0; ; i++) {
3158 ColumnInfoRecord *recp = &(sColumnInfo[idx][i]);
3159 if (recp->name == NULL)
3161 width = recp->width;
3162 if (mview == 0 && strcmp(recp->name, "comment") == 0)
3164 MainViewCallback_addTableColumn(mview, recp->name, width, recp->editable);
3171 MainView_refreshTable(MainView *mview)
3174 if (mview != NULL && mview->mol != NULL) {
3175 MainView_refreshCachedInfo(mview);
3176 if (mview->tableIndex == kMainViewBondTableIndex ||
3177 mview->tableIndex == kMainViewAngleTableIndex ||
3178 mview->tableIndex == kMainViewDihedralTableIndex ||
3179 mview->tableIndex == kMainViewImproperTableIndex ||
3180 mview->tableIndex == kMainViewParameterTableIndex) {
3181 /* Check the parameter table */
3182 if (mview->mol->arena == NULL)
3183 md_arena_new(mview->mol);
3184 if (mview->mol->needsMDRebuild || mview->mol->arena->par == NULL) {
3185 /* Here we do not call MoleculePrepareMDArena(), because we do not want
3186 to modify Molecule by just redrawing tables.
3187 (But MoleculePrepareMDArena() *is* called when the parameter
3188 table is selected. */
3189 md_prepare(mview->mol->arena, 1);
3194 MainViewCallback_reloadTableData(mview);
3196 if (mview != NULL && mview->mol != NULL && mview->tableIndex >= kMainViewAtomTableIndex && mview->tableIndex <= kMainViewImproperTableIndex)
3197 MainViewCallback_setTableSelection(mview, mview->tableSelection);
3201 MainView_numberOfRowsInTable(MainView *mview)
3204 return ParameterTableNumberOfRows(gBuiltinParameters);
3205 if (mview->mol == NULL)
3207 switch (mview->tableIndex) {
3208 case kMainViewParameterTableIndex:
3209 return ParameterTableNumberOfRows(mview->mol->par);
3210 case kMainViewUnitCellTableIndex:
3211 return 13; /* a, b, c, alpha, beta, gamma, a_valid, b_valid, c_valid, av, bv, cv, ov */
3212 case kMainViewAtomTableIndex:
3213 case kMainViewBondTableIndex:
3214 case kMainViewAngleTableIndex:
3215 case kMainViewDihedralTableIndex:
3216 case kMainViewImproperTableIndex:
3217 case kMainViewXtalCoordTableIndex:
3218 if (mview->tableCache == NULL)
3219 MainView_refreshCachedInfo(mview);
3220 return IntGroupGetCount(mview->tableCache);
3221 case kMainViewMOTableIndex:
3222 return (mview->mol->bset != NULL ? mview->mol->bset->ncomps : 0);
3229 MainView_indexToTableRow(MainView *mview, int idx)
3233 switch (mview->tableIndex) {
3234 case kMainViewAtomTableIndex:
3235 case kMainViewBondTableIndex:
3236 case kMainViewAngleTableIndex:
3237 case kMainViewDihedralTableIndex:
3238 case kMainViewImproperTableIndex:
3239 case kMainViewXtalCoordTableIndex:
3240 return IntGroupLookupPoint(mview->tableCache, idx);
3247 MainView_tableRowToIndex(MainView *mview, int row)
3251 switch (mview->tableIndex) {
3252 case kMainViewAtomTableIndex:
3253 case kMainViewBondTableIndex:
3254 case kMainViewAngleTableIndex:
3255 case kMainViewDihedralTableIndex:
3256 case kMainViewImproperTableIndex:
3257 case kMainViewXtalCoordTableIndex:
3258 return IntGroupGetNthPoint(mview->tableCache, row);
3265 sAtomDescription(Atom *ap, char *buf, int bufsize)
3267 snprintf(buf, bufsize, "%d:%.4s", ap->resSeq, ap->aname);
3272 sParameterOfTypeAtIndex(Molecule *mol, int parType, int idx)
3275 if (mol->arena == NULL || mol->needsMDRebuild || mol->arena->par == NULL)
3279 i = mol->arena->vdw_par_i[idx];
3280 if (i >= 0 && i < mol->arena->par->nvdwPars)
3281 return (UnionPar *)(mol->arena->par->vdwPars + i);
3284 i = mol->arena->bond_par_i[idx];
3285 if (i >= 0 && i < mol->arena->par->nbondPars)
3286 return (UnionPar *)(mol->arena->par->bondPars + i);
3289 i = mol->arena->angle_par_i[idx];
3290 if (i >= 0 && i < mol->arena->par->nanglePars)
3291 return (UnionPar *)(mol->arena->par->anglePars + i);
3293 case kDihedralParType:
3294 i = mol->arena->dihedral_par_i[idx];
3295 if (i >= 0 && i < mol->arena->par->ndihedralPars)
3296 return (UnionPar *)(mol->arena->par->dihedralPars + i);
3298 case kImproperParType:
3299 i = mol->arena->improper_par_i[idx];
3300 if (i >= 0 && i < mol->arena->par->nimproperPars)
3301 return (UnionPar *)(mol->arena->par->improperPars + i);
3308 MainView_valueForTable(MainView *mview, int column, int row, char *buf, int bufsize)
3313 char descbuf[4][20], typebuf[4][8];
3317 return ParameterTableGetItemText(gBuiltinParameters, column, row, buf, bufsize);
3319 if (mview == NULL || (mol = mview->mol) == NULL) {
3324 if (mview->tableIndex == kMainViewParameterTableIndex)
3325 return ParameterTableGetItemText(mview->mol->par, column, row, buf, bufsize);
3327 if (mview->tableIndex == kMainViewAtomTableIndex) { /* Atoms */
3328 idx = MainView_tableRowToIndex(mview, row);
3329 ap[0] = ATOM_AT_INDEX(mol->atoms, idx);
3331 case 0: snprintf(buf, bufsize, "%d", idx); break;
3332 case 1: snprintf(buf, bufsize, "%.4s", ap[0]->aname); break;
3333 case 2: snprintf(buf, bufsize, "%.6s", AtomTypeDecodeToString(ap[0]->type, NULL)); break;
3334 case 3: snprintf(buf, bufsize, "%.2s", ap[0]->element); break;
3336 if (ap[0]->resSeq == 0)
3339 snprintf(buf, bufsize, "%.4s.%d", ap[0]->resName, ap[0]->resSeq);
3341 case 5: snprintf(buf, bufsize, "%.3f", ap[0]->r.x); break;
3342 case 6: snprintf(buf, bufsize, "%.3f", ap[0]->r.y); break;
3343 case 7: snprintf(buf, bufsize, "%.3f", ap[0]->r.z); break;
3344 case 8: snprintf(buf, bufsize, "%.3f", ap[0]->charge); break;
3345 default: buf[0] = 0; break;
3347 } else if (mview->tableIndex == kMainViewBondTableIndex) { /* Bonds */
3348 idx = MainView_tableRowToIndex(mview, row);
3349 ip = mol->bonds + idx * 2;
3350 ap[0] = ATOM_AT_INDEX(mol->atoms, ip[0]);
3351 ap[1] = ATOM_AT_INDEX(mol->atoms, ip[1]);
3353 case 0: snprintf(buf, bufsize, "%d-%d", ip[0], ip[1]); break;
3354 case 1: snprintf(buf, bufsize, "%s-%s", sAtomDescription(ap[0], descbuf[0], 20), sAtomDescription(ap[1], descbuf[1], 20)); break;
3355 case 2: snprintf(buf, bufsize, "%.6s-%.6s", AtomTypeDecodeToString(ap[0]->type, typebuf[0]), AtomTypeDecodeToString(ap[1]->type, typebuf[1])); break;
3357 snprintf(buf, bufsize, "%.3f", MoleculeMeasureBond(mview->mol, &(ap[0]->r), &(ap[1]->r)));
3362 BondPar *bp = (BondPar *)sParameterOfTypeAtIndex(mol, kBondParType, idx);
3367 snprintf(buf, bufsize, "%.6s-%.6s", AtomTypeDecodeToString(bp->type1, typebuf[0]), AtomTypeDecodeToString(bp->type2, typebuf[1]));
3369 snprintf(buf, bufsize, "%.3f", (column == 5 ? bp->k * INTERNAL2KCAL : bp->r0));
3373 default: buf[0] = 0; break;
3375 } else if (mview->tableIndex == kMainViewAngleTableIndex) { /* Angles */
3376 idx = MainView_tableRowToIndex(mview, row);
3377 ip = mol->angles + idx * 3;
3378 ap[0] = ATOM_AT_INDEX(mol->atoms, ip[0]);
3379 ap[1] = ATOM_AT_INDEX(mol->atoms, ip[1]);
3380 ap[2] = ATOM_AT_INDEX(mol->atoms, ip[2]);
3382 case 0: snprintf(buf, bufsize, "%d-%d-%d", ip[0], ip[1], ip[2]); break;
3383 case 1: snprintf(buf, bufsize, "%s-%s-%s", sAtomDescription(ap[0], descbuf[0], 20), sAtomDescription(ap[1], descbuf[1], 20), sAtomDescription(ap[2], descbuf[2], 20)); break;
3384 case 2: snprintf(buf, bufsize, "%.6s-%.6s-%.6s", AtomTypeDecodeToString(ap[0]->type, typebuf[0]), AtomTypeDecodeToString(ap[1]->type, typebuf[1]), AtomTypeDecodeToString(ap[2]->type, typebuf[2])); break;
3386 snprintf(buf, bufsize, "%.3f", MoleculeMeasureAngle(mview->mol, &(ap[0]->r), &(ap[1]->r), &(ap[2]->r)));
3391 AnglePar *anp = (AnglePar *)sParameterOfTypeAtIndex(mol, kAngleParType, idx);
3396 snprintf(buf, bufsize, "%.6s-%.6s-%.6s", AtomTypeDecodeToString(anp->type1, typebuf[0]), AtomTypeDecodeToString(anp->type2, typebuf[1]), AtomTypeDecodeToString(anp->type3, typebuf[2]));
3398 snprintf(buf, bufsize, "%.3f", (column == 5 ? anp->k * INTERNAL2KCAL : anp->a0 * kRad2Deg));
3402 default: buf[0] = 0; break;
3404 } else if (mview->tableIndex == kMainViewDihedralTableIndex || mview->tableIndex == kMainViewImproperTableIndex) { /* Dihedrals, Impropers */
3405 int f = (mview->tableIndex == kMainViewDihedralTableIndex);
3406 idx = MainView_tableRowToIndex(mview, row);
3407 ip = (f ? mview->mol->dihedrals : mview->mol->impropers) + idx * 4;
3408 ap[0] = ATOM_AT_INDEX(mview->mol->atoms, ip[0]);
3409 ap[1] = ATOM_AT_INDEX(mview->mol->atoms, ip[1]);
3410 ap[2] = ATOM_AT_INDEX(mview->mol->atoms, ip[2]);
3411 ap[3] = ATOM_AT_INDEX(mview->mol->atoms, ip[3]);
3413 case 0: snprintf(buf, bufsize, "%d-%d-%d-%d", ip[0], ip[1], ip[2], ip[3]); break;
3414 case 1: snprintf(buf, bufsize, "%s-%s-%s-%s", sAtomDescription(ap[0], descbuf[0], 20), sAtomDescription(ap[1], descbuf[1], 20), sAtomDescription(ap[2], descbuf[2], 20), sAtomDescription(ap[3], descbuf[3], 20)); break;
3415 case 2: snprintf(buf, bufsize, "%.6s-%.6s-%.6s-%.6s", AtomTypeDecodeToString(ap[0]->type, typebuf[0]), AtomTypeDecodeToString(ap[1]->type, typebuf[1]), AtomTypeDecodeToString(ap[2]->type, typebuf[2]), AtomTypeDecodeToString(ap[3]->type, typebuf[3])); break;
3417 snprintf(buf, bufsize, "%.3f", MoleculeMeasureDihedral(mview->mol, &(ap[0]->r), &(ap[1]->r), &(ap[2]->r), &(ap[3]->r)));
3423 TorsionPar *tp = (TorsionPar *)sParameterOfTypeAtIndex(mol, (f ? kDihedralParType : kImproperParType), idx);
3426 else if (column == 4)
3427 snprintf(buf, bufsize, "%.6s-%.6s-%.6s-%.6s", AtomTypeDecodeToString(tp->type1, typebuf[0]), AtomTypeDecodeToString(tp->type2, typebuf[1]), AtomTypeDecodeToString(tp->type3, typebuf[2]), AtomTypeDecodeToString(tp->type4, typebuf[3]));
3428 else if (column == 6)
3429 snprintf(buf, bufsize, "%d", tp->period[0]);
3430 else snprintf(buf, bufsize, "%.3f", (column == 5 ? tp->k[0] * INTERNAL2KCAL : tp->phi0[0] * kRad2Deg));
3433 default: buf[0] = 0; break;
3435 } else if (mview->tableIndex == kMainViewUnitCellTableIndex) { /* Unit cell info */
3437 if (mview->mol->cell != NULL) {
3438 static const char *unitCellRowTitles[] = {"a", "b", "c", "alpha", "beta", "gamma", "a_valid", "b_valid", "c_valid", "a_vec", "b_vec", "c_vec", "origin"};
3440 snprintf(buf, bufsize, "%s", unitCellRowTitles[row]);
3443 case 0: case 1: case 2:
3445 snprintf(buf, bufsize, "%.5f", mview->mol->cell->cell[row]);
3446 else if (column == 2 && mview->mol->cell->has_sigma)
3447 snprintf(buf, bufsize, "%.5f", mview->mol->cell->cellsigma[row]);
3449 case 3: case 4: case 5:
3451 snprintf(buf, bufsize, "%.4f", mview->mol->cell->cell[row]);
3452 else if (column == 2 && mview->mol->cell->has_sigma)
3453 snprintf(buf, bufsize, "%.4f", mview->mol->cell->cellsigma[row]);
3455 case 6: case 7: case 8:
3457 snprintf(buf, bufsize, "%d", (int)mview->mol->cell->flags[row - 6]);
3459 case 9: case 10: case 11: case 12: {
3460 Vector *vp = (row == 12 ? &mview->mol->cell->origin : &mview->mol->cell->axes[row - 9]);
3461 Double dval = (column == 1 ? vp->x : (column == 2 ? vp->y : vp->z));
3462 snprintf(buf, bufsize, "%.5f", dval);
3468 } else if (mview->tableIndex == kMainViewXtalCoordTableIndex) { /* fractional coords */
3470 idx = MainView_tableRowToIndex(mview, row);
3471 ap[0] = ATOM_AT_INDEX(mol->atoms, idx);
3472 if (mview->mol->cell != NULL)
3473 TransformVec(&fract_r, mview->mol->cell->rtr, &(ap[0]->r));
3474 else fract_r = ap[0]->r;
3476 case 0: snprintf(buf, bufsize, "%d", idx); break;
3477 case 1: snprintf(buf, bufsize, "%.4s", ap[0]->aname); break;
3478 case 2: snprintf(buf, bufsize, "%.2s", ap[0]->element); break;
3479 case 3: snprintf(buf, bufsize, "%.3f", fract_r.x); break;
3480 case 4: snprintf(buf, bufsize, "%.3f", fract_r.y); break;
3481 case 5: snprintf(buf, bufsize, "%.3f", fract_r.z); break;
3482 case 6: snprintf(buf, bufsize, "%.3f", ap[0]->occupancy); break;
3483 case 7: snprintf(buf, bufsize, "%.3f", ap[0]->tempFactor); break;
3484 default: buf[0] = 0; break;
3486 } else if (mview->tableIndex == kMainViewMOTableIndex) { /* MO energy */
3487 BasisSet *bset = mview->mol->bset;
3490 if (bset != NULL && idx >= 0 && idx < bset->ncomps) {
3492 snprintf(buf, bufsize, "%d", idx + 1);
3493 } else if (column >= 3) {
3494 return; /* Cannot happen */
3496 /* column == 1, alpha; column == 2, beta */
3500 if (idx >= bset->ne_alpha)
3501 eflag = '*'; /* Unoccupied */
3503 if (bset->rflag == 2 && idx >= bset->ne_beta)
3504 eflag = 'S'; /* Singly occupied */
3508 if (bset->rflag != 0)
3509 return; /* No beta orbitals */
3510 if (idx >= bset->ne_beta)
3511 eflag = '*'; /* Unoccupied */
3512 idx += bset->ncomps;
3514 snprintf(buf, bufsize, "%c %.8f", eflag, bset->moenergies[idx]);
3520 /* Set color for the table */
3522 MainView_setColorForTable(MainView *mview, int column, int row, float *fg, float *bg)
3528 if (mview->tableIndex == kMainViewParameterTableIndex && column == -1) {
3529 int src = ParameterTableGetItemSource(mview->mol->par, row);
3530 if (src == -2) { /* separator line */
3531 bg[0] = bg[1] = bg[2] = 0.6;
3533 } else if (src == -1) { /* undefined parameters */
3535 bg[1] = bg[2] = 0.2;
3537 } else if (src == 0) { /* local parameter */
3538 bg[0] = bg[1] = 1.0;
3542 } else if (mview->tableIndex == kMainViewMOTableIndex && column == -1) {
3543 BasisSet *bset = mview->mol->bset;
3547 if (row < 0 || row >= bset->ncomps)
3549 if (row < bset->ne_beta)
3550 n = 0; /* Occupied */
3551 else if (row < bset->ne_alpha)
3552 n = 1; /* Half-occupied */
3553 else n = 2; /* Unoccupied */
3555 case 1: bg[0] = bg[1] = bg[2] = 0.9; break;
3556 case 2: bg[0] = bg[1] = bg[2] = 0.8; break;
3557 default: bg[0] = bg[1] = bg[2] = 1.0; break;
3560 } else if (mview->tableIndex < kMainViewBondTableIndex || mview->tableIndex > kMainViewImproperTableIndex)
3563 /* Bond etc. table */
3564 switch (mview->tableIndex) {
3565 case kMainViewBondTableIndex:
3566 parType = kBondParType;
3568 case kMainViewAngleTableIndex:
3569 parType = kAngleParType;
3571 case kMainViewDihedralTableIndex:
3572 parType = kDihedralParType;
3574 case kMainViewImproperTableIndex:
3575 parType = kImproperParType;
3583 idx = MainView_tableRowToIndex(mview, row);
3584 up = sParameterOfTypeAtIndex(mview->mol, parType, idx);
3589 /* Value column; warn if the value is "abnormal" */
3592 case kBondParType: f = md_check_abnormal_bond(mview->mol->arena, mview->mol, idx); break;
3593 case kAngleParType: f = md_check_abnormal_angle(mview->mol->arena, mview->mol, idx); break;
3594 case kDihedralParType: f = md_check_abnormal_dihedral(mview->mol->arena, mview->mol, idx); break;
3595 case kImproperParType: f = md_check_abnormal_improper(mview->mol->arena, mview->mol, idx); break;
3600 bg[1] = bg[2] = 0.5;
3604 if (up->bond.src == 0) {
3605 bg[0] = bg[1] = 1.0;
3608 } else if (up->bond.src == -1) {
3610 bg[1] = bg[2] = 0.2;
3618 MainView_setSelectionFromTable(MainView *mview)
3621 if (mview == NULL || mview->mol == NULL)
3623 switch (mview->tableIndex) {
3624 case kMainViewAtomTableIndex:
3625 case kMainViewBondTableIndex:
3626 case kMainViewAngleTableIndex:
3627 case kMainViewDihedralTableIndex:
3628 case kMainViewImproperTableIndex:
3629 case kMainViewXtalCoordTableIndex:
3630 break; /* Continue */
3632 return; /* Do nothing */
3634 ig = MainViewCallback_getTableSelection(mview);
3635 sel = IntGroupNew();
3639 for (i = 0; (i1 = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
3640 i2 = IntGroupGetNthPoint(mview->tableCache, i1);
3643 if (mview->tableIndex == kMainViewAtomTableIndex ||
3644 mview->tableIndex == kMainViewXtalCoordTableIndex) { /* Atoms */
3645 IntGroupAdd(sel, i2, 1);
3646 } else if (mview->tableIndex == kMainViewBondTableIndex) { /* Bonds */
3647 ip = mview->mol->bonds + i2 * 2;
3648 IntGroupAdd(sel, ip[0], 1);
3649 IntGroupAdd(sel, ip[1], 1);
3650 } else if (mview->tableIndex == kMainViewAngleTableIndex) { /* Angles */
3651 ip = mview->mol->angles + i2 * 3;
3652 IntGroupAdd(sel, ip[0], 1);
3653 IntGroupAdd(sel, ip[1], 1);
3654 IntGroupAdd(sel, ip[2], 1);
3655 } else if (mview->tableIndex == kMainViewDihedralTableIndex || mview->tableIndex == kMainViewImproperTableIndex) { /* Dihedrals, impropers */
3656 ip = (mview->tableIndex == kMainViewDihedralTableIndex ? mview->mol->dihedrals : mview->mol->impropers) + i2 * 4;
3657 IntGroupAdd(sel, ip[0], 1);
3658 IntGroupAdd(sel, ip[1], 1);
3659 IntGroupAdd(sel, ip[2], 1);
3660 IntGroupAdd(sel, ip[3], 1);
3663 IntGroupRelease(ig);
3665 MoleculeSetSelection(mview->mol, sel);
3666 IntGroupRelease(sel);
3670 sMainView_ParameterTableSetItemText(Molecule *mol, int column, int row, const char *s)
3674 const char *kstr = NULL;
3675 UnionPar *up = NULL;
3676 if (mol == NULL || (par = mol->par) == NULL || row < 0)
3678 up = ParameterTableGetItemPtr(par, row, &type);
3684 case 1: kstr = "atom_type"; break;
3685 case 2: kstr = "eps"; break;
3686 case 3: kstr = "r_eq"; break;
3687 case 4: kstr = "eps14"; break;
3688 case 5: kstr = "r_eq14"; break;
3689 case 6: kstr = "atomic_number"; break;
3690 case 7: kstr = "weight"; break;
3695 case 1: kstr = "atom_types"; break;
3696 case 2: kstr = "k"; break;
3697 case 3: kstr = "r0"; break;
3702 case 1: kstr = "atom_types"; break;
3703 case 2: kstr = "k"; break;
3704 case 3: kstr = "a0"; break;
3707 case kDihedralParType:
3708 case kImproperParType:
3710 case 1: kstr = "atom_types"; break;
3711 case 2: kstr = "k"; break;
3712 case 3: kstr = "period"; break;
3713 case 4: kstr = "phi0"; break;
3716 case kVdwPairParType:
3718 case 1: kstr = "atom_types"; break;
3719 case 2: kstr = "eps"; break;
3720 case 3: kstr = "r_eq"; break;
3721 case 4: kstr = "eps14"; break;
3722 case 5: kstr = "r_eq14"; break;
3730 idx = ParameterTableGetItemIndex(par, row, &type);
3731 MolActionCreateAndPerform(mol, SCRIPT_ACTION("iissi"), "set_parameter_attr", type, idx, kstr, s, 0);
3735 MainView_setValueForTable(MainView *mview, int column, int row, const char *buf)
3741 if (mview == NULL || (mol = mview->mol) == NULL)
3743 MainView_valueForTable(mview, column, row, temp, sizeof temp);
3744 if (strcmp(buf, temp) == 0 || buf[0] == 0)
3745 return; /* No change */
3746 idx = MainView_tableRowToIndex(mview, row);
3747 if (mview->tableIndex == kMainViewAtomTableIndex) { /* Atoms */
3749 case 1: key = "name"; break;
3750 case 2: key = "atom_type"; break;
3751 case 3: key = "element"; break;
3752 case 4: { /* Residue */
3753 IntGroup *ig = IntGroupNewWithPoints(idx, 1, -1);
3754 MolActionCreateAndPerform(mol, SCRIPT_ACTION("Gs"), "assign_residue", ig, buf);
3755 IntGroupRelease(ig);
3758 case 5: key = "x"; break;
3759 case 6: key = "y"; break;
3760 case 7: key = "z"; break;
3761 case 8: key = "charge"; break;
3764 MolActionCreateAndPerform(mol, SCRIPT_ACTION("iss"), "set_atom_attr", idx, key, buf);
3765 } else if (mview->tableIndex == kMainViewParameterTableIndex) { /* Parameters */
3766 sMainView_ParameterTableSetItemText(mview->mol, column, row, buf);
3767 } else if (mview->tableIndex == kMainViewUnitCellTableIndex) { /* Unit cell */
3768 Double cellPars[12], dval;
3772 if (mol->cell == NULL) {
3774 static const Double sCellPars[] = {1, 1, 1, 90, 90, 90};
3775 MolActionCreateAndPerform(mol, gMolActionSetCell, 6, sCellPars, 0);
3777 if (row >= 0 && row < 6) {
3779 memmove(cellPars, mol->cell->cell, sizeof(Double) * 6);
3780 if (mol->cell->has_sigma)
3781 memmove(cellPars + 6, mol->cell->cellsigma, sizeof(Double) * 6);
3782 else memset(cellPars + 6, 0, sizeof(Double) * 6);
3783 dval = strtod(buf, NULL);
3787 cellPars[row] = dval;
3790 cellPars[row + 6] = dval;
3792 MolActionCreateAndPerform(mol, gMolActionSetCell, n1, cellPars, 0);
3794 memmove(vecs, mol->cell->axes, sizeof(Vector) * 3);
3795 vecs[3] = mol->cell->origin;
3796 memmove(flags, mol->cell->flags, 3);
3797 if (row >= 6 && row < 9)
3798 flags[row - 6] = strtol(buf, NULL, 0);
3800 Vector *vp = vecs + (row - 9);
3801 dval = strtod(buf, NULL);
3804 else if (column == 2)
3808 MolActionCreateAndPerform(mol, gMolActionSetBox, vecs, vecs + 1, vecs + 2, vecs + 3, (flags[0] != 0) * 4 + (flags[1] != 0) * 2 + (flags[2] != 0), 0);
3811 } else if (mview->tableIndex == kMainViewXtalCoordTableIndex) { /* Fractional coords */
3813 case 1: key = "name"; break;
3814 case 2: key = "element"; break;
3815 case 3: key = "fract_x"; break;
3816 case 4: key = "fract_y"; break;
3817 case 5: key = "fract_z"; break;
3818 case 6: key = "occupancy"; break;
3819 case 7: key = "temp_factor"; break;
3822 MolActionCreateAndPerform(mol, SCRIPT_ACTION("iss"), "set_atom_attr", idx, key, buf);
3827 MainView_isTableItemEditable(MainView *mview, int column, int row)
3830 return ParameterTableIsItemEditable(gBuiltinParameters, column, row);
3831 if (mview->mol == NULL)
3833 if (mview->tableIndex == kMainViewParameterTableIndex)
3834 return ParameterTableIsItemEditable(mview->mol->par, column, row);
3835 if (mview->tableIndex == kMainViewUnitCellTableIndex) {
3836 if ((row >= 0 && row < 6 && column >= 1 && column <= 3) ||
3837 (row >= 6 && row < 9 && column == 1) ||
3838 (row >= 9 && row < 13 && column >= 1 && column <= 3))
3842 if (mview->tableIndex >= 0 && mview->tableIndex < sizeof(sColumnInfo) / sizeof(sColumnInfo[0]))
3843 return sColumnInfo[mview->tableIndex][column].editable != 0;
3848 MainView_tableType(MainView *mview)
3851 return kMainViewParameterTableIndex;
3852 if (mview->mol == NULL)
3854 return mview->tableIndex;
3858 MainView_dragTableSelectionToRow(MainView *mview, int row)
3860 Int *new2old, i, n, count, natoms, start_idx, to_idx;
3861 IntGroup *sel, *sel2;
3862 if (mview == NULL || mview->mol == NULL)
3864 if (mview->tableIndex != kMainViewAtomTableIndex && mview->tableIndex != kMainViewXtalCoordTableIndex)
3865 return; /* Only atom tables can respond */
3866 if (md_is_running(mview->mol->arena)) {
3867 MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
3870 n = MainView_numberOfRowsInTable(mview);
3871 if (row < 0 || row > n)
3872 return; /* Out of range */
3873 natoms = mview->mol->natoms;
3877 to_idx = MainView_tableRowToIndex(mview, row);
3878 sel = MoleculeGetSelection(mview->mol);
3879 if (sel == NULL || (count = IntGroupGetCount(sel)) == 0)
3881 new2old = (Int *)calloc(sizeof(Int), natoms);
3882 if (new2old == NULL)
3885 // Place the atoms above the target position
3886 for (i = n = 0; i < to_idx; i++) {
3887 if (IntGroupLookupPoint(sel, i) < 0)
3891 // Place the atoms within the selection
3892 for (i = 0; i < count; i++) {
3893 new2old[n++] = IntGroupGetNthPoint(sel, i);
3895 // Place the remaining atoms
3896 for (i = to_idx; i < natoms; i++) {
3897 if (IntGroupLookupPoint(sel, i) < 0)
3900 MolActionCreateAndPerform(mview->mol, gMolActionRenumberAtoms, n, new2old);
3903 // Molecule selection (atom indices)
3904 sel = IntGroupNewWithPoints(start_idx, count, -1);
3905 MolActionCreateAndPerform(mview->mol, gMolActionSetSelection, sel);
3906 // Table selection (row numbers)
3907 sel2 = IntGroupNew();
3908 for (i = 0; i < count; i++) {
3909 int row_i = MainView_indexToTableRow(mview, i + start_idx);
3911 IntGroupAdd(sel2, row_i, 1);
3913 MainViewCallback_setTableSelection(mview, sel2);
3914 IntGroupRelease(sel2);
3915 IntGroupRelease(sel);
3919 MainView_isRowSelectable(MainView *mview, int row)
3921 if (mview->tableIndex == kMainViewParameterTableIndex) {
3922 int src = ParameterTableGetItemSource(mview->mol->par, row);
3923 if (src == -2) { /* separator line */
3931 MainView_selectedMO(MainView *mview)
3935 if (mview == NULL || mview->mol == NULL || mview->tableIndex != kMainViewMOTableIndex)
3937 return MainViewCallback_getTableSelection(mview); /* Note: the indices are 0 based */