123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- """
- @package nviz.animation
- @brief Nviz (3D view) animation
- Classes:
- - animation::Animation
- (C) 2011 by the GRASS Development Team
- This program is free software under the GNU General Public License
- (>=v2). Read the file COPYING that comes with GRASS for details.
- @author Anna Kratochvilova <kratochanna gmail.com>
- """
- import os
- import copy
- import wx
- from grass.pydispatch.signal import Signal
- class Animation:
- """Class represents animation as a sequence of states (views).
- It enables to record, replay the sequence and finally generate
- all image files. Recording and replaying is based on timer events.
- There is no frame interpolation like in the Tcl/Tk based Nviz.
- """
- def __init__(self, mapWindow, timer):
- """Animation constructor
- Signals:
- animationFinished - emitted when animation finished
- - attribute 'mode'
- animationUpdateIndex - emitted during animation to update gui
- - attributes 'index' and 'mode'
- :param mapWindow: glWindow where rendering takes place
- :param timer: timer for recording and replaying
- """
- self.animationFinished = Signal('Animation.animationFinished')
- self.animationUpdateIndex = Signal('Animation.animationUpdateIndex')
- self.animationList = [] # view states
- self.timer = timer
- self.mapWindow = mapWindow
- self.actions = {'record': self.Record,
- 'play': self.Play}
- self.formats = ['tif', 'ppm'] # currently supported formats
- self.mode = 'record' # current mode (record, play, save)
- self.paused = False # recording/replaying paused
- self.currentFrame = 0 # index of current frame
- self.fps = 24 # user settings # Frames per second
- self.stopSaving = False # stop during saving images
- self.animationSaved = False # current animation saved or not
- def Start(self):
- """Start recording/playing"""
- self.timer.Start(self.GetInterval())
- def Pause(self):
- """Pause recording/playing"""
- self.timer.Stop()
- def Stop(self):
- """Stop recording/playing"""
- self.timer.Stop()
- self.PostFinishedEvent()
- def Update(self):
- """Record/play next view state (on timer event)"""
- self.actions[self.mode]()
- def Record(self):
- """Record new view state"""
- self.animationList.append(
- {'view': copy.deepcopy(self.mapWindow.view),
- 'iview': copy.deepcopy(self.mapWindow.iview)})
- self.currentFrame += 1
- self.PostUpdateIndexEvent(index=self.currentFrame)
- self.animationSaved = False
- def Play(self):
- """Render next frame"""
- if not self.animationList:
- self.Stop()
- return
- try:
- self.IterAnimation()
- except IndexError:
- # no more frames
- self.Stop()
- def IterAnimation(self):
- params = self.animationList[self.currentFrame]
- self.UpdateView(params)
- self.currentFrame += 1
- self.PostUpdateIndexEvent(index=self.currentFrame)
- def UpdateView(self, params):
- """Update view data in map window and render"""
- toolWin = self.mapWindow.GetToolWin()
- toolWin.UpdateState(view=params['view'], iview=params['iview'])
- self.mapWindow.UpdateView()
- self.mapWindow.render['quick'] = True
- self.mapWindow.Refresh(False)
- def IsRunning(self):
- """Test if timer is running"""
- return self.timer.IsRunning()
- def SetMode(self, mode):
- """Start animation mode
- :param mode: animation mode (record, play, save)
- """
- self.mode = mode
- def GetMode(self):
- """Get animation mode (record, play, save)"""
- return self.mode
- def IsPaused(self):
- """Test if animation is paused"""
- return self.paused
- def SetPause(self, pause):
- self.paused = pause
- def Exists(self):
- """Returns if an animation has been recorded"""
- return bool(self.animationList)
- def GetFrameCount(self):
- """Return number of recorded frames"""
- return len(self.animationList)
- def Clear(self):
- """Clear all records"""
- self.animationList = []
- self.currentFrame = 0
- def GoToFrame(self, index):
- """Render frame of given index"""
- if index >= len(self.animationList):
- return
- self.currentFrame = index
- params = self.animationList[self.currentFrame]
- self.UpdateView(params)
- def PostFinishedEvent(self):
- """Animation ends"""
- self.animationFinished.emit(mode=self.mode)
- def PostUpdateIndexEvent(self, index):
- """Frame index changed, update tool window"""
- self.animationUpdateIndex(index=index, mode=self.mode)
- def StopSaving(self):
- """Abort image files generation"""
- self.stopSaving = True
- def IsSaved(self):
- """"Test if animation has been saved (to images)"""
- return self.animationSaved
- def SaveAnimationFile(self, path, prefix, format):
- """Generate image files
- :param path: path to direcory
- :param prefix: file prefix
- :param format: index of image file format
- """
- size = self.mapWindow.GetClientSize()
- toolWin = self.mapWindow.GetToolWin()
- formatter = ':04.0f'
- n = len(self.animationList)
- if n < 10:
- formatter = ':01.0f'
- elif n < 100:
- formatter = ':02.0f'
- elif n < 1000:
- formatter = ':03.0f'
- self.currentFrame = 0
- self.mode = 'save'
- for params in self.animationList:
- if not self.stopSaving:
- self.UpdateView(params)
- number = (
- '{frame' +
- formatter +
- '}').format(
- frame=self.currentFrame)
- filename = "{prefix}_{number}.{ext}".format(
- prefix=prefix, number=number, ext=self.formats[format])
- filepath = os.path.join(path, filename)
- self.mapWindow.SaveToFile(
- FileName=filepath,
- FileType=self.formats[format],
- width=size[0],
- height=size[1])
- self.currentFrame += 1
- wx.GetApp().Yield()
- toolWin.UpdateFrameIndex(
- index=self.currentFrame, goToFrame=False)
- else:
- self.stopSaving = False
- break
- self.animationSaved = True
- self.PostFinishedEvent()
- def SetFPS(self, fps):
- """Set Frames Per Second value
- :param fps: frames per second
- """
- self.fps = fps
- def GetInterval(self):
- """Return timer interval in ms"""
- return 1000. / self.fps
|