1
2 /*
3 A* -------------------------------------------------------------------
4 B* This file contains source code for the PyMOL computer program
5 C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
6 D* -------------------------------------------------------------------
7 E* It is unlawful to modify or remove this copyright notice.
8 F* -------------------------------------------------------------------
9 G* Please see the accompanying LICENSE file for further information.
10 H* -------------------------------------------------------------------
11 I* Additional authors of this source file include:
12 -*
13 -*
14 -*
15 Z* -------------------------------------------------------------------
16 */
17 #include"os_python.h"
18
19 #include"os_predef.h"
20 #include"os_std.h"
21 #include"os_gl.h"
22
23 #include"Base.h"
24 #include"OOMac.h"
25 #include"MemoryDebug.h"
26 #include"Executive.h"
27 #include"Ortho.h"
28 #include"Movie.h"
29 #include"Scene.h"
30 #include"MyPNG.h"
31 #include"P.h"
32 #include"Setting.h"
33 #include"main.h"
34 #include"PConv.h"
35 #include"Util.h"
36 #include"Util2.h"
37 #include"Parse.h"
38 #include"PyMOL.h"
39 #include"ScrollBar.h"
40 #include"Menu.h"
41 #include"View.h"
42 #include"Seq.h"
43 #include"CGO.h"
44 #include"MovieScene.h"
45
46 #define cMovieDragModeMoveKey 1
47 #define cMovieDragModeInsDel 2
48 #define cMovieDragModeCopyKey 3
49 #define cMovieDragModeOblate 4
50
CMovie(PyMOLGlobals * G)51 CMovie::CMovie(PyMOLGlobals* G)
52 : Block(G)
53 , m_ScrollBar(G, true)
54 {
55 active = true;
56 OrthoAttach(G, this, cOrthoTool);
57 }
58
MovieViewReinterpolate(PyMOLGlobals * G)59 void MovieViewReinterpolate(PyMOLGlobals *G)
60 {
61 float power = SettingGetGlobal_f(G, cSetting_motion_power);
62 float bias = SettingGetGlobal_f(G, cSetting_motion_bias);
63 float linear = SettingGetGlobal_f(G, cSetting_motion_linear);
64 int hand = SettingGetGlobal_i(G, cSetting_motion_hand);
65
66 MovieView(G, 3, -1, -1, power, bias, 1, /* note simple always = 1 for camera motion...*/
67 linear,
68 SettingGetGlobal_b(G,cSetting_movie_loop) ? 1 : 0 ,
69 hand, 5, 1, NULL, 0.5, -1, 1);
70 }
71
MovieXtoFrame(PyMOLGlobals * G,BlockRect * rect,int frames,int x,int nearest)72 int MovieXtoFrame(PyMOLGlobals *G, BlockRect *rect, int frames, int x, int nearest)
73 {
74 return ViewElemXtoFrame(rect,frames,x,nearest);
75 }
76
MovieViewTrim(PyMOLGlobals * G,int n_frame)77 void MovieViewTrim(PyMOLGlobals *G,int n_frame)
78 {
79 CMovie *I = G->Movie;
80 if(n_frame>=0) {
81 I->Sequence.resize(n_frame);
82 I->Cmd.resize(n_frame);
83 I->ViewElem.resize(n_frame);
84 I->NFrame = n_frame;
85 }
86 }
87
88
MovieViewModify(PyMOLGlobals * G,int action,int index,int count,int target,int freeze,int localize)89 int MovieViewModify(PyMOLGlobals *G,int action, int index, int count,int target, int freeze, int localize)
90 {
91 CMovie *I = G->Movie;
92 int ok = true;
93 MovieClearImages(G);
94 if( (ok = ViewElemModify(G,&I->ViewElem, action, index, count, target)) ) {
95 switch(action) {
96 case cViewElemModifyInsert:
97 I->Sequence.insert(index, count);
98 I->Cmd.insert(I->Cmd.begin() + index, count, "");
99 I->NFrame = VLAGetSize(I->Sequence);
100 {
101 int frame = SceneGetFrame(G);
102 if(frame >= index) {
103 SceneSetFrame(G,0,frame + count);
104 }
105 }
106 break;
107 case cViewElemModifyDelete:
108 I->Sequence.erase(index, count);
109 I->Cmd.erase(I->Cmd.begin() + index, I->Cmd.begin() + index + count);
110 I->NFrame = VLAGetSize(I->Sequence);
111 break;
112 case cViewElemModifyMove:
113 if((index>=0) && (target>=0) && (index<I->NFrame) && (target<I->NFrame)) {
114 int i;
115 for(i=0;i<count;i++) {
116 if( ((i+index)<I->NFrame) && ((i+target)<I->NFrame)) {
117 int src,dst;
118 if(index>target) {
119 src = index+i;
120 dst = target+i;
121 } else {
122 src = index+(count-1)-i;
123 dst = target+(count-1)-i;
124 }
125 I->Sequence[dst] = I->Sequence[src];
126 I->Cmd[dst] = std::move(I->Cmd[src]);
127 I->Cmd[src].clear();
128 }
129 }
130 }
131 break;
132 case cViewElemModifyCopy:
133 if((index>=0) && (target>=0) && (index<I->NFrame) && (target<I->NFrame)) {
134 int i;
135 for(i=0;i<count;i++) {
136 if( ((i+index)<I->NFrame) && ((i+target)<I->NFrame)) {
137 int src,dst;
138 if(index>target) {
139 src = index+i;
140 dst = target+i;
141 } else {
142 src = index+(count-1)-i;
143 dst = target+(count-1)-i;
144 }
145 I->Cmd[dst] = I->Cmd[src];
146 }
147 }
148 }
149 break;
150 }
151 }
152 if(ok && ((!freeze)&&(!localize))) {
153 ExecutiveMotionExtend(G,freeze);
154 }
155 return ok;
156 }
157
MovieGetSpecLevel(PyMOLGlobals * G,int frame)158 int MovieGetSpecLevel(PyMOLGlobals *G,int frame)
159 {
160 CMovie *I = G->Movie;
161 if(I->ViewElem) {
162 int size = VLAGetSize(I->ViewElem);
163 if(frame<0) {
164 int max_level = 0;
165 int i;
166 for(i=0;i<size;i++) {
167 if(max_level < I->ViewElem[i].specification_level)
168 max_level = I->ViewElem[i].specification_level;
169 }
170 return max_level;
171 }
172 if((frame>=0) && (frame<size))
173 return I->ViewElem[frame].specification_level;
174 return 0;
175 }
176 return -1;
177 }
178
MovieSetRealtime(PyMOLGlobals * G,int realtime)179 void MovieSetRealtime(PyMOLGlobals * G, int realtime)
180 {
181 CMovie *I = G->Movie;
182 I->RealtimeFlag = realtime;
183 }
184
MovieGetRealtime(PyMOLGlobals * G)185 int MovieGetRealtime(PyMOLGlobals * G)
186 {
187 CMovie *I = G->Movie;
188 return I->RealtimeFlag;
189 }
190
MovieCopyPrepare(PyMOLGlobals * G,int * width,int * height,int * length)191 void MovieCopyPrepare(PyMOLGlobals * G, int *width, int *height, int *length)
192 {
193 /* assumed locked api, blocked threads, and master thread on entry */
194
195 /* writes the current movie sequence to a set of PNG files
196 * this routine can take a LOT of time --
197 * TODO: develop an interrupt mechanism */
198
199 int start, stop;
200 CMovie *I = G->Movie;
201 int nFrame;
202
203 I->CacheSave = SettingGetGlobal_b(G, cSetting_cache_frames);
204 I->OverlaySave = SettingGetGlobal_i(G, cSetting_overlay);
205 if(!I->CacheSave)
206 MovieClearImages(G);
207 SettingSetGlobal_b(G, cSetting_cache_frames, 1);
208 SettingSetGlobal_i(G, cSetting_overlay, 5);
209 nFrame = I->NFrame;
210 if(!nFrame) {
211 nFrame = SceneGetNFrame(G, NULL);
212 }
213 start = 0;
214 stop = nFrame;
215 if((start != 0) || (stop != (nFrame + 1)))
216 SceneSetFrame(G, 0, 0);
217 MoviePlay(G, cMoviePlay);
218 VecCheck(I->Image, nFrame);
219 SceneGetWidthHeight(G, width, height);
220 {
221 int uniform_height = -1;
222 int uniform_width = -1;
223 int uniform_flag = false;
224 int scene_match = true;
225 int a;
226 /* make sure all the movie frames match the screen size or are pre-rendered and are already the same size */
227 for(a = 0; a < nFrame; a++) {
228 const pymol::Image* image = I->Image[a].get();
229 if(image) {
230 if((image->getHeight() != *height) || (image->getWidth() != *width)) {
231 scene_match = false;
232 if(uniform_height < 0) {
233 uniform_height = image->getHeight();
234 uniform_width = image->getWidth();
235 } else {
236 if((image->getHeight() != uniform_height) || (image->getWidth() != uniform_width))
237 uniform_flag = false;
238 }
239 }
240 } else
241 uniform_flag = false; /* missing at least one image, so not uniform */
242 }
243 if(!scene_match) {
244 if(uniform_flag) {
245 *height = uniform_height;
246 *width = uniform_width;
247 } else {
248 MovieClearImages(G);
249 }
250 }
251
252 }
253 *length = nFrame;
254 }
255
MovieFlushCommands(PyMOLGlobals * G)256 void MovieFlushCommands(PyMOLGlobals * G)
257 {
258 CMovie *I = G->Movie;
259 I->RecursionFlag = true;
260 PFlush(G);
261 I->RecursionFlag = false;
262 }
263
MovieCopyFrame(PyMOLGlobals * G,int frame,int width,int height,int rowbytes,void * ptr)264 int MovieCopyFrame(PyMOLGlobals * G, int frame, int width, int height, int rowbytes,
265 void *ptr)
266 {
267 CMovie *I = G->Movie;
268 int result = false;
269 int nFrame;
270 nFrame = I->NFrame;
271 if(!nFrame) {
272 nFrame = SceneGetNFrame(G, NULL);
273 }
274
275 if((frame < nFrame) && (ptr)) {
276 int a = frame;
277 int i;
278 SceneSetFrame(G, 0, a);
279 MovieDoFrameCommand(G, a);
280 MovieFlushCommands(G);
281 i = MovieFrameToImage(G, a);
282 VecCheck(I->Image, i);
283 if(!I->Image[i]) {
284 SceneUpdate(G, false);
285 SceneMakeMovieImage(G, false, false, cSceneImage_Default);
286 }
287 if(!I->Image[i]) {
288 PRINTFB(G, FB_Movie, FB_Errors)
289 "MoviePNG-Error: Missing rendered image.\n" ENDFB(G);
290 } else {
291 if((I->Image[i]->getHeight() == height) && (I->Image[i]->getWidth() == width)) {
292 unsigned char *srcImage = I->Image[i]->bits();
293 int i, j;
294 for(i = 0; i < height; i++) {
295 unsigned char *dst = ((unsigned char *) ptr) + i * rowbytes;
296 unsigned char *src = srcImage + ((height - 1) - i) * width * 4;
297 for(j = 0; j < width; j++) {
298 *dst++ = src[3];
299 *dst++ = src[0];
300 *dst++ = src[1];
301 *dst++ = src[2];
302 src += 4;
303 }
304 }
305 result = true;
306 } else {
307 /* mismatched dimensions, so furnish a white image */
308 memset(ptr, 0xFF, 4 * height * width);
309 }
310 ExecutiveDrawNow(G);
311 if(G->HaveGUI)
312 PyMOL_SwapBuffers(G->PyMOL);
313 }
314 if(!I->CacheSave) {
315 if(I->Image[i]) {
316 I->Image[i] = nullptr;
317 }
318 }
319 }
320 return result;
321 }
322
MoviePurgeFrame(PyMOLGlobals * G,int frame)323 int MoviePurgeFrame(PyMOLGlobals * G, int frame)
324 {
325 CMovie *I = G->Movie;
326 int result = false;
327 int nFrame;
328 int i;
329 nFrame = I->NFrame;
330 if(!nFrame) {
331 nFrame = SceneGetNFrame(G, NULL);
332 }
333 if(!I->CacheSave) {
334 if(frame < nFrame) {
335 int a = frame;
336 i = MovieFrameToImage(G, a);
337 VecCheck(I->Image, i);
338 if(I->Image[i]) {
339 I->Image[i] = nullptr;
340 result = true;
341 }
342 }
343 }
344 return result;
345 }
346
MovieCopyFinish(PyMOLGlobals * G)347 void MovieCopyFinish(PyMOLGlobals * G)
348 {
349 CMovie *I = G->Movie;
350 SceneInvalidate(G); /* important */
351 SettingSetGlobal_b(G, cSetting_cache_frames, I->CacheSave);
352 SettingSetGlobal_i(G, cSetting_overlay, I->OverlaySave);
353 MoviePlay(G, cMovieStop);
354 if(!I->CacheSave) {
355 MovieClearImages(G);
356 }
357 }
358
MovieLocked(PyMOLGlobals * G)359 int MovieLocked(PyMOLGlobals * G)
360 {
361 CMovie *I = G->Movie;
362 return (I->Locked);
363 }
364
MovieSetLock(PyMOLGlobals * G,int lock)365 void MovieSetLock(PyMOLGlobals * G, int lock)
366 {
367 CMovie *I = G->Movie;
368 I->Locked = lock;
369 }
370
371
372 /*========================================================================*/
MovieDump(PyMOLGlobals * G)373 void MovieDump(PyMOLGlobals * G)
374 {
375 int a;
376 CMovie *I = G->Movie;
377 int flag = false;
378
379 for(a = 0; a < I->NFrame; a++) {
380 if(!I->Cmd[a].empty()) {
381 flag = true;
382 break;
383 }
384 }
385 if(flag && I->NFrame) {
386 PRINTFB(G, FB_Movie, FB_Results)
387 " Movie: General Purpose Commands:\n" ENDFB(G);
388 for(a = 0; a < I->NFrame; a++) {
389 if(!I->Cmd[a].empty()) {
390 auto buffer = pymol::string_format("%5d: %s\n", a + 1, I->Cmd[a]);
391 OrthoAddOutput(G, buffer.c_str());
392 }
393 }
394 } else {
395 PRINTFB(G, FB_Movie, FB_Results)
396 " Movie: No movie commands are defined.\n" ENDFB(G);
397 }
398 }
399
MovieCmdFromPyList(PyMOLGlobals * G,PyObject * list,int * warning)400 static int MovieCmdFromPyList(PyMOLGlobals * G, PyObject * list, int *warning)
401 {
402
403 CMovie *I = G->Movie;
404 int ok = true;
405 int a;
406 int warn = false;
407
408 if(ok)
409 ok = (list != NULL);
410 if(ok)
411 ok = PyList_Check(list);
412
413 for(a = 0; a < I->NFrame; a++) {
414 if(ok)
415 ok = PConvFromPyListItem(G, list, a, I->Cmd[a]);
416 if(ok)
417 warn = (warn || !I->Cmd[a].empty());
418 }
419 *warning = warn;
420 return (ok);
421
422 }
423
424 /*========================================================================*/
MovieFromPyList(PyMOLGlobals * G,PyObject * list,int * warning)425 int MovieFromPyList(PyMOLGlobals * G, PyObject * list, int *warning)
426 {
427 int ok = true;
428 CMovie *I = G->Movie;
429 int ll = 0;
430
431 MovieReset(G);
432 if(ok)
433 ok = PyList_Check(list);
434 if(ok)
435 ll = PyList_Size(list);
436 /* TO SUPPORT BACKWARDS COMPATIBILITY...
437 Always check ll when adding new PyList_GetItem's */
438 if(ok)
439 ok = PConvPyIntToInt(PyList_GetItem(list, 0), &I->NFrame);
440 if(ok)
441 ok = PConvPyIntToInt(PyList_GetItem(list, 1), &I->MatrixFlag);
442 if(ok && I->MatrixFlag)
443 ok =
444 PConvPyListToFloatArrayInPlace(PyList_GetItem(list, 2), I->Matrix, cSceneViewSize);
445 if(ok)
446 ok = PConvPyIntToInt(PyList_GetItem(list, 3), &I->Playing);
447 if(ok && I->NFrame) {
448 I->Sequence = pymol::vla<int>(I->NFrame);
449 I->Cmd = std::vector<std::string>(I->NFrame);
450 if(ok)
451 ok = PConvPyListToIntArrayInPlace(PyList_GetItem(list, 4), I->Sequence.data(), I->NFrame);
452 if(ok)
453 ok = MovieCmdFromPyList(G, PyList_GetItem(list, 5), warning);
454 if((*warning) && G->Security) {
455 MovieSetLock(G, true);
456 }
457 }
458 if(ok && (ll > 6)) {
459 PyObject *tmp;
460 VLAFreeP(I->ViewElem);
461 I->ViewElem = NULL;
462 tmp = PyList_GetItem(list, 6);
463 if(tmp && !(tmp == Py_None))
464 ok = ViewElemVLAFromPyList(G, tmp, &I->ViewElem, I->NFrame);
465 }
466 if(!ok) {
467 MovieReset(G);
468 } else if(MovieDefined(G)) {
469 OrthoReshape(G,-1,-1,true);
470 SceneCountFrames(G);
471 }
472 return (ok);
473 }
474
475
476 /*========================================================================*/
MovieCmdAsPyList(PyMOLGlobals * G)477 static PyObject *MovieCmdAsPyList(PyMOLGlobals * G)
478 {
479
480 CMovie *I = G->Movie;
481 PyObject *result = NULL;
482 int a;
483
484 result = PyList_New(I->NFrame);
485 if(result)
486 for(a = 0; a < I->NFrame; a++) {
487 PyList_SetItem(result, a, PyString_FromString(I->Cmd[a].data()));
488 }
489 return (PConvAutoNone(result));
490
491 }
492
493 /*========================================================================*/
MovieAsPyList(PyMOLGlobals * G)494 PyObject *MovieAsPyList(PyMOLGlobals * G)
495 {
496 CMovie *I = G->Movie;
497 PyObject *result = NULL;
498
499 result = PyList_New(7);
500 PyList_SetItem(result, 0, PyInt_FromLong(I->NFrame));
501 PyList_SetItem(result, 1, PyInt_FromLong(I->MatrixFlag));
502 PyList_SetItem(result, 2, PConvFloatArrayToPyList(I->Matrix, cSceneViewSize));
503 PyList_SetItem(result, 3, PyInt_FromLong(I->Playing));
504 if(I->Sequence) {
505 PyList_SetItem(result, 4, PConvIntArrayToPyList(I->Sequence, I->NFrame));
506 } else {
507 PyList_SetItem(result, 4, PConvAutoNone(NULL));
508 }
509 if(!I->Cmd.empty()) {
510 PyList_SetItem(result, 5, MovieCmdAsPyList(G));
511 } else {
512 PyList_SetItem(result, 5, PConvAutoNone(NULL));
513 }
514 if(I->ViewElem) {
515 PyList_SetItem(result, 6, ViewElemVLAAsPyList(G, I->ViewElem, I->NFrame));
516 } else {
517 PyList_SetItem(result, 6, PConvAutoNone(NULL));
518 }
519
520
521 /* pymol::Image *Image;
522 int *Sequence;
523 MovieCmdType *Cmd;
524 int NImage,NFrame;
525 unsigned Width,Height;
526 int MatrixFlag;
527 float Matrix[16];
528 int Playing;
529 */
530 return (PConvAutoNone(result));
531 }
532
533
534 /*========================================================================*/
MoviePlaying(PyMOLGlobals * G)535 int MoviePlaying(PyMOLGlobals * G)
536 {
537 CMovie *I = G->Movie;
538 if(I->Locked)
539 return false;
540 if(I->Playing && G->Interrupt) {
541 I->Playing = false;
542 }
543 return (I->Playing || I->RecursionFlag);
544 /* returns true if movie is playing OR if we're in the process of evaluating
545 movie commands */
546 }
547
548
549 /*========================================================================*/
MoviePlay(PyMOLGlobals * G,int cmd)550 void MoviePlay(PyMOLGlobals * G, int cmd)
551 {
552 CMovie *I = G->Movie;
553 switch (cmd) {
554 case cMovieToggle:
555 I->Playing = !I->Playing;
556 if(I->Playing && !SettingGetGlobal_b(G, cSetting_movie_loop)) {
557 /* if not looping, and at end of movie, then automatically rewind
558 and force execution of the first movie command */
559 if((SettingGetGlobal_i(G, cSetting_frame)) == (SceneGetNFrame(G, NULL))) {
560 SceneSetFrame(G, 7, 0);
561 }
562 }
563 break;
564 case cMovieStop:
565 I->Playing = false;
566 break;
567 case cMoviePlay:
568 if(!SettingGetGlobal_b(G, cSetting_movie_loop)) {
569 /* if not looping, and at end of movie, then automatically rewind
570 and force execution of the first movie command */
571 if((SettingGetGlobal_i(G, cSetting_frame)) == (SceneGetNFrame(G, NULL))) {
572 SceneSetFrame(G, 7, 0);
573 }
574 }
575 I->Playing = true;
576 break;
577 }
578 OrthoDirty(G);
579 SceneRestartFrameTimer(G);
580 }
581
582
583 /*========================================================================*/
MovieMatrix(PyMOLGlobals * G,int action)584 int MovieMatrix(PyMOLGlobals * G, int action)
585 {
586 CMovie *I = G->Movie;
587 int result = false;
588 switch (action) {
589 case cMovieMatrixClear:
590 I->MatrixFlag = false;
591 result = 1;
592 break;
593 case cMovieMatrixStore:
594 SceneGetView(G, I->Matrix);
595 I->MatrixFlag = true;
596 result = 1;
597 break;
598 case cMovieMatrixRecall:
599 if(I->MatrixFlag) {
600 SceneSetView(G, I->Matrix, true, 0, 0);
601 result = 1;
602 } else
603 result = 0;
604 break;
605 case cMovieMatrixCheck:
606 result = I->MatrixFlag;
607 break;
608 }
609 return result;
610 }
611
612
613 /*========================================================================*/
MovieSetSize(PyMOLGlobals * G,unsigned int width,unsigned int height)614 void MovieSetSize(PyMOLGlobals * G, unsigned int width, unsigned int height)
615 {
616 return;
617 }
618
619
620 /*========================================================================*/
MovieModalPNG(PyMOLGlobals * G,CMovie * I,CMovieModal * M)621 static void MovieModalPNG(PyMOLGlobals * G, CMovie * I, CMovieModal * M)
622 {
623 switch (M->stage) {
624 case 0: /* setup */
625 MovieSetRealtime(G, false);
626 M->save = SettingGetGlobal_b(G, cSetting_cache_frames);
627 if(!M->save)
628 MovieClearImages(G);
629 SettingSetGlobal_b(G, cSetting_cache_frames, 1);
630 OrthoBusyPrime(G);
631 M->nFrame = I->NFrame;
632 if(!M->nFrame) {
633 M->nFrame = SceneGetNFrame(G, NULL);
634 if(M->nFrame < 1) {
635 M->nFrame = 1;
636 }
637 }
638 if(M->start < 0)
639 M->start = 0;
640 if(M->start > M->nFrame)
641 M->start = M->nFrame;
642 if(M->stop < 0)
643 M->stop = M->nFrame;
644 if(M->stop > M->nFrame)
645 M->stop = M->nFrame;
646 {
647 auto buffer = pymol::string_format("Creating movie (%d frames)...", M->nFrame);
648 OrthoBusyMessage(G, buffer.c_str());
649 }
650 if((M->start != 0) || (M->stop != (M->nFrame + 1)))
651 SceneSetFrame(G, 0, 0);
652 MoviePlay(G, cMoviePlay);
653 VecCheck(I->Image, M->nFrame);
654 M->frame = 0;
655 M->stage = 1;
656 if(G->Interrupt) {
657 M->stage = 5; /* abort */
658 }
659 break;
660 case 1: /* RENDER LOOP: advance a frame */
661 if(M->frame < M->nFrame) {
662
663 M->file_missing = true;
664 M->timing = UtilGetSeconds(G); /* start timing the process */
665
666 PRINTFB(G, FB_Movie, FB_Debugging)
667 " MoviePNG-DEBUG: Cycle %d...\n", M->frame ENDFB(G);
668 switch (M->format) {
669 case cMyPNG_FormatPPM:
670 M->fname = pymol::string_format("%s%04d.ppm", M->prefix.c_str(), M->frame + 1);
671 break;
672 case cMyPNG_FormatPNG:
673 default:
674 M->fname = pymol::string_format("%s%04d.png", M->prefix.c_str(), M->frame + 1);
675 break;
676 }
677
678 if(M->missing_only) {
679 FILE *tmp = fopen(M->fname.c_str(), "rb");
680 if(tmp) {
681 fclose(tmp);
682 M->file_missing = false;
683 } else {
684 M->file_missing = true;
685 }
686 }
687 SceneSetFrame(G, 0, M->frame);
688 MovieDoFrameCommand(G, M->frame);
689 MovieFlushCommands(G);
690
691 M->image = MovieFrameToImage(G, M->frame);
692 M->stage = 2;
693 if(G->Interrupt) {
694 M->stage = 5; /* abort */
695 }
696 }
697 break;
698 }
699
700 switch (M->stage) {
701 case 2: /* IN RENDER LOOP: create the image */
702 VecCheck(I->Image, M->image);
703 if((M->frame >= M->start) && /* only render frames in the specified interval... */
704 (M->frame <= M->stop) && (M->file_missing)) { /* ...that don't already exist */
705 if(!I->Image[M->image]) {
706 SceneUpdate(G, false);
707 if(SceneMakeMovieImage(G, false, M->modal, M->mode, M->width, M->height)
708 || !M->modal) {
709 M->stage = 3;
710 } else {
711 /* didn't get an image... */
712 PRINTFB(G, FB_Movie, FB_Errors)
713 " MoviePNG-Error: unable to obtain a valid OpenGL image. Trying again...\n"
714 ENDFB(G);
715 }
716 } else { /* frame already rendered */
717 M->stage = 3;
718 }
719 } else { /* we don't need to render this frame */
720 M->stage = 4;
721 }
722 if(G->Interrupt) {
723 M->stage = 5; /* abort */
724 }
725 break;
726 }
727
728 switch (M->stage) {
729 case 3: /* IN RENDER LOOP: have image, so write to file */
730 if(!I->Image[M->image]) {
731 PRINTFB(G, FB_Movie, FB_Errors)
732 "MoviePNG-Error: Missing rendered image.\n" ENDFB(G);
733 } else {
734 if (!MyPNGWrite(M->fname.c_str(), *I->Image[M->image],
735 SettingGetGlobal_f(G, cSetting_image_dots_per_inch), M->format,
736 M->quiet, SettingGetGlobal_f(G, cSetting_png_screen_gamma),
737 SettingGetGlobal_f(G, cSetting_png_file_gamma))) {
738 PRINTFB(G, FB_Movie, FB_Errors)
739 " MoviePNG-Error: unable to write '%s'\n", M->fname.c_str() ENDFB(G);
740 }
741 ExecutiveDrawNow(G);
742 OrthoBusySlow(G, M->frame, M->nFrame);
743 if(G->HaveGUI)
744 PyMOL_SwapBuffers(G->PyMOL);
745 PRINTFB(G, FB_Movie, FB_Debugging)
746 " MoviePNG-DEBUG: i = %d, I->Image[image] = %p\n", M->image,
747 I->Image[M->image]->bits() ENDFB(G);
748 }
749 if(I->Image[M->image]) {
750 I->Image[M->image] = nullptr;
751 }
752 M->timing = UtilGetSeconds(G) - M->timing;
753 M->accumTiming += M->timing;
754 {
755 double est1 = (M->nFrame - M->frame) * M->timing;
756 double est2 = ((M->nFrame - M->frame) / (float) (M->frame + 1)) * M->accumTiming;
757
758 PRINTFB(G, FB_Movie, FB_Details)
759 " Movie: frame %4d of %4d, %4.2f sec. (%d:%02d:%02d - %d:%02d:%02d to go).\n",
760 M->frame + 1, M->nFrame,
761 M->timing,
762 (int) (est1 / 3600),
763 ((int) (est1 / 60)) % 60,
764 ((int) est1) % 60,
765 (int) (est2 / 3600), ((int) (est2 / 60)) % 60, ((int) est2) % 60 ENDFB(G);
766 }
767 M->stage = 4;
768 if(G->Interrupt) {
769 M->stage = 5; /* abort */
770 }
771 break;
772 }
773
774 switch (M->stage) {
775 case 4: /* IN RENDER LOOP: advance to next frame or kick out when done */
776 M->frame++;
777 if(M->frame >= M->nFrame) {
778 M->stage = 5;
779 } else {
780 M->stage = 1; /* RESTART LOOP */
781 }
782 if(G->Interrupt) {
783 M->stage = 5; /* abort */
784 }
785 break;
786 }
787
788 switch (M->stage) {
789 case 5: /* finish up */
790
791 SceneInvalidate(G); /* important */
792 PRINTFB(G, FB_Movie, FB_Debugging)
793 " MoviePNG-DEBUG: done.\n" ENDFB(G);
794 SettingSetGlobal_b(G, cSetting_cache_frames, M->save);
795 MoviePlay(G, cMovieStop);
796 MovieClearImages(G);
797 MovieSetRealtime(G, true);
798 M->complete = true;
799 M->stage = 6;
800 break;
801 }
802 }
803
804 static void MovieModalDraw(PyMOLGlobals * G);
805
MovieModalDraw(PyMOLGlobals * G)806 static void MovieModalDraw(PyMOLGlobals * G)
807 {
808 CMovie *I = G->Movie;
809 MovieModalPNG(G, I, &I->Modal);
810 if(!I->Modal.complete) /* force modalic return until job is done */
811 PyMOL_SetModalDraw(G->PyMOL, (PyMOLModalDrawFn *) MovieModalDraw);
812 }
813
MoviePNG(PyMOLGlobals * G,const char * prefix,int save,int start,int stop,int missing_only,int modal,int format,int mode,int quiet,int width,int height)814 int MoviePNG(PyMOLGlobals * G, const char* prefix, int save, int start,
815 int stop, int missing_only, int modal, int format, int mode, int quiet,
816 int width, int height)
817 {
818 /* assumes locked api, blocked threads, and master thread on entry */
819 CMovie *I = G->Movie;
820
821 /* new routine allows PyMOL to workaround XP and Vista Windowing
822 issues */
823
824 CMovieModal *M = &I->Modal;
825
826 *M = CMovieModal();
827
828 mode = SceneValidateImageMode(G, mode, width || height);
829
830 /* default behavior is to go modal unless we're ray tracing */
831 if(modal < 0 && mode == cSceneImage_Ray) {
832 modal = 0;
833 }
834
835 M->prefix = prefix;
836 M->save = save;
837 M->start = start;
838 M->stop = stop;
839 M->missing_only = missing_only;
840 M->stage = 0;
841 M->format = format;
842 M->mode = mode;
843 M->quiet = quiet;
844 M->width = width;
845 M->height = height;
846
847 if(SettingGetGlobal_b(G, cSetting_seq_view)) {
848 PRINTFB(G, FB_Movie, FB_Warnings)
849 " MoviePNG-Warning: disabling seq_view, may conflict with movie export\n" ENDFB(G);
850 SettingSetGlobal_b(G, cSetting_seq_view, 0);
851 // force viewport update
852 SeqChanged(G);
853 OrthoDoDraw(G, 0);
854 }
855
856 M->modal = modal;
857
858 if(modal) {
859 PyMOL_SetModalDraw(G->PyMOL, (PyMOLModalDrawFn *) MovieModalDraw);
860 } else {
861 while(!M->complete) {
862 MovieModalPNG(G, I, &I->Modal);
863 }
864 }
865 return true;
866 }
867
868
869 /*========================================================================*/
MovieAppendSequence(PyMOLGlobals * G,const char * str,int start_from,int freeze)870 void MovieAppendSequence(PyMOLGlobals * G, const char *str, int start_from,int freeze)
871 {
872 CMovie *I = G->Movie;
873 int c = 0;
874 int i;
875 /* int old_NFrame = I->NFrame; */
876 const char *s;
877 char number[20];
878
879 if(start_from < 0)
880 start_from = I->NFrame;
881
882 c = start_from;
883
884 PRINTFB(G, FB_Movie, FB_Debugging)
885 " MovieSequence: entered. str:%s\n", str ENDFB(G);
886
887 s = str;
888 while(*s) {
889 s = ParseWord(number, s, 20);
890 if(sscanf(number, "%i", &i)) { /* slow */
891 c++;
892 }
893 }
894
895 if(!c) {
896 VLAFreeP(I->Sequence);
897 I->Cmd.clear();
898 VLAFreeP(I->ViewElem);
899 I->NFrame = 0;
900 } else {
901 // to clear
902 I->Sequence.resize(start_from);
903 I->Cmd.resize(start_from);
904 I->ViewElem.resize(start_from);
905
906 // append (c - start_from) zero-initialized elements
907 I->Sequence.resize(c);
908 I->Cmd.resize(c);
909 I->ViewElem.resize(c);
910 }
911
912 if(c && str[0]) { /* not just a reset */
913 for(i = start_from; i < c; i++)
914 I->Cmd[i].clear();
915 c = start_from;
916 s = str;
917 while(*s) {
918 s = ParseWord(number, s, 20);
919 if(sscanf(number, "%i", &I->Sequence[c])) {
920 c++;
921 }
922 }
923 I->NFrame = c;
924 } else if(!str[0]) {
925 I->NFrame = start_from;
926 }
927
928 // fixes PYMOL-2710
929 MovieClearImages(G);
930
931 I->Image.resize(I->NFrame);
932 PRINTFB(G, FB_Movie, FB_Debugging)
933 " MovieSequence: leaving... I->NFrame%d\n", I->NFrame ENDFB(G);
934
935 if(!freeze) {
936 if(SettingGetGlobal_b(G,cSetting_movie_auto_interpolate))
937 ExecutiveMotionReinterpolate(G);
938 }
939 ExecutiveCountMotions(G);
940 }
941
942
943 /*========================================================================*/
MovieFrameToImage(PyMOLGlobals * G,int frame)944 int MovieFrameToImage(PyMOLGlobals * G, int frame)
945 {
946 int result = 0;
947 int single_image = SettingGetGlobal_b(G, cSetting_single_image);
948 if(single_image)
949 result = MovieFrameToIndex(G, frame);
950 else
951 result = frame;
952 PRINTFB(G, FB_Movie, FB_Debugging)
953 " MovieFrameToImage-DEBUG: result %d\n", result ENDFB(G);
954 return (result);
955 }
956
957
958 /*========================================================================*/
MovieFrameToIndex(PyMOLGlobals * G,int frame)959 int MovieFrameToIndex(PyMOLGlobals * G, int frame)
960 {
961 CMovie *I = G->Movie;
962 if(I->Sequence && I->NFrame) {
963 if(frame >= I->NFrame) {
964 frame = I->NFrame - 1;
965 }
966 if(I->ViewElem && I->ViewElem[frame].state_flag) {
967 return I->ViewElem[frame].state;
968 }
969 return (I->Sequence[frame]);
970 } else {
971 return (frame);
972 }
973 }
974 /*========================================================================*/
MovieSetImage(PyMOLGlobals * G,int index,std::shared_ptr<pymol::Image> image)975 void MovieSetImage(PyMOLGlobals * G, int index, std::shared_ptr<pymol::Image> image)
976 {
977 CMovie *I = G->Movie;
978
979 PRINTFB(G, FB_Movie, FB_Blather)
980 " MovieSetImage: setting movie image %d\n", index + 1 ENDFB(G);
981
982 VecCheck(I->Image, index);
983 I->Image[index] = image;
984 if(I->NImage < (index + 1))
985 I->NImage = index + 1;
986 }
987
MovieSeekScene(PyMOLGlobals * G,int loop)988 int MovieSeekScene(PyMOLGlobals * G, int loop)
989 {
990 CMovie *I = G->Movie;
991 int result = -1;
992 OVreturn_word ret;
993 const char *scene_name = SettingGetGlobal_s(G,cSetting_scene_current_name);
994 if(OVreturn_IS_OK
995 ((ret = OVLexicon_BorrowFromCString
996 (G->Lexicon, scene_name)))) {
997 if(I->ViewElem) {
998 int i,len = MovieGetLength(G);
999 for(i = SceneGetFrame(G); i < len; i++) {
1000 if(I->ViewElem[i].scene_flag) {
1001 if(I->ViewElem[i].scene_name == ret.word) {
1002 result = i;
1003 break;
1004 }
1005 }
1006 }
1007 if(loop) {
1008 len = SceneGetFrame(G);
1009 for(i = 0; i < len; i++ ) {
1010 if(I->ViewElem[i].scene_flag) {
1011 if(I->ViewElem[i].scene_name == ret.word) {
1012 result = i;
1013 break;
1014 }
1015 }
1016 }
1017 }
1018 }
1019 }
1020
1021 return result;
1022 }
1023
1024 /*========================================================================*/
MovieDoFrameCommand(PyMOLGlobals * G,int frame)1025 void MovieDoFrameCommand(PyMOLGlobals * G, int frame)
1026 {
1027 CMovie *I = G->Movie;
1028 if(frame == 0)
1029 MovieMatrix(G, cMovieMatrixRecall);
1030 if(!I->Locked) {
1031 if((frame >= 0) && (frame < I->NFrame)) {
1032 if(!I->Cmd[frame].empty()) {
1033 if(!I->RecursionFlag) {
1034 PParse(G, I->Cmd[frame].data());
1035 }
1036 }
1037 if(I->ViewElem) {
1038 if(I->ViewElem[frame].scene_flag) {
1039 char *st = OVLexicon_FetchCString(G->Lexicon, I->ViewElem[frame].scene_name);
1040 if(strcmp(st, SettingGetGlobal_s(G, cSetting_scene_current_name))) {
1041 MovieSceneRecall(G, st, 0.0,
1042 /* view */ false, true, true, true,
1043 /* frame */ false);
1044 }
1045 }
1046 SceneFromViewElem(G, I->ViewElem + frame, true);
1047 }
1048 }
1049 }
1050 }
1051
1052
1053 /*========================================================================*/
MovieSetCommand(PyMOLGlobals * G,int frame,const char * command)1054 void MovieSetCommand(PyMOLGlobals* G, int frame, const char* command)
1055 {
1056 CMovie *I = G->Movie;
1057 if((frame >= 0) && (frame < I->NFrame)) {
1058 I->Cmd[frame] = command;
1059 } else {
1060 PRINTFB(G, FB_Movie, FB_Errors)
1061 " Movie-Error: frame %d does not exist. Use 'mset' to define movie first.\n",
1062 frame + 1 ENDFB(G);
1063 }
1064 }
1065
1066
1067 /*========================================================================*/
MovieView(PyMOLGlobals * G,int action,int first,int last,float power,float bias,int simple,float linear,int wrap,int hand,int window,int cycles,const char * scene_name,float scene_cut,int state,int quiet)1068 int MovieView(PyMOLGlobals * G, int action, int first,
1069 int last, float power, float bias,
1070 int simple, float linear, int wrap,
1071 int hand, int window, int cycles,
1072 const char *scene_name, float scene_cut, int state, int quiet)
1073 {
1074 CMovie *I = G->Movie;
1075 int frame;
1076
1077 if(wrap<0) {
1078 wrap = SettingGetGlobal_b(G,cSetting_movie_loop);
1079 }
1080 if((action == 7) || (action == 8)) { /* toggle */
1081 frame = first;
1082 if(first < 0)
1083 frame = SceneGetFrame(G);
1084 VLACheck(I->ViewElem, CViewElem, frame);
1085 if(action == 7) {
1086 if(I->ViewElem[frame].specification_level>1) {
1087 action = 1;
1088 } else {
1089 action = 0;
1090 }
1091 } else if(action == 8) {
1092 if(I->ViewElem[frame].specification_level>1) {
1093 int frame;
1094 action = 3;
1095 for(frame=0;frame<I->NFrame;frame++) {
1096 if(I->ViewElem[frame].specification_level==1) {
1097 action = 6;
1098 break;
1099 }
1100 }
1101 }
1102 else if(I->ViewElem[frame].specification_level>0) {
1103 action = 6;
1104 } else {
1105 action = 3;
1106 }
1107 }
1108 }
1109 if(action == 4) {
1110 if(I->ViewElem) {
1111 int save_last = last;
1112 if(first < 0)
1113 first = 0;
1114
1115 if(last < 0) {
1116 last = SceneGetNFrame(G, NULL) - 1;
1117 }
1118 if(last >= I->NFrame) {
1119 last = I->NFrame - 1;
1120 }
1121 if(first <= last) {
1122 int a;
1123 VLACheck(I->ViewElem, CViewElem, last);
1124 for(a = 0; a < cycles; a++) {
1125 ViewElemSmooth(I->ViewElem + first, I->ViewElem + last, window, wrap);
1126 }
1127 }
1128 if(SettingGetGlobal_b(G, cSetting_movie_auto_interpolate)) {
1129 action = 3; /* reinterpolate */
1130 last = save_last;
1131 }
1132 }
1133 }
1134 switch (action) {
1135 case 0: /* store */
1136 if(I->ViewElem) {
1137 if(first < 0)
1138 first = SceneGetFrame(G);
1139 if(last < 0)
1140 last = first;
1141
1142 VLACheck(I->ViewElem, CViewElem, last);
1143
1144 for(frame = first; frame <= last; frame++) {
1145 if((frame >= 0) && (frame < I->NFrame)) {
1146 VLACheck(I->ViewElem, CViewElem, frame);
1147 if(!quiet) {
1148 PRINTFB(G, FB_Movie, FB_Details)
1149 " MovieView: Setting frame %d.\n", frame + 1 ENDFB(G);
1150 }
1151 if(scene_name && (!scene_name[0]))
1152 scene_name = NULL;
1153 SceneToViewElem(G, I->ViewElem + frame, scene_name);
1154 if(state>=0) {
1155 I->ViewElem[frame].state = state;
1156 I->ViewElem[frame].state_flag = true;
1157 } else if(state==-2) {
1158 I->ViewElem[frame].state = SceneGetState(G);
1159 I->ViewElem[frame].state_flag = true;
1160 }
1161
1162 if(power!=0.0F) {
1163 I->ViewElem[frame].power_flag = true;
1164 I->ViewElem[frame].power = power;
1165 }
1166
1167 if(bias > 0.0F) {
1168 I->ViewElem[frame].bias_flag = true;
1169 I->ViewElem[frame].bias = bias;
1170 }
1171
1172 I->ViewElem[frame].specification_level = 2;
1173 }
1174 }
1175 }
1176 break;
1177 case 1: /* clear */
1178 if(I->ViewElem) {
1179 if(first < 0)
1180 first = SceneGetFrame(G);
1181 if(last < 0)
1182 last = first;
1183 for(frame = first; frame <= last; frame++) {
1184 if((frame >= 0) && (frame < I->NFrame)) {
1185 VLACheck(I->ViewElem, CViewElem, frame);
1186 ViewElemArrayPurge(G, I->ViewElem + frame, 1);
1187 UtilZeroMem((void *) (I->ViewElem + frame), sizeof(CViewElem));
1188 }
1189 }
1190 }
1191 break;
1192 case 2: /* interpolate & reinterpolate */
1193 case 3:
1194 if(I->ViewElem) {
1195 int view_found = false;
1196 CViewElem *first_view = NULL, *last_view = NULL;
1197 if(first < 0)
1198 first = 0;
1199
1200 if(first > I->NFrame) {
1201 first = I->NFrame - 1;
1202 }
1203 /* note that we're leaving a blank frame at the end... */
1204 if(last < 0) {
1205 last = MovieGetLength(G);
1206
1207 if(last) {
1208 if(!wrap)
1209 last--;
1210 else {
1211 int frame = 0;
1212 VLACheck(I->ViewElem, CViewElem, last);
1213 for(frame = 0; frame < last; frame++) {
1214 if(I->ViewElem[frame].specification_level > 1) {
1215 last += frame;
1216 break;
1217 }
1218 }
1219 }
1220 }
1221 } else if(last >= I->NFrame) {
1222 last = I->NFrame;
1223 if(last && (!wrap))
1224 last--;
1225 }
1226
1227 VLACheck(I->ViewElem, CViewElem, last);
1228
1229 if(wrap && (last >= I->NFrame)) {
1230 /* if we're interpolating beyond the last frame, then wrap by
1231 copying early frames to last frames */
1232 int a;
1233 for(a = I->NFrame; a <= last; a++) {
1234 ViewElemCopy(G, I->ViewElem + a - I->NFrame, I->ViewElem + a);
1235 }
1236 } else if(!wrap) {
1237 /* if we're not wrapping, then make sure we nuke any stray / old
1238 interpolated frames */
1239 frame = I->NFrame - 1;
1240 while(frame>=0) {
1241 if(I->ViewElem[frame].specification_level > 1)
1242 break;
1243 else
1244 UtilZeroMem((void *) (I->ViewElem + frame), sizeof(CViewElem));
1245 frame--;
1246 }
1247 }
1248
1249 if(!quiet) {
1250 if(action == 2) {
1251 if(last == I->NFrame) {
1252 PRINTFB(G, FB_Movie, FB_Details)
1253 " MovieView: interpolating unspecified frames %d to %d (wrapping)\n",
1254 first + 1, last ENDFB(G);
1255 } else {
1256 PRINTFB(G, FB_Movie, FB_Details)
1257 " MovieView: interpolating unspecified frames %d to %d.\n", first + 1,
1258 last + 1 ENDFB(G);
1259 }
1260
1261 } else {
1262 if(last == I->NFrame) {
1263 PRINTFB(G, FB_Movie, FB_Details)
1264 " MovieView: reinterpolating all frames %d to %d (wrapping).\n", first + 1,
1265 last ENDFB(G);
1266 } else {
1267 PRINTFB(G, FB_Movie, FB_Details)
1268 " MovieView: reinterpolating all frames %d to %d.\n", first + 1, last + 1
1269 ENDFB(G);
1270 }
1271 }
1272 }
1273
1274 for(frame = first; frame <= last; frame++) {
1275 if(!first_view) {
1276 if(I->ViewElem[frame].specification_level == 2) { /* specified */
1277 first_view = I->ViewElem + frame;
1278 view_found = true;
1279 }
1280 } else {
1281 CViewElem *view;
1282 int interpolate_flag = false;
1283 if(I->ViewElem[frame].specification_level == 2) { /* specified */
1284 last_view = I->ViewElem + frame;
1285 if(action == 2) { /* interpolate */
1286 for(view = first_view + 1; view < last_view; view++) {
1287 if(!view->specification_level)
1288 interpolate_flag = true;
1289 }
1290 } else {
1291 interpolate_flag = true;
1292 }
1293 if(interpolate_flag) {
1294 ViewElemInterpolate(G, first_view, last_view,
1295 power, bias, simple, linear, hand, scene_cut);
1296 }
1297 first_view = last_view;
1298 last_view = NULL;
1299 }
1300 }
1301 }
1302
1303 if(first_view) {
1304 if(wrap && (last >= I->NFrame)) {
1305 /* if we're interpolating beyond the last frame, then wrap by
1306 copying the last frames back over the early frames */
1307 int a;
1308 for(a = I->NFrame; a <= last; a++) {
1309 ViewElemCopy(G, I->ViewElem + a, I->ViewElem + a - I->NFrame);
1310 }
1311 }
1312 }
1313
1314 if((!view_found) && (last>=first) && (first>=0) && (last<=I->NFrame)) {
1315 UtilZeroMem(I->ViewElem + first, sizeof(CViewElem) * (1 + (last-first)));
1316 }
1317
1318 if(last >= I->NFrame) { /* now erase temporary views */
1319 ViewElemArrayPurge(G, I->ViewElem + I->NFrame, (1 + last - I->NFrame));
1320 UtilZeroMem((void *) (I->ViewElem + I->NFrame),
1321 sizeof(CViewElem) * (1 + last - I->NFrame));
1322 }
1323 }
1324 break;
1325 case 5: /* reset */
1326 if(I->ViewElem) {
1327 int size = VLAGetSize(I->ViewElem);
1328 I->ViewElem = pymol::vla<CViewElem>(size);
1329 }
1330 break;
1331 case 6: /* uninterpolate */
1332 if(I->ViewElem) {
1333 if(first < 0)
1334 first = 0;
1335 if(last < 0) {
1336 last = SceneGetNFrame(G, NULL) - 1;
1337 }
1338 for(frame = first; frame <= last; frame++) {
1339 if((frame >= 0) && (frame < I->NFrame)) {
1340 VLACheck(I->ViewElem, CViewElem, frame);
1341 if(I->ViewElem[frame].specification_level < 2) {
1342 ViewElemArrayPurge(G, I->ViewElem + frame, 1);
1343 UtilZeroMem((void *) (I->ViewElem + frame), sizeof(CViewElem));
1344 }
1345 }
1346 }
1347 }
1348 break;
1349 }
1350 /* adjust VLA to current movie length */
1351 if(I->ViewElem) {
1352 VLASize(I->ViewElem,CViewElem,I->NFrame);
1353 }
1354 SceneSetFrame(G,1,0); /* force frame update */
1355 return 1;
1356 }
1357
1358
1359 /*========================================================================*/
MovieAppendCommand(PyMOLGlobals * G,int frame,const char * command)1360 void MovieAppendCommand(PyMOLGlobals * G, int frame, const char* command)
1361 {
1362 CMovie *I = G->Movie;
1363 if((frame >= 0) && (frame < I->NFrame)) {
1364 I->Cmd[frame] += command;
1365 } else {
1366 PRINTFB(G, FB_Movie, FB_Errors)
1367 " Movie-Error: frame %d does not exist. Use 'mset' to define movie first.\n",
1368 frame + 1 ENDFB(G);
1369 }
1370 }
1371
1372
1373 /*========================================================================*/
MovieGetImage(PyMOLGlobals * G,int index)1374 std::shared_ptr<pymol::Image> MovieGetImage(PyMOLGlobals * G, int index)
1375 {
1376 CMovie *I = G->Movie;
1377 if((index >= 0) && (index < I->NImage))
1378 return I->Image[index];
1379 else
1380 return nullptr;
1381 }
1382
1383
1384 /*========================================================================*/
MovieDefined(PyMOLGlobals * G)1385 int MovieDefined(PyMOLGlobals * G)
1386 {
1387 CMovie *I = G->Movie;
1388 return (I->NFrame > 0);
1389 }
1390
1391
1392 /*========================================================================*/
MovieGetLength(PyMOLGlobals * G)1393 int MovieGetLength(PyMOLGlobals * G)
1394 {
1395 CMovie *I = G->Movie;
1396 int len;
1397 if(!I->NFrame)
1398 len = -I->NImage;
1399 else
1400 len = I->NFrame;
1401 return (len);
1402 }
1403
1404
1405 /*========================================================================*/
MovieClearImages(PyMOLGlobals * G,CMovie * I)1406 void MovieClearImages(PyMOLGlobals * G, CMovie* I)
1407 {
1408 I->Image.clear();
1409 I->NImage = 0;
1410 SceneInvalidate(G);
1411 SceneSuppressMovieFrame(G);
1412 }
1413
MovieClearImages(PyMOLGlobals * G)1414 void MovieClearImages(PyMOLGlobals * G)
1415 {
1416 PRINTFB(G, FB_Movie, FB_Blather)
1417 " MovieClearImages: clearing...\n" ENDFB(G);
1418 MovieClearImages(G, G->Movie);
1419 }
1420
1421 /*========================================================================*/
MovieReset(PyMOLGlobals * G)1422 void MovieReset(PyMOLGlobals * G)
1423 {
1424 CMovie *I = G->Movie;
1425 MovieClearImages(G);
1426
1427 I->Cmd.clear();
1428 VLAFreeP(I->Sequence);
1429 VLAFreeP(I->ViewElem);
1430
1431 I->NFrame = 0;
1432 I->MatrixFlag = false;
1433 I->Locked = false;
1434 I->Playing = false;
1435 }
1436
1437
1438 /*========================================================================*/
~CMovie()1439 CMovie::~CMovie()
1440 {
1441 MovieClearImages(m_G, this);
1442 }
1443
MovieGetBlock(PyMOLGlobals * G)1444 Block *MovieGetBlock(PyMOLGlobals * G)
1445 {
1446 return G->Movie;
1447 }
1448
1449
MoviePrepareDrag(PyMOLGlobals * G,BlockRect * rect,CObject * obj,int mode,int x,int y,int nearest)1450 void MoviePrepareDrag(PyMOLGlobals *G, BlockRect * rect,
1451 CObject * obj, int mode, int x, int y, int nearest)
1452 {
1453 CMovie *I = G->Movie;
1454 I->DragMode = mode;
1455 I->DragObj = obj;
1456 I->DragX = x;
1457 I->DragY = y;
1458 I->DragRect = *rect;
1459 if(I->DragColumn) {
1460 I->DragRect.top = I->rect.top - 1;
1461 I->DragRect.bottom = I->rect.bottom + 1;
1462 }
1463 I->DragStartFrame = ViewElemXtoFrame(rect,MovieGetLength(G),x,nearest);
1464 if(I->DragStartFrame > MovieGetLength(G))
1465 I->DragStartFrame = MovieGetLength(G);
1466 I->DragCurFrame = ViewElemXtoFrame(rect,MovieGetLength(G),x,nearest);
1467 I->DragNearest = nearest;
1468 }
1469
click(int button,int x,int y,int mod)1470 int CMovie::click(int button, int x, int y, int mod)
1471 {
1472 PyMOLGlobals *G = m_G;
1473 CMovie *I = G->Movie;
1474 int count = ExecutiveCountMotions(G);
1475 short scrolldir = 1;
1476 BlockRect tmpRect = rect;
1477 tmpRect.right -= I->LabelIndent;
1478
1479 switch(button) {
1480 case P_GLUT_RIGHT_BUTTON:
1481 {
1482 int n_frame = MovieGetLength(G);
1483 if(mod == (cOrthoCTRL | cOrthoSHIFT))
1484 I->DragColumn = true;
1485 if(mod == (cOrthoSHIFT))
1486 ExecutiveMotionClick(G,&tmpRect,cMovieDragModeCopyKey,count,x,y,false);
1487 else
1488 ExecutiveMotionClick(G,&tmpRect,cMovieDragModeMoveKey,count,x,y,false);
1489 if(I->DragStartFrame<n_frame) {
1490 I->DragDraw = true;
1491 I->DragMenu = true;
1492 OrthoDirty(G);
1493 } else {
1494 ExecutiveMotionMenuActivate(G,&tmpRect,count,false,x,y,I->DragColumn);
1495 }
1496 }
1497 break;
1498 case P_GLUT_LEFT_BUTTON:
1499 {
1500 switch(mod) {
1501 case cOrthoSHIFT: /* TEMPORAL SELECTIONS -- TO COME in PYMOL 1.3+ */
1502 break;
1503 case (cOrthoSHIFT | cOrthoCTRL):
1504 I->DragColumn = true;
1505 ExecutiveMotionClick(G,&tmpRect,cMovieDragModeInsDel,count,x,y, true);
1506 I->DragDraw = true;
1507 OrthoDirty(G);
1508 break;
1509 case cOrthoCTRL:
1510 ExecutiveMotionClick(G,&tmpRect,cMovieDragModeInsDel,count,x,y, true);
1511 I->DragDraw = true;
1512 OrthoDirty(G);
1513 break;
1514 default:
1515 I->m_ScrollBar.click(button, x, y, mod);
1516 {
1517 SceneSetFrame(G, 7, I->m_ScrollBar.getValue());
1518 }
1519 break;
1520 }
1521 }
1522 break;
1523 case P_GLUT_MIDDLE_BUTTON:
1524 {
1525 switch(mod) {
1526 case (cOrthoCTRL | cOrthoSHIFT):
1527 I->DragColumn = true;
1528 /* intentional fall-through */
1529 case cOrthoCTRL:
1530 I->DragDraw = true;
1531 ExecutiveMotionClick(G,&tmpRect,cMovieDragModeOblate,count,x,y,false);
1532 break;
1533 default:
1534 I->m_ScrollBar.click(button, x, y, mod);
1535 break;
1536 }
1537 }
1538 break;
1539 case P_GLUT_BUTTON_SCROLL_FORWARD:
1540 scrolldir = -1;
1541 case P_GLUT_BUTTON_SCROLL_BACKWARD:
1542 switch(mod) {
1543 case (cOrthoCTRL | cOrthoSHIFT):
1544 SettingSetGlobal_i(G, cSetting_movie_panel_row_height,
1545 SettingGetGlobal_i(G, cSetting_movie_panel_row_height) - scrolldir);
1546 OrthoReshape(G,-1,-1,true);
1547 break;
1548 default:
1549 SceneSetFrame(G, 5, scrolldir);
1550 }
1551 break;
1552 }
1553 return 1;
1554 }
1555
drag(int x,int y,int mod)1556 int CMovie::drag(int x, int y, int mod)
1557 {
1558 PyMOLGlobals *G = m_G;
1559
1560 CMovie *I = this; // TODO: Remove all I's in Movie refactor
1561 if(I->DragMode) {
1562 I->DragDraw = ((y < (rect.top + 50)) && (y > (rect.bottom - 50)));
1563 switch(I->DragMode) {
1564 case cMovieDragModeMoveKey:
1565 case cMovieDragModeCopyKey:
1566 {
1567 int n_frame = MovieGetLength(G);
1568 I->DragCurFrame = ViewElemXtoFrame(&I->DragRect,n_frame,x,false);
1569 if(I->DragStartFrame<n_frame) {
1570 if((abs(x-I->DragX)>3) ||
1571 (abs(y-I->DragY)>5)) {
1572 I->DragMenu = false;
1573 }
1574 OrthoDirty(G);
1575 }
1576 }
1577 break;
1578 case cMovieDragModeOblate:
1579 I->DragCurFrame = ViewElemXtoFrame(&I->DragRect,MovieGetLength(G),x,false);
1580 OrthoDirty(G);
1581 break;
1582 case cMovieDragModeInsDel:
1583 I->DragCurFrame = ViewElemXtoFrame(&I->DragRect,MovieGetLength(G),x,true);
1584 OrthoDirty(G);
1585 break;
1586 }
1587 }
1588 return 1;
1589 }
1590
release(int button,int x,int y,int mod)1591 int CMovie::release(int button, int x, int y, int mod)
1592 {
1593 PyMOLGlobals *G = m_G;
1594 CMovie *I = G->Movie;
1595 I->m_ScrollBar.release(button, x, y, mod);
1596 if(I->DragMode) {
1597 std::string buffer;
1598 std::string extra;
1599
1600 int n_frame = MovieGetLength(G);
1601
1602 if(I->DragColumn) {
1603 extra = ",object=''";
1604 } else if(I->DragObj && ExecutiveValidateObjectPtr(G,I->DragObj,0)) {
1605 extra = pymol::string_format(",object='%s'",I->DragObj->Name);
1606 } else {
1607 extra = ",object='none'";
1608 }
1609 switch(I->DragMode) {
1610 case cMovieDragModeMoveKey:
1611 if((I->DragCurFrame == I->DragStartFrame) && (I->DragMenu)) {
1612 int count = ExecutiveCountMotions(G);
1613 BlockRect tmpRect = rect;
1614 tmpRect.right -= I->LabelIndent;
1615 ExecutiveMotionMenuActivate(G,&tmpRect,count,true,x,y,I->DragColumn);
1616 I->DragMenu = false;
1617 } else if(I->DragDraw &&
1618 (I->DragCurFrame!=I->DragStartFrame) &&
1619 (I->DragCurFrame >= 0) &&
1620 (I->DragCurFrame < n_frame)) {
1621 buffer = pymol::string_format("cmd.mmove(%d,%d,%d%s)", 1+I->DragCurFrame, 1+I->DragStartFrame, 1, extra);
1622 }
1623 break;
1624 case cMovieDragModeCopyKey:
1625 if((I->DragCurFrame == I->DragStartFrame) && (I->DragMenu)) {
1626 int count = ExecutiveCountMotions(G);
1627 BlockRect tmpRect = rect;
1628 tmpRect.right -= I->LabelIndent;
1629 ExecutiveMotionMenuActivate(G,&tmpRect,count,true,x,y,I->DragColumn);
1630 I->DragMenu = false;
1631 } else if(I->DragDraw &&
1632 (I->DragCurFrame!=I->DragStartFrame) &&
1633 (I->DragCurFrame >= 0) &&
1634 (I->DragCurFrame < n_frame)) {
1635 buffer = pymol::string_format("cmd.mcopy(%d,%d,%d%s)", 1+I->DragCurFrame, 1+I->DragStartFrame, 1, extra);
1636 }
1637 break;
1638 case cMovieDragModeOblate:
1639 if(I->DragDraw) {
1640 int min_frame = (I->DragStartFrame < I->DragCurFrame) ? I->DragStartFrame : I->DragCurFrame;
1641 int max_frame = (I->DragStartFrame > I->DragCurFrame) ? I->DragStartFrame : I->DragCurFrame;
1642 if(min_frame<0) min_frame = 0;
1643 if(max_frame<0) max_frame = 0;
1644 if(min_frame>=n_frame) min_frame = n_frame - 1;
1645 if(max_frame>=n_frame) max_frame = n_frame - 1;
1646 if(I->DragColumn) {
1647 extra = ",object='same'";
1648 }
1649 buffer = pymol::string_format("cmd.mview('clear',first=%d,last=%d%s)",
1650 1 + min_frame, 1 + max_frame, extra);
1651 }
1652 break;
1653 case cMovieDragModeInsDel:
1654 if(I->DragDraw) {
1655 if(I->DragCurFrame<0)
1656 I->DragCurFrame = 0;
1657 if(I->DragCurFrame>I->DragStartFrame) {
1658 int first = I->DragStartFrame + 1;
1659 if(first<0) first = 0;
1660 buffer = pymol::string_format("cmd.minsert(%d,%d%s)", I->DragCurFrame - I->DragStartFrame, first, extra);
1661 } else {
1662 int first = I->DragCurFrame;
1663 if(first<0) first = 0;
1664 buffer = pymol::string_format("cmd.mdelete(%d,%d%s)", I->DragStartFrame - I->DragCurFrame, first+1, extra);
1665 }
1666 }
1667 break;
1668 }
1669 if(!buffer.empty()) {
1670 PParse(G, buffer.c_str());
1671 PFlush(G);
1672 PLog(G, buffer.c_str(), cPLog_pym);
1673 }
1674 }
1675 I->DragMode = 0;
1676 I->DragDraw = false;
1677 I->DragMenu = false;
1678 I->DragColumn = false;
1679
1680 return 1;
1681 }
1682
MovieGetPanelHeight(PyMOLGlobals * G)1683 int MovieGetPanelHeight(PyMOLGlobals * G)
1684 {
1685 int movie_panel = SettingGetGlobal_i(G, cSetting_movie_panel);
1686 CMovie *I = G->Movie;
1687 if(movie_panel != 0) {
1688 if(MovieGetLength(G)) {
1689 movie_panel = 1;
1690 } else {
1691 movie_panel = SceneGetNFrame(G) > 1;
1692 }
1693 }
1694
1695 if(movie_panel) {
1696 int row_height = DIP2PIXEL(SettingGetGlobal_i(G,cSetting_movie_panel_row_height));
1697 I->PanelActive = true;
1698 if(SettingGetGlobal_b(G, cSetting_presentation)) {
1699 /* show camera line only when in presentation mode */
1700 return row_height;
1701 } else {
1702 return row_height * ExecutiveCountMotions(G);
1703 }
1704 } else {
1705 I->PanelActive = false;
1706 return 0;
1707 }
1708 }
1709
MovieDrawViewElem(PyMOLGlobals * G,BlockRect * rect,int frames ORTHOCGOARG)1710 void MovieDrawViewElem(PyMOLGlobals *G, BlockRect *rect,int frames ORTHOCGOARG)
1711 {
1712 CMovie *I = G->Movie;
1713 if(I->ViewElem) {
1714 ViewElemDraw(G,I->ViewElem,rect,frames,"camera" ORTHOCGOARGVAR);
1715 }
1716 }
1717
fastDraw(CGO * orthoCGO)1718 bool CMovie::fastDraw(CGO* orthoCGO)
1719 {
1720 return true;
1721 }
1722
draw(CGO * orthoCGO)1723 void CMovie::draw(CGO* orthoCGO)
1724 {
1725 PyMOLGlobals *G = m_G;
1726 CMovie *I = G->Movie;
1727 if(I->PanelActive) {
1728 int n_frame = SceneGetNFrame(G);
1729 int frame = SceneGetFrame(G);
1730 int count = ExecutiveCountMotions(G);
1731 BlockRect tmpRect = rect;
1732 if(count) {
1733 tmpRect.right -= I->LabelIndent;
1734
1735 if(G->HaveGUI && G->ValidContext) {
1736 float black[3] = {0.0F,0.0F,0.0F};
1737 if (orthoCGO){
1738 CGOColorv(orthoCGO, black);
1739 CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
1740 CGOVertex(orthoCGO, tmpRect.right, tmpRect.bottom, 0.f);
1741 CGOVertex(orthoCGO, tmpRect.right, tmpRect.top, 0.f);
1742 CGOVertex(orthoCGO, rect.right, tmpRect.bottom, 0.f);
1743 CGOVertex(orthoCGO, rect.right, tmpRect.top, 0.f);
1744 CGOEnd(orthoCGO);
1745 } else {
1746 glColor3fv(black);
1747 glBegin(GL_POLYGON);
1748 glVertex2f(tmpRect.right, tmpRect.bottom);
1749 glVertex2f(tmpRect.right, tmpRect.top);
1750 glVertex2f(rect.right, tmpRect.top);
1751 glVertex2f(rect.right, tmpRect.bottom);
1752 glEnd();
1753 }
1754 }
1755
1756 if(!n_frame) {
1757 I->m_ScrollBar.setLimits(1, 1);
1758 I->m_ScrollBar.setValue(0);
1759 } else {
1760 float scroll_value = I->m_ScrollBar.getValue();
1761 int new_frame = (int) (scroll_value + 0.5F);
1762 if(I->m_ScrollBar.grabbed()) {
1763 if(new_frame != frame) {
1764 frame = new_frame;
1765 SceneSetFrame(G, 7, frame);
1766 }
1767 }
1768 I->m_ScrollBar.setLimits(n_frame, 1);
1769 }
1770 I->m_ScrollBar.setBox(tmpRect.top, tmpRect.left,
1771 tmpRect.bottom, tmpRect.right);
1772 {
1773 I->m_ScrollBar.draw(orthoCGO);
1774 ExecutiveMotionDraw(G,&tmpRect,count ORTHOCGOARGVAR);
1775 I->m_ScrollBar.drawHandle(0.35F, orthoCGO);
1776 }
1777
1778 /* drag selection box */
1779 if(I->DragDraw) {
1780
1781 float white[4] = {1.0F, 1.0F, 1.0F,0.5F};
1782
1783 switch(I->DragMode) {
1784 case cMovieDragModeMoveKey:
1785 case cMovieDragModeCopyKey:
1786 {
1787 float grey[4] = {0.75F,0.75F,0.75f,0.5};
1788 if(I->DragStartFrame<n_frame)
1789 ViewElemDrawBox(G,&I->DragRect, I->DragStartFrame, I->DragStartFrame+1, n_frame, white, false ORTHOCGOARGVAR);
1790 if((I->DragCurFrame>=0) && (I->DragCurFrame<n_frame)) {
1791 ViewElemDrawBox(G,&I->DragRect, I->DragCurFrame, I->DragCurFrame+1, n_frame, grey, true ORTHOCGOARGVAR);
1792 }
1793 }
1794 break;
1795 case cMovieDragModeOblate:
1796 {
1797 float grey[4] = {0.75F,0.75F,0.75f,0.5};
1798
1799 int min_frame = (I->DragStartFrame < I->DragCurFrame) ? I->DragStartFrame : I->DragCurFrame;
1800 int max_frame = (I->DragStartFrame > I->DragCurFrame) ? I->DragStartFrame : I->DragCurFrame;
1801 if(min_frame<0) min_frame = 0;
1802 if(max_frame<0) max_frame = 0;
1803 if(min_frame>=n_frame) min_frame = n_frame - 1;
1804 if(max_frame>=n_frame) max_frame = n_frame - 1;
1805 ViewElemDrawBox(G,&I->DragRect, min_frame, max_frame+1, n_frame, white, false ORTHOCGOARGVAR);
1806 ViewElemDrawBox(G,&I->DragRect, min_frame, max_frame+1, n_frame, grey, true ORTHOCGOARGVAR);
1807 }
1808 break;
1809 case cMovieDragModeInsDel:
1810 if(I->DragCurFrame==I->DragStartFrame) {
1811 ViewElemDrawBox(G,&I->DragRect, I->DragStartFrame, I->DragStartFrame, n_frame, white, true ORTHOCGOARGVAR);
1812 } else if(I->DragCurFrame>=I->DragStartFrame) {
1813 float green[4] = {0.5F, 1.0F, 0.5F,0.5F};
1814 ViewElemDrawBox(G,&I->DragRect, I->DragStartFrame, I->DragCurFrame, n_frame, green, true ORTHOCGOARGVAR);
1815 } else {
1816 float red[4] = {1.0F, 0.5F, 0.5F,0.5F};
1817 ViewElemDrawBox(G,&I->DragRect, I->DragCurFrame, I->DragStartFrame, n_frame, red, true ORTHOCGOARGVAR);
1818 }
1819 break;
1820 }
1821
1822 }
1823
1824 if (!ViewElem) {
1825 ViewElemDrawLabel(G, "states", &tmpRect, orthoCGO);
1826 }
1827 }
1828 }
1829 }
1830
MovieSetScrollBarFrame(PyMOLGlobals * G,int frame)1831 void MovieSetScrollBarFrame(PyMOLGlobals * G, int frame)
1832 {
1833 CMovie *I = G->Movie;
1834 if(!I->m_ScrollBar.grabbed()) {
1835 I->m_ScrollBar.setValue(frame);
1836 }
1837 }
1838
reshape(int width,int height)1839 void CMovie::reshape(int width, int height)
1840 {
1841 PyMOLGlobals *G = m_G;
1842 CMovie *I = G->Movie;
1843 Block::reshape(width, height);
1844 I->Width = rect.right - rect.left + 1;
1845 I->Height = rect.top - rect.bottom + 1;
1846 if(SettingGetGlobal_b(G, cSetting_presentation)) {
1847 I->LabelIndent = 0;
1848 } else {
1849 I->LabelIndent = DIP2PIXEL(8 * 8);
1850 }
1851 }
1852
1853