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 "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 /* Invalid bond/angle/torsion value, used in internal cache */
34 const Double kInvalidValue = -10000000.0;
36 #pragma mark ==== MainView public methods ====
39 MainView_setViewObject(MainView *mview, void *ref)
41 static GLdouble sIdentity[16] = {
42 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1
45 if (mview->ref == NULL) {
46 /* Initialize GUI-only members */
47 mview->mode = kTrackballRotateMode;
48 mview->tempAtoms[0] = mview->tempAtoms[1] = -1;
49 memmove(mview->modelview_matrix, sIdentity, sizeof(sIdentity));
50 memmove(mview->projection_matrix, sIdentity, sizeof(sIdentity));
51 mview->tableCache = IntGroupNew();
52 mview->tableSelection = IntGroupNew();
54 mview->ref = ref; /* No retain */
55 IntGroupClear(mview->tableCache);
56 IntGroupClear(mview->tableSelection);
57 MoleculeCallback_notifyModification(mview->mol, 0);
62 MainView_isAtomHidden(MainView *mview, int index)
64 if (index < 0 || index >= mview->mol->natoms)
66 else if (mview->visibleFlags == NULL)
68 else return (mview->visibleFlags[index] == 0);
69 /* Atom *ap = ATOM_AT_INDEX(mview->mol->atoms, index);
70 if (ap->exflags & kAtomHiddenFlag)
72 if (!mview->showHydrogens && ap->atomicNumber == 1)
74 if (!mview->showDummyAtoms && ap->atomicNumber == 0)
80 MainView_refreshCachedInfo(MainView *mview)
83 int i, j, n1, n2, n3, n4;
87 if (mview == NULL || (mol = mview->mol) == NULL)
90 /* Rebuild internal caches */
93 if (mview->visibleFlags == NULL)
94 mview->visibleFlags = (Byte *)malloc(mol->natoms);
96 mview->visibleFlags = (Byte *)realloc(mview->visibleFlags, mol->natoms);
97 memset(mview->visibleFlags, 0, mol->natoms);
98 mview->countHidden = 0;
99 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
100 if (ap->exflags & kAtomHiddenFlag) {
101 mview->countHidden++;
104 if (!mview->showHydrogens && ap->atomicNumber == 1)
106 if (!mview->showDummyAtoms && ap->atomicNumber == 0)
108 mview->visibleFlags[i] = 1;
111 /* Selection (only temporary used within this function) */
113 IntGroup *sel = MoleculeGetSelection(mol);
114 if (sel != NULL && IntGroupGetCount(sel) > 0) {
115 for (i = 0; (n1 = IntGroupGetStartPoint(sel, i)) >= 0; i++) {
116 n2 = IntGroupGetEndPoint(sel, i);
117 if (n2 > mol->natoms)
120 mview->visibleFlags[n1++] |= 2;
126 if (mview->tableCache == NULL)
127 mview->tableCache = IntGroupNew();
128 if (mview->tableSelection == NULL)
129 mview->tableSelection = IntGroupNew();
130 IntGroupClear(mview->tableCache);
131 IntGroupClear(mview->tableSelection);
133 if (mview->tableIndex == kMainViewAtomTableIndex) { /* Atoms */
134 for (i = j = 0; i < mol->natoms; i++) {
135 f1 = mview->visibleFlags[i];
137 IntGroupAdd(mview->tableCache, i, 1);
139 IntGroupAdd(mview->tableSelection, j, 1);
143 } else if (mview->tableIndex == kMainViewBondTableIndex) { /* Bonds */
144 for (i = j = 0; i < mol->nbonds; i++) {
145 n1 = mol->bonds[i * 2];
146 n2 = mol->bonds[i * 2 + 1];
147 f1 = mview->visibleFlags[n1];
148 f2 = mview->visibleFlags[n2];
149 if ((f1 & 1) != 0 && (f2 & 1) != 0) {
150 IntGroupAdd(mview->tableCache, i, 1);
151 if ((f1 & 2) != 0 && (f2 & 2) != 0)
152 IntGroupAdd(mview->tableSelection, j, 1);
156 } else if (mview->tableIndex == kMainViewAngleTableIndex) { /* Angles */
157 for (i = j = 0; i < mol->nangles; i++) {
158 n1 = mol->angles[i * 3];
159 n2 = mol->angles[i * 3 + 1];
160 n3 = mol->angles[i * 3 + 2];
161 f1 = mview->visibleFlags[n1];
162 f2 = mview->visibleFlags[n2];
163 f3 = mview->visibleFlags[n3];
164 if ((f1 & 1) != 0 && (f2 & 1) != 0 && (f3 & 1) != 0) {
165 IntGroupAdd(mview->tableCache, i, 1);
166 if ((f1 & 2) != 0 && (f2 & 2) != 0 && (f3 & 2) != 0)
167 IntGroupAdd(mview->tableSelection, j, 1);
171 } else if (mview->tableIndex == kMainViewDihedralTableIndex) { /* Dihedrals */
172 for (i = j = 0; i < mol->ndihedrals; i++) {
173 n1 = mol->dihedrals[i * 4];
174 n2 = mol->dihedrals[i * 4 + 1];
175 n3 = mol->dihedrals[i * 4 + 2];
176 n4 = mol->dihedrals[i * 4 + 3];
177 f1 = mview->visibleFlags[n1];
178 f2 = mview->visibleFlags[n2];
179 f3 = mview->visibleFlags[n3];
180 f4 = mview->visibleFlags[n4];
181 if ((f1 & 1) != 0 && (f2 & 1) != 0 && (f3 & 1) != 0 && (f4 & 1) != 0) {
182 IntGroupAdd(mview->tableCache, i, 1);
183 if ((f1 & 2) != 0 && (f2 & 2) != 0 && (f3 & 2) != 0 && (f4 & 2) != 0)
184 IntGroupAdd(mview->tableSelection, j, 1);
188 } else if (mview->tableIndex == kMainViewImproperTableIndex) { /* Impropers */
189 for (i = j = 0; i < mol->nimpropers; i++) {
190 n1 = mol->impropers[i * 4];
191 n2 = mol->impropers[i * 4 + 1];
192 n3 = mol->impropers[i * 4 + 2];
193 n4 = mol->impropers[i * 4 + 3];
194 f1 = mview->visibleFlags[n1];
195 f2 = mview->visibleFlags[n2];
196 f3 = mview->visibleFlags[n3];
197 f4 = mview->visibleFlags[n4];
198 if ((f1 & 1) != 0 && (f2 & 1) != 0 && (f3 & 1) != 0 && (f4 & 1) != 0) {
199 IntGroupAdd(mview->tableCache, i, 1);
200 if ((f1 & 2) != 0 && (f2 & 2) != 0 && (f3 & 2) != 0 && (f4 & 2) != 0)
201 IntGroupAdd(mview->tableSelection, j, 1);
205 } else if (mview->tableIndex == kMainViewParameterTableIndex || /* Parameter infos */
206 mview->tableIndex == kMainViewUnitCellTableIndex) { /* Unit cell infos */
207 /* Do nothing (tableCache will not be used) */
208 } else if (mview->tableIndex == kMainViewMOTableIndex) { /* MO infos */
209 /* Really no need to cache info, but create it anyway to simplify code */
210 if (mol->bset != NULL && mol->bset->ncomps > 0)
211 IntGroupAdd(mview->tableCache, 0, mol->bset->ncomps);
214 /* Clear internal selection flag */
215 for (i = 0; i < mol->natoms; i++) {
216 mview->visibleFlags[i] &= ~2;
220 #pragma mark ====== 2D/3D transform operations ======
223 MainView_resizeToFit(MainView *mview)
227 if (mview == NULL || mview->mol == NULL)
229 if (mview->mol->natoms == 0) {
230 TrackballReset(mview->track);
233 MoleculeCenterOfMass(mview->mol, &p, NULL);
234 /* if (mview->mol->is_xtal_coord)
235 TransformVec(&p, mview->mol->cell->tr, &p); */
236 f[0] = -p.x / mview->dimension;
237 f[1] = -p.y / mview->dimension;
238 f[2] = -p.z / mview->dimension;
239 TrackballSetTranslate(mview->track, f);
242 r0: the longest distance from the center of mass
243 r0 > dimension: scale = -log(r0/dimension)/log(10) (negative)
244 r0 < dimension: scale = -log(atan2(r0, dimension*cot(15deg))*180deg/pi*2/30deg)/log(10) (positive)
250 double r0 = 0.0, r1, scale;
251 for (i = 0, ap = mview->mol->atoms; i < mview->mol->natoms; i++, ap = ATOM_NEXT(ap)) {
253 /* if (mview->mol->is_xtal_coord)
254 TransformVec(&q, mview->mol->cell->tr, &q); */
260 r0 /= mview->dimension;
264 scale = -log(atan2(r0, kCot15Deg) * kRad2Deg * 2 / 30.0) / kLog10;
266 scale = -log(r0) / kLog10;
267 TrackballSetScale(mview->track, scale);
270 MainViewCallback_setNeedsDisplay(mview, 1);
277 const ElementPar *app;
279 GLdouble max[3], min[3], center[3];
280 float frame[4], width, height, cot15, rsin15, cot_th, rsin_th, d, trans[3];
283 if (mview == NULL || mview->mol == NULL)
287 /* Transform the coordinates of all atoms with the rotation part of
288 * the model-view matrix */
289 memmove((GLdouble *)mm, mview->modelview_matrix, sizeof(float) * 16);
290 natoms = mol->natoms;
291 pp = (GLdouble *)calloc(sizeof(GLdouble), 4 * natoms);
292 MALLOC_CHECK(pp, "resizing the model to fit");
293 for (i = 0; i < natoms; i++) {
294 ap = ATOM_AT_INDEX(mol->atoms, i);
297 p0[0] = ap->r.x; p0[1] = ap->r.y; p0[2] = ap->r.z;
298 p0[3] = 0.0; /* This disables the translation part */
300 MAT_DOT_VEC_4X4(p1, mm, p0); /* #define'd in GLUT/vvector.h */
303 /* Determine the center for each axis */
304 max[0] = max[1] = max[2] = -1e20;
305 min[0] = min[1] = min[2] = 1e20;
306 for (i = 0; i < natoms; i++) {
308 for (j = 0; j < 3; j++) {
315 for (j = 0; j < 3; j++)
316 center[j] = (min[j] + max[j]) * 0.5;
318 /* Get the frame size */
319 MainViewCallback_frame(mview, frame);
320 width = frame[2] - frame[0];
321 height = frame[3] - frame[1];
323 /* Calculate the minimum distance from which the view angle of
324 * the atom becomes no more than 15 degree (for the y direction)
325 * and theta degree (for the x direction, theta = arctan(h/w*tan(15 deg))) */
326 cot15 = 3.73205080756888;
327 rsin15 = 3.86370330515628;
328 cot_th = width / height * cot15;
329 rsin_th = sqrt(1.0 + cot_th * cot_th);
331 for (i = 0; i < natoms; i++) {
333 ap = ATOM_AT_INDEX(mol->atoms, i);
336 app = &(gBuiltinParameters->atomPars[ap->atomicNumber]);
339 r = app->radius * mview->atomRadius;
340 z = pp[i * 4 + 2] - center[2];
341 dx = pp[i * 4] * cot_th + r * rsin_th + z;
342 dy = pp[i * 4 + 1] * cot15 + r * rsin15 + z;
348 mview->dimension = d / cot15;
349 TrackballSetScale(mview->track, 1.0);
350 trans[0] = center[0] / mview->dimension;
351 trans[1] = center[1] / mview->dimension;
352 trans[2] = center[2] / mview->dimension;
353 TrackballSetTranslate(mview->track, trans);
360 MainView_convertScreenPositionToObjectPosition(MainView *mview, const GLfloat *screenPos, GLfloat *objectPos)
363 GLint viewport[4], n;
365 GLdouble posX, posY, posZ;
368 MainViewCallback_frame(mview, rect);
369 viewport[0] = viewport[1] = 0;
370 viewport[2] = (GLint)(rect[2] - rect[0]);
371 viewport[3] = (GLint)(rect[3] - rect[1]);
372 MainViewCallback_lockFocus(mview);
373 if (screenPos[2] >= 0.0)
376 glReadPixels(screenPos[0], screenPos[1], 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
377 gluUnProject(screenPos[0], screenPos[1], winZ, mview->modelview_matrix, mview->projection_matrix, viewport, &posX, &posY, &posZ);
379 MainViewCallback_unlockFocus(mview);
383 if (n != GL_NO_ERROR || winZ == 1.0)
390 MainView_convertObjectPositionToScreenPosition(MainView *mview, const GLfloat *objectPos, GLfloat *screenPos)
394 GLdouble objX, objY, objZ;
397 MainViewCallback_frame(mview, rect);
398 viewport[0] = viewport[1] = 0;
399 viewport[2] = (GLint)(rect[2] - rect[0]);
400 viewport[3] = (GLint)(rect[3] - rect[1]);
401 gluProject(objectPos[0], objectPos[1], objectPos[2], mview->modelview_matrix, mview->projection_matrix, viewport, &objX, &objY, &objZ);
402 if (glGetError() == GL_NO_ERROR) {
406 /* fprintf(stderr, "object(%.3f,%.3f,%.3f) screen(%.3f,%.3f,%.3f)\n", objectPos[0], objectPos[1], objectPos[2], screenPos[0], screenPos[1], screenPos[2]); */
412 MainView_findObjectAtPoint(MainView *mview, const float *mousePos, int *outIndex1, int *outIndex2, int mouseCheck, int ignoreExpandedAtoms)
414 float screenPos[3], op[3], oq[3], pqlen, pqlen2;
415 Vector pq, pa, v1, r1, r2;
416 int i, natoms, nbonds;
419 const ElementPar *ep;
425 if (mview == NULL || mview->mol == NULL) {
426 *outIndex1 = *outIndex2 = -1;
432 screenPos[0] = mousePos[0];
433 screenPos[1] = mousePos[1];
435 if (MainView_convertScreenPositionToObjectPosition(mview, screenPos, op) == 0)
436 return 0; /* Nothing is here */
438 /* PQ is the part of the eyesight line in the visible area */
440 MainView_convertScreenPositionToObjectPosition(mview, screenPos, op);
442 MainView_convertScreenPositionToObjectPosition(mview, screenPos, oq);
443 pq.x = oq[0] - op[0];
444 pq.y = oq[1] - op[1];
445 pq.z = oq[2] - op[2];
446 pqlen2 = VecLength2(pq);
447 pqlen = sqrt(pqlen2);
448 natoms = mol->natoms;
451 for (i = 0; i < natoms; i++) {
453 float pq1len2, pq1len;
454 if (mouseCheck && i % 50 == 0 && MainViewCallback_mouseCheck(mview))
455 return 0; /* If mouse event is detected return immediately */
456 /* Examine if an atom is visible or not */
457 /* The distance of the atom center (A) from line PQ: */
458 /* d = |VecCross(PA,PQ)|/|PQ| */
459 /* z = VecDot(PA,PQ)/|PQ|^2 - sqrt(r^2 - d^2)/|PQ| */
460 ap = ATOM_AT_INDEX(mol->atoms, i);
463 if (MainView_isAtomHidden(mview, i))
465 if (ignoreExpandedAtoms && SYMOP_ALIVE(ap->symop))
468 /* if (mol->is_xtal_coord)
469 TransformVec(&r1, mol->cell->tr, &r1); */
473 if (ap->aniso != NULL) {
474 /* Convert to ellipsoid principal axes */
476 Aniso *anp = ap->aniso;
477 MatrixInvert(m1, anp->pmat);
478 MatrixVec(&pq1, m1, &pq);
479 MatrixVec(&pa1, m1, &pa);
480 r = mview->probabilityScale;
481 pq1len2 = VecLength2(pq1);
482 pq1len = sqrt(pq1len2);
484 if (mview->showEllipsoids) {
485 r = biso2radius(ap->tempFactor) * mview->probabilityScale;
487 ep = &(gElementParameters[ap->atomicNumber]);
490 r = ep->radius * mview->atomRadius;
497 VecCross(v1, pa1, pq1);
498 d2 = VecLength2(v1) / pq1len2;
500 continue; /* Not visible */
501 z = VecDot(pa1, pq1) / pq1len2 - sqrt(r * r - d2) / pq1len;
502 if (z < 0.0 || z > 1.0)
503 continue; /* Out of viewing volume */
509 nbonds = mol->nbonds;
510 for (i = 0; i < nbonds; i++) {
511 Vector vx, vy, vz, vv, vp;
512 Double wb, wa, t, wx, blen;
513 if (mouseCheck && i % 50 == 0 && MainViewCallback_mouseCheck(mview))
514 return 0; /* If mouse event is detected return immediately */
515 /* Examine if a bond is visible or not */
516 ip = &(mol->bonds[i * 2]);
517 ap = ATOM_AT_INDEX(mol->atoms, ip[0]);
518 bp = ATOM_AT_INDEX(mol->atoms, ip[1]);
519 if (MainView_isAtomHidden(mview, ip[0]) || MainView_isAtomHidden(mview, ip[1]))
521 if (ignoreExpandedAtoms && SYMOP_ALIVE(ap->symop) && SYMOP_ALIVE(bp->symop))
523 /* vx/vy/vz is a orthonormal base in which AB parallels the x-axis
524 and AP in in the xy plane */
525 /* vp and vv is AP and PQ in that coordinate system */
528 /* if (mol->is_xtal_coord) {
529 TransformVec(&r1, mol->cell->tr, &r1);
530 TransformVec(&r2, mol->cell->tr, &r2);
536 blen = sqrt(VecLength2(vx));
542 VecCross(vz, vx, v1);
543 if (NormalizeVec(&vz, &vz))
545 VecCross(vy, vz, vx);
546 vp.x = VecDot(v1, vx);
547 vp.y = VecDot(v1, vy);
548 vp.z = VecDot(v1, vz);
549 vv.x = VecDot(pq, vx);
550 vv.y = VecDot(pq, vy);
551 vv.z = VecDot(pq, vz);
552 /* The bond surface is y^2 + z^2 = r^2, 0 <= x <= 1 */
553 /* The eyesight line is (x,y,z) = vp + t * vv, 0 <= t <= 1 */
554 /* 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) */
555 /* (Note that vp.z = 0 by definition) */
556 r = mview->bondRadius;
558 wa = vv.y * vv.y + vv.z * vv.z;
559 d2 = wb * wb - wa * (vp.y * vp.y - r * r);
561 continue; /* Not visible */
562 t = (-wb - sqrt(d2)) / wa;
564 continue; /* Out of visible volume */
565 wx = vp.x + t * vv.x;
566 if (wx < 0 || wx > blen)
567 continue; /* Outside of bond */
576 return (n1 >= 0 || n2 >= 0);
579 /* Examine if anything is drawn at the point */
580 screenPos[0] = mousePos[0];
581 screenPos[1] = mousePos[1];
583 if (MainView_convertScreenPositionToObjectPosition(mview, screenPos, objectPos) == 0)
584 return 0; /* Nothing is here */
585 op[0] = objectPos[0];
586 op[1] = objectPos[1];
587 op[2] = objectPos[2];
589 /* Examine the distance from the atom center with op, and select the one
590 with the smallest difference with the radius on the screen */
591 atomRadius = mview->atomRadius;
592 bondRadius = mview->bondRadius;
593 natoms = mol->natoms;
596 for (i = 0; i < natoms; i++) {
597 ap = ATOM_AT_INDEX(mol->atoms, i);
600 dp = &(gBuiltinParameters->atomPars[ap->atomicNumber]);
603 p[0] = objectPos[0] - ap->r.x;
604 p[1] = objectPos[1] - ap->r.y;
605 p[2] = objectPos[2] - ap->r.z;
606 rr = sqrt(p[0]*p[0] + p[1]*p[1] + p[2]*p[2]) / (dp->radius * atomRadius);
615 nbonds = mol->nbonds;
616 for (i = 0; i < nbonds; i++) {
618 ip = &(mol->bonds[i * 2]);
619 ap = ATOM_AT_INDEX(mol->atoms, ip[0]);
620 bp = ATOM_AT_INDEX(mol->atoms, ip[1]);
621 p[0] = objectPos[0] - ap->r.x;
622 p[1] = objectPos[1] - ap->r.y;
623 p[2] = objectPos[2] - ap->r.z;
624 b[0] = bp->r.x - ap->r.x;
625 b[1] = bp->r.y - ap->r.y;
626 b[2] = bp->r.z - ap->r.z;
627 /* ap.r + q*(b[0],b[1],b[2]) is the closest point on line AB to objectPos */
628 /* If q is outside [0, 1] then the point is outside the line segment AB
630 q = (p[0]*b[0]+p[1]*b[1]+p[2]*b[2])/(b[0]*b[0]+b[1]*b[1]+b[2]*b[2]);
636 dr = fabs(bondRadius - sqrt(p[0]*p[0] + p[1]*p[1] + p[2]*p[2]));
645 return (n1 >= 0 || n2 >= 0);
650 MainView_screenCenterPointOfAtom(MainView *mview, int index, float *outScreenPos)
653 const ElementPar *dp;
658 if (mview == NULL || mview->mol == NULL || index < 0 || index >= mview->mol->natoms)
660 ap = ATOM_AT_INDEX(mview->mol->atoms, index);
662 /* Camera position in object coordinates */
665 /* The atom position (in Cartesian) */
667 /* if (mview->mol->is_xtal_coord)
668 TransformVec(&v, mview->mol->cell->tr, &v); */
670 /* The vector from atom center to camera */
673 /* Get the surface point of the ellipsoid/sphere along the camera vector */
674 if (mview->showEllipsoids) {
676 Aniso *anp = ap->aniso;
679 MatrixInvert(m1, anp->pmat);
680 /* Convert the 'screen plane' vectors to ellipsoid principal axes */
682 VecCross(vx, mview->lookto, vy);
683 MatrixVec(&vx, m1, &vx);
684 MatrixVec(&vy, m1, &vy);
685 /* Touching point of the 'screen plane' to the ellipsoid */
686 VecCross(vz, vx, vy);
687 w = mview->probabilityScale / VecLength(vz) * 1.1;
689 MatrixVec(&vz, anp->pmat, &vz);
690 /* The crossing point of the camera vector with the touching plane */
691 w = fabs(VecDot(pv, vz) / VecLength2(pv));
694 w = mview->probabilityScale * biso2radius(ap->tempFactor) / VecLength(pv) * 1.1;
698 dp = &(gElementParameters[ap->atomicNumber]);
699 rad = dp->radius * mview->atomRadius;
700 w = rad / VecLength(pv) * 1.1;
705 /* Transform to screen coordinate */
709 return MainView_convertObjectPositionToScreenPosition(mview, p, outScreenPos);
713 MainView_getCamera(MainView *mview, Vector *outCamera, Vector *outLookAt, Vector *outUp)
716 *outCamera = mview->camera;
717 *outLookAt = mview->lookat;
722 #pragma mark ====== Draw model ======
727 static GLfloat pos[] = {0.0f, 0.0f, 1.0f, 0.0f};
728 glEnable(GL_LIGHTING);
730 /* On Mac OS 10.6, redefining the light position seems to be necessary
731 every time lighting is made enabled */
732 glMatrixMode(GL_MODELVIEW);
735 glLightfv(GL_LIGHT0, GL_POSITION, pos);
741 MainView_initializeOpenGLView(MainView *mview)
743 static GLfloat ambient[] = {0.6, 0.6, 0.6, 1.0}; // Some white ambient light.
744 static GLfloat diffuse[] = {1.0, 1.0, 1.0, 1.0}; // A white light.
746 glEnable(GL_DEPTH_TEST);
747 glEnable(GL_LIGHTING);
749 // Set the ambient light
750 glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
752 // Set the light and switch it on
753 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
758 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
761 mview->isInitialized = 1;
764 /* Get orthogonal unit vectors */
766 getOrthogonalVectors(const GLfloat *ap, GLfloat *bp, GLfloat *cp)
769 ra = sqrt(ap[0] * ap[0] + ap[1] * ap[1] + ap[2] * ap[2]);
773 if (fabs(ap[0]) < fabs(ap[1])) {
774 if (fabs(ap[0]) < fabs(ap[2])) {
784 if (fabs(ap[1]) < fabs(ap[2])) {
794 rb = 1 / sqrt(bp[0] * bp[0] + bp[1] * bp[1] + bp[2] * bp[2]);
798 cp[0] = ra * (ap[1] * bp[2] - ap[2] * bp[1]);
799 cp[1] = ra * (ap[2] * bp[0] - ap[0] * bp[2]);
800 cp[2] = ra * (ap[0] * bp[1] - ap[1] * bp[0]);
801 /* printf("a = (%f, %f, %f) b = (%f, %f, %f) c = (%f, %f, %f)\n",
802 ap[0], ap[1], ap[2], bp[0], bp[1], bp[2], cp[0], cp[1], cp[2]); */
806 static GLfloat sSinCache[81];
807 static int sSinCacheSect = 0;
810 setSinCache(int sect)
817 if (n != sSinCacheSect) {
819 for (i = 0; i <= m * 5; i++)
820 sSinCache[i] = sin(3.14159265358979 * 2 / n * i);
826 drawCylinder(const GLfloat *a, const GLfloat *b, GLfloat r, int sect, int closed)
831 GLfloat d[3], v[3], w[3];
832 n = setSinCache(sect);
840 if (getOrthogonalVectors(d, v, w) == 0)
842 glBegin(GL_QUAD_STRIP);
843 for (i = 0; i <= n; i++) {
844 nx = v[0] * c[i] + w[0] * s[i];
845 ny = v[1] * c[i] + w[1] * s[i];
846 nz = v[2] * c[i] + w[2] * s[i];
847 glNormal3f(nx, ny, nz);
848 glVertex3f(a[0] + r * nx, a[1] + r * ny, a[2] + r * nz);
849 glVertex3f(b[0] + r * nx, b[1] + r * ny, b[2] + r * nz);
853 glBegin(GL_TRIANGLE_FAN);
854 glNormal3f(-d[0], -d[1], -d[2]);
855 for (i = 0; i <= n; i++) {
856 nx = v[0] * c[i] + w[0] * s[i];
857 ny = v[1] * c[i] + w[1] * s[i];
858 nz = v[2] * c[i] + w[2] * s[i];
859 glVertex3f(a[0] + r * nx, a[1] + r * ny, a[2] + r * nz);
862 glBegin(GL_TRIANGLE_FAN);
863 glNormal3f(d[0], d[1], d[2]);
864 for (i = 0; i <= n; i++) {
865 nx = v[0] * c[i] + w[0] * s[i];
866 ny = v[1] * c[i] + w[1] * s[i];
867 nz = v[2] * c[i] + w[2] * s[i];
868 glVertex3f(b[0] + r * nx, b[1] + r * ny, b[2] + r * nz);
875 drawCone(const GLfloat *a, const GLfloat *b, GLfloat r, int sect, int closed)
879 GLfloat d[3], v[3], w[3];
881 n = setSinCache(sect);
889 if (getOrthogonalVectors(d, v, w) == 0)
891 glBegin(GL_TRIANGLE_FAN);
895 NormalizeVec(&nv, &nv);
896 glNormal3f(nv.x, nv.y, nv.z);
897 glVertex3f(b[0], b[1], b[2]);
898 for (i = 0; i <= n; i++) {
899 nv.x = v[0] * c[i] + w[0] * s[i];
900 nv.y = v[1] * c[i] + w[1] * s[i];
901 nv.z = v[2] * c[i] + w[2] * s[i];
902 glNormal3f(nv.x, nv.y, nv.z);
903 p1.x = a[0] + r * nv.x;
904 p1.y = a[1] + r * nv.y;
905 p1.z = a[2] + r * nv.z;
906 glNormal3f(nv.x, nv.y, nv.z);
907 glVertex3f(p1.x, p1.y, p1.z);
911 glBegin(GL_TRIANGLE_FAN);
912 glNormal3f(d[0], d[1], d[2]);
913 for (i = 0; i <= n; i++) {
914 p1.x = a[0] + r * (v[0] * c[i] + w[0] * s[i]);
915 p1.y = a[1] + r * (v[1] * c[i] + w[1] * s[i]);
916 p1.z = a[2] + r * (v[2] * c[i] + w[2] * s[i]);
917 glVertex3f(p1.x, p1.y, p1.z);
924 drawSphere(const GLfloat *p, GLfloat r, int sect)
928 n = setSinCache(sect);
933 for (i = 0; i <= n; i++) {
934 glBegin(GL_QUAD_STRIP);
935 for (j = 1; j <= n / 2 - 1; j++) {
936 glNormal3f(s[j] * c[i], s[j] * s[i], c[j]);
937 glVertex3f(r * s[j] * c[i] + p[0], r * s[j] * s[i] + p[1], r * c[j] + p[2]);
938 glNormal3f(s[j] * c[i+1], s[j] * s[i+1], c[j]);
939 glVertex3f(r * s[j] * c[i+1] + p[0], r * s[j] * s[i+1] + p[1], r * c[j] + p[2]);
943 glBegin(GL_TRIANGLE_FAN);
945 glVertex3f(p[0], p[1], r + p[2]);
946 for (i = n; i >= 0; i--) {
947 glNormal3f(s[1] * c[i], s[1] * s[i], c[1]);
948 glVertex3f(r * s[1] * c[i] + p[0], r * s[1] * s[i] + p[1], r * c[1] + p[2]);
951 glBegin(GL_TRIANGLE_FAN);
952 glNormal3f(0, 0, -1);
953 glVertex3f(p[0], p[1], -r + p[2]);
954 for (i = 0; i <= n; i++) {
955 glNormal3f(s[1] * c[i], s[1] * s[i], -c[1]);
956 glVertex3f(r * s[1] * c[i] + p[0], r * s[1] * s[i] + p[1], -r * c[1] + p[2]);
962 drawEllipsoid(const GLfloat *p, const GLfloat *v1, const GLfloat *v2, const GLfloat *v3, int sect)
965 static const GLfloat origin[3] = {0, 0, 0};
966 mat[0] = v1[0]; mat[1] = v1[1]; mat[2] = v1[2]; mat[3] = 0;
967 mat[4] = v2[0]; mat[5] = v2[1]; mat[6] = v2[2]; mat[7] = 0;
968 mat[8] = v3[0]; mat[9] = v3[1]; mat[10] = v3[2]; mat[11] = 0;
969 mat[12] = p[0]; mat[13] = p[1]; mat[14] = p[2]; mat[15] = 1;
970 glMatrixMode(GL_MODELVIEW);
973 glEnable(GL_NORMALIZE);
974 // glutSolidSphere(1, sect, sect); /* Is this faster than my code? */
975 drawSphere(origin, 1, sect);
976 glDisable(GL_NORMALIZE);
981 temporarySelection(MainView *mview, int flags, int clickCount, int ignoreExpandedAtoms)
987 natoms = mview->mol->natoms;
988 selectFlags = (char *)calloc(sizeof(char), natoms);
989 if (selectFlags == NULL)
991 if (clickCount > 0) {
993 if (MainView_findObjectAtPoint(mview, mview->dragStartPos, &n1, &n2, 0, 0)) {
994 if (n1 >= 0 && n1 < natoms)
996 if (n2 >= 0 && n2 < natoms)
1000 if (mview->dragStartPos[0] < mview->dragEndPos[0]) {
1001 rect[0] = mview->dragStartPos[0];
1002 rect[2] = mview->dragEndPos[0];
1004 rect[0] = mview->dragEndPos[0];
1005 rect[2] = mview->dragStartPos[0];
1007 if (mview->dragStartPos[1] < mview->dragEndPos[1]) {
1008 rect[1] = mview->dragStartPos[1];
1009 rect[3] = mview->dragEndPos[1];
1011 rect[1] = mview->dragEndPos[1];
1012 rect[3] = mview->dragStartPos[1];
1014 for (i = 0; i < natoms; i++) {
1015 ap = ATOM_AT_INDEX(mview->mol->atoms, i);
1018 if (MainView_isAtomHidden(mview, i))
1020 if (ignoreExpandedAtoms && SYMOP_ALIVE(ap->symop))
1022 if (mview->draggingMode == kMainViewSelectingRegion) {
1023 /* Check if this atom is within the selection rectangle */
1024 GLfloat objectPos[3];
1025 GLfloat screenPos[3];
1028 /* if (mview->mol->is_xtal_coord)
1029 TransformVec(&r1, mview->mol->cell->tr, &r1); */
1030 objectPos[0] = r1.x;
1031 objectPos[1] = r1.y;
1032 objectPos[2] = r1.z;
1033 if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos) && screenPos[0] >= rect[0] && screenPos[0] <= rect[2] && screenPos[1] >= rect[1] && screenPos[1] <= rect[3])
1035 else selectFlags[i] = 0;
1039 if (flags & kShiftKeyMask) {
1040 for (i = 0; i < natoms; i++)
1041 selectFlags[i] ^= (MoleculeIsAtomSelected(mview->mol, i) != 0);
1047 drawGraphite(MainView *mview)
1049 static GLfloat sDarkCyanColor[] = {0, 0.75, 0.75, 1};
1051 MDGraphiteArena *graphite;
1052 Vector xaxis, yaxis, zaxis, origin;
1054 int i, j, i0, i1, j0, j1, ir;
1055 Double x, dx, y, dy, xx, yy;
1057 if ((arena = mview->mol->arena) != NULL && (graphite = arena->graphite) != NULL) {
1058 graphite_get_axes(graphite, &origin, &xaxis, &yaxis, &zaxis, &R);
1060 origin.x = origin.y = origin.z = 0.0;
1061 xaxis.x = yaxis.y = zaxis.z = 1.0;
1062 xaxis.y = xaxis.z = yaxis.x = yaxis.z = zaxis.x = zaxis.y = 0.0;
1065 i0 = -(mview->showGraphite / 2) - 1;
1066 i1 = i0 + mview->showGraphite + 1;
1067 j0 = -(mview->showGraphite / 2);
1068 j1 = j0 + mview->showGraphite;
1070 dy = 0.866025403784439 * R;
1071 glDisable(GL_LIGHTING);
1072 glColor3fv(sDarkCyanColor);
1073 for (i = i0; i <= i1; i++) {
1074 for (j = j0; j <= j1; j++) {
1076 ir = (i % 2 == 0 ? 0 : 1);
1078 y = (2 * j + ir) * dy;
1081 p[0] = xaxis.x * xx + yaxis.x * y + origin.x;
1082 p[1] = xaxis.y * xx + yaxis.y * y + origin.y;
1083 p[2] = xaxis.z * xx + yaxis.z * y + origin.z;
1085 p[3] = xaxis.x * xx + yaxis.x * yy + origin.x;
1086 p[4] = xaxis.y * xx + yaxis.y * yy + origin.y;
1087 p[5] = xaxis.z * xx + yaxis.z * yy + origin.z;
1089 p[6] = xaxis.x * xx + yaxis.x * yy + origin.x;
1090 p[7] = xaxis.y * xx + yaxis.y * yy + origin.y;
1091 p[8] = xaxis.z * xx + yaxis.z * yy + origin.z;
1093 p[9] = xaxis.x * xx + yaxis.x * y + origin.x;
1094 p[10] = xaxis.y * xx + yaxis.y * y + origin.y;
1095 p[11] = xaxis.z * xx + yaxis.z * y + origin.z;
1099 if ((ir == 0 && j == j0) || (ir == 1 && j == j1))
1101 } else if (i == i1) {
1103 if ((ir == 0 && j == j0) || (ir == 1 && j == j1))
1105 } else if (j == j1) {
1108 } else if (i == i0 + 1) {
1110 } else if (i == i1 - 1) {
1133 static GLfloat sRedColor[] = {1, 0, 0, 1};
1136 drawAtom(MainView *mview, int i1, int selected, const Vector *dragOffset, const Vector *periodicOffset)
1139 const ElementPar *dp;
1146 Transform *trp = NULL;
1147 int natoms = mview->mol->natoms;
1149 /* Extra 2 atoms for the bond being newly created */
1150 if (mview->draggingMode != kMainViewCreatingBond)
1152 if (mview->tempAtoms[i1 - natoms] >= 0)
1153 return; /* Already drawn */
1156 r1 = mview->tempAtomPos[i1 - natoms];
1159 ap = ATOM_AT_INDEX(mview->mol->atoms, i1);
1162 an1 = ap->atomicNumber;
1164 strncpy(label, ap->aname, 4);
1166 if (SYMOP_ALIVE(ap->symop))
1169 if (!mview->showHydrogens && an1 == 1)
1171 if (!mview->showDummyAtoms && an1 == 0)
1173 if (!mview->showExpandedAtoms && expanded)
1175 if (ap != NULL && (ap->exflags & kAtomHiddenFlag))
1177 dp = &(gElementParameters[an1]);
1181 memcpy(rgba, sRedColor, sizeof(rgba));
1183 rgba[0] = dp->r; rgba[1] = dp->g; rgba[2] = dp->b; rgba[3] = 1.0;
1185 if (expanded || periodicOffset != NULL) {
1190 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
1191 if (periodicOffset != NULL)
1192 VecInc(r1, *periodicOffset);
1193 p[0] = r1.x; p[1] = r1.y; p[2] = r1.z;
1194 if (mview->draggingMode == kMainViewDraggingSelectedAtoms && selected) {
1195 p[0] += dragOffset->x;
1196 p[1] += dragOffset->y;
1197 p[2] += dragOffset->z;
1199 if (mview->showEllipsoids) {
1200 if (ap != NULL && ap->aniso != NULL) {
1205 MatrixMul(pmat2, mview->mol->cell->rtr, ap->aniso->pmat);
1206 MatrixMul(pmat2, *((Mat33 *)trp), pmat2);
1207 MatrixMul(pmat2, mview->mol->cell->tr, pmat2);
1208 for (i = 0; i < 9; i++)
1209 elip[i] = pmat2[i] * mview->probabilityScale;
1211 for (i = 0; i < 9; i++)
1212 elip[i] = ap->aniso->pmat[i] * mview->probabilityScale;
1214 drawEllipsoid(p, elip, elip+3, elip+6, 15);
1218 rad = biso2radius(ap->tempFactor);
1219 rad *= mview->probabilityScale;
1221 rad = dp->radius * mview->atomRadius;
1223 drawSphere(p, rad, 8);
1226 drawSphere(p, dp->radius * mview->atomRadius, 12);
1228 if (MainView_convertObjectPositionToScreenPosition(mview, p, p + 3)) {
1229 /* fprintf(stderr, "atom %d: {%f, %f, %f}\n", i1, p[3], p[4], p[5]); */
1231 fp[0] = p[3]; fp[1] = p[4]; fp[2] = p[5];
1232 MainViewCallback_drawLabel(mview, fp, label);
1237 drawBond(MainView *mview, int i1, int i2, int selected, int selected2, int draft, const Vector *dragOffset, const Vector *periodicOffset, int isAnchorBond)
1239 const ElementPar *dp;
1247 float rad_mul = 1.0;
1248 float alpha_mul = 1.0;
1249 int natoms = mview->mol->natoms;
1250 expanded[0] = expanded[1] = 0;
1251 anchor[0] = anchor[1] = 0;
1253 for (i = 0; i < 2; i++) {
1255 in = (i == 0 ? i1 : i2);
1256 if (in >= natoms && in < natoms + 2) {
1257 if (mview->tempAtoms[in - natoms] >= 0) {
1258 ap = ATOM_AT_INDEX(mview->mol->atoms, mview->tempAtoms[in - natoms]);
1259 an[i] = ap->atomicNumber;
1263 r[i] = mview->tempAtomPos[in - natoms];
1267 ap = ATOM_AT_INDEX(mview->mol->atoms, in);
1268 an[i] = ap->atomicNumber;
1270 if (SYMOP_ALIVE(ap->symop))
1272 if (ap->anchor != NULL)
1275 if (!mview->showHydrogens && an[i] == 1)
1277 if (!mview->showDummyAtoms && an[i] == 0)
1279 if (!mview->showExpandedAtoms && expanded[i])
1281 if (ap != NULL && (ap->exflags & kAtomHiddenFlag))
1285 if (periodicOffset != NULL) {
1286 VecInc(r[0], *periodicOffset);
1287 VecInc(r[1], *periodicOffset);
1290 if (anchor[0] + anchor[1] == 2)
1292 if (anchor[0] + anchor[1] == 1)
1293 rad_mul = (isAnchorBond ? 0.3 : 0.6);
1295 dp = &(gElementParameters[an[0]]);
1298 if (selected && selected2) {
1299 memcpy(rgba, sRedColor, sizeof(rgba));
1301 rgba[0] = dp->r; rgba[1] = dp->g; rgba[2] = dp->b; rgba[3] = 1.0;
1303 rgba[3] *= alpha_mul;
1304 if (expanded[0] || periodicOffset != NULL) {
1309 if (mview->draggingMode == kMainViewDraggingSelectedAtoms) {
1311 VecInc(r[0], *dragOffset);
1313 VecInc(r[1], *dragOffset);
1315 p[0] = r[0].x; p[1] = r[0].y; p[2] = r[0].z;
1316 p[3] = (r[1].x + p[0]) * 0.5;
1317 p[4] = (r[1].y + p[1]) * 0.5;
1318 p[5] = (r[1].z + p[2]) * 0.5;
1320 glColor3f(rgba[0], rgba[1], rgba[2]);
1322 glVertex3f(p[0], p[1], p[2]);
1323 glVertex3f(p[3], p[4], p[5]);
1326 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
1327 drawCylinder(p, p + 3, mview->bondRadius * rad_mul, 8, 0);
1329 dp = &(gElementParameters[an[1]]);
1332 if (!selected || !selected2) {
1333 rgba[0] = dp->r; rgba[1] = dp->g; rgba[2] = dp->b; rgba[3] = 1.0;
1335 rgba[3] *= alpha_mul;
1336 if (expanded[1] || periodicOffset != NULL) {
1341 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
1342 p[0] = r[1].x; p[1] = r[1].y; p[2] = r[1].z;
1344 glColor3f(rgba[0], rgba[1], rgba[2]);
1346 glVertex3f(p[0], p[1], p[2]);
1347 glVertex3f(p[3], p[4], p[5]);
1350 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
1351 drawCylinder(p, p + 3, mview->bondRadius * rad_mul, 8, 0);
1355 /* Calculate drag offset during moving the selection. */
1357 calcDragOffset(MainView *mview, Vector *outVector)
1360 if (mview->draggingMode == kMainViewDraggingSelectedAtoms
1361 && MainView_convertScreenPositionToObjectPosition(mview, mview->dragStartPos, p)
1362 && MainView_convertScreenPositionToObjectPosition(mview, mview->dragEndPos, p + 3)) {
1363 outVector->x = p[3] - p[0];
1364 outVector->y = p[4] - p[1];
1365 outVector->z = p[5] - p[2];
1367 outVector->x = outVector->y = outVector->z = 0;
1373 drawModel(MainView *mview)
1376 int i, natoms, nbonds;
1377 int amin, amax, bmin, bmax, cmin, cmax, da, db, dc;
1379 Double atomRadius, bondRadius;
1380 Vector periodicOffset;
1382 int selected, selected2;
1385 int draft = mview->lineMode;
1386 /* static Double gray[] = {0.8, 0.8, 0.8, 1}; */
1388 atomRadius = mview->atomRadius;
1389 bondRadius = mview->bondRadius;
1391 natoms = mol->natoms;
1392 /* if (natoms == 0) {
1396 if (mview->draggingMode == kMainViewSelectingRegion)
1397 selectFlags = temporarySelection(mview, mview->modifierFlags, 0, 0);
1398 else selectFlags = NULL;
1400 if (mview->draggingMode == kMainViewDraggingSelectedAtoms)
1401 calcDragOffset(mview, &mview->dragOffset);
1402 else mview->dragOffset.x = mview->dragOffset.y = mview->dragOffset.z = 0;
1404 if (mview->showGraphite > 0 && mview->showGraphiteFlag) {
1405 drawGraphite(mview);
1408 amin = amax = bmin = bmax = cmin = cmax = 0;
1409 if (mview->showExpandedAtoms && mol->cell != NULL) {
1410 if (mol->cell->flags[0] && mview->showPeriodicImageFlag) {
1411 amin = mview->showPeriodicImage[0];
1412 amax = mview->showPeriodicImage[1];
1414 if (mol->cell->flags[1] && mview->showPeriodicImageFlag) {
1415 bmin = mview->showPeriodicImage[2];
1416 bmax = mview->showPeriodicImage[3];
1418 if (mol->cell->flags[2] && mview->showPeriodicImageFlag) {
1419 cmin = mview->showPeriodicImage[4];
1420 cmax = mview->showPeriodicImage[5];
1422 axes = mol->cell->axes;
1428 for (da = amin; da <= amax; da++) {
1429 for (db = bmin; db <= bmax; db++) {
1430 for (dc = cmin; dc <= cmax; dc++) {
1431 original = (da == 0 && db == 0 && dc == 0);
1433 VecScale(periodicOffset, axes[0], da);
1434 VecScaleInc(periodicOffset, axes[1], db);
1435 VecScaleInc(periodicOffset, axes[2], dc);
1437 for (i = 0, ap = mview->mol->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
1438 if (mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
1439 /* Mouse event is detected */
1443 if (mview->draggingMode == kMainViewCreatingBond && (i == mview->tempAtoms[0] || i == mview->tempAtoms[1] || i >= natoms))
1444 selected = 1; /* extra atoms */
1445 else if (selectFlags != NULL)
1446 selected = selectFlags[i];
1448 selected = MoleculeIsAtomSelected(mview->mol, i);
1449 drawAtom(mview, i, selected, &mview->dragOffset, (original ? NULL : &periodicOffset));
1450 if (ap->anchor != NULL) {
1451 /* Draw anchor bonds */
1453 Int *cp = AtomConnectData(&ap->anchor->connect);
1454 for (j = 0; j < ap->anchor->connect.count; j++) {
1456 if (selectFlags != NULL)
1457 selected2 = selectFlags[k];
1459 selected2 = MoleculeIsAtomSelected(mview->mol, k);
1460 drawBond(mview, i, k, selected, selected2, draft, &mview->dragOffset, (original ? NULL : &periodicOffset), 1);
1469 drawAtom(mview, natoms, 1, &mview->dragOffset, NULL);
1470 drawAtom(mview, natoms + 1, 1, &mview->dragOffset, NULL);
1472 /* Expanded atoms */
1473 /* if (mview->showExpandedAtoms) {
1474 for (i = 0; i < mview->mol->nexatoms; i++) {
1475 if (mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
1476 // Mouse event is detected
1481 drawAtom(mview, -i-1, (selectFlags == NULL ? 0 : selectFlags[i]), &dragOffset, (original ? NULL : &periodicOffset));
1490 nbonds = mol->nbonds;
1492 glDisable(GL_LIGHTING);
1493 for (da = amin; da <= amax; da++) {
1494 for (db = bmin; db <= bmax; db++) {
1495 for (dc = cmin; dc <= cmax; dc++) {
1496 original = (da == 0 && db == 0 && dc == 0);
1498 VecScale(periodicOffset, axes[0], da);
1499 VecScaleInc(periodicOffset, axes[1], db);
1500 VecScaleInc(periodicOffset, axes[2], dc);
1503 for (i = 0; i < nbonds; i++) {
1505 if (draft == 0 && mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
1506 /* Mouse event is detected */
1508 glDisable(GL_LIGHTING);
1511 n1 = mview->mol->bonds[i * 2];
1512 n2 = mview->mol->bonds[i * 2 + 1];
1513 if (selectFlags == NULL) {
1514 selected = MoleculeIsAtomSelected(mview->mol, n1);
1515 selected2 = MoleculeIsAtomSelected(mview->mol, n2);
1517 selected = selectFlags[n1];
1518 selected2 = selectFlags[n2];
1520 drawBond(mview, n1, n2, selected, selected2, draft, &mview->dragOffset, (original ? NULL : &periodicOffset), 0);
1524 if (original && mview->draggingMode == kMainViewCreatingBond) {
1525 drawBond(mview, natoms, natoms + 1, 1, 1, draft, &mview->dragOffset, NULL, 0);
1528 /* Expanded bonds */
1529 /* for (i = 0; i < mview->mol->nexbonds; i++) {
1531 if (draft == 0 && mview->draggingMode != 0 && i % 50 == 0 && MainViewCallback_mouseCheck(mview)) {
1532 // Mouse event is detected
1534 glDisable(GL_LIGHTING);
1537 n1 = mview->mol->exbonds[i * 2];
1538 n2 = mview->mol->exbonds[i * 2 + 1];
1543 if (selectFlags == NULL) {
1544 selected = MoleculeIsAtomSelected(mview->mol, n1);
1545 selected2 = MoleculeIsAtomSelected(mview->mol, n2);
1547 selected = selectFlags[n1];
1548 selected2 = selectFlags[n2];
1550 drawBond(mview, mview->mol->exbonds[i * 2], mview->mol->exbonds[i * 2 + 1], selected, selected2, draft, &dragOffset, (original ? NULL : &periodicOffset));
1559 if (selectFlags != NULL)
1564 drawGraphics(MainView *mview)
1568 for (i = 0; i < mview->ngraphics; i++) {
1569 g = &mview->graphics[i];
1570 if (g->visible == 0)
1572 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, g->rgba);
1574 case kMainViewGraphicLine:
1575 glDisable(GL_LIGHTING);
1576 glColor4fv(g->rgba);
1577 glBegin(GL_LINE_STRIP);
1578 for (j = 0; j < g->npoints; j++) {
1579 if (g->points[j * 3] >= kInvalidFloat)
1581 glVertex3fv(&g->points[j * 3]);
1586 case kMainViewGraphicPoly: {
1587 Vector v0, v1, v2, v3;
1588 glBegin(GL_TRIANGLE_FAN);
1589 v1.x = g->points[0] - g->points[g->npoints - 3];
1590 v1.y = g->points[1] - g->points[g->npoints - 2];
1591 v1.z = g->points[2] - g->points[g->npoints - 1];
1593 for (j = 0; j < g->npoints; j++) {
1594 v2.x = g->points[j * 3 + 3] - g->points[j * 3];
1595 v2.y = g->points[j * 3 + 4] - g->points[j * 3 + 1];
1596 v2.z = g->points[j * 3 + 5] - g->points[j * 3 + 2];
1597 VecCross(v3, v1, v2);
1598 if (NormalizeVec(&v3, &v3) == 0)
1599 glNormal3f(v3.x, v3.y, v3.z);
1600 glVertex3fv(&g->points[j * 3]);
1604 VecCross(v3, v1, v0);
1605 if (NormalizeVec(&v3, &v3) == 0)
1606 glNormal3f(v3.x, v3.y, v3.z);
1607 glVertex3fv(g->points);
1612 case kMainViewGraphicCylinder:
1613 drawCylinder(g->points, g->points + 3, g->points[6], 15, g->closed);
1615 case kMainViewGraphicCone:
1616 drawCone(g->points, g->points + 3, g->points[6], 15, g->closed);
1618 case kMainViewGraphicEllipsoid:
1619 drawEllipsoid(g->points, g->points + 3, g->points + 6, g->points + 9, 8);
1626 drawUnitCell(MainView *mview)
1628 GLfloat a[3], b[3], c[3], ab[3], bc[3], ca[3], abc[3];
1631 if (!mview->showUnitCell || (cp = mview->mol->cell) == NULL)
1633 origin[0] = cp->origin.x;
1634 origin[1] = cp->origin.y;
1635 origin[2] = cp->origin.z;
1636 a[0] = cp->axes[0].x + origin[0];
1637 a[1] = cp->axes[0].y + origin[1];
1638 a[2] = cp->axes[0].z + origin[2];
1639 b[0] = cp->axes[1].x + origin[0];
1640 b[1] = cp->axes[1].y + origin[1];
1641 b[2] = cp->axes[1].z + origin[2];
1642 c[0] = cp->axes[2].x + origin[0];
1643 c[1] = cp->axes[2].y + origin[1];
1644 c[2] = cp->axes[2].z + origin[2];
1646 ab[0] = a[0] + b[0]; ab[1] = a[1] + b[1]; ab[2] = a[2] + b[2];
1647 bc[0] = b[0] + c[0]; bc[1] = b[1] + c[1]; bc[2] = b[2] + c[2];
1648 ca[0] = c[0] + a[0]; ca[1] = c[1] + a[1]; ca[2] = c[2] + a[2];
1649 abc[0] = a[0] + bc[0]; abc[1] = a[1] + bc[1]; abc[2] = a[2] + bc[2];
1651 glDisable(GL_LIGHTING);
1652 glColor3f(0.75, 0.2, 0.0);
1654 glVertex3fv(origin);
1656 glVertex3fv(origin);
1658 glVertex3fv(origin);
1684 drawRotationCenter(MainView *mview)
1686 GLfloat ps[3], pe[3], col[4];
1687 float fd[2]; /* Fovy and distance */
1688 float tr[3]; /* Translation */
1690 if (mview == NULL || !mview->showRotationCenter)
1692 TrackballGetTranslate(mview->track, tr);
1693 TrackballGetPerspective(mview->track, fd);
1694 tr[0] *= -mview->dimension;
1695 tr[1] *= -mview->dimension;
1696 tr[2] *= -mview->dimension;
1697 r = fd[1] * mview->dimension * tan(fd[0] * 0.5 * kDeg2Rad) * 0.1;
1702 col[0] = col[1] = col[2] = 0.5; col[3] = 1.0;
1703 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
1704 drawSphere(ps, rr, 8);
1709 col[0] = 1.0; col[1] = col[2] = 0.0;
1710 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
1711 drawSphere(ps, rr, 8);
1712 drawSphere(pe, rr, 8);
1713 drawCylinder(ps, pe, rr, 8, 0);
1718 col[1] = 1.0; col[0] = col[2] = 0.0;
1719 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
1720 drawSphere(ps, rr, 8);
1721 drawSphere(pe, rr, 8);
1722 drawCylinder(ps, pe, rr, 8, 0);
1727 col[2] = 1.0; col[0] = col[1] = 0.0;
1728 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
1729 drawSphere(ps, rr, 8);
1730 drawSphere(pe, rr, 8);
1731 drawCylinder(ps, pe, rr, 8, 0);
1735 compareLabelByDepth(const void *ap, const void *bp)
1737 Double dz = ((const LabelRecord *)bp)->pos.z - ((const LabelRecord *)ap)->pos.z;
1746 drawLabels(MainView *mview)
1748 /* Transform *trp; */
1753 if (mview->nlabels == 0)
1756 /* mview->sortedLabels = (LabelRecord **)calloc(sizeof(LabelRecord *), mview->nlabels);
1757 if (mview->sortedLabels == NULL)
1760 /* if (mview->mol->is_xtal_coord)
1761 trp = &(mview->mol->cell->tr);
1764 /* Get the screen coordinates of the labels */
1766 for (i = 0; i < mview->mol->natoms; i++) {
1767 float scrp[3], f[3];
1768 ap = ATOM_AT_INDEX(mview->mol->atoms, i);
1769 if (ap->exflags & kAtomHiddenFlag)
1771 if (!mview->showHydrogens && ap->atomicNumber == 1)
1773 if (!mview->showDummyAtoms && ap->atomicNumber == 0)
1775 if (ap->labelid <= 0 || ap->labelid > mview->nlabels)
1777 lp = mview->labels + (ap->labelid - 1);
1778 MainView_screenCenterPointOfAtom(mview, lp->idx1, scrp);
1779 /* if (lp->idx2 >= 0) {
1783 TransformVec(&r1, *trp, &r1);
1784 bp = ATOM_AT_INDEX(mview->mol->atoms, lp->idx2);
1785 if (bp->exflags & kAtomHiddenFlag)
1789 TransformVec(&r2, *trp, &r2);
1790 r1.x = (r1.x + r2.x) * 0.5;
1791 r1.y = (r1.y + r2.y) * 0.5;
1792 r1.z = (r1.z + r2.z) * 0.5;
1796 MainView_convertObjectPositionToScreenPosition(mview, objp, scrp);
1798 lp->pos.x = scrp[0];
1799 lp->pos.y = scrp[1];
1800 lp->pos.z = scrp[2];
1801 MainViewCallback_labelSize(lp->label, f);
1802 f[0] = floor(lp->pos.x - f[0] * 0.5);
1803 f[1] = -floor(lp->pos.y + f[1] * 0.5);
1805 /* fprintf(stderr, "label position (%d) = {%f, %f, %f}\n", i, f[0], f[1], f[2]); */
1806 MainViewCallback_drawLabelAtPoint(lp->label, f);
1807 /* mview->sortedLabels[nlabels++] = lp;
1808 if (nlabels >= mview->nlabels)
1812 /* // Sort labels by z coordinates (descending order)
1813 qsort(mview->sortedLabels, nlabels, sizeof(LabelRecord *), compareLabelByDepth);
1816 for (i = 0; i < nlabels; i++) {
1817 LabelRecord *lp = mview->sortedLabels[i];
1819 MainViewCallback_labelSize(lp->label, f);
1820 f[0] = floor(lp->pos.x - f[0] * 0.5);
1821 f[1] = -floor(lp->pos.y + f[1] * 0.5);
1823 MainViewCallback_drawLabelAtPoint(lp->label, f);
1829 MainView_drawModel(MainView *mview)
1831 float w[4], dimension, distance;
1832 float frame[4], width, height;
1841 if (!mview->isInitialized) {
1842 MainView_initializeOpenGLView(mview);
1845 /* Clear the buffer */
1846 glClearColor(mview->background_color[0], mview->background_color[1], mview->background_color[2], 0);
1847 glClear(GL_COLOR_BUFFER_BIT |
1848 GL_DEPTH_BUFFER_BIT);
1850 if (mview->mol == NULL)
1853 dimension = mview->dimension;
1854 /* dimension = [model dimension]; */
1856 MainViewCallback_frame(mview, frame);
1857 width = frame[2] - frame[0];
1858 height = frame[3] - frame[1];
1860 glViewport(0, 0, width, height);
1862 /* Set up the projection */
1863 glMatrixMode(GL_PROJECTION);
1865 TrackballGetPerspective(mview->track, w);
1866 distance = w[1] * dimension;
1867 mview->perspective_vector[0] = w[0];
1868 mview->perspective_vector[1] = width / height;
1869 mview->perspective_vector[2] = dimension;
1870 mview->perspective_vector[3] = distance + 200.0 * dimension;
1871 gluPerspective(mview->perspective_vector[0], mview->perspective_vector[1], mview->perspective_vector[2], mview->perspective_vector[3]);
1873 /* Set up the model view */
1874 glMatrixMode(GL_MODELVIEW);
1876 glTranslatef(0.0, 0.0, -distance);
1877 TrackballGetRotate(mview->track, w);
1878 glRotatef(w[0], w[1], w[2], w[3]);
1879 TrackballGetTranslate(mview->track, w);
1883 glTranslatef(w[0], w[1], w[2]);
1884 mview->lookat.x = -w[0];
1885 mview->lookat.y = -w[1];
1886 mview->lookat.z = -w[2];
1888 MainViewCallback_clearLabels(mview);
1890 drawUnitCell(mview);
1891 drawRotationCenter(mview);
1892 drawGraphics(mview);
1894 /* Get important matrices and vectors */
1895 glGetDoublev(GL_MODELVIEW_MATRIX, mview->modelview_matrix);
1896 glGetDoublev(GL_PROJECTION_MATRIX, mview->projection_matrix);
1897 pp = mview->modelview_matrix;
1898 mtr[0] = pp[0]; mtr[1] = pp[1]; mtr[2] = pp[2];
1899 mtr[3] = pp[4]; mtr[4] = pp[5]; mtr[5] = pp[6];
1900 mtr[6] = pp[8]; mtr[7] = pp[9]; mtr[8] = pp[10];
1901 mtr[9] = pp[12]; mtr[10] = pp[13]; mtr[11] = pp[14];
1902 TransformInvert(mtr, mtr);
1903 mview->camera.x = mtr[9]; mview->camera.y = mtr[10]; mview->camera.z = mtr[11];
1904 mview->lookto.x = mtr[6]; mview->lookto.y = mtr[7]; mview->lookto.z = mtr[8];
1905 mview->up.x = mtr[3]; mview->up.y = mtr[4]; mview->up.z = mtr[5];
1908 glDisable(GL_LIGHTING);
1909 // glDisable (GL_DEPTH_TEST);
1910 // glEnable (GL_BLEND);
1911 // glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1913 glEnable (GL_TEXTURE_RECTANGLE_EXT);
1915 glMatrixMode (GL_PROJECTION);
1917 glMatrixMode (GL_MODELVIEW);
1919 glOrtho(0, width, 0, -height, 0.0,-1.0); /* non-flipped view */
1922 glDisable (GL_TEXTURE_RECTANGLE_EXT);
1924 // glDisable(GL_BLEND);
1925 // glEnable(GL_DEPTH_TEST);
1928 if (mview->draggingMode == kMainViewSelectingRegion) {
1929 /* Draw selection rectangle */
1930 glDisable(GL_LIGHTING);
1931 glDisable(GL_DEPTH_TEST);
1932 glMatrixMode(GL_MODELVIEW);
1934 glMatrixMode(GL_PROJECTION);
1936 glOrtho(0, width, 0, height, -1.0, 1.0);
1937 glColor3f(1.0, 1.0, 0.0);
1938 glBegin(GL_LINE_STRIP);
1939 glVertex2f(mview->dragStartPos[0], mview->dragStartPos[1]);
1940 glVertex2f(mview->dragStartPos[0], mview->dragEndPos[1]);
1941 glVertex2f(mview->dragEndPos[0], mview->dragEndPos[1]);
1942 glVertex2f(mview->dragEndPos[0], mview->dragStartPos[1]);
1943 glVertex2f(mview->dragStartPos[0], mview->dragStartPos[1]);
1945 glEnable(GL_DEPTH_TEST);
1953 #pragma mark ====== Labels ======
1956 MainView_attachLabelToAtom(MainView *mview, int index)
1961 /* const ElementPar *dp; */
1962 static float foreColor[] = {1, 1, 0, 1};
1963 static float backColor[] = {1, 1, 1, 0};
1964 ap = ATOM_AT_INDEX(mview->mol->atoms, index);
1965 if (ap->resSeq == 0)
1966 snprintf(buf, sizeof buf, "%-.4s", ap->aname);
1968 snprintf(buf, sizeof buf, "%d:%-.4s", ap->resSeq, ap->aname);
1969 for (p = buf; *p; p++) {
1975 /* dp = &(gBuiltinParameters->atomPars[ap->atomicNumber]);
1976 foreColor[0] = 1.0 - dp->r;
1977 foreColor[1] = 1.0 - dp->g;
1978 foreColor[2] = 1.0 - dp->b; */
1979 rec.label = MainViewCallback_newLabel(mview, buf, 14, foreColor, backColor);
1981 rec.idx2 = kInvalidIndex;
1982 rec.labelid = mview->nlabels + 1;
1983 ap->labelid = rec.labelid;
1984 AssignArray(&mview->labels, &mview->nlabels, sizeof(LabelRecord), mview->nlabels, &rec);
1988 MainView_detachLabelFromAtom(MainView *mview, int index)
1991 if (index >= 0 && index < mview->mol->natoms) {
1992 ap = ATOM_AT_INDEX(mview->mol->atoms, index);
1993 if (ap->labelid > 0 && ap->labelid <= mview->nlabels) {
1994 mview->labels[ap->labelid - 1].idx1 = kInvalidIndex;
2001 MainView_purgeUnusedLabels(MainView *mview)
2006 if (mview == NULL || mview->nlabels == 0 || mview->mol->natoms == 0)
2008 tempid = (Int *)calloc(sizeof(Int), mview->nlabels);
2012 /* Mark the labels in use */
2013 for (i = 0, ap = mview->mol->atoms; i < mview->mol->natoms; i++, ap = ATOM_NEXT(ap)) {
2014 if (ap->labelid > 0 && ap->labelid <= mview->nlabels)
2015 tempid[ap->labelid - 1] = 1;
2018 /* Release the labels not in use, and assign new IDs */
2020 for (i = 0; i < mview->nlabels; i++) {
2021 if (tempid[i] == 0) {
2022 MainViewCallback_releaseLabel(mview->labels[i].label);
2023 mview->labels[i].label = NULL;
2025 mview->labels[i].labelid = tempid[i] = n++;
2029 /* Purge the unused entries */
2030 for (i = mview->nlabels - 1; i >= 0; i--) {
2031 if (tempid[i] == 0) {
2032 memmove(mview->labels + i + 1, mview->labels + i, (mview->nlabels - 1 - i) * sizeof(LabelRecord));
2036 if (mview->nlabels == 0) {
2037 free(mview->labels);
2038 mview->labels = NULL;
2042 for (i = 0, ap = mview->mol->atoms; i < mview->mol->natoms; i++, ap = ATOM_NEXT(ap)) {
2043 if (ap->labelid > 0 && ap->labelid <= mview->nlabels) {
2044 ap->labelid = tempid[ap->labelid - 1];
2051 #pragma mark ====== Mode ======
2054 MainView_setMode(MainView *mview, int mode)
2061 MainView_getMode(const MainView *mview)
2068 #pragma mark ====== Mouse operations ======
2071 mousePosToTrackballPos(MainView *mview, const float *mousePos, float *pos)
2073 float frame[4], width, height, radius;
2074 NULL_CHECK(mview, "mousePosToTrackballPos");
2075 MainViewCallback_frame(mview, frame);
2076 width = frame[2] - frame[0];
2077 height = frame[3] - frame[1];
2078 radius = (width > height ? height * 0.5 : width * 0.5);
2079 pos[0] = (mousePos[0] - frame[0] - width * 0.5) / radius;
2080 pos[1] = (mousePos[1] - frame[1] - height * 0.5) / radius;
2084 showAtomsInInfoText(MainView *mview, int n1, int n2)
2088 MoleculeGetAtomName(mview->mol, n1, buf, sizeof buf - 1);
2090 int nn = strlen(buf);
2092 MoleculeGetAtomName(mview->mol, n2, buf + nn, sizeof buf - nn);
2094 MainViewCallback_drawInfoText(mview, buf);
2095 } else MainViewCallback_drawInfoText(mview, NULL);
2099 MainView_mouseDown(MainView *mview, const float *mousePos, int flags)
2102 float screenPos[3], objectPos[3];
2105 mview->dragStartPos[0] = mousePos[0];
2106 mview->dragStartPos[1] = mousePos[1];
2107 mview->dragStartPos[2] = 0.5;
2108 found = MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 0, 0);
2109 mview->clickedAtoms[0] = n1;
2110 mview->clickedAtoms[1] = n2;
2112 /* Estimate the screen z-coordinate of the mouse position */
2114 ap = ATOM_AT_INDEX(mview->mol->atoms, n1);
2116 /* if (mview->mol->is_xtal_coord)
2117 TransformVec(&r1, mview->mol->cell->tr, &r1); */
2118 objectPos[0] = r1.x;
2119 objectPos[1] = r1.y;
2120 objectPos[2] = r1.z;
2121 if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos)) {
2123 ap = ATOM_AT_INDEX(mview->mol->atoms, n2);
2125 /* if (mview->mol->is_xtal_coord)
2126 TransformVec(&r2, mview->mol->cell->tr, &r2); */
2127 objectPos[0] = r2.x;
2128 objectPos[1] = r2.y;
2129 objectPos[2] = r2.z;
2130 if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, p)) {
2132 r1.x = p[0] - screenPos[0];
2133 r1.y = p[1] - screenPos[1];
2134 r1.z = p[2] - screenPos[2];
2135 NormalizeVec(&r1, &r1);
2136 r2.x = mousePos[0] - screenPos[0];
2137 r2.y = mousePos[1] - screenPos[1];
2140 VecScale(r2, r1, w);
2141 screenPos[2] += r2.z;
2145 mview->dragStartPos[2] = screenPos[2];
2147 /* Set the 'depth' value of the molecule center (i.e. trackball->translate * dimension)
2148 as the z-coordinate of the drag start position */
2149 TrackballGetTranslate(mview->track, p);
2150 objectPos[0] = -mview->dimension * p[0];
2151 objectPos[1] = -mview->dimension * p[1];
2152 objectPos[2] = -mview->dimension * p[2];
2153 if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos))
2154 mview->dragStartPos[2] = screenPos[2];
2157 mview->isDragging = 0;
2159 switch (mview->mode) {
2160 case kTrackballRotateMode:
2161 case kTrackballTranslateMode:
2162 case kTrackballScaleMode:
2164 mousePosToTrackballPos(mview, mousePos, p);
2165 TrackballStartDragging(mview->track, p, mview->mode);
2166 mview->draggingMode = kMainViewMovingTrackball;
2169 /* No 'break' intentional; drop to next case */
2171 case kTrackballSelectionMode: {
2172 /* Select or move around */
2174 if (flags & kShiftKeyMask) {
2175 /* Shift-key pressed: toggle selection */
2176 MoleculeToggleSelectionOfAtom(mview->mol, n1);
2178 MoleculeToggleSelectionOfAtom(mview->mol, n2);
2181 if (!MoleculeIsAtomSelected(mview->mol, n1)) {
2182 /* If this atom is not selected, select this atom and unselect others */
2183 MoleculeSelectAtom(mview->mol, n1, 0);
2186 if (!MoleculeIsBondSelected(mview->mol, n1, n2)) {
2187 /* If this bond is not selected, select this bond and unselect others */
2188 MoleculeSelectAtom(mview->mol, n1, 0);
2189 MoleculeSelectAtom(mview->mol, n2, 1);
2193 if (MoleculeIsAtomSelected(mview->mol, n1))
2194 mview->draggingMode = kMainViewDraggingSelectedAtoms;
2195 else mview->draggingMode = 0;
2196 } else mview->draggingMode = kMainViewSelectingRegion;
2197 mview->dragEndPos[0] = mousePos[0];
2198 mview->dragEndPos[1] = mousePos[1];
2199 mview->dragEndPos[2] = mview->dragStartPos[2];
2200 mview->modifierFlags = flags;
2203 case kTrackballCreateMode: {
2204 if (md_is_running(mview->mol->arena)) {
2205 MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
2206 mview->draggingMode = 0;
2209 /* Draw a new bond */
2210 MoleculeSetSelection(mview->mol, NULL);
2211 if (found && n2 < 0) {
2212 /* An atom under mouse: create a new atom and a bond */
2213 ap = ATOM_AT_INDEX(mview->mol->atoms, n1);
2214 mview->tempAtoms[0] = n1;
2215 mview->tempAtomPos[0] = ap->r;
2217 /* No atom under mouse: create two new atoms and a bond */
2218 mview->tempAtoms[0] = -1;
2219 screenPos[0] = mousePos[0];
2220 screenPos[1] = mousePos[1];
2221 screenPos[2] = mview->dragStartPos[2];
2222 if (MainView_convertScreenPositionToObjectPosition(mview, screenPos, objectPos) == 0) {
2223 mview->tempAtoms[0] = -1; /* Cannot create */
2224 mview->draggingMode = 0;
2226 mview->tempAtomPos[0].x = objectPos[0];
2227 mview->tempAtomPos[0].y = objectPos[1];
2228 mview->tempAtomPos[0].z = objectPos[2];
2229 /* if (mview->mol->is_xtal_coord)
2230 TransformVec(&mview->tempAtomPos[0], mview->mol->cell->rtr, &mview->tempAtomPos[0]); */
2233 mview->tempAtoms[1] = -1;
2234 mview->tempAtomPos[1] = mview->tempAtomPos[0];
2235 mview->draggingMode = kMainViewCreatingBond;
2239 mview->draggingMode = 0;
2243 showAtomsInInfoText(mview, n1, n2);
2245 showAtomsInInfoText(mview, -1, -1);
2246 /* MainViewCallback_setNeedsDisplay(mview, 1); */
2250 MainView_mouseDragged(MainView *mview, const float *mousePos, int flags)
2253 if (mview->isDragging == 0) {
2254 if (abs(mousePos[0] - mview->dragStartPos[0]) >= 3 || abs(mousePos[1] - mview->dragStartPos[1]) >= 3)
2255 mview->isDragging = 1;
2258 mousePosToTrackballPos(mview, mousePos, p);
2259 switch (mview->draggingMode) {
2260 case kMainViewMovingTrackball:
2261 TrackballDrag(mview->track, p);
2263 case kMainViewSelectingRegion:
2264 case kMainViewDraggingSelectedAtoms:
2265 mview->dragEndPos[0] = mousePos[0];
2266 mview->dragEndPos[1] = mousePos[1];
2267 mview->dragEndPos[2] = mview->dragStartPos[2];
2268 mview->modifierFlags = flags;
2269 MainViewCallback_display(mview);
2271 case kMainViewCreatingBond: {
2273 if (MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 0, 0) && n2 < 0) {
2274 /* An atom under mouse */
2275 Atom *ap = ATOM_AT_INDEX(mview->mol->atoms, n1);
2276 /* printf("n1=%d, n2=%d\n", n1, n2); */
2277 mview->tempAtoms[1] = n1;
2278 mview->tempAtomPos[1] = ap->r;
2280 float screenPos[3], objectPos[3];
2282 mview->tempAtoms[1] = -1;
2283 /* Convert the position of temporary atom 0 to screen position */
2284 r1 = mview->tempAtomPos[0];
2285 /* if (mview->mol->is_xtal_coord)
2286 TransformVec(&r1, mview->mol->cell->tr, &r1); */
2287 objectPos[0] = r1.x;
2288 objectPos[1] = r1.y;
2289 objectPos[2] = r1.z;
2290 if (MainView_convertObjectPositionToScreenPosition(mview, objectPos, screenPos) == 0)
2291 break; /* Do nothing */
2292 /* Convert the mouse position to object position, while using the same Z depth calculated from the temporary atom 0 position */
2293 screenPos[0] = mousePos[0];
2294 screenPos[1] = mousePos[1];
2295 if (MainView_convertScreenPositionToObjectPosition(mview, screenPos, objectPos) == 0)
2296 break; /* Do nothing */
2297 r1.x = objectPos[0];
2298 r1.y = objectPos[1];
2299 r1.z = objectPos[2];
2300 /* if (mview->mol->is_xtal_coord)
2301 TransformVec(&r1, mview->mol->cell->rtr, &r1); */
2302 mview->tempAtomPos[1] = r1;
2304 if (mview->tempAtoms[0] < 0)
2305 showAtomsInInfoText(mview, mview->tempAtoms[1], -1);
2307 showAtomsInInfoText(mview, mview->tempAtoms[0], mview->tempAtoms[1]);
2308 MainViewCallback_display(mview);
2314 MainViewCallback_display(mview);
2318 MainView_mouseMoved(MainView *mview, const float *mousePos, int flags)
2321 if (mview->isDragging)
2323 found = MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 1, 0);
2325 showAtomsInInfoText(mview, n1, n2);
2327 showAtomsInInfoText(mview, -1, -1);
2331 sCreateNewAtom(Molecule *mol, Vector pos)
2336 memset(&a, 0, sizeof(Atom));
2338 for (i = 0; i < 1000; i++) {
2339 snprintf(name, sizeof name, "C%03d", i);
2340 for (j = 0; j < mol->natoms; j++) {
2341 if (strncmp(ATOM_AT_INDEX(mol->atoms, j)->aname, name, 4) == 0)
2344 if (j == mol->natoms)
2347 strncpy(a.aname, name, 4);
2349 strcpy(a.element, "C");
2350 a.type = AtomTypeEncodeToUInt("c3");
2351 a.weight = WeightForAtomicNumber(a.atomicNumber);
2353 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, -1, &i) == 0)
2354 return mol->natoms - 1;
2356 /* return MoleculeAddAtom(mol, &a); */
2360 MainView_mouseUp(MainView *mview, const float *mousePos, int flags, int clickCount)
2365 mousePosToTrackballPos(mview, mousePos, p);
2367 if (clickCount == 2 && mview->isDragging == 0) {
2368 /* Create a new molecular fragment */
2370 found = MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 0, 0);
2372 if (MyAppCallback_getTextWithPrompt("Enter formula (e.g. CH2OCH3)", buf, sizeof buf) == 0)
2374 MolActionCreateAndPerform(mview->mol, SCRIPT_ACTION("si"), "cmd_fuse_or_dock", buf, n1);
2378 if (mview->mode == kTrackballEraseMode) {
2380 found = MainView_findObjectAtPoint(mview, mousePos, &n1, &n2, 0, 0);
2381 if (found && n1 == mview->clickedAtoms[0] && n2 == mview->clickedAtoms[1]) {
2383 /* IntGroup *sel, *newsel;
2384 sel = MoleculeGetSelection(mview->mol);
2386 newsel = MoleculeModifySelectionByRemovingAtoms(mview->mol, sel, IntGroupNewWithPoints(n1, 1, -1));
2387 if (!IntGroupIsEqual(sel, newsel))
2388 MolActionCreateAndPerform(mview->mol, gMolActionSetSelection, newsel);
2390 IntGroupRelease(newsel);
2392 MolActionCreateAndPerform(mview->mol, gMolActionDeleteAnAtom, (Int)n1);
2395 Int bn = MoleculeLookupBond(mview->mol, n1, n2);
2397 IntGroup *ig = IntGroupNewWithPoints(bn, 1, -1);
2398 MolActionCreateAndPerform(mview->mol, gMolActionDeleteBonds, ig);
2399 IntGroupRelease(ig);
2401 fprintf(stderr, "Internal error %s:%d: bond to delete is not found", __FILE__, __LINE__);
2408 switch (mview->draggingMode) {
2409 case kMainViewMovingTrackball:
2410 TrackballEndDragging(mview->track, p);
2412 case kMainViewDraggingSelectedAtoms: {
2414 if (mview->isDragging) {
2415 IntGroup *dupSelection = IntGroupNewFromIntGroup(mview->mol->selection);
2416 calcDragOffset(mview, &offset);
2417 /* if (mview->mol->is_xtal_coord)
2418 TransformVec(&offset, mview->mol->cell->rtr, &offset); */
2419 MolActionCreateAndPerform(mview->mol, gMolActionTranslateAtoms, &offset, dupSelection);
2420 IntGroupRelease(dupSelection);
2421 /* } else if (clickCount == 2) {
2423 if (MyAppCallback_getTextWithPrompt("Enter formula (e.g. CH2OCH3)", buf, sizeof buf) == 0)
2425 MolActionCreateAndPerform(mview->mol, SCRIPT_ACTION("s"), "dock_formula", buf);
2430 case kMainViewSelectingRegion: {
2431 char *selectFlags = temporarySelection(mview, mview->modifierFlags, (mview->isDragging ? 0 : 1), 0);
2432 if (selectFlags != NULL) {
2433 IntGroup *ig = IntGroupNew();
2436 natoms = mview->mol->natoms;
2437 for (i = 0; i < natoms; i++) {
2439 IntGroupAdd(ig, i, 1);
2441 MoleculeSetSelection(mview->mol, ig);
2442 /* printf("current selection = {");
2443 for (i = j = 0; i < natoms; i++) {
2444 if (selectFlags[i]) {
2445 printf("%s%.4s", (j == 0 ? "" : " "), mview->mol->atoms[i].name);
2450 IntGroupRelease(ig);
2456 case kMainViewCreatingBond: {
2459 if (mview->tempAtoms[0] >= 0 && mview->tempAtoms[1] >= 0) {
2460 n1 = mview->tempAtoms[0];
2461 n2 = mview->tempAtoms[1];
2463 if (mview->tempAtoms[0] < 0) {
2464 /* Create the first atom */
2465 n1 = sCreateNewAtom(mview->mol, mview->tempAtomPos[0]);
2466 } else n1 = mview->tempAtoms[0];
2467 if (mview->tempAtoms[1] < 0) {
2468 /* Create the second atom, if not too close */
2469 Vector dr = ATOM_AT_INDEX(mview->mol->atoms, n1)->r;
2470 VecDec(dr, mview->tempAtomPos[1]);
2471 /* if (mview->mol->is_xtal_coord)
2472 TransformVec(&dr, mview->mol->cell->tr, &dr); */
2473 if (VecLength2(dr) > 0.01)
2474 n2 = sCreateNewAtom(mview->mol, mview->tempAtomPos[1]);
2476 } else n2 = mview->tempAtoms[1];
2478 if (n1 >= 0 && n2 >= 0 && n1 != n2) {
2481 b[2] = kInvalidIndex;
2482 MolActionCreateAndPerform(mview->mol, gMolActionAddBonds, 2, b, NULL);
2483 /* MoleculeAddBonds(mview->mol, b, NULL); */
2489 mview->draggingMode = 0;
2490 mview->isDragging = 0;
2491 MainViewCallback_setNeedsDisplay(mview, 1);
2492 MainViewCallback_setKeyboardFocus(mview);
2496 MainView_rotateBySlider(MainView *mview, float angle, int mode, int mouseStatus, int modifierFlags)
2500 if (mouseStatus == 1) { /* mouseDown */
2502 if (mode == kSliderRotateBondMode) {
2503 if (MoleculeIsFragmentRotatable(mview->mol, mview->mol->selection, &n1, &n2, &(mview->rotateFragment))) {
2504 mview->rotateCenter = ATOM_AT_INDEX(mview->mol->atoms, n1)->r;
2505 mview->rotateAxis = ATOM_AT_INDEX(mview->mol->atoms, n2)->r;
2506 VecDec(mview->rotateAxis, mview->rotateCenter);
2507 if (VecLength2(mview->rotateAxis) < 1e-10)
2509 NormalizeVec(&(mview->rotateAxis), &(mview->rotateAxis));
2510 if (modifierFlags & kAltKeyMask) {
2511 /* Option key: reverse the fragment */
2512 IntGroupRelease(mview->rotateFragment);
2513 mview->rotateFragment = MoleculeFragmentExcludingAtoms(mview->mol, n2, 1, &n1);
2514 VecScaleSelf(mview->rotateAxis, -1);
2517 } else if ((modifierFlags & kAltKeyMask) != 0) {
2518 /* Rotate selection */
2520 if (mview->mol->selection == NULL)
2522 mview->rotateFragment = IntGroupNewFromIntGroup(mview->mol->selection);
2523 TrackballGetTranslate(mview->track, tr);
2524 mview->rotateCenter.x = -mview->dimension * tr[0];
2525 mview->rotateCenter.y = -mview->dimension * tr[1];
2526 mview->rotateCenter.z = -mview->dimension * tr[2];
2527 if (mode == kSliderRotateXMode) {
2528 VecCross(mview->rotateAxis, mview->up, mview->lookto);
2530 mview->rotateAxis = mview->up;
2531 VecScaleSelf(mview->rotateAxis, -1);
2534 /* Rotate along x/y axis (no coordinate transform) */
2535 TrackballStartDragging(mview->track, NULL, kTrackballRotateMode);
2536 mview->rotateFragment = NULL; /* This is probably not necessary */
2538 if (mview->rotateFragment != NULL) {
2539 /* Save the original position */
2540 n1 = IntGroupGetCount(mview->rotateFragment);
2541 mview->rotateFragmentOldPos = (Vector *)calloc(sizeof(Vector), n1);
2542 if (mview->rotateFragmentOldPos == NULL) {
2543 IntGroupRelease(mview->rotateFragment);
2544 mview->rotateFragment = NULL;
2547 for (i = 0; i < n1; i++) {
2548 n2 = IntGroupGetNthPoint(mview->rotateFragment, i);
2549 mview->rotateFragmentOldPos[i] = (ATOM_AT_INDEX(mview->mol->atoms, n2))->r;
2553 } else { /* mouseDragged or mouseUp */
2555 if (mview->rotateFragment != NULL) {
2557 /* Restore original positions */
2558 n1 = IntGroupGetCount(mview->rotateFragment);
2559 for (i = 0; i < n1; i++) {
2560 n2 = IntGroupGetNthPoint(mview->rotateFragment, i);
2561 (ATOM_AT_INDEX(mview->mol->atoms, n2))->r = mview->rotateFragmentOldPos[i];
2564 if (mouseStatus == 2) { /* dragged */
2565 MoleculeRotate(mview->mol, &(mview->rotateAxis), angle, &(mview->rotateCenter), mview->rotateFragment);
2566 } else { /* mouse up */
2567 IntGroup *ig = IntGroupNewFromIntGroup(mview->rotateFragment);
2568 MolActionCreateAndPerform(mview->mol, gMolActionRotateAtoms, &(mview->rotateAxis), (double)angle, &(mview->rotateCenter), ig);
2569 IntGroupRelease(ig);
2570 IntGroupRelease(mview->rotateFragment);
2571 mview->rotateFragment = NULL;
2572 free(mview->rotateFragmentOldPos);
2573 mview->rotateFragmentOldPos = NULL;
2578 float cs = cos(angle / 2);
2579 float sn = sin(angle / 2);
2580 if (mouseStatus == 0) { /* mouseUp */
2581 TrackballEndDragging(mview->track, NULL);
2582 } else { /* mouseDragged */
2584 if (mode == kSliderRotateXMode) {
2585 /* Rotation along x axis */
2587 quat[2] = quat[3] = 0;
2590 quat[1] = quat[3] = 0;
2592 TrackballSetTemporaryRotation(mview->track, quat);
2597 MainViewCallback_setNeedsDisplay(mview, 1);
2598 MainViewCallback_setKeyboardFocus(mview);
2601 #pragma mark ====== Menu Commands ======
2604 MainView_selectAll(MainView *mview)
2607 if (mview == NULL || mview->mol == NULL)
2612 IntGroupAdd(ig, 0, mview->mol->natoms);
2613 MoleculeSetSelection(mview->mol, ig);
2614 IntGroupRelease(ig);
2615 MainViewCallback_setNeedsDisplay(mview, 1);
2619 MainView_selectFragment(MainView *mview)
2622 if (mview == NULL || mview->mol == NULL)
2624 ig = MoleculeFragmentWithAtomGroups(mview->mol, MoleculeGetSelection(mview->mol), NULL);
2627 MoleculeSetSelection(mview->mol, ig);
2628 IntGroupRelease(ig);
2629 MainViewCallback_setNeedsDisplay(mview, 1);
2633 MainView_selectReverse(MainView *mview)
2636 if (mview == NULL || mview->mol == NULL)
2638 ig = IntGroupNewFromIntGroup(MoleculeGetSelection(mview->mol));
2639 IntGroupReverse(ig, 0, mview->mol->natoms);
2640 MoleculeSetSelection(mview->mol, ig);
2641 IntGroupRelease(ig);
2642 MainViewCallback_setNeedsDisplay(mview, 1);
2646 MainView_centerSelection(MainView *mview)
2651 if (mview == NULL || mview->mol == NULL)
2653 ig = MoleculeGetSelection(mview->mol);
2654 MoleculeCenterOfMass(mview->mol, &c, ig);
2655 tr[0] = -c.x / mview->dimension;
2656 tr[1] = -c.y / mview->dimension;
2657 tr[2] = -c.z / mview->dimension;
2658 TrackballSetTranslate(mview->track, tr);
2659 MainViewCallback_setNeedsDisplay(mview, 1);
2662 #pragma mark ====== Pasteboard Support ======
2665 MainView_copy(MainView *mview)
2667 Molecule *mol, *mol2;
2669 Int len, result, time;
2671 if (mview == NULL || (mol = mview->mol) == NULL || (sel = MoleculeGetSelection(mol)) == NULL)
2673 if (MoleculeExtract(mol, &mol2, MoleculeGetSelection(mol), 1) != 0
2675 || (p = MoleculeSerialize(mol2, &len, &time)) == NULL)
2677 result = MoleculeCallback_writeToPasteboard(gMoleculePasteboardType, p, len);
2681 MoleculeRelease(mol2);
2682 mview->pasteTimeStamp = time;
2683 mview->pasteCount = 0;
2688 MainView_delete(MainView *mview)
2691 Molecule *mol = mview->mol;
2692 if (md_is_running(mview->mol->arena)) {
2693 MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
2696 result = MolActionCreateAndPerform(mol, gMolActionUnmergeMolecule, MoleculeGetSelection(mol));
2698 result = MolActionCreateAndPerform(mol, gMolActionSetSelection, NULL);
2703 MainView_cut(MainView *mview)
2706 if (md_is_running(mview->mol->arena)) {
2707 MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
2710 result = MainView_copy(mview);
2712 result = MainView_delete(mview);
2717 MainView_isPastable(MainView *mview)
2719 if (MoleculeCallback_isDataInPasteboard(gMoleculePasteboardType))
2725 MainView_paste(MainView *mview)
2728 Int len, result, time;
2732 if (md_is_running(mview->mol->arena)) {
2733 MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
2736 if (!MainView_isPastable(mview))
2738 if (MoleculeCallback_readFromPasteboard(gMoleculePasteboardType, &p, &len) != 0)
2740 if ((mol2 = MoleculeDeserialize(p, len, &time)) == NULL) {
2746 if (time == mview->pasteTimeStamp) {
2747 /* Offset the pasted fragment by something */
2749 mview->pasteCount++;
2750 v.x = v.y = v.z = mview->pasteCount * 0.5 + sin((double)mview->pasteCount) * 0.1; /* sin(..) is to avoid accidental coincidence */
2751 MoleculeTranslate(mol2, &v, NULL);
2753 mview->pasteTimeStamp = time;
2754 mview->pasteCount = 0;
2757 sel = MoleculeGetSelection(mview->mol);
2758 if (sel == NULL || IntGroupGetCount(sel) == 0) {
2759 /* Remove dummy atoms from mol2 */
2762 sel = IntGroupNew();
2763 for (i = 0, ap = mol2->atoms; i < mol2->natoms; i++, ap = ATOM_NEXT(ap)) {
2764 if (ap->atomicNumber == 0 && ap->aname[0] == '_') {
2766 IntGroupAdd(sel, i, 1);
2769 if (IntGroupGetCount(sel) > 0)
2770 MoleculeUnmerge(mol2, NULL, sel, 0, NULL, NULL, 0);
2771 IntGroupRelease(sel);
2774 result = MolActionCreateAndPerform(mview->mol, SCRIPT_ACTION("M"), "dock_fragment", mol2);
2775 MoleculeRelease(mol2);
2780 sMainView_AppendParameterToSerialBuffer(void **pb_ptr, Int *pb_len, Int parType, Int count, const UnionPar *up)
2782 int len = sizeof(Int) * 2 + sizeof(UnionPar) * count;
2785 *pb_ptr = malloc(len + 1);
2786 if (*pb_ptr == NULL)
2788 p = (char *)(*pb_ptr);
2791 *pb_ptr = realloc(*pb_ptr, *pb_len + len);
2792 if (*pb_ptr == NULL)
2794 p = (char *)(*pb_ptr) + *pb_len - 1; /* *pb_len includes the end mark */
2800 *((Int *)p) = count;
2802 memmove(p, up, sizeof(UnionPar) * count);
2803 p += sizeof(UnionPar) * count;
2804 *p = 0; /* End mark */
2809 MainView_pasteParameters(MainView *mview)
2814 if (mview == NULL || mview->mol == NULL)
2816 if (mview->mol->par == NULL)
2817 mview->mol->par = ParameterNew();
2818 if (!MoleculeCallback_isDataInPasteboard(gParameterPasteboardType))
2820 if (MoleculeCallback_readFromPasteboard(gParameterPasteboardType, (void **)&p, &len) != 0)
2823 newsel = IntGroupNew();
2829 if (parType < kFirstParType || parType > kLastParType)
2832 count = *((Int *)p);
2834 up = (UnionPar *)calloc(sizeof(UnionPar), count);
2835 memmove(up, p, sizeof(UnionPar) * count);
2837 /* The global parameters become local when pasted */
2838 for (i = 0; i < count; i++) {
2839 if (up[i].bond.src > 0)
2843 c = ParameterGetCountForType(mview->mol->par, parType);
2844 ig = IntGroupNewWithPoints(c, count, -1);
2845 MolActionCreateAndPerform(mview->mol, gMolActionAddParameters, parType, ig, count, up);
2847 IntGroupRelease(ig);
2848 p += sizeof(UnionPar) * count;
2849 c = ParameterTableGetRowFromTypeAndIndex(mview->mol->par, parType, c);
2850 IntGroupAdd(newsel, c, count);
2854 /* Select newly pasted parameters */
2855 MainViewCallback_setTableSelection(mview, newsel);
2856 IntGroupRelease(newsel);
2858 /* Request MD rebuild */
2859 mview->mol->needsMDRebuild = 1;
2861 /* Suppress clear of parameter table selection */
2862 mview->mol->parameterTableSelectionNeedsClear = 0;
2864 MoleculeCallback_notifyModification(mview->mol, 0);
2868 /* flags: 1, delete; 2, copy; 3, cut */
2870 MainView_copyOrCutParameters(MainView *mview, int flags)
2872 IntGroup *ig = MainViewCallback_getTableSelection(mview);
2874 int i, n, idx, type = -1, t1;
2875 Parameter *par = mview->mol->par;
2876 void *pb_ptr = NULL;
2878 if (ig == NULL || (i = IntGroupGetCount(ig)) == 0)
2883 n = IntGroupGetNthPoint(ig, i);
2885 idx = ParameterTableGetItemIndex(par, n, &t1);
2891 /* Process Parameters for the last group */
2892 if (type >= kFirstParType && ig2 != NULL && (n = IntGroupGetCount(ig2)) > 0) {
2893 UnionPar *up = (UnionPar *)calloc(sizeof(UnionPar), n);
2896 if (ParameterDelete(par, type, up, ig2) < 0)
2898 act = MolActionNew(gMolActionAddParameters, type, ig2, n, up);
2899 MolActionCallback_registerUndo(mview->mol, act);
2900 MolActionRelease(act);
2902 if (ParameterCopy(par, type, up, ig2) < 0)
2906 if (sMainView_AppendParameterToSerialBuffer(&pb_ptr, &pb_len, type, n, up) != 0)
2910 IntGroupRelease(ig2);
2919 ig2 = IntGroupNew();
2920 IntGroupAdd(ig2, idx, 1);
2925 IntGroupRelease(ig2);
2926 IntGroupRelease(ig);
2929 n = MoleculeCallback_writeToPasteboard(gParameterPasteboardType, pb_ptr, pb_len);
2934 /* Clear selection */
2935 MainViewCallback_setTableSelection(mview, NULL);
2936 /* Request MD rebuild */
2937 mview->mol->needsMDRebuild = 1;
2939 MoleculeCallback_notifyModification(mview->mol, 0);
2943 #pragma mark ====== Table View (also for gBuiltinParameters) ======
2945 /* As a special case, the table view functions will handle gBuiltinParameters when mview == NULL. */
2947 typedef struct ColumnInfoRecord {
2953 static ColumnInfoRecord sAtomColumns[] = {
2954 {"atom", 4, 0}, {"name", 4, 1}, {"type", 4, 1}, {"element", 4, 1}, {"residue", 6, 1},
2955 {"x", 6, 1}, {"y", 6, 1}, {"z", 6, 1}, {"charge", 6, 1}, {NULL}
2957 static ColumnInfoRecord sBondColumns[] = {
2958 {"atoms", 9, 0}, {"names", 9, 0}, {"type", 9, 0}, {"length", 8, 0}, {"par type", 8, 0}, {"k", 8, 0}, {"r0", 8, 0}, {NULL}
2960 static ColumnInfoRecord sAngleColumns[] = {
2961 {"atoms", 12, 0}, {"names", 12, 0}, {"type", 12, 0}, {"angle", 8, 0}, {"par type", 8, 0}, {"k", 8, 0}, {"a0", 8, 0}, {NULL}
2963 static ColumnInfoRecord sDihedralColumns[] = {
2964 {"atoms", 15, 0}, {"names", 15, 0}, {"type", 15, 0}, {"dihedral", 8, 0}, {"par type", 8, 0}, {"k", 8, 0}, {"period", 4, 0}, {"phi0", 8, 0}, {NULL}
2966 static ColumnInfoRecord sImproperColumns[] = {
2967 {"atoms", 15, 0}, {"names", 15, 0}, {"type", 15, 0}, {"improper", 8, 0}, {"par type", 8, 0}, {"k", 8, 0}, {"period", 4, 0}, {"phi0", 8, 0}, {NULL}
2969 static ColumnInfoRecord sParameterColumns[] = {
2970 {"class", 5, 0}, {"type", 9, 0}, {"", 6, 0}, {"", 6, 0}, {"", 6, 0}, {"", 6, 0}, {"", 6, 0}, {"", 6, 0}, {"src", 8, 0}, {"comment", 25, 0}, {NULL}
2972 static ColumnInfoRecord sUnitCellColumns[] = {
2973 {"name", 6, 0}, {"values", 9, 1}, {"", 9, 1}, {"", 9, 1}, {NULL}
2975 static ColumnInfoRecord sMOEnergyColumns[] = {
2976 {"MO", 5, 0}, {"alpha energy", 12, 0}, {"beta energy", 12, 0}, {NULL}
2978 static ColumnInfoRecord *sColumnInfo[] = {
2979 sAtomColumns, sBondColumns, sAngleColumns, sDihedralColumns, sImproperColumns, sParameterColumns, sUnitCellColumns, sMOEnergyColumns
2981 static char *sTableTitles[] = {
2982 "atom", "bond", "angle", "dihedral", "improper", "parameter", "unit cell", "MO energy"
2986 MainView_tableTitleForIndex(MainView *mview, int idx, char *buf, int bufsize)
2989 idx = kMainViewParameterTableIndex;
2990 if (idx < 0 || idx >= sizeof(sColumnInfo) / sizeof(sColumnInfo[0])) {
2994 snprintf(buf, bufsize, "%s", sTableTitles[idx]);
2998 MainView_createColumnsForTableAtIndex(MainView *mview, int idx)
3003 idx = kMainViewParameterTableIndex;
3004 if (idx < 0 || idx >= sizeof(sColumnInfo) / sizeof(sColumnInfo[0]))
3007 /* Remove all existing columns */
3008 while (MainViewCallback_removeTableColumnAtIndex(mview, 0) > 0);
3011 mview->tableIndex = idx;
3013 /* Create columns */
3014 for (i = 0; ; i++) {
3016 ColumnInfoRecord *recp = &(sColumnInfo[idx][i]);
3017 if (recp->name == NULL)
3019 width = recp->width;
3020 if (mview == 0 && strcmp(recp->name, "comment") == 0)
3022 MainViewCallback_addTableColumn(mview, recp->name, width, recp->editable);
3027 MainView_refreshTable(MainView *mview)
3030 if (mview != NULL && mview->mol != NULL) {
3031 MainView_refreshCachedInfo(mview);
3032 if (mview->tableIndex == kMainViewBondTableIndex ||
3033 mview->tableIndex == kMainViewAngleTableIndex ||
3034 mview->tableIndex == kMainViewDihedralTableIndex ||
3035 mview->tableIndex == kMainViewImproperTableIndex ||
3036 mview->tableIndex == kMainViewParameterTableIndex) {
3037 /* Check the parameter table */
3038 if (mview->mol->arena == NULL)
3039 md_arena_new(mview->mol);
3040 if (mview->mol->needsMDRebuild || mview->mol->arena->par == NULL) {
3041 /* Here we do not call MoleculePrepareMDArena(), because we do not want
3042 to modify Molecule by just redrawing tables.
3043 (But MoleculePrepareMDArena() *is* called when the parameter
3044 table is selected. */
3045 md_prepare(mview->mol->arena, 1);
3050 MainViewCallback_reloadTableData(mview);
3052 if (mview != NULL && mview->mol != NULL && mview->tableIndex >= kMainViewAtomTableIndex && mview->tableIndex <= kMainViewImproperTableIndex)
3053 MainViewCallback_setTableSelection(mview, mview->tableSelection);
3057 MainView_numberOfRowsInTable(MainView *mview)
3060 return ParameterTableNumberOfRows(gBuiltinParameters);
3061 if (mview->mol == NULL)
3063 if (mview->tableIndex == kMainViewParameterTableIndex)
3064 return ParameterTableNumberOfRows(mview->mol->par);
3065 if (mview->tableIndex == kMainViewUnitCellTableIndex)
3066 return 13; /* a, b, c, alpha, beta, gamma, a_valid, b_valid, c_valid, av, bv, cv, ov */
3067 if (mview->tableCache == NULL)
3068 MainView_refreshCachedInfo(mview);
3069 return IntGroupGetCount(mview->tableCache);
3073 MainView_indexToTableRow(MainView *mview, int idx)
3077 if (mview->tableIndex == kMainViewParameterTableIndex || mview->tableIndex == kMainViewUnitCellTableIndex)
3078 return -1; /* Not supported yet */
3079 return IntGroupLookupPoint(mview->tableCache, idx);
3083 MainView_tableRowToIndex(MainView *mview, int row)
3087 if (mview->tableIndex == kMainViewParameterTableIndex || mview->tableIndex == kMainViewUnitCellTableIndex)
3088 return -1; /* Not supported yet */
3089 return IntGroupGetNthPoint(mview->tableCache, row);
3093 sAtomDescription(Atom *ap, char *buf, int bufsize)
3095 snprintf(buf, bufsize, "%d:%.4s", ap->resSeq, ap->aname);
3100 sParameterOfTypeAtIndex(Molecule *mol, int parType, int idx)
3103 if (mol->arena == NULL || mol->needsMDRebuild || mol->arena->par == NULL)
3107 i = mol->arena->vdw_par_i[idx];
3108 if (i >= 0 && i < mol->arena->par->nvdwPars)
3109 return (UnionPar *)(mol->arena->par->vdwPars + i);
3112 i = mol->arena->bond_par_i[idx];
3113 if (i >= 0 && i < mol->arena->par->nbondPars)
3114 return (UnionPar *)(mol->arena->par->bondPars + i);
3117 i = mol->arena->angle_par_i[idx];
3118 if (i >= 0 && i < mol->arena->par->nanglePars)
3119 return (UnionPar *)(mol->arena->par->anglePars + i);
3121 case kDihedralParType:
3122 i = mol->arena->dihedral_par_i[idx];
3123 if (i >= 0 && i < mol->arena->par->ndihedralPars)
3124 return (UnionPar *)(mol->arena->par->dihedralPars + i);
3126 case kImproperParType:
3127 i = mol->arena->improper_par_i[idx];
3128 if (i >= 0 && i < mol->arena->par->nimproperPars)
3129 return (UnionPar *)(mol->arena->par->improperPars + i);
3136 MainView_valueForTable(MainView *mview, int column, int row, char *buf, int bufsize)
3141 char descbuf[4][20], typebuf[4][8];
3145 return ParameterTableGetItemText(gBuiltinParameters, column, row, buf, bufsize);
3147 if (mview == NULL || (mol = mview->mol) == NULL) {
3152 if (mview->tableIndex == kMainViewParameterTableIndex)
3153 return ParameterTableGetItemText(mview->mol->par, column, row, buf, bufsize);
3155 if (mview->tableIndex == kMainViewAtomTableIndex) { /* Atoms */
3156 idx = IntGroupGetNthPoint(mview->tableCache, row);
3157 ap[0] = ATOM_AT_INDEX(mol->atoms, idx);
3159 case 0: snprintf(buf, bufsize, "%d", idx); break;
3160 case 1: snprintf(buf, bufsize, "%.4s", ap[0]->aname); break;
3161 case 2: snprintf(buf, bufsize, "%.6s", AtomTypeDecodeToString(ap[0]->type, NULL)); break;
3162 case 3: snprintf(buf, bufsize, "%.2s", ap[0]->element); break;
3164 if (ap[0]->resSeq == 0)
3167 snprintf(buf, bufsize, "%.4s.%d", ap[0]->resName, ap[0]->resSeq);
3169 case 5: snprintf(buf, bufsize, "%.3f", ap[0]->r.x); break;
3170 case 6: snprintf(buf, bufsize, "%.3f", ap[0]->r.y); break;
3171 case 7: snprintf(buf, bufsize, "%.3f", ap[0]->r.z); break;
3172 case 8: snprintf(buf, bufsize, "%.3f", ap[0]->charge); break;
3173 default: buf[0] = 0; break;
3175 } else if (mview->tableIndex == kMainViewBondTableIndex) { /* Bonds */
3176 idx = IntGroupGetNthPoint(mview->tableCache, row);
3177 ip = mol->bonds + idx * 2;
3178 ap[0] = ATOM_AT_INDEX(mol->atoms, ip[0]);
3179 ap[1] = ATOM_AT_INDEX(mol->atoms, ip[1]);
3181 case 0: snprintf(buf, bufsize, "%d-%d", ip[0], ip[1]); break;
3182 case 1: snprintf(buf, bufsize, "%s-%s", sAtomDescription(ap[0], descbuf[0], 20), sAtomDescription(ap[1], descbuf[1], 20)); break;
3183 case 2: snprintf(buf, bufsize, "%.6s-%.6s", AtomTypeDecodeToString(ap[0]->type, typebuf[0]), AtomTypeDecodeToString(ap[1]->type, typebuf[1])); break;
3185 snprintf(buf, bufsize, "%.3f", MoleculeMeasureBond(mview->mol, &(ap[0]->r), &(ap[1]->r)));
3190 BondPar *bp = (BondPar *)sParameterOfTypeAtIndex(mol, kBondParType, idx);
3195 snprintf(buf, bufsize, "%.6s-%.6s", AtomTypeDecodeToString(bp->type1, typebuf[0]), AtomTypeDecodeToString(bp->type2, typebuf[1]));
3197 snprintf(buf, bufsize, "%.3f", (column == 5 ? bp->k * INTERNAL2KCAL : bp->r0));
3201 default: buf[0] = 0; break;
3203 } else if (mview->tableIndex == kMainViewAngleTableIndex) { /* Angles */
3204 idx = IntGroupGetNthPoint(mview->tableCache, row);
3205 ip = mol->angles + idx * 3;
3206 ap[0] = ATOM_AT_INDEX(mol->atoms, ip[0]);
3207 ap[1] = ATOM_AT_INDEX(mol->atoms, ip[1]);
3208 ap[2] = ATOM_AT_INDEX(mol->atoms, ip[2]);
3210 case 0: snprintf(buf, bufsize, "%d-%d-%d", ip[0], ip[1], ip[2]); break;
3211 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;
3212 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;
3214 snprintf(buf, bufsize, "%.3f", MoleculeMeasureAngle(mview->mol, &(ap[0]->r), &(ap[1]->r), &(ap[2]->r)));
3219 AnglePar *anp = (AnglePar *)sParameterOfTypeAtIndex(mol, kAngleParType, idx);
3224 snprintf(buf, bufsize, "%.6s-%.6s-%.6s", AtomTypeDecodeToString(anp->type1, typebuf[0]), AtomTypeDecodeToString(anp->type2, typebuf[1]), AtomTypeDecodeToString(anp->type3, typebuf[2]));
3226 snprintf(buf, bufsize, "%.3f", (column == 5 ? anp->k * INTERNAL2KCAL : anp->a0 * kRad2Deg));
3230 default: buf[0] = 0; break;
3232 } else if (mview->tableIndex == kMainViewDihedralTableIndex || mview->tableIndex == kMainViewImproperTableIndex) { /* Dihedrals, Impropers */
3233 int f = (mview->tableIndex == kMainViewDihedralTableIndex);
3234 idx = IntGroupGetNthPoint(mview->tableCache, row);
3235 ip = (f ? mview->mol->dihedrals : mview->mol->impropers) + idx * 4;
3236 ap[0] = ATOM_AT_INDEX(mview->mol->atoms, ip[0]);
3237 ap[1] = ATOM_AT_INDEX(mview->mol->atoms, ip[1]);
3238 ap[2] = ATOM_AT_INDEX(mview->mol->atoms, ip[2]);
3239 ap[3] = ATOM_AT_INDEX(mview->mol->atoms, ip[3]);
3241 case 0: snprintf(buf, bufsize, "%d-%d-%d-%d", ip[0], ip[1], ip[2], ip[3]); break;
3242 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;
3243 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;
3245 snprintf(buf, bufsize, "%.3f", MoleculeMeasureDihedral(mview->mol, &(ap[0]->r), &(ap[1]->r), &(ap[2]->r), &(ap[3]->r)));
3251 TorsionPar *tp = (TorsionPar *)sParameterOfTypeAtIndex(mol, (f ? kDihedralParType : kImproperParType), idx);
3254 else if (column == 4)
3255 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]));
3256 else if (column == 6)
3257 snprintf(buf, bufsize, "%d", tp->period[0]);
3258 else snprintf(buf, bufsize, "%.3f", (column == 5 ? tp->k[0] * INTERNAL2KCAL : tp->phi0[0] * kRad2Deg));
3261 default: buf[0] = 0; break;
3263 } else if (mview->tableIndex == kMainViewUnitCellTableIndex) { /* Unit cell info */
3265 if (mview->mol->cell != NULL) {
3266 static const char *unitCellRowTitles[] = {"a", "b", "c", "alpha", "beta", "gamma", "a_valid", "b_valid", "c_valid", "a_vec", "b_vec", "c_vec", "origin"};
3268 snprintf(buf, bufsize, "%s", unitCellRowTitles[row]);
3271 case 0: case 1: case 2:
3273 snprintf(buf, bufsize, "%.5f", mview->mol->cell->cell[row]);
3274 else if (column == 2 && mview->mol->cell->has_sigma)
3275 snprintf(buf, bufsize, "%.5f", mview->mol->cell->cellsigma[row]);
3277 case 3: case 4: case 5:
3279 snprintf(buf, bufsize, "%.4f", mview->mol->cell->cell[row]);
3280 else if (column == 2 && mview->mol->cell->has_sigma)
3281 snprintf(buf, bufsize, "%.4f", mview->mol->cell->cellsigma[row]);
3283 case 6: case 7: case 8:
3285 snprintf(buf, bufsize, "%d", (int)mview->mol->cell->flags[row - 6]);
3287 case 9: case 10: case 11: case 12: {
3288 Vector *vp = (row == 12 ? &mview->mol->cell->origin : &mview->mol->cell->axes[row - 9]);
3289 Double dval = (column == 1 ? vp->x : (column == 2 ? vp->y : vp->z));
3290 snprintf(buf, bufsize, "%.5f", dval);
3296 } else if (mview->tableIndex == kMainViewMOTableIndex) { /* MO energy */
3297 BasisSet *bset = mview->mol->bset;
3300 if (bset != NULL && idx >= 0 && idx < bset->ncomps) {
3302 snprintf(buf, bufsize, "%d", idx + 1);
3303 } else if (column >= 3) {
3304 return; /* Cannot happen */
3306 /* column == 1, alpha; column == 2, beta */
3310 if (idx >= bset->ne_alpha)
3311 eflag = '*'; /* Unoccupied */
3313 if (bset->rflag == 2 && idx >= bset->ne_beta)
3314 eflag = 'S'; /* Singly occupied */
3318 if (bset->rflag != 0)
3319 return; /* No beta orbitals */
3320 if (idx >= bset->ne_beta)
3321 eflag = '*'; /* Unoccupied */
3322 idx += bset->ncomps;
3324 snprintf(buf, bufsize, "%c %.8f", eflag, bset->moenergies[idx]);
3330 /* Set color for the table */
3332 MainView_setColorForTable(MainView *mview, int column, int row, float *fg, float *bg)
3338 if (mview->tableIndex == kMainViewParameterTableIndex && column == -1) {
3339 int src = ParameterTableGetItemSource(mview->mol->par, row);
3340 if (src == -2) { /* separator line */
3341 bg[0] = bg[1] = bg[2] = 0.6;
3343 } else if (src == -1) { /* undefined parameters */
3345 bg[1] = bg[2] = 0.2;
3347 } else if (src == 0) { /* local parameter */
3348 bg[0] = bg[1] = 1.0;
3352 } else if (mview->tableIndex == kMainViewMOTableIndex && column == -1) {
3353 BasisSet *bset = mview->mol->bset;
3357 if (row < 0 || row >= bset->ncomps)
3359 if (row < bset->ne_beta)
3360 n = 0; /* Occupied */
3361 else if (row < bset->ne_alpha)
3362 n = 1; /* Half-occupied */
3363 else n = 2; /* Unoccupied */
3365 case 1: bg[0] = bg[1] = bg[2] = 0.9; break;
3366 case 2: bg[0] = bg[1] = bg[2] = 0.8; break;
3367 default: bg[0] = bg[1] = bg[2] = 1.0; break;
3370 } else if (mview->tableIndex <= 0 || mview->tableIndex >= 5)
3373 /* Bond etc. table */
3374 switch (mview->tableIndex) {
3375 case kMainViewBondTableIndex:
3376 parType = kBondParType;
3378 case kMainViewAngleTableIndex:
3379 parType = kAngleParType;
3381 case kMainViewDihedralTableIndex:
3382 parType = kDihedralParType;
3384 case kMainViewImproperTableIndex:
3385 parType = kImproperParType;
3393 idx = IntGroupGetNthPoint(mview->tableCache, row);
3394 up = sParameterOfTypeAtIndex(mview->mol, parType, idx);
3399 /* Value column; warn if the value is "abnormal" */
3402 case kBondParType: f = md_check_abnormal_bond(mview->mol->arena, mview->mol, idx); break;
3403 case kAngleParType: f = md_check_abnormal_angle(mview->mol->arena, mview->mol, idx); break;
3404 case kDihedralParType: f = md_check_abnormal_dihedral(mview->mol->arena, mview->mol, idx); break;
3405 case kImproperParType: f = md_check_abnormal_improper(mview->mol->arena, mview->mol, idx); break;
3410 bg[1] = bg[2] = 0.5;
3414 if (up->bond.src == 0) {
3415 bg[0] = bg[1] = 1.0;
3418 } else if (up->bond.src == -1) {
3420 bg[1] = bg[2] = 0.2;
3428 MainView_setSelectionFromTable(MainView *mview)
3431 if (mview == NULL || mview->mol == NULL)
3433 if (mview->tableIndex >= kMainViewParameterTableIndex)
3434 return; /* Do nothing */
3435 ig = MainViewCallback_getTableSelection(mview);
3436 sel = IntGroupNew();
3440 for (i = 0; (i1 = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
3441 i2 = IntGroupGetNthPoint(mview->tableCache, i1);
3444 if (mview->tableIndex == kMainViewAtomTableIndex) { /* Atoms */
3445 IntGroupAdd(sel, i2, 1);
3446 } else if (mview->tableIndex == kMainViewBondTableIndex) { /* Bonds */
3447 ip = mview->mol->bonds + i2 * 2;
3448 IntGroupAdd(sel, ip[0], 1);
3449 IntGroupAdd(sel, ip[1], 1);
3450 } else if (mview->tableIndex == kMainViewAngleTableIndex) { /* Angles */
3451 ip = mview->mol->angles + i2 * 3;
3452 IntGroupAdd(sel, ip[0], 1);
3453 IntGroupAdd(sel, ip[1], 1);
3454 IntGroupAdd(sel, ip[2], 1);
3455 } else if (mview->tableIndex == kMainViewDihedralTableIndex || mview->tableIndex == kMainViewImproperTableIndex) { /* Dihedrals, impropers */
3456 ip = (mview->tableIndex == kMainViewDihedralTableIndex ? mview->mol->dihedrals : mview->mol->impropers) + i2 * 4;
3457 IntGroupAdd(sel, ip[0], 1);
3458 IntGroupAdd(sel, ip[1], 1);
3459 IntGroupAdd(sel, ip[2], 1);
3460 IntGroupAdd(sel, ip[3], 1);
3463 IntGroupRelease(ig);
3465 MoleculeSetSelection(mview->mol, sel);
3466 IntGroupRelease(sel);
3470 sMainView_ParameterTableSetItemText(Molecule *mol, int column, int row, const char *s)
3474 const char *kstr = NULL;
3475 UnionPar *up = NULL;
3476 if (mol == NULL || (par = mol->par) == NULL || row < 0)
3478 up = ParameterTableGetItemPtr(par, row, &type);
3484 case 1: kstr = "atom_type"; 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;
3489 case 6: kstr = "atomic_number"; break;
3490 case 7: kstr = "weight"; break;
3495 case 1: kstr = "atom_types"; break;
3496 case 2: kstr = "k"; break;
3497 case 3: kstr = "r0"; break;
3502 case 1: kstr = "atom_types"; break;
3503 case 2: kstr = "k"; break;
3504 case 3: kstr = "a0"; break;
3507 case kDihedralParType:
3508 case kImproperParType:
3510 case 1: kstr = "atom_types"; break;
3511 case 2: kstr = "k"; break;
3512 case 3: kstr = "period"; break;
3513 case 4: kstr = "phi0"; break;
3516 case kVdwPairParType:
3518 case 1: kstr = "atom_types"; break;
3519 case 2: kstr = "eps"; break;
3520 case 3: kstr = "r_eq"; break;
3521 case 4: kstr = "eps14"; break;
3522 case 5: kstr = "r_eq14"; break;
3530 idx = ParameterTableGetItemIndex(par, row, &type);
3531 MolActionCreateAndPerform(mol, SCRIPT_ACTION("iissi"), "set_parameter_attr", type, idx, kstr, s, 0);
3535 MainView_setValueForTable(MainView *mview, int column, int row, const char *buf)
3541 if (mview == NULL || (mol = mview->mol) == NULL)
3543 MainView_valueForTable(mview, column, row, temp, sizeof temp);
3544 if (strcmp(buf, temp) == 0 || buf[0] == 0)
3545 return; /* No change */
3546 idx = IntGroupGetNthPoint(mview->tableCache, row);
3547 if (mview->tableIndex == kMainViewAtomTableIndex) { /* Atoms */
3549 case 1: key = "name"; break;
3550 case 2: key = "atom_type"; break;
3551 case 3: key = "element"; break;
3552 case 4: { /* Residue */
3553 IntGroup *ig = IntGroupNewWithPoints(idx, 1, -1);
3554 MolActionCreateAndPerform(mol, SCRIPT_ACTION("Gs"), "assign_residue", ig, buf);
3555 IntGroupRelease(ig);
3558 case 5: key = "x"; break;
3559 case 6: key = "y"; break;
3560 case 7: key = "z"; break;
3561 case 8: key = "charge"; break;
3564 MolActionCreateAndPerform(mol, SCRIPT_ACTION("iss"), "set_atom_attr", idx, key, buf);
3565 } else if (mview->tableIndex == kMainViewParameterTableIndex) { /* Parameters */
3566 sMainView_ParameterTableSetItemText(mview->mol, column, row, buf);
3567 } else if (mview->tableIndex == kMainViewUnitCellTableIndex) { /* Unit cell */
3568 Double cellPars[12], dval;
3572 if (mol->cell == NULL) {
3574 static const Double sCellPars[] = {1, 1, 1, 90, 90, 90};
3575 MolActionCreateAndPerform(mol, gMolActionSetCell, 6, sCellPars, 0);
3577 if (row >= 0 && row < 6) {
3579 memmove(cellPars, mol->cell->cell, sizeof(Double) * 6);
3580 if (mol->cell->has_sigma)
3581 memmove(cellPars + 6, mol->cell->cellsigma, sizeof(Double) * 6);
3582 else memset(cellPars + 6, 0, sizeof(Double) * 6);
3583 dval = strtod(buf, NULL);
3587 cellPars[row] = dval;
3590 cellPars[row + 6] = dval;
3592 MolActionCreateAndPerform(mol, gMolActionSetCell, n1, cellPars, 0);
3594 memmove(vecs, mol->cell->axes, sizeof(Vector) * 3);
3595 vecs[3] = mol->cell->origin;
3596 memmove(flags, mol->cell->flags, 3);
3597 if (row >= 6 && row < 9)
3598 flags[row - 6] = strtol(buf, NULL, 0);
3600 Vector *vp = vecs + (row - 9);
3601 dval = strtod(buf, NULL);
3604 else if (column == 2)
3608 MolActionCreateAndPerform(mol, gMolActionSetBox, vecs, vecs + 1, vecs + 2, vecs + 3, (flags[0] != 0) * 4 + (flags[1] != 0) * 2 + (flags[2] != 0), 0);
3615 MainView_isTableItemEditable(MainView *mview, int column, int row)
3618 return ParameterTableIsItemEditable(gBuiltinParameters, column, row);
3619 if (mview->mol == NULL)
3621 if (mview->tableIndex == kMainViewParameterTableIndex)
3622 return ParameterTableIsItemEditable(mview->mol->par, column, row);
3623 if (mview->tableIndex == kMainViewUnitCellTableIndex) {
3624 if ((row >= 0 && row < 6 && column >= 1 && column <= 3) ||
3625 (row >= 6 && row < 9 && column == 1) ||
3626 (row >= 9 && row < 13 && column >= 1 && column <= 3))
3630 if (mview->tableIndex >= 0 && mview->tableIndex < sizeof(sColumnInfo) / sizeof(sColumnInfo[0]))
3631 return sColumnInfo[mview->tableIndex][column].editable != 0;
3636 MainView_tableType(MainView *mview)
3639 return kMainViewParameterTableIndex;
3640 if (mview->mol == NULL)
3642 return mview->tableIndex;
3646 MainView_dragTableSelectionToRow(MainView *mview, int row)
3648 Int *new2old, i, n, count, natoms, start_row;
3650 if (mview == NULL || mview->mol == NULL || mview->tableIndex != 0 || row < 0 || row > (natoms = mview->mol->natoms))
3652 if (md_is_running(mview->mol->arena)) {
3653 MoleculeCallback_cannotModifyMoleculeDuringMDError(mview->mol);
3656 sel = MoleculeGetSelection(mview->mol);
3657 if (sel == NULL || (count = IntGroupGetCount(sel)) == 0)
3659 new2old = (Int *)calloc(sizeof(Int), natoms);
3660 if (new2old == NULL)
3663 // Place the atoms above the target position
3664 for (i = n = 0; i < row; i++) {
3665 if (IntGroupLookupPoint(sel, i) < 0)
3669 // Place the atoms within the selection
3670 for (i = 0; i < count; i++) {
3671 new2old[n++] = IntGroupGetNthPoint(sel, i);
3673 // Place the remaining atoms
3674 for (i = row; i < natoms; i++) {
3675 if (IntGroupLookupPoint(sel, i) < 0)
3678 MolActionCreateAndPerform(mview->mol, gMolActionRenumberAtoms, n, new2old);
3681 sel = IntGroupNewWithPoints(start_row, count, -1);
3682 MolActionCreateAndPerform(mview->mol, gMolActionSetSelection, sel);
3683 IntGroupRelease(sel);
3687 MainView_selectedMO(MainView *mview)
3689 if (mview == NULL || mview->mol == NULL || mview->tableIndex != kMainViewMOTableIndex)
3691 return MainViewCallback_getTableSelection(mview); /* Note: the indices are 0 based */