1 /*
2 A* -------------------------------------------------------------------
3 B* This file contains source code for the PyMOL computer program
4 C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
5 D* -------------------------------------------------------------------
6 E* It is unlawful to modify or remove this copyright notice.
7 F* -------------------------------------------------------------------
8 G* Please see the accompanying LICENSE file for further information.
9 H* -------------------------------------------------------------------
10 I* Additional authors of this source file include:
11 -* sc
12 -*
13 -*
14 Z* -------------------------------------------------------------------
15 */
16 
17 
18 #include"os_std.h"
19 #include"os_gl.h"
20 #include"os_python.h"
21 #include"os_numpy.h"
22 
23 #include"Util.h"
24 
25 #include"Word.h"
26 #include"main.h"
27 #include"Base.h"
28 #include"MemoryDebug.h"
29 #include"Err.h"
30 #include"Matrix.h"
31 #include"ListMacros.h"
32 #include"PyMOLObject.h"
33 #include"Scene.h"
34 #include"SceneRay.h"
35 #include"ScenePicking.h"
36 #include"Ortho.h"
37 #include"Vector.h"
38 #include"ButMode.h"
39 #include"Control.h"
40 #include"Selector.h"
41 #include"Setting.h"
42 #include"Movie.h"
43 #include"MyPNG.h"
44 #include"P.h"
45 #include"Editor.h"
46 #include"Executive.h"
47 #include"Wizard.h"
48 #include"CGO.h"
49 #include"ObjectDist.h"
50 #include"ObjectGadget.h"
51 #include"Seq.h"
52 #include"Menu.h"
53 #include"View.h"
54 #include"ObjectSlice.h"
55 #include"Text.h"
56 #include"PyMOLOptions.h"
57 #include"PyMOL.h"
58 #include"PConv.h"
59 #include"ScrollBar.h"
60 #include "ShaderMgr.h"
61 
62 #ifdef _PYMOL_OPENVR
63 #include"OpenVRMode.h"
64 #endif
65 
66 //#define _OPENVR_STEREO_DEBUG_VIEWS
67 
68 #include <string>
69 #include <vector>
70 #include <algorithm>
71 #include <glm/glm.hpp>
72 
glReadBufferError(PyMOLGlobals * G,GLenum b,GLenum e)73 static void glReadBufferError(PyMOLGlobals *G, GLenum b, GLenum e){
74   PRINTFB(G, FB_OpenGL, FB_Warnings)
75     " WARNING: glReadBuffer caused GL error 0x%04x\n", e ENDFB(G);
76 }
77 // TH 2013-11-01: glReadBuffer fails in JyMOL when picking, OSX 10.9, Intel Graphics
78 // for minor cases (i.e., png is called) this might get called outside of the main
79 // thread (from ExecutiveDrawNow()) in this case, just don't call glReadBuffer for
80 // now, it should be ok because i believe it is used to figure out the size of the
81 // 3D window (using SceneImagePrepareImpl) in situations where the size gets changed.
82 #define glReadBuffer(b) {   int e; if (PIsGlutThread()) glReadBuffer(b); \
83     if((e = glGetError())) glReadBufferError(G, b, e); }
84 
85 #define cSliceMin 1.0F
86 
87 #define SceneLineHeight 127
88 #define SceneTopMargin 0
89 #define SceneBottomMargin 3
90 #define SceneLeftMargin 3
91 
92 /* Shared with ShaderMgr */
93 /** Coefficients from: http://3dtv.at/Knowhow/AnaglyphComparison_en.aspx */
94 /** Optimize the look and feel of anaglyph 3D */
95 /* the last mode is the 3x3 identity */
96 // matrices are column major
97 float anaglyphL_constants[6][9] = { { 0.299, 0.000, 0.000, 0.587, 0.000, 0.000, 0.114, 0.000, 0.000 },
98 				    { 0.299, 0.000, 0.000, 0.587, 0.000, 0.000, 0.114, 0.000, 0.000 },
99 				    { 1.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000 },
100 				    { 0.299, 0.000, 0.000, 0.587, 0.000, 0.000, 0.114, 0.000, 0.000 },
101 				    { 0.000, 0.000, 0.000, 0.700, 0.000, 0.000, 0.300, 0.000, 0.000 },
102 				    { 1.000, 0.000, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 1.000 } };
103 
104 float anaglyphR_constants[6][9] = { { 0.000, 0.000, 0.299, 0.000, 0.000, 0.587, 0.000, 0.000, 0.114 },
105 				    { 0.000, 0.299, 0.299, 0.000, 0.587, 0.587, 0.000, 0.114, 0.114 },
106 				    { 0.000, 0.000, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 1.000 },
107 				    { 0.000, 0.000, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 1.000 },
108 				    { 0.000, 0.000, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 1.000 },
109 				    { 1.000, 0.000, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 1.000 } };
110 
111 #define F2UI(a) (unsigned int) ((a) * 255.0)
112 
113 
114 /* allow up to 10 seconds at 30 FPS */
115 
116 /* EXPERIMENTAL VOLUME RAYTRACING DATA */
117 extern float *rayDepthPixels;
118 extern int rayVolume, rayWidth, rayHeight;
119 
120 static void SceneDoRoving(PyMOLGlobals * G, float old_front,
121                           float old_back, float old_origin,
122                           int adjust_flag, int zoom_flag);
123 
124 #define SceneGetExactScreenVertexScale SceneGetScreenVertexScale
125 
126 static void SceneRestartPerfTimer(PyMOLGlobals * G);
127 static void SceneRotateWithDirty(PyMOLGlobals * G, float angle, float x, float y, float z,
128                                  int dirty);
129 static void SceneClipSetWithDirty(PyMOLGlobals * G, float front, float back, int dirty);
130 
SceneViewEqual(SceneViewType left,SceneViewType right)131 int SceneViewEqual(SceneViewType left, SceneViewType right)
132 {
133   int i;
134   for(i = 0; i < cSceneViewSize; i++) {
135     if(fabs(left[i] - right[i]) > R_SMALL4)
136       return false;
137   }
138   return true;
139 }
140 
GridSetRayViewport(GridInfo * I,int slot,int * x,int * y,int * width,int * height)141 void GridSetRayViewport(GridInfo * I, int slot, int *x, int *y, int *width,
142                         int *height)
143 {
144   if(slot)
145     I->slot = slot + I->first_slot - 1;
146   else
147     I->slot = slot;
148   /* if we are in grid mode, then prepare the grid slot viewport */
149   if(slot < 0) {
150     *x = I->cur_view[0];
151     *y = I->cur_view[1];
152     *width = I->cur_view[2];
153     *height = I->cur_view[3];
154   } else if(!slot) {
155     int vx = 0;
156     int vw = I->cur_view[2] / I->n_col;
157     int vy = 0;
158     int vh = I->cur_view[3] / I->n_row;
159     if(I->n_col < I->n_row) {
160       vw *= I->n_col;
161       vh *= I->n_col;
162     } else {
163       vw *= I->n_row;
164       vh *= I->n_row;
165     }
166     vx += I->cur_view[0] + (I->cur_view[2] - vw) / 2;
167     vy += I->cur_view[1];
168     *x = vx;
169     *y = vy;
170     *width = vw;
171     *height = vh;
172   } else {
173     int abs_grid_slot = slot - I->first_slot;
174     int grid_col = abs_grid_slot % I->n_col;
175     int grid_row = (abs_grid_slot / I->n_col);
176     int vx = (grid_col * I->cur_view[2]) / I->n_col;
177     int vw = ((grid_col + 1) * I->cur_view[2]) / I->n_col - vx;
178     int vy = I->cur_view[3] - ((grid_row + 1) * I->cur_view[3]) / I->n_row;
179     int vh = (I->cur_view[3] - ((grid_row) * I->cur_view[3]) / I->n_row) - vy;
180     vx += I->cur_view[0];
181     vy += I->cur_view[1];
182     *x = vx;
183     *y = vy;
184     *width = vw;
185     *height = vh;
186   }
187 }
188 
GridGetRayViewport(GridInfo * I,int width,int height)189 void GridGetRayViewport(GridInfo * I, int width, int height)
190 {
191   I->cur_view[0] = 0;
192   I->cur_view[1] = 0;
193   I->cur_view[2] = width;
194   I->cur_view[3] = height;
195 }
196 
GridUpdate(GridInfo * I,float asp_ratio,int mode,int size)197 void GridUpdate(GridInfo * I, float asp_ratio, int mode, int size)
198 {
199   if(mode) {
200     I->size = size;
201     I->mode = mode;
202     {
203       int n_row = 1;
204       int n_col = 1;
205       int r_size = size;
206       while((n_row * n_col) < r_size) {
207         float asp1 = asp_ratio * (n_row + 1.0) / n_col;
208         float asp2 = asp_ratio * (n_row) / (n_col + 1.0);
209         if(asp1 < 1.0F)
210           asp1 = 1.0 / asp1;
211         if(asp2 < 1.0F)
212           asp2 = 1.0 / asp2;
213         if(fabs(asp1) > fabs(asp2))
214           n_col++;
215         else
216           n_row++;
217       }
218       I->n_row = n_row;
219       I->n_col = n_col;
220     }
221     if(I->size > 1) {
222       I->active = true;
223       I->asp_adjust = (float) I->n_row / I->n_col;
224       I->first_slot = 1;
225       I->last_slot = I->size;
226     } else {
227       I->active = false;
228     }
229   } else {
230     I->active = false;
231   }
232 }
233 
SceneInvalidateStencil(PyMOLGlobals * G)234 void SceneInvalidateStencil(PyMOLGlobals * G)
235 {
236   CScene *I = G->Scene;
237   I->StencilValid = false;
238 }
239 
SceneGetGridSize(PyMOLGlobals * G,int grid_mode)240 int SceneGetGridSize(PyMOLGlobals * G, int grid_mode)
241 {
242   CScene *I = G->Scene;
243   int slot;
244   int size = 0;
245 
246   switch (grid_mode) {
247   case 1:
248     if(!I->SlotVLA)
249       I->SlotVLA = VLACalloc(int, 1);
250     else {
251       UtilZeroMem(I->SlotVLA, sizeof(int) * VLAGetSize(I->SlotVLA));
252     }
253     {
254       int max_slot = 0;
255       for ( auto it = I->Obj.begin(); it != I->Obj.end(); ++it) {
256         if((slot = (*it)->grid_slot)) {
257           if(max_slot < slot)
258             max_slot = slot;
259           if(slot > 0) {
260             VLACheck(I->SlotVLA, int, slot);
261             I->SlotVLA[slot] = 1;
262           }
263         }
264       }
265       for(slot = 0; slot <= max_slot; slot++) {
266         if(I->SlotVLA[slot])
267           I->SlotVLA[slot] = ++size;
268       }
269     }
270     break;
271   case 2:
272   case 3:
273     if(I->SlotVLA) {
274       VLAFreeP(I->SlotVLA);
275       I->SlotVLA = NULL;
276     }
277     {
278       int max_slot = 0;
279       for (auto& obj : I->Obj) {
280           slot = obj->getNFrame();
281           if(grid_mode == 3) {
282             obj->grid_slot = max_slot; // slot offset for 1st state
283             max_slot += slot;
284           } else if(max_slot < slot) {
285             max_slot = slot;
286           }
287       }
288       size = max_slot;
289     }
290     break;
291   }
292   {
293     int grid_max = SettingGetGlobal_i(G, cSetting_grid_max);
294     if(grid_max >= 0)
295       if(size > grid_max)
296         size = grid_max;
297   }
298   return size;
299 }
300 
SceneHasImage(PyMOLGlobals * G)301 int SceneHasImage(PyMOLGlobals * G)
302 {
303   CScene *I = G->Scene;
304   return (I->Image && !I->Image->empty());
305 }
306 
SceneMustDrawBoth(PyMOLGlobals * G)307 int SceneMustDrawBoth(PyMOLGlobals * G)
308 {
309   CScene *I = G->Scene;
310   return (G->StereoCapable &&
311           ((I->StereoMode == 1) ||
312            SettingGetGlobal_b(G, cSetting_stereo_double_pump_mono)));
313 }
314 
315 static int SceneDeferClickWhen(Block * block, int button, int x, int y, double when,
316                                int mod);
317 
stereo_via_adjacent_array(int stereo_mode)318 static int stereo_via_adjacent_array(int stereo_mode)
319 {
320   switch (stereo_mode) {
321   case cStereo_crosseye:
322   case cStereo_walleye:
323   case cStereo_sidebyside:
324     return true;
325   }
326   return false;
327 }
328 
StereoIsAdjacent(PyMOLGlobals * G)329 int StereoIsAdjacent(PyMOLGlobals * G){
330   CScene *I = G->Scene;
331   return stereo_via_adjacent_array(I->StereoMode);
332 }
333 
get_stereo_x(int x,int * last_x,int width,int * click_side)334 static int get_stereo_x(int x, int *last_x, int width, int *click_side)
335 {
336   int width_2 = width / 2;
337   int width_3 = width / 3;
338   if(!last_x) {
339     if(x > width_2) {
340       x -= width_2;
341       if(click_side)
342         *click_side = 1;        /* right */
343     } else {
344       if(click_side)
345         *click_side = -1;       /* left */
346     }
347   } else {
348     if((x - (*last_x)) > width_3) {
349       x -= width_2;
350       if(click_side)
351         *click_side = 1;        /* right */
352     } else if(((*last_x) - x) > width_3) {
353       x += width_2;
354       if(click_side)
355         *click_side = 1;        /* right */
356     } else {
357       if(click_side)
358         *click_side = -1;       /* left */
359     }
360   }
361   return x;
362 }
363 
SceneAbortAnimation(PyMOLGlobals * G)364 void SceneAbortAnimation(PyMOLGlobals * G)
365 {
366   CScene *I = G->Scene;
367   if(I->cur_ani_elem < I->n_ani_elem) { /* allow user to override animation */
368     I->cur_ani_elem = I->n_ani_elem;
369   }
370 }
371 
ScenePrimeAnimation(PyMOLGlobals * G)372 void ScenePrimeAnimation(PyMOLGlobals * G)
373 {
374   if(G->HaveGUI) {
375     CScene *I = G->Scene;
376     UtilZeroMem(I->ani_elem, sizeof(CViewElem));
377     SceneToViewElem(G, I->ani_elem, NULL);
378     I->ani_elem[0].specification_level = 2;
379     I->n_ani_elem = 0;
380   }
381 }
382 
SceneGetFPS(PyMOLGlobals * G)383 static float SceneGetFPS(PyMOLGlobals * G)
384 {
385   float fps = SettingGetGlobal_f(G, cSetting_movie_fps);
386   float minTime;
387   if(fps <= 0.0F) {
388     if(fps < 0.0)
389       minTime = 0.0;            /* negative fps means full speed */
390     else                        /* 0 fps means use movie_delay instead */
391       minTime = SettingGetGlobal_f(G, cSetting_movie_delay) / 1000.0;
392     if(minTime >= 0.0F)
393       fps = 1.0F / minTime;
394     else
395       fps = 1000.0F;
396   }
397   return fps;
398 }
399 
400 /**
401  * Release `G->Scene->Image` and clear related flags (`CopyType`).
402  *
403  * Invalidates the Ortho CGO and requests a redisplay. It's not entirely clear
404  * why this is done, a code comment says "need to invalidate since text could be
405  * shown".
406  */
ScenePurgeImage(PyMOLGlobals * G)407 static void ScenePurgeImage(PyMOLGlobals * G)
408 {
409   CScene *I = G->Scene;
410   I->CopyType = false;
411   I->Image = nullptr;
412 
413   // TODO does this belong here?
414   if (true /* !noinvalid */)
415     OrthoInvalidateDoDraw(G); // right now, need to invalidate since text could be shown
416 }
417 
SceneInvalidateCopy(PyMOLGlobals * G,int free_buffer)418 void SceneInvalidateCopy(PyMOLGlobals * G, int free_buffer)
419 {
420   CScene *I = G->Scene;
421   if(I) {
422     if(free_buffer){
423       ScenePurgeImage(G);
424     }
425     else{
426       I->Image = nullptr;
427     }
428     if (I->CopyType)
429       OrthoInvalidateDoDraw(G); // right now, need to invalidate since text could be shown
430     I->CopyType = false;
431   }
432 }
433 
SceneInvalidate(PyMOLGlobals * G)434 void SceneInvalidate(PyMOLGlobals * G)
435 {
436   SceneInvalidateCopy(G, false);
437   SceneDirty(G);
438   PyMOL_NeedRedisplay(G->PyMOL);
439 }
440 
SceneLoadAnimation(PyMOLGlobals * G,double duration,int hand)441 void SceneLoadAnimation(PyMOLGlobals * G, double duration, int hand)
442 {
443   if(G->HaveGUI) {
444     double now;
445     int target = (int) (duration * 30);
446     CScene *I = G->Scene;
447     if(target < 1)
448       target = 1;
449     if(target > MAX_ANI_ELEM)
450       target = MAX_ANI_ELEM;
451     UtilZeroMem(I->ani_elem + 1, sizeof(CViewElem) * target);
452     SceneToViewElem(G, I->ani_elem + target, NULL);
453     I->ani_elem[target].specification_level = 2;
454     now = UtilGetSeconds(G);
455     I->ani_elem[0].timing_flag = true;
456     I->ani_elem[0].timing = now + 0.01;
457     I->ani_elem[target].timing_flag = true;
458     I->ani_elem[target].timing = now + duration;
459     ViewElemInterpolate(G, I->ani_elem, I->ani_elem + target,
460                         2.0F, 1.0F, true, 0.0F, hand, 0.0F);
461     SceneFromViewElem(G, I->ani_elem, true);
462     I->cur_ani_elem = 0;
463     I->n_ani_elem = target;
464     I->AnimationStartTime = UtilGetSeconds(G);
465     I->AnimationStartFlag = true;
466     I->AnimationStartFrame = SceneGetFrame(G);
467     I->AnimationLagTime = 0.0;
468   }
469 }
470 
471 static
SceneLoopClick(Block * block,int button,int x,int y,int mod)472 int SceneLoopClick(Block * block, int button, int x, int y, int mod)
473 {
474   PyMOLGlobals *G = block->m_G;
475   CScene *I = G->Scene;
476   I->LoopRect.left = x;
477   I->LoopRect.top = y;
478   I->LoopRect.right = x;
479   I->LoopRect.bottom = y;
480   I->LoopFlag = true;
481   I->LoopMod = mod;
482   OrthoSetLoopRect(G, true, &I->LoopRect);
483   OrthoGrab(G, block);
484   return 1;
485 }
486 
487 static
SceneLoopDrag(Block * block,int x,int y,int mod)488 int SceneLoopDrag(Block * block, int x, int y, int mod)
489 {
490   PyMOLGlobals *G = block->m_G;
491   CScene *I = G->Scene;
492   I->LoopRect.right = x;
493   I->LoopRect.bottom = y;
494   OrthoSetLoopRect(G, true, &I->LoopRect);
495   return 1;
496 }
497 
498 static
SceneLoopRelease(Block * block,int button,int x,int y,int mod)499 int SceneLoopRelease(Block * block, int button, int x, int y, int mod)
500 {
501   PyMOLGlobals *G = block->m_G;
502   CScene *I = G->Scene;
503   int tmp;
504   int mode;
505   mode = ButModeTranslate(G, button, I->LoopMod);
506 
507   if(I->LoopRect.top < I->LoopRect.bottom) {
508     tmp = I->LoopRect.top;
509     I->LoopRect.top = I->LoopRect.bottom;
510     I->LoopRect.bottom = tmp;
511   }
512   if(I->LoopRect.right < I->LoopRect.left) {
513     tmp = I->LoopRect.right;
514     I->LoopRect.right = I->LoopRect.left;
515     I->LoopRect.left = tmp;
516   }
517   OrthoSetLoopRect(G, false, &I->LoopRect);
518   ExecutiveSelectRect(G, &I->LoopRect, mode);
519   I->LoopFlag = false;
520   OrthoUngrab(G);
521   OrthoDirty(G);
522   return 1;
523 }
524 
525 /**
526  * Set FrontSafe and BackSafe
527  */
UpdateFrontBackSafe(CScene * I)528 static void UpdateFrontBackSafe(CScene *I)
529 {
530   float front = I->m_view.m_clip.m_front;
531   float back = I->m_view.m_clip.m_back;
532 
533   // minimum slab
534   if(back - front < cSliceMin) {
535     float avg = (back + front) / 2.0;
536     back = avg + cSliceMin / 2.0;
537     front = avg - cSliceMin / 2.0;
538   }
539 
540   // minimum front
541   if (front < cSliceMin) {
542     front = cSliceMin;
543 
544     // minimum slab
545     if (back < front + cSliceMin)
546       back = front + cSliceMin;
547   }
548 
549   I->m_view.m_clipSafe.m_front = front;
550   I->m_view.m_clipSafe.m_back = back;
551 }
552 
553 #define SELE_MODE_MAX 7
554 
555 static const char SelModeKW[][20] = {
556   "",
557   "byresi",
558   "bychain",
559   "bysegi",
560   "byobject",
561   "bymol",
562   "bca.",
563 };
564 
SceneUpdateInvMatrix(PyMOLGlobals * G)565 static void SceneUpdateInvMatrix(PyMOLGlobals * G)
566 {
567   CScene *I = G->Scene;
568   float *rm = I->m_view.m_rotMatrix;
569   float *im = I->InvMatrix;
570   im[0] = rm[0];
571   im[1] = rm[4];
572   im[2] = rm[8];
573   im[3] = 0.0F;
574   im[4] = rm[1];
575   im[5] = rm[5];
576   im[6] = rm[9];
577   im[7] = 0.0F;
578   im[8] = rm[2];
579   im[9] = rm[6];
580   im[10] = rm[10];
581   im[11] = 0.0F;
582   im[12] = 0.0F;
583   im[13] = 0.0F;
584   im[14] = 0.0F;
585   im[15] = 1.0F;
586 }
587 
SceneUpdateStereo(PyMOLGlobals * G)588 void SceneUpdateStereo(PyMOLGlobals * G)
589 {
590   SceneSetStereo(G, SettingGetGlobal_b(G, cSetting_stereo));
591   PyMOL_NeedRedisplay(G->PyMOL);
592 }
593 
594 /**
595  * Get the selection operator for the `mouse_selection_mode` setting.
596  * Example: `mouse_selection_mode=2` -> "bychain"
597  */
SceneGetSeleModeKeyword(PyMOLGlobals * G)598 const char *SceneGetSeleModeKeyword(PyMOLGlobals * G)
599 {
600   int sel_mode = SettingGetGlobal_i(G, cSetting_mouse_selection_mode);
601   if((sel_mode >= 0) && (sel_mode < SELE_MODE_MAX))
602     return (char *) SelModeKW[sel_mode];
603   return (char *) SelModeKW[0];
604 }
605 
606 #ifdef _PYMOL_OPENVR
607 static float s_oldFov = -1.0f;
608 #endif
609 
SceneToViewElem(PyMOLGlobals * G,CViewElem * elem,const char * scene_name)610 void SceneToViewElem(PyMOLGlobals * G, CViewElem * elem, const char *scene_name)
611 {
612   float *fp;
613   double *dp;
614   CScene *I = G->Scene;
615 
616   float dY = 0, dZ = 0;
617   float fov = SettingGetGlobal_f(G, cSetting_field_of_view);
618   float scale = 1.0f / I->Scale;
619 
620 #ifdef _PYMOL_OPENVR
621   if (I->StereoMode == cStereo_openvr) {
622     float dist = fabsf(I->m_view.m_pos[2]);
623     float fovVR = fov;
624     fov = s_oldFov;
625     dY = scale * 1.0f;
626     dZ = scale * dist * (tanf(fovVR * PI / 360.f) / tanf(fov * PI / 360.f) - 1.0f);
627   }
628 #endif
629 
630   /* copy rotation matrix */
631   elem->matrix_flag = true;
632   dp = elem->matrix;
633   fp = I->m_view.m_rotMatrix;
634   *(dp++) = (double) *(fp++);
635   *(dp++) = (double) *(fp++);
636   *(dp++) = (double) *(fp++);
637   *(dp++) = (double) *(fp++);
638 
639   *(dp++) = (double) *(fp++);
640   *(dp++) = (double) *(fp++);
641   *(dp++) = (double) *(fp++);
642   *(dp++) = (double) *(fp++);
643 
644   *(dp++) = (double) *(fp++);
645   *(dp++) = (double) *(fp++);
646   *(dp++) = (double) *(fp++);
647   *(dp++) = (double) *(fp++);
648 
649   *(dp++) = 0.0F;
650   *(dp++) = 0.0F;
651   *(dp++) = 0.0F;
652   *(dp++) = 1.0F;
653 
654   /* copy position */
655   elem->pre_flag = true;
656   dp = elem->pre;
657   fp = I->m_view.m_pos;
658   *(dp++) = (double) *(fp++) * scale;
659   *(dp++) = (double) *(fp++) * scale - dY;
660   *(dp++) = (double) *(fp++) * scale - dZ;
661 
662   /* copy origin (negative) */
663   elem->post_flag = true;
664   dp = elem->post;
665   fp = I->m_view.m_origin;
666   *(dp++) = (double) (-*(fp++));
667   *(dp++) = (double) (-*(fp++));
668   *(dp++) = (double) (-*(fp++));
669 
670   elem->clip_flag = true;
671   elem->front = I->m_view.m_clip.m_front * scale + dZ;
672   elem->back = I->m_view.m_clip.m_back * scale + dZ;
673 
674   elem->ortho_flag = true;
675   elem->ortho = SettingGetGlobal_b(G, cSetting_ortho) ? fov : -fov;
676 
677   {
678     if(elem->scene_flag && elem->scene_name) {
679       OVLexicon_DecRef(G->Lexicon, elem->scene_name);
680       elem->scene_name = 0;
681       elem->scene_flag = 0;
682     }
683   }
684   {
685     if(!scene_name)
686       scene_name = SettingGetGlobal_s(G, cSetting_scene_current_name);
687     if(scene_name && scene_name[0]) {
688       OVreturn_word result = OVLexicon_GetFromCString(G->Lexicon, scene_name);
689       if(OVreturn_IS_OK(result)) {
690         elem->scene_name = result.word;
691         elem->scene_flag = true;
692       }
693     }
694   }
695 
696 #ifdef _OPENVR_STEREO_DEBUG_VIEWS
697   printf("%-20s IP: %11lf %11lf %11lf, IF/IB: %11lf %11lf, IS: %11lf, IV: %11lf  ==>  EP: %11lf %11lf %11lf, EF/EB: %11lf %11lf, EV: %11lf\n",
698     "SceneToViewElem",
699     I->m_view.m_pos[0], I->m_view.m_pos[1], I->m_view.m_pos[2],
700     I->m_view.m_clip.m_front, I->m_view.m_clip.m_back, I->Scale,
701     SettingGetGlobal_f(G, cSetting_field_of_view),
702     elem->pre[0], elem->pre[1], elem->pre[2],
703     elem->front, elem->back,
704     elem->ortho
705   );
706 #endif // _OPENVR_STEREO_DEBUG_VIEWS
707 }
708 
SceneFromViewElem(PyMOLGlobals * G,CViewElem * elem,int dirty)709 void SceneFromViewElem(PyMOLGlobals * G, CViewElem * elem, int dirty)
710 {
711   CScene *I = G->Scene;
712   float *fp;
713   double *dp;
714   int changed_flag = false;
715 
716   float dY = 0, dZ = 0;
717   float fov = elem->ortho;
718   float scale = I->Scale;
719 
720 #ifdef _PYMOL_OPENVR
721   if (I->StereoMode == cStereo_openvr) {
722     float dist = fabsf(elem->pre[2]);
723     float fovVR = SettingGetGlobal_f(G, cSetting_field_of_view);
724     dY = -1.0f;
725     dZ = scale * dist * (tanf(fabsf(fov) * PI / 360.f) / tanf(fovVR * PI / 360.f) - 1.0f);
726     fov = fov < 0.0f ? -fovVR : fovVR;
727   }
728 #endif
729 
730   if(elem->matrix_flag) {
731     dp = elem->matrix;
732     fp = I->m_view.m_rotMatrix;
733 
734     *(fp++) = (float) *(dp++);
735     *(fp++) = (float) *(dp++);
736     *(fp++) = (float) *(dp++);
737     *(fp++) = (float) *(dp++);
738 
739     *(fp++) = (float) *(dp++);
740     *(fp++) = (float) *(dp++);
741     *(fp++) = (float) *(dp++);
742     *(fp++) = (float) *(dp++);
743 
744     *(fp++) = (float) *(dp++);
745     *(fp++) = (float) *(dp++);
746     *(fp++) = (float) *(dp++);
747     *(fp++) = (float) *(dp++);
748 
749     *(fp++) = (float) *(dp++);
750     *(fp++) = (float) *(dp++);
751     *(fp++) = (float) *(dp++);
752     *(fp++) = (float) *(dp++);
753     changed_flag = true;
754     SceneUpdateInvMatrix(G);
755   }
756 
757   if(elem->pre_flag) {
758     dp = elem->pre;
759     fp = I->m_view.m_pos;
760     *(fp++) = (float) *(dp++) * scale;
761     *(fp++) = (float) *(dp++) * scale - dY;
762     *(fp++) = (float) *(dp++) * scale - dZ;
763     changed_flag = true;
764   }
765 
766   if(elem->post_flag) {
767     dp = elem->post;
768     fp = I->m_view.m_origin;
769     *(fp++) = (float) (-*(dp++));
770     *(fp++) = (float) (-*(dp++));
771     *(fp++) = (float) (-*(dp++));
772     changed_flag = true;
773   }
774 
775   if(elem->clip_flag) {
776     SceneClipSetWithDirty(G, elem->front * scale + dZ, elem->back * scale + dZ, dirty);
777   }
778 
779   if(elem->ortho_flag) {
780     if(fov < 0.0F) {
781       SettingSetGlobal_b(G, cSetting_ortho, 0);
782       if(fov < -(1.0F - R_SMALL4)) {
783         SettingSetGlobal_f(G, cSetting_field_of_view, -fov);
784       }
785     } else {
786       SettingSetGlobal_b(G, cSetting_ortho, (fov > 0.5F));
787       if(fov > (1.0F + R_SMALL4)) {
788         SettingSetGlobal_f(G, cSetting_field_of_view, fov);
789       }
790     }
791   }
792   if(elem->state_flag&&!MovieDefined(G)) {
793     SettingSetGlobal_i(G, cSetting_state, (elem->state)+1);
794   }
795   if(changed_flag) {
796     SceneRestartSweepTimer(G);
797     I->RockFrame = 0;
798     SceneRovingDirty(G);
799   }
800 
801 #ifdef _OPENVR_STEREO_DEBUG_VIEWS
802   printf("%-20s IP: %11lf %11lf %11lf, IF/IB: %11lf %11lf, IS: %11lf, IV: %11lf  <==  EP: %11lf %11lf %11lf, EF/EB: %11lf %11lf, EV: %11lf\n",
803     "SceneFromViewElem",
804     I->m_view.m_pos[0], I->m_view.m_pos[1], I->m_view.m_pos[2],
805     I->m_view.m_clip.m_front, I->m_view.m_clip.m_back, I->Scale,
806     SettingGetGlobal_f(G, cSetting_field_of_view),
807     elem->pre[0], elem->pre[1], elem->pre[2],
808     elem->front, elem->back,
809     elem->ortho
810   );
811 #endif // _OPENVR_STEREO_DEBUG_VIEWS
812 }
813 
ScenePrepareUnitContext(SceneUnitContext * context,int width,int height)814 void ScenePrepareUnitContext(SceneUnitContext * context, int width, int height)
815 {
816   float tw = 1.0F;
817   float th = 1.0F;
818   float aspRat;
819 
820   if(height) {
821     aspRat = width / (float) height;
822   } else {
823     aspRat = 1.0F;
824   }
825 
826   if(aspRat > 1.0F) {
827     tw = aspRat;
828   } else {
829     th = 1.0F / aspRat;
830   }
831 
832   context->unit_left = (1.0F - tw) / 2;
833   context->unit_right = (tw + 1.0F) / 2;
834   context->unit_top = (1.0F - th) / 2;
835   context->unit_bottom = (th + 1.0F) / 2;
836   context->unit_front = -0.5F;
837   context->unit_back = 0.5F;
838   /*
839      printf(
840      "ScenePrepareUnitContext:%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f\n",
841      context->unit_left,
842      context->unit_right,
843      context->unit_top,
844      context->unit_bottom,
845      context->unit_front,
846      context->unit_back);
847    */
848 }
849 
850 /**
851  * cmd.get_viewport()
852  */
SceneGetWidthHeight(PyMOLGlobals * G,int * width,int * height)853 void SceneGetWidthHeight(PyMOLGlobals * G, int *width, int *height)
854 {
855   CScene *I = G->Scene;
856   *width = I->Width;
857   *height = I->Height;
858 }
859 
860 /**
861  * Get the actual current (sub-)viewport size, considering grid mode and
862  * side-by-side stereo
863  */
SceneGetWidthHeightStereo(PyMOLGlobals * G,int * width,int * height)864 void SceneGetWidthHeightStereo(PyMOLGlobals * G, int *width, int *height)
865 {
866   CScene *I = G->Scene;
867 
868   if (I->grid.active) {
869     // TODO: this considers "draw W, H" (PYMOL-2775)
870     *width = I->grid.cur_viewport_size[0];
871     *height = I->grid.cur_viewport_size[1];
872     return;
873   }
874 
875   // TODO: this does NOT consider "draw W, H" (PYMOL-2775)
876   *width = I->Width;
877   *height = I->Height;
878   if (stereo_via_adjacent_array(I->StereoMode))
879     *width /= 2.f;
880 }
881 
SceneSetCardInfo(PyMOLGlobals * G,const char * vendor,const char * renderer,const char * version)882 void SceneSetCardInfo(PyMOLGlobals * G,
883     const char *vendor,
884     const char *renderer,
885     const char *version)
886 {
887   CScene *I = G->Scene;
888   if (!vendor) vendor = "(null)";
889   if (!renderer) renderer = "(null)";
890   if (!version) version = "(null)";
891   UtilNCopy(I->vendor, vendor, sizeof(OrthoLineType) - 1);
892   UtilNCopy(I->renderer, renderer, sizeof(OrthoLineType) - 1);
893   UtilNCopy(I->version, version, sizeof(OrthoLineType) - 1);
894 }
895 
SceneGetStereo(PyMOLGlobals * G)896 int SceneGetStereo(PyMOLGlobals * G)
897 {
898   CScene *I = G->Scene;
899   return (I->StereoMode);
900 }
901 
SceneGetCardInfo(PyMOLGlobals * G,char ** vendor,char ** renderer,char ** version)902 void SceneGetCardInfo(PyMOLGlobals * G, char **vendor, char **renderer, char **version)
903 {
904   CScene *I = G->Scene;
905   (*vendor) = I->vendor;
906   (*renderer) = I->renderer;
907   (*version) = I->version;
908 }
909 
SceneSuppressMovieFrame(PyMOLGlobals * G)910 void SceneSuppressMovieFrame(PyMOLGlobals * G)
911 {
912   CScene *I = G->Scene;
913   I->MovieFrameFlag = false;
914 }
915 
916 /**
917  * Get center of screen in world coordinates
918  *
919  * @param[out] pos 3f output vector
920  */
SceneGetCenter(PyMOLGlobals * G,float * pos)921 void SceneGetCenter(PyMOLGlobals * G, float *pos)
922 {
923   CScene *I = G->Scene;
924 
925   MatrixTransformC44fAs33f3f(I->m_view.m_rotMatrix, I->m_view.m_origin, pos);
926 
927   pos[0] -= I->m_view.m_pos[0];
928   pos[1] -= I->m_view.m_pos[1];
929 
930   MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, pos, pos);
931 }
932 
933 /*========================================================================*/
SceneGetNFrame(PyMOLGlobals * G,int * has_movie)934 int SceneGetNFrame(PyMOLGlobals * G, int *has_movie)
935 {
936   CScene *I = G->Scene;
937   if(has_movie)
938     *has_movie = I->HasMovie;
939   return (I->NFrame);
940 }
941 
942 /*========================================================================*/
943 /**
944    Get information required to define the geometry
945    of a particular view, for shipping to and from python
946    as a list of floats
947    @verbatim
948    0-15 = 4x4 rotation matrix
949    16-18 = position
950    19-21 = origin
951    22    = front plane
952    23    = rear plane
953    24    = orthoscopic flag
954    @endverbatim
955    @param[out] view buffer to fill
956 */
SceneGetView(PyMOLGlobals * G,SceneViewType view)957 void SceneGetView(PyMOLGlobals * G, SceneViewType view)
958 {
959   float *p;
960   int a;
961   CScene *I = G->Scene;
962   p = view;
963 
964   float dY = 0, dZ = 0;
965   float fov = SettingGetGlobal_f(G, cSetting_field_of_view);
966   float scale = 1.0f / I->Scale;
967 
968 #ifdef _PYMOL_OPENVR
969   if (I->StereoMode == cStereo_openvr) {
970     float dist = fabsf(I->m_view.m_pos[2]);
971     float fovVR = fov;
972     fov = s_oldFov;
973     dY = scale * 1.0f;
974     dZ = scale * dist * (tanf(fovVR * PI / 360.f) / tanf(fov * PI / 360.f) - 1.0f);
975   }
976 #endif
977 
978   for(a = 0; a < 16; a++)
979     *(p++) = I->m_view.m_rotMatrix[a];
980   *(p++) = I->m_view.m_pos[0] * scale;
981   *(p++) = I->m_view.m_pos[1] * scale - dY;
982   *(p++) = I->m_view.m_pos[2] * scale - dZ;
983   *(p++) = I->m_view.m_origin[0];
984   *(p++) = I->m_view.m_origin[1];
985   *(p++) = I->m_view.m_origin[2];
986   *(p++) = I->m_view.m_clip.m_front * scale + dZ;
987   *(p++) = I->m_view.m_clip.m_back * scale + dZ;
988   *(p++) = SettingGetGlobal_b(G, cSetting_ortho) ? fov : -fov;
989 
990 #ifdef _OPENVR_STEREO_DEBUG_VIEWS
991   printf("%-20s IP: %11lf %11lf %11lf, IF/IB: %11lf %11lf, IS: %11lf, IV: %11lf  ==>  EP: %11lf %11lf %11lf, EF/EB: %11lf %11lf, EV: %11lf\n",
992     "SceneGetView",
993     I->m_view.m_pos[0], I->m_view.m_pos[1], I->m_view.m_pos[2],
994     I->m_view.m_clip.m_front, I->m_view.m_clip.m_back, I->Scale,
995     SettingGetGlobal_f(G, cSetting_field_of_view),
996     view[16], view[17], view[18],
997     view[22], view[23],
998     view[24]
999   );
1000 #endif // _OPENVR_STEREO_DEBUG_VIEWS
1001 }
1002 
1003 /*========================================================================*/
SceneSetView(PyMOLGlobals * G,const SceneViewType view,int quiet,float animate,int hand)1004 void SceneSetView(PyMOLGlobals * G, const SceneViewType view,
1005                   int quiet, float animate, int hand)
1006 {
1007   const float *p;
1008   int a;
1009   CScene *I = G->Scene;
1010 
1011   if(animate < 0.0F) {
1012     if(SettingGetGlobal_b(G, cSetting_animation))
1013       animate = SettingGetGlobal_f(G, cSetting_animation_duration);
1014     else
1015       animate = 0.0F;
1016   }
1017   if(animate != 0.0F)
1018     ScenePrimeAnimation(G);
1019   else {
1020     SceneAbortAnimation(G);
1021   }
1022 
1023   float dY = 0, dZ = 0;
1024   float fov = view[24];
1025   float scale = I->Scale;
1026 
1027 #ifdef _PYMOL_OPENVR
1028   if (I->StereoMode == cStereo_openvr) {
1029     float dist = fabsf(view[18]);
1030     float fovVR = SettingGetGlobal_f(G, cSetting_field_of_view);
1031     dY = -1.0f;
1032     dZ = scale * dist * (tanf(fabsf(fov) * PI / 360.f) / tanf(fovVR * PI / 360.f) - 1.0f);
1033     fov = fov < 0.0f ? -fovVR : fovVR;
1034   }
1035 #endif
1036 
1037   p = view;
1038   for(a = 0; a < 16; a++)
1039     I->m_view.m_rotMatrix[a] = *(p++);
1040   SceneUpdateInvMatrix(G);
1041   I->m_view.m_pos[0] = *(p++) * scale;
1042   I->m_view.m_pos[1] = *(p++) * scale - dY;
1043   I->m_view.m_pos[2] = *(p++) * scale - dZ;
1044   I->m_view.m_origin[0] = *(p++);
1045   I->m_view.m_origin[1] = *(p++);
1046   I->m_view.m_origin[2] = *(p++);
1047 
1048   I->LastSweep = 0.0F;
1049   I->LastSweepX = 0.0F;
1050   I->LastSweepY = 0.0F;
1051   I->SweepTime = 0.0;
1052   I->RockFrame = 0;
1053 
1054   SceneClipSet(G, p[0] * scale + dZ, p[1] * scale + dZ);
1055 
1056   p += 2;
1057   if(fov < 0.0F) {
1058     SettingSetGlobal_b(G, cSetting_ortho, 0);
1059     if(fov < -(1.0F - R_SMALL4)) {
1060       SettingSetGlobal_f(G, cSetting_field_of_view, -fov);
1061     }
1062   } else {
1063     SettingSetGlobal_b(G, cSetting_ortho, (fov > 0.5F));
1064     if(fov > (1.0F + R_SMALL4)) {
1065       SettingSetGlobal_f(G, cSetting_field_of_view, fov);
1066     }
1067   }
1068   if(!quiet) {
1069     PRINTFB(G, FB_Scene, FB_Actions)
1070       " Scene: view updated.\n" ENDFB(G);
1071   }
1072 
1073 #ifdef _OPENVR_STEREO_DEBUG_VIEWS
1074   printf("%-20s IP: %11lf %11lf %11lf, IF/IB: %11lf %11lf, IS: %11lf, IV: %11lf  <==  EP: %11lf %11lf %11lf, EF/EB: %11lf %11lf, EV: %11lf\n",
1075     "SceneSetView",
1076     I->m_view.m_pos[0], I->m_view.m_pos[1], I->m_view.m_pos[2],
1077     I->m_view.m_clip.m_front, I->m_view.m_clip.m_back, I->Scale,
1078     SettingGetGlobal_f(G, cSetting_field_of_view),
1079     view[16], view[17], view[18],
1080     view[22], view[23],
1081     view[24]
1082   );
1083 #endif // _OPENVR_STEREO_DEBUG_VIEWS
1084 
1085   if(animate != 0.0F)
1086     SceneLoadAnimation(G, animate, hand);
1087 
1088   SceneRovingDirty(G);
1089 }
1090 
1091 
1092 /*========================================================================*/
SceneDontCopyNext(PyMOLGlobals * G)1093 void SceneDontCopyNext(PyMOLGlobals * G)
1094 
1095 /* disables automatic copying of the image for the next rendering run */
1096 {
1097   CScene *I = G->Scene;
1098   I->CopyNextFlag = false;
1099 }
1100 
1101 
1102 /*========================================================================*/
SceneUpdateStereoMode(PyMOLGlobals * G)1103 void SceneUpdateStereoMode(PyMOLGlobals * G)
1104 {
1105   if(G->Scene->StereoMode) {
1106     SceneSetStereo(G, true);
1107   }
1108 }
1109 
1110 #ifdef _PYMOL_OPENVR
1111 /*========================================================================*/
1112 static
ResetFovWidth(PyMOLGlobals * G,bool enableOpenVR,float fovNew)1113 void ResetFovWidth(PyMOLGlobals * G, bool enableOpenVR, float fovNew) {
1114   CScene *I = G->Scene;
1115 
1116 #ifdef _OPENVR_STEREO_DEBUG_VIEWS
1117   printf("%-20s IP: %11lf %11lf %11lf, IF/IB: %11lf %11lf, IS: %11lf  ==>\n",
1118     "ResetFovWidth BEFORE",
1119     I->m_view.m_pos[0], I->m_view.m_pos[1], I->m_view.m_pos[2],
1120     I->m_view.m_clip.m_front, I->m_view.m_clip.m_back, I->Scale
1121   );
1122 #endif // _OPENVR_STEREO_DEBUG_VIEWS
1123 
1124   float fovOld = SettingGetGlobal_f(G, cSetting_field_of_view);
1125   SettingSetGlobal_f(G, cSetting_field_of_view, fovNew);
1126 
1127   const float tanOld = tanf(fovOld * PI / 360.f);
1128   const float tanNew = tanf(fovNew * PI / 360.f);
1129 
1130   const float distOld = fabsf(I->m_view.m_pos[2]);
1131 
1132   const float scaleOld = I->Scale;
1133   const float scaleNew = I->Scale = enableOpenVR ? 1.0f / (distOld * tanOld) : 1.0f;
1134   const float scale = scaleNew / scaleOld;
1135 
1136   I->m_view.m_pos[0] = I->m_view.m_pos[0] * scale;
1137   I->m_view.m_pos[1] = enableOpenVR ? I->m_view.m_pos[1] * scale + 1.0f : (I->m_view.m_pos[1] - 1.0f) * scale;
1138   I->m_view.m_pos[2] = I->m_view.m_pos[2] * scale * tanOld / tanNew;
1139 
1140   const float dZ = fabsf(I->m_view.m_pos[2]) - distOld * scale;
1141   I->m_view.m_clip.m_front = I->m_view.m_clip.m_front * scale + dZ;
1142   I->m_view.m_clip.m_back = I->m_view.m_clip.m_back * scale + dZ;
1143 
1144 #ifdef _OPENVR_STEREO_DEBUG_VIEWS
1145   printf("%-20s IP: %11lf %11lf %11lf, IF/IB: %11lf %11lf, IS: %11lf  <==\n",
1146     "ResetFovWidth AFTER",
1147     I->m_view.m_pos[0], I->m_view.m_pos[1], I->m_view.m_pos[2],
1148     I->m_view.m_clip.m_front, I->m_view.m_clip.m_back, I->Scale
1149   );
1150 #endif // _OPENVR_STEREO_DEBUG_VIEWS
1151 
1152   UpdateFrontBackSafe(I);
1153   SceneRovingDirty(G);
1154 }
1155 
1156 /*========================================================================*/
1157 static
SceneResetOpenVRSettings(PyMOLGlobals * G,bool enableOpenVR)1158 void SceneResetOpenVRSettings(PyMOLGlobals * G, bool enableOpenVR) {
1159   // set FOV = 110 for openVR
1160   float openVRFov = 110.0 * 0.5;
1161 
1162   // old camera props
1163   static bool commonCorrect = false;
1164   if (s_oldFov < 0.0f)
1165     s_oldFov = SettingGetGlobal_f(G, cSetting_field_of_view);
1166 
1167   if (enableOpenVR) {
1168     s_oldFov =  SettingGetGlobal_f(G, cSetting_field_of_view);
1169     ResetFovWidth(G, enableOpenVR, openVRFov);
1170     commonCorrect = true;
1171     SettingSetGlobal_f(G, cSetting_dynamic_width_factor, 0.004f); // for correct line width in lines mode
1172   } else if (commonCorrect){
1173     ResetFovWidth(G, enableOpenVR, s_oldFov);
1174     commonCorrect = false;
1175     SettingSetGlobal_f(G, cSetting_dynamic_width_factor, 0.06f);
1176   }
1177 }
1178 #endif
1179 
1180 /*========================================================================*/
SceneSetStereo(PyMOLGlobals * G,bool flag)1181 void SceneSetStereo(PyMOLGlobals * G, bool flag)
1182 {
1183   CScene *I = G->Scene;
1184   int cur_stereo_mode = I->StereoMode;
1185 
1186   if(flag) {
1187     I->StereoMode = SettingGetGlobal_i(G, cSetting_stereo_mode);
1188   } else {
1189     I->StereoMode = 0;
1190   }
1191 
1192   SettingSetGlobal_b(G, cSetting_stereo, flag);
1193 
1194   if (cur_stereo_mode != I->StereoMode) {
1195     if (cur_stereo_mode == cStereo_geowall ||
1196         I->StereoMode == cStereo_geowall) {
1197       OrthoReshape(G, G->Option->winX, G->Option->winY, true);
1198     }
1199 
1200 #ifdef _PYMOL_OPENVR
1201     // enter or leave OpenVR mode
1202     if (I->StereoMode == cStereo_openvr || cur_stereo_mode == cStereo_openvr) {
1203       bool enableOpenVR = I->StereoMode == cStereo_openvr;
1204 
1205       // reset camera position
1206       SceneResetOpenVRSettings(G, enableOpenVR);
1207 
1208       // force open internal menu
1209       PParse(G, "cmd.set_wizard_stack()");
1210       if (enableOpenVR) {
1211         PParse(G, "wizard openvr");
1212       }
1213     }
1214 #endif
1215 
1216     SceneInvalidateStencil(G);
1217     SceneInvalidate(G);
1218     G->ShaderMgr->Set_Reload_Bits(RELOAD_VARIABLES);
1219   }
1220 }
1221 
1222 
1223 /*========================================================================*/
SceneTranslate(PyMOLGlobals * G,float x,float y,float z)1224 void SceneTranslate(PyMOLGlobals * G, float x, float y, float z)
1225 {
1226   CScene *I = G->Scene;
1227   I->m_view.m_pos[0] += x;
1228   I->m_view.m_pos[1] += y;
1229   I->m_view.m_pos[2] += z;
1230   SceneClipSet(G, I->m_view.m_clip.m_front - z, I->m_view.m_clip.m_back - z);
1231 }
1232 
SceneTranslateScaled(PyMOLGlobals * G,float x,float y,float z,int sdof_mode)1233 void SceneTranslateScaled(PyMOLGlobals * G, float x, float y, float z, int sdof_mode)
1234 {
1235   CScene *I = G->Scene;
1236   int invalidate = false;
1237 
1238   switch (sdof_mode) {
1239   case SDOF_NORMAL_MODE:
1240     if((x != 0.0F) || (y != 0.0F)) {
1241       float vScale = SceneGetExactScreenVertexScale(G, NULL);
1242       float factor = vScale * (I->Height + I->Width) / 2;
1243       I->m_view.m_pos[0] += x * factor;
1244       I->m_view.m_pos[1] += y * factor;
1245       invalidate = true;
1246     }
1247     if(z != 0.0F) {
1248       float factor = ((I->m_view.m_clipSafe.m_front + I->m_view.m_clipSafe.m_back) / 2);        /* average distance within visible space */
1249       if(factor > 0.0F) {
1250         factor *= z;
1251         I->m_view.m_pos[2] += factor;
1252         I->m_view.m_clip.m_front -= factor;
1253         I->m_view.m_clip.m_back -= factor;
1254         UpdateFrontBackSafe(I);
1255       }
1256       invalidate = true;
1257     }
1258     break;
1259   case SDOF_CLIP_MODE:
1260     if((x != 0.0F) || (y != 0.0F)) {
1261       float vScale = SceneGetExactScreenVertexScale(G, NULL);
1262       float factor = vScale * (I->Height + I->Width) / 2;
1263       I->m_view.m_pos[0] += x * factor;
1264       I->m_view.m_pos[1] += y * factor;
1265       invalidate = true;
1266     }
1267     if(z != 0.0F) {
1268       float factor = ((I->m_view.m_clipSafe.m_front + I->m_view.m_clipSafe.m_back) / 2);        /* average distance within visible space */
1269       if(factor > 0.0F) {
1270         factor *= z;
1271         {
1272           float old_front = I->m_view.m_clip.m_front;
1273           float old_back = I->m_view.m_clip.m_back;
1274           float old_origin = -I->m_view.m_pos[2];
1275           SceneClip(G, 7, factor, NULL, 0);
1276           SceneDoRoving(G, old_front, old_back, old_origin, true, true);
1277         }
1278         invalidate = true;
1279       }
1280     }
1281     break;
1282   case SDOF_DRAG_MODE:
1283     {
1284       float v2[3];
1285       float scale = SettingGetGlobal_f(G, cSetting_sdof_drag_scale);
1286 
1287       {
1288         /* when dragging, we treat all axes proportionately */
1289         float vScale = SceneGetExactScreenVertexScale(G, NULL);
1290         float factor = vScale * (I->Height + I->Width) / 2;
1291         x *= factor;
1292         y *= factor;
1293         z *= factor;
1294       }
1295 
1296       v2[0] = x * scale;
1297       v2[1] = y * scale;
1298       v2[2] = z * scale;
1299 
1300       /* transform into model coodinate space */
1301       MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, v2, v2);
1302 
1303       EditorDrag(G, NULL, -1, cButModeMovDrag,
1304                  SettingGetGlobal_i(G, cSetting_state) - 1, NULL, v2, NULL);
1305     }
1306     break;
1307   }
1308   if(invalidate) {
1309     SceneInvalidate(G);
1310     if(SettingGetGlobal_b(G, cSetting_roving_origin)) {
1311       float v2[3];
1312       SceneGetCenter(G, v2);       /* gets position of center of screen */
1313       SceneOriginSet(G, v2, true);
1314     }
1315     if(SettingGetGlobal_b(G, cSetting_roving_detail)) {
1316       SceneRovingPostpone(G);
1317     }
1318   }
1319 }
1320 
SceneRotateScaled(PyMOLGlobals * G,float rx,float ry,float rz,int sdof_mode)1321 void SceneRotateScaled(PyMOLGlobals * G, float rx, float ry, float rz, int sdof_mode)
1322 {
1323   CScene *I = G->Scene;
1324   int invalidate = false;
1325   float axis[3];
1326   switch (sdof_mode) {
1327   case SDOF_NORMAL_MODE:
1328     axis[0] = rx;
1329     axis[1] = ry;
1330     axis[2] = rz;
1331     {
1332       float angle = length3f(axis);
1333       normalize3f(axis);
1334       SceneRotate(G, 60 * angle, axis[0], axis[1], axis[2]);
1335     }
1336     break;
1337   case SDOF_CLIP_MODE:
1338     if((fabs(rz) > fabs(rx)) || (fabs(rz) > fabs(rx))) {
1339       rx = 0.0;
1340       ry = 0.0;
1341     } else {
1342       rz = 0.0;
1343     }
1344     axis[0] = rx;
1345     axis[1] = ry;
1346     axis[2] = 0.0;
1347     {
1348       float angle = length3f(axis);
1349       normalize3f(axis);
1350       SceneRotate(G, 60 * angle, axis[0], axis[1], axis[2]);
1351     }
1352     if(axis[2] != rz) {
1353       SceneClip(G, 5, 1.0F + rz, NULL, 0);
1354     }
1355     break;
1356   case SDOF_DRAG_MODE:
1357     {
1358       float scale = SettingGetGlobal_f(G, cSetting_sdof_drag_scale);
1359       float v1[3], v2[3];
1360       axis[0] = rx;
1361       axis[1] = ry;
1362       axis[2] = rz;
1363 
1364       EditorReadyDrag(G, SettingGetGlobal_i(G, cSetting_state) - 1);
1365 
1366       {
1367         float angle = length3f(axis);
1368         normalize3f(axis);
1369 
1370         v1[0] = cPI * (60 * angle / 180.0F) * scale;
1371 
1372         /* transform into model coodinate space */
1373         MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, axis, v2);
1374 
1375         EditorDrag(G, NULL, -1, cButModeRotDrag,
1376                    SettingGetGlobal_i(G, cSetting_state) - 1, v1, v2, NULL);
1377         invalidate = true;
1378       }
1379     }
1380     break;
1381   }
1382   if(invalidate) {
1383     SceneInvalidate(G);
1384   }
1385 }
1386 
1387 
1388 /*========================================================================*/
1389 
SceneClipSetWithDirty(PyMOLGlobals * G,float front,float back,int dirty)1390 static void SceneClipSetWithDirty(PyMOLGlobals * G, float front, float back, int dirty)
1391 {
1392   CScene *I = G->Scene;
1393 
1394   // minimum slab
1395   float minSlab = cSliceMin * I->Scale;
1396   if(back - front < minSlab) {
1397     float avg = (back + front) / 2.0;
1398     back = avg + minSlab / 2.0;
1399     front = avg - minSlab / 2.0;
1400   }
1401 
1402   I->m_view.m_clip.m_front = front;
1403   I->m_view.m_clip.m_back = back;
1404 
1405   UpdateFrontBackSafe(I);
1406 
1407   if(dirty)
1408     SceneInvalidate(G);
1409   else
1410     SceneInvalidateCopy(G, false);
1411 }
1412 
1413 
1414 /*========================================================================*/
SceneClipSet(PyMOLGlobals * G,float front,float back)1415 void SceneClipSet(PyMOLGlobals * G, float front, float back)
1416 {
1417   SceneClipSetWithDirty(G, front, back, true);
1418 }
1419 
1420 
1421 /*========================================================================*/
SceneClip(PyMOLGlobals * G,int plane,float movement,const char * sele,int state)1422 void SceneClip(PyMOLGlobals * G, int plane, float movement, const char *sele, int state)
1423 {                               /* 0=front, 1=back */
1424   CScene *I = G->Scene;
1425   float avg;
1426   float mn[3], mx[3], cent[3], v0[3], offset[3], origin[3];
1427   switch (plane) {
1428   case 0:                      /* near */
1429     SceneClipSet(G, I->m_view.m_clip.m_front - movement, I->m_view.m_clip.m_back);
1430     break;
1431   case 1:                      /* far */
1432     SceneClipSet(G, I->m_view.m_clip.m_front, I->m_view.m_clip.m_back - movement);
1433     break;
1434   case 2:                      /* move */
1435     SceneClipSet(G, I->m_view.m_clip.m_front - movement, I->m_view.m_clip.m_back - movement);
1436     break;
1437   case 3:                      /* slab */
1438     if(sele[0]) {
1439       if(!ExecutiveGetExtent(G, sele, mn, mx, true, state, false))
1440         sele = NULL;
1441       else {
1442         average3f(mn, mx, cent);        /* get center of selection */
1443         subtract3f(cent, I->m_view.m_origin, v0);        /* how far from origin? */
1444         MatrixTransformC44fAs33f3f(I->m_view.m_rotMatrix, v0, offset);   /* convert to view-space */
1445       }
1446     } else {
1447       sele = NULL;
1448     }
1449     avg = (I->m_view.m_clip.m_front + I->m_view.m_clip.m_back) / 2.0F;
1450     movement /= 2.0F;
1451     if(sele) {
1452       avg = -I->m_view.m_pos[2] - offset[2];
1453     }
1454     SceneClipSet(G, avg - movement, avg + movement);
1455     break;
1456   case 4:                      /* atoms */
1457     if(!sele)
1458       sele = cKeywordAll;
1459     else if(!sele[0]) {
1460       sele = cKeywordAll;
1461     }
1462     if(WordMatchExact(G, sele, cKeywordCenter, true)) {
1463       MatrixTransformC44fAs33f3f(I->m_view.m_rotMatrix, I->m_view.m_origin, origin);      /* convert to view-space */
1464       SceneClipSet(G, origin[2] - movement, origin[2] + movement);
1465     } else if(WordMatchExact(G, sele, cKeywordOrigin, true)) {
1466       SceneClipSet(G, -I->m_view.m_pos[2] - movement, -I->m_view.m_pos[2] + movement);
1467     } else {
1468       if(!ExecutiveGetCameraExtent(G, sele, mn, mx, true, state))
1469         sele = NULL;
1470       if(sele) {
1471         if(sele[0]) {
1472           average3f(mn, mx, cent);      /* get center of selection */
1473           MatrixTransformC44fAs33f3f(I->m_view.m_rotMatrix, I->m_view.m_origin, origin);  /* convert to view-space */
1474           subtract3f(mx, origin, mx);   /* how far from origin? */
1475           subtract3f(mn, origin, mn);   /* how far from origin? */
1476           SceneClipSet(G, -I->m_view.m_pos[2] - mx[2] - movement, -I->m_view.m_pos[2] - mn[2] + movement);
1477         } else {
1478           sele = NULL;
1479         }
1480       }
1481     }
1482     break;
1483   case 5:                      /* scaling */
1484     {
1485       double avg = (I->m_view.m_clip.m_front / 2.0) + (I->m_view.m_clip.m_back / 2.0);
1486       double width_half = I->m_view.m_clip.m_back - avg;
1487       double new_w_half = std::min(movement * width_half,
1488           width_half + 1000.0); // prevent exploding of clipping planes
1489 
1490       SceneClipSet(G, avg - new_w_half, avg + new_w_half);
1491     }
1492     break;
1493   case 6:                      /* proportional movement */
1494     {
1495       float shift = (I->m_view.m_clip.m_front - I->m_view.m_clip.m_back) * movement;
1496       SceneClipSet(G, I->m_view.m_clip.m_front + shift, I->m_view.m_clip.m_back + shift);
1497     }
1498     break;
1499   case 7:                      /* linear movement */
1500     {
1501       SceneClipSet(G, I->m_view.m_clip.m_front + movement, I->m_view.m_clip.m_back + movement);
1502     }
1503     break;
1504   }
1505 }
1506 
1507 
1508 /*========================================================================*/
SceneSetMatrix(PyMOLGlobals * G,float * m)1509 void SceneSetMatrix(PyMOLGlobals * G, float *m)
1510 {
1511   CScene *I = G->Scene;
1512   int a;
1513   for(a = 0; a < 16; a++)
1514     I->m_view.m_rotMatrix[a] = m[a];
1515   SceneUpdateInvMatrix(G);
1516 }
1517 
1518 
1519 /*========================================================================*/
SceneGetViewNormal(PyMOLGlobals * G,float * v)1520 void SceneGetViewNormal(PyMOLGlobals * G, float *v)
1521 {
1522   CScene *I = G->Scene;
1523   copy3f(I->ViewNormal, v);
1524 }
1525 
1526 
1527 /*========================================================================*/
SceneGetState(PyMOLGlobals * G)1528 int SceneGetState(PyMOLGlobals * G)
1529 {
1530   return (SettingGetGlobal_i(G, cSetting_state) - 1);
1531 }
1532 
1533 
1534 /*========================================================================*/
SceneGetMatrix(PyMOLGlobals * G)1535 float *SceneGetMatrix(PyMOLGlobals * G)
1536 {
1537   CScene *I = G->Scene;
1538   return (I->m_view.m_rotMatrix);
1539 }
1540 
SceneGetPmvMatrix(PyMOLGlobals * G)1541 float *SceneGetPmvMatrix(PyMOLGlobals * G)
1542 {
1543   CScene *I = G->Scene;
1544   multiply44f44f44f(I->ModelViewMatrix, I->ProjectionMatrix, I->PmvMatrix);
1545   return (I->PmvMatrix);
1546 }
1547 
1548 
1549 /*========================================================================*/
1550 
SceneCaptureWindow(PyMOLGlobals * G)1551 int SceneCaptureWindow(PyMOLGlobals * G)
1552 {
1553   CScene *I = G->Scene;
1554   int ok = true;
1555 
1556   /* check assumptions */
1557 
1558   if(ok && G->HaveGUI && G->ValidContext) {
1559     int draw_both = SceneMustDrawBoth(G);
1560 
1561     ScenePurgeImage(G);
1562 
1563     if(draw_both) {
1564       SceneCopy(G, GL_BACK_LEFT, true, true);
1565     } else {
1566       SceneCopy(G, GL_BACK, true, true);
1567     }
1568     if(!I->Image)
1569       ok = false;
1570 
1571     if(ok && I->Image) {
1572       I->DirtyFlag = false;
1573       I->CopyType = 2;          /* suppresses display of copied image */
1574       if(SettingGetGlobal_b(G, cSetting_opaque_background))
1575         I->Image->m_needs_alpha_reset = true;
1576     }
1577   } else {
1578     ok = false;
1579   }
1580   return ok;
1581 }
1582 
SceneMakeSizedImage(PyMOLGlobals * G,int width,int height,int antialias)1583 static int SceneMakeSizedImage(PyMOLGlobals * G, int width, int height, int antialias)
1584 {
1585   CScene *I = G->Scene;
1586   int ok = true;
1587   int save_flag = false;
1588   int save_width = 0, save_height = 0;
1589 
1590   /* check assumptions */
1591 
1592   if((width && height && I->Width && I->Height) &&
1593      fabs(((float) (height - (width * I->Height) / (I->Width))) / height) > 0.01F) {
1594     save_width = I->Width;
1595     save_height = I->Height;
1596     save_flag = true;
1597 
1598     /* squish the dimensions as needed to maintain
1599        aspect ratio within the current rectangle */
1600 
1601     if(I->Width > ((width * I->Height) / height))
1602       I->Width = (width * I->Height) / height;
1603     else if(I->Height > ((height * I->Width) / width))
1604       I->Height = (height * I->Width) / width;
1605   }
1606   if((!width) && (!height)) {
1607     width = I->Width;
1608     height = I->Height;
1609   }
1610   if(width && !height) {
1611     height = (I->Height * width) / I->Width;
1612   }
1613   if(height && !width) {
1614     width = (I->Width * height) / I->Height;
1615   }
1616   if(!((width > 0) && (height > 0) && (I->Width > 0) && (I->Height > 0))) {
1617     PRINTFB(G, FB_Scene, FB_Errors)
1618       "SceneMakeSizedImage-Error: invalid image dimensions\n" ENDFB(G);
1619     ok = false;
1620   }
1621 
1622   if(ok && G->HaveGUI && G->ValidContext) {
1623 
1624     int factor = 0;
1625     int shift = 0;
1626     int max_dim[2];
1627 
1628     glGetIntegerv(GL_MAX_VIEWPORT_DIMS, (GLint *) (void *) max_dim);
1629 
1630     /* clamp to what this OpenGL implementation can do */
1631     if(width > max_dim[0]) {
1632       height = (max_dim[0] * height) / width;
1633       width = max_dim[0];
1634       PRINTFB(G, FB_Scene, FB_Warnings)
1635         "Scene-Warning: Maximum OpenGL viewport dimension exceeded." ENDFB(G)
1636     }
1637     if(height > max_dim[0]) {
1638       width = (max_dim[1] * width) / height;
1639       height = max_dim[1];
1640       PRINTFB(G, FB_Scene, FB_Warnings)
1641         "Scene-Warning: Maximum OpenGL viewport dimension exceeded." ENDFB(G)
1642 
1643     }
1644 
1645     if(antialias == 1) {
1646       factor = 2;
1647       shift = 2;
1648     }
1649     if(antialias >= 2) {
1650       factor = 4;
1651       shift = 4;
1652     }
1653 
1654     while(factor > 1) {
1655       if(((width * factor) < max_dim[0]) && ((height * factor) < max_dim[1])) {
1656         width = width * factor;
1657         height = height * factor;
1658         break;
1659       } else {
1660         factor = (factor >> 1);
1661         shift = shift - 2;
1662         if(factor < 2) {
1663           PRINTFB(G, FB_Scene, FB_Blather)
1664             "Scene-Warning: Maximum OpenGL viewport exceeded. Antialiasing disabled."
1665             ENDFB(G);
1666           break;
1667         }
1668       }
1669     }
1670     if(factor < 2)
1671       factor = 0;
1672 
1673     {
1674       int nXStep = (width / (I->Width + 1)) + 1;
1675       int nYStep = (height / (I->Height + 1)) + 1;
1676       int x, y;
1677       int draw_both = SceneMustDrawBoth(G);
1678       /* note here we're treating the buffer as 32-bit unsigned ints, not chars */
1679 
1680       auto final_image = pymol::Image(width, height);
1681 
1682       ScenePurgeImage(G);
1683 
1684       if(draw_both) {
1685         SceneCopy(G, GL_BACK_LEFT, true, false);
1686       } else {
1687         SceneCopy(G, GL_BACK, true, false);
1688       }
1689       if(!I->Image)
1690         ok = false;
1691 
1692       if(ok) {
1693         int total_steps = nXStep * nYStep;
1694 
1695         OrthoBusyPrime(G);
1696 
1697         /* so the trick here is that we need to move the camera around
1698            so that we get a pixel-perfect mosaic */
1699         for(y = 0; y < nYStep; y++) {
1700           int y_offset = -(I->Height * y);
1701 
1702           for(x = 0; x < nXStep; x++) {
1703             int x_offset = -(I->Width * x);
1704             int a, b;
1705             unsigned int *p, *q, *qq, *pp;
1706 
1707             OrthoBusyFast(G, y * nXStep + x, total_steps);
1708 
1709             if(draw_both) {
1710               OrthoDrawBuffer(G, GL_BACK_LEFT);
1711             } else {
1712               OrthoDrawBuffer(G, GL_BACK);
1713             }
1714 
1715             glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
1716             SceneInvalidateCopy(G, false);
1717             SceneRender(G, NULL, x_offset, y_offset, NULL, width, height, 0, 0);
1718             SceneGLClearColor(0.0, 0.0, 0.0, 1.0);
1719 
1720             if(draw_both) {
1721               SceneCopy(G, GL_BACK_LEFT, true, false);
1722             } else {
1723               SceneCopy(G, GL_BACK, true, false);
1724             }
1725 
1726             if(I->Image) {      /* the image into place */
1727               p = I->Image->pixels();
1728               q = final_image.pixels() + (x * I->Width) + (y * I->Height) * width;
1729               {
1730                 int y_limit;
1731                 int x_limit;
1732 
1733                 if(((y + 1) * I->Height) > height)
1734                   y_limit = height - (y * I->Height);
1735                 else
1736                   y_limit = I->Height;
1737                 if(((x + 1) * I->Width) > width)
1738                   x_limit = width - (x * I->Width);
1739                 else
1740                   x_limit = I->Width;
1741                 for(a = 0; a < y_limit; a++) {
1742                   qq = q;
1743                   pp = p;
1744                   for(b = 0; b < x_limit; b++) {
1745                     *(qq++) = *(pp++);
1746                   }
1747                   q += width;
1748                   p += I->Width;
1749                 }
1750               }
1751             }
1752           }
1753         }
1754 
1755         if(!OrthoDeferredWaiting(G)) {
1756           if(SettingGetGlobal_i(G, cSetting_draw_mode) == -2) {
1757             ExecutiveSetSettingFromString(G, cSetting_draw_mode, "-1", "", -1, true,
1758                                           true);
1759             SceneUpdate(G, false);
1760           }
1761         }
1762 
1763         SceneInvalidateCopy(G, true);
1764 
1765         if(factor) {            /* are we oversampling? */
1766           unsigned int src_row_bytes = width * 4;
1767 
1768           width = width / factor;
1769           height = height / factor;
1770 
1771           {
1772             unsigned char *p = final_image.bits();
1773             auto buffer = pymol::Image(width, height);
1774             unsigned char *q = buffer.bits();
1775             unsigned char *pp, *ppp, *pppp;
1776             int a, b, c, d;
1777             unsigned int c1, c2, c3, c4, alpha;
1778             unsigned int factor_col_bytes = factor * 4;
1779             unsigned int factor_row_bytes = factor * src_row_bytes;
1780 
1781             for(b = 0; b < height; b++) {       /* rows */
1782               pp = p;
1783               for(a = 0; a < width; a++) {      /* cols */
1784                 c1 = c2 = c3 = c4 = 0;
1785                 ppp = pp;
1786                 for(d = 0; d < factor; d++) {   /* box rows */
1787                   pppp = ppp;
1788                   for(c = 0; c < factor; c++) { /* box cols */
1789                     c4 += (alpha = pppp[3]);
1790                     c1 += *(pppp++) * alpha;
1791                     c2 += *(pppp++) * alpha;
1792                     c3 += *(pppp++) * alpha;
1793                     pppp++;
1794                   }
1795                   ppp += src_row_bytes;
1796                 }
1797                 if(c4) {        /* divide out alpha channel & average */
1798                   c1 = c1 / c4;
1799                   c2 = c2 / c4;
1800                   c3 = c3 / c4;
1801                 } else {        /* alpha zero! so compute average RGB */
1802                   c1 = c2 = c3 = 0;
1803                   ppp = pp;
1804                   for(d = 0; d < factor; d++) { /* box rows */
1805                     pppp = ppp;
1806                     for(c = 0; c < factor; c++) {       /* box cols */
1807                       c1 += *(pppp++);
1808                       c2 += *(pppp++);
1809                       c3 += *(pppp++);
1810                       pppp++;
1811                     }
1812                     ppp += src_row_bytes;
1813                   }
1814                   c1 = c1 >> shift;
1815                   c2 = c2 >> shift;
1816                   c3 = c3 >> shift;
1817                 }
1818                 *(q++) = c1;
1819                 *(q++) = c2;
1820                 *(q++) = c3;
1821                 *(q++) = c4 >> shift;
1822                 pp += factor_col_bytes;
1823               }
1824               p += factor_row_bytes;
1825             }
1826 
1827             final_image = std::move(buffer);
1828           }
1829         }
1830         ScenePurgeImage(G);
1831 
1832         I->Image = std::make_shared<pymol::Image>(std::move(final_image));
1833         I->DirtyFlag = false;
1834         I->CopyType = true;
1835         I->CopyForced = true;
1836 
1837         if(SettingGetGlobal_b(G, cSetting_opaque_background))
1838           I->Image->m_needs_alpha_reset = true;
1839 
1840       }
1841     }
1842   } else {
1843     ok = false;
1844   }
1845 
1846   if(save_flag) {
1847     I->Width = save_width;
1848     I->Height = save_height;
1849   }
1850   return ok;
1851 
1852 }
1853 
1854 /**
1855  * Prepares the scene image for PNG export.
1856  *
1857  * If there is no rendered image (`G->Scene->CopyType` is false) then read the
1858  * current image from the OpenGL back buffer.
1859  *
1860  * Sets the alpha channel to opaque if the `opaque_background` setting is on.
1861  *
1862  * Modifies `G->Scene->Image`.
1863  *
1864  * @param prior_only Return NULL if there is no prior image (`G->Scene->Image` is NULL)
1865  * @return The scene image
1866  */
SceneImagePrepare(PyMOLGlobals * G,bool prior_only)1867 pymol::Image* SceneImagePrepare(PyMOLGlobals* G, bool prior_only)
1868 {
1869   CScene *I = G->Scene;
1870   pymol::Image* image = nullptr;
1871   int save_stereo = (I->StereoMode == 1);
1872 
1873   if (!(I->CopyType || prior_only)) {
1874     if(G->HaveGUI && G->ValidContext) {
1875       ScenePurgeImage(G);
1876       I->Image = std::make_shared<pymol::Image>(I->Width, I->Height, save_stereo);
1877       image = I->Image.get();
1878 
1879 #ifndef PURE_OPENGL_ES_2
1880       if(SceneMustDrawBoth(G) || save_stereo) {
1881         glReadBuffer(GL_BACK_LEFT);
1882       } else {
1883         glReadBuffer(GL_BACK);
1884       }
1885 #endif
1886       PyMOLReadPixels(I->rect.left, I->rect.bottom, I->Width, I->Height,
1887                       GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *) (image->bits()));
1888 #ifndef PURE_OPENGL_ES_2
1889       if(save_stereo) {
1890         glReadBuffer(GL_BACK_RIGHT);
1891         PyMOLReadPixels(I->rect.left, I->rect.bottom, I->Width, I->Height,
1892                         GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *) (image->bits() + image->getSizeInBytes()));
1893       }
1894 #endif
1895       I->Image->m_needs_alpha_reset = true;
1896     }
1897   } else if(I->Image) {
1898     image = I->Image.get();
1899   }
1900   if(image) {
1901     int opaque_back = SettingGetGlobal_b(G, cSetting_opaque_background);
1902     if(opaque_back && I->Image->m_needs_alpha_reset) {
1903       int i, s = image->getSizeInBytes() * (image->isStereo() ? 2 : 1);
1904       for (i = pymol::Image::Channel::ALPHA; i < s;
1905            i += pymol::Image::getPixelSize())
1906         image->bits()[i] = 0xFF;
1907       I->Image->m_needs_alpha_reset = false;
1908     }
1909   }
1910   return image;
1911 }
1912 
1913 /**
1914  * Get the size of the rendered image. This is either identical to
1915  * cmd.get_viewport(), or the dimensions which were last passed to
1916  * cmd.draw(), cmd.ray() or cmd.png().
1917  */
SceneGetImageSize(PyMOLGlobals * G)1918 std::pair<int, int> SceneGetImageSize(PyMOLGlobals * G)
1919 {
1920   CScene *I = G->Scene;
1921   if (I->CopyType && I->Image) {
1922     return I->Image->getSize();
1923   } else {
1924     return std::make_pair(I->Width, I->Height);
1925   }
1926 }
1927 
SceneGetGridAspectRatio(PyMOLGlobals * G)1928 float SceneGetGridAspectRatio(PyMOLGlobals * G){
1929   CScene *I = G->Scene;
1930   return (I->Width / (float)I->Height) /
1931     (float)(I->grid.cur_viewport_size[0] / (float)I->grid.cur_viewport_size[1]);
1932 }
1933 
SceneCopyExternal(PyMOLGlobals * G,int width,int height,int rowbytes,unsigned char * dest,int mode)1934 int SceneCopyExternal(PyMOLGlobals * G, int width, int height,
1935                       int rowbytes, unsigned char *dest, int mode)
1936 {
1937   auto image = SceneImagePrepare(G, false);
1938   CScene *I = G->Scene;
1939   int result = false;
1940   int i, j;
1941   int premultiply_alpha = true;
1942   int red_index = 0, blue_index = 1, green_index = 2, alpha_index = 3;
1943   int no_alpha = (SettingGetGlobal_b(G, cSetting_opaque_background) &&
1944                   SettingGetGlobal_b(G, cSetting_ray_opaque_background));
1945 
1946   if(mode & 0x1) {
1947     int index = 0;
1948     while(index < 4) {
1949       if(dest[index] == 'R')
1950         red_index = index;
1951       if(dest[index] == 'G')
1952         green_index = index;
1953       if(dest[index] == 'B')
1954         blue_index = index;
1955       if(dest[index] == 'A')
1956         alpha_index = index;
1957       index++;
1958     }
1959   }
1960   if(mode & 0x2) {
1961     premultiply_alpha = false;
1962   }
1963   /*
1964      printf("image %p I->image %p\n");
1965      if(I->Image) {
1966      printf("%d %d %d %d\n",I->Image->width,width,I->Image->height,height);
1967      } */
1968 
1969   if(image && I->Image && (I->Image->getWidth() == width) && (I->Image->getHeight() == height)) {
1970     for(i = 0; i < height; i++) {
1971       unsigned char *src = image->bits() + ((height - 1) - i) * width * 4;
1972       unsigned char *dst;
1973       if(mode & 0x4) {
1974         dst = dest + (height - (i + 1)) * (rowbytes);
1975       } else {
1976         dst = dest + i * (rowbytes);
1977       }
1978       for(j = 0; j < width; j++) {
1979         if(no_alpha) {
1980           dst[red_index] = src[0];      /* no alpha */
1981           dst[green_index] = src[1];
1982           dst[blue_index] = src[2];
1983           dst[alpha_index] = 0xFF;
1984           /*            if(!(i||j)) {
1985              printf("no alpha\n");
1986              } */
1987         } else if(premultiply_alpha) {
1988           dst[red_index] = (((unsigned int) src[0]) * src[3]) / 255;    /* premultiply alpha */
1989           dst[green_index] = (((unsigned int) src[1]) * src[3]) / 255;
1990           dst[blue_index] = (((unsigned int) src[2]) * src[3]) / 255;
1991           dst[alpha_index] = src[3];
1992           /*       if(!(i||j)) {
1993              printf("premult alpha\n");
1994              } */
1995         } else {
1996           dst[red_index] = src[0];      /* standard alpha */
1997           dst[green_index] = src[1];
1998           dst[blue_index] = src[2];
1999           dst[alpha_index] = src[3];
2000           /*            if(!(i||j)) {
2001              printf("standard alpha\n");
2002              } */
2003         }
2004         dst += 4;
2005         src += 4;
2006       }
2007     }
2008     result = true;
2009   } else {
2010     printf("image or size mismatch\n");
2011   }
2012   return (result);
2013 }
2014 
ScenePNG(PyMOLGlobals * G,const char * png,float dpi,int quiet,int prior_only,int format)2015 bool ScenePNG(PyMOLGlobals * G, const char *png, float dpi, int quiet,
2016              int prior_only, int format)
2017 {
2018   CScene *I = G->Scene;
2019   SceneImagePrepare(G, prior_only);
2020   if(I->Image) {
2021     int width, height;
2022     std::tie(width, height) = I->Image->getSize();
2023     auto saveImage = I->Image;
2024     if(I->Image->isStereo()) {
2025       saveImage = std::make_shared<pymol::Image>();
2026       *(saveImage) = I->Image->interlace();
2027     }
2028     if(dpi < 0.0F)
2029       dpi = SettingGetGlobal_f(G, cSetting_image_dots_per_inch);
2030     auto screen_gamma = SettingGetGlobal_f(G, cSetting_png_screen_gamma);
2031     auto file_gamma = SettingGetGlobal_f(G, cSetting_png_file_gamma);
2032     if(MyPNGWrite(png, *saveImage, dpi, format, quiet, screen_gamma, file_gamma)) {
2033       if(!quiet) {
2034         PRINTFB(G, FB_Scene, FB_Actions)
2035           " %s: wrote %dx%d pixel image to file \"%s\".\n", __func__,
2036           width, I->Image->getHeight(), png ENDFB(G);
2037       }
2038     } else {
2039       PRINTFB(G, FB_Scene, FB_Errors)
2040         " %s-Error: error writing \"%s\"! Please check directory...\n", __func__,
2041         png ENDFB(G);
2042     }
2043   }
2044   return I->Image.get() != nullptr;
2045 }
2046 
2047 
2048 /*========================================================================*/
SceneGetFrame(PyMOLGlobals * G)2049 int SceneGetFrame(PyMOLGlobals * G)
2050 {
2051   if(MovieDefined(G))
2052     return (SettingGetGlobal_i(G, cSetting_frame) - 1);
2053   else
2054     return (SettingGetGlobal_i(G, cSetting_state) - 1);
2055 }
2056 
2057 
2058 /*========================================================================*/
2059 /**
2060  * Returns the number of movie frames, or the number of states if no movie
2061  * is defined.
2062  */
SceneCountFrames(PyMOLGlobals * G)2063 int SceneCountFrames(PyMOLGlobals * G)
2064 {
2065   CScene *I = G->Scene;
2066   int mov_len = MovieGetLength(G);
2067   I->HasMovie = (mov_len != 0);
2068   if(mov_len > 0) {
2069     I->NFrame = mov_len;
2070   } else {
2071     if (mov_len < 0) {
2072       // allows you to see cached movie even w/o object
2073       I->NFrame = -mov_len;
2074     } else {
2075       I->NFrame = 0;
2076     }
2077 
2078     for (const auto& obj : I->Obj) {
2079       int n = obj->getNFrame();
2080       if (n > I->NFrame)
2081         I->NFrame = n;
2082     }
2083   }
2084   PRINTFD(G, FB_Scene)" %s: leaving... I->NFrame %d\n", __func__, I->NFrame ENDFD
2085   return I->NFrame;
2086 }
2087 
2088 
2089 /*========================================================================*/
SceneSetFrame(PyMOLGlobals * G,int mode,int frame)2090 void SceneSetFrame(PyMOLGlobals * G, int mode, int frame)
2091 {
2092   CScene *I = G->Scene;
2093   int newFrame;
2094   int newState = 0;
2095   int movieCommand = false;
2096   int suppress = false;
2097 
2098   newFrame = SettingGetGlobal_i(G, cSetting_frame) - 1;
2099   PRINTFD(G, FB_Scene)
2100     " %s: entered.\n", __func__ ENDFD;
2101   switch (mode) {
2102   case -1:                     /* movie/frame override - go to this state absolutely! */
2103     newState = frame;
2104     break;
2105   case 0:                      /* absolute frame */
2106     newFrame = frame;
2107     break;
2108   case 1:                      /* relative frame */
2109     newFrame += frame;
2110     break;
2111   case 2:                      /* end */
2112     newFrame = I->NFrame - 1;
2113     break;
2114   case 3:                      /* middle with automatic movie command */
2115     newFrame = I->NFrame / 2;
2116     movieCommand = true;
2117     break;
2118   case 4:                      /* absolute with automatic movie command */
2119     newFrame = frame;
2120     movieCommand = true;
2121     break;
2122   case 5:                      /* relative with automatic movie command */
2123     newFrame += frame;
2124     movieCommand = true;
2125     break;
2126   case 6:                      /* end with automatic movie command */
2127     newFrame = I->NFrame - 1;
2128     movieCommand = true;
2129     break;
2130   case 7:                      /* absolute with forced movie command */
2131     newFrame = frame;
2132     movieCommand = true;
2133     break;
2134   case 8:                      /* relative with forced movie command */
2135     newFrame += frame;
2136     movieCommand = true;
2137     break;
2138   case 9:                      /* end with forced movie command */
2139     newFrame = I->NFrame - 1;
2140     movieCommand = true;
2141     break;
2142   case 10:  /* seek forward to current scene (if present) */
2143     {
2144       frame = MovieSeekScene(G,true);
2145       if(frame>=0) {
2146 	newFrame = frame;
2147 	movieCommand = true;
2148       } else {
2149 	suppress = true;
2150       }
2151     }
2152     break;
2153   }
2154   if(!suppress) {
2155     SceneCountFrames(G);
2156     if(mode >= 0) {
2157       if(newFrame >= I->NFrame)
2158 	newFrame = I->NFrame - 1;
2159       if(newFrame < 0)
2160 	newFrame = 0;
2161       newState = MovieFrameToIndex(G, newFrame);
2162       if(newFrame == 0) {
2163 	if(MovieMatrix(G, cMovieMatrixRecall)) {
2164 	  SceneAbortAnimation(G); /* if we have a programmed initial
2165 				     orientation, don't allow animation
2166 				     to override it */
2167 	}
2168       }
2169       SettingSetGlobal_i(G, cSetting_frame, newFrame + 1);
2170       SettingSetGlobal_i(G, cSetting_state, newState + 1);
2171       ExecutiveInvalidateSelectionIndicatorsCGO(G);
2172       SceneInvalidatePicking(G);
2173       if(movieCommand) {
2174 #ifndef _PYMOL_NO_UNDO
2175 	int suspend_undo = SettingGetGlobal_b(G, cSetting_suspend_undo);
2176 	if (!suspend_undo){
2177 	  SettingSetGlobal_i(G, cSetting_suspend_undo, 1);
2178 	}
2179 #endif
2180 	MovieDoFrameCommand(G, newFrame);
2181 	MovieFlushCommands(G);
2182 #ifndef _PYMOL_NO_UNDO
2183 	SettingSetGlobal_i(G, cSetting_suspend_undo, suspend_undo);
2184 #endif
2185       }
2186       if(SettingGetGlobal_b(G, cSetting_cache_frames))
2187 	I->MovieFrameFlag = true;
2188     } else {
2189       SettingSetGlobal_i(G, cSetting_frame, newFrame + 1);
2190       SettingSetGlobal_i(G, cSetting_state, newState + 1);
2191       ExecutiveInvalidateSelectionIndicatorsCGO(G);
2192       SceneInvalidatePicking(G);
2193     }
2194     MovieSetScrollBarFrame(G, newFrame);
2195     SeqChanged(G); // SceneInvalidate(G);
2196   }
2197   PRINTFD(G, FB_Scene)
2198     " %s: leaving...\n", __func__ ENDFD;
2199   OrthoInvalidateDoDraw(G);
2200 }
2201 
2202 
2203 /*========================================================================*/
SceneDirty(PyMOLGlobals * G)2204 void SceneDirty(PyMOLGlobals * G)
2205 
2206 /* This means that the current image on the screen (and/or in the buffer)
2207 
2208    needs to be updated */
2209 {
2210   CScene *I = G->Scene;
2211 
2212   PRINTFD(G, FB_Scene)
2213     " %s: called.\n", __func__ ENDFD;
2214 
2215   if(I) {
2216     if(!I->DirtyFlag) {
2217       I->DirtyFlag = true;
2218       /* SceneInvalidateCopy(G,false); */
2219       OrthoDirty(G);
2220     }
2221   }
2222 
2223 }
2224 
SceneRovingPostpone(PyMOLGlobals * G)2225 void SceneRovingPostpone(PyMOLGlobals * G)
2226 {
2227   CScene *I = G->Scene;
2228   float delay;
2229   if(SettingGetGlobal_b(G, cSetting_roving_detail)) {
2230     delay = SettingGetGlobal_f(G, cSetting_roving_delay);
2231     if(delay < 0.0F) {
2232       I->RovingLastUpdate = UtilGetSeconds(G);  /* put off delay */
2233     }
2234   }
2235 }
2236 
SceneRovingDirty(PyMOLGlobals * G)2237 void SceneRovingDirty(PyMOLGlobals * G)
2238 {
2239   CScene *I = G->Scene;
2240 
2241   if(SettingGetGlobal_b(G, cSetting_roving_detail)) {
2242     SceneRovingPostpone(G);
2243     I->RovingDirtyFlag = true;
2244   }
2245 }
2246 
2247 
2248 /*========================================================================*/
SceneChanged(PyMOLGlobals * G)2249 void SceneChanged(PyMOLGlobals * G)
2250 {
2251   CScene *I = G->Scene;
2252   I->ChangedFlag = true;
2253   SceneInvalidateCopy(G, false);
2254   SceneDirty(G);
2255   SeqChanged(G);
2256   PyMOL_NeedRedisplay(G->PyMOL);
2257 }
2258 
2259 
2260 /*========================================================================*/
SceneGetBlock(PyMOLGlobals * G)2261 Block *SceneGetBlock(PyMOLGlobals * G)
2262 {
2263   CScene *I = G->Scene;
2264   return (I);
2265 }
2266 
2267 
2268 /*========================================================================*/
SceneValidateImageMode(PyMOLGlobals * G,int mode,bool defaultdraw)2269 int SceneValidateImageMode(PyMOLGlobals * G, int mode, bool defaultdraw) {
2270   switch (mode) {
2271     case cSceneImage_Normal:
2272     case cSceneImage_Draw:
2273     case cSceneImage_Ray:
2274       return mode;
2275   }
2276 
2277   if (mode != cSceneImage_Default) {
2278     PRINTFB(G, FB_Scene, FB_Warnings)
2279       " %s-Warning: invalid mode %d\n", __FUNCTION__, mode ENDFB(G);
2280   }
2281 
2282   if(!G->HaveGUI || SettingGetGlobal_b(G, cSetting_ray_trace_frames)) {
2283     return cSceneImage_Ray;
2284   }
2285 
2286   if(defaultdraw || SettingGetGlobal_b(G, cSetting_draw_frames)) {
2287     return cSceneImage_Draw;
2288   }
2289 
2290   return cSceneImage_Normal;
2291 }
2292 
2293 
2294 /*========================================================================*/
SceneMakeMovieImage(PyMOLGlobals * G,int show_timing,int validate,int mode,int width,int height)2295 int SceneMakeMovieImage(PyMOLGlobals * G,
2296     int show_timing,
2297     int validate,
2298     int mode,
2299     int width,
2300     int height)
2301 {
2302   CScene *I = G->Scene;
2303   //  float *v;
2304   int valid = true;
2305   PRINTFB(G, FB_Scene, FB_Blather)
2306     " Scene: Making movie image.\n" ENDFB(G);
2307 
2308   // PYMOL-3209 objects inside hidden groups become visible
2309   ExecutiveUpdateSceneMembers(G);
2310 
2311   mode = SceneValidateImageMode(G, mode, width || height);
2312 
2313   I->DirtyFlag = false;
2314   switch (mode) {
2315   case cSceneImage_Ray:
2316     SceneRay(G, width, height, SettingGetGlobal_i(G, cSetting_ray_default_renderer),
2317              NULL, NULL, 0.0F, 0.0F, false, NULL, show_timing, -1);
2318     break;
2319   case cSceneImage_Draw:
2320     SceneMakeSizedImage(G, width, height, SettingGetGlobal_i(G, cSetting_antialias));
2321     break;
2322   case cSceneImage_Normal:
2323     {
2324       int draw_both = SceneMustDrawBoth(G);
2325       if(G->HaveGUI && G->ValidContext) {
2326         if(draw_both) {
2327           OrthoDrawBuffer(G, GL_BACK_LEFT);
2328         } else {
2329           OrthoDrawBuffer(G, GL_BACK);
2330         }
2331         glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
2332         /* insert OpenGL context validation code here? */
2333         SceneRender(G, NULL, 0, 0, NULL, 0, 0, 0, 0);
2334         SceneGLClearColor(0.0, 0.0, 0.0, 1.0);
2335         if(draw_both) {
2336           SceneCopy(G, GL_BACK_LEFT, true, false);
2337         } else {
2338           SceneCopy(G, GL_BACK, true, false);
2339         }
2340         /* insert OpenGL context validation code here? */
2341       }
2342     }
2343     break;
2344   }
2345   MovieSetImage(G,
2346                 MovieFrameToImage(G, SettingGetGlobal_i(G, cSetting_frame) - 1),
2347                 I->Image);
2348   if(I->Image)
2349     I->CopyType = true;
2350   return valid;
2351 }
2352 
2353 
2354 /*========================================================================*/
SceneUpdateCameraRock(PyMOLGlobals * G,int dirty)2355 static void SceneUpdateCameraRock(PyMOLGlobals * G, int dirty)
2356 {
2357 
2358   CScene *I = G->Scene;
2359   float ang_cur, disp, diff;
2360   float sweep_angle = SettingGetGlobal_f(G, cSetting_sweep_angle);
2361   float sweep_speed = SettingGetGlobal_f(G, cSetting_sweep_speed);
2362   float sweep_phase = SettingGetGlobal_f(G, cSetting_sweep_phase);
2363   int sweep_mode = SettingGetGlobal_i(G, cSetting_sweep_mode);
2364   float shift = (float) (PI / 2.0F);
2365 
2366   I->SweepTime += I->RenderTime;
2367   I->LastSweepTime = UtilGetSeconds(G);
2368 
2369   switch (sweep_mode) {
2370   case 0:
2371   case 1:
2372   case 2:
2373     if(sweep_angle <= 0.0F) {
2374       diff = (float) ((PI / 180.0F) * I->RenderTime * 10 * sweep_speed / 0.75F);
2375     } else {
2376       ang_cur = (float) (I->SweepTime * sweep_speed) + sweep_phase;
2377       disp = (float) (sweep_angle * (PI / 180.0F) * sin(ang_cur) / 2);
2378       diff = (float) (disp - I->LastSweep);
2379       I->LastSweep = disp;
2380     }
2381     switch (sweep_mode) {
2382     case 0:
2383       SceneRotateWithDirty(G, (float) (180 * diff / PI), 0.0F, 1.0F, 0.0F, dirty);
2384       break;
2385     case 1:
2386       SceneRotateWithDirty(G, (float) (180 * diff / PI), 1.0F, 0.0F, 0.0F, dirty);
2387       break;
2388     case 2:                    /* z-rotation...useless! */
2389       SceneRotateWithDirty(G, (float) (180 * diff / PI), 0.0F, 0.0F, 1.0F, dirty);
2390       break;
2391     }
2392     break;
2393   case 3:                      /* nutate */
2394     SceneRotateWithDirty(G, (float) (-I->LastSweepY), 0.0F, 1.0F, 0.0F, dirty);
2395     SceneRotateWithDirty(G, (float) (-I->LastSweepX), 1.0F, 0.0F, 0.0F, dirty);
2396     ang_cur = (float) (I->SweepTime * sweep_speed) + sweep_phase;
2397 
2398     I->LastSweepX = (float) (sweep_angle * sin(ang_cur) / 2);
2399     I->LastSweepY = (float) (sweep_angle * sin(ang_cur + shift) / 2);
2400 
2401     if(I->SweepTime * sweep_speed < PI) {
2402       float factor = (float) ((I->SweepTime * sweep_speed) / PI);
2403       I->LastSweepX *= factor;
2404       I->LastSweepY *= factor;
2405     }
2406     SceneRotateWithDirty(G, (float) I->LastSweepX, 1.0F, 0.0F, 0.0F, dirty);
2407     SceneRotateWithDirty(G, (float) I->LastSweepY, 0.0F, 1.0F, 0.0F, dirty);
2408     break;
2409   }
2410 }
2411 
2412 
2413 /*========================================================================*/
SceneIdle(PyMOLGlobals * G)2414 void SceneIdle(PyMOLGlobals * G)
2415 {
2416   CScene *I = G->Scene;
2417   double renderTime;
2418   double minTime;
2419   int frameFlag = false;
2420 
2421   if(I->PossibleSingleClick == 2) {
2422     double now = UtilGetSeconds(G);
2423     double single_click_delay = I->SingleClickDelay;
2424     double diff = now - I->LastReleaseTime;
2425     if(diff > single_click_delay) {
2426       /* post a single click processing event */
2427       SceneDeferClickWhen(I, I->LastButton + P_GLUT_SINGLE_LEFT, I->LastWinX, I->LastWinY, I->LastClickTime, I->LastMod);        /* push a click onto the queue */
2428 
2429       I->PossibleSingleClick = 0;
2430       OrthoDirty(G);            /* force an update */
2431     }
2432   }
2433   if(!OrthoDeferredWaiting(G)) {
2434     if(MoviePlaying(G)) {
2435       renderTime = UtilGetSeconds(G) - I->LastFrameTime;
2436       {
2437         float fps = SettingGetGlobal_f(G, cSetting_movie_fps);
2438         if(fps <= 0.0F) {
2439           if(fps < 0.0)
2440             minTime = 0.0;      /* negative fps means full speed */
2441           else                  /* 0 fps means use movie_delay instead */
2442             minTime = SettingGetGlobal_f(G, cSetting_movie_delay) / 1000.0;
2443           if(minTime >= 0)
2444             fps = 1.0 / minTime;
2445           else
2446             fps = 1000.0F;
2447         } else {
2448           minTime = 1.0 / fps;
2449         }
2450         if(renderTime >= (minTime - I->LastFrameAdjust)) {
2451           float adjust = (renderTime - minTime);
2452           if((fabs(adjust) < minTime) && (fabs(I->LastFrameAdjust) < minTime)) {
2453             float new_adjust = (renderTime - minTime) + I->LastFrameAdjust;
2454             I->LastFrameAdjust = (new_adjust + fps * I->LastFrameAdjust) / (1 + fps);
2455           } else {
2456             I->LastFrameAdjust = 0.0F;
2457           }
2458           frameFlag = true;
2459         }
2460       }
2461     } else if(ControlRocking(G)) {
2462       renderTime = -I->LastSweepTime + UtilGetSeconds(G);
2463       minTime = SettingGetGlobal_f(G, cSetting_rock_delay) / 1000.0;
2464       if(renderTime >= minTime) {
2465         I->RenderTime = renderTime;
2466         SceneUpdateCameraRock(G, true);
2467       }
2468     }
2469 
2470     if(MoviePlaying(G) && frameFlag) {
2471       I->LastFrameTime = UtilGetSeconds(G);
2472       if((SettingGetGlobal_i(G, cSetting_frame) - 1) == (I->NFrame - 1)) {
2473         if(SettingGetGlobal_b(G, cSetting_movie_loop)) {
2474           SceneSetFrame(G, 7, 0);
2475         } else
2476           MoviePlay(G, cMovieStop);
2477       } else {
2478         SceneSetFrame(G, 5, 1);
2479       }
2480       PyMOL_NeedRedisplay(G->PyMOL);
2481     }
2482   }
2483 }
2484 
2485 /*========================================================================*/
2486 /**
2487  * Zoom to location and radius
2488  */
SceneWindowSphere(PyMOLGlobals * G,const float * location,float radius)2489 void SceneWindowSphere(PyMOLGlobals * G, const float *location, float radius)
2490 {
2491   CScene *I = G->Scene;
2492   float v0[3];
2493 
2494 #ifdef _OPENVR_STEREO_DEBUG_VIEWS
2495   printf("%-20s IP: %11lf %11lf %11lf, IF/IB: %11lf %11lf, IS: %11lf  ==>\n",
2496     "WindowSphere BEFORE",
2497     I->m_view.m_pos[0], I->m_view.m_pos[1], I->m_view.m_pos[2],
2498     I->m_view.m_clip.m_front, I->m_view.m_clip.m_back, I->Scale
2499   );
2500 #endif // _OPENVR_STEREO_DEBUG_VIEWS
2501 
2502   if (I->StereoMode == cStereo_openvr) {
2503     I->Scale = 1.0f / radius;
2504     radius = 1.0;
2505   } else {
2506     I->Scale = 1.0;
2507   }
2508   float dist = 2.f * radius / GetFovWidth(G);
2509 
2510   /* find where this point is in relationship to the origin */
2511   subtract3f(I->m_view.m_origin, location, v0);
2512 
2513   MatrixTransformC44fAs33f3f(I->m_view.m_rotMatrix, v0, I->m_view.m_pos); /* convert to view-space */
2514 
2515   if (I->Height > I->Width && I->Height && I->Width)
2516     dist *= I->Height / I->Width;
2517 
2518 #ifdef _PYMOL_OPENVR
2519   /*lift up molecule to the user's head*/
2520   if (I->StereoMode == cStereo_openvr) {
2521     I->m_view.m_pos[0] *= I->Scale;
2522     I->m_view.m_pos[1] = I->m_view.m_pos[1] * I->Scale + 1.0f; //FIXME make it smart
2523     I->m_view.m_pos[2] *= I->Scale;
2524   }
2525 #endif
2526 
2527   I->m_view.m_pos[2] -= dist;
2528   I->m_view.m_clip.m_front = (-I->m_view.m_pos[2] - radius * 1.2F);
2529   I->m_view.m_clip.m_back = (-I->m_view.m_pos[2] + radius * 1.2F);
2530   UpdateFrontBackSafe(I);
2531   SceneRovingDirty(G);
2532 
2533 #ifdef _OPENVR_STEREO_DEBUG_VIEWS
2534   printf("%-20s IP: %11lf %11lf %11lf, IF/IB: %11lf %11lf, IS: %11lf  ==>\n",
2535     "WindowSphere AFTER",
2536     I->m_view.m_pos[0], I->m_view.m_pos[1], I->m_view.m_pos[2],
2537     I->m_view.m_clip.m_front, I->m_view.m_clip.m_back, I->Scale
2538   );
2539 #endif // _OPENVR_STEREO_DEBUG_VIEWS
2540 }
2541 
2542 
2543 /*========================================================================*/
SceneRelocate(PyMOLGlobals * G,const float * location)2544 void SceneRelocate(PyMOLGlobals * G, const float *location)
2545 {
2546   CScene *I = G->Scene;
2547   float v0[3];
2548   float slab_width;
2549   float dist;
2550 
2551   slab_width = I->m_view.m_clip.m_back - I->m_view.m_clip.m_front;
2552 
2553   /* find out how far camera was from previous origin */
2554   dist = I->m_view.m_pos[2];
2555 
2556   // stay in front of camera, empirical value to show at least 1 bond
2557   if (dist > -5.f && I->StereoMode != cStereo_openvr)
2558     dist = -5.f;
2559 
2560   /* find where this point is in relationship to the origin */
2561   subtract3f(I->m_view.m_origin, location, v0);
2562 
2563   /*  printf("%8.3f %8.3f %8.3f\n",I->m_view.m_clip.m_front,I->m_view.m_pos[2],I->m_view.m_clip.m_back); */
2564 
2565   MatrixTransformC44fAs33f3f(I->m_view.m_rotMatrix, v0, I->m_view.m_pos); /* convert to view-space */
2566 
2567   I->m_view.m_pos[2] = dist;
2568   if (I->StereoMode == cStereo_openvr) {
2569     I->m_view.m_pos[1] += 1.0f;
2570   }
2571   I->m_view.m_clip.m_front = (-I->m_view.m_pos[2] - (slab_width * 0.50F));
2572   I->m_view.m_clip.m_back = (-I->m_view.m_pos[2] + (slab_width * 0.50F));
2573   UpdateFrontBackSafe(I);
2574   SceneRovingDirty(G);
2575 
2576 }
2577 
2578 
2579 /*========================================================================*/
2580 /**
2581  * Get the origin of rotation in model space
2582  * cmd.get_view()[12:15]
2583  *
2584  * @param[out] origin
2585  */
SceneOriginGet(PyMOLGlobals * G,float * origin)2586 void SceneOriginGet(PyMOLGlobals * G, float *origin)
2587 {
2588   CScene *I = G->Scene;
2589   copy3f(I->m_view.m_origin, origin);
2590 }
2591 
2592 
2593 /*========================================================================*/
2594 /**
2595  * Set the origin of rotation in model space
2596  * (`cmd.get_view()[12:15]`)
2597  *
2598  * @param origin New origin
2599  * @param preserve preserve current viewing location
2600  */
SceneOriginSet(PyMOLGlobals * G,const float * origin,int preserve)2601 void SceneOriginSet(PyMOLGlobals * G, const float *origin, int preserve)
2602 {
2603   CScene *I = G->Scene;
2604   float v0[3], v1[3];
2605 
2606   if(preserve) {                /* preserve current viewing location */
2607     subtract3f(origin, I->m_view.m_origin, v0);  /* model-space translation */
2608     MatrixTransformC44fAs33f3f(I->m_view.m_rotMatrix, v0, v1);   /* convert to view-space */
2609     add3f(I->m_view.m_pos, v1, I->m_view.m_pos);  /* offset view to compensate */
2610   }
2611   I->m_view.m_origin[0] = origin[0];     /* move origin */
2612   I->m_view.m_origin[1] = origin[1];
2613   I->m_view.m_origin[2] = origin[2];
2614   SceneInvalidate(G);
2615 }
2616 
2617 
2618 /*========================================================================*/
SceneObjectAdd(PyMOLGlobals * G,CObject * obj)2619 int SceneObjectAdd(PyMOLGlobals * G, CObject * obj)
2620 {
2621   CScene *I = G->Scene;
2622   obj->Enabled = true;
2623   I->Obj.push_back(obj);
2624   if(obj->type == cObjectGadget) {
2625     I->GadgetObjs.push_back(obj);
2626   } else {
2627     I->NonGadgetObjs.push_back(obj);
2628   }
2629   SceneCountFrames(G);
2630   SceneChanged(G);
2631   SceneInvalidatePicking(G); // PYMOL-2793
2632   return 1;
2633 }
2634 
2635 
2636 /*========================================================================*/
SceneObjectIsActive(PyMOLGlobals * G,CObject * obj)2637 int SceneObjectIsActive(PyMOLGlobals * G, CObject * obj)
2638 {
2639   int result = false;
2640   CScene *I = G->Scene;
2641   if (find(I->Obj.begin(), I->Obj.end(), obj) != I->Obj.end())
2642       result = true;
2643   return result;
2644 }
2645 
SceneObjectDel(PyMOLGlobals * G,CObject * obj,int allow_purge)2646 int SceneObjectDel(PyMOLGlobals * G, CObject * obj, int allow_purge)
2647 {
2648   CScene *I = G->Scene;
2649   int defer_builds_mode = SettingGetGlobal_i(G, cSetting_defer_builds_mode);
2650 
2651   if(!obj) {                    /* deletes all members */
2652     if(allow_purge && (defer_builds_mode >= 3)) {
2653       for (auto& obj : I->Obj) {
2654         /* purge graphics representation when no longer used */
2655         obj->invalidate(cRepAll, cRepInvPurge, -1);
2656       }
2657     }
2658     I->Obj.clear();
2659     I->GadgetObjs.clear();
2660     I->NonGadgetObjs.clear();
2661   } else {
2662     auto &obj_list = (obj->type == cObjectGadget) ? I->GadgetObjs : I->NonGadgetObjs;
2663     auto itg = find(obj_list.begin(), obj_list.end(), obj);
2664     if (itg != obj_list.end())
2665       obj_list.erase(itg);
2666 
2667     auto it = find(I->Obj.begin(), I->Obj.end(), obj);
2668     if (it != I->Obj.end()){
2669       if(allow_purge && (defer_builds_mode >= 3)) {
2670         /* purge graphics representation when no longer used */
2671         (*it)->invalidate(cRepAll, cRepInvPurge, -1);
2672       }
2673       obj->Enabled = false;
2674       I->Obj.erase(it);
2675     }
2676   }
2677   SceneCountFrames(G);
2678   SceneInvalidate(G);
2679   SceneInvalidatePicking(G);
2680   return 0;
2681 }
2682 
2683 
2684 /*========================================================================*/
SceneLoadPNG(PyMOLGlobals * G,const char * fname,int movie_flag,int stereo,int quiet)2685 int SceneLoadPNG(PyMOLGlobals * G, const char *fname, int movie_flag, int stereo, int quiet)
2686 {
2687   CScene *I = G->Scene;
2688   int ok = false;
2689   if(I->Image) {
2690     ScenePurgeImage(G);
2691     I->CopyType = false;
2692     OrthoInvalidateDoDraw(G); // right now, need to invalidate since text could be shown
2693   }
2694   I->Image = MyPNGRead(fname);
2695   if(I->Image) {
2696     if(!quiet) {
2697       PRINTFB(G, FB_Scene, FB_Details)
2698         " Scene: loaded image from '%s'.\n", fname ENDFB(G);
2699     }
2700     if((stereo > 0) || ((stereo < 0) &&
2701                         (I->Image->getWidth() == 2 * I->Width) &&
2702                         (I->Image->getHeight() == I->Height))) {
2703       *(I->Image) = I->Image->deinterlace(stereo == 2);
2704     }
2705 
2706     I->CopyType = true;
2707     I->CopyForced = true;
2708     OrthoRemoveSplash(G);
2709     SettingSetGlobal_b(G, cSetting_text, 0);
2710     if(movie_flag &&
2711        I->Image && !I->Image->empty()) {
2712       MovieSetImage(G, MovieFrameToImage(G, SettingGetGlobal_i(G, cSetting_frame) - 1)
2713                     , I->Image);
2714       I->MovieFrameFlag = true;
2715     } else {
2716       I->DirtyFlag = false;     /* make sure we don't overwrite image */
2717     }
2718     OrthoDirty(G);
2719     ok = true;
2720   } else {
2721     if(!quiet) {
2722       PRINTFB(G, FB_Scene, FB_Errors)
2723         " Scene: unable to load image from '%s'.\n", fname ENDFB(G);
2724     }
2725   }
2726   return (ok);
2727 }
2728 
2729 
2730 /*static unsigned int byte_max(unsigned int value)
2731 {
2732   return (value>0xFF) ? 0xFF : value;
2733 }
2734 */
2735 
2736 #define SceneClickMargin DIP2PIXEL(2)
2737 #define SceneTopMargin 0
2738 #define SceneToggleMargin DIP2PIXEL(2)
2739 #define SceneRightMargin 0
2740 #define SceneToggleWidth DIP2PIXEL(17)
2741 #define SceneToggleSize DIP2PIXEL(16)
2742 #define SceneToggleTextShift DIP2PIXEL(4)
2743 #define SceneTextLeftMargin DIP2PIXEL(1)
2744 #define SceneScrollBarMargin DIP2PIXEL(1)
2745 #define SceneScrollBarWidth DIP2PIXEL(13)
2746 #ifndef _PYMOL_NOPY
draw_button(int x2,int y2,int z,int w,int h,float * light,float * dark,float * inside ORTHOCGOARG)2747 static void draw_button(int x2, int y2, int z, int w, int h, float *light, float *dark,
2748                         float *inside ORTHOCGOARG)
2749 {
2750   if (orthoCGO){
2751     CGOColorv(orthoCGO, light);
2752     CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
2753     CGOVertex(orthoCGO, x2, y2, z);
2754     CGOVertex(orthoCGO, x2, y2 + h, z);
2755     CGOVertex(orthoCGO, x2 + w, y2, z);
2756     CGOVertex(orthoCGO, x2 + w, y2 + h, z);
2757     CGOEnd(orthoCGO);
2758   } else {
2759     glColor3fv(light);
2760     glBegin(GL_POLYGON);
2761     glVertex3i(x2, y2, z);
2762     glVertex3i(x2, y2 + h, z);
2763     glVertex3i(x2 + w, y2 + h, z);
2764     glVertex3i(x2 + w, y2, z);
2765     glEnd();
2766   }
2767 
2768   if (orthoCGO){
2769     CGOColorv(orthoCGO, dark);
2770     CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
2771     CGOVertex(orthoCGO, x2 + 1, y2, z);
2772     CGOVertex(orthoCGO, x2 + 1, y2 + h - 1, z);
2773     CGOVertex(orthoCGO, x2 + w, y2, z);
2774     CGOVertex(orthoCGO, x2 + w, y2 + h - 1, z);
2775     CGOEnd(orthoCGO);
2776   } else {
2777     glColor3fv(dark);
2778     glBegin(GL_POLYGON);
2779     glVertex3i(x2 + 1, y2, z);
2780     glVertex3i(x2 + 1, y2 + h - 1, z);
2781     glVertex3i(x2 + w, y2 + h - 1, z);
2782     glVertex3i(x2 + w, y2, z);
2783     glEnd();
2784   }
2785 
2786   if(inside) {
2787     if (orthoCGO){
2788       CGOColorv(orthoCGO, inside);
2789       CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
2790       CGOVertex(orthoCGO, x2 + 1, y2 + 1, z);
2791       CGOVertex(orthoCGO, x2 + 1, y2 + h - 1, z);
2792       CGOVertex(orthoCGO, x2 + w - 1, y2 + 1, z);
2793       CGOVertex(orthoCGO, x2 + w - 1, y2 + h - 1, z);
2794       CGOEnd(orthoCGO);
2795     } else {
2796       glColor3fv(inside);
2797       glBegin(GL_POLYGON);
2798       glVertex3i(x2 + 1, y2 + 1, z);
2799       glVertex3i(x2 + 1, y2 + h - 1, z);
2800       glVertex3i(x2 + w - 1, y2 + h - 1, z);
2801       glVertex3i(x2 + w - 1, y2 + 1, z);
2802       glEnd();
2803     }
2804   } else {                      /* rainbow */
2805     if (orthoCGO){
2806       CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
2807       CGOColor(orthoCGO, 0.1F, 1.0F, 0.1F); // green
2808       CGOVertex(orthoCGO, x2 + 1, y2 + h - 1, z);
2809       CGOColor(orthoCGO, 1.0F, 1.0F, 0.1F);  // yellow
2810       CGOVertex(orthoCGO, x2 + w - 1, y2 + h - 1, z);
2811       CGOColor(orthoCGO, 1.f, 0.1f, 0.1f); // red
2812       CGOVertex(orthoCGO, x2 + 1, y2 + 1, z);
2813       CGOColor(orthoCGO, 0.1F, 0.1F, 1.0F);  // blue
2814       CGOVertex(orthoCGO, x2 + w - 1, y2 + 1, z);
2815       CGOEnd(orthoCGO);
2816     } else {
2817       glBegin(GL_POLYGON);
2818       glColor3f(1.0F, 0.1F, 0.1F);
2819       glVertex3i(x2 + 1, y2 + 1, z);
2820       glColor3f(0.1F, 1.0F, 0.1F);
2821       glVertex3i(x2 + 1, y2 + h - 1, z);
2822       glColor3f(1.0F, 1.0F, 0.1F);
2823       glVertex3i(x2 + w - 1, y2 + h - 1, z);
2824       glColor3f(0.1F, 0.1F, 1.0F);
2825       glVertex3i(x2 + w - 1, y2 + 1, z);
2826       glEnd();
2827     }
2828   }
2829 }
2830 #endif
2831 
2832 /**
2833  * Update the G->Scene->SceneVLA names array which is used for scene buttons
2834  *
2835  * @param list List of scene names
2836  */
SceneSetNames(PyMOLGlobals * G,const std::vector<std::string> & list)2837 void SceneSetNames(PyMOLGlobals * G, const std::vector<std::string> &list)
2838 {
2839   CScene *I = G->Scene;
2840   I->NScene = (int)list.size();
2841   VLACheck(I->SceneVLA, SceneElem, I->NScene);
2842   SceneElem *elem = I->SceneVLA;
2843 
2844   for(int a = 0; a < I->NScene; ++a) {
2845     elem->name = (char*) list[a].c_str();
2846     elem->len = (int)list[a].length();
2847     elem->drawn = false;
2848     elem++;
2849   }
2850 
2851   OrthoDirty(G);
2852 }
2853 
2854 
2855 /*========================================================================*/
SceneDrawButtons(Block * block,int draw_for_real ORTHOCGOARG)2856 static void SceneDrawButtons(Block * block, int draw_for_real ORTHOCGOARG)
2857 {
2858 #ifndef _PYMOL_NOPY
2859   PyMOLGlobals *G = block->m_G;
2860   CScene *I = G->Scene;
2861   int x, y, xx, x2;
2862   const char *c = NULL;
2863   float enabledColor[3] = { 0.5F, 0.5F, 0.5F };
2864   float pressedColor[3] = { 0.7F, 0.7F, 0.7F };
2865   float disabledColor[3] = { 0.25F, 0.25F, 0.25F };
2866   float lightEdge[3] = { 0.6F, 0.6F, 0.6F };
2867   float darkEdge[3] = { 0.35F, 0.35F, 0.35F };
2868   int charWidth = DIP2PIXEL(8);
2869   int n_ent;
2870   int n_disp;
2871   int skip = 0;
2872   int row = -1;
2873   int lineHeight = DIP2PIXEL(SettingGetGlobal_i(G, cSetting_internal_gui_control_size));
2874   int text_lift = (lineHeight / 2) - DIP2PIXEL(5);
2875   int op_cnt = 1;
2876 
2877   if(((G->HaveGUI && G->ValidContext) || (!draw_for_real)) &&
2878      ((block->rect.right - block->rect.left) > 6) && (I->NScene)) {
2879     int max_char;
2880     int nChar;
2881     I->ButtonsShown = true;
2882 
2883     /* do we have enough structures to warrant a scroll bar? */
2884     n_ent = I->NScene;
2885 
2886     n_disp =
2887       (((I->rect.top - I->rect.bottom) - (SceneTopMargin)) / lineHeight) -
2888       1;
2889     if(n_disp < 1)
2890       n_disp = 1;
2891 
2892     {
2893       int i;
2894       for(i = 0; i < I->NScene; i++)
2895         I->SceneVLA[i].drawn = false;
2896     }
2897     if(n_ent > n_disp) {
2898       int bar_maxed = I->m_ScrollBar.isMaxed();
2899       if(!I->ScrollBarActive) {
2900         I->m_ScrollBar.setLimits(n_ent, n_disp);
2901         if(bar_maxed) {
2902           I->m_ScrollBar.maxOut();
2903           I->NSkip = static_cast<int>(I->m_ScrollBar.getValue());
2904         } else {
2905           I->m_ScrollBar.setValue(0);
2906           I->NSkip = 0;
2907         }
2908       } else {
2909         I->m_ScrollBar.setLimits(n_ent, n_disp);
2910         if(bar_maxed)
2911           I->m_ScrollBar.maxOut();
2912         I->NSkip = static_cast<int>(I->m_ScrollBar.getValue());
2913       }
2914       I->ScrollBarActive = 1;
2915 
2916     } else {
2917       I->ScrollBarActive = 0;
2918       I->NSkip = 0;
2919     }
2920 
2921     max_char = (((I->rect.right - I->rect.left) -
2922                  (SceneTextLeftMargin + SceneRightMargin + 4)) -
2923                 (op_cnt * SceneToggleWidth));
2924     if(I->ScrollBarActive) {
2925       max_char -= (SceneScrollBarMargin + SceneScrollBarWidth);
2926     }
2927     max_char /= charWidth;
2928 
2929     if(I->ScrollBarActive) {
2930       I->m_ScrollBar.setBox(I->rect.top - SceneScrollBarMargin,
2931                             I->rect.left + SceneScrollBarMargin,
2932                             I->rect.bottom + 2,
2933                             I->rect.left + SceneScrollBarMargin + SceneScrollBarWidth);
2934       if(draw_for_real)
2935         I->m_ScrollBar.draw(orthoCGO);
2936     }
2937 
2938     skip = I->NSkip;
2939     x = I->rect.left + SceneTextLeftMargin;
2940 
2941     /*    y = ((I->rect.top-lineHeight)-SceneTopMargin)-lineHeight; */
2942 
2943     {
2944       int n_vis = n_disp;
2945       if(n_ent < n_vis)
2946         n_vis = n_ent;
2947       y = (I->rect.bottom + SceneBottomMargin) + (n_vis - 1) * lineHeight;
2948     }
2949 
2950     /*    xx = I->rect.right-SceneRightMargin-SceneToggleWidth*(cRepCnt+op_cnt); */
2951     xx = I->rect.right - SceneRightMargin - SceneToggleWidth * (op_cnt);
2952 
2953     if(I->ScrollBarActive) {
2954       x += SceneScrollBarWidth + SceneScrollBarMargin;
2955     }
2956     {
2957       int i;
2958 
2959       for(i = 0; i < n_ent; i++) {
2960         if(skip) {
2961           skip--;
2962         } else {
2963           row++;
2964           x2 = xx;
2965           nChar = max_char;
2966 
2967           if((x - SceneToggleMargin) - (xx - SceneToggleMargin) > -10) {
2968             x2 = x + 10;
2969           }
2970           {
2971             float toggleColor[3] = { 0.5F, 0.5F, 1.0F };
2972 
2973             if(draw_for_real) {
2974               glColor3fv(toggleColor);
2975 
2976               TextSetColor(G, I->TextColor);
2977               TextSetPos2i(G, x + DIP2PIXEL(2), y + text_lift);
2978             }
2979             {
2980               int len;
2981               const char *cur_name = SettingGetGlobal_s(G, cSetting_scene_current_name);
2982               SceneElem *elem = I->SceneVLA + i;
2983               int item = I->NSkip + row;
2984               c = elem->name;
2985               len = elem->len;
2986 
2987               x2 = xx;
2988               if(len > max_char)
2989                 len = max_char;
2990               x2 = x + len * charWidth + DIP2PIXEL(6);
2991 
2992               /* store rectangles for finding clicks */
2993 
2994               elem->drawn = true;
2995 
2996               elem->x1 = x;
2997               elem->y1 = y;
2998               elem->x2 = x2;
2999               elem->y2 = y + lineHeight;
3000 
3001               if(I->ButtonMargin < x2)
3002                 I->ButtonMargin = x2;
3003 
3004               if(draw_for_real) {
3005 
3006                 if((item == I->Pressed) && (item == I->Over)) {
3007                   draw_button(x, y, 0, (x2 - x) - 1, (lineHeight - 1), lightEdge,
3008                               darkEdge, pressedColor ORTHOCGOARGVAR);
3009                 } else if(cur_name && elem->name && (!strcmp(elem->name, cur_name))) {
3010                   draw_button(x, y, 0, (x2 - x) - 1, (lineHeight - 1), lightEdge,
3011                               darkEdge, enabledColor ORTHOCGOARGVAR);
3012                 } else {
3013                   draw_button(x, y, 0, (x2 - x) - 1, (lineHeight - 1), lightEdge,
3014                               darkEdge, disabledColor ORTHOCGOARGVAR);
3015                 }
3016 
3017                 TextSetColor(G, I->TextColor);
3018 
3019                 if(c) {
3020                   while(*c) {
3021                     if((nChar--) > 0)
3022                       TextDrawChar(G, *(c++) ORTHOCGOARGVAR);
3023                     else
3024                       break;
3025                   }
3026                 }
3027               }
3028             }
3029           }
3030           y -= lineHeight;
3031           if(y < (I->rect.bottom))
3032             break;
3033         }
3034       }
3035     }
3036     I->HowFarDown = y;
3037     I->ButtonsValid = true;
3038   }
3039 #endif
3040 }
3041 
SceneDrawImageOverlay(PyMOLGlobals * G,int override ORTHOCGOARG)3042 int SceneDrawImageOverlay(PyMOLGlobals * G, int override ORTHOCGOARG){
3043   CScene *I = G->Scene;
3044   int drawn = false;
3045   int text = SettingGetGlobal_b(G, cSetting_text);
3046     /* is the text/overlay (ESC) on? */
3047   int overlay = OrthoGetOverlayStatus(G);
3048 
3049   if(((!text) || overlay) && (override || I->CopyType == true) && I->Image && !I->Image->empty()) {
3050     /* show transparent bg as checkboard? */
3051     int show_alpha = SettingGetGlobal_b(G, cSetting_show_alpha_checker);
3052     const float *bg_color = ColorGet(G, SettingGet_color(G, NULL, NULL, cSetting_bg_rgb));
3053     unsigned int bg_rr, bg_r = (unsigned int) (255 * bg_color[0]);
3054     unsigned int bg_gg, bg_g = (unsigned int) (255 * bg_color[1]);
3055     unsigned int bg_bb, bg_b = (unsigned int) (255 * bg_color[2]);
3056     int width = I->Image->getWidth();
3057     int height = I->Image->getHeight();
3058     unsigned char *data = I->Image->bits();
3059 
3060     if(I->Image->isStereo()) {
3061       int buffer;
3062       glGetIntegerv(GL_DRAW_BUFFER, (GLint *) & buffer);
3063       if(buffer == GL_BACK_RIGHT)     /* hardware stereo */
3064 	data += I->Image->getSizeInBytes();
3065       else {
3066 	int stereo = SettingGetGlobal_i(G, cSetting_stereo);
3067 	if (stereo){
3068 	  switch (OrthoGetRenderMode(G)) {
3069 	  case cStereo_geowall:
3070 	    data += I->Image->getSizeInBytes();
3071 	    break;
3072 	  }
3073 	}
3074       }
3075       /* if drawing the right buffer, then draw the right image */
3076     }
3077 
3078     if((height > I->Height) || (width > I->Width)) {  /* image is oversize */
3079       {
3080 	int factor = 1;
3081 	int shift = 0;
3082 	int tmp_height = I->Image->getHeight();
3083 	int tmp_width = I->Image->getWidth();
3084 	int src_row_bytes = I->Image->getWidth() * pymol::Image::getPixelSize();
3085 	unsigned int color_word;
3086 	float rgba[4] = { 0.0F, 0.0F, 0.0F, 1.0F };
3087 
3088 	ColorGetBkrdContColor(G, rgba, false);
3089 	color_word = ColorGet32BitWord(G, rgba);
3090 
3091 	while(tmp_height && tmp_width &&
3092 	      ((tmp_height > (I->Height - 3)) || (tmp_width > (I->Width - 3)))) {
3093 	  tmp_height = (tmp_height >> 1);
3094 	  tmp_width = (tmp_width >> 1);
3095 	  factor = (factor << 1);
3096 	  shift++;
3097 	}
3098 	tmp_width += 2;
3099 	tmp_height += 2;
3100 
3101 	if(tmp_height && tmp_width) {
3102 	  unsigned int buffer_size = tmp_height * tmp_width * 4;
3103 	  unsigned char *buffer = pymol::malloc<unsigned char>(buffer_size);
3104 
3105 	  if(buffer && data) {
3106 	    unsigned char *p = data;
3107 	    unsigned char *q = buffer;
3108 	    unsigned char *pp, *ppp, *pppp;
3109 	    int a, b, c, d;
3110 	    unsigned int c1, c2, c3, c4, alpha, tot, bg;
3111 	    unsigned int factor_col_bytes = factor * 4;
3112 	    unsigned int factor_row_bytes = factor * src_row_bytes;
3113 
3114 	    shift = shift + shift;
3115 
3116 	    for(a = 0; a < tmp_width; a++) {      /* border, first row */
3117 	      *((unsigned int *) (q)) = color_word;
3118 	      q += 4;
3119 	    }
3120 	    for(b = 1; b < tmp_height-1; b++) { /* rows */
3121 	      pp = p;
3122 	      *((unsigned int *) (q)) = color_word;        /* border */
3123 	      q += 4;
3124 	      for(a = 1; a < tmp_width-1; a++) {      /* cols */
3125 		ppp = pp;
3126 
3127 		c1 = c2 = c3 = c4 = tot = 0;
3128 
3129 		if(show_alpha && (((a >> 4) + (b >> 4)) & 0x1)) { /* introduce checkerboard */
3130 		  bg_rr = ((bg_r & 0x80) ? bg_r - TRN_BKG : bg_r + TRN_BKG);
3131 		  bg_gg = ((bg_g & 0x80) ? bg_g - TRN_BKG : bg_g + TRN_BKG);
3132 		  bg_bb = ((bg_b & 0x80) ? bg_b - TRN_BKG : bg_b + TRN_BKG);
3133 		} else {
3134 		  bg_rr = bg_r;
3135 		  bg_gg = bg_g;
3136 		  bg_bb = bg_b;
3137 		}
3138 
3139 		for(d = 0; d < factor; d++) {     /* box rows */
3140 		  pppp = ppp;
3141 		  for(c = 0; c < factor; c++) {   /* box cols */
3142 		    alpha = pppp[3];
3143 		    c1 += *(pppp++) * alpha;
3144 		    c2 += *(pppp++) * alpha;
3145 		    c3 += *(pppp++) * alpha;
3146 		    pppp++;
3147 		    c4 += alpha;
3148 		    tot += 0xFF;
3149 		  }
3150 		  ppp += src_row_bytes;
3151 		}
3152 		if(c4) {
3153 		  bg = tot - c4;
3154 		  *(q++) = (c1 + bg_rr * bg) / tot;
3155 		  *(q++) = (c2 + bg_gg * bg) / tot;
3156 		  *(q++) = (c3 + bg_bb * bg) / tot;
3157 		  *(q++) = 0xFF;
3158 		} else {
3159 		  *(q++) = bg_rr;
3160 		  *(q++) = bg_gg;
3161 		  *(q++) = bg_bb;
3162 		  *(q++) = 0xFF;
3163 		}
3164 		pp += factor_col_bytes;
3165 	      }
3166 	      *((unsigned int *) (q)) = color_word;        /* border */
3167 	      q += 4;
3168 	      p += factor_row_bytes;
3169 	    }
3170 	    for(a = 0; a < tmp_width; a++) {      /* border, last row */
3171 	      *((unsigned int *) (q)) = color_word;
3172 	      q += 4;
3173 	    }
3174 
3175 
3176 #ifndef PURE_OPENGL_ES_2
3177         glRasterPos3i((int) ((I->Width - tmp_width) / 2 + I->rect.left),
3178               (int) ((I->Height - tmp_height) / 2 + I->rect.bottom),
3179 			  -10);
3180 #endif
3181 	    PyMOLDrawPixels(tmp_width, tmp_height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
3182 	    drawn = true;
3183 	  }
3184 	  FreeP(buffer);
3185 	}
3186 	{
3187 	  char buffer[255];
3188 	  int text_pos = (I->Height - tmp_height) / 2 - 15;
3189 	  int x_pos, y_pos;
3190 	  if(text_pos < 0) {
3191 	    text_pos = (I->Height - tmp_height) / 2 + 3;
3192 	    x_pos = (I->Width - tmp_width) / 2 + 3;
3193 	    y_pos = text_pos;
3194 	  } else {
3195 	    x_pos = (I->Width - tmp_width) / 2;
3196 	    y_pos = text_pos;
3197 	  }
3198 
3199 	  sprintf(buffer, "Image size = %d x %d", I->Image->getWidth(), I->Image->getHeight());
3200 
3201 	  TextSetColor3f(G, rgba[0], rgba[1], rgba[2]);
3202 	  TextDrawStrAt(G, buffer,
3203             x_pos + I->rect.left, y_pos + I->rect.bottom ORTHOCGOARGVAR);
3204 	}
3205       }
3206     } else if(((width < I->Width) || (height < I->Height)) && ((I->Width - width) > 2) && ((I->Height - height) > 2)) {
3207       /* but a border around image */
3208 
3209       unsigned int color_word;
3210       float rgba[4] = { 0.0F, 0.0F, 0.0F, 1.0F };
3211       unsigned int tmp_height = height + 2;
3212       unsigned int tmp_width = width + 2;
3213       unsigned int border = 1;
3214       unsigned int upscale = 1;
3215 
3216       // Upscale for Retina/4K
3217       if (DIP2PIXEL(height) == I->Height && DIP2PIXEL(width) == I->Width) {
3218         upscale = DIP2PIXEL(1);
3219         tmp_height = DIP2PIXEL(height);
3220         tmp_width = DIP2PIXEL(width);
3221         border = 0;
3222       }
3223 
3224       unsigned int n_word = tmp_height * tmp_width;
3225       unsigned int *tmp_buffer = pymol::malloc<unsigned int>(n_word);
3226       ColorGetBkrdContColor(G, rgba, false);
3227       color_word = ColorGet32BitWord(G, rgba);
3228 
3229       if(tmp_buffer) {
3230 	unsigned int a, b;
3231 	unsigned int *p = (unsigned int *) data;
3232 	unsigned int *q = tmp_buffer;
3233 
3234 	// top border
3235 	for(a = 0; a < border; ++a) {
3236 	  for(b = 0; b < tmp_width; b++)
3237 	    *(q++) = color_word;
3238 	}
3239 
3240 	for(a = border; a < tmp_height - border; a++) {
3241 	  // left border
3242 	  for(b = 0; b < border; ++b) {
3243 	    *(q++) = color_word;
3244 	  }
3245 
3246 	  for(b = border; b < tmp_width - border; b++) {
3247 	    unsigned char *qq = (unsigned char *) q;
3248 	    unsigned char *pp = (unsigned char *) p;
3249 	    unsigned char bg;
3250 	    if(show_alpha && (((a >> 4) + (b >> 4)) & 0x1)) {     /* introduce checkerboard */
3251 	      bg_rr = ((bg_r & 0x80) ? bg_r - TRN_BKG : bg_r + TRN_BKG);
3252 	      bg_gg = ((bg_g & 0x80) ? bg_g - TRN_BKG : bg_g + TRN_BKG);
3253 	      bg_bb = ((bg_b & 0x80) ? bg_b - TRN_BKG : bg_b + TRN_BKG);
3254 	    } else {
3255 	      bg_rr = bg_r;
3256 	      bg_gg = bg_g;
3257 	      bg_bb = bg_b;
3258 	    }
3259 	    if(pp[3]) {
3260 	      bg = 0xFF - pp[3];
3261 	      *(qq++) = (pp[0] * pp[3] + bg_rr * bg) / 0xFF;
3262 	      *(qq++) = (pp[1] * pp[3] + bg_gg * bg) / 0xFF;
3263 	      *(qq++) = (pp[2] * pp[3] + bg_bb * bg) / 0xFF;
3264 	      *(qq++) = 0xFF;
3265 	    } else {
3266 	      *(qq++) = bg_rr;
3267 	      *(qq++) = bg_gg;
3268 	      *(qq++) = bg_bb;
3269 	      *(qq++) = 0xFF;
3270 	    }
3271 	    q++;
3272 
3273 	    if ((b + 1 - border) % upscale == 0) {
3274 	      p++;
3275 	    }
3276 	  }
3277 
3278 	  if ((a + 1 - border) % upscale != 0) {
3279 	    // read row again
3280 	    p -= width;
3281 	  }
3282 
3283 	  // right border
3284 	  for(b = 0; b < border; ++b) {
3285 	    *(q++) = color_word;
3286 	  }
3287 	}
3288 
3289 	// bottom border
3290         for(a = 0; a < border; ++a) {
3291 	  for(b = 0; b < tmp_width; b++)
3292 	    *(q++) = color_word;
3293 	}
3294 
3295 #ifndef PURE_OPENGL_ES_2
3296     glRasterPos3i((int) ((I->Width - tmp_width) / 2 + I->rect.left),
3297               (int) ((I->Height - tmp_height) / 2 + I->rect.bottom),
3298 		      -10);
3299 #endif
3300 	PyMOLDrawPixels(tmp_width, tmp_height, GL_RGBA, GL_UNSIGNED_BYTE, tmp_buffer);
3301 	drawn = true;
3302       }
3303       FreeP(tmp_buffer);
3304     } else if(I->CopyForced) {        /* near-exact fit */
3305       float rgba[4] = { 0.0F, 0.0F, 0.0F, 1.0F };
3306       unsigned int n_word = height * width;
3307       unsigned int *tmp_buffer = pymol::malloc<unsigned int>(n_word);
3308       ColorGetBkrdContColor(G, rgba, false);
3309 
3310       if(tmp_buffer) {
3311 	unsigned int a, b;
3312 	unsigned int *p = (unsigned int *) data;
3313 	unsigned int *q = tmp_buffer;
3314 	for(a = 0; a < (unsigned int) height; a++) {
3315 	  for(b = 0; b < (unsigned int) width; b++) {
3316 	    unsigned char *qq = (unsigned char *) q;
3317 	    unsigned char *pp = (unsigned char *) p;
3318 	    unsigned char bg;
3319 	    if(show_alpha && (((a >> 4) + (b >> 4)) & 0x1)) { /* introduce checkerboard */
3320 	      bg_rr = ((bg_r & 0x80) ? bg_r - TRN_BKG : bg_r + TRN_BKG);
3321 	      bg_gg = ((bg_g & 0x80) ? bg_g - TRN_BKG : bg_g + TRN_BKG);
3322 	      bg_bb = ((bg_b & 0x80) ? bg_b - TRN_BKG : bg_b + TRN_BKG);
3323 	    } else {
3324 	      bg_rr = bg_r;
3325 	      bg_gg = bg_g;
3326 	      bg_bb = bg_b;
3327 	    }
3328 	    if(pp[3]) {
3329 	      bg = 0xFF - pp[3];
3330 	      *(qq++) = (pp[0] * pp[3] + bg_rr * bg) / 0xFF;
3331 	      *(qq++) = (pp[1] * pp[3] + bg_gg * bg) / 0xFF;
3332 	      *(qq++) = (pp[2] * pp[3] + bg_bb * bg) / 0xFF;
3333 	      *(qq++) = 0xFF;
3334 	    } else {
3335 	      *(qq++) = bg_rr;
3336 	      *(qq++) = bg_gg;
3337 	      *(qq++) = bg_bb;
3338 	    *(qq++) = 0xFF;
3339 	    }
3340 	    q++;
3341 	    p++;
3342 	  }
3343 	}
3344       }
3345 #ifndef PURE_OPENGL_ES_2
3346       glRasterPos3i((int) ((I->Width - width) / 2 + I->rect.left),
3347             (int) ((I->Height - height) / 2 + I->rect.bottom), -10);
3348 #endif
3349       PyMOLDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, tmp_buffer);
3350       drawn = true;
3351       FreeP(tmp_buffer);
3352     } else {                  /* not a forced copy, so don't show/blend alpha */
3353 #ifndef PURE_OPENGL_ES_2
3354       glRasterPos3i((int) ((I->Width - width) / 2 + I->rect.left),
3355             (int) ((I->Height - height) / 2 + I->rect.bottom), -10);
3356 #endif
3357       PyMOLDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
3358       drawn = true;
3359     }
3360 
3361     I->LastRender = UtilGetSeconds(G);
3362   }
3363   return drawn;
3364 }
3365 
draw(CGO * orthoCGO)3366 void CScene::draw(CGO* orthoCGO) /* returns true if scene was drawn (using a cached image) */
3367 {
3368   PyMOLGlobals *G = m_G;
3369   CScene *I = G->Scene;
3370   int drawn = false;
3371 
3372   if(G->HaveGUI && G->ValidContext) {
3373 
3374     I->ButtonsShown = false;
3375 
3376     drawn = SceneDrawImageOverlay(G, 0 ORTHOCGOARGVAR);
3377 
3378     if(SettingGetGlobal_b(G, cSetting_scene_buttons)) {
3379       SceneDrawButtons(this, true ORTHOCGOARGVAR);
3380     } else {
3381       I->ButtonMargin = 0;
3382     }
3383   }
3384   if(drawn)
3385     OrthoDrawWizardPrompt(G ORTHOCGOARGVAR); /* ugly hack necessitated because wizard
3386 						prompt is overwritten when image is drawn */
3387 
3388 }
3389 
SceneGetButtonMargin(PyMOLGlobals * G)3390 int SceneGetButtonMargin(PyMOLGlobals * G)
3391 {
3392   CScene *I = G->Scene;
3393   return I->ButtonMargin;
3394 }
3395 
3396 /*========================================================================*/
SceneRelease(Block * block,int button,int x,int y,int mod,double when)3397 static int SceneRelease(Block * block, int button, int x, int y, int mod, double when)
3398 {
3399   PyMOLGlobals *G = block->m_G;
3400   CScene *I = G->Scene;
3401   int release_handled = false;
3402   if(I->ButtonsShown && I->PressMode) {
3403     if(I->ScrollBarActive) {
3404       if((x - I->rect.left) < (SceneScrollBarWidth + SceneScrollBarMargin)) {
3405         I->m_ScrollBar.release(button, x, y, mod);
3406         release_handled = true;
3407       }
3408     }
3409     if(!release_handled) {
3410       int ungrab = true;
3411       if(I->PressMode) {
3412         int i;
3413         SceneElem *elem = I->SceneVLA;
3414         I->Over = -1;
3415         for(i = 0; i < I->NScene; i++) {
3416           if(elem->drawn &&
3417              (x >= elem->x1) && (y >= elem->y1) && (x < elem->x2) && (y < elem->y2)) {
3418             I->Over = i;
3419             break;
3420           }
3421           elem++;
3422         }
3423         if(I->Over >= 0) {
3424           release_handled = true;
3425           switch (I->PressMode) {
3426           case 1:
3427             if(I->Over == I->Pressed) {
3428               OrthoLineType buffer;
3429               sprintf(buffer, "cmd.scene('''%s''')", elem->name);
3430               PParse(G, buffer);
3431               PFlush(G);
3432               PLog(G, buffer, cPLog_pym);
3433             }
3434             break;
3435           case 2:
3436             {
3437               const char *cur_name = SettingGetGlobal_s(G, cSetting_scene_current_name);
3438               if(cur_name && elem->name && (strcmp(cur_name, elem->name))) {
3439                 OrthoLineType buffer;
3440                 sprintf(buffer, "cmd.scene('''%s''')", elem->name);
3441                 PParse(G, buffer);
3442                 PFlush(G);
3443                 PLog(G, buffer, cPLog_pym);
3444               }
3445             }
3446             break;
3447           case 3:
3448             if(I->Pressed == I->Over) {
3449               Block *block = MenuActivate1Arg(G, I->LastWinX, I->LastWinY + 20,        /* scene menu */
3450 					      I->LastWinX, I->LastWinY, true, "scene_menu", elem->name);
3451 	      if (block)
3452             block->drag(x, y, mod);
3453               ungrab = false;
3454             }
3455             break;
3456           }
3457         }
3458       }
3459       I->LastPickVertexFlag = false;
3460       I->Pressed = -1;
3461       I->Over = -1;
3462       I->PressMode = 0;
3463       if(ungrab)
3464         OrthoUngrab(G);
3465     }
3466   }
3467   if(!release_handled) {
3468     ObjectMolecule *obj;
3469     I->LastReleaseTime = when;
3470     if(I->PossibleSingleClick == 1) {
3471       double slowest_single_click = 0.25F;
3472       double diff = when - I->LastClickTime;
3473 
3474       slowest_single_click += I->ApproxRenderTime;
3475 
3476       if((diff < 0.0) || (diff > slowest_single_click))
3477         I->PossibleSingleClick = 0;
3478       else {
3479         int but = -1;
3480         I->PossibleSingleClick = 2;
3481         I->SingleClickDelay = 0.15;
3482 
3483         switch (I->LastButton) {
3484         case P_GLUT_LEFT_BUTTON:
3485           but = P_GLUT_DOUBLE_LEFT;
3486           break;
3487         case P_GLUT_MIDDLE_BUTTON:
3488           but = P_GLUT_DOUBLE_MIDDLE;
3489           break;
3490         case P_GLUT_RIGHT_BUTTON:
3491           but = P_GLUT_DOUBLE_RIGHT;
3492           break;
3493         }
3494         if(but > 0) {
3495           int mode = ButModeTranslate(G, but, mod);
3496           if(mode == cButModeNone)
3497             I->SingleClickDelay = 0.0;  /* no double-click set? force immediate single click */
3498         }
3499       }
3500     }
3501     if(I->LoopFlag) {
3502       I->PossibleSingleClick = 0;
3503       return SceneLoopRelease(block, button, x, y, mod);
3504     }
3505     OrthoUngrab(G);
3506     I->LoopFlag = false;
3507     if(I->SculptingFlag) {
3508       /* SettingSet(G,cSetting_sculpting,1); */
3509       obj = (ObjectMolecule *) I->LastPicked.context.object;
3510       if(obj) {
3511         obj->AtomInfo[I->LastPicked.src.index].protekted = I->SculptingSave;
3512       }
3513       I->SculptingFlag = 0;
3514     }
3515   }
3516   if(I->ReinterpolateFlag && I->ReinterpolateObj) {
3517     if(ExecutiveValidateObjectPtr(G, I->ReinterpolateObj, 0)) {
3518       ObjectMotionReinterpolate(I->ReinterpolateObj);
3519     }
3520     I->ReinterpolateFlag = true;
3521     I->ReinterpolateObj = NULL;
3522   }
3523   if(I->MotionGrabbedObj) {
3524     if(ExecutiveValidateObjectPtr(G, I->MotionGrabbedObj, 0)) {
3525       I->MotionGrabbedObj->Grabbed = false;
3526       I->MotionGrabbedObj = NULL;
3527     }
3528   }
3529   return 1;
3530 }
3531 
3532 
3533 /*========================================================================*/
SceneDoRoving(PyMOLGlobals * G,float old_front,float old_back,float old_origin,int adjust_flag,int zoom_flag)3534 static void SceneDoRoving(PyMOLGlobals * G, float old_front,
3535                           float old_back, float old_origin,
3536                           int adjust_flag, int zoom_flag)
3537 {
3538   EditorFavorOrigin(G, NULL);
3539   if(SettingGetGlobal_b(G, cSetting_roving_origin)) {
3540 
3541     CScene *I = G->Scene;
3542     float delta_front, delta_back;
3543     float front_weight, back_weight, slab_width;
3544     float z_buffer = 3.0;
3545     float old_pos2 = 0.0F;
3546     float v2[3];
3547 
3548     z_buffer = SettingGetGlobal_f(G, cSetting_roving_origin_z_cushion);
3549 
3550     delta_front = I->m_view.m_clip.m_front - old_front;
3551     delta_back = I->m_view.m_clip.m_back - old_back;
3552 
3553     zero3f(v2);
3554 
3555     slab_width = I->m_view.m_clip.m_back - I->m_view.m_clip.m_front;
3556 
3557     /* first, check to make sure that the origin isn't too close to either plane */
3558     if((z_buffer * 2) > slab_width)
3559       z_buffer = slab_width * 0.5F;
3560 
3561     if(old_origin < (I->m_view.m_clip.m_front + z_buffer)) {    /* old origin behind front plane */
3562       front_weight = 1.0F;
3563       delta_front = (I->m_view.m_clip.m_front + z_buffer) - old_origin; /* move origin into allowed regioin */
3564     } else if(old_origin > (I->m_view.m_clip.m_back - z_buffer)) {      /* old origin was behind back plane */
3565       front_weight = 0.0F;
3566       delta_back = (I->m_view.m_clip.m_back - z_buffer) - old_origin;
3567 
3568     } else if(slab_width >= R_SMALL4) { /* otherwise, if slab exists */
3569       front_weight = (old_back - old_origin) / slab_width;      /* weight based on relative proximity */
3570     } else {
3571       front_weight = 0.5F;
3572     }
3573 
3574     back_weight = 1.0F - front_weight;
3575 
3576     if((front_weight > 0.2) && (back_weight > 0.2)) {   /* origin not near edge */
3577       if(delta_front * delta_back > 0.0F) {     /* planes moving in same direction */
3578         if(fabs(delta_front) > fabs(delta_back)) {      /* so stick with whichever moves less */
3579           v2[2] = delta_back;
3580         } else {
3581           v2[2] = delta_front;
3582         }
3583       } else {
3584         /* planes moving in opposite directions (increasing slab size) */
3585         /* don't move origin */
3586       }
3587     } else {                    /* origin is near edge -- move origin with plane having highest weight */
3588       if(front_weight < back_weight) {
3589         v2[2] = delta_back;
3590       } else {
3591         v2[2] = delta_front;
3592       }
3593     }
3594 
3595     old_pos2 = I->m_view.m_pos[2];
3596 
3597     MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, v2, v2);        /* transform offset into realspace */
3598     subtract3f(I->m_view.m_origin, v2, v2);      /* calculate new origin location */
3599     SceneOriginSet(G, v2, true);        /* move origin, preserving camera location */
3600 
3601     if(SettingGetGlobal_b(G, cSetting_ortho) || zoom_flag) {
3602       /* we're orthoscopic, so we don't want the effective field of view
3603          to change.  Thus, we have to hold Pos[2] constant, and instead
3604          move the planes.
3605        */
3606       float delta = old_pos2 - I->m_view.m_pos[2];
3607       I->m_view.m_pos[2] += delta;
3608       SceneClipSet(G, I->m_view.m_clip.m_front - delta, I->m_view.m_clip.m_back - delta);
3609     }
3610     slab_width = I->m_view.m_clip.m_back - I->m_view.m_clip.m_front;
3611 
3612     /* first, check to make sure that the origin isn't too close to either plane */
3613     if((z_buffer * 2) > slab_width)
3614       z_buffer = slab_width * 0.5F;
3615 
3616   }
3617   if((adjust_flag) && SettingGetGlobal_b(G, cSetting_roving_detail)) {
3618     SceneRovingPostpone(G);
3619   }
3620   if(SettingGetGlobal_b(G, cSetting_roving_detail)) {
3621     SceneRovingDirty(G);
3622   }
3623 }
3624 
3625 #define cDoubleTime 0.35
3626 
SceneNoteMouseInteraction(PyMOLGlobals * G)3627 static void SceneNoteMouseInteraction(PyMOLGlobals * G)
3628 {
3629   SceneAbortAnimation(G);
3630   if(SettingGet_b(G, NULL, NULL, cSetting_mouse_restart_movie_delay)) {
3631     SceneRestartFrameTimer(G);
3632   }
3633 }
3634 
3635 
3636 /*========================================================================*/
SceneClick(Block * block,int button,int x,int y,int mod,double when)3637 static int SceneClick(Block * block, int button, int x, int y, int mod, double when)
3638 {
3639   PyMOLGlobals *G = block->m_G;
3640   CScene *I = G->Scene;
3641   CObject *obj;
3642   ObjectMolecule *objMol;
3643   OrthoLineType buffer, buf1, buf2;
3644   WordType selName = "";
3645   int mode = 0;        /* trying to work around something... */
3646   int atIndex;
3647   const char *sel_mode_kw = "";
3648   int is_single_click = ((button == P_GLUT_SINGLE_LEFT) ||
3649                          (button == P_GLUT_SINGLE_MIDDLE) ||
3650                          (button == P_GLUT_SINGLE_RIGHT));
3651   int click_handled = false;
3652   int click_side = 0;
3653 
3654   if(!is_single_click) {
3655     int click_handled = false;
3656 
3657     if(I->ButtonsShown) {
3658 
3659       int i;
3660       SceneElem *elem = I->SceneVLA;
3661 
3662       /* check & handle a click on the scrollbar */
3663       if(I->ScrollBarActive) {
3664         if((x - I->rect.left) < (SceneScrollBarWidth + SceneScrollBarMargin)) {
3665           click_handled = true;
3666           I->m_ScrollBar.click(button, x, y, mod);
3667         }
3668       }
3669       if(!click_handled) {
3670         for(i = 0; i < I->NScene; i++) {
3671           if(elem->drawn &&
3672              (x >= elem->x1) && (y >= elem->y1) && (x < elem->x2) && (y < elem->y2)) {
3673             click_handled = true;
3674             break;
3675           }
3676           elem++;
3677         }
3678       }
3679     }
3680 
3681     if(!click_handled) {
3682       // check for double click (within 0.35s and 10sq. pixels
3683       if(((ButModeCheckPossibleSingleClick(G, button, mod) || (!mod))
3684           && ((when - I->LastClickTime) < cDoubleTime))) {
3685         int dx, dy;
3686         dx = abs(I->LastWinX - x);
3687         dy = abs(I->LastWinY - y);
3688         if((dx < 10) && (dy < 10) && (I->LastButton == button)) {
3689           switch (button) {
3690           case P_GLUT_LEFT_BUTTON:
3691             button = P_GLUT_DOUBLE_LEFT;
3692             break;
3693           case P_GLUT_MIDDLE_BUTTON:
3694             button = P_GLUT_DOUBLE_MIDDLE;
3695             break;
3696           case P_GLUT_RIGHT_BUTTON:
3697             button = P_GLUT_DOUBLE_RIGHT;
3698             break;
3699           }
3700         }
3701       }
3702     } // end not click handled
3703 
3704     if(ButModeCheckPossibleSingleClick(G, button, mod) || (!mod)) {
3705       I->PossibleSingleClick = 1;
3706     } else {
3707       const char *but_mode_name = SettingGetGlobal_s(G, cSetting_button_mode_name);
3708       if(but_mode_name && but_mode_name[0] == '1') {
3709         I->PossibleSingleClick = 1;
3710       } else {
3711         I->PossibleSingleClick = 0;
3712       }
3713     }
3714   } // end not single-click
3715 
3716   I->LastWinX = x;
3717   I->LastWinY = y;
3718   I->LastClickTime = when;
3719   I->LastButton = button;
3720   I->LastMod = mod;
3721   I->Threshold = 0;
3722 
3723   SceneGetCenter(G, I->LastClickVertex);
3724   {
3725     float vScale = SceneGetExactScreenVertexScale(G, I->LastClickVertex);
3726     float v[3];
3727     v[0] = -(I->Width / 2 - (x - I->rect.left)) * vScale;
3728     v[1] = -(I->Height / 2 - (y - I->rect.bottom)) * vScale;
3729     v[2] = 0;
3730     MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, v, v);
3731     add3f(v, I->LastClickVertex, I->LastClickVertex);
3732   }
3733 
3734   if(I->ButtonsShown) {
3735     int i;
3736     SceneElem *elem = I->SceneVLA;
3737 
3738     if(I->ScrollBarActive) {
3739       if((x - I->rect.left) < (SceneScrollBarWidth + SceneScrollBarMargin)) {
3740         click_handled = true;
3741         I->m_ScrollBar.click(button, x, y, mod);
3742       }
3743     }
3744     if(!click_handled) {
3745       for(i = 0; i < I->NScene; i++) {
3746         if(elem->drawn &&
3747            (x >= elem->x1) && (y >= elem->y1) && (x < elem->x2) && (y < elem->y2)) {
3748           switch (button) {
3749           case P_GLUT_LEFT_BUTTON:     /* normal activate (with interpolation) */
3750             I->Pressed = i;
3751             I->Over = i;
3752             I->PressMode = 1;
3753             SceneDirty(G);
3754             click_handled = true;
3755             break;
3756           case P_GLUT_MIDDLE_BUTTON:   /* rapid browse mode */
3757             I->Pressed = i;
3758             I->PressMode = 2;
3759             I->Over = i;
3760             click_handled = true;
3761             {
3762               const char *cur_name = SettingGetGlobal_s(G, cSetting_scene_current_name);
3763               int animate = -1;
3764               if(mod & cOrthoCTRL)
3765                 animate = 0;
3766               if(cur_name && elem->name && (strcmp(cur_name, elem->name))) {
3767                 OrthoLineType buffer;
3768                 sprintf(buffer, "cmd.scene('''%s''',animate=%d)", elem->name, animate);
3769                 PParse(G, buffer);
3770                 PFlush(G);
3771                 PLog(G, buffer, cPLog_pym);
3772               }
3773             }
3774             break;
3775           case P_GLUT_RIGHT_BUTTON:    /* drag or menu... */
3776             I->Pressed = i;
3777             I->PressMode = 3;
3778             I->Over = i;
3779             click_handled = true;
3780             break;
3781           }
3782           break;
3783         }
3784         elem++;
3785       }
3786     }
3787   }
3788   if(!click_handled) {
3789 
3790     mode = ButModeTranslate(G, button, mod);
3791 
3792     I->Button = button;
3793     I->SculptingSave = 0;
3794     switch (mode) {
3795     case cButModeScaleSlabExpand:
3796       SceneNoteMouseInteraction(G);
3797       SceneClip(G, 5, 1.0F + (0.2 * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale)),
3798                 NULL, 0);
3799       break;
3800     case cButModeScaleSlabShrink:
3801       SceneNoteMouseInteraction(G);
3802       SceneClip(G, 5, 1.0F - (0.2 * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale)),
3803                 NULL, 0);
3804       break;
3805     case cButModeMoveSlabForward:
3806       SceneNoteMouseInteraction(G);
3807       {
3808         float old_front = I->m_view.m_clip.m_front;
3809         float old_back = I->m_view.m_clip.m_back;
3810         float old_origin = -I->m_view.m_pos[2];
3811         SceneClip(G, 6, 0.1F * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale), NULL,
3812                   0);
3813         SceneDoRoving(G, old_front, old_back, old_origin, true, false);
3814       }
3815       break;
3816     case cButModeMoveSlabBackward:
3817       SceneNoteMouseInteraction(G);
3818       {
3819         float old_front = I->m_view.m_clip.m_front;
3820         float old_back = I->m_view.m_clip.m_back;
3821         float old_origin = -I->m_view.m_pos[2];
3822 
3823         SceneClip(G, 6, -0.1F * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale), NULL,
3824                   0);
3825         SceneDoRoving(G, old_front, old_back, old_origin, true, false);
3826       }
3827       break;
3828     case cButModeZoomForward:
3829       SceneNoteMouseInteraction(G);
3830       {
3831         float factor = -((I->m_view.m_clipSafe.m_front + I->m_view.m_clipSafe.m_back) / 2) * 0.1 *
3832           SettingGetGlobal_f(G, cSetting_mouse_wheel_scale);
3833         if(factor <= 0.0F) {
3834           I->m_view.m_pos[2] += factor;
3835           I->m_view.m_clip.m_front -= factor;
3836           I->m_view.m_clip.m_back -= factor;
3837           UpdateFrontBackSafe(I);
3838         }
3839       }
3840       break;
3841     case cButModeZoomBackward:
3842       SceneNoteMouseInteraction(G);
3843       {
3844         float factor = ((I->m_view.m_clipSafe.m_front + I->m_view.m_clipSafe.m_back) / 2) * 0.1F
3845           * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale);
3846         if(factor >= 0.0F) {
3847           I->m_view.m_pos[2] += factor;
3848           I->m_view.m_clip.m_front -= factor;
3849           I->m_view.m_clip.m_back -= factor;
3850           UpdateFrontBackSafe(I);
3851         }
3852       }
3853       break;
3854     case cButModeMoveSlabAndZoomForward:
3855       SceneNoteMouseInteraction(G);
3856       {
3857         float old_front = I->m_view.m_clip.m_front;
3858         float old_back = I->m_view.m_clip.m_back;
3859         float old_origin = -I->m_view.m_pos[2];
3860         SceneClip(G, 6, 0.1F * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale), NULL,
3861                   0);
3862         SceneDoRoving(G, old_front, old_back, old_origin, true, true);
3863       }
3864       break;
3865     case cButModeMoveSlabAndZoomBackward:
3866       SceneNoteMouseInteraction(G);
3867       {
3868         float old_front = I->m_view.m_clip.m_front;
3869         float old_back = I->m_view.m_clip.m_back;
3870         float old_origin = -I->m_view.m_pos[2];
3871         SceneClip(G, 6, -0.1F * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale), NULL,
3872                   0);
3873         SceneDoRoving(G, old_front, old_back, old_origin, true, true);
3874       }
3875       break;
3876     case cButModeRectAdd:      /* deprecated */
3877     case cButModeRectSub:      /* deprecated */
3878     case cButModeRect:         /* deprecated */
3879     case cButModeSeleAddBox:
3880     case cButModeSeleSetBox:
3881     case cButModeSeleSubBox:
3882       return SceneLoopClick(block, button, x, y, mod);
3883       break;
3884     case cButModeRotDrag:
3885     case cButModeMovDrag:
3886     case cButModeMovDragZ:
3887       SceneNoteMouseInteraction(G);
3888       SceneDontCopyNext(G);
3889 
3890       y = y - I->margin.bottom;
3891       x = x - I->margin.left;
3892 
3893       if(stereo_via_adjacent_array(I->StereoMode))
3894         x = get_stereo_x(x, NULL, I->Width, NULL);
3895 
3896       I->LastX = x;
3897       I->LastY = y;
3898       EditorReadyDrag(G, SettingGetGlobal_i(G, cSetting_state) - 1);
3899 
3900       if(EditorDraggingObjectMatrix(G)) {
3901         obj = EditorDragObject(G);
3902         if(obj) {
3903           if(SettingGetGlobal_b(G,cSetting_movie_auto_store)) {
3904             ObjectTranslateTTT(obj, NULL, true);
3905             I->MotionGrabbedObj = obj;
3906             obj->Grabbed = true;
3907             if(SettingGetGlobal_i(G,cSetting_movie_auto_interpolate)) {
3908               I->ReinterpolateFlag = true;
3909               I->ReinterpolateObj = obj;
3910             }
3911           } else {
3912             ObjectTranslateTTT(obj,NULL,false);
3913           }
3914         }
3915       }
3916       break;
3917     case cButModeRotXYZ:
3918     case cButModeTransXY:
3919     case cButModeTransZ:
3920     case cButModeClipNF:
3921     case cButModeClipN:
3922     case cButModeClipF:
3923     case cButModeRotZ:
3924     case cButModeInvRotZ:
3925     case cButModeRotL:
3926     case cButModeMovL:
3927     case cButModeMvzL:
3928       SceneNoteMouseInteraction(G);
3929       SceneDontCopyNext(G);
3930 
3931       y = y - I->margin.bottom;
3932       x = x - I->margin.left;
3933 
3934       if(stereo_via_adjacent_array(I->StereoMode))
3935         x = get_stereo_x(x, NULL, I->Width, NULL);
3936 
3937       I->LastX = x;
3938       I->LastY = y;
3939       break;
3940     case cButModePickAtom1:
3941     case cButModePickAtom:
3942     case cButModeMenu:
3943       if(stereo_via_adjacent_array(I->StereoMode))
3944         x = get_stereo_x(x, NULL, I->Width, NULL);
3945 
3946       if(SceneDoXYPick(G, x, y, click_side)) {
3947         obj = (CObject *) I->LastPicked.context.object;
3948         y = y - I->margin.bottom;
3949         x = x - I->margin.left;
3950         I->LastX = x;
3951         I->LastY = y;
3952         switch (obj->type) {
3953         case cObjectMolecule:
3954           switch (mode) {
3955           case cButModeMenu:
3956             {
3957               ObjectMolecule *objMol = (ObjectMolecule *) obj;
3958               int active_sele = ExecutiveGetActiveSele(G);
3959               if(active_sele
3960                  && SelectorIsMember(G,
3961                                      objMol->AtomInfo[I->LastPicked.src.index].selEntry,
3962                                      active_sele)) {
3963 		/* user clicked on a selected atom */
3964                 ObjectNameType name;
3965                 ExecutiveGetActiveSeleName(G, name, false,
3966                                            SettingGetGlobal_i(G, cSetting_logging));
3967                 MenuActivate2Arg(G, I->LastWinX, I->LastWinY + 20,      /* selection menu */
3968                                  I->LastWinX, I->LastWinY,
3969                                  is_single_click, "pick_sele", name, name);
3970               } else {
3971 		/* user clicked on an atom not in a selection */
3972                 obj->describeElement(I->LastPicked.src.index, buffer);
3973                 ObjectMoleculeGetAtomSeleLog((ObjectMolecule *) obj,
3974                                              I->LastPicked.src.index, buf1, false);
3975                 MenuActivate2Arg(G, I->LastWinX, I->LastWinY + 20, I->LastWinX,
3976                                  I->LastWinY, is_single_click, "pick_menu", buffer, buf1);
3977               }
3978             }
3979             break;
3980           case cButModePickAtom1:
3981             if(obj && obj->type == cObjectMolecule) {
3982               if(Feedback(G, FB_Scene, FB_Results)) {
3983                 obj->describeElement(I->LastPicked.src.index, buffer);
3984                 PRINTF " You clicked %s -> (%s)\n", buffer, cEditorSele1 ENDF(G);
3985               }
3986               if(SettingGetGlobal_i(G, cSetting_logging)) {
3987                 objMol = (ObjectMolecule *) obj;
3988                 ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buffer,
3989                                              false);
3990                 sprintf(buf2, "cmd.edit(\"%s\",pkresi=1)", buffer);
3991                 PLog(G, buf2, cPLog_pym);
3992               }
3993               OrthoRestorePrompt(G);
3994               sprintf(buffer, "%s`%d", obj->Name, I->LastPicked.src.index + 1);
3995               EditorInactivate(G);
3996               SelectorCreate(G, cEditorSele1, buffer, NULL, true, NULL);
3997               EditorActivate(G, SettingGetGlobal_i(G, cSetting_state) - 1, false);
3998               if(EditorActive(G)) {
3999                 EditorDefineExtraPks(G);
4000               }
4001               WizardDoPick(G, 0, I->LastPicked.context.state);
4002             }
4003             break;
4004           case cButModePickAtom:
4005             if(obj && obj->type == cObjectMolecule) {
4006               WordType name;
4007               obj->describeElement(I->LastPicked.src.index, buffer);
4008               if(EditorIsBondMode(G)
4009                  /* &&!(EditorIsAnActiveObject(G,(ObjectMolecule*)obj)) */
4010                 ) {
4011                 EditorInactivate(G);
4012                 EditorLogState(G, false);
4013               }
4014               if((!EditorIsBondMode(G)) &&
4015                  EditorDeselectIfSelected(G,
4016                                           (ObjectMolecule *) obj, I->LastPicked.src.index,
4017                                           true)) {
4018                 PRINTF " You unpicked %s.", buffer ENDF(G);
4019                 if(EditorActive(G))
4020                   EditorDefineExtraPks(G);
4021                 EditorLogState(G, false);
4022               } else {
4023                 if(EditorIsBondMode(G) &&
4024                    EditorDeselectIfSelected(G,
4025                                             (ObjectMolecule *) obj,
4026                                             I->LastPicked.src.index, false)) {
4027                   EditorInactivate(G);
4028                 }
4029                 EditorGetNextMultiatom(G, name);
4030 
4031                 PRINTFB(G, FB_Scene, FB_Results) " You clicked %s -> (%s)\n", buffer,
4032                   name ENDFB(G);
4033                 /* TODO: logging */
4034 
4035                 sprintf(buffer, "%s`%d", obj->Name, I->LastPicked.src.index + 1);
4036                 ExecutiveDelete(G, name);
4037                 SelectorCreate(G, name, buffer, NULL, true, NULL);
4038                 EditorActivate(G, SettingGetGlobal_i(G, cSetting_state) - 1, false);
4039                 if(EditorActive(G)) {
4040                   EditorDefineExtraPks(G);
4041                 }
4042                 EditorLogState(G, false);
4043                 WizardDoPick(G, 0, I->LastPicked.context.state);
4044               }
4045             }
4046             break;
4047           }
4048           break;
4049         case cObjectGadget:
4050           break;
4051         default:
4052           EditorInactivate(G);
4053           break;
4054         }
4055       } else {                  /* no atom picked */
4056         switch (mode) {
4057         case cButModeMenu:
4058 
4059           MenuActivate3fv(G, I->LastWinX, I->LastWinY,
4060                           I->LastWinX, I->LastWinY,
4061                           is_single_click, "main_menu", I->LastClickVertex);
4062           break;
4063         default:
4064           EditorInactivate(G);
4065           if(SettingGetGlobal_i(G, cSetting_logging)) {
4066             PLog(G, "cmd.edit()", cPLog_pym);
4067           }
4068           break;
4069         }
4070       }
4071       SceneDirty(G);
4072       break;
4073     case cButModePickBond:
4074     case cButModePkTorBnd:
4075       if(stereo_via_adjacent_array(I->StereoMode))
4076         x = get_stereo_x(x, NULL, I->Width, &click_side);
4077 
4078       if(SceneDoXYPick(G, x, y, click_side)) {
4079         obj = (CObject *) I->LastPicked.context.object;
4080         y = y - I->margin.bottom;
4081         x = x - I->margin.left;
4082         I->LastX = x;
4083         I->LastY = y;
4084 
4085         if(mode == cButModePkTorBnd) {
4086           I->Threshold = 3;
4087           I->ThresholdX = x;
4088           I->ThresholdY = y;
4089         }
4090 
4091         switch (obj->type) {
4092         case cObjectMolecule:
4093 
4094           EditorInactivate(G);
4095           if(Feedback(G, FB_Scene, FB_Results)) {
4096             obj->describeElement(I->LastPicked.src.index, buffer);
4097             PRINTF " You clicked %s -> (%s)", buffer, cEditorSele1 ENDF(G);
4098             OrthoRestorePrompt(G);
4099           }
4100 
4101           /*        ObjectMoleculeChooseBondDir(objMol,I->LastPicked.bond,
4102              &I->LastPicked.src.index,&atIndex); */
4103 
4104           sprintf(buffer, "%s`%d", obj->Name, I->LastPicked.src.index + 1);
4105           SelectorCreate(G, cEditorSele1, buffer, NULL, true, NULL);
4106           objMol = (ObjectMolecule *) obj;
4107           if(I->LastPicked.src.bond >= 0) {
4108             atIndex = objMol->Bond[I->LastPicked.src.bond].index[0];
4109             if(atIndex == I->LastPicked.src.index)
4110               atIndex = objMol->Bond[I->LastPicked.src.bond].index[1];
4111             if(Feedback(G, FB_Scene, FB_Results)) {
4112               obj->describeElement(atIndex, buffer);
4113               PRINTF " You clicked %s -> (%s)", buffer, cEditorSele2 ENDF(G);
4114               OrthoRestorePrompt(G);
4115             }
4116 
4117             if(SettingGetGlobal_i(G, cSetting_logging)) {
4118               objMol = (ObjectMolecule *) obj;
4119               ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buf1, false);
4120               ObjectMoleculeGetAtomSeleLog(objMol, atIndex, buf2, false);
4121               sprintf(buffer, "cmd.edit(\"%s\",\"%s\")", buf1, buf2);
4122               PLog(G, buffer, cPLog_pym);
4123             }
4124             sprintf(buffer, "%s`%d", obj->Name, atIndex + 1);
4125             SelectorCreate(G, cEditorSele2, buffer, NULL, true, NULL);
4126             EditorActivate(G, SettingGetGlobal_i(G, cSetting_state) - 1, true);
4127 
4128             if(mode == cButModePkTorBnd) {
4129               /* get ready to drag */
4130               SceneDontCopyNext(G);
4131               switch (obj->type) {
4132               case cObjectMolecule:
4133                 objMol = (ObjectMolecule *) obj;
4134                 EditorPrepareDrag(G, obj, -1, I->LastPicked.src.index,
4135                                   SettingGetGlobal_i(G, cSetting_state) - 1, mode);
4136                 I->SculptingFlag = 1;
4137                 I->SculptingSave = objMol->AtomInfo[I->LastPicked.src.index].protekted;
4138                 objMol->AtomInfo[I->LastPicked.src.index].protekted = 2;
4139                 break;
4140               }
4141             }
4142             WizardDoPick(G, 1, I->LastPicked.context.state);
4143           } else {
4144             WizardDoPick(G, 0, I->LastPicked.context.state);
4145           }
4146           if(SettingGetGlobal_b(G, cSetting_auto_hide_selections))
4147             ExecutiveHideSelections(G);
4148           break;
4149         case cObjectGadget:
4150           break;
4151         default:
4152           EditorInactivate(G);
4153           break;
4154         }
4155       } else {
4156         EditorInactivate(G);
4157         EditorLogState(G, false);
4158       }
4159       SceneInvalidate(G);
4160       break;
4161     case cButModeRotObj:
4162     case cButModeMovObj:
4163     case cButModeMovObjZ:
4164     case cButModeRotView:
4165     case cButModeMovView:
4166     case cButModeMovViewZ:
4167     case cButModeRotFrag:
4168     case cButModeMovFrag:
4169     case cButModeMovFragZ:
4170     case cButModeTorFrag:
4171     case cButModeMoveAtom:
4172     case cButModeMoveAtomZ:
4173       if(stereo_via_adjacent_array(I->StereoMode))
4174         x = get_stereo_x(x, NULL, I->Width, &click_side);
4175 
4176       if(SceneDoXYPick(G, x, y, click_side)) {
4177         obj = (CObject *) I->LastPicked.context.object;
4178         y = y - I->margin.bottom;
4179         x = x - I->margin.left;
4180         I->LastX = x;
4181         I->LastY = y;
4182         switch (obj->type) {
4183         case cObjectMolecule:
4184 
4185           if(I->LastPicked.src.bond == cPickableLabel) {
4186             /* if user picks a label with move object/move fragment,
4187                then move the object/fragment, not the label */
4188 
4189             switch (mode) {
4190             case cButModeRotObj:
4191             case cButModeMovObj:
4192             case cButModeMovObjZ:
4193             case cButModeRotFrag:
4194             case cButModeMovFrag:
4195             case cButModeMovFragZ:
4196             case cButModeMovViewZ:
4197             case cButModeRotView:
4198             case cButModeMovView:
4199               I->LastPicked.src.bond = cPickableAtom;
4200               break;
4201             }
4202           }
4203 
4204           switch(mode) {
4205           case cButModeMovViewZ:
4206           case cButModeRotView:
4207           case cButModeMovView:
4208             {
4209               if(SettingGetGlobal_b(G,cSetting_movie_auto_store)) {
4210                 ObjectTranslateTTT(obj, NULL, true);
4211                 I->MotionGrabbedObj = obj;
4212                 obj->Grabbed = true;
4213                 if(SettingGetGlobal_i(G,cSetting_movie_auto_interpolate)) {
4214                   I->ReinterpolateFlag = true;
4215                   I->ReinterpolateObj = obj;
4216                 }
4217               } else {
4218                 ObjectTranslateTTT(obj, NULL, false);
4219               }
4220             }
4221             break;
4222           }
4223 
4224           if(I->LastPicked.src.bond >= cPickableAtom) {
4225             if(Feedback(G, FB_Scene, FB_Results)) {
4226               obj->describeElement(I->LastPicked.src.index, buffer);
4227               PRINTF " You clicked %s", buffer ENDF(G);
4228               OrthoRestorePrompt(G);
4229             }
4230           }
4231           objMol = (ObjectMolecule *) obj;
4232           EditorPrepareDrag(G, obj, -1, I->LastPicked.src.index,
4233                             SettingGetGlobal_i(G, cSetting_state) - 1, mode);
4234 
4235           if(I->LastPicked.src.bond >= cPickableAtom) {
4236             I->SculptingFlag = 1;
4237             I->SculptingSave = objMol->AtomInfo[I->LastPicked.src.index].protekted;
4238             objMol->AtomInfo[I->LastPicked.src.index].protekted = 2;
4239           }
4240           break;
4241         case cObjectSlice:
4242           if(ObjectSliceGetVertex((ObjectSlice *) obj, I->LastPicked.src.index,
4243                                   I->LastPicked.src.bond, I->LastPickVertex)) {
4244             I->LastPickVertexFlag = true;
4245           }
4246           break;
4247         case cObjectMeasurement:
4248           break;
4249         case cObjectGadget:
4250           break;
4251         default:
4252           EditorInactivate(G);
4253           break;
4254         }
4255       }
4256       break;
4257 
4258     case cButModeSeleSet:
4259     case cButModeSeleToggle:
4260       sel_mode_kw = SceneGetSeleModeKeyword(G);
4261 
4262       /* intentional pass through */
4263 
4264     case cButModeLB:
4265     case cButModeMB:
4266     case cButModeRB:
4267     case cButModeAddToLB:
4268     case cButModeAddToMB:
4269     case cButModeAddToRB:
4270     case cButModeSimpleClick:
4271     case cButModeOrigAt:
4272     case cButModeCent:
4273     case cButModeDragMol:
4274     case cButModeDragObj:
4275       if(stereo_via_adjacent_array(I->StereoMode))
4276         x = get_stereo_x(x, NULL, I->Width, &click_side);
4277 
4278       if(SceneDoXYPick(G, x, y, click_side)) {
4279         obj = (CObject *) I->LastPicked.context.object;
4280 
4281         switch (obj->type) {
4282         case cObjectMolecule:
4283           if(Feedback(G, FB_Scene, FB_Results)) {
4284             obj->describeElement(I->LastPicked.src.index, buffer);
4285             PRINTF " You clicked %s", buffer ENDF(G);
4286             OrthoRestorePrompt(G);
4287           }
4288           sprintf(buffer, "%s`%d", obj->Name, I->LastPicked.src.index + 1);
4289           switch (mode) {
4290           case cButModeLB:
4291           case cButModeAddToLB:
4292             strcpy(selName, "lb");
4293             break;
4294           case cButModeMB:
4295           case cButModeAddToMB:
4296             strcpy(selName, "mb");
4297             break;
4298           case cButModeRB:
4299           case cButModeAddToRB:
4300             strcpy(selName, "rb");
4301             break;
4302           case cButModeSeleSet:
4303           case cButModeSeleToggle:
4304             ExecutiveGetActiveSeleName(G, selName, true, SettingGetGlobal_i(G, cSetting_logging));
4305             break;
4306           case cButModeDragMol:
4307             {
4308               objMol = (ObjectMolecule *) obj;
4309               ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buf1, false);
4310               sprintf(buffer, "cmd.drag(\"bymol (%s)\")", buf1);
4311               PParse(G, buffer);
4312               PLog(G, buffer, cPLog_pym);
4313             }
4314             break;
4315           case cButModeDragObj:
4316             {
4317               objMol = (ObjectMolecule *) obj;
4318               ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buf1, false);
4319               sprintf(buffer, "cmd.drag(\"byobject (%s)\")", buf1);
4320               PParse(G, buffer);
4321               PLog(G, buffer, cPLog_pym);
4322             }
4323             break;
4324           case cButModeOrigAt:
4325             SceneNoteMouseInteraction(G);
4326             {
4327               float v1[3];
4328 
4329               if(ObjectMoleculeGetAtomTxfVertex((ObjectMolecule *) obj,
4330                                                 I->LastPicked.context.state,
4331                                                 I->LastPicked.src.index, v1)) {
4332                 EditorFavorOrigin(G, v1);
4333                 ExecutiveOrigin(G, NULL, true, NULL, v1, 0);
4334               }
4335             }
4336             if(obj->type == cObjectMolecule) {
4337               if(SettingGetGlobal_i(G, cSetting_logging)) {
4338                 objMol = (ObjectMolecule *) obj;
4339                 ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buf1,
4340                                              false);
4341                 sprintf(buffer, "cmd.origin(\"%s\")", buf1);
4342                 PLog(G, buffer, cPLog_pym);
4343 
4344               }
4345               if(Feedback(G, FB_Scene, FB_Results)) {
4346                 obj->describeElement(I->LastPicked.src.index, buffer);
4347                 PRINTF " You clicked %s", buffer ENDF(G);
4348                 OrthoRestorePrompt(G);
4349               }
4350             }
4351             PRINTFB(G, FB_Scene, FB_Actions)
4352               " Scene: Origin set.\n" ENDFB(G);
4353             break;
4354           case cButModeCent:
4355             SceneNoteMouseInteraction(G);
4356             {
4357               float v1[3];
4358 
4359               if(ObjectMoleculeGetAtomTxfVertex((ObjectMolecule *) obj,
4360                                                 I->LastPicked.context.state,
4361                                                 I->LastPicked.src.index, v1)) {
4362                 ExecutiveCenter(G, NULL, 0, true, -1, v1, true);
4363               }
4364             }
4365 
4366             if(SettingGetGlobal_i(G, cSetting_logging)) {
4367               objMol = (ObjectMolecule *) obj;
4368               ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buf1, false);
4369               sprintf(buffer, "cmd.center(\"%s\",state=%d)", buf1, I->LastPicked.context.state + 1);
4370               PLog(G, buffer, cPLog_pym);
4371             }
4372             break;
4373           }
4374           switch (mode) {
4375           case cButModeSimpleClick:
4376 	    {
4377 	      float pos_store[3], *pos = pos_store;
4378 	      int index = I->LastPicked.src.index; /* 1-based */
4379 	      int state = I->LastPicked.context.state;
4380 	      if(!( (obj->type == cObjectMolecule) &&
4381 		    (I->LastPicked.src.bond != cPickableNoPick ) &&
4382 		    ObjectMoleculeGetAtomTxfVertex((ObjectMolecule *)obj,-1,index, pos)))
4383 		pos = NULL;
4384 	      PyMOL_SetClickReady(G->PyMOL, obj->Name, I->LastPicked.src.index,
4385 				  button, mod, I->LastWinX, I->Height - (I->LastWinY + 1),
4386 				  pos, state + 1); /* send a 1-based state index */
4387 	    }
4388             break;
4389           case cButModeLB:
4390           case cButModeMB:
4391           case cButModeRB:
4392           case cButModeSeleSet:
4393             sprintf(buf2, "(%s(%s))", sel_mode_kw, buffer);
4394             SelectorCreate(G, selName, buf2, NULL, false, NULL);
4395             if(SettingGetGlobal_b(G, cSetting_auto_hide_selections))
4396               ExecutiveHideSelections(G);
4397             if(SettingGetGlobal_b(G, cSetting_auto_show_selections))
4398               ExecutiveSetObjVisib(G, selName, 1, false);
4399             if(obj->type == cObjectMolecule) {
4400               if(SettingGetGlobal_i(G, cSetting_logging)) {
4401                 objMol = (ObjectMolecule *) obj;
4402                 ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buf1,
4403                                              false);
4404                 sprintf(buffer, "cmd.select('%s',\"%s(%s)\",enable=1)", selName,
4405                         sel_mode_kw, buf1);
4406                 PLog(G, buffer, cPLog_pym);
4407               }
4408             }
4409             WizardDoSelect(G, selName, I->LastPicked.context.state);
4410             break;
4411           case cButModeAddToLB:
4412           case cButModeAddToMB:
4413           case cButModeAddToRB:
4414           case cButModeSeleToggle:
4415             if(SelectorIndexByName(G, selName) >= 0) {
4416               sprintf(buf2, "(((%s) or %s(%s)) and not ((%s(%s)) and %s(%s)))",
4417                       selName, sel_mode_kw, buffer, sel_mode_kw, buffer, sel_mode_kw,
4418                       selName);
4419               SelectorCreate(G, selName, buf2, NULL, false, NULL);
4420               if(obj->type == cObjectMolecule) {
4421                 if(SettingGetGlobal_i(G, cSetting_logging)) {
4422                   objMol = (ObjectMolecule *) obj;
4423                   ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buffer,
4424                                                false);
4425                   sprintf(buf2, "(((%s) or %s(%s)) and not ((%s(%s)) and %s(%s)))",
4426                           selName, sel_mode_kw, buffer, sel_mode_kw, buffer, sel_mode_kw,
4427                           selName);
4428                   sprintf(buffer, "cmd.select('%s',\"%s(%s)\",enable=1)", selName,
4429                           sel_mode_kw, buf2);
4430                   PLog(G, buffer, cPLog_pym);
4431                 }
4432               }
4433             } else {
4434               sprintf(buf2, "%s(%s)", sel_mode_kw, buffer);
4435               SelectorCreate(G, selName, buf2, NULL, false, NULL);
4436               if(obj->type == cObjectMolecule) {
4437                 if(SettingGetGlobal_i(G, cSetting_logging)) {
4438                   objMol = (ObjectMolecule *) obj;
4439                   ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buf1,
4440                                                false);
4441                   sprintf(buffer, "cmd.select('%s',\"%s(%s)\")", selName, sel_mode_kw,
4442                           buf1);
4443                   PLog(G, buffer, cPLog_pym);
4444                 }
4445               }
4446             }
4447             if(SettingGetGlobal_b(G, cSetting_auto_hide_selections))
4448               ExecutiveHideSelections(G);
4449             if(SettingGetGlobal_b(G, cSetting_auto_show_selections))
4450               ExecutiveSetObjVisib(G, selName, 1, false);
4451             WizardDoSelect(G, selName, I->LastPicked.context.state);
4452             break;
4453           }
4454         case cObjectGadget:
4455           break;
4456         default:
4457           EditorInactivate(G);
4458           break;
4459         }
4460       } else {
4461         switch (mode) {
4462         case cButModeSeleSet:
4463           {
4464             OrthoLineType buf2;
4465             ObjectNameType name;
4466 
4467             if(ExecutiveGetActiveSeleName
4468                (G, name, false, SettingGetGlobal_i(G, cSetting_logging))) {
4469               SelectorCreate(G, name, "none", NULL, true, NULL);
4470               if(SettingGetGlobal_i(G, cSetting_logging)) {
4471                 sprintf(buf2, "cmd.select('%s','none')\n", name);
4472                 PLog(G, buf2, cPLog_no_flush);
4473               }
4474               SeqDirty(G);
4475             }
4476           }
4477         case cButModeSeleToggle:
4478           {
4479             OrthoLineType buf2;
4480             ObjectNameType name;
4481 
4482             if(ExecutiveGetActiveSeleName
4483                (G, name, false, SettingGetGlobal_i(G, cSetting_logging))) {
4484               ExecutiveSetObjVisib(G, name, 0, false);
4485               if(SettingGetGlobal_i(G, cSetting_logging)) {
4486                 sprintf(buf2, "cmd.disable('%s')\n", name);
4487                 PLog(G, buf2, cPLog_no_flush);
4488               }
4489             }
4490           }
4491           break;
4492         case cButModeSimpleClick:
4493           PyMOL_SetClickReady(G->PyMOL, "", -1, button, mod, I->LastWinX,
4494                               I->Height - (I->LastWinY + 1), NULL, 0);
4495           break;
4496         }
4497         PRINTFB(G, FB_Scene, FB_Blather)
4498           " %s: no atom found nearby.\n", __func__ ENDFB(G);
4499         SceneInvalidate(G);     /* this here to prevent display weirdness after
4500                                    an unsuccessful picking pass... not sure it helps though */
4501         OrthoRestorePrompt(G);
4502       }
4503     }
4504 
4505     I->StartX = I->LastX;
4506     I->StartY = I->LastY;
4507   }
4508   return (1);
4509 }
4510 
ScenePushRasterMatrix(PyMOLGlobals * G,float * v)4511 float ScenePushRasterMatrix(PyMOLGlobals * G, float *v)
4512 {
4513   float scale = SceneGetExactScreenVertexScale(G, v);
4514   CScene *I = G->Scene;
4515   glMatrixMode(GL_MODELVIEW);
4516   glPushMatrix();
4517   glTranslatef(v[0], v[1], v[2]);       /* go to this position */
4518   glMultMatrixf(I->InvMatrix);
4519   glScalef(scale, scale, scale);
4520   return scale;
4521 }
4522 
ScenePopRasterMatrix(PyMOLGlobals * G)4523 void ScenePopRasterMatrix(PyMOLGlobals * G)
4524 {
4525   glMatrixMode(GL_MODELVIEW);
4526   glPopMatrix();
4527 }
4528 
4529 /**
4530  * Compose the ModelViewMatrix from Pos, RotMatrix and Origin
4531  * See also: CScene.ModMatrix (queried from OpenGL)
4532  *
4533  * @param[out] modelView 4x4 matrix
4534  */
SceneComposeModelViewMatrix(CScene * I,float * modelView)4535 static void SceneComposeModelViewMatrix(CScene * I, float * modelView) {
4536   identity44f(modelView);
4537   MatrixTranslateC44f(modelView, I->m_view.m_pos[0], I->m_view.m_pos[1], I->m_view.m_pos[2]);
4538   MatrixMultiplyC44f(I->m_view.m_rotMatrix, modelView);
4539   MatrixTranslateC44f(modelView, -I->m_view.m_origin[0], -I->m_view.m_origin[1], -I->m_view.m_origin[2]);
4540 }
4541 
4542 /*========================================================================*/
SceneGetEyeNormal(PyMOLGlobals * G,float * v1,float * normal)4543 void SceneGetEyeNormal(PyMOLGlobals * G, float *v1, float *normal)
4544 {
4545   CScene *I = G->Scene;
4546   float p1[4], p2[4];
4547   float modelView[16];
4548 
4549   SceneComposeModelViewMatrix(I, modelView);
4550 
4551   copy3f(v1, p1);
4552   p1[3] = 1.0;
4553   MatrixTransformC44f4f(modelView, p1, p2);     /* modelview transformation */
4554   copy3f(p2, p1);
4555   normalize3f(p1);
4556   MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, p1, p2);
4557   invert3f3f(p2, normal);
4558 }
4559 
4560 /**
4561  * Return true if the v1 is within the safe clipping planes
4562  */
SceneGetVisible(PyMOLGlobals * G,const float * v1)4563 bool SceneGetVisible(PyMOLGlobals * G, const float *v1)
4564 {
4565   CScene *I = G->Scene;
4566   float depth = SceneGetRawDepth(G, v1);
4567   return (I->m_view.m_clipSafe.m_back >= depth && depth >= I->m_view.m_clipSafe.m_front);
4568 }
4569 
4570 /**
4571  * Get the depth (camera space Z) of v1
4572  *
4573  * @param v1 point (3f) in world space or NULL (= origin)
4574  */
SceneGetRawDepth(PyMOLGlobals * G,const float * v1)4575 float SceneGetRawDepth(PyMOLGlobals * G, const float *v1)
4576 {
4577   CScene *I = G->Scene;
4578   float vt[3];
4579   float modelView[16];
4580 
4581   if(!v1 || SettingGetGlobal_i(G, cSetting_ortho))
4582     return -I->m_view.m_pos[2];
4583 
4584   SceneComposeModelViewMatrix(I, modelView);
4585 
4586   MatrixTransformC44f3f(modelView, v1, vt);
4587   return -vt[2];
4588 }
4589 
4590 /**
4591  * Get the depth (camera space Z) of v1 in normalized clip space
4592  * from 0.0 (near) to 1.0 (far)
4593  *
4594  * @param v1 point (3f) in world space or NULL (= origin)
4595  */
SceneGetDepth(PyMOLGlobals * G,const float * v1)4596 float SceneGetDepth(PyMOLGlobals * G, const float *v1)
4597 {
4598   CScene *I = G->Scene;
4599   float rawDepth = SceneGetRawDepth(G, v1);
4600   return ((rawDepth - I->m_view.m_clipSafe.m_front)/(I->m_view.m_clipSafe.m_back-I->m_view.m_clipSafe.m_front));
4601 }
4602 
4603 /*========================================================================*/
4604 /**
4605  * Get the angstrom per pixel factor at v1. If v1 is NULL, return the
4606  * factor at the origin, but clamped to an empirical positive value.
4607  *
4608  * @param v1 point (3f) in world space or NULL (= origin)
4609  */
SceneGetScreenVertexScale(PyMOLGlobals * G,const float * v1)4610 float SceneGetScreenVertexScale(PyMOLGlobals * G, const float *v1)
4611 
4612 /* does not require OpenGL-provided matrices */
4613 {
4614   float depth = SceneGetRawDepth(G, v1);
4615   float ratio = depth * GetFovWidth(G) / G->Scene->Height;
4616 
4617   if(!v1 && ratio < R_SMALL4)
4618     // origin depth, return a safe clipped value (origin must not be
4619     // behind or very close in front of the camera)
4620     ratio = R_SMALL4;
4621 
4622   return ratio;
4623 }
4624 
SceneRovingChanged(PyMOLGlobals * G)4625 void SceneRovingChanged(PyMOLGlobals * G)
4626 {
4627   CScene *I = G->Scene;
4628   SceneRovingDirty(G);
4629   I->RovingCleanupFlag = true;
4630 }
4631 
SceneRovingCleanup(PyMOLGlobals * G)4632 static void SceneRovingCleanup(PyMOLGlobals * G)
4633 {
4634   CScene *I = G->Scene;
4635   const char *s;
4636   char buffer[OrthoLineLength];
4637 
4638   I->RovingCleanupFlag = false;
4639 
4640   s = SettingGet_s(G, NULL, NULL, cSetting_roving_selection);
4641 
4642   sprintf(buffer, "cmd.hide('lines','''%s''')", s);
4643   PParse(G, buffer);
4644   PFlush(G);
4645   sprintf(buffer, "cmd.hide('sticks','''%s''')", s);
4646   PParse(G, buffer);
4647   PFlush(G);
4648   sprintf(buffer, "cmd.hide('spheres','''%s''')", s);
4649   PParse(G, buffer);
4650   PFlush(G);
4651   sprintf(buffer, "cmd.hide('ribbon','''%s''')", s);
4652   PParse(G, buffer);
4653   PFlush(G);
4654   sprintf(buffer, "cmd.hide('cartoon','''%s''')", s);
4655   PParse(G, buffer);
4656   PFlush(G);
4657   sprintf(buffer, "cmd.hide('labels','''%s''')", s);
4658   PParse(G, buffer);
4659   PFlush(G);
4660   sprintf(buffer, "cmd.hide('nonbonded','''%s''')", s);
4661   PParse(G, buffer);
4662   PFlush(G);
4663   sprintf(buffer, "cmd.hide('nb_spheres','''%s''')", s);
4664   PParse(G, buffer);
4665   PFlush(G);
4666 }
4667 
SceneRovingUpdate(PyMOLGlobals * G)4668 void SceneRovingUpdate(PyMOLGlobals * G)
4669 {
4670   CScene *I = G->Scene;
4671   char buffer[OrthoLineLength];
4672   float sticks, lines, spheres, labels, ribbon, cartoon;
4673   float polar_contacts, polar_cutoff, nonbonded, nb_spheres;
4674   char byres[10] = "byres";
4675   char not_[4] = "not";
4676   char empty[1] = "";
4677   char *p1;
4678   char *p2;
4679   const char *s;
4680   int refresh_flag = false;
4681   const char *name;
4682   float level;
4683   float isosurface, isomesh;
4684 
4685   if(I->RovingDirtyFlag && ((UtilGetSeconds(G) - I->RovingLastUpdate) >
4686                             fabs(SettingGetGlobal_f(G, cSetting_roving_delay)))) {
4687 
4688     if(I->RovingCleanupFlag)
4689       SceneRovingCleanup(G);
4690 
4691     s = SettingGet_s(G, NULL, NULL, cSetting_roving_selection);
4692     sticks = SettingGetGlobal_f(G, cSetting_roving_sticks);
4693     lines = SettingGetGlobal_f(G, cSetting_roving_lines);
4694     labels = SettingGetGlobal_f(G, cSetting_roving_labels);
4695     spheres = SettingGetGlobal_f(G, cSetting_roving_spheres);
4696     ribbon = SettingGetGlobal_f(G, cSetting_roving_ribbon);
4697     cartoon = SettingGetGlobal_f(G, cSetting_roving_cartoon);
4698     polar_contacts = SettingGetGlobal_f(G, cSetting_roving_polar_contacts);
4699     polar_cutoff = SettingGetGlobal_f(G, cSetting_roving_polar_cutoff);
4700     nonbonded = SettingGetGlobal_f(G, cSetting_roving_nonbonded);
4701     nb_spheres = SettingGetGlobal_f(G, cSetting_roving_nb_spheres);
4702 
4703     isomesh = SettingGetGlobal_f(G, cSetting_roving_isomesh);
4704     isosurface = SettingGetGlobal_f(G, cSetting_roving_isosurface);
4705 
4706     if(SettingGetGlobal_b(G, cSetting_roving_byres))
4707       p2 = byres;
4708     else
4709       p2 = empty;
4710 
4711     if(sticks != 0.0F) {
4712       if(sticks < 0.0F) {
4713         p1 = not_;
4714         sticks = (float) fabs(sticks);
4715       } else {
4716         p1 = empty;
4717       }
4718       sprintf(buffer,
4719               "cmd.hide('sticks','''%s''');cmd.show('sticks','%s & enabled & %s %s (center expand %1.3f)')",
4720               s, s, p1, p2, sticks);
4721       PParse(G, buffer);
4722       PFlush(G);
4723       refresh_flag = true;
4724     }
4725 
4726     if(lines != 0.0F) {
4727       if(lines < 0.0F) {
4728         p1 = not_;
4729         lines = (float) fabs(lines);
4730       } else {
4731         p1 = empty;
4732       }
4733       sprintf(buffer,
4734               "cmd.hide('lines','''%s''');cmd.show('lines','%s & enabled & %s %s (center expand %1.3f)')",
4735               s, s, p1, p2, lines);
4736       PParse(G, buffer);
4737       PFlush(G);
4738       refresh_flag = true;
4739     }
4740 
4741     if(labels != 0.0F) {
4742       if(labels < 0.0F) {
4743         p1 = not_;
4744         labels = (float) fabs(labels);
4745       } else {
4746         p1 = empty;
4747       }
4748       sprintf(buffer,
4749               "cmd.hide('labels','''%s''');cmd.show('labels','%s & enabled & %s %s (center expand %1.3f)')",
4750               s, s, p1, p2, labels);
4751       PParse(G, buffer);
4752       PFlush(G);
4753       refresh_flag = true;
4754     }
4755 
4756     if(spheres != 0.0F) {
4757       if(spheres < 0.0F) {
4758         p1 = not_;
4759         spheres = (float) fabs(spheres);
4760       } else {
4761         p1 = empty;
4762       }
4763       sprintf(buffer,
4764               "cmd.hide('spheres','''%s''');cmd.show('spheres','%s & enabled & %s %s (center expand %1.3f)')",
4765               s, s, p1, p2, spheres);
4766       PParse(G, buffer);
4767       PFlush(G);
4768       refresh_flag = true;
4769     }
4770 
4771     if(cartoon != 0.0F) {
4772       if(cartoon < 0.0F) {
4773         p1 = not_;
4774         cartoon = (float) fabs(cartoon);
4775       } else {
4776         p1 = empty;
4777       }
4778       sprintf(buffer,
4779               "cmd.hide('cartoon','''%s''');cmd.show('cartoon','%s & enabled & %s %s (center expand %1.3f)')",
4780               s, s, p1, p2, cartoon);
4781       PParse(G, buffer);
4782       PFlush(G);
4783       refresh_flag = true;
4784     }
4785 
4786     if(ribbon != 0.0F) {
4787       if(ribbon < 0.0F) {
4788         p1 = not_;
4789         ribbon = (float) fabs(ribbon);
4790       } else {
4791         p1 = empty;
4792       }
4793       sprintf(buffer,
4794               "cmd.hide('ribbon','''%s''');cmd.show('ribbon','%s & enabled & %s %s (center expand %1.3f)')",
4795               s, s, p1, p2, ribbon);
4796       PParse(G, buffer);
4797       PFlush(G);
4798 
4799       refresh_flag = true;
4800     }
4801 
4802     if(polar_contacts != 0.0F) {
4803       int label_flag = 0;
4804       if(polar_contacts < 0.0F) {
4805         p1 = not_;
4806         polar_contacts = (float) fabs(polar_contacts);
4807       } else {
4808         p1 = empty;
4809       }
4810       if(polar_cutoff < 0.0F) {
4811         label_flag = true;
4812         polar_cutoff = (float) fabs(polar_cutoff);
4813       }
4814       sprintf(buffer,
4815               "cmd.delete('rov_pc');cmd.dist('rov_pc','%s & enabled & %s %s (center expand %1.3f)','same',%1.4f,mode=2,label=%d,quiet=2)",
4816               s, p1, p2, polar_contacts, polar_cutoff, label_flag);
4817       PParse(G, buffer);
4818       PFlush(G);
4819 
4820       refresh_flag = true;
4821     }
4822 
4823     if(nonbonded != 0.0F) {
4824       if(nonbonded < 0.0F) {
4825         p1 = not_;
4826         nonbonded = (float) fabs(nonbonded);
4827       } else {
4828         p1 = empty;
4829       }
4830       sprintf(buffer,
4831               "cmd.hide('nonbonded','''%s''');cmd.show('nonbonded','%s & enabled & %s %s (center expand %1.3f)')",
4832               s, s, p1, p2, nonbonded);
4833       PParse(G, buffer);
4834       PFlush(G);
4835       refresh_flag = true;
4836     }
4837 
4838     if(nb_spheres != 0.0F) {
4839       if(nb_spheres < 0.0F) {
4840         p1 = not_;
4841         nb_spheres = (float) fabs(nb_spheres);
4842       } else {
4843         p1 = empty;
4844       }
4845       sprintf(buffer,
4846               "cmd.hide('nb_spheres','''%s''');cmd.show('nb_spheres','%s & enabled & %s %s (center expand %1.3f)')",
4847               s, s, p1, p2, nb_spheres);
4848       PParse(G, buffer);
4849       PFlush(G);
4850       refresh_flag = true;
4851     }
4852 
4853     if(isomesh != 0.0F) {
4854       int auto_save;
4855 
4856       auto_save = SettingGetGlobal_i(G, cSetting_auto_zoom);
4857       SettingSetGlobal_i(G, cSetting_auto_zoom, 0);
4858 
4859       name = SettingGet_s(G, NULL, NULL, cSetting_roving_map1_name);
4860       if(name)
4861         if(name[0])
4862           if(ExecutiveFindObjectByName(G, name)) {
4863             level = SettingGetGlobal_f(G, cSetting_roving_map1_level);
4864             sprintf(buffer,
4865                     "cmd.isomesh('rov_m1','%s',%8.6f,'center',%1.3f)",
4866                     name, level, isomesh);
4867             PParse(G, buffer);
4868             PFlush(G);
4869             refresh_flag = true;
4870           }
4871 
4872       name = SettingGet_s(G, NULL, NULL, cSetting_roving_map2_name);
4873       if(name)
4874         if(name[0])
4875           if(ExecutiveFindObjectByName(G, name)) {
4876             level = SettingGetGlobal_f(G, cSetting_roving_map2_level);
4877             sprintf(buffer,
4878                     "cmd.isomesh('rov_m2','%s',%8.6f,'center',%1.3f)",
4879                     name, level, isomesh);
4880             PParse(G, buffer);
4881             PFlush(G);
4882             refresh_flag = true;
4883           }
4884 
4885       name = SettingGet_s(G, NULL, NULL, cSetting_roving_map3_name);
4886       if(name)
4887         if(name[0])
4888           if(ExecutiveFindObjectByName(G, name)) {
4889             level = SettingGetGlobal_f(G, cSetting_roving_map3_level);
4890             sprintf(buffer,
4891                     "cmd.isomesh('rov_m3','%s',%8.6f,'center',%1.3f)",
4892                     name, level, isomesh);
4893             PParse(G, buffer);
4894             PFlush(G);
4895             refresh_flag = true;
4896           }
4897       SettingSetGlobal_i(G, cSetting_auto_zoom, auto_save);
4898     }
4899 
4900     if(isosurface != 0.0F) {
4901       int auto_save;
4902 
4903       auto_save = SettingGetGlobal_i(G, cSetting_auto_zoom);
4904       SettingSetGlobal_i(G, cSetting_auto_zoom, 0);
4905 
4906       name = SettingGet_s(G, NULL, NULL, cSetting_roving_map1_name);
4907       if(name)
4908         if(name[0])
4909           if(ExecutiveFindObjectByName(G, name)) {
4910             level = SettingGetGlobal_f(G, cSetting_roving_map1_level);
4911             sprintf(buffer,
4912                     "cmd.isosurface('rov_s1','%s',%8.6f,'center',%1.3f)",
4913                     name, level, isosurface);
4914             PParse(G, buffer);
4915             PFlush(G);
4916             refresh_flag = true;
4917           }
4918 
4919       name = SettingGet_s(G, NULL, NULL, cSetting_roving_map2_name);
4920       if(name)
4921         if(name[0])
4922           if(ExecutiveFindObjectByName(G, name)) {
4923             level = SettingGetGlobal_f(G, cSetting_roving_map2_level);
4924             sprintf(buffer,
4925                     "cmd.isosurface('rov_s2','%s',%8.6f,'center',%1.3f)",
4926                     name, level, isosurface);
4927             PParse(G, buffer);
4928             PFlush(G);
4929             refresh_flag = true;
4930           }
4931 
4932       name = SettingGet_s(G, NULL, NULL, cSetting_roving_map3_name);
4933       if(name)
4934         if(name[0])
4935           if(ExecutiveFindObjectByName(G, name)) {
4936             level = SettingGetGlobal_f(G, cSetting_roving_map3_level);
4937             sprintf(buffer,
4938                     "cmd.isosurface('rov_s3','%s',%8.6f,'center',%1.3f)",
4939                     name, level, isosurface);
4940             PParse(G, buffer);
4941             PFlush(G);
4942             refresh_flag = true;
4943           }
4944       SettingSetGlobal_i(G, cSetting_auto_zoom, auto_save);
4945     }
4946 
4947     if(refresh_flag) {
4948       PParse(G, "cmd.refresh()");
4949       PFlush(G);
4950     }
4951 
4952     I->RovingLastUpdate = UtilGetSeconds(G);
4953     I->RovingDirtyFlag = false;
4954   }
4955 }
4956 
4957 
4958 /*========================================================================*/
SceneDrag(Block * block,int x,int y,int mod,double when)4959 static int SceneDrag(Block * block, int x, int y, int mod, double when)
4960 {
4961   PyMOLGlobals *G = block->m_G;
4962   CScene *I = G->Scene;
4963   float scale, vScale;
4964   float v1[3], v2[3], n1[3], n2[3], r1, r2, cp[3], v3[3];
4965   float dx, dy, dt;
4966   float axis[3], axis2[3], theta, omega;
4967   float old_front, old_back, old_origin;
4968   int mode;
4969   int eff_width;
4970   int moved_flag;
4971   int adjust_flag;
4972   int drag_handled = false;
4973   int virtual_trackball;
4974   CObject *obj;
4975 
4976   if(I->PossibleSingleClick) {
4977     double slowest_single_click_drag = 0.15;
4978     if((when - I->LastClickTime) > slowest_single_click_drag) {
4979       I->PossibleSingleClick = 0;
4980     }
4981   }
4982 
4983   if(I->LoopFlag) {
4984     return SceneLoopDrag(block, x, y, mod);
4985   }
4986   if(I->ButtonsShown && I->PressMode) {
4987     if(I->ButtonsValid) {
4988       SceneElem *elem = I->SceneVLA;
4989       int i;
4990       drag_handled = true;
4991       I->Over = -1;
4992       for(i = 0; i < I->NScene; i++) {
4993         if(elem->drawn &&
4994            (x >= elem->x1) && (y >= elem->y1) && (x < elem->x2) && (y < elem->y2)) {
4995           I->Over = i;
4996           OrthoDirty(G);
4997           break;
4998         }
4999         elem++;
5000       }
5001       switch (I->PressMode) {
5002       case 2:
5003         if(I->Over >= 0) {
5004           if(I->Pressed != I->Over) {
5005             const char *cur_name = SettingGetGlobal_s(G, cSetting_scene_current_name);
5006             if(cur_name && elem->name && (strcmp(cur_name, elem->name))) {
5007               OrthoLineType buffer;
5008               int animate = -1;
5009               if(mod & cOrthoCTRL)
5010                 animate = 0;
5011               sprintf(buffer, "cmd.scene('''%s''',animate=%d)", elem->name, animate);
5012               PParse(G, buffer);
5013               PFlush(G);
5014               PLog(G, buffer, cPLog_pym);
5015             }
5016             I->Pressed = I->Over;
5017           }
5018         } else {
5019           I->Pressed = -1;
5020         }
5021       case 3:
5022         if((I->Over >= 0) && (I->Pressed != I->Over))
5023           I->PressMode = 4;     /* activate dragging */
5024         break;
5025       }
5026 
5027       if(I->PressMode == 4) {   /* dragging */
5028         if((I->Over >= 0) && (I->Pressed != I->Over) && (I->Pressed >= 0)) {
5029 
5030           SceneElem *pressed = I->SceneVLA + I->Pressed;
5031           OrthoLineType buffer;
5032 
5033           if(I->Over > 0) {     /* not over the first scene in list */
5034             SceneElem *first = elem - 1;
5035             SceneElem *second = pressed;
5036             if(first >= pressed) {
5037               first = elem;
5038               second = pressed;
5039             }
5040             sprintf(buffer, "cmd.scene_order('''%s %s''')", first->name, second->name);
5041           } else {
5042             sprintf(buffer, "cmd.scene_order('''%s''',location='top')", pressed->name);
5043           }
5044           PParse(G, buffer);
5045           PFlush(G);
5046           PLog(G, buffer, cPLog_pym);
5047           I->Pressed = I->Over;
5048           I->ButtonsValid = false;
5049 	  if(SettingGetGlobal_b(G, cSetting_scene_buttons)){
5050 	    OrthoInvalidateDoDraw(G);
5051 	  }
5052         }
5053       }
5054     }
5055   }
5056 
5057   if(!drag_handled) {
5058 
5059     mode = ButModeTranslate(G, I->Button, mod);
5060 
5061     y = y - I->margin.bottom;
5062 
5063     scale = (float) I->Height;
5064     if(scale > I->Width)
5065       scale = (float) I->Width;
5066     scale = 0.45F * scale;
5067     SceneInvalidateCopy(G, false);
5068     SceneDontCopyNext(G);
5069     switch (mode) {
5070     case cButModePickAtom:
5071       obj = (CObject *) I->LastPicked.context.object;
5072       if(obj)
5073         switch (obj->type) {
5074         case cObjectGadget:
5075           {
5076             ObjectGadget *gad;
5077             gad = (ObjectGadget *) obj;
5078 
5079             ObjectGadgetGetVertex(gad, I->LastPicked.src.index, I->LastPicked.src.bond,
5080                                   v1);
5081 
5082             vScale = SceneGetExactScreenVertexScale(G, v1);
5083             if(stereo_via_adjacent_array(I->StereoMode)) {
5084               x = get_stereo_x(x, &I->LastX, I->Width, NULL);
5085             }
5086 
5087             /* transform into model coodinate space */
5088             switch (obj->Context) {
5089             case 1:
5090               {
5091                 float divisor;
5092                 divisor = (float) I->Width;
5093                 if(I->Height < I->Width)
5094                   divisor = (float) I->Height;
5095                 v2[0] = (x - I->LastX) / divisor;
5096                 v2[1] = (y - I->LastY) / divisor;
5097                 v2[2] = 0;
5098               }
5099               break;
5100             default:
5101             case 0:
5102               v2[0] = (x - I->LastX) * vScale;
5103               v2[1] = (y - I->LastY) * vScale;
5104               v2[2] = 0;
5105               MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, v2, v2);
5106               break;
5107             }
5108             add3f(v1, v2, v2);
5109             ObjectGadgetSetVertex(gad, I->LastPicked.src.index, I->LastPicked.src.bond,
5110                                   v2);
5111             if (I->LastPicked.src.index){ // pick id on gadget is 1 for band (to change values),
5112                                           // 0 for everything else (to move it around)
5113               SceneChanged(G);   // changing values, need to update gadget text
5114             } else {
5115               PyMOL_NeedRedisplay(G->PyMOL);    // moving gadget, just re-draw
5116             }
5117           }
5118           break;
5119         }
5120       I->LastX = x;
5121       I->LastY = y;
5122       break;
5123     case cButModeRotDrag:
5124       eff_width = I->Width;
5125       if(stereo_via_adjacent_array(I->StereoMode)) {
5126         eff_width = I->Width / 2;
5127         x = get_stereo_x(x, &I->LastX, I->Width, NULL);
5128       }
5129 
5130       virtual_trackball = SettingGet_i(G, NULL, NULL, cSetting_virtual_trackball);
5131 
5132       if (virtual_trackball==2 &&
5133 	  (!I->prev_no_z_rotation1 || !I->prev_no_z_rotation2)) {
5134 	/* when virtual_trackball=2 and twisting, need to set v1,v2 relative to orig */
5135         v2[0] = (float) (eff_width / 2) - I->orig_x_rotation;
5136         v2[1] = (float) (I->Height / 2) - I->orig_y_rotation;
5137 
5138         v1[0] = v2[0] - (float) (x - I->LastX);
5139         v1[1] = v2[1] - (float) (y - I->LastY);
5140 
5141       } else if(virtual_trackball==1){
5142         v1[0] = (float) (eff_width / 2) - x;
5143         v1[1] = (float) (I->Height / 2) - y;
5144 
5145         v2[0] = (float) (eff_width / 2) - I->LastX;
5146         v2[1] = (float) (I->Height / 2) - I->LastY;
5147 
5148       } else {
5149         v1[0] = (float) (I->LastX) - x;
5150         v1[1] = (float) (I->LastY) - y;
5151 
5152         v2[0] = 0;
5153         v2[1] = 0;
5154       }
5155 
5156       r1 = (float) sqrt1f(v1[0] * v1[0] + v1[1] * v1[1]);
5157       r2 = (float) sqrt1f(v2[0] * v2[0] + v2[1] * v2[1]);
5158 
5159       {
5160 	short r1lt, r2lt;
5161 	if(virtual_trackball==2) {
5162 	  r1lt = I->prev_no_z_rotation1;
5163 	  r2lt = I->prev_no_z_rotation2;
5164 	} else {
5165 	  r1lt = r1 < scale;
5166 	  r2lt = r2 < scale;
5167 	  I->prev_no_z_rotation1 = r1 < scale;
5168 	  I->prev_no_z_rotation2 = r2 < scale;
5169 	  I->orig_x_rotation = x;
5170 	  I->orig_y_rotation = y;
5171 	}
5172 	if(r1lt) {
5173 	  v1[2] = (float) sqrt1f(scale * scale - r1 * r1);
5174 	} else {
5175 	  v1[2] = 0.0;
5176 	}
5177 	if(r2lt) {
5178 	  v2[2] = (float) sqrt1f(scale * scale - r2 * r2);
5179 	} else {
5180 	  v2[2] = 0.0;
5181 	}
5182       }
5183       normalize23f(v1, n1);
5184       normalize23f(v2, n2);
5185       cross_product3f(n1, n2, cp);
5186       theta = (float) (SettingGet_f(G, NULL, NULL, cSetting_mouse_scale) *
5187                        2 * 180 *
5188                        asin(sqrt1f(cp[0] * cp[0] + cp[1] * cp[1] + cp[2] * cp[2])) / cPI);
5189       dx = (v1[0] - v2[0]);
5190       dy = (v1[1] - v2[1]);
5191       dt =
5192         (float) (SettingGet_f(G, NULL, NULL, cSetting_mouse_limit) *
5193                  sqrt1f(dx * dx + dy * dy) / scale);
5194 
5195       if(theta > dt)
5196         theta = dt;
5197 
5198       normalize23f(cp, axis);
5199 
5200       axis[2] = -axis[2];
5201 
5202       theta = theta / (1.0F + (float) fabs(axis[2]));
5203 
5204       /* transform into model coodinate space */
5205       MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, axis, v2);
5206       v1[0] = (float) (cPI * theta / 180.0);
5207       EditorDrag(G, NULL, -1, mode,
5208                  SettingGetGlobal_i(G, cSetting_state) - 1, v1, v2, v3);
5209       I->LastX = x;
5210       I->LastY = y;
5211       break;
5212     case cButModeMovDrag:
5213     case cButModeMovDragZ:
5214       if(I->Threshold) {
5215         if((abs(x - I->ThresholdX) > I->Threshold) ||
5216            (abs(y - I->ThresholdY) > I->Threshold)) {
5217           I->Threshold = 0;
5218         }
5219       }
5220       if(!I->Threshold) {
5221 
5222         copy3f(I->m_view.m_origin, v1);
5223         vScale = SceneGetExactScreenVertexScale(G, v1);
5224         if(stereo_via_adjacent_array(I->StereoMode)) {
5225           x = get_stereo_x(x, &I->LastX, I->Width, NULL);
5226         }
5227 
5228         if(mode == cButModeMovDragZ) {
5229           v2[0] = 0;
5230           v2[1] = 0;
5231           v2[2] = -(y - I->LastY) * vScale;
5232         } else {
5233           v2[0] = (x - I->LastX) * vScale;
5234           v2[1] = (y - I->LastY) * vScale;
5235           v2[2] = 0;
5236         }
5237 
5238         v3[0] = 0.0F;
5239         v3[1] = 0.0F;
5240         v3[2] = 1.0F;
5241 
5242         /* transform into model coodinate space */
5243         MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, v2, v2);
5244         MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, v3, v3);
5245 
5246         EditorDrag(G, NULL, -1, mode,
5247                    SettingGetGlobal_i(G, cSetting_state) - 1, v1, v2, v3);
5248       }
5249       I->LastX = x;
5250       I->LastY = y;
5251       break;
5252     case cButModeRotObj:
5253     case cButModeMovObj:
5254     case cButModeMovObjZ:
5255     case cButModeRotView:
5256     case cButModeMovView:
5257     case cButModeMovViewZ:
5258     case cButModeRotFrag:
5259     case cButModeMovFrag:
5260     case cButModeMovFragZ:
5261     case cButModeTorFrag:
5262     case cButModeMoveAtom:
5263     case cButModeMoveAtomZ:
5264     case cButModePkTorBnd:
5265       obj = (CObject *) I->LastPicked.context.object;
5266       if(obj) {
5267         if(I->Threshold) {
5268           if((abs(x - I->ThresholdX) > I->Threshold) ||
5269              (abs(y - I->ThresholdY) > I->Threshold)) {
5270             I->Threshold = 0;
5271           }
5272         }
5273         if(!I->Threshold)
5274           switch (obj->type) {
5275           case cObjectGadget:  /* note repeated above */
5276             {
5277               ObjectGadget *gad;
5278               gad = (ObjectGadget *) obj;
5279 
5280               ObjectGadgetGetVertex(gad, I->LastPicked.src.index, I->LastPicked.src.bond,
5281                                     v1);
5282 
5283               vScale = SceneGetExactScreenVertexScale(G, v1);
5284               if(stereo_via_adjacent_array(I->StereoMode)) {
5285                 x = get_stereo_x(x, &I->LastX, I->Width, NULL);
5286               }
5287 
5288               /* transform into model coodinate space */
5289               switch (obj->Context) {
5290               case 1:
5291                 {
5292                   float divisor;
5293                   divisor = (float) I->Width;
5294                   if(I->Height < I->Width)
5295                     divisor = (float) I->Height;
5296                   v2[0] = (x - I->LastX) / divisor;
5297                   v2[1] = (y - I->LastY) / divisor;
5298                   v2[2] = 0;
5299                 }
5300                 break;
5301               default:
5302               case 0:
5303                 v2[0] = (x - I->LastX) * vScale;
5304                 v2[1] = (y - I->LastY) * vScale;
5305                 v2[2] = 0;
5306                 MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, v2, v2);
5307                 break;
5308               }
5309               add3f(v1, v2, v2);
5310               ObjectGadgetSetVertex(gad, I->LastPicked.src.index, I->LastPicked.src.bond,
5311                                     v2);
5312               if (I->LastPicked.src.index){ // pick id on gadget is 1 for band (to change values),
5313                                             // 0 for everything else (to move it around)
5314                 SceneChanged(G);   // changing values, need to update gadget text
5315               } else {
5316                 PyMOL_NeedRedisplay(G->PyMOL);    // moving gadget, just re-draw
5317               }
5318             }
5319             break;
5320           case cObjectMolecule:
5321             if(ObjectMoleculeGetAtomTxfVertex((ObjectMolecule *) obj,
5322                                               I->LastPicked.context.state,
5323                                               I->LastPicked.src.index, v1)) {
5324               /* scale properly given the current projection matrix */
5325               vScale = SceneGetExactScreenVertexScale(G, v1);
5326 
5327               if(stereo_via_adjacent_array(I->StereoMode)) {
5328                 x = get_stereo_x(x, &I->LastX, I->Width, NULL);
5329               }
5330 
5331               switch (mode) {
5332               case cButModeMovFragZ:
5333               case cButModeMovObjZ:
5334               case cButModeMovViewZ:
5335               case cButModeMoveAtomZ:
5336                 v2[0] = 0;
5337                 v2[1] = 0;
5338                 v2[2] = -(y - I->LastY) * vScale;
5339                 break;
5340               default:
5341                 v2[0] = (x - I->LastX) * vScale;
5342                 v2[1] = (y - I->LastY) * vScale;
5343                 v2[2] = 0;
5344                 break;
5345               }
5346 
5347               v3[0] = 0.0F;
5348               v3[1] = 0.0F;
5349               v3[2] = 1.0F;
5350 
5351               /* transform into model coodinate space */
5352               MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, v2, v2);
5353               MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, v3, v3);
5354 
5355               if(I->LastPicked.src.bond >= cPickableAtom) {
5356                 if((mode != cButModeMoveAtom) && (mode != cButModeMoveAtomZ)) {
5357                   EditorDrag(G, obj, I->LastPicked.src.index, mode,
5358                              SettingGetGlobal_i(G, cSetting_state) - 1, v1, v2, v3);
5359                   switch(mode) {
5360                   case cButModeMovViewZ:
5361                   case cButModeRotView:
5362                   case cButModeMovView:
5363                     if(SettingGetGlobal_i(G,cSetting_movie_auto_store) &&
5364                        SettingGetGlobal_i(G,cSetting_movie_auto_interpolate)) {
5365                       I->ReinterpolateFlag = true;
5366                       I->ReinterpolateObj = obj;
5367                     }
5368                     break;
5369                   }
5370                 } else {
5371                   int log_trans = SettingGetGlobal_b(G, cSetting_log_conformations);
5372                   ObjectMolecule *objOM = (ObjectMolecule *) obj;
5373                   ObjectMoleculeMoveAtom(objOM,
5374 					 I->LastPicked.context.state,
5375                                          I->LastPicked.src.index, v2, 1, log_trans);
5376 		  /* -- JV - if this object knows about distances, then move them if necessary */
5377 		  /* check the dynamic_measures setting and make sure the object has a distance measure, first  */
5378 		  /* obviated by new method
5379 		  if (SettingGetGlobal_i(G, cSetting_dynamic_measures))
5380 		    ObjectMoleculeMoveDist( (ObjectMolecule *) obj, SettingGetGlobal_i(G, cSetting_state)-1, I->LastPicked.src.index, v2, 1, log_trans);
5381 		  */
5382                   SceneInvalidate(G);
5383                 }
5384               } else {
5385                 int log_trans = SettingGetGlobal_b(G, cSetting_log_conformations);
5386                 ObjectMolecule *objOM = (ObjectMolecule *) obj;
5387 		float diffInPixels[2];
5388 		diffInPixels[0] = (x - I->LastX);
5389 		diffInPixels[1] = (y - I->LastY);
5390                 ObjectMoleculeMoveAtomLabel(objOM,
5391 					    I->LastPicked.context.state,
5392                                             I->LastPicked.src.index, v2, log_trans, diffInPixels);
5393                 SceneInvalidate(G);
5394               }
5395             }
5396             break;
5397           case cObjectSlice:
5398             {
5399               ObjectSlice *slice = (ObjectSlice *) obj;
5400 
5401               if(I->LastPickVertexFlag) {
5402 
5403                 copy3f(I->LastPickVertex, v1);
5404 
5405                 vScale = SceneGetExactScreenVertexScale(G, v1);
5406 
5407                 if(stereo_via_adjacent_array(I->StereoMode)) {
5408                   x = get_stereo_x(x, &I->LastX, I->Width, NULL);
5409                 }
5410 
5411                 v2[0] = (x - I->LastX) * vScale;
5412                 v2[1] = (y - I->LastY) * vScale;
5413                 v2[2] = 0;
5414 
5415                 v3[0] = 0.0F;
5416                 v3[1] = 0.0F;
5417                 v3[2] = 1.0F;
5418 
5419                 /* transform into model coodinate space */
5420                 MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, v2, v2);
5421                 MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, v3, v3);
5422 
5423                 ObjectSliceDrag(slice, SceneGetState(G), mode, v1, v2, v3);
5424               }
5425             }
5426             break;
5427           case cObjectMeasurement:
5428             if(ObjectDistGetLabelTxfVertex((ObjectDist *) obj,
5429                                            I->LastPicked.context.state,
5430                                            I->LastPicked.src.index, v1)) {
5431               /* scale properly given the current projection matrix */
5432               vScale = SceneGetExactScreenVertexScale(G, v1);
5433               if(stereo_via_adjacent_array(I->StereoMode)) {
5434                 x = get_stereo_x(x, &I->LastX, I->Width, NULL);
5435               }
5436 
5437               switch (mode) {
5438               case cButModeMovFragZ:
5439               case cButModeMovObjZ:
5440               case cButModeMovViewZ:
5441               case cButModeMoveAtomZ:
5442                 v2[0] = 0;
5443                 v2[1] = 0;
5444                 v2[2] = -(y - I->LastY) * vScale;
5445                 break;
5446               default:
5447                 v2[0] = (x - I->LastX) * vScale;
5448                 v2[1] = (y - I->LastY) * vScale;
5449                 v2[2] = 0;
5450                 break;
5451               }
5452 
5453               v3[0] = 0.0F;
5454               v3[1] = 0.0F;
5455               v3[2] = 1.0F;
5456 
5457               /* transform into model coodinate space */
5458               MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, v2, v2);
5459               MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, v3, v3);
5460 
5461               if(I->LastPicked.src.bond == cPickableLabel) {
5462                 int log_trans = SettingGetGlobal_b(G, cSetting_log_conformations);
5463                 ObjectDistMoveLabel((ObjectDist *) obj,
5464                                     I->LastPicked.context.state,
5465                                     I->LastPicked.src.index, v2, 1, log_trans);
5466                 SceneInvalidate(G);
5467               }
5468             }
5469             break;
5470           default:
5471             break;
5472           }
5473       }
5474       I->LastX = x;
5475       I->LastY = y;
5476       break;
5477     case cButModeTransXY:
5478 
5479       SceneNoteMouseInteraction(G);
5480 
5481       vScale = SceneGetExactScreenVertexScale(G, I->m_view.m_origin);
5482       if(stereo_via_adjacent_array(I->StereoMode)) {
5483 
5484         x = get_stereo_x(x, &I->LastX, I->Width, NULL);
5485       }
5486 
5487       v2[0] = (x - I->LastX) * vScale;
5488       v2[1] = (y - I->LastY) * vScale;
5489       v2[2] = 0.0F;
5490 
5491       moved_flag = false;
5492       if(I->LastX != x) {
5493         I->m_view.m_pos[0] += v2[0];
5494         I->LastX = x;
5495         SceneInvalidate(G);
5496         moved_flag = true;
5497       }
5498       if(I->LastY != y) {
5499         I->m_view.m_pos[1] += v2[1];
5500         I->LastY = y;
5501         SceneInvalidate(G);
5502         moved_flag = true;
5503       }
5504 
5505       EditorFavorOrigin(G, NULL);
5506       if(moved_flag && SettingGetGlobal_b(G, cSetting_roving_origin)) {
5507         SceneGetCenter(G, v2);     /* gets position of center of screen */
5508         SceneOriginSet(G, v2, true);
5509       }
5510       if(moved_flag && SettingGetGlobal_b(G, cSetting_roving_detail)) {
5511         SceneRovingDirty(G);
5512       }
5513       break;
5514     case cButModeRotXYZ:
5515     case cButModeRotZ:
5516     case cButModeInvRotZ:
5517     case cButModeTransZ:
5518     case cButModeClipNF:
5519     case cButModeClipN:
5520     case cButModeClipF:
5521     case cButModeRotL:
5522     case cButModeMovL:
5523     case cButModeMvzL:
5524 
5525       SceneNoteMouseInteraction(G);
5526       virtual_trackball = SettingGet_i(G, NULL, NULL, cSetting_virtual_trackball);
5527       eff_width = I->Width;
5528       if(stereo_via_adjacent_array(I->StereoMode)) {
5529         eff_width = I->Width / 2;
5530         x = get_stereo_x(x, &I->LastX, I->Width, NULL);
5531       }
5532       if (virtual_trackball==2 &&
5533 	  (!I->prev_no_z_rotation1 || !I->prev_no_z_rotation2)) {
5534 	/* when virtual_trackball=2 and twisting, need to set v1,v2 relative to orig */
5535         v2[0] = (float) (eff_width / 2) - I->orig_x_rotation;
5536         v2[1] = (float) (I->Height / 2) - I->orig_y_rotation;
5537 
5538         v1[0] = v2[0] - (float) (x - I->LastX);
5539         v1[1] = v2[1] - (float) (y - I->LastY);
5540 
5541       } else if(virtual_trackball==1){
5542         v1[0] = (float) (eff_width / 2) - x;
5543         v1[1] = (float) (I->Height / 2) - y;
5544 
5545         v2[0] = (float) (eff_width / 2) - I->LastX;
5546         v2[1] = (float) (I->Height / 2) - I->LastY;
5547 
5548       } else {
5549         v1[0] = (float) (I->LastX) - x;
5550         v1[1] = (float) (I->LastY) - y;
5551 
5552         v2[0] = 0;
5553         v2[1] = 0;
5554       }
5555 
5556       r1 = (float) sqrt1f(v1[0] * v1[0] + v1[1] * v1[1]);
5557       r2 = (float) sqrt1f(v2[0] * v2[0] + v2[1] * v2[1]);
5558 
5559       {
5560 	short r1lt, r2lt;
5561 	if(SettingGet_i(G, NULL, NULL, cSetting_virtual_trackball)==2) {
5562 	  r1lt = I->prev_no_z_rotation1;
5563 	  r2lt = I->prev_no_z_rotation2;
5564 	} else {
5565 	  r1lt = r1 < scale;
5566 	  r2lt = r2 < scale;
5567 	  I->prev_no_z_rotation1 = r1 < scale;
5568 	  I->prev_no_z_rotation2 = r2 < scale;
5569 	  I->orig_x_rotation = x;
5570 	  I->orig_y_rotation = y;
5571 	}
5572 	if(r1lt) {
5573       float val = scale * scale - r1 * r1;
5574       short isNeg = val < 0.f;
5575       v1[2] = (float) sqrt(fabs(val)) * (isNeg ? -1.f : 1.f);
5576 	} else {
5577 	  v1[2] = 0.0;
5578 	}
5579 	if(r2lt) {
5580       float val = scale * scale - r2 * r2;
5581       short isNeg = val < 0.f;
5582       v2[2] = (float) sqrt(fabs(val)) * (isNeg ? -1.f : 1.f);
5583 	} else {
5584 	  v2[2] = 0.0;
5585 	}
5586       }
5587       normalize23f(v1, n1);
5588       normalize23f(v2, n2);
5589       cross_product3f(n1, n2, cp);
5590       theta = (float) (SettingGet_f(G, NULL, NULL, cSetting_mouse_scale) *
5591                        2 * 180 *
5592                        asin(sqrt1f(cp[0] * cp[0] + cp[1] * cp[1] + cp[2] * cp[2])) / cPI);
5593 
5594       dx = (v1[0] - v2[0]);
5595       dy = (v1[1] - v2[1]);
5596       dt =
5597         (float) (SettingGet_f(G, NULL, NULL, cSetting_mouse_limit) *
5598                  sqrt1f(dx * dx + dy * dy) / scale);
5599 
5600       if(theta > dt)
5601         theta = dt;
5602 
5603       normalize23f(cp, axis);
5604 
5605       theta = theta / (1.0F + (float) fabs(axis[2]));
5606 
5607       v1[2] = 0.0;
5608       v2[2] = 0.0;
5609       normalize23f(v1, n1);
5610       normalize23f(v2, n2);
5611       cross_product3f(n1, n2, cp);
5612       omega =
5613         (float) (2 * 180 * asin(sqrt1f(cp[0] * cp[0] + cp[1] * cp[1] + cp[2] * cp[2])) /
5614                  cPI);
5615       normalize23f(cp, axis2);
5616 
5617       old_front = I->m_view.m_clip.m_front;
5618       old_back = I->m_view.m_clip.m_back;
5619       old_origin = -I->m_view.m_pos[2];
5620 
5621       moved_flag = false;
5622       adjust_flag = false;
5623       switch (mode) {
5624       case cButModeRotXYZ:
5625         if(I->LastX != x) {
5626           SceneRotate(G, theta, axis[0], axis[1], -axis[2]);
5627           I->LastX = x;
5628           adjust_flag = true;
5629         }
5630         if(I->LastY != y) {
5631           SceneRotate(G, theta, axis[0], axis[1], -axis[2]);
5632           I->LastY = y;
5633           adjust_flag = true;
5634         }
5635         break;
5636       case cButModeRotZ:
5637         if(I->LastX != x) {
5638           SceneRotate(G, omega, axis2[0], axis2[1], -axis2[2]);
5639           I->LastX = x;
5640           adjust_flag = true;
5641         }
5642         if(I->LastY != y) {
5643           SceneRotate(G, omega, axis2[0], axis2[1], -axis2[2]);
5644           I->LastY = y;
5645           adjust_flag = true;
5646         }
5647         break;
5648       case cButModeInvRotZ:
5649         if(I->LastX != x) {
5650           SceneRotate(G, (I->LastX - x) / 2.0F, 0.0F, 0.0F, 1.0F);
5651           I->LastX = x;
5652           adjust_flag = true;
5653         }
5654         break;
5655       case cButModeTransZ:
5656         // zoom
5657         if(I->LastY != y) {
5658           {
5659             // the 5.0 min depth below is an empirical value
5660             float factor = SettingGetGlobal_f(G, cSetting_mouse_z_scale) *
5661               (y - I->LastY) / 400.0 * std::max(5.f, -I->m_view.m_pos[2]);
5662             if(!SettingGetGlobal_b(G, cSetting_legacy_mouse_zoom))
5663               factor = -factor;
5664             I->m_view.m_pos[2] += factor;
5665             I->m_view.m_clip.m_front -= factor;
5666             I->m_view.m_clip.m_back -= factor;
5667             UpdateFrontBackSafe(I);
5668           }
5669           I->LastY = y;
5670           SceneInvalidate(G);
5671           adjust_flag = true;
5672         }
5673         break;
5674       case cButModeClipNF:
5675         if(I->LastX != x) {
5676           I->m_view.m_clip.m_back -= (((float) x) - I->LastX) / 10;
5677           I->LastX = x;
5678           moved_flag = true;
5679         }
5680         if(I->LastY != y) {
5681           I->m_view.m_clip.m_front -= (((float) y) - I->LastY) / 10;
5682           I->LastY = y;
5683           moved_flag = true;
5684         }
5685         if(moved_flag) {
5686           SceneClipSet(G, I->m_view.m_clip.m_front, I->m_view.m_clip.m_back);
5687         }
5688         break;
5689       case cButModeClipN:
5690         if(I->LastX != x || I->LastY != y) {
5691           I->m_view.m_clip.m_front -= (x - I->LastX + y - I->LastY) / 10.f;
5692           I->LastX = x;
5693           I->LastY = y;
5694           SceneClipSet(G, I->m_view.m_clip.m_front, I->m_view.m_clip.m_back);
5695           moved_flag = true;
5696         }
5697         break;
5698       case cButModeClipF:
5699         if(I->LastX != x || I->LastY != y) {
5700           I->m_view.m_clip.m_back -= (x - I->LastX + y - I->LastY) / 10.f;
5701           I->LastX = x;
5702           I->LastY = y;
5703           SceneClipSet(G, I->m_view.m_clip.m_front, I->m_view.m_clip.m_back);
5704           moved_flag = true;
5705         }
5706         break;
5707       case cButModeRotL:
5708       case cButModeMovL:
5709 	{
5710 	  /* when light_count == 1, there is an ambient light;
5711 	   * when light_count == 2, there are two lights, the ambient and
5712 	   * a directional, called "light". When there are three, it's the
5713 	   * ambient, light and the third is light2, and so on.
5714 	   *
5715 	   * Should we use an off-by-one here to make this easier for the
5716 	   * user to understand? light1 is ambient, light2 is first directional
5717 	   *
5718 	   */
5719 	  float pos[3];
5720 	  int which_light;
5721 	  float ms = 0.01;
5722 
5723 	  which_light = light_setting_indices[glm::clamp(
5724               SettingGetGlobal_i(G, cSetting_edit_light), 1, 9) - 1];
5725 
5726 	  copy3f(SettingGet<const float *>(G, which_light), pos);
5727 
5728 	  pos[0] += (float) dx * ms;
5729 	  pos[1] += (float) dy * ms;
5730 
5731 	  SettingSet_3fv(G->Setting, which_light, pos);
5732           SettingGenerateSideEffects(G, which_light, NULL, 0, 1);
5733 	  I->LastX = x;
5734 	  I->LastY = y;
5735 	}
5736 	break;
5737       case cButModeMvzL:
5738 	{
5739 	  float pos[3];
5740 	  int which_light;
5741 	  float ms = 0.01;
5742 	  float factor = 0.f;
5743 
5744 	  /* when light_count == 1, there is an ambient light;
5745 	   * when light_count == 2, there are two lights, the ambient and
5746 	   * a directional, called "light". When there are three, it's the
5747 	   * ambient, light and the third is light2, and so on.
5748 	   */
5749 
5750 	  which_light = light_setting_indices[glm::clamp(
5751               SettingGetGlobal_i(G, cSetting_edit_light), 1, 9) - 1];
5752 
5753 	  if(I->LastY != y) {
5754 	    factor = 400 / ((I->m_view.m_clipSafe.m_front + I->m_view.m_clipSafe.m_back) / 2);
5755 	    if(factor >= 0.0F) {
5756 	      factor = SettingGetGlobal_f(G, cSetting_mouse_z_scale) *
5757 		(((float) y) - I->LastY) / factor;
5758 	      if(!SettingGetGlobal_b(G, cSetting_legacy_mouse_zoom))
5759 		factor = -factor;
5760 	    }
5761 	  }
5762 
5763 	  copy3f(SettingGet<const float *>(G, which_light), pos);
5764           SettingGenerateSideEffects(G, which_light, NULL, 0, 1);
5765 
5766 	  pos[2] -= factor * ms;
5767 
5768 	  SettingSet_3fv(G->Setting, which_light, pos);
5769 	  I->LastX = x;
5770 	  I->LastY = y;
5771 	}
5772 	break;
5773       }
5774       if(moved_flag)
5775         SceneDoRoving(G, old_front, old_back, old_origin, adjust_flag, false);
5776     }
5777   }
5778   if(I->PossibleSingleClick) {
5779     int max_single_click_drag = 4;
5780     int dx = abs(I->StartX - I->LastX);
5781     int dy = abs(I->StartY - I->LastY);
5782     if((dx > max_single_click_drag) || (dy > max_single_click_drag)) {
5783       I->PossibleSingleClick = false;
5784     }
5785   }
5786   return (1);
5787 }
5788 
SceneDeferredClick(DeferredMouse * dm)5789 static int SceneDeferredClick(DeferredMouse * dm)
5790 {
5791   if(!SceneClick(dm->block, dm->button, dm->x, dm->y, dm->mod, dm->when)) {
5792   }
5793   return 1;
5794 }
5795 
5796 /**
5797  * Will call cmd.raw_image_callback(img) with the current RGBA image, copied
5798  * to a WxHx4 numpy array. Return false if no callback is defined
5799  * (cmd.raw_image_callback == None).
5800  */
5801 static
call_raw_image_callback(PyMOLGlobals * G)5802 bool call_raw_image_callback(PyMOLGlobals * G) {
5803   bool done = false;
5804 
5805 #ifndef _PYMOL_NOPY
5806   int blocked = PAutoBlock(G);
5807   auto raw_image_callback =
5808     PyObject_GetAttrString(G->P_inst->cmd, "raw_image_callback");
5809 
5810   if (raw_image_callback != Py_None) {
5811 #ifdef _PYMOL_NUMPY
5812     auto& image = G->Scene->Image;
5813 
5814     // RGBA image as uint8 numpy array
5815     import_array1(0);
5816     npy_intp dims[3] = {image->getWidth(), image->getHeight(), 4};
5817     auto py = PyArray_SimpleNew(3, dims, NPY_UINT8);
5818     memcpy(PyArray_DATA((PyArrayObject *)py), image->bits(), dims[0] * dims[1] * 4);
5819 
5820     PYOBJECT_CALLFUNCTION(raw_image_callback, "O", py);
5821     Py_DECREF(py);
5822 
5823     done = true;
5824 #else
5825     PRINTFB(G, FB_Scene, FB_Errors)
5826       " raw_image_callback-Error: no numpy support\n" ENDFB(G);
5827 #endif
5828   }
5829 
5830   Py_XDECREF(raw_image_callback);
5831   PAutoUnblock(G, blocked);
5832 #endif
5833 
5834   return done;
5835 }
5836 
SceneDeferredImage(DeferredImage * di)5837 static int SceneDeferredImage(DeferredImage * di)
5838 {
5839   PyMOLGlobals *G = di->m_G;
5840   SceneMakeSizedImage(G, di->width, di->height, di->antialias);
5841   if(!di->filename.empty()) {
5842     ScenePNG(G, di->filename.c_str(), di->dpi, di->quiet, false, di->format);
5843   } else if(call_raw_image_callback(G)) {
5844   } else if(G->HaveGUI && SettingGetGlobal_b(G, cSetting_auto_copy_images)) {
5845 #ifdef _PYMOL_IP_EXTRAS
5846     if(IncentiveCopyToClipboard(G, di->quiet)) {
5847     }
5848 #else
5849 #ifdef PYMOL_EVAL
5850     PRINTFB(G, FB_Scene, FB_Warnings)
5851       " Warning: Clipboard image transfers disabled in Evaluation builds.\n" ENDFB(G);
5852 #endif
5853 #endif
5854   }
5855   return 1;
5856 }
5857 
SceneDeferImage(PyMOLGlobals * G,int width,int height,const char * filename,int antialias,float dpi,int format,int quiet)5858 int SceneDeferImage(PyMOLGlobals * G, int width, int height,
5859                     const char *filename, int antialias, float dpi, int format, int quiet)
5860 {
5861   auto di = pymol::make_unique<DeferredImage>(G);
5862   if(di) {
5863     di->width = width;
5864     di->height = height;
5865     di->antialias = antialias;
5866     di->fn = (DeferredFn *) SceneDeferredImage;
5867     di->dpi = dpi;
5868     di->format = format;
5869     di->quiet = quiet;
5870     if(filename){
5871       di->filename = filename;
5872     }
5873   }
5874   OrthoDefer(G, std::move(di));
5875   return 1;
5876 }
5877 
click(int button,int x,int y,int mod)5878 int CScene::click(int button, int x, int y, int mod) // Originally SceneDeferClick!!
5879 {
5880   return SceneDeferClickWhen(this, button, x, y, UtilGetSeconds(m_G), mod);
5881 }
5882 
SceneDeferClickWhen(Block * block,int button,int x,int y,double when,int mod)5883 static int SceneDeferClickWhen(Block * block, int button, int x, int y, double when,
5884                                int mod)
5885 {
5886   PyMOLGlobals *G = block->m_G;
5887   auto dm = pymol::make_unique<DeferredMouse>(G);
5888   if(dm) {
5889     dm->block = block;
5890     dm->button = button;
5891     dm->x = x;
5892     dm->y = y;
5893     dm->when = when;
5894     dm->mod = mod;
5895     dm->fn = (DeferredFn *) SceneDeferredClick;
5896   }
5897   OrthoDefer(G, std::move(dm));
5898   return 1;
5899 }
5900 
SceneDeferredDrag(DeferredMouse * dm)5901 static int SceneDeferredDrag(DeferredMouse * dm)
5902 {
5903   SceneDrag(dm->block, dm->x, dm->y, dm->mod, dm->when);
5904   return 1;
5905 }
5906 
drag(int x,int y,int mod)5907 int CScene::drag(int x, int y, int mod) //Originally SceneDeferDrag
5908 {
5909   PyMOLGlobals *G = m_G;
5910   auto dm = pymol::make_unique<DeferredMouse>(G);
5911   if(dm) {
5912     dm->block = this;
5913     dm->x = x;
5914     dm->y = y;
5915     dm->mod = mod;
5916     dm->when = UtilGetSeconds(G);
5917     dm->fn = (DeferredFn *) SceneDeferredDrag;
5918   }
5919   OrthoDefer(G, std::move(dm));
5920   return 1;
5921 }
5922 
SceneDeferredRelease(DeferredMouse * dm)5923 static int SceneDeferredRelease(DeferredMouse * dm)
5924 {
5925   SceneRelease(dm->block, dm->button, dm->x, dm->y, dm->mod, dm->when);
5926   return 1;
5927 }
5928 
release(int button,int x,int y,int mod)5929 int CScene::release(int button, int x, int y, int mod) // Originally SceneDeferRelease
5930 {
5931   PyMOLGlobals *G = m_G;
5932   auto dm = pymol::make_unique<DeferredMouse>(G);
5933   if(dm) {
5934     dm->block = this;
5935     dm->button = button;
5936     dm->x = x;
5937     dm->y = y;
5938     dm->mod = mod;
5939     dm->when = UtilGetSeconds(G);
5940     dm->fn = (DeferredFn *) SceneDeferredRelease;
5941   }
5942   OrthoDefer(G, std::move(dm));
5943   return 1;
5944 }
5945 
5946 /*========================================================================*/
SceneFree(PyMOLGlobals * G)5947 void SceneFree(PyMOLGlobals * G)
5948 {
5949   CScene *I = G->Scene;
5950 #if !defined(PURE_OPENGL_ES_2) || defined(_WEBGL)
5951   CGOFree(I->offscreenCGO);
5952 #endif
5953 
5954   CGOFree(I->AlphaCGO);
5955   CGOFree(I->offscreenCGO);
5956   CGOFree(I->offscreenOIT_CGO);
5957   CGOFree(I->offscreenOIT_CGO_copy);
5958   VLAFreeP(I->SceneVLA);
5959   VLAFreeP(I->SceneNameVLA);
5960   VLAFreeP(I->SlotVLA);
5961   I->Obj.clear();
5962   I->GadgetObjs.clear();
5963   I->NonGadgetObjs.clear();
5964 
5965   ScenePurgeImage(G);
5966   CGOFree(G->DebugCGO);
5967   delete G->Scene;
5968 }
5969 
5970 
5971 /*========================================================================*/
SceneResetMatrix(PyMOLGlobals * G)5972 void SceneResetMatrix(PyMOLGlobals * G)
5973 {
5974   CScene *I = G->Scene;
5975   identity44f(I->m_view.m_rotMatrix);
5976   SceneUpdateInvMatrix(G);
5977 }
5978 
5979 
5980 /*========================================================================*/
SceneSetDefaultView(PyMOLGlobals * G)5981 void SceneSetDefaultView(PyMOLGlobals * G)
5982 {
5983   CScene *I = G->Scene;
5984 
5985   identity44f(I->m_view.m_rotMatrix);
5986   SceneUpdateInvMatrix(G);
5987 
5988   I->ViewNormal[0] = 0.0F;
5989   I->ViewNormal[1] = 0.0F;
5990   I->ViewNormal[2] = 1.0F;
5991 
5992   I->m_view.m_pos[0] = 0.0F;
5993   I->m_view.m_pos[1] = 0.0F;
5994   I->m_view.m_pos[2] = -50.0F;
5995 
5996   I->m_view.m_origin[0] = 0.0F;
5997   I->m_view.m_origin[1] = 0.0F;
5998   I->m_view.m_origin[2] = 0.0F;
5999 
6000   I->m_view.m_clip.m_front = 40.0F;
6001   I->m_view.m_clip.m_back = 100.0F;
6002   UpdateFrontBackSafe(I);
6003 
6004   I->Scale = 1.0F;
6005 
6006 }
6007 
SceneReinitialize(PyMOLGlobals * G)6008 int SceneReinitialize(PyMOLGlobals * G)
6009 {
6010   int ok = true;
6011   SceneSetDefaultView(G);
6012   SceneCountFrames(G);
6013   SceneSetFrame(G, 0, 0);
6014   SceneInvalidate(G);
6015   G->Scene->NScene = 0;
6016   return (ok);
6017 }
6018 
6019 
6020 /*========================================================================*/
SceneInit(PyMOLGlobals * G)6021 int SceneInit(PyMOLGlobals * G)
6022 {
6023   CScene *I = NULL;
6024   I = (G->Scene = new CScene(G));
6025   if(I) {
6026 
6027     /* all defaults to zero, so only initialize non-zero elements */
6028     G->DebugCGO = CGONew(G);
6029 
6030     I->TextColor[0] = 0.2F;
6031     I->TextColor[1] = 1.0F;
6032     I->TextColor[2] = 0.2F;
6033 
6034     I->LastClickTime = UtilGetSeconds(G);
6035 
6036     SceneSetDefaultView(G);
6037 
6038     I->Scale = 1.0;
6039     I->active = true;
6040 
6041     OrthoAttach(G, I, cOrthoScene);
6042 
6043     I->DirtyFlag = true;
6044 
6045     I->LastRender = UtilGetSeconds(G);
6046     I->LastFrameTime = UtilGetSeconds(G);
6047 
6048     I->LastSweepTime = UtilGetSeconds(G);
6049 
6050     I->LastStateBuilt = -1;
6051     I->CopyNextFlag = true;
6052 
6053     SceneRestartFrameTimer(G);
6054     SceneRestartPerfTimer(G);
6055 
6056     I->Width = 640;             /* standard defaults */
6057     I->Height = 480;
6058 
6059     I->VertexScale = 0.01F;
6060 
6061     /* scene list */
6062 
6063     I->Pressed = -1;
6064     I->Over = -1;
6065 
6066     I->SceneNameVLA = VLAlloc(char, 10);
6067     I->SceneVLA = VLAlloc(SceneElem, 10);
6068 
6069     return 1;
6070   } else
6071     return 0;
6072 }
6073 
6074 
6075 /*========================================================================*/
reshape(int width,int height)6076 void CScene::reshape(int width, int height)
6077 {
6078   PyMOLGlobals *G = m_G;
6079   CScene *I = G->Scene;
6080 
6081   if(I->margin.right) {
6082     width -= I->margin.right;
6083     if(width < 1)
6084       width = 1;
6085   }
6086 
6087   if(I->margin.top) {
6088     height -= I->margin.top;
6089   }
6090 
6091   I->Width = width;
6092 
6093   I->Height = height;
6094 
6095   I->rect.top = I->Height;
6096   I->rect.left = 0;
6097   I->rect.bottom = 0;
6098   I->rect.right = I->Width;
6099 
6100   if(I->margin.bottom) {
6101     height -= I->margin.bottom;
6102     if(height < 1)
6103       height = 1;
6104     I->Height = height;
6105     I->rect.bottom = I->rect.top - I->Height;
6106   }
6107   SceneDirty(G);
6108 
6109   if(I->CopyType && (!I->CopyForced)) {
6110     SceneInvalidateCopy(G, false);
6111   }
6112   /*MovieClearImages(G); */
6113   MovieSetSize(G, I->Width, I->Height);
6114   SceneInvalidateStencil(G);
6115 }
6116 
6117 
6118 /*========================================================================*/
SceneResetNormal(PyMOLGlobals * G,int lines)6119 void SceneResetNormal(PyMOLGlobals * G, int lines)
6120 {
6121   CScene *I = G->Scene;
6122   if(G->HaveGUI && G->ValidContext) {
6123     if(lines)
6124       glNormal3fv(I->LinesNormal);
6125     else
6126       glNormal3fv(I->ViewNormal);
6127   }
6128 }
6129 
SceneResetNormalCGO(PyMOLGlobals * G,CGO * cgo,int lines)6130 void SceneResetNormalCGO(PyMOLGlobals * G, CGO *cgo, int lines)
6131 {
6132   CScene *I = G->Scene;
6133   if(G->HaveGUI && G->ValidContext) {
6134     if(lines)
6135       CGONormalv(cgo, I->LinesNormal);
6136     else
6137       CGONormalv(cgo, I->ViewNormal);
6138   }
6139 }
6140 
SceneResetNormalToViewVector(PyMOLGlobals * G,short use_shader)6141 void SceneResetNormalToViewVector(PyMOLGlobals * G, short use_shader)
6142 {
6143   CScene *I = G->Scene;
6144   if(G->HaveGUI && G->ValidContext) {
6145 #if defined(PURE_OPENGL_ES_2)
6146     glVertexAttrib3f(VERTEX_NORMAL, I->ModMatrix[2], I->ModMatrix[6], I->ModMatrix[10]);
6147 #else
6148     if (use_shader){
6149       glVertexAttrib3f(VERTEX_NORMAL, I->ModMatrix[2], I->ModMatrix[6], I->ModMatrix[10]);
6150     } else {
6151       glNormal3f(I->ModMatrix[2], I->ModMatrix[6], I->ModMatrix[10]);
6152     }
6153 #endif
6154   }
6155 }
6156 
SceneResetNormalUseShader(PyMOLGlobals * G,int lines,short use_shader)6157 void SceneResetNormalUseShader(PyMOLGlobals * G, int lines, short use_shader)
6158 {
6159   CScene *I = G->Scene;
6160   if(G->HaveGUI && G->ValidContext) {
6161 #ifdef PURE_OPENGL_ES_2
6162     if(lines)
6163       glVertexAttrib3fv(VERTEX_NORMAL, I->LinesNormal);
6164     else
6165       glVertexAttrib3fv(VERTEX_NORMAL, I->ViewNormal);
6166 #else
6167     if (use_shader){
6168       if(lines)
6169 	glVertexAttrib3fv(VERTEX_NORMAL, I->LinesNormal);
6170       else
6171 	glVertexAttrib3fv(VERTEX_NORMAL, I->ViewNormal);
6172     } else {
6173       if(lines)
6174 	glNormal3fv(I->LinesNormal);
6175       else
6176 	glNormal3fv(I->ViewNormal);
6177     }
6178 #endif
6179   }
6180 }
6181 
SceneResetNormalUseShaderAttribute(PyMOLGlobals * G,int lines,short use_shader,int attr)6182 void SceneResetNormalUseShaderAttribute(PyMOLGlobals * G, int lines, short use_shader, int attr)
6183 {
6184   CScene *I = G->Scene;
6185   if(G->HaveGUI && G->ValidContext) {
6186 #ifdef PURE_OPENGL_ES_2
6187     if (attr < 0)
6188       return;
6189     if(lines)
6190       glVertexAttrib3fv(attr, I->LinesNormal);
6191     else
6192       glVertexAttrib3fv(attr, I->ViewNormal);
6193 #else
6194     if (use_shader){
6195       if(lines)
6196 	glVertexAttrib3fv(attr, I->LinesNormal);
6197       else
6198 	glVertexAttrib3fv(attr, I->ViewNormal);
6199     } else {
6200       if(lines)
6201 	glNormal3fv(I->LinesNormal);
6202       else
6203 	glNormal3fv(I->ViewNormal);
6204     }
6205 #endif
6206   }
6207 }
6208 
6209 
SceneGetResetNormal(PyMOLGlobals * G,float * normal,int lines)6210 void SceneGetResetNormal(PyMOLGlobals * G, float *normal, int lines)
6211 {
6212   CScene *I = G->Scene;
6213   float *norm;
6214   if(G->HaveGUI && G->ValidContext) {
6215     if(lines)
6216       norm = I->LinesNormal;
6217     else
6218       norm = I->ViewNormal;
6219     normal[0] = norm[0]; normal[1] = norm[1]; normal[2] = norm[2];
6220   }
6221 }
6222 
6223 /*========================================================================*/
SceneApplyImageGamma(PyMOLGlobals * G,unsigned int * buffer,int width,int height)6224 void SceneApplyImageGamma(PyMOLGlobals * G, unsigned int *buffer, int width,
6225                           int height)
6226 {
6227   float gamma = SettingGetGlobal_f(G, cSetting_gamma);
6228 
6229   if(gamma > R_SMALL4)
6230     gamma = 1.0F / gamma;
6231   else
6232     gamma = 1.0F;
6233 
6234   if(buffer && height && width) {
6235     float _inv3 = 1 / (255 * 3.0F);
6236     float _1 = 1 / 3.0F;
6237     unsigned char *p;
6238     int x, y;
6239     float c1, c2, c3, inp, sig;
6240     unsigned int i1, i2, i3;
6241     p = (unsigned char *) buffer;
6242     for(y = 0; y < height; y++) {
6243       for(x = 0; x < width; x++) {
6244         c1 = p[0];
6245         c2 = p[1];
6246         c3 = p[2];
6247         inp = (c1 + c2 + c3) * _inv3;
6248         if(inp < R_SMALL4)
6249           sig = _1;
6250         else
6251           sig = (float) (pow(inp, gamma) / inp);
6252         i1 = (unsigned int) (sig * c1);
6253         i2 = (unsigned int) (sig * c2);
6254         i3 = (unsigned int) (sig * c3);
6255         if(i1 > 255)
6256           i1 = 255;
6257         if(i2 > 255)
6258           i2 = 255;
6259         if(i3 > 255)
6260           i3 = 255;
6261         p[0] = i1;
6262         p[1] = i2;
6263         p[2] = i3;
6264         p += 4;
6265       }
6266     }
6267   }
6268 }
6269 
SceneUpdateAnimation(PyMOLGlobals * G)6270 void SceneUpdateAnimation(PyMOLGlobals * G)
6271 {
6272   CScene *I = G->Scene;
6273   int rockFlag = false;
6274   int dirty = false;
6275   int movie_rock = SettingGetGlobal_i(G, cSetting_movie_rock);
6276 
6277   if(movie_rock < 0)
6278     movie_rock = ControlRocking(G);
6279 
6280   if(MoviePlaying(G) && movie_rock) {
6281 
6282     if(MovieGetRealtime(G) && !SettingGetGlobal_b(G, cSetting_movie_animate_by_frame)) {
6283       I->RenderTime = UtilGetSeconds(G) - I->LastSweepTime;
6284       rockFlag = true;
6285       dirty = true;             /* force a subsequent update */
6286     } else {
6287       float fps = SceneGetFPS(G);       /* guaranteed to be >= 0.0F */
6288       if(fps > 0.0F) {
6289         int rock_frame = SceneGetFrame(G);
6290         if(rock_frame != I->RockFrame) {
6291           I->RockFrame = rock_frame;
6292           rockFlag = true;
6293           I->RenderTime = 1.0 / fps;
6294         }
6295       } else {
6296         I->RenderTime = UtilGetSeconds(G) - I->LastSweepTime;
6297         rockFlag = true;
6298       }
6299     }
6300   } else
6301     dirty = true;
6302 
6303   if(I->cur_ani_elem < I->n_ani_elem) { /* play motion animation */
6304     double now;
6305 
6306     int cur = I->cur_ani_elem;
6307 
6308     if(I->AnimationStartFlag) {
6309       /* allow animation timing to lag since it may take a few seconds
6310          to get here given geometry updates, etc. */
6311 
6312       I->AnimationLagTime = UtilGetSeconds(G) - I->AnimationStartTime;
6313       I->AnimationStartFlag = false;
6314     }
6315 
6316     if((!MoviePlaying(G)) ||
6317        ((MovieGetRealtime(G) &&
6318          !SettingGetGlobal_b(G, cSetting_movie_animate_by_frame)))) {
6319       now = UtilGetSeconds(G) - I->AnimationLagTime;
6320     } else {
6321       float fps = SceneGetFPS(G);       /* guaranteed to be >= 0.0F */
6322       int frame = SceneGetFrame(G);
6323       int n_frame = 0;
6324 
6325       cur = 0;                  /* allow backwards interpolation */
6326       if(frame >= I->AnimationStartFrame) {
6327         n_frame = frame - I->AnimationStartFrame;
6328       } else {
6329         n_frame = frame + (I->NFrame - I->AnimationStartFrame);
6330       }
6331       now = I->AnimationStartTime + n_frame / fps;
6332     }
6333 
6334     while(I->ani_elem[cur].timing < now) {
6335       cur++;
6336       if(cur >= I->n_ani_elem) {
6337         cur = I->n_ani_elem;
6338         break;
6339       }
6340     }
6341     I->cur_ani_elem = cur;
6342     SceneFromViewElem(G, I->ani_elem + cur, dirty);
6343     OrthoDirty(G);
6344   }
6345   if(rockFlag && (I->RenderTime != 0.0)) {
6346     SceneUpdateCameraRock(G, dirty);
6347   }
6348 }
6349 
SceneGetDrawFlag(GridInfo * grid,int * slot_vla,int slot)6350 int SceneGetDrawFlag(GridInfo * grid, int *slot_vla, int slot)
6351 {
6352   int draw_flag = false;
6353   if(grid && grid->active) {
6354     switch (grid->mode) {
6355     case 1:                    /* assigned grid slots (usually by group) */
6356       {
6357         if(((slot < 0) && grid->slot) ||
6358            ((slot == 0) && (grid->slot == 0)) ||
6359            (slot_vla && (slot_vla[slot] == grid->slot))) {
6360           draw_flag = true;
6361         }
6362       }
6363       break;
6364     case 2:                    /* each state in a separate slot */
6365     case 3:                    /* each object-state */
6366       draw_flag = true;
6367       break;
6368     }
6369   } else {
6370     draw_flag = true;
6371   }
6372   return draw_flag;
6373 }
6374 
SceneGetDrawFlagGrid(PyMOLGlobals * G,GridInfo * grid,int slot)6375 int SceneGetDrawFlagGrid(PyMOLGlobals * G, GridInfo * grid, int slot)
6376 {
6377   CScene *I = G->Scene;
6378   return SceneGetDrawFlag(grid, I->SlotVLA, slot);
6379 }
6380 
6381 /*========================================================================*/
SceneCopy(PyMOLGlobals * G,GLenum buffer,int force,int entire_window)6382 void SceneCopy(PyMOLGlobals * G, GLenum buffer, int force, int entire_window)
6383 {
6384   CScene *I = G->Scene;
6385   unsigned int buffer_size;
6386 
6387   if (buffer == GL_BACK) {
6388     buffer = G->DRAW_BUFFER0;
6389   }
6390 
6391   if(force || (!(I->StereoMode ||
6392                  SettingGetGlobal_b(G, cSetting_stereo_double_pump_mono) || I->ButtonsShown))) {
6393     /* no copies while in stereo mode */
6394     if(force || ((!I->DirtyFlag) && (!I->CopyType))) {
6395       int x, y, w, h;
6396       if(entire_window) {
6397         x = 0;
6398         y = 0;
6399         h = OrthoGetHeight(G);
6400         w = OrthoGetWidth(G);
6401       } else {
6402         x = I->rect.left;
6403         y = I->rect.bottom;
6404         w = I->Width;
6405         h = I->Height;
6406       }
6407       ScenePurgeImage(G);
6408       buffer_size = 4 * w * h;
6409       if(buffer_size) {
6410         I->Image = std::make_shared<pymol::Image>(w, h);
6411         if(G->HaveGUI && G->ValidContext) {
6412           glReadBuffer(buffer);
6413           PyMOLReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, I->Image->bits());
6414         }
6415       }
6416       I->CopyType = true;
6417       I->Image->m_needs_alpha_reset = true;
6418       I->CopyForced = force;
6419     }
6420   }
6421 }
6422 
6423 
6424 /*========================================================================*/
SceneRovingCheckDirty(PyMOLGlobals * G)6425 int SceneRovingCheckDirty(PyMOLGlobals * G)
6426 {
6427   CScene *I = G->Scene;
6428   return (I->RovingDirtyFlag);
6429 }
6430 
6431 struct _CObjectUpdateThreadInfo {
6432   CObject *obj;
6433 };
6434 
SceneObjectUpdateThread(CObjectUpdateThreadInfo * T)6435 void SceneObjectUpdateThread(CObjectUpdateThreadInfo * T)
6436 {
6437   if(T->obj) {
6438     T->obj->update();
6439   }
6440 }
6441 
6442 #ifndef _PYMOL_NOPY
SceneObjectUpdateSpawn(PyMOLGlobals * G,CObjectUpdateThreadInfo * Thread,int n_thread,int n_total)6443 static void SceneObjectUpdateSpawn(PyMOLGlobals * G, CObjectUpdateThreadInfo * Thread,
6444                                    int n_thread, int n_total)
6445 {
6446   if(n_total == 1) {
6447     SceneObjectUpdateThread(Thread);
6448   } else if(n_total) {
6449     int blocked;
6450     PyObject *info_list;
6451     int a, n = 0;
6452     blocked = PAutoBlock(G);
6453 
6454     PRINTFB(G, FB_Scene, FB_Blather)
6455       " Scene: updating objects with %d threads...\n", n_thread ENDFB(G);
6456     info_list = PyList_New(n_total);
6457     for(a = 0; a < n_total; a++) {
6458       PyList_SetItem(info_list, a, PyCObject_FromVoidPtr(Thread + a, NULL));
6459       n++;
6460     }
6461     PXDecRef(PYOBJECT_CALLMETHOD
6462              (G->P_inst->cmd, "_object_update_spawn", "Oi", info_list, n_thread));
6463     Py_DECREF(info_list);
6464     PAutoUnblock(G, blocked);
6465   }
6466 }
6467 #endif
6468 
SceneStencilCheck(PyMOLGlobals * G)6469 static void SceneStencilCheck(PyMOLGlobals *G)
6470 {
6471   CScene *I = G->Scene;
6472   if( I->StereoMode == cStereo_stencil_by_row ) {
6473     int bottom = 0;
6474 
6475 #ifndef _PYMOL_PRETEND_GLUT
6476     if(G->Main)
6477       bottom = p_glutGet(P_GLUT_WINDOW_Y) + p_glutGet(P_GLUT_WINDOW_HEIGHT);
6478 #endif
6479 
6480     int parity = bottom & 0x1;
6481     if(parity != I->StencilParity) {
6482       I->StencilValid = false;
6483       I->StencilParity = parity;
6484       SceneDirty(G);
6485     }
6486   }
6487 }
6488 
6489 /*========================================================================*/
SceneUpdate(PyMOLGlobals * G,int force)6490 void SceneUpdate(PyMOLGlobals * G, int force)
6491 {
6492   CScene *I = G->Scene;
6493 
6494   int cur_state = SettingGetGlobal_i(G, cSetting_state) - 1;
6495   int defer_builds_mode = SettingGetGlobal_i(G, cSetting_defer_builds_mode);
6496 
6497   PRINTFD(G, FB_Scene)
6498     " SceneUpdate: entered.\n" ENDFD;
6499 
6500   OrthoBusyPrime(G);
6501   WizardDoPosition(G, false);
6502   WizardDoView(G, false);
6503   EditorUpdate(G);
6504   SceneStencilCheck(G);
6505 
6506   if(defer_builds_mode == 0) {
6507     if(SettingGetGlobal_i(G, cSetting_draw_mode) == -2) {
6508       defer_builds_mode = 1;
6509     }
6510   }
6511 
6512   if(force || I->ChangedFlag || ((cur_state != I->LastStateBuilt) &&
6513                                  (defer_builds_mode > 0))) {
6514 
6515     SceneCountFrames(G);
6516 
6517     if(force || (defer_builds_mode != 5)) {     /* mode 5 == immediate mode */
6518 
6519       PyMOL_SetBusy(G->PyMOL, true);    /*  race condition -- may need to be fixed */
6520 
6521       /* update all gadgets first (single-threaded since they're thread-unsafe) */
6522       for (auto& GadgetObj : I->GadgetObjs) {
6523         GadgetObj->update();
6524       }
6525 
6526       {
6527 #ifndef _PYMOL_NOPY
6528         int n_thread = SettingGetGlobal_i(G, cSetting_max_threads);
6529         int multithread = SettingGetGlobal_i(G, cSetting_async_builds);
6530         if(multithread && (n_thread > 1)) {
6531           int min_start = -1;
6532           int max_stop = -1;
6533           int n_obj = 0;
6534           for (auto& obj : I->Obj) {
6535             int start = 0;
6536             n_obj++;
6537             int stop = obj->getNFrame();
6538 	    /* set start/stop to define the range for this object
6539 	     * depending upon various build settings */
6540             ObjectAdjustStateRebuildRange(obj, &start, &stop);
6541             if(min_start < 0) {
6542               min_start = start;
6543               max_stop = stop;
6544             } else {
6545               if(min_start > start)
6546                 min_start = start;
6547               if(max_stop < stop)
6548                 max_stop = stop;
6549             }
6550           }
6551 
6552           int n_frame = max_stop - min_start;
6553 
6554           if(n_frame > n_thread) {
6555             n_thread = 1;
6556             /* prevent n_thread * n_thread -- only multithread within
6557                individual object states (typically more balanced) */
6558           } else if(n_frame > 1) {
6559             n_thread = n_thread / n_frame;
6560           }
6561 
6562           if(n_thread < 1)
6563             n_thread = 1;
6564         }
6565 
6566 	/* Note: we might want to optimize this by doing multi-threaded updates
6567 	   for all objects. */
6568         if(multithread && (n_thread > 1)) {
6569           /* multi-threaded geometry update */
6570           int cnt = I->NonGadgetObjs.size();
6571 
6572           if(cnt) {
6573             CObjectUpdateThreadInfo *thread_info = pymol::malloc<CObjectUpdateThreadInfo>(cnt);
6574             if(thread_info) {
6575               cnt = 0;
6576               for (auto& NonGadgetObj : I->NonGadgetObjs) {
6577                 thread_info[cnt++].obj = NonGadgetObj;
6578               }
6579               SceneObjectUpdateSpawn(G, thread_info, n_thread, cnt);
6580               FreeP(thread_info);
6581             }
6582           }
6583         } else
6584 #endif
6585           /* single-threaded update */
6586           for (auto& obj : I->Obj) {
6587             obj->update();
6588           }
6589       }
6590       PyMOL_SetBusy(G->PyMOL, false);   /*  race condition -- may need to be fixed */
6591     } else { /* defer builds mode == 5 -- for now, only update non-molecular objects */
6592       /* single-threaded update */
6593       for (auto& obj : I->Obj) {
6594         if(obj->type != cObjectMolecule) {
6595           obj->update();
6596         }
6597       }
6598     }
6599 
6600     I->ChangedFlag = false;
6601 
6602     if((defer_builds_mode >= 2) && (force || (defer_builds_mode != 5)) &&
6603        (cur_state != I->LastStateBuilt)) {
6604       /* purge graphics representation when no longer used */
6605       if(I->LastStateBuilt >= 0) {
6606         for ( auto it = I->Obj.begin(); it != I->Obj.end(); ++it) {
6607           if ((*it)->type != cObjectMolecule || force || defer_builds_mode != 5) {
6608             int static_singletons =
6609               SettingGet_b(G, (*it)->Setting, NULL, cSetting_static_singletons);
6610             int async_builds =
6611               SettingGet_b(G, (*it)->Setting, NULL, cSetting_async_builds);
6612             int max_threads =
6613               SettingGet_i(G, (*it)->Setting, NULL, cSetting_max_threads);
6614             int nFrame = 0;
6615             nFrame = (*it)->getNFrame();
6616             if((nFrame > 1) || (!static_singletons)) {
6617               int start = I->LastStateBuilt;
6618               int stop = start + 1;
6619               int ste;
6620               if(async_builds && (max_threads > 1)) {
6621                 if((start / max_threads) == (cur_state / max_threads)) {
6622                   stop = start; /* don't purge current batch */
6623                 } else {
6624                   int base = start / max_threads;       /* now purge previous batch */
6625                   start = base * max_threads;
6626                   stop = (base + 1) * max_threads;
6627                 }
6628               }
6629               for(ste = start; ste < stop; ste++) {
6630                 (*it)->invalidate(cRepAll, cRepInvPurge, ste);
6631               }
6632             }
6633           }
6634         }
6635       }
6636     }
6637     I->LastStateBuilt = cur_state;
6638     WizardDoScene(G);
6639     if(!MovieDefined(G)) {
6640       if(SettingGetGlobal_i(G, cSetting_frame) != (cur_state + 1))
6641         SettingSetGlobal_i(G, cSetting_frame, (cur_state + 1));
6642     }
6643   }
6644 
6645   PRINTFD(G, FB_Scene)
6646     " %s: leaving...\n", __func__ ENDFD;
6647 }
6648 
6649 
6650 /*========================================================================*/
SceneRenderCached(PyMOLGlobals * G)6651 int SceneRenderCached(PyMOLGlobals * G)
6652 {
6653   /* sets up a cached image buffer is one is available, or if we are
6654    * using cached images by default */
6655   CScene *I = G->Scene;
6656   std::shared_ptr<pymol::Image> image;
6657   int renderedFlag = false;
6658   int draw_mode = SettingGetGlobal_i(G, cSetting_draw_mode);
6659   PRINTFD(G, FB_Scene)
6660     " %s: entered.\n", __func__ ENDFD;
6661 
6662   G->ShaderMgr->Check_Reload();
6663   if(I->DirtyFlag) {
6664     int moviePlaying = MoviePlaying(G);
6665 
6666     if(I->MovieFrameFlag || (moviePlaying && SettingGetGlobal_b(G, cSetting_cache_frames))) {
6667       I->MovieFrameFlag = false;
6668       image = MovieGetImage(G,
6669                             MovieFrameToImage(G,
6670                                               SettingGetGlobal_i(G, cSetting_frame) - 1));
6671       if(image) {
6672         if(I->Image){
6673           ScenePurgeImage(G);
6674         }
6675         I->CopyType = true;
6676         I->Image = image;
6677         OrthoDirty(G);
6678         renderedFlag = true;
6679       } else {
6680         SceneMakeMovieImage(G, true, false, cSceneImage_Default);
6681         renderedFlag = true;
6682       }
6683     } else if(draw_mode == 3) {
6684       auto show_progress = SettingGet<int>(G, cSetting_show_progress);
6685       SettingSetGlobal_i(G, cSetting_show_progress, 0);
6686       SceneRay(G, 0, 0, SettingGetGlobal_i(G, cSetting_ray_default_renderer),
6687                NULL, NULL, 0.0F, 0.0F, false, NULL, false, -1);
6688       SettingSetGlobal_i(G,cSetting_show_progress, show_progress);
6689     } else if(moviePlaying && SettingGetGlobal_b(G, cSetting_ray_trace_frames)) {
6690       SceneRay(G, 0, 0, SettingGetGlobal_i(G, cSetting_ray_default_renderer),
6691                NULL, NULL, 0.0F, 0.0F, false, NULL, true, -1);
6692     } else if((moviePlaying && SettingGetGlobal_b(G, cSetting_draw_frames)) || (draw_mode == 2)) {
6693       SceneMakeSizedImage(G, 0, 0, SettingGetGlobal_i(G, cSetting_antialias));
6694     } else if(I->CopyType == true) {    /* true vs. 2 */
6695       renderedFlag = true;
6696     } else {
6697       renderedFlag = false;
6698     }
6699   } else if(I->CopyType == true) {      /* true vs. 2 */
6700     renderedFlag = true;
6701   }
6702 
6703   PRINTFD(G, FB_Scene)
6704     " %s: leaving...renderedFlag %d\n", __func__, renderedFlag ENDFD;
6705 
6706   return (renderedFlag);
6707 }
6708 
SceneGetSpecularValue(PyMOLGlobals * G,float spec,int limit)6709 float SceneGetSpecularValue(PyMOLGlobals * G, float spec, int limit)
6710 {
6711   int n_light = SettingGetGlobal_i(G, cSetting_spec_count);
6712   if(n_light < 0)
6713     n_light = SettingGetGlobal_i(G, cSetting_light_count);
6714   if(n_light > limit)
6715     n_light = limit;
6716   if(n_light > 2) {
6717     spec = spec / pow(n_light - 1, 0.6F);
6718   }
6719   return (spec > 1.F) ? 1.F : (spec < 0.F) ? 0.F : spec;
6720 }
6721 
SceneGetReflectScaleValue(PyMOLGlobals * G,int limit)6722 float SceneGetReflectScaleValue(PyMOLGlobals * G, int limit)
6723 {
6724   float result = 1.0F;
6725   int n_light = SettingGetGlobal_i(G, cSetting_light_count);
6726   if(n_light > limit)
6727     n_light = limit;
6728   if(n_light > 1) {
6729     float tmp[3];
6730     float sum = 0.0F;
6731     for (int i = 0; i < n_light - 1; ++i) {
6732       copy3f(SettingGetGlobal_3fv(G, light_setting_indices[i]), tmp);
6733       normalize3f(tmp);
6734       sum += 1.f - tmp[2];
6735     }
6736     sum *= 0.5;
6737     return result / sum;
6738   }
6739   return result;
6740 }
6741 
SceneGetModel2WorldMatrix(PyMOLGlobals * G,float * matrix)6742 void SceneGetModel2WorldMatrix(PyMOLGlobals * G, float *matrix) {
6743   CScene *I = G->Scene;
6744   if (!I)
6745     return;
6746 
6747   identity44f(matrix);
6748   MatrixTranslateC44f(matrix, I->m_view.m_pos[0], I->m_view.m_pos[1], I->m_view.m_pos[2]);
6749   MatrixMultiplyC44f(I->m_view.m_rotMatrix, matrix);
6750   MatrixTranslateC44f(matrix, -I->m_view.m_origin[0], -I->m_view.m_origin[1], -I->m_view.m_origin[2]);
6751 }
6752 
SceneSetModel2WorldMatrix(PyMOLGlobals * G,float const * matrix)6753 void SceneSetModel2WorldMatrix(PyMOLGlobals * G, float const *matrix) {
6754   CScene *I = G->Scene;
6755   if (!I)
6756     return;
6757 
6758   // build inverse origin translate
6759   float invOriginTranslate[16];
6760   identity44f(invOriginTranslate);
6761   MatrixTranslateC44f(invOriginTranslate, I->m_view.m_origin[0], I->m_view.m_origin[1], I->m_view.m_origin[2]);
6762   // get shiftRot from m2wNew
6763   float temp[16];
6764   memcpy(temp, matrix, sizeof(temp));
6765   MatrixMultiplyC44f(invOriginTranslate, temp);
6766   // decompose shiftRot
6767   memcpy(I->m_view.m_rotMatrix, temp, sizeof(I->m_view.m_rotMatrix));
6768   I->m_view.m_rotMatrix[12] = I->m_view.m_rotMatrix[13] = I->m_view.m_rotMatrix[14] = 0.0f;
6769   I->m_view.m_pos[0] = temp[12];
6770   I->m_view.m_pos[1] = temp[13];
6771   I->m_view.m_pos[2] = temp[14];
6772 }
6773 
6774 /**
6775  * Get specular and shininess, adjusted to the number of lights.
6776  *
6777  * @param[out] ptr_spec              specular for lights 2-N
6778  * @param[out] ptr_spec_power        shininess for lights 2-N
6779  * @param[out] ptr_spec_direct       specular for light 1
6780  * @param[out] ptr_spec_direct_power shininess for light 1
6781  * @param limit number of lights (e.g. `light_count` setting)
6782  */
SceneGetAdjustedLightValues(PyMOLGlobals * G,float * ptr_spec,float * ptr_spec_power,float * ptr_spec_direct,float * ptr_spec_direct_power,int limit)6783 void SceneGetAdjustedLightValues(PyMOLGlobals * G,
6784     float *ptr_spec,
6785     float *ptr_spec_power,
6786     float *ptr_spec_direct,
6787     float *ptr_spec_direct_power,
6788     int limit)
6789 {
6790   float specular = SettingGetGlobal_f(G, cSetting_specular);
6791   if (specular == 1.0F)
6792     specular = SettingGetGlobal_f(G, cSetting_specular_intensity);
6793   if (specular < R_SMALL4)
6794     specular = 0.0F;
6795 
6796   float spec_power = SettingGetGlobal_f(G, cSetting_spec_power);
6797   if (spec_power < 0.0F)
6798     spec_power = SettingGetGlobal_f(G, cSetting_shininess);
6799 
6800   float spec_reflect = SettingGetGlobal_f(G, cSetting_spec_reflect);
6801   if (spec_reflect < 0.0F)
6802     spec_reflect = specular;
6803 
6804   float spec_direct = SettingGetGlobal_f(G, cSetting_spec_direct);
6805   if (spec_direct < 0.0F)
6806     spec_direct = specular;
6807 
6808   float spec_direct_power = SettingGetGlobal_f(G, cSetting_spec_direct_power);
6809   if (spec_direct_power < 0.0F)
6810     spec_direct_power = spec_power;
6811 
6812   *ptr_spec = SceneGetSpecularValue(G, spec_reflect, limit);
6813   *ptr_spec_power = spec_power;
6814   *ptr_spec_direct = spec_direct > 1.F ? 1.F : spec_direct;
6815   *ptr_spec_direct_power = spec_direct_power;
6816 }
6817 
6818 /*
6819  * Shader attribute names
6820  */
6821 #define TEMPLATE(i) "g_LightSource[" #i "].position"
6822 const char * lightsource_position_names[] = {
6823   TEMPLATE(0), TEMPLATE(1), TEMPLATE(2), TEMPLATE(3), TEMPLATE(4),
6824   TEMPLATE(5), TEMPLATE(6), TEMPLATE(7), TEMPLATE(8), TEMPLATE(9)
6825 };
6826 #undef TEMPLATE
6827 
6828 #define TEMPLATE(i) "g_LightSource[" #i "].diffuse"
6829 const char * lightsource_diffuse_names[] = {
6830   TEMPLATE(0), TEMPLATE(1), TEMPLATE(2), TEMPLATE(3), TEMPLATE(4),
6831   TEMPLATE(5), TEMPLATE(6), TEMPLATE(7), TEMPLATE(8), TEMPLATE(9)
6832 };
6833 #undef TEMPLATE
6834 
6835 /**
6836  * Sets up lighting for immediate mode if shaderPrg=NULL, otherwise
6837  * sets lighting uniforms for the given shader program.
6838  *
6839  * Supports up to light_count=8
6840  */
SceneProgramLighting(PyMOLGlobals * G,CShaderPrg * shaderPrg)6841 void SceneProgramLighting(PyMOLGlobals * G, CShaderPrg * shaderPrg)
6842 {
6843 
6844   /* load up the light positions relative to the camera while
6845      MODELVIEW still has the identity */
6846   int n_light = glm::clamp(SettingGetGlobal_i(G, cSetting_light_count), 0, 8);
6847   int spec_count = SettingGetGlobal_i(G, cSetting_spec_count);
6848   float direct = SettingGetGlobal_f(G, cSetting_direct);
6849   float reflect = SettingGetGlobal_f(G, cSetting_reflect) * SceneGetReflectScaleValue(G, n_light);
6850   float spec[4];
6851   float diff[4];
6852   const float zero[4] = { 0.0F, 0.0F, 0.0F, 1.0F };
6853   float vv[4] = {0.F, 0.F, 1.F, 0.F}; // position
6854   float spec_value, shine, spec_direct, spec_direct_power;
6855 
6856   SceneGetAdjustedLightValues(G,
6857       &spec_value,
6858       &shine,
6859       &spec_direct,
6860       &spec_direct_power,
6861       n_light);
6862 
6863   if (n_light < 2) {
6864     direct += reflect;
6865     if(direct > 1.0F)
6866       direct = 1.0F;
6867   }
6868 
6869   if(spec_count < 0) {
6870     spec_count = n_light;
6871   }
6872 
6873   // light 0
6874 
6875   white4f(diff, SettingGetGlobal_f(G, cSetting_ambient));
6876 
6877 #ifndef PURE_OPENGL_ES_2
6878   if (!shaderPrg) {
6879     glEnable(GL_LIGHTING);
6880     glLightModelfv(GL_LIGHT_MODEL_AMBIENT, diff);
6881     glLightfv(GL_LIGHT0, GL_POSITION, vv);
6882     glLightfv(GL_LIGHT0, GL_AMBIENT, zero);
6883     if(direct > R_SMALL4) {
6884       white4f(diff, direct);
6885       white4f(spec, spec_direct);
6886       glEnable(GL_LIGHT0);
6887       glLightfv(GL_LIGHT0, GL_DIFFUSE, diff);
6888       glLightfv(GL_LIGHT0, GL_SPECULAR, spec);
6889     } else {
6890       glLightfv(GL_LIGHT0, GL_DIFFUSE, zero);
6891       glLightfv(GL_LIGHT0, GL_SPECULAR, zero);
6892     }
6893   } else
6894 #endif
6895   {
6896     shaderPrg->Set4fv("g_LightModel.ambient", diff);
6897     white4f(diff, (direct > R_SMALL4) ? direct : 0.f);
6898     shaderPrg->Set4fv(lightsource_diffuse_names[0], diff);
6899     shaderPrg->Set4fv(lightsource_position_names[0], vv);
6900   }
6901 
6902   // light 1-N
6903 
6904   white4f(spec, spec_value);
6905   white4f(diff, reflect);
6906 
6907   for (int i = 1; i < n_light; ++i) {
6908     // normalized/inverted light direction
6909     copy3f(SettingGetGlobal_3fv(G, light_setting_indices[i - 1]), vv);
6910     normalize3f(vv);
6911     invert3f(vv);
6912 
6913 #ifndef PURE_OPENGL_ES_2
6914     if (!shaderPrg) {
6915       glEnable(GL_LIGHT0 + i);
6916       glLightfv(GL_LIGHT0 + i, GL_POSITION, vv);
6917       glLightfv(GL_LIGHT0 + i, GL_SPECULAR, (spec_count >= i) ? spec : zero);
6918       glLightfv(GL_LIGHT0 + i, GL_AMBIENT, zero);
6919       glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, diff);
6920     } else
6921 #endif
6922     {
6923       shaderPrg->Set4fv(lightsource_position_names[i], vv);
6924       shaderPrg->Set4fv(lightsource_diffuse_names[i], diff);
6925     }
6926   }
6927 
6928 #ifndef PURE_OPENGL_ES_2
6929   if (!shaderPrg) {
6930     // TODO: this was depending on two_sided_lighting, surface_cavity_mode
6931     // and transparency_mode
6932     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
6933 
6934     // disable unused lights
6935     for (int i = 7; i >= n_light; --i) {
6936       glDisable(GL_LIGHT0 + i);
6937     }
6938 
6939     // material
6940     white4f(spec, 1.F);
6941     glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
6942     glMaterialf(GL_FRONT, GL_SHININESS, glm::clamp(shine, 0.F, 128.F));
6943   }
6944 #endif
6945 }
6946 
6947 /**
6948  * Set up the Scene Fog* member variables and immediate mode fog (incl.
6949  * gl_Fog struct for non-ES2 shaders)
6950  */
SceneSetFog(PyMOLGlobals * G)6951 int SceneSetFog(PyMOLGlobals *G){
6952   CScene *I = G->Scene;
6953   int fog_active = false;
6954   float fog_density = SettingGetGlobal_f(G, cSetting_fog);
6955   I->FogStart = (I->m_view.m_clipSafe.m_back - I->m_view.m_clipSafe.m_front) * SettingGetGlobal_f(G, cSetting_fog_start) + I->m_view.m_clipSafe.m_front;
6956   if((fog_density > R_SMALL8) && (fog_density != 1.0F)) {
6957     I->FogEnd = I->FogStart + (I->m_view.m_clipSafe.m_back - I->FogStart) / fog_density;
6958   } else {
6959     I->FogEnd = I->m_view.m_clipSafe.m_back;
6960   }
6961 
6962   if(SettingGetGlobal_b(G, cSetting_depth_cue) && fog_density != 0.0F) {
6963     fog_active = true;
6964   }
6965 
6966 #ifndef PURE_OPENGL_ES_2
6967   if (ALWAYS_IMMEDIATE_OR(!SettingGetGlobal_b(G, cSetting_use_shaders))) {
6968   const float *bg_rgb = ColorGet(G, SettingGetGlobal_color(G, cSetting_bg_rgb));
6969   float fog[4] = {bg_rgb[0], bg_rgb[1], bg_rgb[2], 1.0};
6970 
6971   glFogf(GL_FOG_MODE, GL_LINEAR);
6972   glFogf(GL_FOG_START, I->FogStart);
6973   glFogf(GL_FOG_END, I->FogEnd);
6974   glFogf(GL_FOG_DENSITY, fog_density > R_SMALL8 ? fog_density : 1.0F);
6975   glFogfv(GL_FOG_COLOR, fog);
6976   if (fog_active)
6977     glEnable(GL_FOG);
6978   else
6979     glDisable(GL_FOG);
6980   }
6981 #endif
6982 
6983   return fog_active;
6984 }
6985 
6986 /**
6987  * Set the g_Fog_* uniforms for ES2 shaders
6988  */
SceneSetFogUniforms(PyMOLGlobals * G,CShaderPrg * shaderPrg)6989 void SceneSetFogUniforms(PyMOLGlobals * G, CShaderPrg * shaderPrg) {
6990   CScene *I = G->Scene;
6991   if (shaderPrg) {
6992     float fogScale = 1.0f / (I->FogEnd - I->FogStart);
6993     shaderPrg->Set1f("g_Fog_end", I->FogEnd);
6994     shaderPrg->Set1f("g_Fog_scale", fogScale);
6995   }
6996 }
6997 
SceneSetupGLPicking(PyMOLGlobals * G)6998 void SceneSetupGLPicking(PyMOLGlobals * G){
6999       /* picking mode: we want flat, unshaded, unblended, unsmooth colors */
7000 
7001       glDisable(GL_FOG);
7002       glDisable(GL_COLOR_MATERIAL);
7003       glDisable(GL_LIGHTING);
7004       glDisable(GL_LINE_SMOOTH);
7005       glDisable(GL_DITHER);
7006       glDisable(GL_BLEND);
7007       glDisable(GL_POLYGON_SMOOTH);
7008       if(G->Option->multisample)
7009         glDisable(0x809D);      /* GL_MULTISAMPLE_ARB */
7010       glShadeModel(GL_FLAT);
7011 }
7012 
7013 /*========================================================================*/
SceneRestartFrameTimer(PyMOLGlobals * G)7014 void SceneRestartFrameTimer(PyMOLGlobals * G)
7015 {
7016   CScene *I = G->Scene;
7017   I->LastFrameTime = UtilGetSeconds(G);
7018 }
7019 
SceneRestartPerfTimer(PyMOLGlobals * G)7020 static void SceneRestartPerfTimer(PyMOLGlobals * G)
7021 {
7022   CScene *I = G->Scene;
7023   I->LastRender = UtilGetSeconds(G);
7024   I->RenderTime = 0.0;
7025 }
7026 
SceneRestartSweepTimer(PyMOLGlobals * G)7027 void SceneRestartSweepTimer(PyMOLGlobals * G)
7028 {
7029   CScene *I = G->Scene;
7030   I->LastSweep = 0.0F;          /* continue to defer rocking until this is done */
7031   I->LastSweepX = 0.0F;
7032   I->LastSweepY = 0.0F;
7033   I->SweepTime = 0.0;
7034   I->LastSweepTime = UtilGetSeconds(G);
7035   SceneRestartPerfTimer(G);
7036 
7037 }
7038 
7039 
7040 /*========================================================================*/
ScenePrepareMatrix(PyMOLGlobals * G,int mode,int stereo_mode)7041 void ScenePrepareMatrix(PyMOLGlobals * G, int mode, int stereo_mode /* = 0 */)
7042 {
7043   CScene *I = G->Scene;
7044 
7045   float stAng, stShift;
7046 
7047 #ifdef _PYMOL_OPENVR
7048   bool isOpenVR = (stereo_mode == cStereo_openvr) && OpenVRReady(G);
7049 
7050   if(isOpenVR) {
7051 
7052     /* stereo OpenVR */
7053 
7054     if (!mode) {
7055       // average projection matrix for picking
7056       glMatrixMode(GL_PROJECTION);
7057       OpenVRLoadPickingProjectionMatrix(G, I->m_view.m_clipSafe.m_front, I->m_view.m_clipSafe.m_back);
7058 
7059       // mono matrix for picking
7060       glMatrixMode(GL_MODELVIEW);
7061       glLoadMatrixf(OpenVRGetPickingMatrix(G));
7062 
7063     } else {
7064       glMatrixMode(GL_PROJECTION);
7065       OpenVRLoadProjectionMatrix(G, I->m_view.m_clipSafe.m_front, I->m_view.m_clipSafe.m_back);
7066 
7067       glMatrixMode(GL_MODELVIEW);
7068       OpenVRLoadWorld2EyeMatrix(G);
7069 
7070     }
7071 
7072     if (OpenVRIsMoleculeCaptured(G)) {
7073       float scaler;
7074       float const *mol2world = OpenVRGetMolecule2WorldMatrix(G, &scaler);
7075       // save old plane shifts
7076       float dist = fabsf(I->m_view.m_pos[2]);
7077       float frontShift = fabsf(dist - I->m_view.m_clip.m_front);
7078       float backShift = fabsf(dist - I->m_view.m_clip.m_back);
7079       // apply new transform to molecule
7080       SceneSetModel2WorldMatrix(G, mol2world);
7081       SceneScale(G, scaler);
7082       // renew front and back planes
7083       dist = fabsf(I->m_view.m_pos[2]);
7084       SceneClipSet(G, dist - frontShift * scaler, dist + backShift * scaler);
7085     }
7086 
7087     /* move the camera to the location we are looking at */
7088     glTranslatef(I->m_view.m_pos[0], I->m_view.m_pos[1], I->m_view.m_pos[2]);
7089 
7090     /* scale molecule */
7091     glScalef(I->Scale, I->Scale, I->Scale);
7092 
7093     /* rotate about the origin (the the center of rotation) */
7094     glMultMatrixf(I->m_view.m_rotMatrix);
7095 
7096     /* move the origin to the center of rotation */
7097     glTranslatef(-I->m_view.m_origin[0], -I->m_view.m_origin[1], -I->m_view.m_origin[2]);
7098 
7099     // TODO don't do the immediate mode detour
7100     glGetFloatv(GL_PROJECTION_MATRIX, I->ProjectionMatrix);
7101     glGetFloatv(GL_MODELVIEW_MATRIX, I->ModelViewMatrix);
7102 
7103   } else
7104 #endif
7105   {
7106     if (!mode){
7107       SceneComposeModelViewMatrix(I, I->ModelViewMatrix);
7108     } else {
7109       /* stereo */
7110       float tmpMatrix[16];
7111       stAng = SettingGetGlobal_f(G, cSetting_stereo_angle);// * cPI / 180.f;
7112       stShift = SettingGetGlobal_f(G, cSetting_stereo_shift);
7113 
7114       stShift = (float) (stShift * fabs(I->m_view.m_pos[2]) / 100.0);
7115       stAng = (float) (-stAng * atan(stShift / fabs(I->m_view.m_pos[2])) / 2.f);
7116 
7117       if(mode == 2) {             /* left hand */
7118 	stAng = -stAng;
7119 	stShift = -stShift;
7120       }
7121 
7122       PRINTFD(G, FB_Scene)
7123 	" StereoMatrix-Debug: mode %d stAng %8.3f stShift %8.3f \n", mode, stAng, stShift
7124 	ENDFD;
7125       identity44f(tmpMatrix);
7126       identity44f(I->ModelViewMatrix);
7127       MatrixRotateC44f(I->ModelViewMatrix, stAng, 0.f, 1.f, 0.f);
7128       MatrixTranslateC44f(tmpMatrix, I->m_view.m_pos[0] + stShift, I->m_view.m_pos[1], I->m_view.m_pos[2]);
7129       MatrixMultiplyC44f(tmpMatrix, I->ModelViewMatrix);
7130       MatrixMultiplyC44f(I->m_view.m_rotMatrix, I->ModelViewMatrix);
7131       MatrixTranslateC44f(I->ModelViewMatrix, -I->m_view.m_origin[0], -I->m_view.m_origin[1], -I->m_view.m_origin[2]);
7132 
7133     }
7134   }
7135 
7136 #ifndef PURE_OPENGL_ES_2
7137   if (ALWAYS_IMMEDIATE_OR(!SettingGetGlobal_b(G, cSetting_use_shaders))) {
7138     glLoadMatrixf(I->ModelViewMatrix);
7139   }
7140 #endif
7141 }
7142 
7143 
7144 /*========================================================================*/
SceneRotateWithDirty(PyMOLGlobals * G,float angle,float x,float y,float z,int dirty)7145 static void SceneRotateWithDirty(PyMOLGlobals * G, float angle, float x, float y, float z,
7146                                  int dirty)
7147 {
7148   CScene *I = G->Scene;
7149   float temp[16];
7150   int a;
7151   angle = (float) (-PI * angle / 180.0);
7152   identity44f(temp);
7153   MatrixRotateC44f(temp, angle, x, y, z);
7154   MatrixMultiplyC44f(I->m_view.m_rotMatrix, temp);
7155   for(a = 0; a < 16; a++)
7156     I->m_view.m_rotMatrix[a] = temp[a];
7157   SceneUpdateInvMatrix(G);
7158   if(dirty) {
7159     SceneInvalidate(G);
7160   } else {
7161     SceneInvalidateCopy(G, false);
7162   }
7163   PyMOL_NeedRedisplay(G->PyMOL);
7164   /*  glPushMatrix();
7165      glLoadIdentity();
7166      glRotatef(angle,x,y,z);
7167      glMultMatrixf(I->m_view.m_rotMatrix);
7168      glGetFloatv(GL_MODELVIEW_MATRIX,I->m_view.m_rotMatrix);
7169      glPopMatrix(); */
7170 }
7171 
SceneRotate(PyMOLGlobals * G,float angle,float x,float y,float z)7172 void SceneRotate(PyMOLGlobals * G, float angle, float x, float y, float z)
7173 {
7174   SceneRotateWithDirty(G, angle, x, y, z, true);
7175 }
7176 
7177 
7178 /*========================================================================*/
SceneApplyMatrix(PyMOLGlobals * G,float * m)7179 void SceneApplyMatrix(PyMOLGlobals * G, float *m)
7180 {
7181   CScene *I = G->Scene;
7182   MatrixMultiplyC44f(m, I->m_view.m_rotMatrix);
7183   SceneDirty(G);
7184 
7185   /*  glPushMatrix();
7186      glLoadIdentity();
7187      glMultMatrixf(m);
7188      glMultMatrixf(I->m_view.m_rotMatrix);
7189      glGetFloatv(GL_MODELVIEW_MATRIX,I->m_view.m_rotMatrix);
7190      glPopMatrix(); */
7191 }
7192 
7193 
7194 /*========================================================================*/
SceneScale(PyMOLGlobals * G,float scale)7195 void SceneScale(PyMOLGlobals * G, float scale)
7196 {
7197   CScene *I = G->Scene;
7198   I->Scale *= scale;
7199   SceneInvalidate(G);
7200 }
7201 
SceneZoom(PyMOLGlobals * G,float scale)7202 void SceneZoom(PyMOLGlobals * G, float scale){
7203   CScene *I = G->Scene;
7204   float factor = -((I->m_view.m_clipSafe.m_front + I->m_view.m_clipSafe.m_back) / 2) * 0.1 * scale;
7205   /*    SettingGetGlobal_f(G, cSetting_mouse_wheel_scale); */
7206   I->m_view.m_pos[2] += factor;
7207   I->m_view.m_clip.m_front -= factor;
7208   I->m_view.m_clip.m_back -= factor;
7209   UpdateFrontBackSafe(I);
7210   SceneInvalidate(G);
7211 }
7212 
SceneGetTwoSidedLighting(PyMOLGlobals * G)7213 int SceneGetTwoSidedLighting(PyMOLGlobals * G){
7214   return SceneGetTwoSidedLightingSettings(G, NULL, NULL);
7215 }
7216 
SceneGetTwoSidedLightingSettings(PyMOLGlobals * G,const CSetting * set1,const CSetting * set2)7217 int SceneGetTwoSidedLightingSettings(PyMOLGlobals * G,
7218     const CSetting *set1,
7219     const CSetting *set2) {
7220   int two_sided_lighting = SettingGet_b(G, set1, set2, cSetting_two_sided_lighting);
7221   if(two_sided_lighting<0) {
7222     if(SettingGet_i(G, set1, set2, cSetting_surface_cavity_mode))
7223       two_sided_lighting = true;
7224     else
7225       two_sided_lighting = false;
7226   }
7227   two_sided_lighting = two_sided_lighting || (SettingGet_i(G, set1, set2, cSetting_transparency_mode) ==1);
7228   return two_sided_lighting;
7229 }
7230 
SceneGetDynamicLineWidth(RenderInfo * info,float line_width)7231 float SceneGetDynamicLineWidth(RenderInfo * info, float line_width)
7232 {
7233   if(info && info->dynamic_width) {
7234     float factor;
7235     if(info->vertex_scale > R_SMALL4) {
7236       factor = info->dynamic_width_factor / info->vertex_scale;
7237       if(factor > info->dynamic_width_max)
7238         factor = info->dynamic_width_max;
7239       if(factor < info->dynamic_width_min) {
7240         factor = info->dynamic_width_min;
7241       }
7242     } else {
7243       factor = info->dynamic_width_max;
7244     }
7245     return factor * line_width;
7246   }
7247   return line_width;
7248 }
7249 
SceneGetLineWidthForCylinders(PyMOLGlobals * G,RenderInfo * info,float line_width_arg)7250 float SceneGetLineWidthForCylinders(PyMOLGlobals * G, RenderInfo * info, float line_width_arg){
7251   float line_width = SceneGetDynamicLineWidth(info, line_width_arg);
7252 
7253   float pixel_scale_value = SettingGetGlobal_f(G, cSetting_ray_pixel_scale);
7254 
7255   if(pixel_scale_value < 0)
7256     pixel_scale_value = 1.0F;
7257   /* the radius of the cylinders is the vertex_scale * ray_pixel_scale */
7258   /* this turns out to be exactly right, but changes if the scene or user
7259      moves */
7260   return info->vertex_scale * pixel_scale_value * line_width / 2.f;
7261 }
7262 
7263 // line width arg has been processed as dynamic already
SceneGetLineWidthForCylindersStatic(PyMOLGlobals * G,RenderInfo * info,float dynamic_line_width_arg,float line_width_arg)7264 float SceneGetLineWidthForCylindersStatic(PyMOLGlobals * G, RenderInfo * info, float dynamic_line_width_arg, float line_width_arg){
7265   float pixel_scale_value = SettingGetGlobal_f(G, cSetting_ray_pixel_scale);
7266   if(pixel_scale_value < 0)
7267     pixel_scale_value = 1.0F;
7268   /* the radius of the cylinders is the vertex_scale * ray_pixel_scale */
7269   /* this turns out to be exactly right, but changes if the scene or user
7270      moves */
7271   if (SceneGetStereo(G) == cStereo_openvr)
7272     // note: that is reversion of magic dynamic line modifications in PYMOL
7273     return pixel_scale_value * 0.07f * line_width_arg / 2.0f;
7274 
7275   return info->vertex_scale * pixel_scale_value * dynamic_line_width_arg / 2.f;
7276 }
7277 
ScenePushModelViewMatrix(PyMOLGlobals * G)7278 void ScenePushModelViewMatrix(PyMOLGlobals * G) {
7279   CScene *I = G->Scene;
7280   auto& stack = I->m_ModelViewMatrixStack;
7281   auto& depth = I->m_ModelViewMatrixStackDepth;
7282 
7283   stack.resize(16 * (depth + 1));
7284   copy44f(I->ModelViewMatrix, &stack[16 * depth++]);
7285 }
7286 
ScenePopModelViewMatrix(PyMOLGlobals * G,bool immediate)7287 void ScenePopModelViewMatrix(PyMOLGlobals * G, bool immediate) {
7288   CScene *I = G->Scene;
7289   auto& stack = I->m_ModelViewMatrixStack;
7290   auto& depth = I->m_ModelViewMatrixStackDepth;
7291 
7292   if (depth == 0) {
7293     printf("ERROR: depth == 0\n");
7294     return;
7295   }
7296 
7297   copy44f(&stack[--depth * 16], I->ModelViewMatrix);
7298 
7299 #ifndef PURE_OPENGL_ES_2
7300   if (ALWAYS_IMMEDIATE_OR(immediate)) {
7301     glMatrixMode(GL_MODELVIEW);
7302     glLoadMatrixf(I->ModelViewMatrix);
7303   }
7304 #endif
7305 }
7306 
SceneGetModelViewMatrix(PyMOLGlobals * G)7307 float *SceneGetModelViewMatrix(PyMOLGlobals * G){
7308   CScene *I = G->Scene;
7309   return (I->ModelViewMatrix);
7310 }
SceneGetProjectionMatrix(PyMOLGlobals * G)7311 float *SceneGetProjectionMatrix(PyMOLGlobals * G){
7312   CScene *I = G->Scene;
7313   return (I->ProjectionMatrix);
7314 }
SceneSetBackgroundColorAlreadySet(PyMOLGlobals * G,int background_color_already_set)7315 void SceneSetBackgroundColorAlreadySet(PyMOLGlobals * G, int background_color_already_set){
7316   CScene *I = G->Scene;
7317   I->background_color_already_set = background_color_already_set;
7318 }
SceneGetBackgroundColorAlreadySet(PyMOLGlobals * G)7319 int SceneGetBackgroundColorAlreadySet(PyMOLGlobals * G){
7320   CScene *I = G->Scene;
7321   return (I->background_color_already_set);
7322 }
SceneSetDoNotClearBackground(PyMOLGlobals * G,int do_not_clear)7323 void SceneSetDoNotClearBackground(PyMOLGlobals * G, int do_not_clear){
7324   CScene *I = G->Scene;
7325   I->do_not_clear = do_not_clear;
7326 }
7327 
SceneGetDoNotClearBackground(PyMOLGlobals * G)7328 int SceneGetDoNotClearBackground(PyMOLGlobals * G){
7329   CScene *I = G->Scene;
7330   return (I->do_not_clear);
7331 }
7332 
7333 
7334 #ifdef _PYMOL_IOS
SceneTranslateSceneXYWithScale(PyMOLGlobals * G,float x,float y)7335 void SceneTranslateSceneXYWithScale(PyMOLGlobals * G, float x, float y){
7336   CScene *I = G->Scene;
7337   int moved_flag;
7338   float v2[3], vScale;
7339   float old_front, old_back, old_origin;
7340 
7341   old_front = I->m_view.m_clip.m_front;
7342   old_back = I->m_view.m_clip.m_back;
7343   old_origin = -I->m_view.m_pos[2];
7344 
7345   vScale = SceneGetExactScreenVertexScale(G, I->m_view.m_origin);
7346   /*  if(stereo_via_adjacent_array(I->StereoMode)) {
7347     x = get_stereo_x(x, &I->LastX, I->Width, NULL);
7348     }*/
7349 
7350   v2[0] = x * vScale;
7351   v2[1] = y * vScale;
7352   v2[2] = 0.0F;
7353 
7354   moved_flag = false;
7355   if(x != 0.f) {
7356     I->m_view.m_pos[0] += v2[0];
7357     I->LastX = x;
7358     SceneInvalidate(G);
7359     moved_flag = true;
7360   }
7361   if(y != 0.f) {
7362     I->m_view.m_pos[1] += v2[1];
7363     I->LastY = y;
7364     SceneInvalidate(G);
7365     moved_flag = true;
7366   }
7367   EditorFavorOrigin(G, NULL);
7368   if(moved_flag && SettingGetGlobal_b(G, cSetting_roving_origin)) {
7369     SceneGetCenter(G, v2);     /* gets position of center of screen */
7370     SceneOriginSet(G, v2, true);
7371   }
7372   if(moved_flag && SettingGetGlobal_b(G, cSetting_roving_detail)) {
7373     SceneRovingDirty(G);
7374   }
7375   if(moved_flag)
7376     SceneDoRoving(G, old_front, old_back, old_origin, 0 /* adjust flag */, false);
7377   PyMOL_NeedRedisplay(G->PyMOL);
7378 }
SceneIsTwisting(PyMOLGlobals * G)7379 int SceneIsTwisting(PyMOLGlobals * G){
7380   CScene *I = G->Scene;
7381   return (!I->prev_no_z_rotation1 || !I->prev_no_z_rotation2);
7382 }
7383 
7384 #endif
7385 
SceneGLClear(PyMOLGlobals * G,GLbitfield mask)7386 void SceneGLClear(PyMOLGlobals * G, GLbitfield mask){
7387   glClear(mask);
7388 }
7389 
SceneIncrementTextureRefreshes(PyMOLGlobals * G)7390 int SceneIncrementTextureRefreshes(PyMOLGlobals * G){
7391   CScene *I = G->Scene;
7392   return ++(I->n_texture_refreshes);
7393 }
7394 
SceneResetTextureRefreshes(PyMOLGlobals * G)7395 void SceneResetTextureRefreshes(PyMOLGlobals * G){
7396   CScene *I = G->Scene;
7397   I->n_texture_refreshes = 0;
7398 }
7399 
SceneGetScaledAxesAtPoint(PyMOLGlobals * G,float * pt,float * xn,float * yn)7400 void SceneGetScaledAxesAtPoint(PyMOLGlobals * G, float *pt, float *xn, float *yn)
7401 {
7402   CScene *I = G->Scene;
7403   float xn0[3] = { 1.0F, 0.0F, 0.0F };
7404   float yn0[3] = { 0.0F, 1.0F, 0.0F };
7405   float v_scale;
7406 
7407   v_scale = SceneGetScreenVertexScale(G, pt);
7408 
7409   MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, xn0, xn0);
7410   MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, yn0, yn0);
7411   scale3f(xn0, v_scale, xn);
7412   scale3f(yn0, v_scale, yn);
7413 }
7414 
SceneGetScaledAxes(PyMOLGlobals * G,CObject * obj,float * xn,float * yn)7415 void SceneGetScaledAxes(PyMOLGlobals * G, CObject *obj, float *xn, float *yn)
7416 {
7417   CScene *I = G->Scene;
7418   float *v;
7419   float vt[3];
7420   float xn0[3] = { 1.0F, 0.0F, 0.0F };
7421   float yn0[3] = { 0.0F, 1.0F, 0.0F };
7422   float v_scale;
7423 
7424   v = TextGetPos(G);
7425 
7426   if(obj->TTTFlag) {
7427     transformTTT44f3f(obj->TTT, v, vt);
7428   } else {
7429     copy3f(v, vt);
7430   }
7431 
7432   v_scale = SceneGetScreenVertexScale(G, vt);
7433 
7434   MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, xn0, xn0);
7435   MatrixInvTransformC44fAs33f3f(I->m_view.m_rotMatrix, yn0, yn0);
7436   scale3f(xn0, v_scale, xn);
7437   scale3f(yn0, v_scale, yn);
7438 }
7439 
SceneGetCopyType(PyMOLGlobals * G)7440 int SceneGetCopyType(PyMOLGlobals * G) {
7441   return G->Scene->CopyType;
7442 }
7443 
SceneGenerateMatrixToAnotherZFromZ(PyMOLGlobals * G,float * convMatrix,float * curpt,float * pt)7444 void SceneGenerateMatrixToAnotherZFromZ(PyMOLGlobals *G, float *convMatrix, float *curpt, float *pt){
7445   CScene *I = G->Scene;
7446   float scaleMatrix[16];
7447   float cscale = SceneGetExactScreenVertexScale(G, curpt);
7448   float pscale = SceneGetExactScreenVertexScale(G, pt);
7449   identity44f(scaleMatrix);
7450   MatrixSetScaleC44f(scaleMatrix, pscale);
7451   identity44f(convMatrix);
7452   MatrixSetScaleC44f(convMatrix, 1.f/cscale);
7453   MatrixMultiplyC44f(I->m_view.m_rotMatrix, convMatrix);
7454   MatrixTranslateC44f(convMatrix, pt[0]-curpt[0], pt[1]-curpt[1], pt[2]-curpt[2]);
7455   MatrixMultiplyC44f(I->InvMatrix, convMatrix);
7456   MatrixMultiplyC44f(scaleMatrix, convMatrix);
7457 }
7458 
SceneAdjustZtoScreenZ(PyMOLGlobals * G,float * pos,float zarg)7459 void SceneAdjustZtoScreenZ(PyMOLGlobals *G, float *pos, float zarg){
7460   CScene *I = G->Scene;
7461   float clipRange = (I->m_view.m_clipSafe.m_back-I->m_view.m_clipSafe.m_front);
7462   float z = (zarg + 1.f) / 2.f;
7463   float zInPreProj = -(z * clipRange + I->m_view.m_clipSafe.m_front);
7464   float pos4[4], tpos[4], npos[4];
7465   float InvModMatrix[16];
7466   copy3f(pos, pos4);
7467   pos4[3] = 1.f;
7468   MatrixTransformC44f4f(I->ModMatrix, pos4, tpos);
7469   normalize4f(tpos);
7470   /* NEED TO ACCOUNT FOR ORTHO */
7471   if (SettingGetGlobal_b(G, cSetting_ortho)){
7472     npos[0] = tpos[0];
7473     npos[1] = tpos[1];
7474   } else {
7475     npos[0] = zInPreProj * tpos[0] / tpos[2];
7476     npos[1] = zInPreProj * tpos[1] / tpos[2];
7477   }
7478   npos[2] = zInPreProj;
7479   npos[3] = 1.f;
7480 
7481   MatrixInvertC44f(I->ModMatrix, InvModMatrix);
7482   MatrixTransformC44f4f(InvModMatrix, npos, npos);
7483   normalize4f(npos);
7484   copy3f(npos, pos);
7485 }
7486 
7487 /* this function takes a screen point, where z is normalized between the clipping
7488    planes, and converts it to the world coordinates */
SceneSetPointToWorldScreenRelative(PyMOLGlobals * G,float * pos,float * screenPt)7489 void SceneSetPointToWorldScreenRelative(PyMOLGlobals *G, float *pos, float *screenPt)
7490 {
7491   float npos[4];
7492   float InvPmvMatrix[16];
7493   int width, height;
7494   SceneGetWidthHeightStereo(G, &width, &height);
7495   npos[0] = (.5f + floor(screenPt[0]*width)) /width ;  // add .5, in middle of pixels?
7496   npos[1] = (.5f + floor(screenPt[1]*height)) /height ; // add .5, in middle of pixels?
7497   npos[2] = 0.f;
7498   npos[3] = 1.f;
7499   MatrixInvertC44f(SceneGetPmvMatrix(G), InvPmvMatrix);
7500   MatrixTransformC44f4f(InvPmvMatrix, npos, npos);
7501   normalize4f(npos);
7502   SceneAdjustZtoScreenZ(G, npos, screenPt[2]);
7503   copy3f(npos, pos);
7504 }
7505 
SceneGetCurrentBackSafe(PyMOLGlobals * G)7506 float SceneGetCurrentBackSafe(PyMOLGlobals *G){
7507   CScene *I = G->Scene;
7508   return (I->m_view.m_clipSafe.m_back);
7509 }
SceneGetCurrentFrontSafe(PyMOLGlobals * G)7510 float SceneGetCurrentFrontSafe(PyMOLGlobals *G){
7511   CScene *I = G->Scene;
7512   return (I->m_view.m_clipSafe.m_front);
7513 }
7514 
7515 /**
7516  * Get the field-of-view width at a depth of 1.0
7517  */
GetFovWidth(PyMOLGlobals * G)7518 float GetFovWidth(PyMOLGlobals * G)
7519 {
7520   float fov = SettingGetGlobal_f(G, cSetting_field_of_view);
7521   return 2.f * tanf(fov * PI / 360.f);
7522 }
7523 
SceneInvalidatePicking(PyMOLGlobals * G)7524 void SceneInvalidatePicking(PyMOLGlobals * G){
7525   CScene *I = G->Scene;
7526   I->pickmgr.invalidate();
7527 }
7528 
SceneGetScale(PyMOLGlobals * G)7529 float SceneGetScale(PyMOLGlobals * G) {
7530   return G->Scene->Scale;
7531 }
7532 
ScenePickAtomInWorld(PyMOLGlobals * G,int x,int y,float * atomWorldPos)7533 void ScenePickAtomInWorld(PyMOLGlobals * G, int x, int y, float *atomWorldPos) {
7534   CScene *I = G->Scene;
7535   if (SceneDoXYPick(G, x, y, 0)) {
7536     CObject *obj = (CObject *) I->LastPicked.context.object;
7537     if (obj->type != cObjectMolecule) {
7538       return;
7539     }
7540     // get atom pos in Local CS
7541     float atomPos[3];
7542     ObjectMoleculeGetAtomTxfVertex((ObjectMolecule *)I->LastPicked.context.object, 0, I->LastPicked.src.index, atomPos);
7543     // muptiply by molecule world matrix
7544     MatrixTransformC44f3f(I->ModMatrix, atomPos, atomWorldPos);
7545   }
7546 }
7547 
SceneClipGetEnum(pymol::zstring_view mode)7548 cSceneClip SceneClipGetEnum(pymol::zstring_view mode)
7549 {
7550   static const std::unordered_map<pymol::zstring_view, cSceneClip> modes{
7551       {"near", cSceneClip_near},
7552       {"far", cSceneClip_far},
7553       {"move", cSceneClip_move},
7554       {"slab", cSceneClip_slab},
7555       {"atoms", cSceneClip_atoms},
7556   };
7557 
7558   auto it = modes.find(mode);
7559   return (it == modes.end()) ? cSceneClip_invalid : it->second;
7560 }
7561 
7562