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"
31 #define biso2radius(r) ((r) > 0.5 ? sqrt((r) / 78.9568352087147) : 0.08)
33 /* pp is a pointer to Parameter record */
34 #define VDW_RADIUS(pp) (pp->vdw_radius == 0.0 ? pp->radius * 0.51 + 1.20 : pp->vdw_radius)
36 /* Invalid bond/angle/torsion value, used in internal cache */
37 const Double kInvalidValue = -10000000.0;
39 #pragma mark ==== MainView public methods ====
42 MainView_setViewObject(MainView *mview, void *ref)
44 static GLdouble sIdentity[16] = {
45 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1
48 if (mview->ref == NULL) {
49 /* Initialize GUI-only members */
50 mview->mode = kTrackballRotateMode;
51 mview->tempAtoms[0] = mview->tempAtoms[1] = -1;
52 memmove(mview->modelview_matrix, sIdentity, sizeof(sIdentity));
53 memmove(mview->projection_matrix, sIdentity, sizeof(sIdentity));
54 mview->tableCache = IntGroupNew();
55 mview->tableSelection = IntGroupNew();
57 mview->ref = ref; /* No retain */
58 IntGroupClear(mview->tableCache);
59 IntGroupClear(mview->tableSelection);
60 MoleculeCallback_notifyModification(mview->mol, 0);
65 MainView_isAtomHidden(MainView *mview, int index)
67 if (index < 0 || index >= mview->mol->natoms)
69 else if (mview->visibleFlags == NULL)
71 else return (mview->visibleFlags[index] == 0);
72 /* Atom *ap = ATOM_AT_INDEX(mview->mol->atoms, index);
73 if (ap->exflags & kAtomHiddenFlag)
75 if (!mview->showHydrogens && ap->atomicNumber == 1)
77 if (!mview->showDummyAtoms && ap->atomicNumber == 0)
83 MainView_refreshCachedInfo(MainView *mview)
86 int i, j, n1, n2, n3, n4;
90 if (mview == NULL || (mol = mview->mol) == NULL)
93 /* Rebuild internal caches */
96 if (mview->visibleFlags == NULL)
97 mview->visibleFlags = (Byte *)malloc(mol->natoms);
99 mview->visibleFlags = (Byte *)realloc(mview->visibleFlags, mol->natoms);
100 memset(mview->visibleFlags, 0, mol->natoms);
101 mview->countHidden = 0;
102 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
103 if (ap->exflags & kAtomHiddenFlag) {
104 mview->countHidden++;
107 if (!mview->showHydrogens && ap->atomicNumber == 1)
109 if (!mview->showDummyAtoms && ap->atomicNumber == 0)
111 mview->visibleFlags[i] = 1;
114 /* Selection (only temporary used within this function) */
116 IntGroup *sel = MoleculeGetSelection(mol);
117 if (sel != NULL && IntGroupGetCount(sel) > 0) {
118 for (i = 0; (n1 = IntGroupGetStartPoint(sel, i)) >= 0; i++) {
119 n2 = IntGroupGetEndPoint(sel, i);
120 if (n2 > mol->natoms)
123 mview->visibleFlags[n1++] |= 2;
129 if (mview->tableCache == NULL)
130 mview->tableCache = IntGroupNew();
131 if (mview->tableSelection == NULL)
132 mview->tableSelection = IntGroupNew();
133 IntGroupClear(mview->tableCache);
134 IntGroupClear(mview->tableSelection);
136 if (mview->tableIndex == kMainViewAtomTableIndex ||
137 mview->tableIndex == kMainViewXtalCoordTableIndex) { /* Atoms */
138 for (i = j = 0; i < mol->natoms; i++) {
139 f1 = mview->visibleFlags[i];
141 IntGroupAdd(mview->tableCache, i, 1);
143 IntGroupAdd(mview->tableSelection, j, 1);
147 } else if (mview->tableIndex == kMainViewBondTableIndex) { /* Bonds */
148 for (i = j = 0; i < mol->nbonds; i++) {
149 n1 = mol->bonds[i * 2];
150 n2 = mol->bonds[i * 2 + 1];
151 f1 = mview->visibleFlags[n1];
152 f2 = mview->visibleFlags[n2];
153 if ((f1 & 1) != 0 && (f2 & 1) != 0) {
154 IntGroupAdd(mview->tableCache, i, 1);
155 if ((f1 & 2) != 0 && (f2 & 2) != 0)
156 IntGroupAdd(mview->tableSelection, j, 1);
160 } else if (mview->tableIndex == kMainViewAngleTableIndex) { /* Angles */
161 for (i = j = 0; i < mol->nangles; i++) {
162 n1 = mol->angles[i * 3];
163 n2 = mol->angles[i * 3 + 1];
164 n3 = mol->angles[i * 3 + 2];
165 f1 = mview->visibleFlags[n1];
166 f2 = mview->visibleFlags[n2];
167 f3 = mview->visibleFlags[n3];
168 if ((f1 & 1) != 0 && (f2 & 1) != 0 && (f3 & 1) != 0) {
169 IntGroupAdd(mview->tableCache, i, 1);
170 if ((f1 & 2) != 0 && (f2 & 2) != 0 && (f3 & 2) != 0)
171 IntGroupAdd(mview->tableSelection, j, 1);
175 } else if (mview->tableIndex == kMainViewDihedralTableIndex) { /* Dihedrals */
176 for (i = j = 0; i < mol->ndihedrals; i++) {
177 n1 = mol->dihedrals[i * 4];
178 n2 = mol->dihedrals[i * 4 + 1];
179 n3 = mol->dihedrals[i * 4 + 2];
180 n4 = mol->dihedrals[i * 4 + 3];
181 f1 = mview->visibleFlags[n1];
182 f2 = mview->visibleFlags[n2];
183 f3 = mview->visibleFlags[n3];
184 f4 = mview->visibleFlags[n4];
185 if ((f1 & 1) != 0 && (f2 & 1) != 0 && (f3 & 1) != 0 && (f4 & 1) != 0) {
186 IntGroupAdd(mview->tableCache, i, 1);
187 if ((f1 & 2) != 0 && (f2 & 2) != 0 && (f3 & 2) != 0 && (f4 & 2) != 0)
188 IntGroupAdd(mview->tableSelection, j, 1);
192 } else if (mview->tableIndex == kMainViewImproperTableIndex) { /* Impropers */
193 for (i = j = 0; i < mol->nimpropers; i++) {
194 n1 = mol->impropers[i * 4];
195 n2 = mol->impropers[i * 4 + 1];
196 n3 = mol->impropers[i * 4 + 2];
197 n4 = mol->impropers[i * 4 + 3];
198 f1 = mview->visibleFlags[n1];
199 f2 = mview->visibleFlags[n2];
200 f3 = mview->visibleFlags[n3];
201 f4 = mview->visibleFlags[n4];
202 if ((f1 & 1) != 0 && (f2 & 1) != 0 && (f3 & 1) != 0 && (f4 & 1) != 0) {
203 IntGroupAdd(mview->tableCache, i, 1);
204 if ((f1 & 2) != 0 && (f2 & 2) != 0 && (f3 & 2) != 0 && (f4 & 2) != 0)
205 IntGroupAdd(mview->tableSelection, j, 1);
210 /* Cache is left empty (not used) */
213 // } else if (mview->tableIndex == kMainViewParameterTableIndex || /* Parameter infos */
214 // mview->tableIndex == kMainViewUnitCellTableIndex) { /* Unit cell infos */
215 // /* Do nothing (tableCache will not be used) */
216 // } else if (mview->tableIndex == kMainViewMOTableIndex) { /* MO infos */
217 // /* Really no need to cache info, but create it anyway to simplify code */
218 // if (mol->bset != NULL && mol->bset->ncomps > 0)
219 // IntGroupAdd(mview->tableCache, 0, mol->bset->ncomps);
222 /* Clear internal selection flag */
223 for (i = 0; i < mol->natoms; i++) {
224 mview->visibleFlags[i] &= ~2;
228 #pragma mark ====== 2D/3D transform operations ======
231 MainView_resizeToFit(MainView *mview)
235 if (mview == NULL || mview->mol == NULL)
237 if (mview->mol->natoms == 0) {
238 TrackballReset(mview->track);
241 MoleculeCenterOfMass(mview->mol, &p, NULL);
242 f[0] = -p.x / mview->dimension;
243 f[1] = -p.y / mview->dimension;
244 f[2] = -p.z / mview->dimension;
245 TrackballSetTranslate(mview->track, f);
248 r0: the longest distance from the center of mass
249 r0 > dimension: scale = -log(r0/dimension)/log(10) (negative)
250 r0 < dimension: scale = -log(atan2(r0, dimension*cot(15deg))*180deg/pi*2/30deg)/log(10) (positive)
256 double r0 = 0.0, r1, scale;
257 for (i = 0, ap = mview->mol->atoms; i < mview->mol->natoms; i++, ap = ATOM_NEXT(ap)) {
264 r0 /= mview->dimension;
268 scale = -log(atan2(r0, kCot15Deg) * kRad2Deg * 2 / 30.0) / kLog10;
270 scale = -log(r0) / kLog10;
271 TrackballSetScale(mview->track, scale);
274 MainViewCallback_setNeedsDisplay(mview, 1);
278 MainView_convertScreenPositionToObjectPosition(MainView *mview, const GLfloat *screenPos, double *objectPos)
281 GLint viewport[4], n;
283 GLdouble posX, posY, posZ;
286 MainViewCallback_frame(mview, rect);
287 viewport[0] = viewport[1] = 0;
288 viewport[2] = (GLint)(rect[2] - rect[0]);
289 viewport[3] = (GLint)(rect[3] - rect[1]);
290 MainViewCallback_lockFocus(mview);
291 if (screenPos[2] >= 0.0)
294 glReadPixels(screenPos[0], screenPos[1], 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
295 gluUnProject(screenPos[0], screenPos[1], winZ, mview->modelview_matrix, mview->projection_matrix, viewport, &posX, &posY, &posZ);
297 MainViewCallback_unlockFocus(mview);
301 if (n != GL_NO_ERROR || winZ == 1.0)
308 MainView_convertObjectPositionToScreenPosition(MainView *mview, const double *objectPos, GLfloat *screenPos)
312 GLdouble objX, objY, objZ;
315 MainViewCallback_frame(mview, rect);
316 viewport[0] = viewport[1] = 0;
317 viewport[2] = (GLint)(rect[2] - rect[0]);
318 viewport[3] = (GLint)(rect[3] - rect[1]);
319 gluProject(objectPos[0], objectPos[1], objectPos[2], mview->modelview_matrix, mview->projection_matrix, viewport, &objX, &objY, &objZ);
320 if (glGetError() == GL_NO_ERROR) {
324 /* fprintf(stderr, "object(%.3f,%.3f,%.3f) screen(%.3f,%.3f,%.3f)\n", objectPos[0], objectPos[1], objectPos[2], screenPos[0], screenPos[1], screenPos[2]); */
330 MainView_findObjectAtPoint(MainView *mview, const float *mousePos, int *outIndex1, int *outIndex2, int mouseCheck, int ignoreExpandedAtoms)
333 double op[3], oq[3], pqlen, pqlen2;
334 Vector pq, pa, v1, r1, r2;
335 int i, natoms, nbonds;
338 const ElementPar *ep;
344 if (mview == NULL || mview->mol == NULL) {
345 *outIndex1 = *outIndex2 = -1;
350 screenPos[0] = mousePos[0];
351 screenPos[1] = mousePos[1];
353 if (MainView_convertScreenPositionToObjectPosition(mview, screenPos, op) == 0)
354 return 0; /* Nothing is here */
356 /* PQ is the part of the eyesight line in the visible area */
358 MainView_convertScreenPositionToObjectPosition(mview, screenPos, op);
360 MainView_convertScreenPositionToObjectPosition(mview, screenPos, oq);
361 pq.x = oq[0] - op[0];
362 pq.y = oq[1] - op[1];
363 pq.z = oq[2] - op[2];
364 pqlen2 = VecLength2(pq);
365 pqlen = sqrt(pqlen2);
366 natoms = mol->natoms;
369 for (i = 0; i < natoms; i++) {
371 double pq1len2, pq1len;
372 if (mouseCheck && i % 50 == 0 && MainViewCallback_mouseCheck(mview))
373 return 0; /* If mouse event is detected return immediately */
374 /* Examine if an atom is visible or not */
375 /* The distance of the atom center (A) from line PQ: */
376 /* d = |VecCross(PA,PQ)|/|PQ| */
377 /* z = VecDot(PA,PQ)/|PQ|^2 - sqrt(r^2 - d^2)/|PQ| */
378 ap = ATOM_AT_INDEX(mol->atoms, i);
381 if (MainView_isAtomHidden(mview, i))
383 if (ignoreExpandedAtoms && SYMOP_ALIVE(ap->symop))
386 /* if (mol->is_xtal_coord)
387 TransformVec(&r1, mol->cell->tr, &r1); */
391 if (mview->showEllipsoids && ap->aniso != NULL) {
392 /* Convert to ellipsoid principal axes */
394 Aniso *anp = ap->aniso;
395 MatrixInvert(m1, anp->pmat);
396 MatrixVec(&pq1, m1, &pq);
397 MatrixVec(&pa1, m1, &pa);
398 r = mview->probabilityScale;
399 pq1len2 = VecLength2(pq1);
400 pq1len = sqrt(pq1len2);
402 if (mview->showEllipsoids) {
403 r = biso2radius(ap->tempFactor) * mview->probabilityScale;
405 ep = &(gElementParameters[ap->atomicNumber]);
408 r = VDW_RADIUS(ep) * mview->atomRadius;
415 VecCross(v1, pa1, pq1);
416 d2 = VecLength2(v1) / pq1len2;
418 continue; /* Not visible */
419 z = VecDot(pa1, pq1) / pq1len2 - sqrt(r * r - d2) / pq1len;
420 if (z < 0.0 || z > 1.0)
421 continue; /* Out of viewing volume */
427 nbonds = mol->nbonds;
428 for (i = 0; i < nbonds; i++) {
429 Vector vx, vy, vz, vv, vp;
430 Double wb, wa, t, wx, blen;
431 if (mouseCheck && i % 50 == 0 && MainViewCallback_mouseCheck(mview))
432 return 0; /* If mouse event is detected return immediately */
433 /* Examine if a bond is visible or not */
434 ip = &(mol->bonds[i * 2]);
435 ap = ATOM_AT_INDEX(mol->atoms, ip[0]);
436 bp = ATOM_AT_INDEX(mol->atoms, ip[1]);
437 if (MainView_isAtomHidden(mview, ip[0]) || MainView_isAtomHidden(mview, ip[1]))
439 if (ignoreExpandedAtoms && SYMOP_ALIVE(ap->symop) && SYMOP_ALIVE(bp->symop))
441 /* vx/vy/vz is a orthonormal base in which AB parallels the x-axis
442 and AP in in the xy plane */
443 /* vp and vv is AP and PQ in that coordinate system */
446 /* if (mol->is_xtal_coord) {
447 TransformVec(&r1, mol->cell->tr, &r1);
448 TransformVec(&r2, mol->cell->tr, &r2);
454 blen = sqrt(VecLength2(vx));
460 VecCross(vz, vx, v1);
461 if (NormalizeVec(&vz, &vz))
463 VecCross(vy, vz, vx);
464 vp.x = VecDot(v1, vx);
465 vp.y = VecDot(v1, vy);
466 vp.z = VecDot(v1, vz);
467 vv.x = VecDot(pq, vx);
468 vv.y = VecDot(pq, vy);
469 vv.z = VecDot(pq, vz);
470 /* The bond surface is y^2 + z^2 = r^2, 0 <= x <= 1 */
471 /* The eyesight line is (x,y,z) = vp + t * vv, 0 <= t <= 1 */
472 /* 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) */
473 /* (Note that vp.z = 0 by definition) */
474 r = mview->bondRadius;
476 wa = vv.y * vv.y + vv.z * vv.z;
477 d2 = wb * wb - wa * (vp.y * vp.y - r * r);
479 continue; /* Not visible */
480 t = (-wb - sqrt(d2)) / wa;
482 continue; /* Out of visible volume */
483 wx = vp.x + t * vv.x;
484 if (wx < 0 || wx > blen)
485 continue; /* Outside of bond */
494 return (n1 >= 0 || n2 >= 0);
498 MainView_screenCenterPointOfAtom(MainView *mview, int index, float *outScreenPos)
501 const ElementPar *dp;
506 if (mview == NULL || mview->mol == NULL || index < 0 || index >= mview->mol->natoms)
508 ap = ATOM_AT_INDEX(mview->mol->atoms, index);
510 /* Camera position in object coordinates */
513 /* The atom position (in Cartesian) */
515 /* if (mview->mol->is_xtal_coord)
516 TransformVec(&v, mview->mol->cell->tr, &v); */
518 /* The vector from atom center to camera */
521 /* Get the surface point of the ellipsoid/sphere along the camera vector */
522 if (mview->showEllipsoids) {
524 Aniso *anp = ap->aniso;
527 MatrixInvert(m1, anp->pmat);
528 /* Convert the 'screen plane' vectors to ellipsoid principal axes */
530 VecCross(vx, mview->lookto, vy);
531 MatrixVec(&vx, m1, &vx);
532 MatrixVec(&vy, m1, &vy);
533 /* Touching point of the 'screen plane' to the ellipsoid */
534 VecCross(vz, vx, vy);
535 w = mview->probabilityScale / VecLength(vz) * 1.1;
537 MatrixVec(&vz, anp->pmat, &vz);
538 /* The crossing point of the camera vector with the touching plane */
539 w = fabs(VecDot(pv, vz) / VecLength2(pv));
542 w = mview->probabilityScale * biso2radius(ap->tempFactor) / VecLength(pv) * 1.1;
546 dp = &(gElementParameters[ap->atomicNumber]);
547 rad = VDW_RADIUS(dp) * mview->atomRadius;
548 w = rad / VecLength(pv) * 1.1;
553 /* Transform to screen coordinate */
557 return MainView_convertObjectPositionToScreenPosition(mview, p, outScreenPos);
561 MainView_getCamera(MainView *mview, Vector *outCamera, Vector *outLookAt, Vector *outUp)
564 *outCamera = mview->camera;
565 *outLookAt = mview->lookat;
570 #pragma mark ====== Draw model ======
575 static GLfloat pos[] = {0.0f, 0.0f, 1.0f, 0.0f};
576 glEnable(GL_LIGHTING);
578 /* On Mac OS 10.6, redefining the light position seems to be necessary
579 every time lighting is made enabled */
580 glMatrixMode(GL_MODELVIEW);
583 glLightfv(GL_LIGHT0, GL_POSITION, pos);
589 MainView_initializeOpenGL(void)
591 static GLfloat ambient[] = {0.6, 0.6, 0.6, 1.0}; // Some white ambient light.
592 static GLfloat diffuse[] = {1.0, 1.0, 1.0, 1.0}; // A white light.
594 glEnable(GL_DEPTH_TEST);
595 glEnable(GL_LIGHTING);
597 // Set the ambient light
598 glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
600 // Set the light and switch it on
601 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
606 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
609 /* Get orthogonal unit vectors */
611 getOrthogonalVectors(const GLfloat *ap, GLfloat *bp, GLfloat *cp)
614 ra = sqrt(ap[0] * ap[0] + ap[1] * ap[1] + ap[2] * ap[2]);
618 if (fabs(ap[0]) < fabs(ap[1])) {
619 if (fabs(ap[0]) < fabs(ap[2])) {
629 if (fabs(ap[1]) < fabs(ap[2])) {
639 rb = 1 / sqrt(bp[0] * bp[0] + bp[1] * bp[1] + bp[2] * bp[2]);
643 cp[0] = ra * (ap[1] * bp[2] - ap[2] * bp[1]);
644 cp[1] = ra * (ap[2] * bp[0] - ap[0] * bp[2]);
645 cp[2] = ra * (ap[0] * bp[1] - ap[1] * bp[0]);
646 /* printf("a = (%f, %f, %f) b = (%f, %f, %f) c = (%f, %f, %f)\n",
647 ap[0], ap[1], ap[2], bp[0], bp[1], bp[2], cp[0], cp[1], cp[2]); */
651 static GLfloat sSinCache[81];
652 static int sSinCacheSect = 0;
655 setSinCache(int sect)
662 if (n != sSinCacheSect) {
664 for (i = 0; i <= m * 5; i++)
665 sSinCache[i] = sin(3.14159265358979 * 2 / n * i);
671 drawCylinder(const GLfloat *a, const GLfloat *b, GLfloat r, int sect, int closed)
676 GLfloat d[3], v[3], w[3];
677 n = setSinCache(sect);
685 if (getOrthogonalVectors(d, v, w) == 0)
687 glBegin(GL_QUAD_STRIP);
688 for (i = 0; i <= n; i++) {
689 nx = v[0] * c[i] + w[0] * s[i];
690 ny = v[1] * c[i] + w[1] * s[i];
691 nz = v[2] * c[i] + w[2] * s[i];
692 glNormal3f(nx, ny, nz);
693 glVertex3f(a[0] + r * nx, a[1] + r * ny, a[2] + r * nz);
694 glVertex3f(b[0] + r * nx, b[1] + r * ny, b[2] + r * nz);
698 glBegin(GL_TRIANGLE_FAN);
699 glNormal3f(-d[0], -d[1], -d[2]);
700 for (i = 0; i <= n; i++) {
701 nx = v[0] * c[i] + w[0] * s[i];
702 ny = v[1] * c[i] + w[1] * s[i];
703 nz = v[2] * c[i] + w[2] * s[i];
704 glVertex3f(a[0] + r * nx, a[1] + r * ny, a[2] + r * nz);
707 glBegin(GL_TRIANGLE_FAN);
708 glNormal3f(d[0], d[1], d[2]);
709 for (i = 0; i <= n; i++) {
710 nx = v[0] * c[i] + w[0] * s[i];
711 ny = v[1] * c[i] + w[1] * s[i];
712 nz = v[2] * c[i] + w[2] * s[i];
713 glVertex3f(b[0] + r * nx, b[1] + r * ny, b[2] + r * nz);
720 drawCone(const GLfloat *a, const GLfloat *b, GLfloat r, int sect, int closed)
724 GLfloat d[3], v[3], w[3];
726 n = setSinCache(sect);
734 if (getOrthogonalVectors(d, v, w) == 0)
736 glBegin(GL_TRIANGLE_FAN);
740 NormalizeVec(&nv, &nv);
741 glNormal3f(nv.x, nv.y, nv.z);
742 glVertex3f(b[0], b[1], b[2]);
743 for (i = 0; i <= n; i++) {
744 nv.x = v[0] * c[i] + w[0] * s[i];
745 nv.y = v[1] * c[i] + w[1] * s[i];
746 nv.z = v[2] * c[i] + w[2] * s[i];
747 glNormal3f(nv.x, nv.y, nv.z);
748 p1.x = a[0] + r * nv.x;
749 p1.y = a[1] + r * nv.y;
750 p1.z = a[2] + r * nv.z;
751 glNormal3f(nv.x, nv.y, nv.z);
752 glVertex3f(p1.x, p1.y, p1.z);
756 glBegin(GL_TRIANGLE_FAN);
757 glNormal3f(d[0], d[1], d[2]);
758 for (i = 0; i <= n; i++) {
759 p1.x = a[0] + r * (v[0] * c[i] + w[0] * s[i]);
760 p1.y = a[1] + r * (v[1] * c[i] + w[1] * s[i]);
761 p1.z = a[2] + r * (v[2] * c[i] + w[2] * s[i]);
762 glVertex3f(p1.x, p1.y, p1.z);
769 drawSphere(const GLfloat *p, GLfloat r, int sect)
773 n = setSinCache(sect);
778 for (i = 0; i <= n; i++) {
779 glBegin(GL_QUAD_STRIP);
780 for (j = 1; j <= n / 2 - 1; j++) {
781 glNormal3f(s[j] * c[i], s[j] * s[i], c[j]);
782 glVertex3f(r * s[j] * c[i] + p[0], r * s[j] * s[i] + p[1], r * c[j] + p[2]);
783 glNormal3f(s[j] * c[i+1], s[j] * s[i+1], c[j]);
784 glVertex3f(r * s[j] * c[i+1] + p[0], r * s[j] * s[i+1] + p[1], r * c[j] + p[2]);
788 glBegin(GL_TRIANGLE_FAN);
790 glVertex3f(p[0], p[1], r + p[2]);
791 for (i = n; i >= 0; i--) {
792 glNormal3f(s[1] * c[i], s[1] * s[i], c[1]);
793 glVertex3f(r * s[1] * c[i] + p[0], r * s[1] * s[i] + p[1], r * c[1] + p[2]);
796 glBegin(GL_TRIANGLE_FAN);
797 glNormal3f(0, 0, -1);
798 glVertex3f(p[0], p[1], -r + p[2]);
799 for (i = 0; i <= n; i++) {
800 glNormal3f(s[1] * c[i], s[1] * s[i], -c[1]);
801 glVertex3f(r * s[1] * c[i] + p[0], r * s[1] * s[i] + p[1], -r * c[1] + p[2]);
807 drawEllipsoid(const GLfloat *p, const GLfloat *v1, const GLfloat *v2, const GLfloat *v3, int sect)
810 static const GLfloat origin[3] = {0, 0, 0};
811 mat[0] = v1[0]; mat[1] = v1[1]; mat[2] = v1[2]; mat[3] = 0;
812 mat[4] = v2[0]; mat[5] = v2[1]; mat[6] = v2[2]; mat[7] = 0;
813 mat[8] = v3[0]; mat[9] = v3[1]; mat[10] = v3[2]; mat[11] = 0;
814 mat[12] = p[0]; mat[13] = p[1]; mat[14] = p[2]; mat[15] = 1;
815 glMatrixMode(GL_MODELVIEW);
818 glEnable(GL_NORMALIZE);
819 // glutSolidSphere(1, sect, sect); /* Is this faster than my code? */
820 drawSphere(origin, 1, sect);
821 glDisable(GL_NORMALIZE);
826 temporarySelection(MainView *mview, int flags, int clickCount, int ignoreExpandedAtoms)
832 natoms = mview->mol->natoms;
833 selectFlags = (char *)calloc(sizeof(char), natoms);
834 if (selectFlags == NULL)
836 if (clickCount > 0) {
838 if (MainView_findObjectAtPoint(mview, mview->dragStartPos, &n1, &n2, 0, 0)) {
839 if (n1 >= 0 && n1 < natoms)
841 if (n2 >= 0 && n2 < natoms)
845 if (mview->dragStartPos[0] < mview->dragEndPos[0]) {
846 rect[0] = mview->dragStartPos[0];
847 rect[2] = mview->dragEndPos[0];
849 rect[0] = mview->dragEndPos[0];
850 rect[2] = mview->dragStartPos[0];
852 if (mview->dragStartPos[1] < mview->dragEndPos[1]) {
853 rect[1] = mview->dragStartPos[1];
854 rect[3] = mview->dragEndPos[1];
856 rect[1] = mview->dragEndPos[1];
857 rect[3] = mview->dragStartPos[1];
859 for (i = 0; i < natoms; i++) {
860 ap = ATOM_AT_INDEX(mview->mol->atoms, i);
863 if (MainView_isAtomHidden(mview, i))
865 if (ignoreExpandedAtoms && SYMOP_ALIVE(ap->symop))
867 if (mview->draggingMode == kMainViewSelectingRegion) {
868 /* Check if this atom is within the selection rectangle */
870 GLfloat screenPos[3];
873 /* if (mview->mol->is_xtal_coord)
874 TransformVec(&r1, mview->mol->cell->tr, &r1); */
878 if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos) && screenPos[0] >= rect[0] && screenPos[0] <= rect[2] && screenPos[1] >= rect[1] && screenPos[1] <= rect[3])
880 else selectFlags[i] = 0;
884 if (flags & kShiftKeyMask) {
885 for (i = 0; i < natoms; i++)
886 selectFlags[i] ^= (MoleculeIsAtomSelected(mview->mol, i) != 0);
892 drawGraphite(MainView *mview)
894 static GLfloat sDarkCyanColor[] = {0, 0.75, 0.75, 1};
896 MDGraphiteArena *graphite;
897 Vector xaxis, yaxis, zaxis, origin;
899 int i, j, i0, i1, j0, j1, ir;
900 Double x, dx, y, dy, xx, yy;
902 if ((arena = mview->mol->arena) != NULL && (graphite = arena->graphite) != NULL) {
903 graphite_get_axes(graphite, &origin, &xaxis, &yaxis, &zaxis, &R);
905 origin.x = origin.y = origin.z = 0.0;
906 xaxis.x = yaxis.y = zaxis.z = 1.0;
907 xaxis.y = xaxis.z = yaxis.x = yaxis.z = zaxis.x = zaxis.y = 0.0;
910 i0 = -(mview->showGraphite / 2) - 1;
911 i1 = i0 + mview->showGraphite + 1;
912 j0 = -(mview->showGraphite / 2);
913 j1 = j0 + mview->showGraphite;
915 dy = 0.866025403784439 * R;
916 glDisable(GL_LIGHTING);
917 glColor3fv(sDarkCyanColor);
918 for (i = i0; i <= i1; i++) {
919 for (j = j0; j <= j1; j++) {
921 ir = (i % 2 == 0 ? 0 : 1);
923 y = (2 * j + ir) * dy;
926 p[0] = xaxis.x * xx + yaxis.x * y + origin.x;
927 p[1] = xaxis.y * xx + yaxis.y * y + origin.y;
928 p[2] = xaxis.z * xx + yaxis.z * y + origin.z;
930 p[3] = xaxis.x * xx + yaxis.x * yy + origin.x;
931 p[4] = xaxis.y * xx + yaxis.y * yy + origin.y;
932 p[5] = xaxis.z * xx + yaxis.z * yy + origin.z;
934 p[6] = xaxis.x * xx + yaxis.x * yy + origin.x;
935 p[7] = xaxis.y * xx + yaxis.y * yy + origin.y;
936 p[8] = xaxis.z * xx + yaxis.z * yy + origin.z;
938 p[9] = xaxis.x * xx + yaxis.x * y + origin.x;
939 p[10] = xaxis.y * xx + yaxis.y * y + origin.y;
940 p[11] = xaxis.z * xx + yaxis.z * y + origin.z;
944 if ((ir == 0 && j == j0) || (ir == 1 && j == j1))
946 } else if (i == i1) {
948 if ((ir == 0 && j == j0) || (ir == 1 && j == j1))
950 } else if (j == j1) {
953 } else if (i == i0 + 1) {
955 } else if (i == i1 - 1) {
978 static GLfloat sRedColor[] = {1, 0, 0, 1};
981 drawAtom(MainView *mview, int i1, int selected, const Vector *dragOffset, const Vector *periodicOffset)
984 const ElementPar *dp;
993 Transform *trp = NULL;
994 int natoms = mview->mol->natoms;
996 /* Extra 2 atoms for the bond being newly created */
997 if (mview->draggingMode != kMainViewCreatingBond)
999 if (mview->tempAtoms[i1 - natoms] >= 0)
1000 return; /* Already drawn */
1003 r1 = mview->tempAtomPos[i1 - natoms];
1006 ap = ATOM_AT_INDEX(mview->mol->atoms, i1);
1009 an1 = ap->atomicNumber;
1011 strncpy(label, ap->aname, 4);
1013 if (SYMOP_ALIVE(ap->symop))
1016 if (!mview->showHydrogens && an1 == 1)
1018 if (!mview->showDummyAtoms && an1 == 0)
1020 if (!mview->showExpandedAtoms && expanded)
1022 if (ap != NULL && (ap->exflags & kAtomHiddenFlag))
1024 dp = &(gElementParameters[an1]);
1028 memcpy(rgba, sRedColor, sizeof(rgba));
1030 rgba[0] = dp->red / 65535.0;
1031 rgba[1] = dp->green / 65535.0;
1032 rgba[2] = dp->blue / 65535.0;
1035 if (expanded || periodicOffset != NULL) {
1040 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
1041 if (periodicOffset != NULL)
1042 VecInc(r1, *periodicOffset);
1043 p[0] = r1.x; p[1] = r1.y; p[2] = r1.z;
1044 if (mview->draggingMode == kMainViewDraggingSelectedAtoms && selected) {
1045 p[0] += dragOffset->x;
1046 p[1] += dragOffset->y;
1047 p[2] += dragOffset->z;
1049 rad = VDW_RADIUS(dp) * mview->atomRadius;
1050 if (mview->showEllipsoids) {
1051 if (ap != NULL && ap->aniso != NULL) {
1056 MatrixMul(pmat2, mview->mol->cell->rtr, ap->aniso->pmat);
1057 MatrixMul(pmat2, *((Mat33 *)trp), pmat2);
1058 MatrixMul(pmat2, mview->mol->cell->tr, pmat2);
1059 for (i = 0; i < 9; i++)
1060 elip[i] = pmat2[i] * mview->probabilityScale;
1062 for (i = 0; i < 9; i++)
1063 elip[i] = ap->aniso->pmat[i] * mview->probabilityScale;
1065 drawEllipsoid(p, elip, elip+3, elip+6, mview->atomResolution * 3 / 2); /* Use higher resolution than spheres */
1068 // Recalculate radius from temperature factor
1069 rad = biso2radius(ap->tempFactor);
1070 rad *= mview->probabilityScale;
1072 drawSphere(p, rad, mview->atomResolution);
1075 drawSphere(p, rad, mview->atomResolution);
1080 if (MainView_convertObjectPositionToScreenPosition(mview, pp, p + 3)) {
1081 /* fprintf(stderr, "atom %d: {%f, %f, %f}\n", i1, p[3], p[4], p[5]); */
1083 fp[0] = p[3]; fp[1] = p[4]; fp[2] = p[5];
1084 MainViewCallback_drawLabel(mview, fp, label);
1089 drawBond(MainView *mview, int i1, int i2, int selected, int selected2, int draft, const Vector *dragOffset, const Vector *periodicOffset, int isAnchorBond)
1091 const ElementPar *dp;
1099 float rad_mul = 1.0;
1100 float alpha_mul = 1.0;
1101 int natoms = mview->mol->natoms;
1102 expanded[0] = expanded[1] = 0;
1103 anchor[0] = anchor[1] = 0;
1105 for (i = 0; i < 2; i++) {
1107 in = (i == 0 ? i1 : i2);
1108 if (in >= natoms && in < natoms + 2) {
1109 if (mview->tempAtoms[in - natoms] >= 0) {
1110 ap = ATOM_AT_INDEX(mview->mol->atoms, mview->tempAtoms[in - natoms]);
1111 an[i] = ap->atomicNumber;
1115 r[i] = mview->tempAtomPos[in - natoms];
1119 ap = ATOM_AT_INDEX(mview->mol->atoms, in);
1120 an[i] = ap->atomicNumber;
1122 if (SYMOP_ALIVE(ap->symop))
1124 if (ap->anchor != NULL)
1127 if (!mview->showHydrogens && an[i] == 1)
1129 if (!mview->showDummyAtoms && an[i] == 0)
1131 if (!mview->showExpandedAtoms && expanded[i])
1133 if (ap != NULL && (ap->exflags & kAtomHiddenFlag))
1137 if (periodicOffset != NULL) {
1138 VecInc(r[0], *periodicOffset);
1139 VecInc(r[1], *periodicOffset);
1142 if (anchor[0] + anchor[1] == 2)
1144 if (anchor[0] + anchor[1] == 1)
1145 rad_mul = (isAnchorBond ? 0.3 : 0.6);
1147 dp = &(gElementParameters[an[0]]);
1150 if (selected && selected2) {
1151 memcpy(rgba, sRedColor, sizeof(rgba));
1153 rgba[0] = dp->red / 65535.0;
1154 rgba[1] = dp->green / 65535.0;
1155 rgba[2] = dp->blue / 65535.0;
1158 rgba[3] *= alpha_mul;
1159 if (expanded[0] || periodicOffset != NULL) {
1164 if (mview->draggingMode == kMainViewDraggingSelectedAtoms) {
1166 VecInc(r[0], *dragOffset);
1168 VecInc(r[1], *dragOffset);
1170 p[0] = r[0].x; p[1] = r[0].y; p[2] = r[0].z;
1171 p[3] = (r[1].x + p[0]) * 0.5;
1172 p[4] = (r[1].y + p[1]) * 0.5;
1173 p[5] = (r[1].z + p[2]) * 0.5;
1175 glColor3f(rgba[0], rgba[1], rgba[2]);
1177 glVertex3f(p[0], p[1], p[2]);
1178 glVertex3f(p[3], p[4], p[5]);
1181 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
1182 drawCylinder(p, p + 3, mview->bondRadius * rad_mul, mview->bondResolution, 0);
1184 dp = &(gElementParameters[an[1]]);
1187 if (!selected || !selected2) {
1188 rgba[0] = dp->red / 65535.0;
1189 rgba[1] = dp->green / 65535.0;
1190 rgba[2] = dp->blue / 65535.0;
1193 rgba[3] *= alpha_mul;
1194 if (expanded[1] || periodicOffset != NULL) {
1199 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
1200 p[0] = r[1].x; p[1] = r[1].y; p[2] = r[1].z;
1202 glColor3f(rgba[0], rgba[1], rgba[2]);
1204 glVertex3f(p[0], p[1], p[2]);
1205 glVertex3f(p[3], p[4], p[5]);
1208 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
1209 drawCylinder(p, p + 3, mview->bondRadius * rad_mul, 8, 0);
1213 /* Calculate drag offset during moving the selection. */
1215 calcDragOffset(MainView *mview, Vector *outVector)
1218 if (mview->draggingMode == kMainViewDraggingSelectedAtoms
1219 && MainView_convertScreenPositionToObjectPosition(mview, mview->dragStartPos, p)
1220 && MainView_convertScreenPositionToObjectPosition(mview, mview->dragEndPos, p + 3)) {
1221 outVector->x = p[3] - p[0];
1222 outVector->y = p[4] - p[1];
1223 outVector->z = p[5] - p[2];
1225 outVector->x = outVector->y = outVector->z = 0;
1231 drawSurface(MainView *mview)
1236 if (mview->mol == NULL || mview->mol->mcube == NULL)
1238 mc = mview->mol->mcube;
1239 for (sn = 0; sn <= 1; sn++) {
1240 if (mc->c[sn].ntriangles == 0)
1242 for (i = 0; i < 4; i++)
1243 rgba[i] = mc->c[sn].rgba[i];
1244 k = (sn == 0 ? -1 : 1);
1245 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
1246 glBegin(GL_TRIANGLES);
1247 for (i = 0; mc->c[sn].triangles[i] >= 0; i++) {
1248 MCubePoint *mcp = mc->c[sn].cubepoints + mc->c[sn].triangles[i];
1249 glNormal3f(mcp->grad[0] * k, mcp->grad[1] * k, mcp->grad[2] * k);
1250 glVertex3f(mcp->pos[0], mcp->pos[1], mcp->pos[2]);
1257 drawModel(MainView *mview)
1260 int i, natoms, nbonds;
1261 int amin, amax, bmin, bmax, cmin, cmax, da, db, dc;
1263 Double atomRadius, bondRadius;
1264 Vector periodicOffset;
1266 int selected, selected2;
1269 int draft = mview->lineMode;
1270 /* static Double gray[] = {0.8, 0.8, 0.8, 1}; */
1272 atomRadius = mview->atomRadius;
1273 bondRadius = mview->bondRadius;
1275 natoms = mol->natoms;
1276 /* if (natoms == 0) {
1280 if (mview->draggingMode == kMainViewSelectingRegion)
1281 selectFlags = temporarySelection(mview, mview->modifierFlags, 0, 0);
1282 else selectFlags = NULL;
1284 if (mview->draggingMode == kMainViewDraggingSelectedAtoms)
1285 calcDragOffset(mview, &mview->dragOffset);
1286 else mview->dragOffset.x = mview->dragOffset.y = mview->dragOffset.z = 0;
1288 if (mview->showGraphite > 0 && mview->showGraphiteFlag) {
1289 drawGraphite(mview);
1292 amin = amax = bmin = bmax = cmin = cmax = 0;
1293 if (mview->showExpandedAtoms && mol->cell != NULL) {
1294 if (mol->cell->flags[0] && mview->showPeriodicImageFlag) {
1295 amin = mview->showPeriodicImage[0];
1296 amax = mview->showPeriodicImage[1];
1298 if (mol->cell->flags[1] && mview->showPeriodicImageFlag) {
1299 bmin = mview->showPeriodicImage[2];
1300 bmax = mview->showPeriodicImage[3];
1302 if (mol->cell->flags[2] && mview->showPeriodicImageFlag) {
1303 cmin = mview->showPeriodicImage[4];
1304 cmax = mview->showPeriodicImage[5];
1306 axes = mol->cell->axes;
1312 for (da = amin; da <= amax; da++) {
1313 for (db = bmin; db <= bmax; db++) {
1314 for (dc = cmin; dc <= cmax; dc++) {
1315 original = (da == 0 && db == 0 && dc == 0);
1317 VecScale(periodicOffset, axes[0], da);
1318 VecScaleInc(periodicOffset, axes[1], db);
1319 VecScaleInc(periodicOffset, axes[2], dc);
1321 for (i = 0, ap = mview->mol->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
1322 if (mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
1323 /* Mouse event is detected */
1327 if (mview->draggingMode == kMainViewCreatingBond && (i == mview->tempAtoms[0] || i == mview->tempAtoms[1] || i >= natoms))
1328 selected = 1; /* extra atoms */
1329 else if (selectFlags != NULL)
1330 selected = selectFlags[i];
1332 selected = MoleculeIsAtomSelected(mview->mol, i);
1333 drawAtom(mview, i, selected, &mview->dragOffset, (original ? NULL : &periodicOffset));
1334 if (ap->anchor != NULL) {
1335 /* Draw anchor bonds */
1337 Int *cp = AtomConnectData(&ap->anchor->connect);
1338 for (j = 0; j < ap->anchor->connect.count; j++) {
1340 if (selectFlags != NULL)
1341 selected2 = selectFlags[k];
1343 selected2 = MoleculeIsAtomSelected(mview->mol, k);
1344 drawBond(mview, i, k, selected, selected2, draft, &mview->dragOffset, (original ? NULL : &periodicOffset), 1);
1353 drawAtom(mview, natoms, 1, &mview->dragOffset, NULL);
1354 drawAtom(mview, natoms + 1, 1, &mview->dragOffset, NULL);
1356 /* Expanded atoms */
1357 /* if (mview->showExpandedAtoms) {
1358 for (i = 0; i < mview->mol->nexatoms; i++) {
1359 if (mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
1360 // Mouse event is detected
1365 drawAtom(mview, -i-1, (selectFlags == NULL ? 0 : selectFlags[i]), &dragOffset, (original ? NULL : &periodicOffset));
1374 nbonds = mol->nbonds;
1376 glDisable(GL_LIGHTING);
1377 for (da = amin; da <= amax; da++) {
1378 for (db = bmin; db <= bmax; db++) {
1379 for (dc = cmin; dc <= cmax; dc++) {
1380 original = (da == 0 && db == 0 && dc == 0);
1382 VecScale(periodicOffset, axes[0], da);
1383 VecScaleInc(periodicOffset, axes[1], db);
1384 VecScaleInc(periodicOffset, axes[2], dc);
1387 for (i = 0; i < nbonds; i++) {
1389 if (draft == 0 && mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
1390 /* Mouse event is detected */
1392 glDisable(GL_LIGHTING);
1395 n1 = mview->mol->bonds[i * 2];
1396 n2 = mview->mol->bonds[i * 2 + 1];
1397 if (selectFlags == NULL) {
1398 selected = MoleculeIsAtomSelected(mview->mol, n1);
1399 selected2 = MoleculeIsAtomSelected(mview->mol, n2);
1401 selected = selectFlags[n1];
1402 selected2 = selectFlags[n2];
1404 drawBond(mview, n1, n2, selected, selected2, draft, &mview->dragOffset, (original ? NULL : &periodicOffset), 0);
1408 if (original && mview->draggingMode == kMainViewCreatingBond) {
1409 drawBond(mview, natoms, natoms + 1, 1, 1, draft, &mview->dragOffset, NULL, 0);
1412 /* Expanded bonds */
1413 /* for (i = 0; i < mview->mol->nexbonds; i++) {
1415 if (draft == 0 && mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
1416 // Mouse event is detected
1418 glDisable(GL_LIGHTING);
1421 n1 = mview->mol->exbonds[i * 2];
1422 n2 = mview->mol->exbonds[i * 2 + 1];
1427 if (selectFlags == NULL) {
1428 selected = MoleculeIsAtomSelected(mview->mol, n1);
1429 selected2 = MoleculeIsAtomSelected(mview->mol, n2);
1431 selected = selectFlags[n1];
1432 selected2 = selectFlags[n2];
1434 drawBond(mview, mview->mol->exbonds[i * 2], mview->mol->exbonds[i * 2 + 1], selected, selected2, draft, &dragOffset, (original ? NULL : &periodicOffset));
1443 if (selectFlags != NULL)
1448 drawGraphics(MainView *mview)
1452 for (i = 0; i < mview->ngraphics; i++) {
1453 g = &mview->graphics[i];
1454 if (g->visible == 0)
1456 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, g->rgba);
1458 case kMainViewGraphicLine:
1459 glDisable(GL_LIGHTING);
1460 glColor4fv(g->rgba);
1461 glBegin(GL_LINE_STRIP);
1462 for (j = 0; j < g->npoints; j++) {
1463 if (g->points[j * 3] >= kInvalidFloat)
1465 glVertex3fv(&g->points[j * 3]);
1470 case kMainViewGraphicPoly: {
1471 Vector v0, v1, v2, v3;
1472 glBegin(GL_TRIANGLE_FAN);
1473 v1.x = g->points[0] - g->points[g->npoints - 3];
1474 v1.y = g->points[1] - g->points[g->npoints - 2];
1475 v1.z = g->points[2] - g->points[g->npoints - 1];
1477 for (j = 0; j < g->npoints; j++) {
1478 v2.x = g->points[j * 3 + 3] - g->points[j * 3];
1479 v2.y = g->points[j * 3 + 4] - g->points[j * 3 + 1];
1480 v2.z = g->points[j * 3 + 5] - g->points[j * 3 + 2];
1481 VecCross(v3, v1, v2);
1482 if (NormalizeVec(&v3, &v3) == 0)
1483 glNormal3f(v3.x, v3.y, v3.z);
1484 glVertex3fv(&g->points[j * 3]);
1488 VecCross(v3, v1, v0);
1489 if (NormalizeVec(&v3, &v3) == 0)
1490 glNormal3f(v3.x, v3.y, v3.z);
1491 glVertex3fv(g->points);
1496 case kMainViewGraphicCylinder:
1497 drawCylinder(g->points, g->points + 3, g->points[6], 15, g->closed);
1499 case kMainViewGraphicCone:
1500 drawCone(g->points, g->points + 3, g->points[6], 15, g->closed);
1502 case kMainViewGraphicEllipsoid:
1503 drawEllipsoid(g->points, g->points + 3, g->points + 6, g->points + 9, 8);
1510 drawUnitCell(MainView *mview)
1512 GLfloat a[3], b[3], c[3], ab[3], bc[3], ca[3], abc[3];
1515 if (!mview->showUnitCell || (cp = mview->mol->cell) == NULL)
1517 origin[0] = cp->origin.x;
1518 origin[1] = cp->origin.y;
1519 origin[2] = cp->origin.z;
1520 a[0] = cp->axes[0].x + origin[0];
1521 a[1] = cp->axes[0].y + origin[1];
1522 a[2] = cp->axes[0].z + origin[2];
1523 b[0] = cp->axes[1].x + origin[0];
1524 b[1] = cp->axes[1].y + origin[1];
1525 b[2] = cp->axes[1].z + origin[2];
1526 c[0] = cp->axes[2].x + origin[0];
1527 c[1] = cp->axes[2].y + origin[1];
1528 c[2] = cp->axes[2].z + origin[2];
1530 ab[0] = a[0] + b[0]; ab[1] = a[1] + b[1]; ab[2] = a[2] + b[2];
1531 bc[0] = b[0] + c[0]; bc[1] = b[1] + c[1]; bc[2] = b[2] + c[2];
1532 ca[0] = c[0] + a[0]; ca[1] = c[1] + a[1]; ca[2] = c[2] + a[2];
1533 abc[0] = a[0] + bc[0]; abc[1] = a[1] + bc[1]; abc[2] = a[2] + bc[2];
1535 glDisable(GL_LIGHTING);
1536 glColor3f(0.75, 0.2, 0.0);
1538 glVertex3fv(origin);
1540 glVertex3fv(origin);
1542 glVertex3fv(origin);
1568 drawRotationCenter(MainView *mview)
1570 GLfloat ps[3], pe[3], col[4];
1571 double fd[2]; /* Fovy and distance */
1572 double tr[3]; /* Translation */
1574 if (mview == NULL || !mview->showRotationCenter)
1576 TrackballGetTranslate(mview->track, tr);
1577 TrackballGetPerspective(mview->track, fd);
1578 tr[0] *= -mview->dimension;
1579 tr[1] *= -mview->dimension;
1580 tr[2] *= -mview->dimension;
1581 r = fd[1] * mview->dimension * tan(fd[0] * 0.5 * kDeg2Rad) * 0.1;
1586 col[0] = col[1] = col[2] = 0.5; col[3] = 1.0;
1587 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
1588 drawSphere(ps, rr, 8);
1593 col[0] = 1.0; col[1] = col[2] = 0.0;
1594 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
1595 drawSphere(ps, rr, 8);
1596 drawSphere(pe, rr, 8);
1597 drawCylinder(ps, pe, rr, 8, 0);
1602 col[1] = 1.0; col[0] = col[2] = 0.0;
1603 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
1604 drawSphere(ps, rr, 8);
1605 drawSphere(pe, rr, 8);
1606 drawCylinder(ps, pe, rr, 8, 0);
1611 col[2] = 1.0; col[0] = col[1] = 0.0;
1612 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
1613 drawSphere(ps, rr, 8);
1614 drawSphere(pe, rr, 8);
1615 drawCylinder(ps, pe, rr, 8, 0);
1619 compareLabelByDepth(const void *ap, const void *bp)
1621 Double dz = ((const LabelRecord *)bp)->pos.z - ((const LabelRecord *)ap)->pos.z;
1630 drawLabels(MainView *mview)
1632 /* Transform *trp; */
1637 if (mview->nlabels == 0)
1640 /* mview->sortedLabels = (LabelRecord **)calloc(sizeof(LabelRecord *), mview->nlabels);
1641 if (mview->sortedLabels == NULL)
1644 /* if (mview->mol->is_xtal_coord)
1645 trp = &(mview->mol->cell->tr);
1648 /* Get the screen coordinates of the labels */
1650 for (i = 0; i < mview->mol->natoms; i++) {
1651 float scrp[3], f[3];
1652 ap = ATOM_AT_INDEX(mview->mol->atoms, i);
1653 if (ap->exflags & kAtomHiddenFlag)
1655 if (!mview->showHydrogens && ap->atomicNumber == 1)
1657 if (!mview->showDummyAtoms && ap->atomicNumber == 0)
1659 if (ap->labelid <= 0 || ap->labelid > mview->nlabels)
1661 lp = mview->labels + (ap->labelid - 1);
1662 MainView_screenCenterPointOfAtom(mview, lp->idx1, scrp);
1663 /* if (lp->idx2 >= 0) {
1667 TransformVec(&r1, *trp, &r1);
1668 bp = ATOM_AT_INDEX(mview->mol->atoms, lp->idx2);
1669 if (bp->exflags & kAtomHiddenFlag)
1673 TransformVec(&r2, *trp, &r2);
1674 r1.x = (r1.x + r2.x) * 0.5;
1675 r1.y = (r1.y + r2.y) * 0.5;
1676 r1.z = (r1.z + r2.z) * 0.5;
1680 MainView_convertObjectPositionToScreenPosition(mview, objp, scrp);
1682 lp->pos.x = scrp[0];
1683 lp->pos.y = scrp[1];
1684 lp->pos.z = scrp[2];
1685 MainViewCallback_labelSize(lp->label, f);
1686 f[0] = floor(lp->pos.x - f[0] * 0.5);
1687 f[1] = -floor(lp->pos.y + f[1] * 0.5);
1689 /* fprintf(stderr, "label position (%d) = {%f, %f, %f}\n", i, f[0], f[1], f[2]); */
1690 MainViewCallback_drawLabelAtPoint(lp->label, f);
1691 /* mview->sortedLabels[nlabels++] = lp;
1692 if (nlabels >= mview->nlabels)
1696 /* // Sort labels by z coordinates (descending order)
1697 qsort(mview->sortedLabels, nlabels, sizeof(LabelRecord *), compareLabelByDepth);
1700 for (i = 0; i < nlabels; i++) {
1701 LabelRecord *lp = mview->sortedLabels[i];
1703 MainViewCallback_labelSize(lp->label, f);
1704 f[0] = floor(lp->pos.x - f[0] * 0.5);
1705 f[1] = -floor(lp->pos.y + f[1] * 0.5);
1707 MainViewCallback_drawLabelAtPoint(lp->label, f);
1713 MainView_drawModel(MainView *mview)
1715 double w[4], dimension, distance;
1716 float frame[4], width, height, scale;
1725 if (!mview->isInitialized) {
1726 MainView_initializeOpenGL();
1727 mview->isInitialized = 1;
1730 dimension = mview->dimension;
1732 if (mview->offline_scale == 0.0)
1735 scale = mview->offline_scale;
1737 MainViewCallback_frame(mview, frame);
1738 width = (frame[2] - frame[0]) * scale;
1739 height = (frame[3] - frame[1]) * scale;
1741 glViewport(0, 0, width, height);
1743 /* Clear the buffer */
1744 glClearColor(mview->background_color[0], mview->background_color[1], mview->background_color[2], mview->background_color[3]);
1745 glClear(GL_COLOR_BUFFER_BIT |
1746 GL_DEPTH_BUFFER_BIT);
1748 if (mview->mol == NULL)
1751 /* Set up the projection */
1752 glMatrixMode(GL_PROJECTION);
1754 TrackballGetPerspective(mview->track, w);
1755 distance = w[1] * dimension;
1756 mview->perspective_vector[0] = w[0];
1757 mview->perspective_vector[1] = width / height;
1758 mview->perspective_vector[2] = dimension;
1759 mview->perspective_vector[3] = distance + 200.0 * dimension;
1760 gluPerspective(mview->perspective_vector[0], mview->perspective_vector[1], mview->perspective_vector[2], mview->perspective_vector[3]);
1762 /* Set up the model view */
1763 glMatrixMode(GL_MODELVIEW);
1765 glTranslatef(0.0, 0.0, -distance);
1766 TrackballGetRotate(mview->track, w);
1767 glRotatef(w[0], w[1], w[2], w[3]);
1768 TrackballGetTranslate(mview->track, w);
1772 glTranslatef(w[0], w[1], w[2]);
1773 mview->lookat.x = -w[0];
1774 mview->lookat.y = -w[1];
1775 mview->lookat.z = -w[2];
1777 MainViewCallback_clearLabels(mview);
1780 drawUnitCell(mview);
1781 drawRotationCenter(mview);
1782 drawGraphics(mview);
1784 /* Get important matrices and vectors */
1785 glGetDoublev(GL_MODELVIEW_MATRIX, mview->modelview_matrix);
1786 glGetDoublev(GL_PROJECTION_MATRIX, mview->projection_matrix);
1787 pp = mview->modelview_matrix;
1788 mtr[0] = pp[0]; mtr[1] = pp[1]; mtr[2] = pp[2];
1789 mtr[3] = pp[4]; mtr[4] = pp[5]; mtr[5] = pp[6];
1790 mtr[6] = pp[8]; mtr[7] = pp[9]; mtr[8] = pp[10];
1791 mtr[9] = pp[12]; mtr[10] = pp[13]; mtr[11] = pp[14];
1792 TransformInvert(mtr, mtr);
1793 mview->camera.x = mtr[9]; mview->camera.y = mtr[10]; mview->camera.z = mtr[11];
1794 mview->lookto.x = mtr[6]; mview->lookto.y = mtr[7]; mview->lookto.z = mtr[8];
1795 mview->up.x = mtr[3]; mview->up.y = mtr[4]; mview->up.z = mtr[5];
1798 glDisable(GL_LIGHTING);
1799 // glDisable (GL_DEPTH_TEST);
1800 // glEnable (GL_BLEND);
1801 // glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1803 glEnable (GL_TEXTURE_RECTANGLE_EXT);
1805 glMatrixMode (GL_PROJECTION);
1807 glMatrixMode (GL_MODELVIEW);
1809 glOrtho(0, width, 0, -height, 0.0, -1.0); /* non-flipped view */
1812 glDisable (GL_TEXTURE_RECTANGLE_EXT);
1814 // glDisable(GL_BLEND);
1815 // glEnable(GL_DEPTH_TEST);
1818 if (mview->draggingMode == kMainViewSelectingRegion) {
1819 /* Draw selection rectangle */
1820 glDisable(GL_LIGHTING);
1821 glDisable(GL_DEPTH_TEST);
1822 glMatrixMode(GL_MODELVIEW);
1824 glMatrixMode(GL_PROJECTION);
1826 glOrtho(0, width, 0, height, -1.0, 1.0);
1827 glColor3f(1.0, 1.0, 0.0);
1828 glBegin(GL_LINE_STRIP);
1829 glVertex2f(mview->dragStartPos[0], mview->dragStartPos[1]);
1830 glVertex2f(mview->dragStartPos[0], mview->dragEndPos[1]);
1831 glVertex2f(mview->dragEndPos[0], mview->dragEndPos[1]);
1832 glVertex2f(mview->dragEndPos[0], mview->dragStartPos[1]);
1833 glVertex2f(mview->dragStartPos[0], mview->dragStartPos[1]);
1835 glEnable(GL_DEPTH_TEST);
1843 #pragma mark ====== Labels ======
1846 MainView_attachLabelToAtom(MainView *mview, int index)
1851 /* const ElementPar *dp; */
1852 static float foreColor[] = {1, 1, 0, 1};
1853 static float backColor[] = {1, 1, 1, 0};
1854 ap = ATOM_AT_INDEX(mview->mol->atoms, index);
1855 if (ap->resSeq == 0)
1856 snprintf(buf, sizeof buf, "%-.4s", ap->aname);
1858 snprintf(buf, sizeof buf, "%d:%-.4s", ap->resSeq, ap->aname);
1859 for (p = buf; *p; p++) {
1865 /* dp = &(gBuiltinParameters->atomPars[ap->atomicNumber]);
1866 foreColor[0] = 1.0 - dp->r;
1867 foreColor[1] = 1.0 - dp->g;
1868 foreColor[2] = 1.0 - dp->b; */
1869 rec.label = MainViewCallback_newLabel(mview, buf, 14, foreColor, backColor);
1871 rec.idx2 = kInvalidIndex;
1872 rec.labelid = mview->nlabels + 1;
1873 ap->labelid = rec.labelid;
1874 AssignArray(&mview->labels, &mview->nlabels, sizeof(LabelRecord), mview->nlabels, &rec);
1878 MainView_detachLabelFromAtom(MainView *mview, int index)
1881 if (index >= 0 && index < mview->mol->natoms) {
1882 ap = ATOM_AT_INDEX(mview->mol->atoms, index);
1883 if (ap->labelid > 0 && ap->labelid <= mview->nlabels) {
1884 mview->labels[ap->labelid - 1].idx1 = kInvalidIndex;
1891 MainView_purgeUnusedLabels(MainView *mview)
1896 if (mview == NULL || mview->nlabels == 0 || mview->mol->natoms == 0)
1898 tempid = (Int *)calloc(sizeof(Int), mview->nlabels);
1902 /* Mark the labels in use */
1903 for (i = 0, ap = mview->mol->atoms; i < mview->mol->natoms; i++, ap = ATOM_NEXT(ap)) {
1904 if (ap->labelid > 0 && ap->labelid <= mview->nlabels)
1905 tempid[ap->labelid - 1] = 1;
1908 /* Release the labels not in use, and assign new IDs */
1910 for (i = 0; i < mview->nlabels; i++) {
1911 if (tempid[i] == 0) {
1912 MainViewCallback_releaseLabel(mview->labels[i].label);
1913 mview->labels[i].label = NULL;
1915 mview->labels[i].labelid = tempid[i] = n++;
1919 /* Purge the unused entries */
1920 for (i = mview->nlabels - 1; i >= 0; i--) {
1921 if (tempid[i] == 0) {
1922 memmove(mview->labels + i + 1, mview->labels + i, (mview->nlabels - 1 - i) * sizeof(LabelRecord));
1926 if (mview->nlabels == 0) {
1927 free(mview->labels);
1928 mview->labels = NULL;
1932 for (i = 0, ap = mview->mol->atoms; i < mview->mol->natoms; i++, ap = ATOM_NEXT(ap)) {
1933 if (ap->labelid > 0 && ap->labelid <= mview->nlabels) {
1934 ap->labelid = tempid[ap->labelid - 1];
1941 #pragma mark ====== Mode ======
1944 MainView_setMode(MainView *mview, int mode)
1951 MainView_getMode(const MainView *mview)
1958 #pragma mark ====== Mouse operations ======
1961 mousePosToTrackballPos(MainView *mview, const float *mousePos, float *pos)
1963 float frame[4], width, height, radius;
1964 NULL_CHECK(mview, "mousePosToTrackballPos");
1965 MainViewCallback_frame(mview, frame);
1966 width = frame[2] - frame[0];
1967 height = frame[3] - frame[1];
1968 radius = (width > height ? height * 0.5 : width * 0.5);
1969 pos[0] = (mousePos[0] - frame[0] - width * 0.5) / radius;
1970 pos[1] = (mousePos[1] - frame[1] - height * 0.5) / radius;
1974 showAtomsInInfoText(MainView *mview, int n1, int n2)
1978 MoleculeGetAtomName(mview->mol, n1, buf, sizeof buf - 1);
1980 int nn = strlen(buf);
1982 MoleculeGetAtomName(mview->mol, n2, buf + nn, sizeof buf - nn);
1984 MainViewCallback_drawInfoText(mview, buf);
1985 } else MainViewCallback_drawInfoText(mview, NULL);
1989 MainView_mouseDown(MainView *mview, const float *mousePos, int flags)
1994 double objectPos[3];
1997 mview->dragStartPos[0] = mousePos[0];
1998 mview->dragStartPos[1] = mousePos[1];
1999 mview->dragStartPos[2] = 0.5;
2000 found = MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 0, 0);
2001 mview->clickedAtoms[0] = n1;
2002 mview->clickedAtoms[1] = n2;
2004 /* Estimate the screen z-coordinate of the mouse position */
2006 ap = ATOM_AT_INDEX(mview->mol->atoms, n1);
2008 /* if (mview->mol->is_xtal_coord)
2009 TransformVec(&r1, mview->mol->cell->tr, &r1); */
2010 objectPos[0] = r1.x;
2011 objectPos[1] = r1.y;
2012 objectPos[2] = r1.z;
2013 if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos)) {
2015 ap = ATOM_AT_INDEX(mview->mol->atoms, n2);
2017 /* if (mview->mol->is_xtal_coord)
2018 TransformVec(&r2, mview->mol->cell->tr, &r2); */
2019 objectPos[0] = r2.x;
2020 objectPos[1] = r2.y;
2021 objectPos[2] = r2.z;
2022 if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, fp)) {
2024 r1.x = fp[0] - screenPos[0];
2025 r1.y = fp[1] - screenPos[1];
2026 r1.z = fp[2] - screenPos[2];
2027 NormalizeVec(&r1, &r1);
2028 r2.x = mousePos[0] - screenPos[0];
2029 r2.y = mousePos[1] - screenPos[1];
2032 VecScale(r2, r1, w);
2033 screenPos[2] += r2.z;
2037 mview->dragStartPos[2] = screenPos[2];
2039 /* Set the 'depth' value of the molecule center (i.e. trackball->translate * dimension)
2040 as the z-coordinate of the drag start position */
2041 TrackballGetTranslate(mview->track, p);
2042 objectPos[0] = -mview->dimension * p[0];
2043 objectPos[1] = -mview->dimension * p[1];
2044 objectPos[2] = -mview->dimension * p[2];
2045 if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos))
2046 mview->dragStartPos[2] = screenPos[2];
2049 mview->isDragging = 0;
2051 switch (mview->mode) {
2052 case kTrackballRotateMode:
2053 case kTrackballTranslateMode:
2054 case kTrackballScaleMode:
2056 mousePosToTrackballPos(mview, mousePos, fp);
2057 TrackballStartDragging(mview->track, fp, mview->mode);
2058 mview->draggingMode = kMainViewMovingTrackball;
2061 /* No 'break' intentional; drop to next case */
2063 case kTrackballSelectionMode: {
2064 /* Select or move around */
2066 if (flags & kShiftKeyMask) {
2067 /* Shift-key pressed: toggle selection */
2068 MoleculeToggleSelectionOfAtom(mview->mol, n1);
2070 MoleculeToggleSelectionOfAtom(mview->mol, n2);
2073 if (!MoleculeIsAtomSelected(mview->mol, n1)) {
2074 /* If this atom is not selected, select this atom and unselect others */
2075 MoleculeSelectAtom(mview->mol, n1, 0);
2078 if (!MoleculeIsBondSelected(mview->mol, n1, n2)) {
2079 /* If this bond is not selected, select this bond and unselect others */
2080 MoleculeSelectAtom(mview->mol, n1, 0);
2081 MoleculeSelectAtom(mview->mol, n2, 1);
2085 if (MoleculeIsAtomSelected(mview->mol, n1))
2086 mview->draggingMode = kMainViewDraggingSelectedAtoms;
2087 else mview->draggingMode = 0;
2088 } else mview->draggingMode = kMainViewSelectingRegion;
2089 mview->dragEndPos[0] = mousePos[0];
2090 mview->dragEndPos[1] = mousePos[1];
2091 mview->dragEndPos[2] = mview->dragStartPos[2];
2092 mview->modifierFlags = flags;
2095 case kTrackballCreateMode: {
2096 if (md_is_running(mview->mol->arena)) {
2097 MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
2098 mview->draggingMode = 0;
2101 /* Draw a new bond */
2102 MoleculeSetSelection(mview->mol, NULL);
2103 if (found && n2 < 0) {
2104 /* An atom under mouse: create a new atom and a bond */
2105 ap = ATOM_AT_INDEX(mview->mol->atoms, n1);
2106 mview->tempAtoms[0] = n1;
2107 mview->tempAtomPos[0] = ap->r;
2109 /* No atom under mouse: create two new atoms and a bond */
2110 mview->tempAtoms[0] = -1;
2111 screenPos[0] = mousePos[0];
2112 screenPos[1] = mousePos[1];
2113 screenPos[2] = mview->dragStartPos[2];
2114 if (MainView_convertScreenPositionToObjectPosition(mview, screenPos, objectPos) == 0) {
2115 mview->tempAtoms[0] = -1; /* Cannot create */
2116 mview->draggingMode = 0;
2118 mview->tempAtomPos[0].x = objectPos[0];
2119 mview->tempAtomPos[0].y = objectPos[1];
2120 mview->tempAtomPos[0].z = objectPos[2];
2121 /* if (mview->mol->is_xtal_coord)
2122 TransformVec(&mview->tempAtomPos[0], mview->mol->cell->rtr, &mview->tempAtomPos[0]); */
2125 mview->tempAtoms[1] = -1;
2126 mview->tempAtomPos[1] = mview->tempAtomPos[0];
2127 mview->draggingMode = kMainViewCreatingBond;
2131 mview->draggingMode = 0;
2135 showAtomsInInfoText(mview, n1, n2);
2137 showAtomsInInfoText(mview, -1, -1);
2138 /* MainViewCallback_setNeedsDisplay(mview, 1); */
2142 MainView_mouseDragged(MainView *mview, const float *mousePos, int flags)
2145 if (mview->isDragging == 0) {
2146 if (abs(mousePos[0] - mview->dragStartPos[0]) >= 3 || abs(mousePos[1] - mview->dragStartPos[1]) >= 3)
2147 mview->isDragging = 1;
2150 mousePosToTrackballPos(mview, mousePos, p);
2151 switch (mview->draggingMode) {
2152 case kMainViewMovingTrackball:
2153 TrackballDrag(mview->track, p);
2155 case kMainViewSelectingRegion:
2156 case kMainViewDraggingSelectedAtoms:
2157 mview->dragEndPos[0] = mousePos[0];
2158 mview->dragEndPos[1] = mousePos[1];
2159 mview->dragEndPos[2] = mview->dragStartPos[2];
2160 mview->modifierFlags = flags;
2161 MainViewCallback_display(mview);
2163 case kMainViewCreatingBond: {
2165 if (MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 0, 0) && n2 < 0) {
2166 /* An atom under mouse */
2167 Atom *ap = ATOM_AT_INDEX(mview->mol->atoms, n1);
2168 /* printf("n1=%d, n2=%d\n", n1, n2); */
2169 mview->tempAtoms[1] = n1;
2170 mview->tempAtomPos[1] = ap->r;
2173 double objectPos[3];
2175 mview->tempAtoms[1] = -1;
2176 /* Convert the position of temporary atom 0 to screen position */
2177 r1 = mview->tempAtomPos[0];
2178 /* if (mview->mol->is_xtal_coord)
2179 TransformVec(&r1, mview->mol->cell->tr, &r1); */
2180 objectPos[0] = r1.x;
2181 objectPos[1] = r1.y;
2182 objectPos[2] = r1.z;
2183 if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos) == 0)
2184 break; /* Do nothing */
2185 /* Convert the mouse position to object position, while using the same Z depth calculated from the temporary atom 0 position */
2186 screenPos[0] = mousePos[0];
2187 screenPos[1] = mousePos[1];
2188 if (MainView_convertScreenPositionToObjectPosition(mview, screenPos, objectPos) == 0)
2189 break; /* Do nothing */
2190 r1.x = objectPos[0];
2191 r1.y = objectPos[1];
2192 r1.z = objectPos[2];
2193 /* if (mview->mol->is_xtal_coord)
2194 TransformVec(&r1, mview->mol->cell->rtr, &r1); */
2195 mview->tempAtomPos[1] = r1;
2197 if (mview->tempAtoms[0] < 0)
2198 showAtomsInInfoText(mview, mview->tempAtoms[1], -1);
2200 showAtomsInInfoText(mview, mview->tempAtoms[0], mview->tempAtoms[1]);
2201 MainViewCallback_display(mview);
2207 MainViewCallback_display(mview);
2211 MainView_mouseMoved(MainView *mview, const float *mousePos, int flags)
2214 if (mview->isDragging)
2216 found = MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 1, 0);
2218 showAtomsInInfoText(mview, n1, n2);
2220 showAtomsInInfoText(mview, -1, -1);
2224 sCreateNewAtom(Molecule *mol, Vector pos)
2229 memset(&a, 0, sizeof(Atom));
2231 for (i = 0; i < 1000; i++) {
2232 snprintf(name, sizeof name, "C%03d", i);
2233 for (j = 0; j < mol->natoms; j++) {
2234 if (strncmp(ATOM_AT_INDEX(mol->atoms, j)->aname, name, 4) == 0)
2237 if (j == mol->natoms)
2240 strncpy(a.aname, name, 4);
2242 strcpy(a.element, "C");
2243 a.type = AtomTypeEncodeToUInt("c3");
2244 a.weight = WeightForAtomicNumber(a.atomicNumber);
2246 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, -1, &i) == 0)
2247 return mol->natoms - 1;
2249 /* return MoleculeAddAtom(mol, &a); */
2253 MainView_mouseUp(MainView *mview, const float *mousePos, int flags, int clickCount)
2258 mousePosToTrackballPos(mview, mousePos, p);
2260 if (clickCount == 2 && mview->isDragging == 0) {
2261 /* Create a new molecular fragment */
2263 found = MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 0, 0);
2265 if (MyAppCallback_getTextWithPrompt("Enter formula (e.g. CH2OCH3)", buf, sizeof buf) == 0)
2267 MolActionCreateAndPerform(mview->mol, SCRIPT_ACTION("si"), "cmd_fuse_or_dock", buf, n1);
2271 if (mview->mode == kTrackballEraseMode) {
2273 found = MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 0, 0);
2274 if (found && n1 == mview->clickedAtoms[0] && n2 == mview->clickedAtoms[1]) {
2276 /* IntGroup *sel, *newsel;
2277 sel = MoleculeGetSelection(mview->mol);
2279 newsel = MoleculeModifySelectionByRemovingAtoms(mview->mol, sel, IntGroupNewWithPoints(n1, 1, -1));
2280 if (!IntGroupIsEqual(sel, newsel))
2281 MolActionCreateAndPerform(mview->mol, gMolActionSetSelection, newsel);
2283 IntGroupRelease(newsel);
2285 MolActionCreateAndPerform(mview->mol, gMolActionDeleteAnAtom, (Int)n1);
2288 Int bn = MoleculeLookupBond(mview->mol, n1, n2);
2290 IntGroup *ig = IntGroupNewWithPoints(bn, 1, -1);
2291 MolActionCreateAndPerform(mview->mol, gMolActionDeleteBonds, ig);
2292 IntGroupRelease(ig);
2294 fprintf(stderr, "Internal error %s:%d: bond to delete is not found", __FILE__, __LINE__);
2301 switch (mview->draggingMode) {
2302 case kMainViewMovingTrackball:
2303 TrackballEndDragging(mview->track, p);
2305 case kMainViewDraggingSelectedAtoms: {
2307 if (mview->isDragging) {
2308 IntGroup *dupSelection = IntGroupNewFromIntGroup(mview->mol->selection);
2309 calcDragOffset(mview, &offset);
2310 /* if (mview->mol->is_xtal_coord)
2311 TransformVec(&offset, mview->mol->cell->rtr, &offset); */
2312 MolActionCreateAndPerform(mview->mol, gMolActionTranslateAtoms, &offset, dupSelection);
2313 IntGroupRelease(dupSelection);
2314 /* } else if (clickCount == 2) {
2316 if (MyAppCallback_getTextWithPrompt("Enter formula (e.g. CH2OCH3)", buf, sizeof buf) == 0)
2318 MolActionCreateAndPerform(mview->mol, SCRIPT_ACTION("s"), "dock_formula", buf);
2323 case kMainViewSelectingRegion: {
2324 char *selectFlags = temporarySelection(mview, mview->modifierFlags, (mview->isDragging ? 0 : 1), 0);
2325 if (selectFlags != NULL) {
2326 IntGroup *ig = IntGroupNew();
2329 natoms = mview->mol->natoms;
2330 for (i = 0; i < natoms; i++) {
2332 IntGroupAdd(ig, i, 1);
2334 MoleculeSetSelection(mview->mol, ig);
2335 /* printf("current selection = {");
2336 for (i = j = 0; i < natoms; i++) {
2337 if (selectFlags[i]) {
2338 printf("%s%.4s", (j == 0 ? "" : " "), mview->mol->atoms[i].name);
2343 IntGroupRelease(ig);
2349 case kMainViewCreatingBond: {
2352 if (mview->tempAtoms[0] >= 0 && mview->tempAtoms[1] >= 0) {
2353 n1 = mview->tempAtoms[0];
2354 n2 = mview->tempAtoms[1];
2356 if (mview->tempAtoms[0] < 0) {
2357 /* Create the first atom */
2358 n1 = sCreateNewAtom(mview->mol, mview->tempAtomPos[0]);
2359 } else n1 = mview->tempAtoms[0];
2360 if (mview->tempAtoms[1] < 0) {
2361 /* Create the second atom, if not too close */
2362 Vector dr = ATOM_AT_INDEX(mview->mol->atoms, n1)->r;
2363 VecDec(dr, mview->tempAtomPos[1]);
2364 /* if (mview->mol->is_xtal_coord)
2365 TransformVec(&dr, mview->mol->cell->tr, &dr); */
2366 if (VecLength2(dr) > 0.01)
2367 n2 = sCreateNewAtom(mview->mol, mview->tempAtomPos[1]);
2369 } else n2 = mview->tempAtoms[1];
2371 if (n1 >= 0 && n2 >= 0 && n1 != n2) {
2374 b[2] = kInvalidIndex;
2375 MolActionCreateAndPerform(mview->mol, gMolActionAddBonds, 2, b, NULL);
2376 /* MoleculeAddBonds(mview->mol, b, NULL); */
2382 mview->draggingMode = 0;
2383 mview->isDragging = 0;
2384 MainViewCallback_setNeedsDisplay(mview, 1);
2385 MainViewCallback_setKeyboardFocus(mview);
2389 MainView_rotateBySlider(MainView *mview, float angle, int mode, int mouseStatus, int modifierFlags)
2393 if (mouseStatus == 1) { /* mouseDown */
2395 if (mode == kSliderRotateBondMode) {
2396 if (MoleculeIsFragmentRotatable(mview->mol, mview->mol->selection, &n1, &n2, &(mview->rotateFragment))) {
2397 mview->rotateCenter = ATOM_AT_INDEX(mview->mol->atoms, n1)->r;
2398 mview->rotateAxis = ATOM_AT_INDEX(mview->mol->atoms, n2)->r;
2399 VecDec(mview->rotateAxis, mview->rotateCenter);
2400 if (VecLength2(mview->rotateAxis) < 1e-10)
2402 NormalizeVec(&(mview->rotateAxis), &(mview->rotateAxis));
2403 if (modifierFlags & kAltKeyMask) {
2404 /* Option key: reverse the fragment */
2405 IntGroupRelease(mview->rotateFragment);
2406 mview->rotateFragment = MoleculeFragmentExcludingAtoms(mview->mol, n2, 1, &n1);
2407 VecScaleSelf(mview->rotateAxis, -1);
2410 } else if ((modifierFlags & kAltKeyMask) != 0) {
2411 /* Rotate selection */
2413 if (mview->mol->selection == NULL)
2415 mview->rotateFragment = IntGroupNewFromIntGroup(mview->mol->selection);
2416 TrackballGetTranslate(mview->track, tr);
2417 mview->rotateCenter.x = -mview->dimension * tr[0];
2418 mview->rotateCenter.y = -mview->dimension * tr[1];
2419 mview->rotateCenter.z = -mview->dimension * tr[2];
2420 if (mode == kSliderRotateXMode) {
2421 VecCross(mview->rotateAxis, mview->up, mview->lookto);
2423 mview->rotateAxis = mview->up;
2424 VecScaleSelf(mview->rotateAxis, -1);
2427 /* Rotate along x/y axis (no coordinate transform) */
2428 TrackballStartDragging(mview->track, NULL, kTrackballRotateMode);
2429 mview->rotateFragment = NULL; /* This is probably not necessary */
2431 if (mview->rotateFragment != NULL) {
2432 /* Save the original position */
2433 n1 = IntGroupGetCount(mview->rotateFragment);
2434 mview->rotateFragmentOldPos = (Vector *)calloc(sizeof(Vector), n1);
2435 if (mview->rotateFragmentOldPos == NULL) {
2436 IntGroupRelease(mview->rotateFragment);
2437 mview->rotateFragment = NULL;
2440 for (i = 0; i < n1; i++) {
2441 n2 = IntGroupGetNthPoint(mview->rotateFragment, i);
2442 mview->rotateFragmentOldPos[i] = (ATOM_AT_INDEX(mview->mol->atoms, n2))->r;
2446 } else { /* mouseDragged or mouseUp */
2448 if (mview->rotateFragment != NULL) {
2450 /* Restore original positions */
2451 n1 = IntGroupGetCount(mview->rotateFragment);
2452 for (i = 0; i < n1; i++) {
2453 n2 = IntGroupGetNthPoint(mview->rotateFragment, i);
2454 (ATOM_AT_INDEX(mview->mol->atoms, n2))->r = mview->rotateFragmentOldPos[i];
2457 if (mouseStatus == 2) { /* dragged */
2458 MoleculeRotate(mview->mol, &(mview->rotateAxis), angle, &(mview->rotateCenter), mview->rotateFragment);
2459 } else { /* mouse up */
2460 IntGroup *ig = IntGroupNewFromIntGroup(mview->rotateFragment);
2461 MolActionCreateAndPerform(mview->mol, gMolActionRotateAtoms, &(mview->rotateAxis), (double)angle, &(mview->rotateCenter), ig);
2462 IntGroupRelease(ig);
2463 IntGroupRelease(mview->rotateFragment);
2464 mview->rotateFragment = NULL;
2465 free(mview->rotateFragmentOldPos);
2466 mview->rotateFragmentOldPos = NULL;
2471 double cs = cos(angle / 2);
2472 double sn = sin(angle / 2);
2473 if (mouseStatus == 0) { /* mouseUp */
2474 TrackballEndDragging(mview->track, NULL);
2475 } else { /* mouseDragged */
2477 if (mode == kSliderRotateXMode) {
2478 /* Rotation along x axis */
2480 quat[2] = quat[3] = 0;
2483 quat[1] = quat[3] = 0;
2485 TrackballSetTemporaryRotation(mview->track, quat);
2490 MainViewCallback_setNeedsDisplay(mview, 1);
2491 MainViewCallback_setKeyboardFocus(mview);
2494 #pragma mark ====== Menu Commands ======
2497 MainView_selectAll(MainView *mview)
2500 if (mview == NULL || mview->mol == NULL)
2505 IntGroupAdd(ig, 0, mview->mol->natoms);
2506 MoleculeSetSelection(mview->mol, ig);
2507 IntGroupRelease(ig);
2508 MainViewCallback_setNeedsDisplay(mview, 1);
2512 MainView_selectFragment(MainView *mview)
2515 if (mview == NULL || mview->mol == NULL)
2517 ig = MoleculeFragmentWithAtomGroups(mview->mol, MoleculeGetSelection(mview->mol), NULL);
2520 MoleculeSetSelection(mview->mol, ig);
2521 IntGroupRelease(ig);
2522 MainViewCallback_setNeedsDisplay(mview, 1);
2526 MainView_selectReverse(MainView *mview)
2529 if (mview == NULL || mview->mol == NULL)
2531 ig = IntGroupNewFromIntGroup(MoleculeGetSelection(mview->mol));
2532 IntGroupReverse(ig, 0, mview->mol->natoms);
2533 MoleculeSetSelection(mview->mol, ig);
2534 IntGroupRelease(ig);
2535 MainViewCallback_setNeedsDisplay(mview, 1);
2539 MainView_centerSelection(MainView *mview)
2544 if (mview == NULL || mview->mol == NULL)
2546 ig = MoleculeGetSelection(mview->mol);
2547 MoleculeCenterOfMass(mview->mol, &c, ig);
2548 tr[0] = -c.x / mview->dimension;
2549 tr[1] = -c.y / mview->dimension;
2550 tr[2] = -c.z / mview->dimension;
2551 TrackballSetTranslate(mview->track, tr);
2552 MainViewCallback_setNeedsDisplay(mview, 1);
2555 #pragma mark ====== Pasteboard Support ======
2558 MainView_copy(MainView *mview)
2560 Molecule *mol, *mol2;
2562 Int len, result, time;
2564 if (mview == NULL || (mol = mview->mol) == NULL || (sel = MoleculeGetSelection(mol)) == NULL)
2566 if (MoleculeExtract(mol, &mol2, MoleculeGetSelection(mol), 1) != 0
2568 || (p = MoleculeSerialize(mol2, &len, &time)) == NULL)
2570 result = MoleculeCallback_writeToPasteboard(gMoleculePasteboardType, p, len);
2574 MoleculeRelease(mol2);
2575 mview->pasteTimeStamp = time;
2576 mview->pasteCount = 0;
2581 MainView_delete(MainView *mview)
2584 Molecule *mol = mview->mol;
2585 if (md_is_running(mview->mol->arena)) {
2586 MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
2589 result = MolActionCreateAndPerform(mol, gMolActionUnmergeMolecule, MoleculeGetSelection(mol));
2591 result = MolActionCreateAndPerform(mol, gMolActionSetSelection, NULL);
2596 MainView_cut(MainView *mview)
2599 if (md_is_running(mview->mol->arena)) {
2600 MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
2603 result = MainView_copy(mview);
2605 result = MainView_delete(mview);
2610 MainView_isPastable(MainView *mview)
2612 if (MoleculeCallback_isDataInPasteboard(gMoleculePasteboardType))
2618 MainView_paste(MainView *mview)
2621 Int len, result, time;
2625 if (md_is_running(mview->mol->arena)) {
2626 MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
2629 if (!MainView_isPastable(mview))
2631 if (MoleculeCallback_readFromPasteboard(gMoleculePasteboardType, &p, &len) != 0)
2633 if ((mol2 = MoleculeDeserialize(p, len, &time)) == NULL) {
2639 if (time == mview->pasteTimeStamp) {
2640 /* Offset the pasted fragment by something */
2642 mview->pasteCount++;
2643 v.x = v.y = v.z = mview->pasteCount * 0.5 + sin((double)mview->pasteCount) * 0.1; /* sin(..) is to avoid accidental coincidence */
2644 MoleculeTranslate(mol2, &v, NULL);
2646 mview->pasteTimeStamp = time;
2647 mview->pasteCount = 0;
2650 sel = MoleculeGetSelection(mview->mol);
2651 if (sel == NULL || IntGroupGetCount(sel) == 0) {
2652 /* Remove dummy atoms from mol2 */
2655 sel = IntGroupNew();
2656 for (i = 0, ap = mol2->atoms; i < mol2->natoms; i++, ap = ATOM_NEXT(ap)) {
2657 if (ap->atomicNumber == 0 && ap->aname[0] == '_') {
2659 IntGroupAdd(sel, i, 1);
2662 if (IntGroupGetCount(sel) > 0)
2663 MoleculeUnmerge(mol2, NULL, sel, 0, NULL, NULL, 0);
2664 IntGroupRelease(sel);
2667 result = MolActionCreateAndPerform(mview->mol, SCRIPT_ACTION("M"), "dock_fragment", mol2);
2668 MoleculeRelease(mol2);
2673 sMainView_AppendParameterToSerialBuffer(void **pb_ptr, Int *pb_len, Int parType, Int count, const UnionPar *up)
2675 int len = sizeof(Int) * 2 + sizeof(UnionPar) * count;
2678 *pb_ptr = malloc(len + 1);
2679 if (*pb_ptr == NULL)
2681 p = (char *)(*pb_ptr);
2684 *pb_ptr = realloc(*pb_ptr, *pb_len + len);
2685 if (*pb_ptr == NULL)
2687 p = (char *)(*pb_ptr) + *pb_len - 1; /* *pb_len includes the end mark */
2693 *((Int *)p) = count;
2695 memmove(p, up, sizeof(UnionPar) * count);
2696 p += sizeof(UnionPar) * count;
2697 *p = 0; /* End mark */
2702 MainView_pasteParameters(MainView *mview)
2707 if (mview == NULL || mview->mol == NULL)
2709 if (mview->mol->par == NULL)
2710 mview->mol->par = ParameterNew();
2711 if (!MoleculeCallback_isDataInPasteboard(gParameterPasteboardType))
2713 if (MoleculeCallback_readFromPasteboard(gParameterPasteboardType, (void **)&p, &len) != 0)
2716 newsel = IntGroupNew();
2722 if (parType < kFirstParType || parType > kLastParType)
2725 count = *((Int *)p);
2727 up = (UnionPar *)calloc(sizeof(UnionPar), count);
2728 memmove(up, p, sizeof(UnionPar) * count);
2730 /* The global parameters become local when pasted */
2731 for (i = 0; i < count; i++) {
2732 if (up[i].bond.src > 0)
2736 c = ParameterGetCountForType(mview->mol->par, parType);
2737 ig = IntGroupNewWithPoints(c, count, -1);
2738 MolActionCreateAndPerform(mview->mol, gMolActionAddParameters, parType, ig, count, up);
2740 IntGroupRelease(ig);
2741 p += sizeof(UnionPar) * count;
2742 c = ParameterTableGetRowFromTypeAndIndex(mview->mol->par, parType, c);
2743 IntGroupAdd(newsel, c, count);
2747 /* Select newly pasted parameters */
2748 MainViewCallback_setTableSelection(mview, newsel);
2749 IntGroupRelease(newsel);
2751 /* Request MD rebuild */
2752 mview->mol->needsMDRebuild = 1;
2754 /* Suppress clear of parameter table selection */
2755 mview->mol->parameterTableSelectionNeedsClear = 0;
2757 MoleculeCallback_notifyModification(mview->mol, 0);
2761 /* flags: 1, delete; 2, copy; 3, cut */
2763 MainView_copyOrCutParameters(MainView *mview, int flags)
2765 IntGroup *ig = MainViewCallback_getTableSelection(mview);
2767 int i, n, idx, type = -1, t1;
2768 Parameter *par = mview->mol->par;
2769 void *pb_ptr = NULL;
2771 if (ig == NULL || (i = IntGroupGetCount(ig)) == 0)
2776 n = IntGroupGetNthPoint(ig, i);
2778 idx = ParameterTableGetItemIndex(par, n, &t1);
2784 /* Process Parameters for the last group */
2785 if (type >= kFirstParType && ig2 != NULL && (n = IntGroupGetCount(ig2)) > 0) {
2786 UnionPar *up = (UnionPar *)calloc(sizeof(UnionPar), n);
2789 if (ParameterDelete(par, type, up, ig2) < 0)
2791 act = MolActionNew(gMolActionAddParameters, type, ig2, n, up);
2792 MolActionCallback_registerUndo(mview->mol, act);
2793 MolActionRelease(act);
2795 if (ParameterCopy(par, type, up, ig2) < 0)
2799 if (sMainView_AppendParameterToSerialBuffer(&pb_ptr, &pb_len, type, n, up) != 0)
2803 IntGroupRelease(ig2);
2812 ig2 = IntGroupNew();
2813 IntGroupAdd(ig2, idx, 1);
2818 IntGroupRelease(ig2);
2819 IntGroupRelease(ig);
2822 n = MoleculeCallback_writeToPasteboard(gParameterPasteboardType, pb_ptr, pb_len);
2827 /* Clear selection */
2828 MainViewCallback_setTableSelection(mview, NULL);
2829 /* Request MD rebuild */
2830 mview->mol->needsMDRebuild = 1;
2832 MoleculeCallback_notifyModification(mview->mol, 0);
2836 #pragma mark ====== Table View (also for gBuiltinParameters) ======
2838 /* As a special case, the table view functions will handle gBuiltinParameters when mview == NULL. */
2840 typedef struct ColumnInfoRecord {
2846 static ColumnInfoRecord sAtomColumns[] = {
2847 {"atom", 4, 0}, {"name", 4, 1}, {"type", 4, 1}, {"element", 4, 1}, {"residue", 6, 1},
2848 {"x", 6, 1}, {"y", 6, 1}, {"z", 6, 1}, {"charge", 6, 1}, {NULL}
2850 static ColumnInfoRecord sBondColumns[] = {
2851 {"atoms", 9, 0}, {"names", 9, 0}, {"type", 9, 0}, {"length", 8, 0},
2852 {"par type", 8, 0}, {"k", 8, 0}, {"r0", 8, 0}, {NULL}
2854 static ColumnInfoRecord sAngleColumns[] = {
2855 {"atoms", 12, 0}, {"names", 12, 0}, {"type", 12, 0}, {"angle", 8, 0},
2856 {"par type", 8, 0}, {"k", 8, 0}, {"a0", 8, 0}, {NULL}
2858 static ColumnInfoRecord sDihedralColumns[] = {
2859 {"atoms", 15, 0}, {"names", 15, 0}, {"type", 15, 0}, {"dihedral", 8, 0},
2860 {"par type", 8, 0}, {"k", 8, 0}, {"period", 4, 0}, {"phi0", 8, 0}, {NULL}
2862 static ColumnInfoRecord sImproperColumns[] = {
2863 {"atoms", 15, 0}, {"names", 15, 0}, {"type", 15, 0}, {"improper", 8, 0},
2864 {"par type", 8, 0}, {"k", 8, 0}, {"period", 4, 0}, {"phi0", 8, 0}, {NULL}
2866 static ColumnInfoRecord sParameterColumns[] = {
2867 {"class", 5, 0}, {"type", 9, 0}, {"", 6, 0}, {"", 6, 0}, {"", 6, 0},
2868 {"", 6, 0}, {"", 6, 0}, {"", 6, 0}, {"src", 8, 0}, {"comment", 25, 0}, {NULL}
2870 static ColumnInfoRecord sUnitCellColumns[] = {
2871 {"name", 6, 0}, {"values", 9, 1}, {"", 9, 1}, {"", 9, 1}, {NULL}
2873 static ColumnInfoRecord sXtalCoordColumns[] = {
2874 {"atom", 4, 0}, {"name", 4, 1}, {"element", 4, 1},
2875 {"frx", 6, 1}, {"fry", 6, 1}, {"frz", 6, 1},
2876 {"occ", 6, 1}, {"temp", 6, 1}, {NULL}
2878 static ColumnInfoRecord sMOEnergyColumns[] = {
2879 {"MO", 5, 0}, {"alpha energy", 12, 0}, {"beta energy", 12, 0}, {NULL}
2881 static ColumnInfoRecord *sColumnInfo[] = {
2882 sAtomColumns, sBondColumns, sAngleColumns, sDihedralColumns,
2883 sImproperColumns, NULL, sParameterColumns, NULL,
2884 sUnitCellColumns, sXtalCoordColumns, NULL, sMOEnergyColumns
2886 static char *sTableTitles[] = {
2887 "atoms", "bonds", "angles", "dihedrals", "impropers", "--------",
2888 "MM/MD pars", "--------", "unit cell", "xtal coords", "--------", "MO energy"
2892 MainView_tableTitleForIndex(MainView *mview, int idx, char *buf, int bufsize)
2895 idx = kMainViewParameterTableIndex;
2896 if (idx < 0 || idx >= sizeof(sColumnInfo) / sizeof(sColumnInfo[0])) {
2900 snprintf(buf, bufsize, "%s", sTableTitles[idx]);
2904 MainView_createColumnsForTableAtIndex(MainView *mview, int idx)
2909 idx = kMainViewParameterTableIndex;
2910 if (idx < 0 || idx >= sizeof(sColumnInfo) / sizeof(sColumnInfo[0]))
2911 return 0; /* Invalid index */
2912 if (sColumnInfo[idx] == NULL)
2913 return 0; /* Invalid index */
2915 /* Remove all existing columns */
2916 while (MainViewCallback_removeTableColumnAtIndex(mview, 0) > 0);
2919 mview->tableIndex = idx;
2921 /* Create columns */
2922 for (i = 0; ; i++) {
2924 ColumnInfoRecord *recp = &(sColumnInfo[idx][i]);
2925 if (recp->name == NULL)
2927 width = recp->width;
2928 if (mview == 0 && strcmp(recp->name, "comment") == 0)
2930 MainViewCallback_addTableColumn(mview, recp->name, width, recp->editable);
2937 MainView_refreshTable(MainView *mview)
2940 if (mview != NULL && mview->mol != NULL) {
2941 MainView_refreshCachedInfo(mview);
2942 if (mview->tableIndex == kMainViewBondTableIndex ||
2943 mview->tableIndex == kMainViewAngleTableIndex ||
2944 mview->tableIndex == kMainViewDihedralTableIndex ||
2945 mview->tableIndex == kMainViewImproperTableIndex ||
2946 mview->tableIndex == kMainViewParameterTableIndex) {
2947 /* Check the parameter table */
2948 if (mview->mol->arena == NULL)
2949 md_arena_new(mview->mol);
2950 if (mview->mol->needsMDRebuild || mview->mol->arena->par == NULL) {
2951 /* Here we do not call MoleculePrepareMDArena(), because we do not want
2952 to modify Molecule by just redrawing tables.
2953 (But MoleculePrepareMDArena() *is* called when the parameter
2954 table is selected. */
2955 md_prepare(mview->mol->arena, 1);
2960 MainViewCallback_reloadTableData(mview);
2962 if (mview != NULL && mview->mol != NULL && mview->tableIndex >= kMainViewAtomTableIndex && mview->tableIndex <= kMainViewImproperTableIndex)
2963 MainViewCallback_setTableSelection(mview, mview->tableSelection);
2967 MainView_numberOfRowsInTable(MainView *mview)
2970 return ParameterTableNumberOfRows(gBuiltinParameters);
2971 if (mview->mol == NULL)
2973 switch (mview->tableIndex) {
2974 case kMainViewParameterTableIndex:
2975 return ParameterTableNumberOfRows(mview->mol->par);
2976 case kMainViewUnitCellTableIndex:
2977 return 13; /* a, b, c, alpha, beta, gamma, a_valid, b_valid, c_valid, av, bv, cv, ov */
2978 case kMainViewAtomTableIndex:
2979 case kMainViewBondTableIndex:
2980 case kMainViewAngleTableIndex:
2981 case kMainViewDihedralTableIndex:
2982 case kMainViewImproperTableIndex:
2983 case kMainViewXtalCoordTableIndex:
2984 if (mview->tableCache == NULL)
2985 MainView_refreshCachedInfo(mview);
2986 return IntGroupGetCount(mview->tableCache);
2987 case kMainViewMOTableIndex:
2988 return (mview->mol->bset != NULL ? mview->mol->bset->ncomps : 0);
2995 MainView_indexToTableRow(MainView *mview, int idx)
2999 switch (mview->tableIndex) {
3000 case kMainViewAtomTableIndex:
3001 case kMainViewBondTableIndex:
3002 case kMainViewAngleTableIndex:
3003 case kMainViewDihedralTableIndex:
3004 case kMainViewImproperTableIndex:
3005 case kMainViewXtalCoordTableIndex:
3006 return IntGroupLookupPoint(mview->tableCache, idx);
3013 MainView_tableRowToIndex(MainView *mview, int row)
3017 switch (mview->tableIndex) {
3018 case kMainViewAtomTableIndex:
3019 case kMainViewBondTableIndex:
3020 case kMainViewAngleTableIndex:
3021 case kMainViewDihedralTableIndex:
3022 case kMainViewImproperTableIndex:
3023 case kMainViewXtalCoordTableIndex:
3024 return IntGroupGetNthPoint(mview->tableCache, row);
3031 sAtomDescription(Atom *ap, char *buf, int bufsize)
3033 snprintf(buf, bufsize, "%d:%.4s", ap->resSeq, ap->aname);
3038 sParameterOfTypeAtIndex(Molecule *mol, int parType, int idx)
3041 if (mol->arena == NULL || mol->needsMDRebuild || mol->arena->par == NULL)
3045 i = mol->arena->vdw_par_i[idx];
3046 if (i >= 0 && i < mol->arena->par->nvdwPars)
3047 return (UnionPar *)(mol->arena->par->vdwPars + i);
3050 i = mol->arena->bond_par_i[idx];
3051 if (i >= 0 && i < mol->arena->par->nbondPars)
3052 return (UnionPar *)(mol->arena->par->bondPars + i);
3055 i = mol->arena->angle_par_i[idx];
3056 if (i >= 0 && i < mol->arena->par->nanglePars)
3057 return (UnionPar *)(mol->arena->par->anglePars + i);
3059 case kDihedralParType:
3060 i = mol->arena->dihedral_par_i[idx];
3061 if (i >= 0 && i < mol->arena->par->ndihedralPars)
3062 return (UnionPar *)(mol->arena->par->dihedralPars + i);
3064 case kImproperParType:
3065 i = mol->arena->improper_par_i[idx];
3066 if (i >= 0 && i < mol->arena->par->nimproperPars)
3067 return (UnionPar *)(mol->arena->par->improperPars + i);
3074 MainView_valueForTable(MainView *mview, int column, int row, char *buf, int bufsize)
3079 char descbuf[4][20], typebuf[4][8];
3083 return ParameterTableGetItemText(gBuiltinParameters, column, row, buf, bufsize);
3085 if (mview == NULL || (mol = mview->mol) == NULL) {
3090 if (mview->tableIndex == kMainViewParameterTableIndex)
3091 return ParameterTableGetItemText(mview->mol->par, column, row, buf, bufsize);
3093 if (mview->tableIndex == kMainViewAtomTableIndex) { /* Atoms */
3094 idx = MainView_tableRowToIndex(mview, row);
3095 ap[0] = ATOM_AT_INDEX(mol->atoms, idx);
3097 case 0: snprintf(buf, bufsize, "%d", idx); break;
3098 case 1: snprintf(buf, bufsize, "%.4s", ap[0]->aname); break;
3099 case 2: snprintf(buf, bufsize, "%.6s", AtomTypeDecodeToString(ap[0]->type, NULL)); break;
3100 case 3: snprintf(buf, bufsize, "%.2s", ap[0]->element); break;
3102 if (ap[0]->resSeq == 0)
3105 snprintf(buf, bufsize, "%.4s.%d", ap[0]->resName, ap[0]->resSeq);
3107 case 5: snprintf(buf, bufsize, "%.3f", ap[0]->r.x); break;
3108 case 6: snprintf(buf, bufsize, "%.3f", ap[0]->r.y); break;
3109 case 7: snprintf(buf, bufsize, "%.3f", ap[0]->r.z); break;
3110 case 8: snprintf(buf, bufsize, "%.3f", ap[0]->charge); break;
3111 default: buf[0] = 0; break;
3113 } else if (mview->tableIndex == kMainViewBondTableIndex) { /* Bonds */
3114 idx = MainView_tableRowToIndex(mview, row);
3115 ip = mol->bonds + idx * 2;
3116 ap[0] = ATOM_AT_INDEX(mol->atoms, ip[0]);
3117 ap[1] = ATOM_AT_INDEX(mol->atoms, ip[1]);
3119 case 0: snprintf(buf, bufsize, "%d-%d", ip[0], ip[1]); break;
3120 case 1: snprintf(buf, bufsize, "%s-%s", sAtomDescription(ap[0], descbuf[0], 20), sAtomDescription(ap[1], descbuf[1], 20)); break;
3121 case 2: snprintf(buf, bufsize, "%.6s-%.6s", AtomTypeDecodeToString(ap[0]->type, typebuf[0]), AtomTypeDecodeToString(ap[1]->type, typebuf[1])); break;
3123 snprintf(buf, bufsize, "%.3f", MoleculeMeasureBond(mview->mol, &(ap[0]->r), &(ap[1]->r)));
3128 BondPar *bp = (BondPar *)sParameterOfTypeAtIndex(mol, kBondParType, idx);
3133 snprintf(buf, bufsize, "%.6s-%.6s", AtomTypeDecodeToString(bp->type1, typebuf[0]), AtomTypeDecodeToString(bp->type2, typebuf[1]));
3135 snprintf(buf, bufsize, "%.3f", (column == 5 ? bp->k * INTERNAL2KCAL : bp->r0));
3139 default: buf[0] = 0; break;
3141 } else if (mview->tableIndex == kMainViewAngleTableIndex) { /* Angles */
3142 idx = MainView_tableRowToIndex(mview, row);
3143 ip = mol->angles + idx * 3;
3144 ap[0] = ATOM_AT_INDEX(mol->atoms, ip[0]);
3145 ap[1] = ATOM_AT_INDEX(mol->atoms, ip[1]);
3146 ap[2] = ATOM_AT_INDEX(mol->atoms, ip[2]);
3148 case 0: snprintf(buf, bufsize, "%d-%d-%d", ip[0], ip[1], ip[2]); break;
3149 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;
3150 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;
3152 snprintf(buf, bufsize, "%.3f", MoleculeMeasureAngle(mview->mol, &(ap[0]->r), &(ap[1]->r), &(ap[2]->r)));
3157 AnglePar *anp = (AnglePar *)sParameterOfTypeAtIndex(mol, kAngleParType, idx);
3162 snprintf(buf, bufsize, "%.6s-%.6s-%.6s", AtomTypeDecodeToString(anp->type1, typebuf[0]), AtomTypeDecodeToString(anp->type2, typebuf[1]), AtomTypeDecodeToString(anp->type3, typebuf[2]));
3164 snprintf(buf, bufsize, "%.3f", (column == 5 ? anp->k * INTERNAL2KCAL : anp->a0 * kRad2Deg));
3168 default: buf[0] = 0; break;
3170 } else if (mview->tableIndex == kMainViewDihedralTableIndex || mview->tableIndex == kMainViewImproperTableIndex) { /* Dihedrals, Impropers */
3171 int f = (mview->tableIndex == kMainViewDihedralTableIndex);
3172 idx = MainView_tableRowToIndex(mview, row);
3173 ip = (f ? mview->mol->dihedrals : mview->mol->impropers) + idx * 4;
3174 ap[0] = ATOM_AT_INDEX(mview->mol->atoms, ip[0]);
3175 ap[1] = ATOM_AT_INDEX(mview->mol->atoms, ip[1]);
3176 ap[2] = ATOM_AT_INDEX(mview->mol->atoms, ip[2]);
3177 ap[3] = ATOM_AT_INDEX(mview->mol->atoms, ip[3]);
3179 case 0: snprintf(buf, bufsize, "%d-%d-%d-%d", ip[0], ip[1], ip[2], ip[3]); break;
3180 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;
3181 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;
3183 snprintf(buf, bufsize, "%.3f", MoleculeMeasureDihedral(mview->mol, &(ap[0]->r), &(ap[1]->r), &(ap[2]->r), &(ap[3]->r)));
3189 TorsionPar *tp = (TorsionPar *)sParameterOfTypeAtIndex(mol, (f ? kDihedralParType : kImproperParType), idx);
3192 else if (column == 4)
3193 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]));
3194 else if (column == 6)
3195 snprintf(buf, bufsize, "%d", tp->period[0]);
3196 else snprintf(buf, bufsize, "%.3f", (column == 5 ? tp->k[0] * INTERNAL2KCAL : tp->phi0[0] * kRad2Deg));
3199 default: buf[0] = 0; break;
3201 } else if (mview->tableIndex == kMainViewUnitCellTableIndex) { /* Unit cell info */
3203 if (mview->mol->cell != NULL) {
3204 static const char *unitCellRowTitles[] = {"a", "b", "c", "alpha", "beta", "gamma", "a_valid", "b_valid", "c_valid", "a_vec", "b_vec", "c_vec", "origin"};
3206 snprintf(buf, bufsize, "%s", unitCellRowTitles[row]);
3209 case 0: case 1: case 2:
3211 snprintf(buf, bufsize, "%.5f", mview->mol->cell->cell[row]);
3212 else if (column == 2 && mview->mol->cell->has_sigma)
3213 snprintf(buf, bufsize, "%.5f", mview->mol->cell->cellsigma[row]);
3215 case 3: case 4: case 5:
3217 snprintf(buf, bufsize, "%.4f", mview->mol->cell->cell[row]);
3218 else if (column == 2 && mview->mol->cell->has_sigma)
3219 snprintf(buf, bufsize, "%.4f", mview->mol->cell->cellsigma[row]);
3221 case 6: case 7: case 8:
3223 snprintf(buf, bufsize, "%d", (int)mview->mol->cell->flags[row - 6]);
3225 case 9: case 10: case 11: case 12: {
3226 Vector *vp = (row == 12 ? &mview->mol->cell->origin : &mview->mol->cell->axes[row - 9]);
3227 Double dval = (column == 1 ? vp->x : (column == 2 ? vp->y : vp->z));
3228 snprintf(buf, bufsize, "%.5f", dval);
3234 } else if (mview->tableIndex == kMainViewXtalCoordTableIndex) { /* fractional coords */
3236 idx = MainView_tableRowToIndex(mview, row);
3237 ap[0] = ATOM_AT_INDEX(mol->atoms, idx);
3238 if (mview->mol->cell != NULL)
3239 TransformVec(&fract_r, mview->mol->cell->rtr, &(ap[0]->r));
3240 else fract_r = ap[0]->r;
3242 case 0: snprintf(buf, bufsize, "%d", idx); break;
3243 case 1: snprintf(buf, bufsize, "%.4s", ap[0]->aname); break;
3244 case 2: snprintf(buf, bufsize, "%.2s", ap[0]->element); break;
3245 case 3: snprintf(buf, bufsize, "%.3f", fract_r.x); break;
3246 case 4: snprintf(buf, bufsize, "%.3f", fract_r.y); break;
3247 case 5: snprintf(buf, bufsize, "%.3f", fract_r.z); break;
3248 case 6: snprintf(buf, bufsize, "%.3f", ap[0]->occupancy); break;
3249 case 7: snprintf(buf, bufsize, "%.3f", ap[0]->tempFactor); break;
3250 default: buf[0] = 0; break;
3252 } else if (mview->tableIndex == kMainViewMOTableIndex) { /* MO energy */
3253 BasisSet *bset = mview->mol->bset;
3256 if (bset != NULL && idx >= 0 && idx < bset->ncomps) {
3258 snprintf(buf, bufsize, "%d", idx + 1);
3259 } else if (column >= 3) {
3260 return; /* Cannot happen */
3262 /* column == 1, alpha; column == 2, beta */
3266 if (idx >= bset->ne_alpha)
3267 eflag = '*'; /* Unoccupied */
3269 if (bset->rflag == 2 && idx >= bset->ne_beta)
3270 eflag = 'S'; /* Singly occupied */
3274 if (bset->rflag != 0)
3275 return; /* No beta orbitals */
3276 if (idx >= bset->ne_beta)
3277 eflag = '*'; /* Unoccupied */
3278 idx += bset->ncomps;
3280 snprintf(buf, bufsize, "%c %.8f", eflag, bset->moenergies[idx]);
3286 /* Set color for the table */
3288 MainView_setColorForTable(MainView *mview, int column, int row, float *fg, float *bg)
3294 if (mview->tableIndex == kMainViewParameterTableIndex && column == -1) {
3295 int src = ParameterTableGetItemSource(mview->mol->par, row);
3296 if (src == -2) { /* separator line */
3297 bg[0] = bg[1] = bg[2] = 0.6;
3299 } else if (src == -1) { /* undefined parameters */
3301 bg[1] = bg[2] = 0.2;
3303 } else if (src == 0) { /* local parameter */
3304 bg[0] = bg[1] = 1.0;
3308 } else if (mview->tableIndex == kMainViewMOTableIndex && column == -1) {
3309 BasisSet *bset = mview->mol->bset;
3313 if (row < 0 || row >= bset->ncomps)
3315 if (row < bset->ne_beta)
3316 n = 0; /* Occupied */
3317 else if (row < bset->ne_alpha)
3318 n = 1; /* Half-occupied */
3319 else n = 2; /* Unoccupied */
3321 case 1: bg[0] = bg[1] = bg[2] = 0.9; break;
3322 case 2: bg[0] = bg[1] = bg[2] = 0.8; break;
3323 default: bg[0] = bg[1] = bg[2] = 1.0; break;
3326 } else if (mview->tableIndex < kMainViewBondTableIndex || mview->tableIndex > kMainViewImproperTableIndex)
3329 /* Bond etc. table */
3330 switch (mview->tableIndex) {
3331 case kMainViewBondTableIndex:
3332 parType = kBondParType;
3334 case kMainViewAngleTableIndex:
3335 parType = kAngleParType;
3337 case kMainViewDihedralTableIndex:
3338 parType = kDihedralParType;
3340 case kMainViewImproperTableIndex:
3341 parType = kImproperParType;
3349 idx = MainView_tableRowToIndex(mview, row);
3350 up = sParameterOfTypeAtIndex(mview->mol, parType, idx);
3355 /* Value column; warn if the value is "abnormal" */
3358 case kBondParType: f = md_check_abnormal_bond(mview->mol->arena, mview->mol, idx); break;
3359 case kAngleParType: f = md_check_abnormal_angle(mview->mol->arena, mview->mol, idx); break;
3360 case kDihedralParType: f = md_check_abnormal_dihedral(mview->mol->arena, mview->mol, idx); break;
3361 case kImproperParType: f = md_check_abnormal_improper(mview->mol->arena, mview->mol, idx); break;
3366 bg[1] = bg[2] = 0.5;
3370 if (up->bond.src == 0) {
3371 bg[0] = bg[1] = 1.0;
3374 } else if (up->bond.src == -1) {
3376 bg[1] = bg[2] = 0.2;
3384 MainView_setSelectionFromTable(MainView *mview)
3387 if (mview == NULL || mview->mol == NULL)
3389 switch (mview->tableIndex) {
3390 case kMainViewAtomTableIndex:
3391 case kMainViewBondTableIndex:
3392 case kMainViewAngleTableIndex:
3393 case kMainViewDihedralTableIndex:
3394 case kMainViewImproperTableIndex:
3395 case kMainViewXtalCoordTableIndex:
3396 break; /* Continue */
3398 return; /* Do nothing */
3400 ig = MainViewCallback_getTableSelection(mview);
3401 sel = IntGroupNew();
3405 for (i = 0; (i1 = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
3406 i2 = IntGroupGetNthPoint(mview->tableCache, i1);
3409 if (mview->tableIndex == kMainViewAtomTableIndex ||
3410 mview->tableIndex == kMainViewXtalCoordTableIndex) { /* Atoms */
3411 IntGroupAdd(sel, i2, 1);
3412 } else if (mview->tableIndex == kMainViewBondTableIndex) { /* Bonds */
3413 ip = mview->mol->bonds + i2 * 2;
3414 IntGroupAdd(sel, ip[0], 1);
3415 IntGroupAdd(sel, ip[1], 1);
3416 } else if (mview->tableIndex == kMainViewAngleTableIndex) { /* Angles */
3417 ip = mview->mol->angles + i2 * 3;
3418 IntGroupAdd(sel, ip[0], 1);
3419 IntGroupAdd(sel, ip[1], 1);
3420 IntGroupAdd(sel, ip[2], 1);
3421 } else if (mview->tableIndex == kMainViewDihedralTableIndex || mview->tableIndex == kMainViewImproperTableIndex) { /* Dihedrals, impropers */
3422 ip = (mview->tableIndex == kMainViewDihedralTableIndex ? mview->mol->dihedrals : mview->mol->impropers) + i2 * 4;
3423 IntGroupAdd(sel, ip[0], 1);
3424 IntGroupAdd(sel, ip[1], 1);
3425 IntGroupAdd(sel, ip[2], 1);
3426 IntGroupAdd(sel, ip[3], 1);
3429 IntGroupRelease(ig);
3431 MoleculeSetSelection(mview->mol, sel);
3432 IntGroupRelease(sel);
3436 sMainView_ParameterTableSetItemText(Molecule *mol, int column, int row, const char *s)
3440 const char *kstr = NULL;
3441 UnionPar *up = NULL;
3442 if (mol == NULL || (par = mol->par) == NULL || row < 0)
3444 up = ParameterTableGetItemPtr(par, row, &type);
3450 case 1: kstr = "atom_type"; break;
3451 case 2: kstr = "eps"; break;
3452 case 3: kstr = "r_eq"; break;
3453 case 4: kstr = "eps14"; break;
3454 case 5: kstr = "r_eq14"; break;
3455 case 6: kstr = "atomic_number"; break;
3456 case 7: kstr = "weight"; break;
3461 case 1: kstr = "atom_types"; break;
3462 case 2: kstr = "k"; break;
3463 case 3: kstr = "r0"; break;
3468 case 1: kstr = "atom_types"; break;
3469 case 2: kstr = "k"; break;
3470 case 3: kstr = "a0"; break;
3473 case kDihedralParType:
3474 case kImproperParType:
3476 case 1: kstr = "atom_types"; break;
3477 case 2: kstr = "k"; break;
3478 case 3: kstr = "period"; break;
3479 case 4: kstr = "phi0"; break;
3482 case kVdwPairParType:
3484 case 1: kstr = "atom_types"; break;
3485 case 2: kstr = "eps"; break;
3486 case 3: kstr = "r_eq"; break;
3487 case 4: kstr = "eps14"; break;
3488 case 5: kstr = "r_eq14"; break;
3496 idx = ParameterTableGetItemIndex(par, row, &type);
3497 MolActionCreateAndPerform(mol, SCRIPT_ACTION("iissi"), "set_parameter_attr", type, idx, kstr, s, 0);
3501 MainView_setValueForTable(MainView *mview, int column, int row, const char *buf)
3507 if (mview == NULL || (mol = mview->mol) == NULL)
3509 MainView_valueForTable(mview, column, row, temp, sizeof temp);
3510 if (strcmp(buf, temp) == 0 || buf[0] == 0)
3511 return; /* No change */
3512 idx = MainView_tableRowToIndex(mview, row);
3513 if (mview->tableIndex == kMainViewAtomTableIndex) { /* Atoms */
3515 case 1: key = "name"; break;
3516 case 2: key = "atom_type"; break;
3517 case 3: key = "element"; break;
3518 case 4: { /* Residue */
3519 IntGroup *ig = IntGroupNewWithPoints(idx, 1, -1);
3520 MolActionCreateAndPerform(mol, SCRIPT_ACTION("Gs"), "assign_residue", ig, buf);
3521 IntGroupRelease(ig);
3524 case 5: key = "x"; break;
3525 case 6: key = "y"; break;
3526 case 7: key = "z"; break;
3527 case 8: key = "charge"; break;
3530 MolActionCreateAndPerform(mol, SCRIPT_ACTION("iss"), "set_atom_attr", idx, key, buf);
3531 } else if (mview->tableIndex == kMainViewParameterTableIndex) { /* Parameters */
3532 sMainView_ParameterTableSetItemText(mview->mol, column, row, buf);
3533 } else if (mview->tableIndex == kMainViewUnitCellTableIndex) { /* Unit cell */
3534 Double cellPars[12], dval;
3538 if (mol->cell == NULL) {
3540 static const Double sCellPars[] = {1, 1, 1, 90, 90, 90};
3541 MolActionCreateAndPerform(mol, gMolActionSetCell, 6, sCellPars, 0);
3543 if (row >= 0 && row < 6) {
3545 memmove(cellPars, mol->cell->cell, sizeof(Double) * 6);
3546 if (mol->cell->has_sigma)
3547 memmove(cellPars + 6, mol->cell->cellsigma, sizeof(Double) * 6);
3548 else memset(cellPars + 6, 0, sizeof(Double) * 6);
3549 dval = strtod(buf, NULL);
3553 cellPars[row] = dval;
3556 cellPars[row + 6] = dval;
3558 MolActionCreateAndPerform(mol, gMolActionSetCell, n1, cellPars, 0);
3560 memmove(vecs, mol->cell->axes, sizeof(Vector) * 3);
3561 vecs[3] = mol->cell->origin;
3562 memmove(flags, mol->cell->flags, 3);
3563 if (row >= 6 && row < 9)
3564 flags[row - 6] = strtol(buf, NULL, 0);
3566 Vector *vp = vecs + (row - 9);
3567 dval = strtod(buf, NULL);
3570 else if (column == 2)
3574 MolActionCreateAndPerform(mol, gMolActionSetBox, vecs, vecs + 1, vecs + 2, vecs + 3, (flags[0] != 0) * 4 + (flags[1] != 0) * 2 + (flags[2] != 0), 0);
3577 } else if (mview->tableIndex == kMainViewXtalCoordTableIndex) { /* Fractional coords */
3579 case 1: key = "name"; break;
3580 case 2: key = "element"; break;
3581 case 3: key = "fract_x"; break;
3582 case 4: key = "fract_y"; break;
3583 case 5: key = "fract_z"; break;
3584 case 6: key = "occupancy"; break;
3585 case 7: key = "temp_factor"; break;
3588 MolActionCreateAndPerform(mol, SCRIPT_ACTION("iss"), "set_atom_attr", idx, key, buf);
3593 MainView_isTableItemEditable(MainView *mview, int column, int row)
3596 return ParameterTableIsItemEditable(gBuiltinParameters, column, row);
3597 if (mview->mol == NULL)
3599 if (mview->tableIndex == kMainViewParameterTableIndex)
3600 return ParameterTableIsItemEditable(mview->mol->par, column, row);
3601 if (mview->tableIndex == kMainViewUnitCellTableIndex) {
3602 if ((row >= 0 && row < 6 && column >= 1 && column <= 3) ||
3603 (row >= 6 && row < 9 && column == 1) ||
3604 (row >= 9 && row < 13 && column >= 1 && column <= 3))
3608 if (mview->tableIndex >= 0 && mview->tableIndex < sizeof(sColumnInfo) / sizeof(sColumnInfo[0]))
3609 return sColumnInfo[mview->tableIndex][column].editable != 0;
3614 MainView_tableType(MainView *mview)
3617 return kMainViewParameterTableIndex;
3618 if (mview->mol == NULL)
3620 return mview->tableIndex;
3624 MainView_dragTableSelectionToRow(MainView *mview, int row)
3626 Int *new2old, i, n, count, natoms, start_idx, to_idx;
3628 if (mview == NULL || mview->mol == NULL)
3630 if (mview->tableIndex != kMainViewAtomTableIndex && mview->tableIndex != kMainViewXtalCoordTableIndex)
3631 return; /* Only atom tables can respond */
3632 if (md_is_running(mview->mol->arena)) {
3633 MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
3636 n = MainView_numberOfRowsInTable(mview);
3637 if (row < 0 || row > n)
3638 return; /* Out of range */
3639 natoms = mview->mol->natoms;
3643 to_idx = MainView_tableRowToIndex(mview, row);
3644 sel = MoleculeGetSelection(mview->mol);
3645 if (sel == NULL || (count = IntGroupGetCount(sel)) == 0)
3647 new2old = (Int *)calloc(sizeof(Int), natoms);
3648 if (new2old == NULL)
3651 // Place the atoms above the target position
3652 for (i = n = 0; i < to_idx; i++) {
3653 if (IntGroupLookupPoint(sel, i) < 0)
3657 // Place the atoms within the selection
3658 for (i = 0; i < count; i++) {
3659 new2old[n++] = IntGroupGetNthPoint(sel, i);
3661 // Place the remaining atoms
3662 for (i = to_idx; i < natoms; i++) {
3663 if (IntGroupLookupPoint(sel, i) < 0)
3666 MolActionCreateAndPerform(mview->mol, gMolActionRenumberAtoms, n, new2old);
3669 sel = IntGroupNewWithPoints(start_idx, count, -1);
3670 MolActionCreateAndPerform(mview->mol, gMolActionSetSelection, sel);
3671 IntGroupRelease(sel);
3675 MainView_selectedMO(MainView *mview)
3677 if (mview == NULL || mview->mol == NULL || mview->tableIndex != kMainViewMOTableIndex)
3679 return MainViewCallback_getTableSelection(mview); /* Note: the indices are 0 based */