1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2009-06-26 10:56:39 -0500 (Fri, 26 Jun 2009) $
4  * $Revision: 11127 $
5  *
6  * Copyright (C) 2003-2005  The Jmol Development Team
7  *
8  * Contact: jmol-developers@lists.sf.net
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 package org.jmol.viewer;
25 
26 
27 import java.util.Map;
28 
29 
30 import org.jmol.script.T;
31 import org.jmol.thread.JmolThread;
32 import org.jmol.util.BSUtil;
33 //import javajs.util.List;
34 
35 import org.jmol.api.Interface;
36 import javajs.util.BS;
37 import org.jmol.modelset.ModelSet;
38 
39 public class AnimationManager {
40 
41   public JmolThread animationThread;
42   public Viewer vwr;
43 
AnimationManager(Viewer vwr)44   AnimationManager(Viewer vwr) {
45     this.vwr = vwr;
46   }
47 
48   // used by AnimationThread, Viewer, or StateCreator:
49 
50   public boolean animationOn;
51   public int animationFps;  // set in stateManager
52   public int firstFrameDelayMs;
53   public int lastFrameDelayMs;
54 
setAnimationOn(boolean animationOn)55   public void setAnimationOn(boolean animationOn) {
56     if (animationOn == this.animationOn)
57       return;
58 
59     if (!animationOn || vwr.headless) {
60       stopThread(false);
61       return;
62     }
63     if (!vwr.tm.spinOn)
64       vwr.refresh(Viewer.REFRESH_SYNC_MASK, "Anim:setAnimationOn");
65     setAnimationRange(-1, -1);
66     resumeAnimation();
67   }
68 
stopThread(boolean isPaused)69   public void stopThread(boolean isPaused) {
70     boolean stopped = false;
71     if (animationThread != null) {
72       animationThread.interrupt();
73       animationThread = null;
74       stopped = true;
75     }
76     animationPaused = isPaused;
77     if (stopped && !vwr.tm.spinOn)
78       vwr.refresh(Viewer.REFRESH_SYNC_MASK, "Viewer:setAnimationOff");
79     animation(false);
80     //stopModulationThread();
81     vwr.setStatusFrameChanged(false, false);
82 
83   }
84 
setAnimationNext()85   public boolean setAnimationNext() {
86     return setAnimationRelative(animationDirection);
87   }
88 
currentIsLast()89   public boolean currentIsLast() {
90     return (isMovie ? lastFramePainted == caf
91         : lastModelPainted == cmi);
92   }
93 
currentFrameIs(int f)94   public boolean currentFrameIs(int f) {
95     int i = cmi;
96     return (morphCount == 0 ? i == f : Math.abs(currentMorphModel - f) < 0.001f);
97   }
98 
99   // required by Viewer or stateCreator
100 
101   // used by StateCreator or Viewer:
102 
103   final static int FRAME_FIRST = -1;
104   final static int FRAME_LAST = 1;
105   final static int MODEL_CURRENT = 0;
106 
107   final BS bsVisibleModels = new BS();
108 
109   public int animationReplayMode = T.once;
110 
111   BS bsDisplay;
112 
113   int[] animationFrames;
114 
115   public boolean isMovie;
116   boolean animationPaused;
117 
118   /**
119    * current model index
120    *
121    */
122   public int cmi;
123 
124   /**
125    * current animation frame
126    *
127    */
128   int caf;
129   int morphCount;
130   int animationDirection = 1;
131   int currentDirection = 1;
132   int firstFrameIndex;
133   int lastFrameIndex;
134   int frameStep;
135   int backgroundModelIndex = -1;
136 
137   float currentMorphModel;
138   float firstFrameDelay;
139   float lastFrameDelay = 1;
140 
clear()141   void clear() {
142     setMovie(null);
143     initializePointers(0);
144     setAnimationOn(false);
145     setModel(0, true);
146     currentDirection = 1;
147     cai = -1;
148     setAnimationDirection(1);
149     setAnimationFps(10);
150     setAnimationReplayMode(T.once, 0, 0);
151     initializePointers(0);
152   }
153 
getModelSpecial(int i)154   String getModelSpecial(int i) {
155     switch (i) {
156     case FRAME_FIRST:
157       if (animationFrames != null)
158         return "1";
159       i = firstFrameIndex;
160       break;
161     case MODEL_CURRENT:
162       if (morphCount > 0)
163         return "-" + (1 + currentMorphModel);
164       i = cmi;
165       break;
166     case FRAME_LAST:
167       if (animationFrames != null)
168         return "" + animationFrames.length;
169       i = lastFrameIndex;
170       break;
171     }
172     return vwr.getModelNumberDotted(i);
173   }
174 
setDisplay(BS bs)175   void setDisplay(BS bs) {
176     bsDisplay = (bs == null || bs.isEmpty() ? null : BSUtil.copy(bs));
177   }
178 
setMorphCount(int n)179   public void setMorphCount(int n) {
180     morphCount = (isMovie ? 0 : n); // for now -- no morphing in movies
181   }
182 
morph(float modelIndex)183   public void morph(float modelIndex) {
184     int m = (int) modelIndex;
185     if (Math.abs(m - modelIndex) < 0.001f)
186       modelIndex = m;
187     else if (Math.abs(m - modelIndex) > 0.999f)
188       modelIndex = m = m + 1;
189     float f = modelIndex - m;
190     m -= 1;
191     if (f == 0) {
192       currentMorphModel = m;
193       setModel(m, true);
194       return;
195     }
196     int m1;
197     setModel(m, true);
198     m1 = m + 1;
199     currentMorphModel = m + f;
200     if (m1 == m || m1 < 0 || m < 0)
201       return;
202     vwr.ms.morphTrajectories(m, m1, f);
203   }
204 
setModel(int modelIndex, boolean clearBackgroundModel)205   void setModel(int modelIndex, boolean clearBackgroundModel) {
206     if (modelIndex < 0)
207       stopThread(false);
208     int formerModelIndex = cmi;
209     ModelSet modelSet = vwr.ms;
210     int modelCount = (modelSet == null ? 0 : modelSet.mc);
211     if (modelCount == 1)
212       cmi = modelIndex = 0;
213     else if (modelIndex < 0 || modelIndex >= modelCount)
214       modelIndex = -1;
215     String ids = null;
216     boolean isSameSource = false;
217     if (cmi != modelIndex) {
218       if (modelCount > 0) {
219         ModelSet ms = vwr.ms;
220         boolean toDataModel = ms.isJmolDataFrameForModel(modelIndex);
221         boolean fromDataModel = ms.isJmolDataFrameForModel(cmi);
222         if (fromDataModel)
223           ms.setJmolDataFrame(null, -1, cmi);
224         if (cmi != -1)
225           vwr.saveModelOrientation();
226         if (fromDataModel || toDataModel) {
227           ids = ms.getJmolFrameType(modelIndex)
228           + " "  + modelIndex + " <-- "
229           + " " + cmi + " "
230           + ms.getJmolFrameType(cmi);
231 
232           isSameSource = (ms.getJmolDataSourceFrame(modelIndex) == ms
233               .getJmolDataSourceFrame(cmi));
234         }
235       }
236       cmi = modelIndex;
237       if (ids != null) {
238         if (modelIndex >= 0)
239           vwr.restoreModelOrientation(modelIndex);
240         if (isSameSource && (ids.indexOf("quaternion") >= 0
241             || ids.indexOf("plot") < 0
242             && ids.indexOf("ramachandran") < 0
243             && ids.indexOf(" property ") < 0)) {
244           vwr.restoreModelRotation(formerModelIndex);
245         }
246       }
247     }
248     setViewer(clearBackgroundModel);
249   }
250 
setBackgroundModelIndex(int modelIndex)251   void setBackgroundModelIndex(int modelIndex) {
252     ModelSet modelSet = vwr.ms;
253     if (modelSet == null || modelIndex < 0 || modelIndex >= modelSet.mc)
254       modelIndex = -1;
255     backgroundModelIndex = modelIndex;
256     if (modelIndex >= 0)
257       vwr.ms.setTrajectory(modelIndex);
258     vwr.setTainted(true);
259     setFrameRangeVisible();
260   }
261 
initializePointers(int frameStep)262   void initializePointers(int frameStep) {
263     firstFrameIndex = 0;
264     lastFrameIndex = (frameStep == 0 ? 0 : getFrameCount()) - 1;
265     this.frameStep = frameStep;
266     vwr.setFrameVariables();
267   }
268 
setAnimationDirection(int animationDirection)269   public void setAnimationDirection(int animationDirection) {
270     this.animationDirection = animationDirection;
271     //if (animationReplayMode != ANIMATION_LOOP)
272       //currentDirection = 1;
273   }
274 
setAnimationFps(int fps)275   void setAnimationFps(int fps) {
276     if (fps < 1)
277       fps = 1;
278     if (fps > 50)
279       fps = 50;
280     animationFps = fps;
281     vwr.setFrameVariables();
282   }
283 
284   // 0 = once
285   // 1 = loop
286   // 2 = palindrome
287 
setAnimationReplayMode(int animationReplayMode, float firstFrameDelay, float lastFrameDelay)288   public void setAnimationReplayMode(int animationReplayMode,
289                                      float firstFrameDelay,
290                                      float lastFrameDelay) {
291     this.firstFrameDelay = firstFrameDelay > 0 ? firstFrameDelay : 0;
292     firstFrameDelayMs = (int)(this.firstFrameDelay * 1000);
293     this.lastFrameDelay = lastFrameDelay > 0 ? lastFrameDelay : 0;
294     lastFrameDelayMs = (int)(this.lastFrameDelay * 1000);
295     this.animationReplayMode = animationReplayMode;
296     vwr.setFrameVariables();
297   }
298 
setAnimationRange(int framePointer, int framePointer2)299   void setAnimationRange(int framePointer, int framePointer2) {
300     int frameCount = getFrameCount();
301     if (framePointer < 0) framePointer = 0;
302     if (framePointer2 < 0) framePointer2 = frameCount;
303     if (framePointer >= frameCount) framePointer = frameCount - 1;
304     if (framePointer2 >= frameCount) framePointer2 = frameCount - 1;
305     firstFrameIndex = framePointer;
306     currentMorphModel = firstFrameIndex;
307     lastFrameIndex = framePointer2;
308     frameStep = (framePointer2 < framePointer ? -1 : 1);
309     rewindAnimation();
310   }
311 
pauseAnimation()312   void pauseAnimation() {
313     stopThread(true);
314   }
315 
reverseAnimation()316   void reverseAnimation() {
317     currentDirection = -currentDirection;
318     if (!animationOn)
319       resumeAnimation();
320   }
321 
repaintDone()322   void repaintDone() {
323     lastModelPainted = cmi;
324     lastFramePainted = caf;
325   }
326 
resumeAnimation()327   void resumeAnimation() {
328     if(cmi < 0)
329       setAnimationRange(firstFrameIndex, lastFrameIndex);
330     if (getFrameCount() <= 1) {
331       animation(false);
332       return;
333     }
334     animation(true);
335     animationPaused = false;
336     if (animationThread == null) {
337       intAnimThread++;
338       animationThread = (JmolThread) Interface.getOption("thread.AnimationThread", vwr, "script");
339       animationThread.setManager(this, vwr, new int[] {firstFrameIndex, lastFrameIndex, intAnimThread} );
340       animationThread.start();
341     }
342   }
343 
setAnimationLast()344   void setAnimationLast() {
345     setFrame(animationDirection > 0 ? lastFrameIndex : firstFrameIndex);
346   }
347 
rewindAnimation()348   void rewindAnimation() {
349     setFrame(animationDirection > 0 ? firstFrameIndex : lastFrameIndex);
350     currentDirection = 1;
351     vwr.setFrameVariables();
352   }
353 
setAnimationPrevious()354   boolean setAnimationPrevious() {
355     return setAnimationRelative(-animationDirection);
356   }
357 
getAnimRunTimeSeconds()358   float getAnimRunTimeSeconds() {
359     int frameCount = getFrameCount();
360     if (firstFrameIndex == lastFrameIndex || lastFrameIndex < 0
361         || firstFrameIndex < 0 || lastFrameIndex >= frameCount
362         || firstFrameIndex >= frameCount)
363       return 0;
364     int i0 = Math.min(firstFrameIndex, lastFrameIndex);
365     int i1 = Math.max(firstFrameIndex, lastFrameIndex);
366     float nsec = 1f * (i1 - i0) / animationFps + firstFrameDelay
367         + lastFrameDelay;
368     for (int i = i0; i <= i1; i++)
369       nsec += vwr.ms.getFrameDelayMs(modelIndexForFrame(i)) / 1000f;
370     return nsec;
371   }
372 
373   /**
374    * support for PyMOL movies and
375    * anim FRAMES [....]
376    *
377    * currently no support for scripted movies
378    *
379    * @param info
380    */
setMovie(Map<String, Object> info)381   public void setMovie(Map<String, Object> info) {
382     isMovie = (info != null && info.get("scripts") == null);
383     if (isMovie) {
384       animationFrames = (int[]) info.get("frames");
385       if (animationFrames == null || animationFrames.length == 0) {
386         isMovie = false;
387       } else {
388         caf = ((Integer) info.get("currentFrame")).intValue();
389         if (caf < 0 || caf >= animationFrames.length)
390           caf = 0;
391       }
392       setFrame(caf);
393     }
394     if (!isMovie) {
395       //movie = null;
396       animationFrames = null;
397     }
398     vwr.setBooleanProperty("_ismovie", isMovie);
399     bsDisplay = null;
400     currentMorphModel = morphCount = 0;
401     vwr.setFrameVariables();
402   }
403 
modelIndexForFrame(int i)404   int modelIndexForFrame(int i) {
405     return (isMovie ? animationFrames[i] - 1 : i);
406   }
407 
getFrameCount()408   public int getFrameCount() {
409     return (isMovie ? animationFrames.length : vwr.ms.mc);
410   }
411 
setFrame(int i)412   public void setFrame(int i) {
413     try {
414     if (isMovie) {
415       int iModel = modelIndexForFrame(i);
416       caf = i;
417       i = iModel;
418     } else {
419       caf = i;
420     }
421     setModel(i, true);
422     } catch (Exception e) {
423       // ignore
424     }
425   }
426 
427   // private methods and fields
428 
429   private int lastFramePainted;
430   private int lastModelPainted;
431   private int intAnimThread;
432   private int cai = -1;
433 
getUnitCellAtomIndex()434   public int getUnitCellAtomIndex() {
435     return cai;
436   }
437 
setUnitCellAtomIndex(int iAtom)438   public void setUnitCellAtomIndex(int iAtom) {
439     cai = iAtom;
440   }
441 
442 
setViewer(boolean clearBackgroundModel)443   private void setViewer(boolean clearBackgroundModel) {
444     vwr.ms.setTrajectory(cmi);
445     vwr.tm.setFrameOffset(cmi);
446     if (cmi == -1 && clearBackgroundModel)
447       setBackgroundModelIndex(-1);
448     vwr.setTainted(true);
449     int nDisplay = setFrameRangeVisible();
450     vwr.setStatusFrameChanged(false, false);
451     if (!vwr.g.selectAllModels)
452       setSelectAllSubset(nDisplay < 2);
453   }
454 
455   void setSelectAllSubset(boolean justOne) {
456     if (vwr.ms != null)
457       vwr.slm.setSelectionSubset(justOne ? vwr.ms
458           .getModelAtomBitSetIncludingDeleted(cmi, true) : vwr.ms
459           .getModelAtomBitSetIncludingDeletedBs(bsVisibleModels));
460   }
461 
462   private int setFrameRangeVisible() {
463     int nDisplayed = 0;
464     bsVisibleModels.clearAll();
465     if (backgroundModelIndex >= 0) {
466       bsVisibleModels.set(backgroundModelIndex);
467       nDisplayed = 1;
468     }
469     if (cmi >= 0) {
470       bsVisibleModels.set(cmi);
471       return ++nDisplayed;
472     }
473     if (frameStep == 0)
474       return nDisplayed;
475     int frameDisplayed = 0;
476     nDisplayed = 0;
477     for (int iframe = firstFrameIndex; iframe != lastFrameIndex; iframe += frameStep) {
478       int i = modelIndexForFrame(iframe);
479       if (!vwr.ms.isJmolDataFrameForModel(i)) {
480         bsVisibleModels.set(i);
481         nDisplayed++;
482         frameDisplayed = iframe;
483       }
484     }
485     int i = modelIndexForFrame(lastFrameIndex);
486     if (firstFrameIndex == lastFrameIndex || !vwr.ms.isJmolDataFrameForModel(i)
487         || nDisplayed == 0) {
488       bsVisibleModels.set(i);
489       if (nDisplayed == 0)
490         firstFrameIndex = lastFrameIndex;
491       nDisplayed = 0;
492     }
493     if (nDisplayed == 1 && cmi < 0)
494       setFrame(frameDisplayed);
495     return nDisplayed;
496   }
497 
animation(boolean TF)498   private void animation(boolean TF) {
499     animationOn = TF;
500     vwr.setBooleanProperty("_animating", TF);
501   }
502 
setAnimationRelative(int direction)503   private boolean setAnimationRelative(int direction) {
504     int frameStep = getFrameStep(direction);
505     int thisFrame = (isMovie ? caf : cmi);
506     int frameNext = thisFrame + frameStep;
507     float morphStep = 0f, nextMorphFrame = 0f;
508     boolean isDone;
509     if (morphCount > 0) {
510       morphStep = 1f / (morphCount + 1);
511       nextMorphFrame = currentMorphModel + frameStep * morphStep;
512       isDone = isNotInRange(nextMorphFrame);
513     } else {
514       isDone = isNotInRange(frameNext);
515     }
516     if (isDone) {
517       switch (animationReplayMode) {
518       case T.once:
519         return false;
520       case T.loop:
521         nextMorphFrame = frameNext = (animationDirection == currentDirection ? firstFrameIndex
522             : lastFrameIndex);
523         break;
524       case T.palindrome:
525         currentDirection = -currentDirection;
526         frameNext -= 2 * frameStep;
527         nextMorphFrame -= 2 * frameStep * morphStep;
528       }
529     }
530     //Logger.debug("next="+modelIndexNext+" dir="+currentDirection+" isDone="+isDone);
531     //System.out.println("setAnimRel dir=" + direction + " step=" + frameStep + " this=" + thisFrame + " next=" + frameNext + " morphcount=" + morphCount + " done=" + isDone + " mode=" + animationReplayMode);
532     if (morphCount < 1) {
533       if (frameNext < 0 || frameNext >= getFrameCount())
534         return false;
535       setFrame(frameNext);
536       return true;
537     }
538     morph(nextMorphFrame + 1);
539     return true;
540   }
541 
isNotInRange(float frameNext)542   private boolean isNotInRange(float frameNext) {
543     float f = frameNext - 0.001f;
544     return (f > firstFrameIndex && f > lastFrameIndex
545         || (f = frameNext + 0.001f) < firstFrameIndex
546         && f < lastFrameIndex);
547   }
548 
getFrameStep(int direction)549   private int getFrameStep(int direction) {
550     return frameStep * direction * currentDirection;
551   }
552 
553 
554 }
555