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_gl.h"
20 
21 #include"Base.h"
22 #include"OOMac.h"
23 #include"main.h"
24 #include"View.h"
25 #include"Ray.h"
26 #include"Setting.h"
27 #include"PConv.h"
28 #include"OVLexicon.h"
29 #include"Text.h"
30 #include"Feedback.h"
31 #include"Ortho.h"
32 #include"CGO.h"
33 
ViewElemModify(PyMOLGlobals * G,CViewElem ** handle,int action,int index,int count,int target)34 int ViewElemModify(PyMOLGlobals *G, CViewElem **handle, int action, int index, int count, int target)
35 {
36   int ok = true;
37   CViewElem *vla = *handle;
38   if(!vla) {
39     vla = VLACalloc(CViewElem, 0);
40   }
41   if(vla) {
42     int n_frame = VLAGetSize(vla);
43     switch(action) {
44     case cViewElemModifyInsert:
45       VLAInsert(vla,CViewElem,index,count);
46       break;
47     case cViewElemModifyDelete:
48       VLADelete(vla,CViewElem,index,count);
49       break;
50     case cViewElemModifyMove:
51       if((index>=0) && (target>=0) && (index<n_frame) && (target<n_frame)) {
52         if((count>1)||(vla[index].specification_level>1)) {
53 
54           int i;
55           for(i=0;i<count;i++) {
56             if( ((i+index)<n_frame) && ((i+target)<n_frame)) {
57               int src,dst;
58               if(index>target) {
59                 src = index+i;
60                 dst = target+i;
61               } else {
62                 src = index+(count-1)-i;
63                 dst = target+(count-1)-i;
64               }
65               memcpy(vla + dst, vla + src, sizeof(CViewElem));
66               memset(vla + src, 0, sizeof(CViewElem));
67             }
68           }
69         }
70       }
71       break;
72     case cViewElemModifyCopy:
73       if((index>=0) && (target>=0) && (index<n_frame) && (target<n_frame)) {
74         if((count>1)||(vla[index].specification_level>1)) {
75           int i;
76           for(i=0;i<count;i++) {
77             if( ((i+index)<n_frame) && ((i+target)<n_frame)) {
78               int src,dst;
79               if(index>target) {
80                 src = index+i;
81                 dst = target+i;
82               } else {
83                 src = index+(count-1)-i;
84                 dst = target+(count-1)-i;
85               }
86             memcpy(vla + dst, vla + src, sizeof(CViewElem));
87             }
88           }
89         }
90       }
91       break;
92     }
93   }
94   *handle = vla;
95   return ok;
96 }
97 
ViewElemXtoFrame(BlockRect * rect,int frames,int x,int nearest)98 int ViewElemXtoFrame(BlockRect *rect, int frames, int x, int nearest)
99 {
100   int offset = 0;
101   float width = (float) (rect->right - rect->left);
102   float extra = (nearest ? 0.4999F : 0.0F);
103   int frame = (int)(extra + (frames * (x - rect->left )) / width + offset);
104   return frame;
105 }
106 
ViewElemDrawBox(PyMOLGlobals * G,BlockRect * rect,int first,int last,int frames,float * color4,int fill ORTHOCGOARG)107 void ViewElemDrawBox(PyMOLGlobals *G, BlockRect *rect, int first, int last,
108                      int frames, float *color4,int fill ORTHOCGOARG)
109 {
110   if(G->HaveGUI && G->ValidContext && rect) {
111     int nDrawn = frames;
112     int offset = 0;
113     float width = (float) (rect->right - rect->left);
114     float top = rect->top - 1;
115     float bot = rect->bottom + 1;
116     float start = (int)(rect->left + (width * (first - offset)) / nDrawn);
117     float stop = (int)(rect->left + (width * (last - offset)) / nDrawn);
118     if((stop - start) < 1.0F)
119       stop = start+1.0F;
120     if(fill) {
121       glEnable(GL_BLEND);
122       if (orthoCGO){
123 	float prevAlpha = orthoCGO->alpha;
124 	CGOAlpha(orthoCGO, color4[3]);
125 	CGOColorv(orthoCGO, color4);
126 	CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
127 	CGOVertex(orthoCGO, start, bot, 0.f);
128 	CGOVertex(orthoCGO, start, top, 0.f);
129 	CGOVertex(orthoCGO, stop, bot, 0.f);
130 	CGOVertex(orthoCGO, stop, top, 0.f);
131 	CGOEnd(orthoCGO);
132 	CGOAlpha(orthoCGO, prevAlpha);
133       } else {
134 	glColor4fv(color4);
135 	glBegin(GL_POLYGON);
136 	glVertex2f(start, bot);
137 	glVertex2f(start, top);
138 	glVertex2f(stop, top);
139 	glVertex2f(stop, bot);
140 	glEnd();
141       }
142       glDisable(GL_BLEND);
143     } else {
144       if (orthoCGO){
145 	CGOLineAsTriangleStrips(orthoCGO, start, bot, stop, top);
146       } else {
147 	glBegin(GL_LINE_LOOP);
148 	glVertex2f(start, bot);
149 	glVertex2f(start, top);
150 	glVertex2f(stop, top);
151 	glVertex2f(stop, bot);
152 	glEnd();
153       }
154     }
155   }
156 }
157 
ViewElemDraw(PyMOLGlobals * G,const CViewElem * view_elem,const BlockRect * rect,int frames,const char * title ORTHOCGOARG)158 void ViewElemDraw(PyMOLGlobals *G,
159     const CViewElem * view_elem,
160     const BlockRect *rect, int frames,
161     const char *title ORTHOCGOARG)
162 {
163   if(G->HaveGUI && G->ValidContext && view_elem) {
164     int size = VLAGetSize(view_elem);
165     float width = (float) (rect->right - rect->left);
166     float start = 0.0F, stop;
167     const int last = size;
168     float top = rect->top - 2;
169     float bot = rect->bottom + 2;
170     float mid_top = (int)((0.499F + 3 * top + 2 * bot) / 5);
171     float mid_bot = (int)((0.499F + 2 * top + 3 * bot) / 5);
172     float top_color[3] = { 0.6, 0.6, 1.0 };
173     float key_color[3] = { 0.4, 0.4, 0.8 };
174     float bar_color[3] = { 0.3, 0.3, 0.6 };
175     float bot_color[3] = { 0.2, 0.2, 0.4 };
176     int cur_level = -1, last_level = -1;
177     int cur;
178     for(cur = 0; cur <= last; cur++) {
179       if(cur < last) {
180           cur_level = view_elem->specification_level;
181       } else {
182         cur_level = -1;
183       }
184       if(cur_level != last_level) {
185         stop = (int)(rect->left + (width * cur) / frames);
186         switch (last_level) {
187         case 0:
188           break;
189         case 1:
190 	  if (orthoCGO){
191 	    CGOColorv(orthoCGO, bar_color);
192 	    CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
193 	    CGOVertex(orthoCGO, start, mid_bot, 0.f);
194 	    CGOVertex(orthoCGO, start, mid_top, 0.f);
195 	    CGOVertex(orthoCGO, stop, mid_bot, 0.f);
196 	    CGOVertex(orthoCGO, stop, mid_top, 0.f);
197 	    CGOEnd(orthoCGO);
198 	  } else {
199 	    glColor3fv(bar_color);
200 	    glBegin(GL_POLYGON);
201 	    glVertex2f(start, mid_bot);
202 	    glVertex2f(start, mid_top);
203 	    glVertex2f(stop, mid_top);
204 	    glVertex2f(stop, mid_bot);
205 	    glEnd();
206 	  }
207 	  if (orthoCGO){
208 	    CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
209 	    CGOColorv(orthoCGO, key_color);
210 	    CGOVertex(orthoCGO, start, mid_top, 0.f);
211 	    CGOVertex(orthoCGO, start, mid_top+1, 0.f);
212 	    CGOVertex(orthoCGO, stop, mid_top, 0.f);
213 	    CGOVertex(orthoCGO, stop, mid_top+1, 0.f);
214 	    CGOEnd(orthoCGO);
215 	    CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
216 	    CGOColorv(orthoCGO, bot_color);
217 	    CGOVertex(orthoCGO, start, mid_bot-1, 0.f);
218 	    CGOVertex(orthoCGO, start, mid_bot, 0.f);
219 	    CGOVertex(orthoCGO, stop, mid_bot-1, 0.f);
220 	    CGOVertex(orthoCGO, stop, mid_bot, 0.f);
221 	    CGOEnd(orthoCGO);
222 	  } else {
223 	    glColor3fv(key_color);
224 	    glBegin(GL_LINES);
225 	    glVertex2f(start,mid_top);
226 	    glVertex2f(stop,mid_top);
227 	    glColor3fv(bot_color);
228 	    glVertex2f(start,mid_bot-1);
229 	    glVertex2f(stop,mid_bot-1);
230 	    glEnd();
231 	  }
232           break;
233         case 2:
234           if((stop - start) < 1.0F)
235             stop = start+1.0F;
236 	  if (orthoCGO){
237 	    CGOColorv(orthoCGO, key_color);
238 	    CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
239 	    CGOVertex(orthoCGO, start, bot, 0.f);
240 	    CGOVertex(orthoCGO, start, top, 0.f);
241 	    CGOVertex(orthoCGO, stop, bot, 0.f);
242 	    CGOVertex(orthoCGO, stop, top, 0.f);
243 	    CGOEnd(orthoCGO);
244 	  } else {
245 	    glColor3fv(key_color);
246 	    glBegin(GL_POLYGON);
247 	    glVertex2f(start, bot);
248 	    glVertex2f(start, top);
249 	    glVertex2f(stop, top);
250 	    glVertex2f(stop, bot);
251 	    glEnd();
252 	  }
253 
254 	  if (orthoCGO){
255 	    CGOColorv(orthoCGO, bot_color);
256 	    CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
257 	    CGOVertex(orthoCGO, start,bot-1,0.f);
258 	    CGOVertex(orthoCGO, start,bot,0.f);
259 	    CGOVertex(orthoCGO, stop,bot-1,0.f);
260 	    CGOVertex(orthoCGO, stop,bot,0.f);
261 	    CGOEnd(orthoCGO);
262 	    CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
263 	    CGOVertex(orthoCGO, stop,bot,0.f);
264 	    CGOVertex(orthoCGO, stop,top,0.f);
265 	    CGOVertex(orthoCGO, stop+1,bot,0.f);
266 	    CGOVertex(orthoCGO, stop+1,top,0.f);
267 	    CGOEnd(orthoCGO);
268 
269 	    CGOColorv(orthoCGO, top_color);
270 	    CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
271 	    CGOVertex(orthoCGO, start,top,0.f);
272 	    CGOVertex(orthoCGO, start,top+1,0.f);
273 	    CGOVertex(orthoCGO, stop,top,0.f);
274 	    CGOVertex(orthoCGO, stop,top+1,0.f);
275 	    CGOEnd(orthoCGO);
276 
277 	    CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
278 	    CGOVertex(orthoCGO, start,bot,0.f);
279 	    CGOVertex(orthoCGO, start,top,0.f);
280 	    CGOVertex(orthoCGO, start+1,bot,0.f);
281 	    CGOVertex(orthoCGO, start+1,top,0.f);
282 	    CGOEnd(orthoCGO);
283 	  } else {
284 	    glBegin(GL_LINES);
285 	    glColor3fv(bot_color);
286 	    glVertex2f(start,bot-1);
287 	    glVertex2f(stop,bot-1);
288 	    glVertex2f(stop,bot);
289 	    glVertex2f(stop,top);
290 	    glColor3fv(top_color);
291 	    glVertex2f(start,top);
292 	    glVertex2f(stop,top);
293 	    glVertex2f(start,bot);
294 	    glVertex2f(start,top);
295 	    glEnd();
296 	  }
297           break;
298         }
299         start = stop;
300       }
301       last_level = cur_level;
302       view_elem++;
303     }
304 
305     if(title)
306       ViewElemDrawLabel(G, title, rect, orthoCGO);
307   }
308 }
309 
ViewElemDrawLabel(PyMOLGlobals * G,const char * label,const BlockRect * rect,CGO * orthoCGO)310 void ViewElemDrawLabel(
311     PyMOLGlobals* G, const char* label, const BlockRect* rect, CGO* orthoCGO)
312 {
313   TextDrawStrAt(
314       G, label, rect->right + 1, (rect->bottom + rect->top) / 2 - 3, orthoCGO);
315 }
316 
ViewElemCopy(PyMOLGlobals * G,const CViewElem * src,CViewElem * dst)317 void ViewElemCopy(PyMOLGlobals * G, const CViewElem * src, CViewElem * dst)
318 {
319   if(dst->scene_flag && dst->scene_name) {
320     OVLexicon_DecRef(G->Lexicon, dst->scene_name);
321   }
322   *dst = *src;
323   if(dst->scene_flag && dst->scene_name) {
324     OVLexicon_IncRef(G->Lexicon, dst->scene_name);
325   }
326 }
327 
ViewElemArrayPurge(PyMOLGlobals * G,CViewElem * view,int nFrame)328 void ViewElemArrayPurge(PyMOLGlobals * G, CViewElem * view, int nFrame)
329 {
330   int a;
331   for(a = 0; a < nFrame; a++) {
332     if(view->scene_flag && view->scene_name) {
333       OVLexicon_DecRef(G->Lexicon, view->scene_name);
334       view->scene_name = 0;
335       view->scene_flag = false;
336     }
337     view++;
338   }
339 }
340 
ViewElemAsPyList(PyMOLGlobals * G,const CViewElem * view)341 PyObject *ViewElemAsPyList(PyMOLGlobals * G, const CViewElem * view)
342 {
343 #ifdef _PYMOL_NOPY
344   return NULL;
345 #else
346   PyObject *result = NULL;
347 
348   result = PyList_New(21);
349 
350   if(result) {
351     PyList_SetItem(result, 0, PyInt_FromLong(view->matrix_flag));
352     if(view->matrix_flag) {
353       PyList_SetItem(result, 1, PConvDoubleArrayToPyList(view->matrix, 16));
354     } else {
355       PyList_SetItem(result, 1, PConvAutoNone(NULL));
356     }
357 
358     PyList_SetItem(result, 2, PyInt_FromLong(view->pre_flag));
359     if(view->pre_flag) {
360       PyList_SetItem(result, 3, PConvDoubleArrayToPyList(view->pre, 3));
361     } else {
362       PyList_SetItem(result, 3, PConvAutoNone(NULL));
363     }
364 
365     PyList_SetItem(result, 4, PyInt_FromLong(view->post_flag));
366     if(view->post_flag) {
367       PyList_SetItem(result, 5, PConvDoubleArrayToPyList(view->post, 3));
368     } else {
369       PyList_SetItem(result, 5, PConvAutoNone(NULL));
370     }
371 
372     PyList_SetItem(result, 6, PyInt_FromLong(view->clip_flag));
373     if(view->post_flag) {
374       PyList_SetItem(result, 7, PyFloat_FromDouble((double) view->front));
375       PyList_SetItem(result, 8, PyFloat_FromDouble((double) view->back));
376     } else {
377       PyList_SetItem(result, 7, PConvAutoNone(NULL));
378       PyList_SetItem(result, 8, PConvAutoNone(NULL));
379     }
380 
381     PyList_SetItem(result, 9, PyInt_FromLong(view->ortho_flag));
382     if(view->ortho_flag) {
383       PyList_SetItem(result, 10, PyFloat_FromDouble(view->ortho));
384     } else {
385       PyList_SetItem(result, 10, PConvAutoNone(NULL));
386     }
387 
388     PyList_SetItem(result, 11, PyInt_FromLong(view->view_mode));
389 
390     PyList_SetItem(result, 12, PyInt_FromLong(view->specification_level));
391 
392     PyList_SetItem(result, 13, PyInt_FromLong(view->scene_flag));
393 
394     if(view->scene_flag && view->scene_name) {
395       char null_st[1] = "";
396       char *st = null_st;
397 
398       st = OVLexicon_FetchCString(G->Lexicon, view->scene_name);
399       PyList_SetItem(result, 14, PyString_FromString(st));
400     } else {
401       PyList_SetItem(result, 14, PyInt_FromLong(0));
402     }
403 
404     PyList_SetItem(result, 15, PyInt_FromLong(view->power_flag));
405     if(view->ortho_flag) {
406       PyList_SetItem(result, 16, PyFloat_FromDouble(view->power));
407     } else {
408       PyList_SetItem(result, 16, PConvAutoNone(NULL));
409     }
410 
411     PyList_SetItem(result, 17, PyInt_FromLong(view->bias_flag));
412     if(view->bias_flag) {
413       PyList_SetItem(result, 18, PyFloat_FromDouble(view->bias));
414     } else {
415       PyList_SetItem(result, 18, PConvAutoNone(NULL));
416     }
417 
418     PyList_SetItem(result, 19, PyInt_FromLong(view->state_flag));
419     if(view->state_flag) {
420       PyList_SetItem(result, 20, PyInt_FromLong(view->state));
421     } else {
422       PyList_SetItem(result, 20, PConvAutoNone(NULL));
423     }
424 
425   }
426 
427   return PConvAutoNone(result);
428 #endif
429 }
430 
ViewElemFromPyList(PyMOLGlobals * G,PyObject * list,CViewElem * view)431 int ViewElemFromPyList(PyMOLGlobals * G, PyObject * list, CViewElem * view)
432 {
433   int ok = true;
434   ov_size ll = 0;
435 
436   if(ok)
437     ok = (list != NULL);
438   if(ok)
439     ok = PyList_Check(list);
440   if(ok)
441     ok = ((ll = PyList_Size(list)) > 11);
442 
443   if(ok)
444     ok = PConvPyIntToInt(PyList_GetItem(list, 0), &view->matrix_flag);
445   if(ok && view->matrix_flag)
446     ok = PConvPyListToDoubleArrayInPlace(PyList_GetItem(list, 1), view->matrix, 16);
447 
448   if(ok)
449     ok = PConvPyIntToInt(PyList_GetItem(list, 2), &view->pre_flag);
450   if(ok && view->pre_flag)
451     ok = PConvPyListToDoubleArrayInPlace(PyList_GetItem(list, 3), view->pre, 3);
452 
453   if(ok)
454     ok = PConvPyIntToInt(PyList_GetItem(list, 4), &view->post_flag);
455   if(ok && view->post_flag)
456     ok = PConvPyListToDoubleArrayInPlace(PyList_GetItem(list, 5), view->post, 3);
457 
458   if(ok)
459     ok = PConvPyIntToInt(PyList_GetItem(list, 6), &view->clip_flag);
460   if(view->post_flag) {
461     if(ok)
462       ok = PConvPyFloatToFloat(PyList_GetItem(list, 7), &view->front);
463     if(ok)
464       ok = PConvPyFloatToFloat(PyList_GetItem(list, 8), &view->back);
465   }
466 
467   if(ok)
468     ok = PConvPyIntToInt(PyList_GetItem(list, 9), &view->ortho_flag);
469   if(ok && view->ortho_flag) {
470     ok = PConvPyFloatToFloat(PyList_GetItem(list, 10), &view->ortho);
471     if(!ok) {
472       int dummy_int;
473       ok = PConvPyIntToInt(PyList_GetItem(list, 10), &dummy_int);
474       view->ortho = dummy_int;
475     }
476   }
477 
478   if(ok)
479     ok = PConvPyIntToInt(PyList_GetItem(list, 11), &view->view_mode);
480   if(ok)
481     ok = PConvPyIntToInt(PyList_GetItem(list, 12), &view->specification_level);
482 
483   if(ok & (ll > 14)) {
484     if(ok)
485       ok = PConvPyIntToInt(PyList_GetItem(list, 13), &view->scene_flag);
486     if(ok && view->scene_flag) {
487       const char *ptr = NULL;
488       view->scene_flag = false;
489       if(PConvPyStrToStrPtr(PyList_GetItem(list, 14), &ptr)) {
490         OVreturn_word result = OVLexicon_GetFromCString(G->Lexicon, ptr);
491         if(OVreturn_IS_OK(result)) {
492           view->scene_name = result.word;
493           view->scene_flag = true;
494         }
495       }
496     }
497   }
498   if(ok && (ll>16)) {
499     ok = PConvPyIntToInt(PyList_GetItem(list, 15), &view->power_flag);
500     if(ok && view->power_flag) {
501       ok = PConvPyFloatToFloat(PyList_GetItem(list, 16), &view->power);
502     } else {
503       view->power = 0.0F;
504     }
505   }
506   if(ok && (ll>18)) {
507     ok = PConvPyIntToInt(PyList_GetItem(list, 17), &view->bias_flag);
508     if(ok && view->bias_flag) {
509       ok = PConvPyFloatToFloat(PyList_GetItem(list, 18), &view->bias);
510     } else {
511       view->bias = 1.0F;
512     }
513   }
514   if(ok && (ll>20)) {
515     ok = PConvPyIntToInt(PyList_GetItem(list, 19), &view->state_flag);
516     if(ok && view->state_flag) {
517       ok = PConvPyIntToInt(PyList_GetItem(list, 20), &view->state);
518     } else {
519       view->state = 0;
520     }
521   }
522   return ok;
523 }
524 
ViewElemVLAFromPyList(PyMOLGlobals * G,PyObject * list,CViewElem ** vla_ptr,int nFrame)525 int ViewElemVLAFromPyList(PyMOLGlobals * G, PyObject * list, CViewElem ** vla_ptr,
526                           int nFrame)
527 {
528   int ok = true;
529   CViewElem *vla = NULL;
530   if(ok)
531     ok = (list != NULL);
532   if(ok)
533     ok = PyList_Check(list);
534   if(ok)
535     ok = (PyList_Size(list) == nFrame);
536   if(ok)
537     ok = ((vla = VLACalloc(CViewElem, nFrame)) != NULL);
538   if(ok) {
539     int a;
540     for(a = 0; a < nFrame; a++) {
541       if(ok)
542         ok = ViewElemFromPyList(G, PyList_GetItem(list, a), vla + a);
543       else
544         break;
545     }
546   }
547   if(!ok) {
548     VLAFreeP(vla);
549   } else
550     *vla_ptr = vla;
551   return ok;
552 }
553 
ViewElemVLAAsPyList(PyMOLGlobals * G,const CViewElem * vla,int nFrame)554 PyObject *ViewElemVLAAsPyList(PyMOLGlobals * G, const CViewElem * vla, int nFrame)
555 {
556 #ifdef _PYMOL_NOPY
557   return NULL;
558 #else
559 
560   PyObject *result = NULL;
561   int a;
562   result = PyList_New(nFrame);
563   for(a = 0; a < nFrame; a++) {
564     PyList_SetItem(result, a, ViewElemAsPyList(G, vla + a));
565   }
566   return (PConvAutoNone(result));
567 #endif
568 }
569 
ViewNew(PyMOLGlobals * G)570 CView *ViewNew(PyMOLGlobals * G)
571 {
572   OOAlloc(G, CView);
573   I->G = G;
574   I->View = NULL;
575   return I;
576 }
577 
ViewFree(CView * I)578 void ViewFree(CView * I)
579 {
580   if(I)
581     VLAFreeP(I->View);
582 }
583 
ViewGetIterator(CView * I)584 CViewIterator ViewGetIterator(CView * I)
585 {
586   return 0;
587 }
588 
ViewIterate(CView * I,CViewIterator * iter,CRay * ray,int at_least_once)589 int ViewIterate(CView * I, CViewIterator * iter, CRay * ray, int at_least_once)
590 {
591   int result;
592   CViewElem *elem = NULL;
593 
594   if((!I) || (!I->NView)) {     /* trusting short-circuit to avoid segfault */
595     if(at_least_once) {
596       if(!*iter) {              /* do loop at least once if asked to do so */
597         *iter = 1;
598         result = true;
599       } else
600         result = false;
601     } else {
602       result = false;
603     }
604   } else {
605     if(*iter < I->NView) {
606       elem = I->View + (*iter)++;
607       result = true;
608     } else
609       result = false;
610   }
611   if(elem) {                    /* are we to apply a transformation? */
612     if(ray) {
613 
614     } else if(I->G->HaveGUI && I->G->ValidContext) {
615 
616       if(elem->pre_flag) {
617         /* move the camera to the location we are looking at */
618 #ifdef PURE_OPENGL_ES_2
619         /* TODO */
620 #else
621         glTranslated(elem->pre[0], elem->pre[1], elem->pre[2]);
622 #endif
623       }
624 
625       if(elem->matrix_flag) {
626         /* rotate about the origin (the the center of rotation) */
627 #ifdef PURE_OPENGL_ES_2
628         /* TODO */
629 #else
630         glMultMatrixd(elem->matrix);
631 #endif
632       }
633 
634       if(elem->post_flag) {
635         /* move the origin to the center of rotation */
636 #ifdef PURE_OPENGL_ES_2
637         /* TODO */
638 #else
639 	glTranslated(elem->post[0], elem->post[1], elem->post[2]);
640 #endif
641       }
642 
643     }
644   }
645   return result;
646 }
647 
matrix_interpolate(Matrix53f imat,Matrix53f mat,float * pivot_point,float * bisect_dir,float * rot_axis,float rotate_angle,float * trans_axis,float translate_angle,float fxn,float linearity)648 static void matrix_interpolate(Matrix53f imat, Matrix53f mat,
649                                float *pivot_point,
650                                float *bisect_dir,
651                                float *rot_axis,
652                                float rotate_angle,
653                                float *trans_axis,
654                                float translate_angle, float fxn, float linearity)
655 {
656   int a;
657   float pos[3], adj[3], opp[3], oppdir[3];
658   float p0[3], p1[3], center[3];
659   float hyplen, adjlen, opplen;
660   float tAlpha;
661 
662   rotation_to_matrix(imat, rot_axis, fxn * rotate_angle);
663 
664   /*           ______--------______
665    *        /____________          \
666    *     /   \   opp     |adj         \
667    *   |      \          |        trans   |
668    * (CM)---------------------------------->(CM)
669    *    \       \        |               /
670    *       \      \hyp   |-bisect_dir  /
671    *          \p0   \    |        p1/
672    *             \    \  |        /
673    *                \  \ |     /
674    *                   \\v  /
675    *            <--------O pivot
676    *                      F-raxis
677    */
678 
679   subtract3f(&mat[3][0], pivot_point, p0);
680   subtract3f(&mat[4][0], pivot_point, p1);
681 
682   hyplen = (float) length3f(p0);
683 
684   average3f(&mat[3][0], &mat[4][0], center);
685 
686   cross_product3f(bisect_dir, trans_axis, oppdir);
687   normalize3f(oppdir);
688 
689   tAlpha = (float) (fabs(0.5 - fxn) * translate_angle);
690   opplen = (float) fabs(hyplen * sin(tAlpha));
691   adjlen = (float) fabs(hyplen * cos(tAlpha));
692 
693   scale3f(oppdir, opplen, opp);
694   scale3f(bisect_dir, -adjlen, adj);
695 
696   add3f(pivot_point, adj, pos);
697 
698   if(fxn <= 0.5) {
699     add3f(pos, opp, pos);
700   } else {
701     subtract3f(pos, opp, pos);
702   }
703 
704   /* straight linear for now... */
705 
706   for(a = 0; a < 3; a++) {
707     imat[4][a] = (float) ((((1.0 - fxn) * mat[3][a] + fxn * mat[4][a]) * linearity) +
708                           (1.0 - linearity) * pos[a]);
709   }
710 }
711 
ViewElemSmooth(CViewElem * first,CViewElem * last,int window,int loop)712 int ViewElemSmooth(CViewElem * first, CViewElem * last, int window, int loop)
713 {
714   ov_diff n = (last - first) + 1;
715   int delta;
716   if(window > n)
717     window = (int) n;
718   delta = (window - 1) / 2;
719   if(n && delta) {
720     CViewElem *cpy = pymol::malloc<CViewElem>((n + 2 * delta));
721     CViewElem *src, *dst;
722     int a, b, c, cnt;
723     memcpy(cpy + delta, first, sizeof(CViewElem) * n);
724     if(loop) {
725       for(a = 0; a < delta; a++) {
726         memcpy(cpy + a, last - delta + a, sizeof(CViewElem));
727         memcpy(cpy + (delta + n) + a, first + a, sizeof(CViewElem));
728       }
729     } else {
730       for(a = 0; a < delta; a++) {
731         memcpy(cpy + a, first, sizeof(CViewElem));
732         memcpy(cpy + (delta + n) + a, last, sizeof(CViewElem));
733       }
734     }
735     for(a = 0; a < n; a++) {
736       int above, below;
737       dst = first + a;
738 
739       above = delta;
740       below = delta;
741       if(above > a)
742         above = a;
743       if(below > ((n - 1) - a))
744         below = (int) ((n - 1) - a);
745 
746       if(dst->specification_level) {    /* has to be specified */
747 
748         if(dst->matrix_flag) {
749           cnt = 1;
750           for(b = -below; b <= above; b++) {
751             if(b) {
752               src = cpy + delta + a + b;
753               if(src->matrix_flag) {
754                 cnt++;
755                 for(c = 0; c < 16; c++) {
756                   dst->matrix[c] += src->matrix[c];
757                 }
758               }
759             }
760           }
761           for(c = 0; c < 16; c++) {
762             dst->matrix[c] /= cnt;
763           }
764           reorient44d(dst->matrix);     /* convert those averages into a valid matrix */
765         }
766 
767         if(dst->pre_flag) {
768           cnt = 1;
769           for(b = -below; b <= above; b++) {
770             if(b) {
771               src = cpy + delta + a + b;
772               if(src->pre_flag) {
773                 cnt++;
774                 for(c = 0; c < 3; c++) {
775                   dst->pre[c] += src->pre[c];
776                 }
777               }
778             }
779           }
780           for(c = 0; c < 3; c++) {
781             dst->pre[c] /= cnt;
782           }
783         }
784 
785         if(dst->post_flag) {
786           cnt = 1;
787           for(b = -below; b <= above; b++) {
788             if(b) {
789               src = cpy + delta + a + b;
790               if(src->post_flag) {
791                 cnt++;
792                 for(c = 0; c < 3; c++) {
793                   dst->post[c] += src->post[c];
794                 }
795               }
796             }
797           }
798           for(c = 0; c < 3; c++) {
799             dst->post[c] /= cnt;
800           }
801         }
802 
803         if(dst->clip_flag) {
804           cnt = 1;
805           for(b = -below; b <= above; b++) {
806             if(b) {
807               src = cpy + delta + a + b;
808               if(src->clip_flag) {
809                 cnt++;
810                 dst->front += src->front;
811                 dst->back += src->back;
812               }
813             }
814           }
815           dst->front /= cnt;
816           dst->back /= cnt;
817         }
818 
819       }
820     }
821     FreeP(cpy);
822   }
823   return 1;
824 }
825 
ViewElemInterpolate(PyMOLGlobals * G,CViewElem * first,CViewElem * last,float power,float bias,int simple,float linearity,int hand,float cut)826 int ViewElemInterpolate(PyMOLGlobals * G, CViewElem * first, CViewElem * last,
827                         float power, float bias,
828                         int simple, float linearity, int hand, float cut)
829 {
830   float first3x3[9];
831   float last3x3[9];
832   float inverse3x3[9];
833   float inter3x3[9];
834   float rot_axis[3], trans_axis[3] = { 0.0F, 0.0F, 0.0F };
835   float angle;
836   CViewElem *current;
837   ov_diff n = (last - first) - 1;
838   Matrix53f rot, imat;
839   int a;
840   float tVector[3], tCenter[3], tDir[3];
841   float tLen = 0.0F;
842   float bisect[3], v2[3];
843   float translate_angle = 0.0F;
844   float pivot[3] = { 0.0F, 0.0F, 0.0F };
845   const float _1 = 1.0F, _p5 = 0.5F;
846   int parabolic = true;
847   int timing_flag;
848   double timing = 0.0F;
849   int state_flag;
850   int state = 0;
851   float pre[3];
852   float firstC44f[16], firstRTTT[16], firstR44f[16];
853   float lastC44f[16], lastRTTT[16], lastR44f[16];
854   int linear = false;
855   int debug = Feedback(G,FB_Movie, FB_Debugging);
856 
857   if(hand == 0)
858     hand = 1;
859 
860   if(debug) {
861     printf("ViewElemInterpolate: %8.3f %8.3f %d %8.3f %d %8.3f\n",
862            power, bias, simple, linearity, hand, cut);
863     dump44d(first->matrix,"first->matrix");
864     dump44d(last->matrix,"last->matrix");
865     printf("first->pre_flag %d first->post_flag %d\n",first->pre_flag, first->post_flag);
866     dump3d(first->pre,"first->pre");
867     dump3d(first->post,"first->post");
868     printf("last->pre_flag %d last->post_flag %d\n",last->pre_flag, last->post_flag);
869     dump3d(last->pre,"last->pre");
870     dump3d(last->post,"last->post");
871   }
872   if(power == 0.0F) {
873     if(first->power_flag && last->power_flag) {
874       if(((first->power > 0.0F) && (last->power > 0.0F)) ||
875          ((first->power < 0.0F) && (last->power < 0.0F))) {
876         power = (first->power + last->power) / 2.0F;
877       } else if(fabs(first->power) > fabs(last->power)) {
878         power = first->power;
879       } else if(last->power < 0.0F) {
880         power = last->power;
881       } else {
882         power = first->power;
883       }
884     } else if(first->power_flag) {
885       power = first->power;
886     } else if(last->power_flag) {
887       power = last->power;
888     } else {
889       power = 1.4F; /* default */
890     }
891   }
892   if(power < 0.0F) {
893     parabolic = false;
894     power = -power;
895   }
896 
897   if(bias < 0.0F) { /* default */
898     if(first->bias_flag && last->bias_flag) {
899       if((first->bias > 0.0F) && (last->bias > 0.0F)) {
900         bias = (first->bias * 1.0F/last->bias);
901       } else if(fabs(first->bias) > 0.0) {
902         bias = first->bias;
903       } else if(last->bias > 0.0F) {
904         bias = 1.0F/last->bias;
905       } else {
906         bias = 1.0F;
907       }
908     } else if(first->bias_flag) {
909       bias = first->bias;
910     } else if(last->bias_flag) {
911       bias = 1.0F/last->bias;
912     } else {
913       bias = 1.0F; /* default */
914     }
915   }
916 
917   if(bias <= 0.0F) {
918     bias = 1.0F;
919   }
920 
921   /* WARNING: this routine is operating on column-major matrices!!! */
922 
923   copy44d33f(first->matrix, first3x3);
924   copy44d33f(last->matrix, last3x3);
925 
926   transpose33f33f(first3x3, inverse3x3);
927 
928   multiply33f33f(inverse3x3, last3x3, &rot[0][0]);      /* [rot] = [first]^-1 [last] */
929   matrix_to_rotation(rot, rot_axis, &angle);
930 
931   if(debug)
932     dump3f(rot_axis, "rot_axis");
933 
934   if(hand) {
935     if((cPI - fabs(angle)) < 0.01F) {   /* this a complete 180 degree motion */
936       if(((rot_axis[0] * 0.7F + rot_axis[1] * 0.8F + rot_axis[2] * 0.9F) * hand * angle) >
937          0.0F) {
938         invert3f(rot_axis);
939         if(angle > 0) {
940           angle = (float) ((2 * cPI) - angle);
941         } else {
942           angle = (float) (-(2 * cPI) - angle);
943         }
944       }
945     }
946   }
947 
948   if(!simple) {
949     /* switch back into row major to promote developer sanity */
950 
951     copy33f44f(first3x3, firstC44f);
952     copy33f44f(last3x3, lastC44f);
953 
954     transpose44f44f(firstC44f, firstRTTT);
955     transpose44f44f(lastC44f, lastRTTT);
956 
957     /* form TTTs */
958 
959     firstRTTT[12] = (float) -first->pre[0];
960     firstRTTT[13] = (float) -first->pre[1];
961     firstRTTT[14] = (float) -first->pre[2];
962 
963     firstRTTT[3] = (float) first->post[0];
964     firstRTTT[7] = (float) first->post[1];
965     firstRTTT[11] = (float) first->post[2];
966 
967     lastRTTT[12] = (float) -last->pre[0];
968     lastRTTT[13] = (float) -last->pre[1];
969     lastRTTT[14] = (float) -last->pre[2];
970 
971     lastRTTT[3] = (float) last->post[0];
972     lastRTTT[7] = (float) last->post[1];
973     lastRTTT[11] = (float) last->post[2];
974 
975     if(debug)
976       dump44f(firstRTTT, "firstRTTT");
977     if(debug)
978       dump44f(lastRTTT, "lastRTTT");
979 
980     /* convert to homogenous */
981 
982     convertTTTfR44f(firstRTTT, firstR44f);
983     convertTTTfR44f(lastRTTT, lastR44f);
984 
985     /* reset both matrices to a common origin */
986 
987     {
988       float first_pre[3], last_pre[3];
989       float post[4], *dst;
990 
991       copy3d3f(first->pre, first_pre);
992       copy3d3f(last->pre, last_pre);
993       average3f(first_pre, last_pre, pre);
994 
995       transform44f3fas33f3f(firstR44f, pre, post);
996       copy44f(firstR44f, firstRTTT);
997       firstRTTT[3] += post[0];
998       firstRTTT[7] += post[1];
999       firstRTTT[11] += post[2];
1000       dst = firstRTTT + 12;
1001       invert3f3f(pre, dst);
1002 
1003       transform44f3fas33f3f(lastR44f, pre, post);
1004       copy44f(lastR44f, lastRTTT);
1005       lastRTTT[3] += post[0];
1006       lastRTTT[7] += post[1];
1007       lastRTTT[11] += post[2];
1008       dst = lastRTTT + 12;
1009       invert3f3f(pre, dst);
1010     }
1011 
1012     if(debug)
1013       dump44f(firstRTTT, "firstRTTT");
1014     if(debug)
1015       dump44f(lastRTTT, "lastRTTT");
1016 
1017     /*    convertTTTfR44f(firstRTTT, firstR44f);
1018        convertTTTfR44f(lastRTTT, lastR44f); */
1019 
1020     /* now populate the translation fields */
1021 
1022     rot[3][0] = firstRTTT[3];
1023     rot[3][1] = firstRTTT[7];
1024     rot[3][2] = firstRTTT[11];
1025 
1026     rot[4][0] = lastRTTT[3];
1027     rot[4][1] = lastRTTT[7];
1028     rot[4][2] = lastRTTT[11];
1029 
1030     /* now set up the interpolation */
1031 
1032     subtract3f(&rot[4][0], &rot[3][0], tVector);
1033     tLen = (float) length3f(tVector);
1034     average3f(&rot[4][0], &rot[3][0], tCenter);
1035 
1036     if(tLen < 0.0001F) {
1037       if(debug)
1038         printf("translation too short %8.3f\n", tLen);
1039       simple = true;
1040     }
1041   }
1042 
1043   if(!simple) {
1044 
1045     normalize23f(tVector, tDir);
1046     if(debug)
1047       dump3f(tDir, "tDir");
1048     cross_product3f(tDir, rot_axis, bisect);
1049     /* bisect is a vector in the translation arc */
1050     if(length3f(bisect) < 0.0001F) {
1051       if(debug)
1052         printf("rotation coincident with translation\n");
1053       linear = true;
1054     }
1055   }
1056 
1057   if(!(simple || linear)) {
1058     normalize3f(bisect);
1059 
1060     /* this section needs work... */
1061 
1062     cross_product3f(bisect, tDir, trans_axis);
1063     normalize3f(trans_axis);
1064 
1065     transform33Tf3f(&rot[0][0], bisect, v2);    /* column major */
1066 
1067     remove_component3f(v2, trans_axis, v2);
1068     normalize3f(v2);            /* project vector onto plane _|_ to axis */
1069 
1070     if(debug) {
1071       dump3f(rot_axis, "rot_axis");
1072       dump3f(tDir, "tDir");
1073       dump3f(bisect, "bisect");
1074       dump3f(trans_axis, "trans_axis");
1075       dump3f(v2, "v2");
1076     }
1077 
1078     {
1079       double dot = dot_product3f(bisect, v2);
1080       if(dot < -1.0F)
1081         dot = -1.0F;
1082       if(dot > 1.0F)
1083         dot = 1.0F;
1084       translate_angle = (float) acos(dot);
1085 
1086       /* if translation angle > rotation angle then sets translation angle
1087        * to same as rotation angle, with proper sign of course */
1088 
1089       if((fabs(translate_angle) > fabs(angle)) && (fabs(angle) > R_SMALL4)) {
1090         translate_angle = (float) (fabs(angle) * (translate_angle / fabs(angle)));
1091       }
1092 
1093       if(fabs(translate_angle) < 0.0001F) {
1094         linear = true;
1095         if(debug)
1096           printf("no significant rotation\n");
1097       }
1098 
1099       if((translate_angle * angle) < 0.0F) {
1100         /* if motions are in opposing directions, then flip translation axis and location */
1101         invert3f(bisect);
1102         invert3f(trans_axis);
1103       }
1104 
1105     }
1106   }
1107 
1108   if(!(simple || linear)) {
1109     float pLen = (float) tan(translate_angle / 2);
1110     if(fabs(pLen) > 0.0000001)
1111       pLen = (tLen / 2) / pLen;
1112     else {
1113       if(debug)
1114         printf("pLen too short %8.3f\n", pLen);
1115       simple = true;
1116     }
1117 
1118     if(!simple) {
1119       pivot[0] = tCenter[0] + pLen * bisect[0];
1120       pivot[1] = tCenter[1] + pLen * bisect[1];
1121       pivot[2] = tCenter[2] + pLen * bisect[2];
1122     }
1123 
1124     if(debug && !simple) {
1125       dump3f(tCenter, "center");
1126       dump3f(pivot, "pivot");
1127       printf("pLen %8.3f angle %8.3f translate_angle %8.3f\n",
1128              pLen, angle, translate_angle);
1129     }
1130   }
1131 
1132   /* now interpolate */
1133 
1134   state_flag = first->state_flag && last->state_flag;
1135 
1136   timing_flag = first->timing_flag && last->timing_flag;
1137 
1138   current = first + 1;
1139 
1140   if(debug)
1141     dump44f(firstR44f, "first");
1142 
1143   for(a = 0; a < n; a++) {
1144     double fxn = (a + 1.0) / (n + 1.0);
1145     double fxn_1 = 1.0 - fxn;
1146 
1147     if(timing_flag) {
1148       timing = (first->timing * fxn_1) + (last->timing * fxn);
1149     }
1150 
1151     if(state_flag) { /* states are interpolated linearly by default */
1152       state = (int)(first->state * (1.0F - fxn) + (last->state * fxn) + 0.499F);
1153     }
1154 
1155     if(bias != 1.0F) {
1156       fxn = 1 - (float) pow(1 - pow(fxn, bias), _1 / bias);
1157     }
1158 
1159     if((power != 1.0F) || (!parabolic)) {
1160       if(fxn < 0.5F) {
1161         if(!parabolic)
1162           fxn = (float) ((_1 - cos(cPI * fxn)) * _p5);  /* circular */
1163         fxn = (float) pow(fxn * 2.0F, power) * _p5;     /* parabolic */
1164       } else if(fxn > 0.5F) {
1165         fxn = _1 - fxn;
1166         if(!parabolic)
1167           fxn = (float) ((_1 - cos(cPI * fxn)) * _p5);
1168         fxn = (float) pow(fxn * 2.0F, power) * _p5;     /* parabolic */
1169         fxn = _1 - fxn;
1170       }
1171     }
1172 
1173     fxn_1 = 1.0F - fxn;
1174 
1175     ViewElemCopy(G, first, current);
1176 
1177     if(simple) {
1178       rotation_matrix3f(fxn * angle, rot_axis[0], rot_axis[1], rot_axis[2], &imat[0][0]);
1179 
1180 
1181 /* [cur] = [first] [partial-rot], so....
1182    at start: [cur] = [first] [identity] = [first]
1183    at end: [cur] = [first] [first]^-1 [last] = [last]
1184 */
1185       current->matrix_flag = true;
1186       multiply33f33f(first3x3, &imat[0][0], inter3x3);
1187 
1188       copy33f44d(inter3x3, current->matrix);
1189 
1190       if(first->pre_flag && last->pre_flag) {
1191         mix3d(first->pre, last->pre, (double) fxn, current->pre);
1192         current->pre_flag = true;
1193       } else {
1194         current->pre_flag = false;
1195       }
1196       if(first->post_flag && last->post_flag) {
1197         mix3d(first->post, last->post, (double) fxn, current->post);
1198         current->post_flag = true;
1199       } else {
1200         current->post_flag = false;
1201       }
1202     } else if(linear) {
1203       int b;
1204       rotation_matrix3f(fxn * angle, rot_axis[0], rot_axis[1], rot_axis[2], &imat[0][0]);
1205       current->matrix_flag = true;
1206       multiply33f33f(first3x3, &imat[0][0], inter3x3);
1207 
1208       copy33f44d(inter3x3, current->matrix);
1209 
1210       current->pre_flag = true;
1211       copy3f3d(pre, current->pre);
1212 
1213       current->post_flag = true;
1214       for(b = 0; b < 3; b++) {
1215         imat[4][b] = (float) ((1.0 - fxn) * rot[3][b] + fxn * rot[4][b]);
1216       }
1217       copy3f3d(&imat[4][0], current->post);
1218     } else {
1219       matrix_interpolate(imat, rot,
1220                          pivot, bisect,
1221                          rot_axis, angle, trans_axis, translate_angle, fxn, linearity);
1222 
1223       current->matrix_flag = true;
1224       multiply33f33f(first3x3, &imat[0][0], inter3x3);
1225 
1226       copy33f44d(inter3x3, current->matrix);
1227 
1228       current->pre_flag = true;
1229       copy3f3d(pre, current->pre);
1230 
1231       current->post_flag = true;
1232       copy3f3d(&imat[4][0], current->post);
1233 
1234     }
1235     if(debug) {
1236       if((a == 0) || (a == n - 1)) {
1237         float curC44f[16], curRTTT[16], curR44f[16];
1238 
1239         copy33f44f(inter3x3, curC44f);
1240 
1241         transpose44f44f(curC44f, curRTTT);
1242 
1243         /* form TTTs */
1244 
1245         curRTTT[12] = (float) -current->pre[0];
1246         curRTTT[13] = (float) -current->pre[1];
1247         curRTTT[14] = (float) -current->pre[2];
1248 
1249         curRTTT[3] = (float) current->post[0];
1250         curRTTT[7] = (float) current->post[1];
1251         curRTTT[11] = (float) current->post[2];
1252 
1253         convertTTTfR44f(curRTTT, curR44f);
1254         dump44f(curR44f, "cur");
1255       }
1256     }
1257 
1258     if(first->clip_flag && last->clip_flag) {
1259       current->front = first->front * fxn_1 + last->front * fxn;
1260       current->back = first->back * fxn_1 + last->back * fxn;
1261       current->clip_flag = true;
1262     } else {
1263       current->clip_flag = false;
1264     }
1265 
1266     if(first->ortho_flag && last->ortho_flag) {
1267       float approx_ortho = first->ortho * fxn_1 + last->ortho * fxn;
1268       if(first->pre_flag && last->pre_flag) {
1269         float first_far = first->pre[2] * tan(cPI * fabs(first->ortho) / 360.0);
1270         float last_far = last->pre[2] * tan(cPI * fabs(last->ortho) / 360.0);
1271 
1272         float cur_far = first_far * fxn_1 + last_far * fxn;
1273         current->ortho = 360.0 * atan(cur_far / current->pre[2]) / cPI;
1274 
1275         if((current->ortho * approx_ortho) < 0) /* fix sign */
1276           current->ortho = -current->ortho;
1277       } else {
1278         current->ortho = approx_ortho;
1279       }
1280     }
1281     current->specification_level = 1;
1282 
1283     if(state_flag) {
1284       current->state_flag = true;
1285       current->state = state;
1286     }
1287 
1288     if(timing_flag) {
1289       current->timing_flag = true;
1290       current->timing = timing;
1291     }
1292 
1293 
1294     if(first->scene_flag && last->scene_flag) {
1295       if(current->scene_name) {
1296         OVLexicon_DecRef(G->Lexicon, current->scene_name);
1297       }
1298       current->scene_flag = true;
1299       if(fxn >= cut) {
1300         current->scene_name = last->scene_name;
1301       } else {
1302         current->scene_name = first->scene_name;
1303       }
1304       OVLexicon_IncRef(G->Lexicon, current->scene_name);
1305     }
1306     current++;
1307   }
1308   if(debug)
1309     dump44f(lastR44f, "last");
1310 
1311   return 1;
1312 }
1313