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