1 /*
2  ----------------------------------------------------------------------------
3  "THE BEER-WARE LICENSE" (Revision 42):
4  <dkratzert@gmx.de> wrote this file. As long as you retain
5  this notice you can do whatever you want with this stuff. If we meet some day,
6  and you think this stuff is worth it, you can buy me a beer in return.
7  Daniel Kratzert
8  ----------------------------------------------------------------------------
9 */
10 
11 #include "dsrglwindow.h"
12 #include "molecule.h"
13 #include "chgl.h"
14 #include "dsrgui.h"
15 
16 
DSRGlWindow(QWidget * parent,Molecule * m,DSRMol header,QString fragment)17 DSRGlWindow::DSRGlWindow(QWidget *parent, Molecule *m,
18                          DSRMol header, QString fragment) {
19   this->setParent(parent);
20   m_molecule=m;
21   showFit=nullptr;
22   showFitLabel=nullptr;
23   mole.cell.symmops.clear();
24   mole.cell.trans.clear();
25   mole.asymm.clear();
26   mole.sdm.clear();
27   mole.showbonds.clear();
28   mole.showatoms.clear();
29   mole.selectedatoms.clear();
30   mole.envi_sdm.clear();
31   mole.contact.clear();
32   mole.envi_sdm.clear();
33   mole.contact.clear();
34   mole.symmopsEQIV.clear();
35   mole.labelEQIV.clear();
36   mole.transEQIV.clear();
37   mole.freeatoms.clear();
38   mole.bindatoms.clear();
39   mole.selectedatoms.clear();
40   mole.showatoms.clear();
41   dsrLabelColor = QColor("white");
42   V3 nl(0,0,0);
43   mole.cell.trans.append(nl);
44   mole.cell.symmops.append(Matrix(1,0,0, 0,1,0, 0,0,1));
45   mole.pmin=10000;
46   mole.pmax=-10000;
47   mole.LOD=3;
48   // initial zoom level:
49   // The Zom will later be adapted to fragment size.
50   L=50.0;
51   mole.transEQIV.clear();
52   mole.symmopsEQIV.clear();
53   mole.labelEQIV.clear();
54   mole.freeatoms.clear();
55   mole.bindatoms.clear();
56   mole.envi_sdm.clear();
57   if (fragment.size() > 0) {
58     display_fragment(header);
59   }
60 }
61 
~DSRGlWindow()62 DSRGlWindow::~DSRGlWindow() {
63 }
64 
transform_point(GLdouble out[4],const GLdouble m[16],const GLdouble in[4])65 static inline void transform_point(GLdouble out[4], const GLdouble m[16], const GLdouble in[4]) {
66 #define M(row,col)  m[col*4+row]
67   out[0] =
68 	  M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3];
69   out[1] =
70 	  M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3];
71   out[2] =
72 	  M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3];
73   out[3] =
74 	  M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3];
75 #undef M
76 }
77 
posTo2D(V3 obj,const GLdouble model[16],const GLdouble proj[16],const GLint viewport[4],GLdouble * winx,GLdouble * winy)78 static inline bool  posTo2D(V3 obj,
79 		const GLdouble model[16], const GLdouble proj[16],
80 		const GLint viewport[4],
81 		GLdouble * winx, GLdouble * winy) {
82   GLdouble in[4], out[4];
83 
84   in[0] = obj.x;
85   in[1] = obj.y;
86   in[2] = obj.z;
87   in[3] = 1.0;
88   transform_point(out, model, in);
89   transform_point(in, proj, out);
90 
91   if (in[3] == 0.0) return false;
92 
93   in[0] /= in[3];
94   in[1] /= in[3];
95   in[2] /= in[3];
96 
97   *winx = viewport[0] + (1 + in[0]) * viewport[2] / 2;
98   *winy = viewport[1] + (1 - in[1]) * viewport[3] / 2;
99   return true;
100 }
101 
102 
draw()103 void DSRGlWindow::draw(){
104   /*const GLfloat  OBJ_SPE[]   = { 0.8, 0.8, 0.8, 1.0 };
105     const GLfloat  OBJ_SHIN    = 127.0;
106     glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR,             OBJ_SPE  );
107     glEnable     ( GL_COLOR_MATERIAL ) ;
108     glColorMaterial ( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) ;
109     glMaterialf(  GL_FRONT_AND_BACK, GL_SHININESS,           OBJ_SHIN );
110   */
111   mole.dratom = 0;
112   glPushMatrix();
113   glScaled( L, L, L );
114   GLdouble model[16];
115   GLdouble proj[16];
116   GLint viewport[4];
117   glGetIntegerv(GL_VIEWPORT, viewport);
118 #if defined Q_OS_MAC && (QT_VERSION >= 0x050000)
119   viewport[2]/=2;
120   viewport[3]/=2;
121 #endif
122   //      printf("%d %d %d %d\n",viewport[0],viewport[1],viewport[2],viewport[3]);
123   glGetDoublev( GL_PROJECTION_MATRIX, (double*)proj );
124   glGetDoublev( GL_MODELVIEW_MATRIX, (double*)model );
125   mole.adp = 0;
126   mole.intern = 0;
127   mole.tubes = 0;
128   glDisable(GL_BLEND);
129   mole.atoms(xd, 50);
130   mole.bonds(bonds);
131   glEnable(GL_COLOR_MATERIAL);
132   qglColor(dsrLabelColor);  // The fragment label color
133   glClear( GL_DEPTH_BUFFER_BIT);
134   for (int i=0; i<xd.size(); i++){
135       GLdouble in[4], out[4];
136       in[0] = xd.at(i).pos.x;
137       in[1] = xd.at(i).pos.y;
138       in[2] = xd.at(i).pos.z;
139       in[3] = 1.0;
140       transform_point(out, model, in);
141       posTo2D(xd.at(i).pos,model,proj,viewport, &xd[i].screenX, &xd[i].screenY);
142       bool issel=false;
143       for (int j=0; j<selFragAt.size(); j++) {
144         if ((showFitLabel!=nullptr)
145               && (!showFitLabel->isChecked())
146               && (j<m_molecule->selectedatoms.size())
147               && (xd.at(i).Label==selFragAt.at(j).Label)) {
148           issel=true;
149           renderText( xd.at(i).pos.x, xd.at(i).pos.y, xd.at(i).pos.z,
150               xd.at(i).Label+"->"+m_molecule->selectedatoms.at(j).Label, myFont);
151         }
152       }
153       if (!issel) {
154         renderText( xd.at(i).pos.x, xd.at(i).pos.y, xd.at(i).pos.z,
155             xd.at(i).Label, myFont);
156       }
157   }
158   glPopMatrix();
159   if (!selFragAt.isEmpty()){
160     glPushMatrix();{
161       glScaled( L, L, L );
162       mole.tubes=0;
163       mole.intern=1;
164       mole.adp=0;
165       mole.dratom=1;
166       mole.atoms(selFragAt);
167       mole.dratom=0;
168       glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
169     }glPopMatrix();
170   }
171   if ((showFit!=nullptr)&&(!fita.isEmpty())&&(showFit->isChecked())){
172     glPushMatrix();{
173       glEnable(GL_BLEND);
174       glScaled( L, L, L );
175       mole.tubes=0;
176       mole.intern=0;
177       mole.adp=0;
178       mole.dratom=5;
179       mole.atoms(fita);
180       mole.dbond(fitabonds);
181       mole.lbond();
182       if (showFitLabel->isChecked()){
183         qglColor(QColor(0, 0, 175));  // The surrounding atoms label color
184         for (int i=0; i<fita.size(); i++){
185           renderText( fita.at(i).pos.x, fita.at(i).pos.y, fita.at(i).pos.z, fita.at(i).Label, myFont);
186         }
187       }
188     }glPopMatrix();
189   }
190 }
191 
initializeGL()192 void DSRGlWindow::initializeGL(){
193   glEnable(GL_LINE_SMOOTH);
194   glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
195   //glEnable(GL_POLYGON_SMOOTH);
196   myFont = QFont("Arial", 12, -1, false);  // the initial label font size
197   const GLfloat  position[] = {100.0f, 100.0f,100.0f,0.0f};
198   const GLfloat  diffuse[]  = { 1.0f, 1.0f, 1.0f, 1.0f };
199   const GLfloat  specular[] = { 1.0f, 0.9f, 0.9f, 1.0f };
200   const GLfloat  ambient[]  = { 0.4f, 0.4f, 0.4f, 1.0f };
201   glLightModeli(  GL_LIGHT_MODEL_LOCAL_VIEWER, 1 );
202   glLightfv( GL_LIGHT0, GL_POSITION, position );
203   glLightfv( GL_LIGHT0, GL_AMBIENT,  ambient );
204   glLightfv( GL_LIGHT0, GL_DIFFUSE,  diffuse );
205   glLightfv( GL_LIGHT0, GL_SPECULAR, specular );
206   glLightfv( GL_LIGHT1, GL_POSITION, position );
207   glLightfv( GL_LIGHT1, GL_DIFFUSE,  diffuse );
208   glLightfv( GL_LIGHT1, GL_AMBIENT,  ambient );
209   glLightfv( GL_LIGHT2, GL_DIFFUSE,  diffuse );
210   glEnable( GL_LIGHTING );
211   glEnable( GL_LIGHT0 );
212   //  glEnable( GL_BLEND);
213   glDisable(GL_BLEND);
214   glAlphaFunc ( GL_GREATER, 0.01f ) ;
215   //glEnable(GL_ALPHA_TEST);
216   glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
217   const GLfloat  OBJ_SPE[]   = { 1.0, 1.0, 1.0, 1.0 };
218   const GLfloat  OBJ_SHIN    = 127.0;
219   glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR,             OBJ_SPE  );
220   glEnable     ( GL_COLOR_MATERIAL ) ;
221   glColorMaterial ( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) ;
222   glMaterialf(  GL_FRONT_AND_BACK, GL_SHININESS,           OBJ_SHIN );
223   glShadeModel( GL_SMOOTH );
224   glEnable(GL_NORMALIZE);
225   glClearColor(0.6f,0.6f,0.6f,1.0f);
226   glEnable(GL_DEPTH_TEST );
227   glDepthFunc(GL_LEQUAL);
228   gluLookAt_(0.0, 200, 50 ,   0.0, 0.0, 0.0,   0.0, -100, 400 );
229   glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
230 }
231 
resizeGL(int width,int height)232 void DSRGlWindow::resizeGL(int width, int height){
233 #if defined Q_OS_MAC && (QT_VERSION >= 0x050000)
234   glViewport(0, 0, width*2,height*2);
235 #else
236   glViewport(0, 0, width, height);
237 #endif
238   glGetIntegerv(GL_VIEWPORT, vp);
239   glMatrixMode(GL_PROJECTION);
240   glLoadIdentity();
241   gluPerspective_( 29.0, (double)width/height, 5.0, 8000.0 );
242 }
243 
minimumSizeHint() const244 QSize DSRGlWindow::minimumSizeHint() const
245 {
246     return QSize(250, 250);
247 }
248 
sizeHint() const249 QSize DSRGlWindow::sizeHint() const
250 {
251     return QSize(400, 400);
252 }
253 
paintGL()254 void DSRGlWindow::paintGL(){
255   glClearColor(0.6f,0.6f,0.6f,1.0f); // The background color
256   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
257 #if defined Q_OS_MAC && (QT_VERSION >= 0x050000)
258   glViewport(0, 0, QGLWidget::width()*2, QGLWidget::height()*2);
259 #else
260   glViewport(0, 0, QGLWidget::width(), QGLWidget::height());
261 #endif
262   glGetIntegerv(GL_VIEWPORT, vp);
263   glMatrixMode(GL_PROJECTION);
264   glLoadIdentity();
265   gluPerspective_( 29.0, (double)QGLWidget::width()/QGLWidget::height(), 5.0, 8000.0 );
266   glMatrixMode(GL_MODELVIEW);
267   glPushMatrix();
268   draw();
269   glPopMatrix();
270 }
271 
clear_molecule()272 void DSRGlWindow::clear_molecule()
273 { // clear the 3D view
274   xd.clear();
275   bonds.clear();
276   fita.clear();
277   fitabonds.clear();
278   selFragAt.clear();
279   source_atoms.clear();
280   updateGL();
281 }
282 
display_fragment(DSRMol frag,bool clear_sel)283 void DSRGlWindow::display_fragment(DSRMol frag, bool clear_sel)
284 { //! uses a QStringlist of atoms from DSR and fills
285   //! them into a MyAtom. MyAtom is then drawn in OpenGL
286   //! @param The fragment from DSR
287   //! @param clear_sel if enabled, the selected atoms are cleared
288   //! before drawing the molecule
289   V3 mid = V3(0,0,0);
290   MyAtom newAtom;
291   newAtom.part = 0;
292   newAtom.resiNr = 0;
293   newAtom.hidden = 0;
294   newAtom.symmGroup = 0;
295   newAtom.afix = 0;
296   xd.clear();
297   bonds.clear();
298   fita.clear();
299   fitabonds.clear();
300   // Do not clear the selection during invert:
301   if (clear_sel) {
302     selFragAt.clear();
303   }
304   if (frag.atoms.isEmpty()) {
305     return;
306   }
307   foreach (QStringList atom, frag.atoms) {
308     if (atom.length() < 4)  continue;  // Do not crash on empty string
309     newAtom.Label = atom.at(0);
310     newAtom.an = abs(atom.at(1).toInt())-1;
311     newAtom.pos.x = atom.at(2).toDouble();
312     newAtom.pos.y = atom.at(3).toDouble();
313     newAtom.pos.z = atom.at(4).toDouble();
314     mid += newAtom.pos;
315     //qDebug()<< newAtom.Label << newAtom.an << newAtom.pos.x << newAtom.pos.y << newAtom.pos.z;
316     xd.append(newAtom);
317   }
318   mid *= -1.0/xd.size();
319   for (int i=0; i<xd.size(); i++){
320     xd[i].pos += mid;
321   }
322   L = 90.0/mole.dimension(xd); // Zoom level of the molecule inside the widget
323   bonds=mole.connecting(xd,true);
324   updateGL();
325 }
326 
327 
set_label_color()328 void DSRGlWindow::set_label_color() {
329     //! Sets the label color. The default color was not visible
330     //! for white or yellow atoms.
331     QColor lc= QColorDialog::getColor(QColor(6, 167, 222), this);
332     if (lc.isValid()) {
333         dsrLabelColor=lc;
334     } else {
335         dsrLabelColor = QColor("white");
336     }
337     updateGL();
338 }
339 
340 
mousePressEvent(QMouseEvent * event)341 void DSRGlWindow::mousePressEvent(QMouseEvent *event){
342   //! Handles the mousePressEvents in the 3D view.
343   //! Atoms are selectable if showFit is not nullptr.
344   //! New atoms will be selected if the mousePressEvent is inside the
345   //! size of the respective atom and less than three atoms.
346   //! With more than three atoms, the first selection will be removed.
347   moux=event->pos().x();
348   mouy=event->pos().y();
349   if((event->buttons() == Qt::RightButton && event->modifiers() == Qt::ControlModifier)){
350       QMenu *menu = new QMenu("");
351       menu->addAction(QString("Set label color"), this, SLOT(set_label_color()));
352       menu->exec(QCursor::pos());
353   }
354   if (event->buttons() == Qt::LeftButton) {
355     // Has to be after moux/mouy, otherwise zoom is strange:
356     if (showFit == nullptr) return;  // <-prevents selection of atoms in edit window
357     double nahda=200.0, da=0;
358     int nahdai=-1;
359     for (int j=0; j<xd.size();j++){
360       if (xd.at(j).hidden) continue;
361       da=(((xd.at(j).screenX-event->x())*( xd.at(j).screenX-event->x()))+
362           ((xd.at(j).screenY-event->y())*( xd.at(j).screenY-event->y())));
363       nahdai=(da<nahda)?j:nahdai;
364       nahda=qMin(nahda,da);
365     }
366     if ((nahdai >- 1) && (nahdai < xd.size())){
367       // This might be a good idea in the first place, but results in wired behavior:
368       //if (selFragAt.contains(xd[nahdai])) {
369       //    return;
370       //}
371       selFragAt.append(xd[nahdai]);
372       if (selFragAt.size() > 3) selFragAt.removeFirst();
373       updateGL();
374       if (selFragAt.size() == 3) {
375         source_atoms = QString("%1 %2 %3")
376                         .arg(selFragAt.at(0).Label)
377                         .arg(selFragAt.at(1).Label)
378                         .arg(selFragAt.at(2).Label);
379         emit sourceStringChanged();
380       }
381     }
382     makeInfo();
383   }
384 }
385 
makeInfo()386 void DSRGlWindow::makeInfo(){
387   //! makes an info box that shows the fragment fit distances
388   double f12dis=0, f13dis=0, t12dis=0, t13dis=0;
389   if (selFragAt.size()>1) f12dis=sqrt(Distance(selFragAt.at(0).pos,selFragAt.at(1).pos));
390   if (selFragAt.size()>2) f13dis=sqrt(Distance(selFragAt.at(0).pos,selFragAt.at(2).pos));
391   if (m_molecule->selectedatoms.size()>1) t12dis=sqrt(Distance(m_molecule->selectedatoms.at(0).pos,m_molecule->selectedatoms.at(1).pos));
392   if (m_molecule->selectedatoms.size()>2) t13dis=sqrt(Distance(m_molecule->selectedatoms.at(0).pos,m_molecule->selectedatoms.at(2).pos));
393   emit updateInfo(QString(
394         "<table>"
395         "<tr><th colspan=\"2\" align=\"left\">fragment: </th>"
396         "<th><th>"
397         "    <th colspan=\"2\" align=\"left\">target:</th>"
398         "</tr>"
399         "<tr><td colspan=\"2\"><font color=blue>%1</font></td>"
400         "<td><td>"
401         "    <td colspan=\"2\"><font color=blue>%2</font></td>"
402         "</tr>"
403         "<tr><td align=\"left\"><font color=blue>%3</font></td> "
404         "    <td align=\"left\">1,2: <b>%7 &Aring;</b></td>"
405         "<td><td>"
406         "    <td align=\"left\"><font color=blue>%4 </font></td>"
407         "    <td align=\"right\">1,2: <b>%9 &Aring;</b></td>"
408         "</tr>"
409         "<tr><td align=\"left\"><font color=blue>%5</font></td>"
410         "    <td align=\"left\">1,3: <b>%8 &Aring;</b></td>"
411         "<td><td>"
412         "    <td align=\"right\"><font color=blue>%6 </font></td>"
413         "    <td align=\"right\">1,3: <b>%10 &Aring;<b></td>"
414         "</tr>"
415         "</table>")
416     .arg((selFragAt.size()>0)?selFragAt.at(0).Label:"")
417     .arg((m_molecule->selectedatoms.size()>0)?m_molecule->selectedatoms.at(0).Label:"")
418     .arg((selFragAt.size()>1)?selFragAt.at(1).Label:"")
419     .arg((m_molecule->selectedatoms.size()>1)?m_molecule->selectedatoms.at(1).Label:"")
420     .arg((selFragAt.size()>2)?selFragAt.at(2).Label:"")
421     .arg((m_molecule->selectedatoms.size()>2)?m_molecule->selectedatoms.at(2).Label:"")
422     .arg(f12dis, 4, 'f', 2)
423     .arg(f13dis, 4, 'f', 2)
424     .arg(t12dis, 4, 'f', 2)
425     .arg(t13dis, 4, 'f', 2)
426     );
427     tryfit();
428 }
429 
430 //void glRotateL(double ang, double x, double y, double z);
__RotateCS(double c,double s,double & X,double & Y)431 inline void DSRGlWindow::__RotateCS( double c, double s, double& X, double& Y ) {
432   double T = X;
433   X = c*X - s*Y;
434   Y = s*T + c*Y;
435 }
436 
glTranslateL(const double dx,const double dy,const double dz)437 void DSRGlWindow::glTranslateL( const double dx, const double dy, const double dz ) {
438   double mat[4][4];
439 
440   glGetDoublev( GL_MODELVIEW_MATRIX, (double*)mat );
441   mat[3][0] += dx;  mat[3][1] += dy;  mat[3][2] += dz;
442   glLoadMatrixd((double*)mat);
443 }
glRotateL(const double dang,const double x,const double y,const double z)444 void DSRGlWindow::glRotateL( const double dang, const double x, const double y, const double z ) {
445   double mat[4][4];
446 #ifndef M_PI
447 #define	M_PI		3.14159265358979323846	/* pi */
448 #endif
449 
450   double s = z;
451   s = sin(dang*M_PI/180);
452   const double c = cos(dang*M_PI/180);
453   glGetDoublev( GL_MODELVIEW_MATRIX, (double*)mat );
454   //  glGetDoublev( GL_PROJECTION_MATRIX, (double*)mat );
455   if( x!=0.0 ){
456     __RotateCS( c, s, mat[0][1], mat[0][2] );
457     __RotateCS( c, s, mat[1][1], mat[1][2] );
458     __RotateCS( c, s, mat[2][1], mat[2][2] );
459     //    printf("x\n");
460   }else if( y!=0.0 ){
461     __RotateCS( c, s, mat[0][2], mat[0][0] );
462     __RotateCS( c, s, mat[1][2], mat[1][0] );
463     __RotateCS( c, s, mat[2][2], mat[2][0] );
464     //    printf("y\n");
465   }else{
466     __RotateCS( c, s, mat[0][0], mat[0][1] );
467     __RotateCS( c, s, mat[1][0], mat[1][1] );
468     __RotateCS( c, s, mat[2][0], mat[2][1] );
469     //    printf("z\n");
470   }
471   glLoadMatrixd((double*)mat);
472 }
473 
474 
475 
mouseMoveEvent(QMouseEvent * event)476 void DSRGlWindow::mouseMoveEvent(QMouseEvent *event){
477   //! Either rotates (left mouse button) the molecule
478   //! or zooms the molecule (right mouse button).
479   double x = event->pos().x();
480   double y = event->pos().y();
481   GLfloat dx = GLfloat( event->x() - moux) / width();
482   GLfloat dy = GLfloat( event->y() - mouy) / height();
483   if((event->buttons() & Qt::RightButton)){
484     glScaled(1.0-dy,1.0-dy,1.0-dy);
485   }else if((event->buttons() && Qt::LeftButton)){
486     glRotateL(dy*360.0,1.0,0.0,0.0);
487     glRotateL(dx*360.0,0.0,1.0,0.0);
488   }
489   moux=x;
490   mouy=y;
491   updateGL();
492 }
493 
wheelEvent(QWheelEvent * event)494 void DSRGlWindow::wheelEvent(QWheelEvent *event){
495   /*
496 modifiers:
497 Qt::NoModifier	        0x00000000  No modifier key is pressed.
498 Qt::ShiftModifier	      0x02000000  A Shift key on the keyboard is pressed.
499 Qt::ControlModifier	    0x04000000  A Ctrl key on the keyboard is pressed.
500 Qt::AltModifier	        0x08000000  An Alt key on the keyboard is pressed.
501 Qt::MetaModifier	      0x10000000  A Meta key on the keyboard is pressed.
502 Qt::KeypadModifier	    0x20000000  A keypad button is pressed.
503 Qt::GroupSwitchModifier	0x40000000  X11 only. A Mode_switch key on the keyboard is pressed.
504 */
505   int numDegrees = event->delta() / 8;
506   int numSteps = numDegrees / 15;
507   if (event->modifiers()==Qt::NoModifier){
508     int d = myFont.pointSize();
509     d = (d+numSteps>4)?d+numSteps:d;
510     d = qMax(d,7);
511     myFont.setPointSize(d);
512     updateGL();
513   }
514 
515 }
516 
tryfit()517 void DSRGlWindow::tryfit(){
518   if (showFit==nullptr) return;
519   fita.clear();
520   fitabonds.clear();
521   if ((selFragAt.size()!=3)||(m_molecule->selectedatoms.size()!=3)) {updateGL();return;}
522 
523   //tree orthogonal vectors of unit size form a rotation matrix
524   //the transponse of the latter is its inverse
525   V3 a1,a2,a3;
526   a1=Normalize(m_molecule->selectedatoms.at(0).pos-m_molecule->selectedatoms.at(1).pos);//a1=(A-B)/|A-B|
527   a2=Normalize(m_molecule->selectedatoms.at(0).pos-m_molecule->selectedatoms.at(2).pos);//a2=(A-C)/|A-B|
528   a3=Normalize(a1%a2);//a3=(a1 x a2)/(a1 x a2)
529   a2=a3%a1;//a2 = a3 x a1
530   Matrix amat=Matrix(a1,a2,a3);
531 
532   V3 b1,b2,b3;
533   b1=Normalize(selFragAt.at(0).pos-selFragAt.at(1).pos);
534   b2=Normalize(selFragAt.at(0).pos-selFragAt.at(2).pos);
535   b3=Normalize(b1%b2);
536   b2=b3%b1;
537   Matrix bmat=transponse(Matrix(b1,b2,b3));
538   if ( (a1*(a2%a3)<0.9) || (b1*(b2%b3)<0.9) ) {  //linear dependence id determinant is 0
539     updateGL();
540     return;
541   }
542   // m_molecule->showatoms was m_molecule->asymm before, but showatoms is needed for disorder
543   // around special positions:
544   for (int i=0; i<m_molecule->showatoms.size(); i++) {
545     fita.append(m_molecule->showatoms[i]);
546     fita.last().pos+=-1.0*(m_molecule->selectedatoms.at(0).pos);
547     fita.last().pos=fita.last().pos*amat;
548     fita.last().pos=fita.last().pos*bmat;
549     fita.last().pos+=selFragAt.at(0).pos;
550     fita.last().part=1;
551     if (Norm (fita.last().pos) > 12) {  // 4 Angstroem cut-off (3*4=12)
552       fita.removeLast();
553     }
554   }
555   fitabonds=mole.connecting(fita,true);
556   updateGL();
557   printf("FIT:\n================================================================================\n");
558   for (int i=0; i<xd.size(); i++){
559     int imin=0;
560     double dmin=10.0;
561     for (int j=0; j<m_molecule->showatoms.size(); j++){
562       V3 ort=m_molecule->showatoms.at(j).pos;
563       ort+=-1.0*(m_molecule->selectedatoms.at(0).pos);
564       ort=ort*amat;
565       ort=ort*bmat;
566       ort+=selFragAt.at(0).pos;
567 
568       double d=Distance(ort,xd.at(i).pos);
569       if (d<dmin) imin=j;
570       dmin=(d<dmin)?d:dmin;
571     }
572     printf("%-8s <==> %-8s %f\n", xd.at(i).Label.toStdString().c_str(),
573            m_molecule->showatoms.at(imin).Label.toStdString().c_str(), sqrt(dmin));
574   }
575 }
576