[GRASS-SVN] r48971 - in grass/trunk: gui/wxpython/gui_modules
gui/wxpython/icons include lib/nviz
svn_grass at osgeo.org
svn_grass at osgeo.org
Thu Oct 27 15:23:21 EDT 2011
Author: annakrat
Date: 2011-10-27 12:23:21 -0700 (Thu, 27 Oct 2011)
New Revision: 48971
Modified:
grass/trunk/gui/wxpython/gui_modules/mapdisp.py
grass/trunk/gui/wxpython/gui_modules/nviz_mapdisp.py
grass/trunk/gui/wxpython/gui_modules/nviz_preferences.py
grass/trunk/gui/wxpython/gui_modules/nviz_tools.py
grass/trunk/gui/wxpython/gui_modules/preferences.py
grass/trunk/gui/wxpython/gui_modules/wxnviz.py
grass/trunk/gui/wxpython/icons/icon.py
grass/trunk/include/nviz.h
grass/trunk/lib/nviz/change_view.c
Log:
wxNviz: fly-through mode added
Modified: grass/trunk/gui/wxpython/gui_modules/mapdisp.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/mapdisp.py 2011-10-27 16:47:34 UTC (rev 48970)
+++ grass/trunk/gui/wxpython/gui_modules/mapdisp.py 2011-10-27 19:23:21 UTC (rev 48971)
@@ -599,7 +599,9 @@
self.toolbars['map'].Enable2D(False)
# add rotate tool to map toolbar
self.toolbars['map'].InsertTool((('rotate', Icons['nviz']['rotate'],
- self.OnRotate, wx.ITEM_CHECK,7),)) # 7 is position
+ self.OnRotate, wx.ITEM_CHECK, 7),)) # 7 is position
+ self.toolbars['map'].InsertTool((('flyThrough', Icons['nviz']['flyThrough'],
+ self.OnFlyThrough, wx.ITEM_CHECK, 8),))
self.toolbars['map'].ChangeToolsDesc(mode2d = False)
# update status bar
@@ -662,6 +664,7 @@
def RemoveNviz(self):
"""!Restore 2D view"""
self.toolbars['map'].RemoveTool(self.toolbars['map'].rotate)
+ self.toolbars['map'].RemoveTool(self.toolbars['map'].flyThrough)
# update status bar
self.statusbarManager.ShowStatusbarChoiceItemsByClass(self.statusbarItemsHiddenInNviz)
self.statusbarManager.SetMode(UserSettings.Get(group = 'display',
@@ -670,7 +673,7 @@
self.SetStatusText(_("Please wait, unloading data..."), 0)
self._layerManager.goutput.WriteCmdLog(_("Switching back to 2D view mode..."),
switchPage = False)
- self.MapWindow3D.UnloadDataLayers(force = True)
+ self.MapWindow3D.OnClose(event = None)
# switch from MapWindowGL to MapWindow
self._mgr.GetPane('2d').Show()
self._mgr.GetPane('3d').Hide()
@@ -827,6 +830,19 @@
# change the cursor
self.MapWindow.SetCursor(self.cursors["hand"])
+ def OnFlyThrough(self, event):
+ """!Fly-through mode
+ """
+ if self.GetMapToolbar():
+ self.toolbars['map'].OnTool(event)
+ self.toolbars['map'].action['desc'] = ''
+
+ self.MapWindow.mouse['use'] = "fly"
+
+ # change the cursor
+ self.MapWindow.SetCursor(self.cursors["hand"])
+ self.MapWindow.SetFocus()
+
def OnZoomRegion(self, event):
"""!Zoom to region
"""
Modified: grass/trunk/gui/wxpython/gui_modules/nviz_mapdisp.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/nviz_mapdisp.py 2011-10-27 16:47:34 UTC (rev 48970)
+++ grass/trunk/gui/wxpython/gui_modules/nviz_mapdisp.py 2011-10-27 19:23:21 UTC (rev 48971)
@@ -154,7 +154,11 @@
self.decoration = self.nvizDefault.SetDecorDefaultProp(type = 'arrow')
self.decoration['scalebar'] = []
self.decoration['arrow']['size'] = self._getDecorationSize()
+ self.fly = self.InitFly()
+ # timer for flythrough
+ self.timerFly = wx.Timer(self, id = wx.NewId())
+
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_PAINT, self.OnPaint)
@@ -165,14 +169,107 @@
self.Bind(EVT_UPDATE_LIGHT, self.UpdateLight)
self.Bind(EVT_UPDATE_CPLANE, self.UpdateCPlane)
+ self.Bind(wx.EVT_TIMER, self.OnTimerFly, self.timerFly)
+ self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
+ self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
+
self.Bind(wx.EVT_CLOSE, self.OnClose)
# cplanes cannot be initialized now
wx.CallAfter(self.InitCPlanes)
+ def InitFly(self):
+ """!Initialize fly through dictionary"""
+ fly = {'interval' : 10, # interval for timerFly
+ 'value': [0, 0, 0], # calculated values for navigation
+ 'mode' : 0, # fly through mode (0, 1)
+ 'exag' : { # sensitivity
+ 'move' : UserSettings.Get(group = 'nviz', key = 'fly', subkey = ['exag', 'move']),
+ 'turn' : UserSettings.Get(group = 'nviz', key = 'fly', subkey = ['exag', 'turn'])},
+ 'exagMultiplier' : 3, # speed up by Shift
+ 'mouseControl' : None, # if mouse or keys are used
+ 'pos' : {'x' : 0, 'y' : 0}, # virtual mouse position when using arrows
+ 'arrowStep' : 50, # step in pixels (when using arrows)
+ }
+
+ return fly
+
+ def OnTimerFly(self, event):
+ """!Fly event was emitted, move the scene"""
+ if self.mouse['use'] != 'fly':
+ return
+
+ if self.fly['mouseControl']:
+ mx, my = self.ComputeMxMy(*self.mouse['tmp'])
+ else:
+ mx, my = self.ComputeMxMy(self.fly['pos']['x'], self.fly['pos']['y'])
+
+ self.ComputeFlyValues(mx = mx, my = my)
+ self._display.FlyThrough(flyInfo = self.fly['value'], mode = self.fly['mode'],
+ exagInfo = self.fly['exag'])
+ self.render['quick'] = True
+ self.Refresh(False)
+
+ def ComputeMxMy(self, x, y):
+ """!Compute values for flythrough navigation
+ (ComputeFlyValues should follow).
+
+ Based on visualization/nviz/src/togl_flythrough.c.
+ @param x,y screen coordinates
+ """
+ sx, sy = self.GetClientSizeTuple()
+ dx = dy = 0.01
+
+ mx = 2 * (float(x) / sx) - 1
+ my = 2 * (float(y) / sy) - 1
+
+ if mx < - dx:
+ mx += dx
+ elif mx > dx:
+ mx -= dx
+ else:
+ mx = 0.0 # ?
+ if my < - dy:
+ my += dy
+ elif my > dy:
+ my -= dy
+ else:
+ my = 0.0
+
+ mx = mx / (1.0 - dx)
+ my = my / (1.0 - dy)
+
+ # Quadratic seems smoother
+ mx *= abs(mx)
+ my *= abs(my)
+
+ return mx, my
+
+ def ComputeFlyValues(self, mx, my):
+ """!Compute parameters for fly-through navigation
+
+ @params mx,my results from ComputeMxMy method
+ """
+ self.fly['value'] = [0, 0, 0]
+
+ if self.fly['mode'] == 0:
+ self.fly['value'][0] = - my * 500.0 * self.fly['interval'] / 1000. # forward */
+ self.fly['value'][1] = mx * 0.1 *self.fly['interval'] / 1000. # heading */
+ else:
+ self.fly['value'][0] = mx * 100.0 * self.fly['interval'] /1000.
+ self.fly['value'][2] = - my * 100.0 * self.fly['interval'] /1000.
+
+
def __del__(self):
+ """!Stop timers if running, unload data"""
+ self.StopTimer(self.timerFly)
self.UnloadDataLayers(force = True)
-
+
+ def StopTimer(self, timer):
+ """!Stop timer if running"""
+ if timer.IsRunning():
+ timer.Stop()
+
def _bindMouseEvents(self):
self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseAction)
self.Bind(wx.EVT_MOTION, self.OnMotion)
@@ -185,6 +282,7 @@
self.cplanes.append(cplane)
def OnClose(self, event):
+ self.StopTimer(self.timerFly)
# cleanup when window actually closes (on quit) and not just is hidden
self.UnloadDataLayers(force = True)
@@ -201,6 +299,13 @@
self._display.ResizeWindow(size.width,
size.height)
self.size = size
+
+ # reposition checkbox in statusbar
+ self.parent.StatusbarReposition()
+
+ # update statusbar
+ self.parent.StatusbarUpdate()
+
event.Skip()
def OnPaint(self, event):
@@ -386,8 +491,70 @@
if texture.HitTest(mouseX, mouseY, radius):
return texture.id
return -1
-
-
+
+ def OnKeyDown(self, event):
+ """!Key was pressed.
+
+ Used for fly-through mode.
+ """
+ if not self.mouse['use'] == 'fly':
+ return
+
+ key = event.GetKeyCode()
+ if key == wx.WXK_CONTROL: # Mac ?
+ self.fly['mode'] = 1
+
+ elif key == wx.WXK_SHIFT:
+ self.fly['exag']['move'] *= self.fly['exagMultiplier']
+ self.fly['exag']['turn'] *= self.fly['exagMultiplier']
+
+ elif key == wx.WXK_ESCAPE and self.timerFly.IsRunning() and not self.fly['mouseControl']:
+ self.StopTimer(self.timerFly)
+ self.fly['mouseControl'] = None
+ self.render['quick'] = False
+ self.Refresh(False)
+
+ elif key in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT, wx.WXK_RIGHT):
+ if not self.timerFly.IsRunning():
+ sx, sy = self.GetClientSizeTuple()
+ self.fly['pos']['x'] = sx / 2
+ self.fly['pos']['y'] = sy / 2
+ self.fly['mouseControl'] = False # controlled by keyboard
+ self.timerFly.Start(self.fly['interval'])
+
+ self.ProcessFlyByArrows(keyCode = key)
+
+ event.Skip()
+
+ def ProcessFlyByArrows(self, keyCode):
+ """!Process arrow key during fly-through"""
+ step = self.fly['arrowStep']
+ if keyCode == wx.WXK_UP:
+ self.fly['pos']['y'] -= step
+ elif keyCode == wx.WXK_DOWN:
+ self.fly['pos']['y'] += step
+ elif keyCode == wx.WXK_LEFT:
+ self.fly['pos']['x'] -= step
+ elif keyCode == wx.WXK_RIGHT:
+ self.fly['pos']['x'] += step
+
+ def OnKeyUp(self, event):
+ """!Key was released.
+
+ Used for fly-through mode.
+ """
+ if not self.mouse['use'] == 'fly':
+ return
+
+ key = event.GetKeyCode()
+ if key == wx.WXK_CONTROL: # Mac ?
+ self.fly['mode'] = 0
+ elif key == wx.WXK_SHIFT:
+ self.fly['exag']['move'] = math.floor(self.fly['exag']['move'] / self.fly['exagMultiplier'])
+ self.fly['exag']['turn'] = math.floor(self.fly['exag']['turn'] / self.fly['exagMultiplier'])
+
+ event.Skip()
+
def OnMouseAction(self, event):
"""!Handle mouse events"""
# zoom with mouse wheel
@@ -453,6 +620,11 @@
self.dragid = self.FindObjects(self.mouse['tmp'][0], self.mouse['tmp'][1],
self.hitradius)
+ if self.mouse['use'] == 'fly':
+ if not self.timerFly.IsRunning():
+ self.timerFly.Start(self.fly['interval'])
+ self.fly['mouseControl'] = True
+
event.Skip()
def OnDragging(self, event):
@@ -463,7 +635,7 @@
if self.mouse['use'] == 'rotate':
dx, dy = event.GetX() - self.mouse['tmp'][0], event.GetY() - self.mouse['tmp'][1]
- self.mouse['tmp'] = event.GetPositionTuple()
+
angle, x, y, z = self._display.GetRotationParameters(dx, dy)
self._display.Rotate(angle, x, y, z)
@@ -472,6 +644,8 @@
if self.mouse['use'] == 'pan':
self.FocusPanning(event)
+
+ self.mouse['tmp'] = event.GetPositionTuple()
event.Skip()
@@ -568,11 +742,19 @@
self.saveHistory = True
self.render['quick'] = False
self.Refresh(False)
+
elif self.mouse['use'] == 'pan':
self.saveHistory = True
self.render['quick'] = False
self.Refresh(False)
+ elif self.mouse['use'] == 'fly':
+ if self.fly['mouseControl']:
+ self.StopTimer(self.timerFly)
+ self.fly['mouseControl'] = None
+ self.render['quick'] = False
+ self.Refresh(False)
+
elif self.mouse['use'] == 'zoom':
self.DoZoom(zoomtype = self.zoomtype, pos = self.mouse['end'])
event.Skip()
@@ -1481,22 +1663,6 @@
if remove and item in self.layers:
self.layers.remove(item)
-
- def OnZoomToMap(self, event):
- """!Set display extents to match selected raster or vector
- map or volume.
-
- @todo vector, volume
- """
- layer = self.GetSelectedLayer()
-
- if layer is None:
- return
-
- Debug.msg (3, "GLWindow.OnZoomToMap(): layer = %s, type = %s" % \
- (layer.name, layer.type))
-
- self._display.SetViewportDefault()
def ResetView(self):
"""!Reset to default view"""
@@ -2251,8 +2417,10 @@
"""!Get display instance"""
return self._display
- def ZoomToMap(self):
+ def ZoomToMap(self, layers):
"""!Reset view
+
+ @param layers so far unused
"""
self.lmgr.nviz.OnResetView(None)
Modified: grass/trunk/gui/wxpython/gui_modules/nviz_preferences.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/nviz_preferences.py 2011-10-27 16:47:34 UTC (rev 48970)
+++ grass/trunk/gui/wxpython/gui_modules/nviz_preferences.py 2011-10-27 19:23:21 UTC (rev 48971)
@@ -36,6 +36,7 @@
# create notebook pages
self._createViewPage(self.notebook)
+ self._createFlyPage(self.notebook)
self._createLightPage(self.notebook)
self._createSurfacePage(self.notebook)
self._createVectorPage(self.notebook)
@@ -192,7 +193,55 @@
panel.SetSizer(pageSizer)
return panel
-
+
+ def _createFlyPage(self, notebook):
+ """!Create notebook page for view settings"""
+ panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+
+ notebook.AddPage(page = panel,
+ text = " %s " % _("Fly-through"))
+ pageSizer = wx.BoxSizer(wx.VERTICAL)
+ # fly throuhg mode
+ box = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+ label = " %s " % (_("Fly-through mode")))
+ boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+ gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+ gridSizer.AddGrowableCol(0)
+
+ # move exag
+ gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+ label = _("Move exag:")),
+ pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+
+ moveExag = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 1, max = 20,
+ initial = UserSettings.Get(group = 'nviz', key = 'fly',
+ subkey = ['exag', 'move']),
+ size = (65, -1))
+ self.winId['nviz:fly:exag:move'] = moveExag.GetId()
+ gridSizer.Add(item = moveExag, pos = (0, 1))
+
+ # turn exag
+ gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+ label = _("Turn exag:")),
+ pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+
+ turnExag = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 1, max = 20,
+ initial = UserSettings.Get(group = 'nviz', key = 'fly',
+ subkey = ['exag', 'turn']),
+ size = (65, -1))
+ self.winId['nviz:fly:exag:turn'] = turnExag.GetId()
+ gridSizer.Add(item = turnExag, pos = (1, 1))
+
+ boxSizer.Add(item = gridSizer, proportion = 1,
+ flag = wx.ALL | wx.EXPAND, border = 5)
+ pageSizer.Add(item = boxSizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALL,
+ border = 5)
+
+ panel.SetSizer(pageSizer)
+
+ return panel
+
def _createLightPage(self, notebook):
"""!Create notebook page for light settings"""
panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
Modified: grass/trunk/gui/wxpython/gui_modules/nviz_tools.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/nviz_tools.py 2011-10-27 16:47:34 UTC (rev 48970)
+++ grass/trunk/gui/wxpython/gui_modules/nviz_tools.py 2011-10-27 19:23:21 UTC (rev 48971)
@@ -239,12 +239,15 @@
self.mapWindow.light = kwargs['light']
self.FindWindowById(self.win['light']['position']).data = kwargs['light']
self.FindWindowById(self.win['light']['position']).PostDraw()
+ if 'fly' in kwargs:
+ self.mapWindow.fly['exag'] = kwargs['fly']['exag']
def LoadSettings(self):
"""!Load Nviz settings and apply to current session"""
view = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'view')) # copy
light = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'light')) # copy
- self.UpdateState(view = view, light = light)
+ fly = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'fly')) # copy
+ self.UpdateState(view = view, light = light, fly = fly)
self.PostViewEvent(zExag = True)
self.PostLightEvent()
self.UpdatePage('view')
Modified: grass/trunk/gui/wxpython/gui_modules/preferences.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/preferences.py 2011-10-27 16:47:34 UTC (rev 48970)
+++ grass/trunk/gui/wxpython/gui_modules/preferences.py 2011-10-27 19:23:21 UTC (rev 48971)
@@ -543,6 +543,12 @@
'color' : (255, 255, 255, 255), # white
},
},
+ 'fly' : {
+ 'exag' : {
+ 'move' : 1,
+ 'turn' : 1,
+ }
+ },
'surface' : {
'shine': {
'map' : False,
Modified: grass/trunk/gui/wxpython/gui_modules/wxnviz.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/wxnviz.py 2011-10-27 16:47:34 UTC (rev 48970)
+++ grass/trunk/gui/wxpython/gui_modules/wxnviz.py 2011-10-27 19:23:21 UTC (rev 48971)
@@ -1860,7 +1860,21 @@
def Start2D(self):
Nviz_set_2D(self.width, self.height)
+ def FlyThrough(self, flyInfo, mode, exagInfo):
+ """!Fly through the scene
+ @param flyInfo fly parameters
+ @param mode 0 or 1 for different fly behaviour
+ @param exagInfo parameters changing fly speed
+ """
+ fly = (c_float * 3)()
+ for i, item in enumerate(flyInfo):
+ fly[i] = item
+ exag = (c_int * 2)()
+ exag[0] = int(exagInfo['move'])
+ exag[1] = int(exagInfo['turn'])
+ Nviz_flythrough(self.data, fly, exag, mode)
+
class Texture(object):
"""!Class representing OpenGL texture"""
def __init__(self, filepath, overlayId, coords):
Modified: grass/trunk/gui/wxpython/icons/icon.py
===================================================================
--- grass/trunk/gui/wxpython/icons/icon.py 2011-10-27 16:47:34 UTC (rev 48970)
+++ grass/trunk/gui/wxpython/icons/icon.py 2011-10-27 19:23:21 UTC (rev 48971)
@@ -342,6 +342,10 @@
'rotate': MetaIcon(img = iconSet.get('3d-rotate', wx.ART_ERROR),
label = _('Rotate 3D scene'),
desc = _('Drag with mouse to rotate 3D scene')),
+ 'flyThrough': MetaIcon(img = iconSet.get('', wx.ART_MISSING_IMAGE),
+ label = _('Fly-through mode'),
+ desc = _('Drag with mouse, hold Ctrl down for different mode'
+ ' or Shift to accelerate')),
'zoomIn': MetaIcon(img = iconSet.get('zoom-in', wx.ART_ERROR),
label = _('Zoom in'),
desc = _('Click mouse to zoom')),
Modified: grass/trunk/include/nviz.h
===================================================================
--- grass/trunk/include/nviz.h 2011-10-27 16:47:34 UTC (rev 48970)
+++ grass/trunk/include/nviz.h 2011-10-27 19:23:21 UTC (rev 48971)
@@ -157,6 +157,7 @@
void Nviz_set_rotation(double, double, double, double);
void Nviz_unset_rotation(void);
void Nviz_init_rotation(void);
+void Nviz_flythrough(nv_data *, float *, int *, int);
/* cplanes_obj.c */
int Nviz_new_cplane(nv_data *, int);
Modified: grass/trunk/lib/nviz/change_view.c
===================================================================
--- grass/trunk/lib/nviz/change_view.c 2011-10-27 16:47:34 UTC (rev 48970)
+++ grass/trunk/lib/nviz/change_view.c 2011-10-27 19:23:21 UTC (rev 48971)
@@ -12,6 +12,8 @@
\author Updated/modified by Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
*/
+#include <math.h>
+
#include <grass/glocale.h>
#include <grass/nviz.h>
@@ -275,3 +277,73 @@
{
GS_init_rotation();
}
+
+/*!
+ \brief Fly through the scene
+
+ Computes parameters needed for moving scene.
+ Changes viewpoint and viewdir.
+ Based on visualization/nviz/src/togl_flythrough.c and simplified.
+
+ \param fly_info values computed from mouse movement
+ \param scale rate of movement
+ \param lateral type of movement
+
+*/
+void Nviz_flythrough(nv_data *data, float *fly_info, int *scale, int lateral)
+{
+ float dir[3], from[4], cur_from[4], cur_dir[4], cur[3];
+ float speed, h, p, sh, ch, sp, cp;
+ float diff_x, diff_y, diff_z;
+ float quasi_zero;
+
+ quasi_zero = 0.0001;
+
+ GS_get_from(cur_from);
+ GS_get_viewdir(cur_dir);
+
+ p = asin(cur_dir[Z]);
+ h = atan2(- cur_dir[X], - cur_dir[Y]);
+
+ speed = scale[0] * fly_info[0];
+
+ h += scale[1] * fly_info[1]; /* change heading */
+ //if (!lateral) /* in case of "lateral" doesn't change pitch */
+ //p -= scale * fly_info[2];
+
+ h = fmod(h + M_PI, 2 * M_PI) - M_PI;
+
+ sh = sin(h);
+ ch = cos(h);
+ sp = sin(p);
+ cp = cos(p);
+
+ dir[X] = -sh * cp;
+ dir[Y] = -ch * cp;
+ dir[Z] = sp;
+
+ if (lateral) {
+ from[X] = cur_from[X] + speed * dir[Y];
+ from[Y] = cur_from[Y] - speed * dir[X];
+ from[Z] = cur_from[Z] + scale[0] * fly_info[2];
+ }
+ else {
+ from[X] = cur_from[X] + speed * dir[X];
+ from[Y] = cur_from[Y] + speed * dir[Y];
+ /* not sure how this should behave (change Z coord or not ?)*/
+ from[Z] = cur_from[Z]; /* + speed * dir[Z]*/
+ }
+
+ diff_x = fabs(cur_dir[X] - dir[X]);
+ diff_y = fabs(cur_dir[Y] - dir[Y]);
+ diff_z = fabs(cur_dir[Z] - dir[Z]);
+
+ if ( /* something has changed */
+ (diff_x > quasi_zero) || (diff_y > quasi_zero) ||
+ (diff_z > quasi_zero) || (cur_from[X] != from[X]) ||
+ (cur_from[Y] != from[Y]) || (cur_from[Z] != from[Z])
+ ) {
+ GS_moveto(from);
+ GS_set_viewdir(dir); /* calls gsd_set_view */
+ }
+}
More information about the grass-commit
mailing list