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