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_gl.h"
21 #include"os_std.h"
22 
23 #include"main.h"
24 #include"PyMOLObject.h"
25 #include"Color.h"
26 #include"Ortho.h"
27 #include"Scene.h"
28 #include"Util.h"
29 #include"Ray.h"
30 #include"PConv.h"
31 #include"Matrix.h"
32 #include"MemoryDebug.h"
33 #include"Movie.h"
34 #include"View.h"
35 #include"Err.h"
36 
37 #include"Executive.h"
38 #include"CGO.h"
39 #include"Selector.h"
40 #include"vla.h"
41 
ObjectPurgeSettings(CObject * I)42 void ObjectPurgeSettings(CObject * I)
43 {
44   SettingFreeP(I->Setting);
45   I->Setting = NULL;
46 }
47 
ObjectMotionTrim(CObject * I,int n_frame)48 void ObjectMotionTrim(CObject *I, int n_frame)
49 {
50   if(I->ViewElem) {
51     VLASize(I->ViewElem,CViewElem,n_frame);
52   }
53 }
54 
ObjectMotionGetLength(CObject * I)55 int ObjectMotionGetLength(CObject *I)
56 {
57   if(I->ViewElem) {
58     return VLAGetSize(I->ViewElem);
59   }
60   return 0;
61 }
62 
ObjectMotionReinterpolate(CObject * I)63 void ObjectMotionReinterpolate(CObject *I)
64 {
65   float power  = SettingGet_f(I->G, NULL, I->Setting, cSetting_motion_power);
66   float bias   = SettingGet_f(I->G, NULL, I->Setting, cSetting_motion_bias);
67   int simple   = SettingGet_i(I->G, NULL, I->Setting, cSetting_motion_simple);
68   float linear = SettingGet_f(I->G, NULL, I->Setting, cSetting_motion_linear);
69   int hand     = SettingGet_i(I->G, NULL, I->Setting, cSetting_motion_hand);
70 
71   /*
72      int ObjectMotion(CObject * I, int action, int first,
73                  int last, float power, float bias,
74                  int simple, float linear, int wrap,
75                  int hand, int window, int cycles, int state, int quiet);
76   */
77   ObjectMotion(I, 3, -1, -1, power, bias, simple, linear,
78                SettingGetGlobal_b(I->G,cSetting_movie_loop) ? 1 : 0,
79                hand, 5, 1, -1, 1);
80 }
81 
ObjectMotionModify(CObject * I,int action,int index,int count,int target,int freeze,int localize)82 int ObjectMotionModify(CObject *I,int action, int index, int count,int target,int freeze,int localize)
83 {
84   int ok;
85 
86   if(I->type == cObjectGroup) { /* propagate */
87     ok = ExecutiveGroupMotionModify(I->G,I,action,index,count,target,freeze);
88   } else {
89     ok = ViewElemModify(I->G, &I->ViewElem,action,index,count,target);
90     if(ok && I->ViewElem) {
91       int size = VLAGetSize(I->ViewElem);
92       int n_frame = MovieGetLength(I->G);
93       if(n_frame != size) {
94         /* extend entire movie */
95         if(!localize)
96           ExecutiveMotionExtend(I->G,true);
97         if((!freeze) && SettingGetGlobal_i(I->G,cSetting_movie_auto_interpolate)) {
98           ExecutiveMotionReinterpolate(I->G);
99         }
100       } else if((!freeze) && SettingGetGlobal_i(I->G,cSetting_movie_auto_interpolate)) {
101         ObjectMotionReinterpolate(I);
102       }
103     }
104   }
105   return ok;
106 }
107 
TTTToViewElem(float * TTT,CViewElem * elem)108 static void TTTToViewElem(float *TTT, CViewElem * elem)
109 {
110   float *fp = TTT;
111   double *dp;
112 
113   /* convert row-major TTT to column-major ViewElem */
114 
115   elem->matrix_flag = true;
116   dp = elem->matrix;
117 
118   dp[0] = (double) fp[0];
119   dp[1] = (double) fp[4];
120   dp[2] = (double) fp[8];
121   dp[3] = 0.0;
122 
123   dp[4] = (double) fp[1];
124   dp[5] = (double) fp[5];
125   dp[6] = (double) fp[9];
126   dp[7] = 0.0;
127 
128   dp[8] = (double) fp[2];
129   dp[9] = (double) fp[6];
130   dp[10] = (double) fp[10];
131   dp[11] = 0.0;
132 
133   dp[12] = 0.0;
134   dp[13] = 0.0;
135   dp[14] = 0.0;
136   dp[15] = 1.0;
137 
138   /* copy inverse pre */
139 
140   elem->pre_flag = true;
141   dp = elem->pre;
142   *(dp++) = (double) -TTT[12];
143   *(dp++) = (double) -TTT[13];
144   *(dp++) = (double) -TTT[14];
145 
146   /* copy post */
147 
148   elem->post_flag = true;
149   dp = elem->post;
150   *(dp++) = (double) TTT[3];
151   *(dp++) = (double) TTT[7];
152   *(dp++) = (double) TTT[11];
153 
154 }
155 
TTTFromViewElem(float * TTT,CViewElem * elem)156 static void TTTFromViewElem(float *TTT, CViewElem * elem)
157 {
158   float *fp = TTT;
159   double *dp;
160 
161   if(elem->matrix_flag) {
162     dp = elem->matrix;
163 
164     fp[0] = (float) dp[0];
165     fp[1] = (float) dp[4];
166     fp[2] = (float) dp[8];
167     fp[3] = 0.0;
168 
169     fp[4] = (float) dp[1];
170     fp[5] = (float) dp[5];
171     fp[6] = (float) dp[9];
172     fp[7] = 0.0;
173 
174     fp[8] = (float) dp[2];
175     fp[9] = (float) dp[6];
176     fp[10] = (float) dp[10];
177     fp[11] = 0.0;
178 
179     fp[12] = 0.0;
180     fp[13] = 0.0;
181     fp[14] = 0.0;
182     fp[15] = 1.0;
183   }
184 
185   if(elem->pre_flag) {
186     dp = elem->pre;
187     fp[12] = (float) (-*(dp++));
188     fp[13] = (float) (-*(dp++));
189     fp[14] = (float) (-*(dp++));
190   }
191 
192   if(elem->post_flag) {
193     dp = elem->post;
194     fp[3] = (float) *(dp++);
195     fp[7] = (float) *(dp++);
196     fp[11] = (float) *(dp++);
197   }
198   fp[15] = 1.0F;
199 }
200 
ObjectGetSpecLevel(CObject * I,int frame)201 int ObjectGetSpecLevel(CObject * I, int frame)
202 {
203   if(I->ViewElem) {
204     int size = VLAGetSize(I->ViewElem);
205     if(frame<0) {
206       int max_level = 0;
207       int i;
208       for(i=0;i<size;i++) {
209         if(max_level < I->ViewElem[i].specification_level)
210           max_level = I->ViewElem[i].specification_level;
211       }
212       return max_level;
213     }
214     if((frame>=0) && (frame<size))
215       return I->ViewElem[frame].specification_level;
216     return 0;
217   }
218   return -1;
219 }
220 
ObjectDrawViewElem(CObject * I,BlockRect * rect,int frames ORTHOCGOARG)221 void ObjectDrawViewElem(CObject *I, BlockRect *rect,int frames ORTHOCGOARG)
222 {
223   if(I->ViewElem) {
224     ViewElemDraw(I->G,I->ViewElem,rect,frames,I->Name ORTHOCGOARGVAR);
225   }
226 }
227 
ObjectMotion(CObject * I,int action,int first,int last,float power,float bias,int simple,float linear,int wrap,int hand,int window,int cycles,int state,int quiet)228 int ObjectMotion(CObject * I, int action, int first,
229                int last, float power, float bias,
230                int simple, float linear, int wrap,
231                int hand, int window, int cycles, int state, int quiet)
232 {
233   PyMOLGlobals *G = I->G;
234   if(I->type == cObjectGroup) { /* propagate */
235     return ExecutiveGroupMotion(G,I,action,first,last, power,bias,simple,linear,
236                                 wrap,hand,window,cycles,state,quiet);
237   } else {
238 
239     int frame;
240     int nFrame = MovieGetLength(I->G);
241 
242     if(wrap<0) {
243       wrap = SettingGet_b(I->G,NULL, I->Setting, cSetting_movie_loop);
244     }
245 
246     if(nFrame < 0)
247       nFrame = -nFrame;
248 
249     if(!I->ViewElem) {
250       I->ViewElem = pymol::vla<CViewElem>(0);
251     }
252 
253     if((action == 7) || (action == 8)) { /* toggle */
254       frame = first;
255       if(first < 0)
256         frame = SceneGetFrame(G);
257       VLACheck(I->ViewElem, CViewElem, frame);
258       if(action == 7) {
259         if(I->ViewElem[frame].specification_level>1) {
260           action = 1;
261         } else {
262           action = 0;
263         }
264       } else if(action == 8) {
265         if(I->ViewElem[frame].specification_level>1) {
266           int frame;
267           action = 3;
268           for(frame=0;frame<nFrame;frame++) {
269             if(I->ViewElem[frame].specification_level==1) {
270               action = 6;
271               break;
272             }
273           }
274         }
275         else if(I->ViewElem[frame].specification_level>0) {
276           action = 6;
277         } else {
278           action = 3;
279         }
280       }
281     }
282 
283     if(action == 4) {   /* smooth */
284       int save_last = last;
285       if(first < 0)
286         first = 0;
287 
288       if(last < 0) {
289         last = nFrame;
290       }
291       if(last >= nFrame) {
292         last = nFrame - 1;
293       }
294       if(first <= last) {
295         int a;
296         VLACheck(I->ViewElem, CViewElem, last);
297           for(a = 0; a < cycles; a++) {
298             ViewElemSmooth(I->ViewElem + first, I->ViewElem + last, window, wrap);
299           }
300       }
301       if(SettingGet_b(I->G, NULL, I->Setting, cSetting_movie_auto_interpolate)){
302         action = 3; /* reinterpolate */
303         last = save_last;
304       }
305     }
306     switch (action) {
307     case 0:                      /* store */
308       if(!I->TTTFlag) {
309         float mn[3], mx[3], orig[3];
310         if(ExecutiveGetExtent(G, I->Name, mn, mx, true, -1, true)) {
311           average3f(mn, mx, orig);
312           ObjectSetTTTOrigin(I, orig);
313         } else {
314           initializeTTT44f(I->TTT);
315           I->TTTFlag = true;
316         }
317       }
318       if(I->ViewElem && I->TTTFlag) {
319         if(first < 0)
320           first = SceneGetFrame(G);
321         if(last < 0)
322           last = first;
323         {
324           int state_tmp=0, state_flag = false;
325           if(state>=0) {
326             state_tmp = state;
327             state_flag = true;
328           } else if(SettingGetIfDefined_i(G, I->Setting, cSetting_state, &state_tmp)) {
329             state_flag = true;
330             state_tmp--;
331           }
332 
333           for(frame = first; frame <= last; frame++) {
334             if((frame >= 0) && (frame < nFrame)) {
335               VLACheck(I->ViewElem, CViewElem, frame);
336               if(!quiet) {
337                 PRINTFB(G, FB_Object, FB_Details)
338                   " ObjectMotion: Setting frame %d.\n", frame + 1 ENDFB(G);
339               }
340               TTTToViewElem(I->TTT, I->ViewElem + frame);
341 
342               if(state_flag) {
343                 I->ViewElem[frame].state_flag = state_flag;
344                 I->ViewElem[frame].state = state_tmp;
345               }
346 
347               if(power!=0.0F) {
348                 I->ViewElem[frame].power_flag = true;
349                 I->ViewElem[frame].power = power;
350               }
351 
352               if(bias > 0.0F) {
353                 I->ViewElem[frame].bias_flag = true;
354                 I->ViewElem[frame].bias = bias;
355               }
356 
357               I->ViewElem[frame].specification_level = 2;
358             }
359 
360           }
361         }
362       }
363       break;
364     case 1:                      /* clear */
365       if(I->ViewElem) {
366         if(first < 0)
367           first = SceneGetFrame(G);
368         if(last < 0)
369           last = first;
370         for(frame = first; frame <= last; frame++) {
371           if((frame >= 0) && (frame < nFrame)) {
372             VLACheck(I->ViewElem, CViewElem, frame);
373             ViewElemArrayPurge(G, I->ViewElem + frame, 1);
374             UtilZeroMem((void *) (I->ViewElem + frame), sizeof(CViewElem));
375           }
376         }
377       }
378       break;
379     case 2:                      /* interpolate & reinterpolate */
380     case 3:
381       {
382         CViewElem *first_view = NULL, *last_view = NULL;
383         int view_found = false;
384 
385         if(first < 0)
386           first = 0;
387         if(first > nFrame) {
388           first = nFrame - 1;
389         }
390 
391         if(last < 0) {
392           last = nFrame;
393           if(last) {
394             if(!wrap)
395               last--;
396             else {
397               int frame = 0;
398               VLACheck(I->ViewElem, CViewElem, last);
399               for(frame = 0; frame < last; frame++) {
400                 if(I->ViewElem[frame].specification_level > 1) {
401                   last += frame;
402                   break;
403                 }
404               }
405             }
406           }
407         } else {
408           if(last >= nFrame) {
409             last = nFrame;
410             if(last && !wrap)
411               last--;
412           }
413         }
414 
415         VLACheck(I->ViewElem, CViewElem, last);
416 
417         if(wrap && (last >= nFrame)) {
418           /* if we're interpolating beyond the last frame, then wrap by
419              copying early frames to last frames */
420           int a;
421           for(a = nFrame; a <= last; a++) {
422             ViewElemCopy(G, I->ViewElem + a - nFrame, I->ViewElem + a);
423           }
424         } else if(!wrap) {
425           /* if we're not wrapping, then make sure we nuke any stray / old
426              interpolated frames */
427           frame = nFrame - 1;
428           while(frame>=0) {
429             if(I->ViewElem[frame].specification_level > 1)
430               break;
431             else
432               UtilZeroMem((void *) (I->ViewElem + frame), sizeof(CViewElem));
433             frame--;
434           }
435         }
436         VLACheck(I->ViewElem, CViewElem, last);
437         if(!quiet) {
438           if(action == 2) {
439             if(last == nFrame) {
440               PRINTFB(G, FB_Object, FB_Details)
441                 " ObjectMotion: interpolating unspecified frames %d to %d (wrapping).\n",
442                 first + 1, last ENDFB(G);
443             } else {
444               PRINTFB(G, FB_Object, FB_Details)
445                 " ObjectMotion: interpolating unspecified frames %d to %d.\n", first + 1,
446                 last + 1 ENDFB(G);
447             }
448           } else {
449             if(last == nFrame) {
450               PRINTFB(G, FB_Object, FB_Details)
451                 " ObjectMotion: reinterpolating all frames %d to %d (wrapping).\n", first + 1,
452                 last ENDFB(G);
453             } else {
454               PRINTFB(G, FB_Object, FB_Details)
455                 " ObjectMotion: reinterpolating all frames %d to %d.\n", first + 1, last + 1
456                 ENDFB(G);
457             }
458           }
459         }
460         for(frame = first; frame <= last; frame++) {
461           if(!first_view) {
462             if(I->ViewElem[frame].specification_level == 2) {     /* specified */
463               first_view = I->ViewElem + frame;
464               view_found = true;
465             }
466           } else {
467             CViewElem *view;
468             int interpolate_flag = false;
469             if(I->ViewElem[frame].specification_level == 2) {     /* specified */
470               last_view = I->ViewElem + frame;
471               if(action == 2) {   /* interpolate */
472                 for(view = first_view + 1; view < last_view; view++) {
473                   if(!view->specification_level)
474                     interpolate_flag = true;
475                 }
476               } else {
477                 interpolate_flag = true;
478               }
479               if(interpolate_flag) {
480                 ViewElemInterpolate(G, first_view, last_view,
481                                     power, bias, simple, linear, hand, 0.0F);
482               }
483               first_view = last_view;
484               last_view = NULL;
485             }
486           }
487         }
488 
489         if(first_view) {
490           if(wrap && (last >= nFrame)) {
491             /* if we're interpolating beyond the last frame, then wrap by
492                copying the last frames back over the early frames */
493             int a;
494             for(a = nFrame; a <= last; a++) {
495               ViewElemCopy(G, I->ViewElem + a, I->ViewElem + a - nFrame);
496             }
497           }
498         }
499 
500         if((!view_found) && (last>=first) && (first>=0) && (last<=nFrame)) {
501           UtilZeroMem(I->ViewElem + first, sizeof(CViewElem) * (1 + (last-first)));
502         }
503 
504         if(last >= nFrame) {   /* now erase temporary views */
505           ViewElemArrayPurge(G, I->ViewElem + nFrame, (1 + last - nFrame));
506           UtilZeroMem((void *) (I->ViewElem + nFrame),
507                       sizeof(CViewElem) * (1 + last - nFrame));
508         }
509       }
510       break;
511     case 5:                      /* reset */
512       if(I->ViewElem) {
513         VLAFreeP(I->ViewElem);
514       }
515       I->ViewElem = pymol::vla<CViewElem>(0);
516       break;
517     case 6:                      /* uninterpolate */
518       if(I->ViewElem) {
519         if(first < 0)
520           first = 0;
521         if(last < 0) {
522           last = nFrame - 1;
523         }
524         for(frame = first; frame <= last; frame++) {
525           if((frame >= 0) && (frame <= last)) {
526             VLACheck(I->ViewElem, CViewElem, frame);
527             if(I->ViewElem[frame].specification_level < 2) {
528               ViewElemArrayPurge(G, I->ViewElem + frame, 1);
529               UtilZeroMem((void *) (I->ViewElem + frame), sizeof(CViewElem));
530             }
531           }
532         }
533       }
534       break;
535     case 9:
536       if(I->ViewElem) {
537         VLAFreeP(I->ViewElem);
538       }
539       break;
540     }
541     if(I->ViewElem) {
542       VLASize(I->ViewElem,CViewElem,nFrame);
543     }
544   }
545   return 1;
546 }
547 
ObjectAdjustStateRebuildRange(CObject * I,int * start,int * stop)548 void ObjectAdjustStateRebuildRange(CObject * I, int *start, int *stop)
549 {
550   /* on entry, start and stop should hold the valid range for the object */
551   int defer_builds_mode =
552     SettingGet_i(I->G, NULL, I->Setting, cSetting_defer_builds_mode);
553   int async_builds = SettingGet_b(I->G, NULL, I->Setting, cSetting_async_builds);
554   int max_threads = SettingGet_i(I->G, NULL, I->Setting, cSetting_max_threads);
555   int all_states = SettingGet_i(I->G, NULL, I->Setting, cSetting_all_states);
556   int dummy;
557   if (all_states)
558     return;
559   if(defer_builds_mode >= 3) {
560     if(SceneObjectIsActive(I->G, I))
561       defer_builds_mode = 2;
562   }
563   switch (defer_builds_mode) {
564   case 1:                      /* defer geometry builds until needed */
565   case 2:                      /* defer and destroy continuously for increase memory conservation */
566     if(SettingGetIfDefined_i(I->G, I->Setting, cSetting_state, &dummy)) {
567       /* decoupled...so always build all states.  Otherwise, geometry
568       may not be there when we need it... unfortunately, this defeats
569       the purpose of defer_builds_mode! */
570     } else {
571       int min = *start;
572       int max = *stop;
573       int global_state = SceneGetState(I->G);
574       int obj_state = ObjectGetCurrentState(I, false);
575 
576       *start = obj_state;
577       if((obj_state != global_state) || (!async_builds) || (max_threads < 1)) {
578         *stop = *start + 1;
579         if(*stop > max )
580           *stop = max;
581       } else {
582         int base = (*start / max_threads);
583         *start = (base) * max_threads;
584         *stop = (base + 1) * max_threads;
585         if(*start < min)
586           *start = min;
587         if(*start > max)
588           *start = max;
589         if(*stop < min)
590           *stop = min;
591         if(*stop > max)
592           *stop = max;
593       }
594       if(*start > obj_state)
595         *start = obj_state;
596       if(*stop <= obj_state)
597         *stop = obj_state + 1;
598       if(*start < 0)
599         *start = 0;
600     }
601     break;
602   case 3:                      /* object not active, so do not rebuild anything */
603     *stop = *start;
604     break;
605   }
606 }
607 
608 /**
609  * Replaces invalid characters in the given object name with an underscore,
610  * or strips them if they are terminal or sequential.
611  * @param[in,out] name Object name to validate
612  * @return true if name was modified, false otherwise
613  */
ObjectMakeValidName(char * name)614 bool ObjectMakeValidName(char *name)
615 {
616   bool modified = false;
617   char *p = name, *q;
618   if(p) {
619     /* currently legal are A to Z, a to z, 0 to 9, -, _, + */
620     while(*p) {
621       switch (*p) {
622         case '+':
623         case '-':
624         case '.':
625         case '^':
626         case '_':
627           break;
628         default:
629           if (('A' <= *p && *p <= 'Z') ||
630               ('a' <= *p && *p <= 'z') ||
631               ('0' <= *p && *p <= '9'))
632             break;
633         /* must be an ASCII-visible character */
634         *p = 1;                 /* placeholder for non-printable */
635         modified = true;
636       }
637       p++;
638     }
639     /* eliminate sequential and terminal nonprintables */
640     p = name;
641     q = name;
642     while(*p) {
643       if(q == name)
644         while(*p == 1)
645           p++;
646       while((*p == 1) && (p[1] == 1))
647         p++;
648       *q++ = *p++;
649       if(!p[-1])
650         break;
651     }
652     *q = 0;
653     while(q > name) {
654       if(q[-1] == 1) {
655         q[-1] = 0;
656         q--;
657       } else
658         break;
659     }
660     /* convert invalides to underscore */
661     p = name;
662     while(*p) {
663       if(*p == 1)
664         *p = '_';
665       p++;
666     }
667   }
668   return modified;
669 }
670 
671 /**
672  * Replaces invalid characters in `name` with an underscore,
673  * or strips them if they are terminal or sequential - if `name` equals a reserved
674  * selection keyword, then also append an underscore.
675  *
676  * @param[in,out] name Object name to validate
677  * @param quiet If false, print warnings if the name gets modified.
678  */
ObjectMakeValidName(PyMOLGlobals * G,char * name,bool quiet)679 void ObjectMakeValidName(PyMOLGlobals * G, char *name, bool quiet)
680 {
681   if (ObjectMakeValidName(name) && !quiet) {
682     PRINTFB(G, FB_Executive, FB_Warnings)
683       " Warning: Invalid characters in '%s' have been replaced or stripped\n",
684       name ENDFB(G);
685   }
686 
687   if (SelectorNameIsKeyword(G, name)) {
688     if (!quiet) {
689       PRINTFB(G, FB_Executive, FB_Warnings)
690       " Warning: '%s' is a reserved keyword, appending underscore\n", name
691       ENDFB(G);
692     }
693     strcat(name, "_");
694     return;
695   }
696 
697   static bool once_protein = false;
698   static bool once_nucleic = false;
699 
700   if (!once_protein && strcmp(name, "protein") == 0) {
701     once_protein = true;
702   } else if (!once_nucleic && strcmp(name, "nucleic") == 0) {
703     once_nucleic = true;
704   } else {
705     return;
706   }
707 
708   {
709     // Warn the user if "protein" or "nucleic" are used as names, but
710     // don't modify the name (yet).
711     PRINTFB(G, FB_Executive, FB_Warnings)
712       " Warning: '%s' may become a reserved selection keyword in the future\n", name
713       ENDFB(G);
714   }
715 }
716 
717 /**
718  * Get a pointer to an object state.
719  * @param state State (0-indexed) or -2/-3 for current state
720  * @return NULL if state is out of bounds or empty
721  */
getObjectState(int state)722 CObjectState* CObject::getObjectState(int state)
723 {
724   if (state == -2 /* cSelectorUpdateTableCurrentState */ ||
725       state == -3 /* cSelectorUpdateTableEffectiveStates */) {
726     state = getCurrentState();
727   }
728   if (state < 0 || state >= getNFrame()) {
729     return nullptr;
730   }
731   return _getObjectState(state);
732 }
733 
734 /**
735  * Get the effective state (0-indexed) of an object, based on the `state` and
736  * `static_singletons` settings. Will not validate the value of the `state`
737  * setting, it could be `<0` or `>=getNFrame()`.
738  */
getCurrentState() const739 int CObject::getCurrentState() const
740 {
741   if (getNFrame() == 1 &&
742       SettingGet<bool>(G, Setting, nullptr, cSetting_static_singletons))
743     return 0;
744   return SettingGet<int>(G, Setting, nullptr, cSetting_state) - 1;
745 }
746 
747 /**
748  * Like CObject::getCurrentState() but will return `-1` if the `all_states`
749  * setting is set.
750  *
751  * Note: Clamps negative values at `-1` (all states). The usefulness of this
752  * should be questioned, in particular with `ignore_all_states=true` a caller
753  * is likely to discard all negative values, including -1.
754  *
755  * @param ignore_all_states Boolean flag, should be false. You most likely
756  * should use CObject::getCurrentState() instead of setting `ignore_all_states`
757  * to true.
758  */
ObjectGetCurrentState(CObject * I,int ignore_all_states)759 int ObjectGetCurrentState(CObject * I, int ignore_all_states)
760 {
761   assert("use CObject::getCurrentState()" && !ignore_all_states);
762 
763   // the previous implementation (up to PyMOL 1.7.6) ignored
764   // object-level state=0 (all states)
765 
766   if (!ignore_all_states &&
767       SettingGet_b(I->G, I->Setting, NULL, cSetting_all_states))
768     return -1;
769 
770   return std::max(-1, I->getCurrentState());
771 }
772 
ObjectAsPyList(CObject * I)773 PyObject *ObjectAsPyList(CObject * I)
774 {
775   PyObject *result = NULL;
776   result = PyList_New(14);
777   PyList_SetItem(result, 0, PyInt_FromLong(I->type));
778   PyList_SetItem(result, 1, PyString_FromString(I->Name));
779   PyList_SetItem(result, 2, PyInt_FromLong(I->Color));
780   PyList_SetItem(result, 3, PyInt_FromLong(I->visRep));
781   PyList_SetItem(result, 4, PConvFloatArrayToPyList(I->ExtentMin, 3));
782   PyList_SetItem(result, 5, PConvFloatArrayToPyList(I->ExtentMax, 3));
783   PyList_SetItem(result, 6, PyInt_FromLong(I->ExtentFlag));
784   PyList_SetItem(result, 7, PyInt_FromLong(I->TTTFlag));
785   PyList_SetItem(result, 8, SettingAsPyList(I->Setting));
786 
787   PyList_SetItem(result, 9, PyInt_FromLong(I->Enabled));
788   PyList_SetItem(result, 10, PyInt_FromLong(I->Context));
789   PyList_SetItem(result, 11, PConvFloatArrayToPyList(I->TTT, 16));
790   if(I->ViewElem) {
791     int nFrame = VLAGetSize(I->ViewElem);
792     PyList_SetItem(result, 12, PyInt_FromLong(nFrame));
793     PyList_SetItem(result, 13, ViewElemVLAAsPyList(I->G, I->ViewElem, nFrame));
794   } else {
795     PyList_SetItem(result, 12, PyInt_FromLong(0));
796     PyList_SetItem(result, 13, PConvAutoNone(NULL));
797   }
798   return (PConvAutoNone(result));
799 }
800 
ObjectFromPyList(PyMOLGlobals * G,PyObject * list,CObject * I)801 int ObjectFromPyList(PyMOLGlobals * G, PyObject * list, CObject * I)
802 {
803   int ok = true;
804   int ll = 0;
805   I->G = G;
806   if(ok)
807     ok = (list != NULL);
808   if(ok)
809     ok = PyList_Check(list);
810   if(ok)
811     ll = PyList_Size(list);
812   if(ok)
813     ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 0, reinterpret_cast<int*>(&I->type));
814   if(ok)
815     ok = PConvPyStrToStr(PyList_GetItem(list, 1), I->Name, WordLength);
816   if(ok)
817     ok = PConvPyIntToInt(PyList_GetItem(list, 2), &I->Color);
818   if(ok)
819     I->Color = ColorConvertOldSessionIndex(G, I->Color);
820   if(ok) {
821     PyObject *val = PyList_GetItem(list, 3);
822     if(PyList_Check(val)) {
823       ok = PConvPyListToBitmask(val, &I->visRep, cRepCnt);
824     } else {
825       ok = PConvPyIntToInt(val, &I->visRep);
826     }
827     CPythonVal_Free(val);
828   }
829   if(ok)
830     ok = PConvPyListToFloatArrayInPlaceAutoZero(PyList_GetItem(list, 4), I->ExtentMin, 3);
831   if(ok)
832     ok = PConvPyListToFloatArrayInPlaceAutoZero(PyList_GetItem(list, 5), I->ExtentMax, 3);
833   if(ok)
834     ok = PConvPyIntToInt(PyList_GetItem(list, 6), &I->ExtentFlag);
835   if(ok)
836     ok = PConvPyIntToInt(PyList_GetItem(list, 7), &I->TTTFlag);
837   if(ok)
838     I->Setting = SettingNewFromPyList(G, PyList_GetItem(list, 8));
839   if(ok && (ll > 9))
840     ok = PConvPyIntToInt(PyList_GetItem(list, 9), &I->Enabled);
841   if(ok && (ll > 10))
842     ok = PConvPyIntToInt(PyList_GetItem(list, 10), &I->Context);
843   if(ok && (ll > 11))
844     ok = PConvPyListToFloatArrayInPlaceAutoZero(PyList_GetItem(list, 11), I->TTT, 16);
845   if(ok && (ll > 13)) {
846     PyObject *tmp;
847     int nFrame;
848     VLAFreeP(I->ViewElem);
849     I->ViewElem = NULL;
850     if(ok)
851       ok = PConvPyIntToInt(PyList_GetItem(list, 12), &nFrame);
852     if(ok && nFrame) {
853       tmp = PyList_GetItem(list, 13);
854       if(tmp && !(tmp == Py_None))
855         ok = ViewElemVLAFromPyList(G, tmp, &I->ViewElem, nFrame);
856     }
857   }
858   /* TO SUPPORT BACKWARDS COMPATIBILITY...
859      Always check ll when adding new PyList_GetItem's */
860 
861   return (ok);
862 }
863 
ObjectCopyHeader(CObject * I,const CObject * src)864 int ObjectCopyHeader(CObject * I, const CObject * src)
865 {
866   int ok = true;
867 
868   I->G = src->G;
869   I->type = src->type;
870   UtilNCopy(I->Name, src->Name, WordLength);
871   I->Color = src->Color;
872   I->visRep = src->visRep;
873   copy3f(src->ExtentMin, I->ExtentMin);
874   copy3f(src->ExtentMax, I->ExtentMax);
875 
876   I->ExtentFlag = src->ExtentFlag;
877   I->TTTFlag = src->TTTFlag;
878   I->Setting = NULL;            /* to do */
879   I->Enabled = src->Enabled;
880   I->Context = src->Context;
881   {
882     int a;
883     for(a = 0; a < 16; a++)
884       I->TTT[a] = src->TTT[a];
885   }
886   I->ViewElem = NULL;           /* to do */
887 
888   return (ok);
889 }
890 
891 
892 /*========================================================================*/
ObjectCombineTTT(CObject * I,const float * ttt,int reverse_order,int store)893 void ObjectCombineTTT(CObject * I, const float *ttt, int reverse_order, int store)
894 {
895   if(I->type == cObjectGroup) {
896     ExecutiveGroupCombineTTT(I->G, I, ttt, reverse_order,store);
897   } else {
898     float cpy[16];
899     if(!I->TTTFlag) {
900       I->TTTFlag = true;
901       initializeTTT44f(cpy);
902     } else {
903       UtilCopyMem(cpy, I->TTT, sizeof(float) * 16);
904     }
905     if(reverse_order) {
906       combineTTT44f44f(cpy, ttt, I->TTT);
907     } else {
908       combineTTT44f44f(ttt, cpy, I->TTT);
909     }
910     if(store<0)
911       store = SettingGet_i(I->G, I->Setting, NULL, cSetting_movie_auto_store);
912     if(store && MovieDefined(I->G)) {
913       if(!I->ViewElem)
914         I->ViewElem = pymol::vla<CViewElem>(0);
915       if(I->ViewElem) { /* update motion path waypoint, if active */
916         int frame = SceneGetFrame(I->G);
917         if(frame >= 0) {
918           VLACheck(I->ViewElem, CViewElem, frame);
919           TTTToViewElem(I->TTT, I->ViewElem + frame);
920           I->ViewElem[frame].specification_level = 2;
921         }
922       }
923     }
924   }
925 }
926 /*========================================================================*/
ObjectTranslateTTT(CObject * I,const float * v,int store)927 void ObjectTranslateTTT(CObject * I, const float *v, int store)
928 {
929   if(I->type == cObjectGroup) {
930     ExecutiveGroupTranslateTTT(I->G, I, v, store);
931   } else {
932     if(!I->TTTFlag) {
933       I->TTTFlag = true;
934       initializeTTT44f(I->TTT);
935     }
936     if(v) {
937       I->TTT[3] += v[0];
938       I->TTT[7] += v[1];
939       I->TTT[11] += v[2];
940     }
941     if(store<0)
942       store = SettingGet_i(I->G, I->Setting, NULL, cSetting_movie_auto_store);
943     if(store && MovieDefined(I->G)) {
944       if(!I->ViewElem)
945         I->ViewElem = pymol::vla<CViewElem>(0);
946       if(I->ViewElem) { /* update motion path waypoint, if active */
947         int frame = SceneGetFrame(I->G);
948         if(frame >= 0) {
949           VLACheck(I->ViewElem, CViewElem, frame);
950           TTTToViewElem(I->TTT, I->ViewElem + frame);
951           I->ViewElem[frame].specification_level = 2;
952         }
953       }
954     }
955   }
956 }
957 
958 
959 /*========================================================================*/
ObjectSetTTT(CObject * I,const float * ttt,int state,int store)960 void ObjectSetTTT(CObject * I, const float *ttt, int state, int store)
961 {
962   if(state < 0) {
963     if(ttt) {
964       UtilCopyMem(I->TTT, ttt, sizeof(float) * 16);
965       I->TTTFlag = true;
966     } else {
967       I->TTTFlag = false;
968       return;
969     }
970     if(store<0)
971       store = SettingGet_i(I->G, I->Setting, NULL, cSetting_movie_auto_store);
972     if(store && MovieDefined(I->G)) {
973       if(!I->ViewElem)
974         I->ViewElem = pymol::vla<CViewElem>(0);
975       if(I->ViewElem) { /* update motion path waypoint, if active */
976         int frame = SceneGetFrame(I->G);
977         if(frame >= 0) {
978           VLACheck(I->ViewElem, CViewElem, frame);
979           TTTToViewElem(I->TTT, I->ViewElem + frame);
980           I->ViewElem[frame].specification_level = 2;
981         }
982       }
983     }
984   } else {
985     /* to do */
986   }
987 }
988 
989 /*========================================================================*/
ObjectGetTTT(CObject * I,const float ** ttt,int state)990 int ObjectGetTTT(CObject * I, const float **ttt, int state)
991 {
992   if(state < 0) {
993     if(I->TTTFlag) {
994       *ttt = I->TTT;
995       return 1;
996     } else {
997       *ttt = NULL;
998     }
999 
1000   } else {
1001   }
1002   return 0;
1003 }
1004 
1005 
1006 /*========================================================================*/
ObjectResetTTT(CObject * I,int store)1007 void ObjectResetTTT(CObject * I,int store)
1008 {
1009 
1010   I->TTTFlag = false;
1011   if(store<0)
1012     store = SettingGet_i(I->G, I->Setting, NULL, cSetting_movie_auto_store);
1013   if(store && MovieDefined(I->G)) {
1014     if(!I->ViewElem)
1015       I->ViewElem = pymol::vla<CViewElem>(0);
1016     if(I->ViewElem) { /* update motion path waypoint, if active */
1017       int frame = SceneGetFrame(I->G);
1018       if(frame >= 0) {
1019         identity44f(I->TTT);
1020         VLACheck(I->ViewElem, CViewElem, frame);
1021         TTTToViewElem(I->TTT, I->ViewElem + frame);
1022         I->ViewElem[frame].specification_level = 2;
1023       }
1024     }
1025   }
1026 }
1027 
1028 
1029 /*========================================================================*/
1030 /**
1031  * Get the combined transformation of TTT and state matrix. State matrix is
1032  * only included if `history=true` or `matrix_mode > 0`.
1033  *
1034  * @param state See CObject::getObjectState
1035  * @param history Boolean flag
1036  * @param[out] matrix Homogeneous 4x4 matrix
1037  * @return True if `matrix` was populated
1038  */
ObjectGetTotalMatrix(CObject * I,int state,int history,double * matrix)1039 int ObjectGetTotalMatrix(CObject * I, int state, int history, double *matrix)
1040 {
1041   int result = false;
1042   if(I->TTTFlag) {
1043     convertTTTfR44d(I->TTT, matrix);
1044     result = true;
1045   }
1046 
1047   if (!history) {
1048     history =
1049         SettingGet<int>(I->G, I->Setting, nullptr, cSetting_matrix_mode) > 0;
1050   }
1051 
1052   if (history) {
1053     {
1054       CObjectState* obj_state = I->getObjectState(state);
1055       if (obj_state) {
1056           if(!obj_state->Matrix.empty()) {
1057             const double *state_matrix = obj_state->Matrix.data();
1058             if(result) {
1059               right_multiply44d44d(matrix, state_matrix);
1060             } else {
1061               copy44d(state_matrix, matrix);
1062             }
1063             result = true;
1064           }
1065       }
1066     }
1067   }
1068   return result;
1069 }
1070 
1071 
1072 /*========================================================================*/
ObjectPrepareContext(CObject * I,RenderInfo * info)1073 void ObjectPrepareContext(CObject * I, RenderInfo * info)
1074 {
1075   CRay * ray = info ? info->ray : NULL;
1076 
1077   if(I->ViewElem) {
1078     int frame = SceneGetFrame(I->G);
1079     if(frame >= 0) {
1080       VLACheck(I->ViewElem, CViewElem, frame);
1081 
1082       if(I->Grabbed) {
1083         TTTToViewElem(I->TTT, I->ViewElem + frame);
1084         I->ViewElem[frame].specification_level = 2;
1085       } else {
1086         if(I->ViewElem[frame].specification_level) {
1087           TTTFromViewElem(I->TTT, I->ViewElem + frame);
1088           I->TTTFlag = true;
1089         }
1090         if(I->ViewElem[frame].state_flag) {
1091           SettingCheckHandle(I->G,&I->Setting);
1092           if(I->Setting) {
1093             /* note: this assumes that the state has already been
1094                calculated and can thus be displayed.  How can we
1095                guarantee this to be true? */
1096             SettingSet_i(I->Setting,cSetting_state,I->ViewElem[frame].state + 1);
1097           }
1098         }
1099       }
1100     }
1101   }
1102   if(ray) {
1103     RaySetTTT(ray, I->TTTFlag, I->TTT);
1104   } else {
1105     PyMOLGlobals *G = I->G;
1106     if(G->HaveGUI && G->ValidContext) {
1107       if(I->TTTFlag) {
1108         /* convert the row-major TTT matrix to a column-major OpenGL matrix */
1109         float gl[16], *ttt;
1110 
1111         ttt = I->TTT;
1112         gl[0] = ttt[0];
1113         gl[4] = ttt[1];
1114         gl[8] = ttt[2];
1115         gl[12] = ttt[3];
1116         gl[1] = ttt[4];
1117         gl[5] = ttt[5];
1118         gl[9] = ttt[6];
1119         gl[13] = ttt[7];
1120         gl[2] = ttt[8];
1121         gl[6] = ttt[9];
1122         gl[10] = ttt[10];
1123         gl[14] = ttt[11];
1124         gl[3] = 0.0;
1125         gl[7] = 0.0;
1126         gl[11] = 0.0;
1127         gl[15] = 1.0;
1128 
1129         auto mvm = SceneGetModelViewMatrix(G);
1130         MatrixMultiplyC44f(gl, mvm);
1131         MatrixTranslateC44f(mvm, ttt[12], ttt[13], ttt[14]);
1132 
1133 #ifndef PURE_OPENGL_ES_2
1134         if (ALWAYS_IMMEDIATE_OR(!info->use_shaders)) {
1135           glLoadMatrixf(mvm);
1136         }
1137 #endif
1138       }
1139     }
1140   }
1141 }
1142 
1143 
1144 /*========================================================================*/
ObjectSetTTTOrigin(CObject * I,float * origin)1145 void ObjectSetTTTOrigin(CObject * I, float *origin)
1146 {
1147   float homo[16];
1148   float *dst;
1149   float post[3];
1150 
1151   if(!I->TTTFlag) {
1152     I->TTTFlag = true;
1153     initializeTTT44f(I->TTT);
1154   }
1155 
1156   /* convert the existing TTT into a homogenous transformation matrix */
1157 
1158   convertTTTfR44f(I->TTT, homo);
1159 
1160   /* now reset to the passed-in origin */
1161 
1162   transform44f3fas33f3f(homo, origin, post);
1163 
1164   homo[3] += post[0];
1165   homo[7] += post[1];
1166   homo[11] += post[2];
1167 
1168   dst = homo + 12;
1169 
1170   invert3f3f(origin, dst);
1171 
1172   copy44f(homo, I->TTT);
1173 }
1174 
1175 
1176 /*========================================================================*/
getSettingHandle(int state)1177 CSetting **CObject::getSettingHandle(int state)
1178 {
1179   return &Setting;
1180 }
1181 
1182 
1183 /*========================================================================*/
describeElement(int index,char * buffer) const1184 void CObject::describeElement(int index, char* buffer) const
1185 {
1186   buffer[0] = 0;
1187 }
1188 
1189 
1190 /*========================================================================*/
ObjectToggleRepVis(CObject * I,int rep)1191 void ObjectToggleRepVis(CObject * I, int rep)
1192 {
1193   if((rep >= 0) && (rep < cRepCnt))
1194     I->visRep ^= (1 << rep);
1195 }
1196 
1197 
1198 /*========================================================================*/
ObjectSetRepVisMask(CObject * I,int repmask,int value)1199 void ObjectSetRepVisMask(CObject * I, int repmask, int value)
1200 {
1201   switch (value) {
1202     case cVis_HIDE:
1203       I->visRep &= ~repmask;
1204       break;
1205     case cVis_SHOW:
1206       I->visRep |= repmask;
1207       break;
1208     case cVis_AS:
1209       I->visRep = repmask;
1210       break;
1211     case cVis_TOGGLE:
1212       I->visRep ^= repmask;
1213       break;
1214     default:
1215       printf("error: invalid value: %d\n", value);
1216   }
1217 }
1218 
1219 
1220 /*========================================================================*/
ObjectSetName(CObject * I,const char * name)1221 void ObjectSetName(CObject * I, const char *name)
1222 {
1223   UtilNCopy(I->Name, name, WordLength);
1224   if(SettingGetGlobal_b(I->G, cSetting_validate_object_names))
1225     ObjectMakeValidName(I->G, I->Name);
1226 }
1227 
1228 
1229 /*========================================================================*/
~CObject()1230 CObject::~CObject()
1231 {
1232   SceneObjectDel(this->G, this, false);
1233   SettingFreeP(this->Setting);
1234 }
1235 
1236 
1237 /*========================================================================*/
ObjectUseColor(CObject * I)1238 void ObjectUseColor(CObject * I)
1239 {
1240   PyMOLGlobals *G = I->G;
1241   if(G->HaveGUI && G->ValidContext) {
1242     glColor3fv(ColorGet(I->G, I->Color));
1243   }
1244 }
1245 
ObjectUseColorCGO(CGO * cgo,CObject * I)1246 void ObjectUseColorCGO(CGO *cgo, CObject * I)
1247 {
1248   PyMOLGlobals *G = I->G;
1249   if(G->HaveGUI && G->ValidContext) {
1250     CGOColorv(cgo, ColorGet(I->G, I->Color));
1251   }
1252 }
1253 
1254 /*========================================================================*/
1255 /**
1256  * Render a unit box (dummy representation)
1257  */
render(RenderInfo * info)1258 void CObject::render(RenderInfo * info)
1259 {
1260   if(G->HaveGUI && G->ValidContext) {
1261 #ifdef PURE_OPENGL_ES_2
1262     /* TODO */
1263 #else
1264     glBegin(GL_LINE_LOOP);
1265     glVertex3i(-1, -1, -1);
1266     glVertex3i(-1, -1, 1);
1267     glVertex3i(-1, 1, 1);
1268     glVertex3i(-1, 1, -1);
1269 
1270     glVertex3i(1, 1, -1);
1271     glVertex3i(1, 1, 1);
1272     glVertex3i(1, -1, 1);
1273     glVertex3i(1, -1, -1);
1274     glEnd();
1275 
1276     glBegin(GL_LINES);
1277     glVertex3i(0, 0, 0);
1278     glVertex3i(1, 0, 0);
1279 
1280     glVertex3i(0, 0, 0);
1281     glVertex3i(0, 3, 0);
1282 
1283     glVertex3i(0, 0, 0);
1284     glVertex3i(0, 0, 9);
1285 
1286     glEnd();
1287 #endif
1288   }
1289 }
1290 
1291 
1292 /*========================================================================*/
CObject(PyMOLGlobals * G)1293 CObject::CObject(PyMOLGlobals * G) : G(G)
1294 {
1295   OrthoRemoveSplash(G);         /* HMM... this seems like an inappropriate sideeffect */
1296   visRep = cRepBitmask & ~(cRepCellBit | cRepExtentBit);
1297 }
1298 
1299 /*========================================================================*/
1300 
ObjectStateInit(PyMOLGlobals * G,CObjectState * I)1301 void ObjectStateInit(PyMOLGlobals * G, CObjectState * I)
1302 {
1303   I->G = G;
1304 }
1305 
ObjectStatePurge(CObjectState * I)1306 void ObjectStatePurge(CObjectState * I)
1307 {
1308 }
1309 
ObjectStateSetMatrix(CObjectState * I,double * matrix)1310 int ObjectStateSetMatrix(CObjectState * I, double *matrix)
1311 {
1312   int ok = true;
1313   if(matrix) {
1314     I->Matrix.resize(16);
1315     copy44d(matrix, I->Matrix.data());
1316   } else {
1317     I->Matrix.clear();
1318   }
1319   I->InvMatrix.clear();
1320   return ok;
1321 }
1322 
ObjectStateRightCombineMatrixR44d(CObjectState * I,double * matrix)1323 void ObjectStateRightCombineMatrixR44d(CObjectState * I, double *matrix)
1324 {
1325   if(matrix) {
1326     if(I->Matrix.empty()) {
1327       I->Matrix = std::vector<double>(16);
1328       copy44d(matrix, I->Matrix.data());
1329     } else {
1330       right_multiply44d44d(I->Matrix.data(), matrix);
1331     }
1332   }
1333   I->InvMatrix.clear();
1334 }
1335 
ObjectStateLeftCombineMatrixR44d(CObjectState * I,double * matrix)1336 void ObjectStateLeftCombineMatrixR44d(CObjectState * I, double *matrix)
1337 {
1338   if(matrix) {
1339     if(I->Matrix.empty()) {
1340       I->Matrix = std::vector<double>(16);
1341       copy44d(matrix, I->Matrix.data());
1342     } else {
1343       left_multiply44d44d(matrix, I->Matrix.data());
1344     }
1345   }
1346   I->InvMatrix.clear();
1347 }
1348 
ObjectStateCombineMatrixTTT(CObjectState * I,float * matrix)1349 void ObjectStateCombineMatrixTTT(CObjectState * I, float *matrix)
1350 {
1351 
1352   if(matrix) {
1353     if(I->Matrix.empty()) {
1354       I->Matrix = std::vector<double>(16);
1355       convertTTTfR44d(matrix, I->Matrix.data());
1356     } else {
1357       double tmp[16];
1358       convertTTTfR44d(matrix, tmp);
1359       right_multiply44d44d(I->Matrix.data(), tmp);
1360     }
1361   }
1362   I->InvMatrix.clear();
1363 }
1364 
ObjectStateGetMatrix(CObjectState * I)1365 double *ObjectStateGetMatrix(CObjectState * I)
1366 {
1367   if(!I->Matrix.empty()) {
1368     return I->Matrix.data();
1369   }
1370   return nullptr;
1371 }
1372 
1373 /*
1374  * Get the Matrix inverse
1375  */
ObjectStateGetInvMatrix(CObjectState * I)1376 double *ObjectStateGetInvMatrix(CObjectState * I)
1377 {
1378   if(!I->Matrix.empty() && I->InvMatrix.empty()) {
1379     I->InvMatrix = std::vector<double>(16);
1380     xx_matrix_invert(I->InvMatrix.data(), I->Matrix.data(), 4);
1381   }
1382   return I->InvMatrix.data();
1383 }
1384 
ObjectStateTransformMatrix(CObjectState * I,double * matrix)1385 void ObjectStateTransformMatrix(CObjectState * I, double *matrix)
1386 {
1387   if(I->Matrix.empty()) {
1388     I->Matrix = std::vector<double>(16);
1389     if(!I->Matrix.empty()) {
1390       copy44d(matrix, I->Matrix.data());
1391     }
1392   } else {
1393     right_multiply44d44d(I->Matrix.data(), matrix);
1394   }
1395   I->InvMatrix.clear();
1396 }
1397 
ObjectStatePushAndApplyMatrix(CObjectState * I,RenderInfo * info)1398 int ObjectStatePushAndApplyMatrix(CObjectState * I, RenderInfo * info)
1399 {
1400   PyMOLGlobals *G = I->G;
1401   float matrix[16];
1402   const double *i_matrix = nullptr;
1403   if(!I->Matrix.empty()) {
1404     i_matrix = I->Matrix.data();
1405   }
1406   int result = false;
1407   if(i_matrix) {
1408     if(info->ray) {
1409       float ttt[16], matrix[16], i_matrixf[16];
1410       RayPushTTT(info->ray);
1411       RayGetTTT(info->ray, ttt);
1412       convertTTTfR44f(ttt, matrix);
1413       copy44d44f(i_matrix, i_matrixf);
1414       right_multiply44f44f(matrix, i_matrixf);
1415       RaySetTTT(info->ray, true, matrix);
1416       result = true;
1417     } else if(G->HaveGUI && G->ValidContext) {
1418       matrix[0] = i_matrix[0];
1419       matrix[1] = i_matrix[4];
1420       matrix[2] = i_matrix[8];
1421       matrix[3] = i_matrix[12];
1422       matrix[4] = i_matrix[1];
1423       matrix[5] = i_matrix[5];
1424       matrix[6] = i_matrix[9];
1425       matrix[7] = i_matrix[13];
1426       matrix[8] = i_matrix[2];
1427       matrix[9] = i_matrix[6];
1428       matrix[10] = i_matrix[10];
1429       matrix[11] = i_matrix[14];
1430       matrix[12] = i_matrix[3];
1431       matrix[13] = i_matrix[7];
1432       matrix[14] = i_matrix[11];
1433       matrix[15] = i_matrix[15];
1434 
1435       ScenePushModelViewMatrix(G);
1436       auto mvm = SceneGetModelViewMatrix(G);
1437       MatrixMultiplyC44f(matrix, mvm);
1438 
1439 #ifndef PURE_OPENGL_ES_2
1440       if (ALWAYS_IMMEDIATE_OR(!info->use_shaders)) {
1441         glLoadMatrixf(mvm);
1442       }
1443 #endif
1444 
1445       result = true;
1446     }
1447   }
1448   return result;
1449 }
1450 
ObjectStatePopMatrix(CObjectState * I,RenderInfo * info)1451 void ObjectStatePopMatrix(CObjectState * I, RenderInfo * info)
1452 {
1453   PyMOLGlobals *G = I->G;
1454   if(info->ray) {
1455     RayPopTTT(info->ray);
1456   } else if(G->HaveGUI && G->ValidContext) {
1457     ScenePopModelViewMatrix(G, !info->use_shaders);
1458   }
1459 }
1460 
ObjectStateResetMatrix(CObjectState * I)1461 void ObjectStateResetMatrix(CObjectState* I)
1462 {
1463   I->Matrix.clear();
1464   I->InvMatrix.clear();
1465 }
1466 
ObjectStateAsPyList(CObjectState * I)1467 PyObject *ObjectStateAsPyList(CObjectState * I)
1468 {
1469   PyObject *result = NULL;
1470 
1471   if(I) {
1472     result = PyList_New(1);
1473 
1474     if(!I->Matrix.empty()) {
1475       PyList_SetItem(result, 0, PConvDoubleArrayToPyList(I->Matrix.data(), 16));
1476     } else {
1477       PyList_SetItem(result, 0, PConvAutoNone(Py_None));
1478     }
1479   }
1480   return (PConvAutoNone(result));
1481 }
1482 
ObjectStateFromPyList(PyMOLGlobals * G,PyObject * list,CObjectState * I)1483 int ObjectStateFromPyList(PyMOLGlobals * G, PyObject * list, CObjectState * I)
1484 {
1485   PyObject *tmp;
1486   int ok = true;
1487 
1488   ObjectStateInit(G, I);
1489 
1490   if(list && (list != Py_None)) {       /* allow None */
1491     if(ok)
1492       ok = (list != NULL);
1493     if(ok)
1494       ok = PyList_Check(list);
1495     /* TO SUPPORT BACKWARDS COMPATIBILITY...
1496        Always check ll when adding new PyList_GetItem's */
1497     if(ok) {
1498       tmp = PyList_GetItem(list, 0);
1499       if(tmp != Py_None)
1500         ok = PConvFromPyObject(G, tmp, I->Matrix);
1501     }
1502   }
1503   return (ok);
1504 }
1505