1 
2 /*
3 A* -------------------------------------------------------------------
4 B* This file contains source code for the PyMOL computer program
5 C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
6 D* -------------------------------------------------------------------
7 E* It is unlawful to modify or remove this copyright notice.
8 F* -------------------------------------------------------------------
9 G* Please see the accompanying LICENSE file for further information.
10 H* -------------------------------------------------------------------
11 I* Additional authors of this source file include:
12 -*
13 -*
14 -*
15 Z* -------------------------------------------------------------------
16 */
17 #include"os_python.h"
18 
19 #include"os_predef.h"
20 #include"os_std.h"
21 
22 #include"Base.h"
23 #include"OOMac.h"
24 #include"Vector.h"
25 #include"MemoryDebug.h"
26 #include"Err.h"
27 #include"Setting.h"
28 #include"Scene.h"
29 #include"Ray.h"
30 #include"ObjectDist.h"
31 #include"Selector.h"
32 #include"PConv.h"
33 #include"ObjectMolecule.h"
34 #include"Feedback.h"
35 #include"DistSet.h"
36 #include"ListMacros.h"
37 #ifdef _PYMOL_INCENTIVE
38 #endif
39 
40 static void ObjectDistFree(ObjectDist * I);
41 static void ObjectDistUpdateExtents(ObjectDist * I);
42 
ObjectDistGetLabelTxfVertex(ObjectDist * I,int state,int index,float * v)43 int ObjectDistGetLabelTxfVertex(ObjectDist * I, int state, int index, float *v)
44 {
45   int result = 0;
46   if(I->DSet) {
47     if(state < 0)
48       state = SettingGet_i(I->G, NULL, I->Setting, cSetting_state) - 1;
49     if(state < 0)
50       state = SceneGetState(I->G);
51     if(I->NDSet == 1)
52       state = 0;                /* static singletons always active here it seems */
53     state = state % I->NDSet;
54     {
55       DistSet *ds = I->DSet[state];
56       if((!ds) && (SettingGet_b(I->G, I->Setting, NULL, cSetting_all_states))) {
57         state = 0;
58         ds = I->DSet[state];
59       }
60       if(ds) {
61         result = DistSetGetLabelVertex(ds, index, v);
62       }
63     }
64   }
65   return (result);
66 }
67 
ObjectDistMoveLabel(ObjectDist * I,int state,int index,float * v,int mode,int log)68 int ObjectDistMoveLabel(ObjectDist * I, int state, int index, float *v, int mode, int log)
69 {
70   int result = 0;
71   DistSet *ds;
72   /* determine which state we're using */
73   if(state < 0)
74     state = 0;
75   if(I->NDSet == 1)
76     state = 0;
77   state = state % I->NDSet;
78   if((!I->DSet[state])
79      && (SettingGet_b(I->G, I->Setting, NULL, cSetting_all_states)))
80     state = 0;
81   /* find the corresponding distance set, for this state */
82   ds = I->DSet[state];
83   if(ds) {
84     result = DistSetMoveLabel(I->DSet[state], index, v, mode);
85     /* force this object to redraw itself; invalidate the Label's coordinates
86      * with the new data set, ds */
87     ds->invalidateRep(cRepLabel, cRepInvCoord);
88     /*      ExecutiveUpdateCoordDepends(I->G,I); */
89   }
90   return (result);
91 }
92 
93 
94 /* ObjectDistMoveWithObject -- updates the vertex positions of a distance measure
95  *
96  * PARAMS
97  *   (ObjectDist*) I
98  *     the object to update
99  *   (ObjectMolecule*) O
100  *     the object that moved, causing this function to be called
101  * RETURNS
102  *   (integer) 0=nothing moved; 1=something moved
103  */
ObjectDistMoveWithObject(ObjectDist * I,struct ObjectMolecule * O)104 int ObjectDistMoveWithObject(ObjectDist * I, struct ObjectMolecule * O) {
105   int result = 0, curResult = 0;
106   int i;
107   DistSet* ds;
108 
109   /* bail if the distance object is empty, or it doesn't have any distances */
110   if (!I || !I->NDSet || !I->DSet ) {
111     return 0;
112   }
113 
114   /* ask each DistSet to move itself, if required */
115   for (i=0; i<I->NDSet; i++) {
116     ds = I->DSet[i];
117     if (ds) {
118       curResult = DistSetMoveWithObject(ds, O);
119       result |= curResult;
120     }
121   }
122 
123   PRINTFD(I->G, FB_ObjectDist) " ObjectDist-Move: Out of Move\n" ENDFD;
124   return result;
125 }
126 /* -- JV end */
127 
128 
129 /*========================================================================*/
130 
ObjectDistUpdateExtents(ObjectDist * I)131 void ObjectDistUpdateExtents(ObjectDist * I)
132 {
133   float maxv[3] = { FLT_MAX, FLT_MAX, FLT_MAX };
134   float minv[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX };
135   int a;
136   DistSet *ds;
137 
138   /* update extents */
139   copy3f(maxv, I->ExtentMin);
140   copy3f(minv, I->ExtentMax);
141   I->ExtentFlag = false;
142   for(a = 0; a < I->NDSet; a++) {
143     ds = I->DSet[a];
144     if(ds) {
145       if(DistSetGetExtent(ds, I->ExtentMin, I->ExtentMax))
146         I->ExtentFlag = true;
147     }
148   }
149 }
150 
ObjectDistDSetAsPyList(ObjectDist * I)151 static PyObject *ObjectDistDSetAsPyList(ObjectDist * I)
152 {
153   PyObject *result = NULL;
154   int a;
155   result = PyList_New(I->NDSet);
156   for(a = 0; a < I->NDSet; a++) {
157     if(I->DSet[a]) {
158       PyList_SetItem(result, a, DistSetAsPyList(I->DSet[a]));
159     } else {
160       PyList_SetItem(result, a, PConvAutoNone(Py_None));
161     }
162   }
163   return (PConvAutoNone(result));
164 }
165 
ObjectDistDSetFromPyList(ObjectDist * I,PyObject * list)166 static int ObjectDistDSetFromPyList(ObjectDist * I, PyObject * list)
167 {
168   int ok = true;
169   int a;
170   if(ok)
171     ok = PyList_Check(list);
172   if(ok) {
173     VLACheck(I->DSet, DistSet *, I->NDSet);
174     for(a = 0; a < I->NDSet; a++) {
175       if(ok){
176         auto *val = PyList_GetItem(list, a);
177         ok = DistSetFromPyList(I->G, val, &I->DSet[a]);
178       }
179       if(ok)
180         I->DSet[a]->Obj = I;
181     }
182   }
183   return (ok);
184 }
185 
186 /*========================================================================*/
ObjectDistAsPyList(ObjectDist * I)187 PyObject *ObjectDistAsPyList(ObjectDist * I)
188 {
189   PyObject *result = NULL;
190 
191   /* first, dump the atoms */
192 
193   result = PyList_New(4);
194   PyList_SetItem(result, 0, ObjectAsPyList(I));
195   PyList_SetItem(result, 1, PyInt_FromLong(I->NDSet));
196   PyList_SetItem(result, 2, ObjectDistDSetAsPyList(I));
197   PyList_SetItem(result, 3, PyInt_FromLong(0));
198 
199   return (PConvAutoNone(result));
200 }
201 
ObjectDistNewFromPyList(PyMOLGlobals * G,PyObject * list,ObjectDist ** result)202 int ObjectDistNewFromPyList(PyMOLGlobals * G, PyObject * list, ObjectDist ** result)
203 {
204   int ok = true;
205   ObjectDist *I = NULL;
206   (*result) = NULL;
207 
208   if(ok)
209     ok = PyList_Check(list);
210 
211   I = new ObjectDist(G);
212   if(ok)
213     ok = (I != NULL);
214 
215   if(ok){
216     auto *val = PyList_GetItem(list, 0);
217     ok = ObjectFromPyList(G, val, I);
218   }
219   if(ok)
220     ok = PConvPyIntToInt(PyList_GetItem(list, 1), &I->NDSet);
221   if(ok)
222     ok = ObjectDistDSetFromPyList(I, PyList_GetItem(list, 2));
223 
224   ObjectDistInvalidateRep(I, cRepAll);
225   if(ok) {
226     (*result) = I;
227     ObjectDistUpdateExtents(I);
228   } else {
229     /* cleanup? */
230   }
231 
232   return (ok);
233 }
234 
235 /*========================================================================*/
getNFrame() const236 int ObjectDist::getNFrame() const
237 {
238   return NDSet;
239 }
240 
241 
242 /*========================================================================*/
update()243 void ObjectDist::update()
244 {
245   auto I = this;
246   int a;
247   OrthoBusyPrime(I->G);
248   for(a = 0; a < I->NDSet; a++)
249     if(I->DSet[a]) {
250       OrthoBusySlow(I->G, a, I->NDSet);
251       /*           printf(" ObjectDist: updating state %d of \"%s\".\n" , a+1, I->Name); */
252       I->DSet[a]->update(a);
253     }
254 }
255 
256 
257 /*========================================================================*/
ObjectDistInvalidateRep(ObjectDist * I,int rep)258 void ObjectDistInvalidateRep(ObjectDist * I, int rep)
259 {
260   int a;
261   PRINTFD(I->G, FB_ObjectDist)
262     " ObjectDistInvalidateRep: entered.\n" ENDFD;
263 
264   for(a = 0; a < I->NDSet; a++)
265     if(I->DSet[a]) {
266       I->DSet[a]->invalidateRep(rep, cRepInvAll);
267     }
268 }
269 
270 
271 /*========================================================================*/
render(RenderInfo * info)272 void ObjectDist::render(RenderInfo * info)
273 {
274   auto I = this;
275   int state = info->state;
276   int pass = info->pass;
277   CRay *ray = info->ray;
278   auto pick = info->pick;
279   bool shouldRender = false;
280 
281   if(ray || pick) {
282     shouldRender = true;
283   } else {
284     shouldRender = pass != 0;  // distance measurements should render
285                                // both in opaque and transparency loop,
286                                // the rep decides based on transparency
287                                // whether it renders in that loop.
288   }
289   if (!shouldRender)
290     return;
291 
292   ObjectPrepareContext(I, info);
293 
294   for(StateIterator iter(I->G, I->Setting, state, I->NDSet);
295       iter.next();) {
296     DistSet * ds = I->DSet[iter.state];
297     if(ds)
298       ds->render(info);
299   }
300 }
301 
302 #if 0
303 static CSetting **ObjectDistGetSettingHandle(ObjectDist * I, int state)
304 {
305   if(state < 0) {
306     return (&I->Setting);
307   } else {
308     return (NULL);
309   }
310 }
311 #endif
312 
invalidate(int rep,int level,int state)313 void ObjectDist::invalidate(int rep, int level, int state){
314   auto I = this;
315   for(StateIterator iter(I->G, I->Setting, state, I->NDSet);
316       iter.next();) {
317     DistSet * ds = I->DSet[iter.state];
318     if(ds)
319       ds->invalidateRep(rep, level);
320   }
321 }
322 
323 /*========================================================================*/
ObjectDist(PyMOLGlobals * G)324 ObjectDist::ObjectDist(PyMOLGlobals * G) : CObject(G)
325 {
326   auto I = this;
327   I->type = cObjectMeasurement;
328   I->DSet.reserve(10);  /* auto-zero */
329   I->Color = ColorGetIndex(G, "dash");
330 }
331 
332 
333 /*========================================================================*/
ObjectDistReset(PyMOLGlobals * G,ObjectDist * I)334 static void ObjectDistReset(PyMOLGlobals * G, ObjectDist * I)
335 {
336 	/* This wipes out all the distance sets and clears the state */
337   int a;
338   for(a = 0; a < I->NDSet; a++)
339     DeleteP(I->DSet[a]);
340   I->NDSet = 0;
341 }
342 
343 
344 /*========================================================================*/
checkFrozenState(PyMOLGlobals * G,int sele,int & state)345 static bool checkFrozenState(PyMOLGlobals * G, int sele, int &state) {
346   if (state >= 0)
347     return true;
348 
349   if (sele < 0)
350     return false;
351 
352   auto obj = (const CObject*) SelectorGetSingleObjectMolecule(G, sele);
353   if(!obj ||
354       !SettingGetIfDefined_i(G, obj->Setting, cSetting_state, &state))
355     return false;
356 
357   --state;
358   return true;
359 }
360 
361 
362 /*========================================================================*/
ObjectDistNewFromSele(PyMOLGlobals * G,ObjectDist * oldObj,int sele1,int sele2,int mode,float cutoff,int labels,int reset,float * result,int state,int state1,int state2)363 ObjectDist *ObjectDistNewFromSele(PyMOLGlobals * G, ObjectDist * oldObj,
364                                   int sele1, int sele2, int mode, float cutoff,
365                                   int labels, int reset, float *result, int state,
366                                   int state1, int state2)
367 {
368   int a, mn;
369   float dist_sum = 0.0, dist;
370   int dist_cnt = 0;
371   int n_state1, n_state2;
372   int frozen1 = -1, frozen2 = -1;
373   ObjectDist *I;
374 
375   /* if the distance name we presented exists and is an object, just
376    * overwrite it by resetting it; otherwise intialize the
377    * objectDistance and its base class */
378   if(!oldObj)
379     I = new ObjectDist(G);
380   else {
381     I = oldObj;
382     if(reset)
383       ObjectDistReset(G, I);
384   }
385 
386   *result = 0.0;
387 
388   /* max number of states */
389   mn = 0;
390   SelectorUpdateTable(G, state, -1);
391 
392   /* here we determine the highest number of states with which we need to concern ourselves */
393   n_state1 = SelectorGetSeleNCSet(G, sele1);
394   n_state2 = SelectorGetSeleNCSet(G, sele2);
395   /* take the larger state count */
396   mn = (n_state2>n_state1) ? n_state2 : n_state1;
397 
398   /* updated state handling */
399   frozen1 = checkFrozenState(G, sele1, state1);
400   frozen2 = checkFrozenState(G, sele2, state2);
401 
402   /* FIX for incorrectly handling state=-1 for multi-molecule selections */
403   if(state1<0) state1=0;
404   if(state2<0) state2=0;
405 
406   if(mn) {
407     /* loop over the max number of states */
408     for(a = 0; a < mn; a++) {
409 
410       /* the state param is valid, set it */
411       if(state >= 0) {
412         if(state >= mn)  /* bail */
413           break;
414         a = state;  /* otherwise, set a to state */
415       }
416 
417       PRINTFB(G, FB_ObjectDist, FB_Blather)
418 	" ObjectDistNewFromSele: obj1 is frozen = %d into state %d+1\n", frozen1, state1
419 	ENDFB(G);
420       PRINTFB(G, FB_ObjectDist, FB_Blather)
421 	" ObjectDistNewFromSele: obj1 is frozen = %d into state %d+1\n", frozen2, state2
422 	ENDFB(G);
423 
424       VLACheck(I->DSet, DistSet *, a);
425       if(!frozen1)
426 	state1 = (n_state1>1) ? a : 0;
427       if(!frozen2)
428 	state2 = (n_state2>1) ? a : 0;
429 
430       /* this does the actual work of creating the distances for this state */
431       /* I->DSet[a] = new DistSet(G, selections, states, etc) -- created this new DistSet */
432       if (5 <= mode && mode <= 7) {
433 #ifdef _PYMOL_INCENTIVE
434 #else
435         PRINTFB(G, FB_ObjectDist, FB_Errors)
436           " ObjectDist-Error: modes 5-7 only available in Incentive PyMOL\n"
437           ENDFB(G);
438         I->DSet[a] = nullptr;
439 #endif
440       } else {
441         I->DSet[a] = SelectorGetDistSet(
442             G, I->DSet[a], sele1, state1, sele2, state2, mode, cutoff, &dist);
443       }
444 
445       /* if the distances are valid, then tally the total and set the ObjectMolecule pointer as necessary */
446       if(I->DSet[a]) {
447         dist_sum += dist;	/* average distance over N states */
448         dist_cnt++;
449         I->DSet[a]->Obj = I;	/* point to the ObjectMolecule for this state's DistanceSet */
450         I->NDSet = a + 1;
451       }
452 
453       if(state >= 0 || (frozen1 && frozen2))
454 	break;
455     }
456   }
457   /* set the object's bounds and redraw */
458   ObjectDistUpdateExtents(I);
459   ObjectDistInvalidateRep(I, cRepAll);
460 
461   /* return the avg dist */
462   if(dist_cnt)
463     (*result) = dist_sum / dist_cnt;
464 
465   SceneChanged(G);
466   return (I);
467 }
468 
ObjectDistNewFromAngleSele(PyMOLGlobals * G,ObjectDist * oldObj,int sele1,int sele2,int sele3,int mode,int labels,float * result,int reset,int state,int state1,int state2,int state3)469 ObjectDist *ObjectDistNewFromAngleSele(PyMOLGlobals * G, ObjectDist * oldObj,
470                                        int sele1, int sele2, int sele3, int mode,
471                                        int labels, float *result, int reset, int state,
472                                        int state1, int state2, int state3)
473 {
474   int a, mn;
475   float angle_sum = 0.0;
476   int angle_cnt = 0;
477   int n_state1, n_state2, n_state3;
478   ObjectDist *I;
479 
480   int frozen1=-1, frozen2=-1, frozen3=-1;
481   if(!oldObj)                   /* create object if new */
482     I = new ObjectDist(G);
483   else {                        /* otherwise, use existing object */
484     I = oldObj;
485     if(reset) {                 /* if reseting, then clear out all existing coordinate sets */
486       ObjectDistReset(G, I);
487     }
488   }
489   *result = 0.0;
490 
491   /* count number of states in each selection */
492 
493   SelectorUpdateTable(G, state, -1);
494   n_state1 = SelectorGetSeleNCSet(G, sele1);
495   n_state2 = SelectorGetSeleNCSet(G, sele2);
496   n_state3 = SelectorGetSeleNCSet(G, sele3);
497 
498   /* figure out the total number of states */
499 
500   mn = n_state1;
501   if(n_state2 > mn)
502     mn = n_state2;
503   if(n_state3 > mn)
504     mn = n_state3;
505 
506   /* updated state handling */
507   frozen1 = checkFrozenState(G, sele1, state1);
508   frozen2 = checkFrozenState(G, sele2, state2);
509   frozen3 = checkFrozenState(G, sele3, state3);
510 
511   if(mn) {
512     for(a = 0; a < mn; a++) {
513       if(state >= 0) {
514         if(state > mn)
515           break;
516         a = state;
517       }
518       /* treat selections with one state as static singletons */
519 
520       PRINTFB(G, FB_ObjectDist, FB_Blather)
521 	" ObjectDistNewFromAngleSele: obj1 is frozen = %d into state %d+1\n", frozen1, state1
522 	ENDFB(G);
523       PRINTFB(G, FB_ObjectDist, FB_Blather)
524 	" ObjectDistNewFromAngleSele: obj2 is frozen = %d into state %d+1\n", frozen2, state2
525 	ENDFB(G);
526       PRINTFB(G, FB_ObjectDist, FB_Blather)
527 	" ObjectDistNewFromAngleSele: obj3 is frozen = %d into state %d+1\n", frozen3, state3
528 	ENDFB(G);
529 
530       if(!frozen1)
531 	state1 = (n_state1>1) ? a : 0;
532       if(!frozen2)
533 	state2 = (n_state2>1) ? a : 0;
534       if(!frozen3)
535 	state3 = (n_state3>1) ? a : 0;
536 
537       VLACheck(I->DSet, DistSet *, a+1);
538       I->DSet[a] = SelectorGetAngleSet(G, I->DSet[a], sele1, state1, sele2,
539                                        state2, sele3, state3, mode, &angle_sum,
540                                        &angle_cnt);
541 
542       if(I->DSet[a]) {
543         I->DSet[a]->Obj = I;
544         if(I->NDSet <= a)
545           I->NDSet = a + 1;
546       }
547       if(state >= 0 || (frozen1 && frozen2 && frozen3))
548         break;
549     }
550   }
551   /* else {
552      VLAFreeP(I->DSet);
553      OOFreeP(I);
554      }
555    */
556   ObjectDistUpdateExtents(I);
557   ObjectDistInvalidateRep(I, cRepAll);
558   if(angle_cnt)
559     (*result) = angle_sum / angle_cnt;
560 
561   SceneChanged(G);
562   return (I);
563 }
564 
ObjectDistNewFromDihedralSele(PyMOLGlobals * G,ObjectDist * oldObj,int sele1,int sele2,int sele3,int sele4,int mode,int labels,float * result,int reset,int state)565 ObjectDist *ObjectDistNewFromDihedralSele(PyMOLGlobals * G, ObjectDist * oldObj,
566                                           int sele1, int sele2, int sele3, int sele4,
567                                           int mode, int labels, float *result,
568                                           int reset, int state)
569 {
570   int a, mn;
571   float angle_sum = 0.0;
572   int angle_cnt = 0;
573   int n_state1, n_state2, n_state3, n_state4;
574   int state1 = -1, state2 = -1, state3 = -1, state4 = -1;
575   ObjectDist *I;
576 
577   int frozen1=-1, frozen2=-1, frozen3=-1, frozen4=-1;
578 
579   if(!oldObj)                   /* create object if new */
580     I = new ObjectDist(G);
581   else {                        /* otherwise, use existing object */
582     I = oldObj;
583     if(reset) {                 /* if reseting, then clear out all existing coordinate sets */
584       ObjectDistReset(G, I);
585     }
586   }
587   *result = 0.0;
588 
589   /* count number of states in each selection */
590 
591   SelectorUpdateTable(G, state, -1);
592 
593   n_state1 = SelectorGetSeleNCSet(G, sele1);
594   n_state2 = SelectorGetSeleNCSet(G, sele2);
595   n_state3 = SelectorGetSeleNCSet(G, sele3);
596   n_state4 = SelectorGetSeleNCSet(G, sele4);
597 
598   /* figure out the total number of states */
599 
600   mn = n_state1;
601   if(n_state2 > mn)
602     mn = n_state2;
603   if(n_state3 > mn)
604     mn = n_state3;
605   if(n_state4 > mn)
606     mn = n_state4;
607 
608   /* updated state handling */
609   frozen1 = checkFrozenState(G, sele1, state1);
610   frozen2 = checkFrozenState(G, sele2, state2);
611   frozen3 = checkFrozenState(G, sele3, state3);
612   frozen4 = checkFrozenState(G, sele4, state4);
613 
614   if(mn) {
615     for(a = 0; a < mn; a++) {
616       if(state >= 0) {
617         if(state > mn)
618           break;
619         a = state;
620       }
621       /* treat selections with one state as static singletons */
622 
623       if(!frozen1)
624 	state1 = (n_state1>1) ? a : 0;
625       if(!frozen2)
626 	state2 = (n_state2>1) ? a : 0;
627       if(!frozen3)
628 	state3 = (n_state3>1) ? a : 0;
629       if(!frozen4)
630 	state4 = (n_state4>1) ? a : 0;
631 
632       VLACheck(I->DSet, DistSet *, a);
633       I->DSet[a] = SelectorGetDihedralSet(G, I->DSet[a], sele1, state1, sele2,
634                                           state2, sele3, state3, sele4, state4,
635                                           mode, &angle_sum, &angle_cnt);
636 
637       if(I->DSet[a]) {
638         I->DSet[a]->Obj = I;
639         if(I->NDSet <= a)
640           I->NDSet = a + 1;
641       }
642 
643       if(state >= 0 || (frozen1 && frozen2 && frozen3 && frozen4))
644         break;
645     }
646   }
647   /* else {
648      VLAFreeP(I->DSet);
649      OOFreeP(I);
650      }
651    */
652   ObjectDistUpdateExtents(I);
653   ObjectDistInvalidateRep(I, cRepAll);
654 
655   if(angle_cnt)
656     (*result) = angle_sum / angle_cnt;
657 
658   SceneChanged(G);
659   return (I);
660 }
661 
662 
663 /*========================================================================*/
~ObjectDist()664 ObjectDist::~ObjectDist()
665 {
666   auto I = this;
667   for(int a = 0; a < I->NDSet; a++)
668     DeleteP(I->DSet[a]);
669   VLAFreeP(I->DSet);
670 }
671