"""Class used to create and control VRPN devices."""
from direct.showbase.DirectObject import DirectObject
from panda3d.core import *
from panda3d.vrpn import *
ANALOG_MIN = -0.95
ANALOG_MAX = 0.95
ANALOG_DEADBAND = 0.125
ANALOG_CENTER = 0.0
[docs]class DirectDeviceManager(VrpnClient, DirectObject):
[docs] def __init__(self, server = None):
# Determine which server to use
if server is not None:
# One given as constructor argument
self.server = server
else:
# Check config file, if that fails, use default
self.server = ConfigVariableString('vrpn-server', 'spacedyne').getValue()
# Create a vrpn client
VrpnClient.__init__(self, self.server)
[docs] def createAnalogs(self, device):
return DirectAnalogs(self, device)
[docs] def createTracker(self, device):
return DirectTracker(self, device)
[docs] def createDials(self, device):
return DirectDials(self, device)
[docs] def createTimecodeReader(self, device):
return DirectTimecodeReader(self, device)
[docs]class DirectAnalogs(AnalogNode, DirectObject):
analogCount = 0
_analogDeadband = ConfigVariableDouble('vrpn-analog-deadband', ANALOG_DEADBAND)
_analogMin = ConfigVariableDouble('vrpn-analog-min', ANALOG_MIN)
_analogMax = ConfigVariableDouble('vrpn-analog-max', ANALOG_MAX)
_analogCenter = ConfigVariableDouble('vrpn-analog-center', ANALOG_CENTER)
[docs] def __init__(self, vrpnClient, device):
# Keep track of number of analogs created
DirectAnalogs.analogCount += 1
# Create a new analog node for the given device
AnalogNode.__init__(self, vrpnClient, device)
# Create a unique name for this analog object
self.name = 'DirectAnalogs-' + repr(DirectAnalogs.analogCount)
# Attach node to data graph
try:
self._base = base
except:
self._base = simbase
self.nodePath = self._base.dataRoot.attachNewNode(self)
# See if any of the general analog parameters are dconfig'd
self.analogDeadband = self._analogDeadband.getValue()
self.analogMin = self._analogMin.getValue()
self.analogMax = self._analogMax.getValue()
self.analogCenter = self._analogCenter.getValue()
self.analogRange = self.analogMax - self.analogMin
def __getitem__(self, index):
if (index < 0) or (index >= self.getNumControls()):
raise IndexError
return self.getControlState(index)
def __len__(self):
return self.getNumControls()
[docs] def enable(self):
self.nodePath.reparentTo(self._base.dataRoot)
[docs] def disable(self):
self.nodePath.reparentTo(self._base.dataUnused)
[docs] def normalizeWithoutCentering(self, val, minVal = -1, maxVal = 1):
#
# This is the old code that doesn't incorporate the centering fix
#
# First record sign
if val < 0:
sign = -1
else:
sign = 1
# Zero out values in deadband
val = sign * max(abs(val) - self.analogDeadband, 0.0)
# Clamp value between analog range min and max and scale about center
val = min(max(val, self.analogMin), self.analogMax)
# Normalize values to given minVal and maxVal range
return (((maxVal - minVal) *
((val - self.analogMin) / float(self.analogRange))) + minVal)
[docs] def normalize(self, rawValue, minVal = -1, maxVal = 1, sf = 1.0):
aMax = self.analogMax
aMin = self.analogMin
center = self.analogCenter
deadband = self.analogDeadband
# Zero out values in deadband
if abs(rawValue - center) <= deadband:
return 0.0
# Clamp value between aMin and aMax and scale around center
if rawValue >= center:
# Convert positive values to range 0 to 1
val = min(rawValue * sf, aMax)
percentVal = ((val - (center + deadband))/
float(aMax - (center + deadband)))
else:
# Convert negative values to range -1 to 0
val = max(rawValue * sf, aMin)
percentVal = -((val - (center - deadband))/
float(aMin - (center - deadband)))
# Normalize values to given minVal and maxVal range
return ((maxVal - minVal) * ((percentVal + 1)/2.0)) + minVal
[docs] def normalizeChannel(self, chan, minVal = -1, maxVal = 1, sf = 1.0):
try:
return self.normalize(self[chan], minVal, maxVal, sfx)
except IndexError:
return 0.0
[docs] def getName(self):
return self.name
[docs] def getNodePath(self):
return self.nodePath
def __repr__(self):
string = self.name + ': '
for val in self:
string = string + '%.3f' % val + ' '
return string
[docs]class DirectTracker(TrackerNode, DirectObject):
trackerCount = 0
[docs] def __init__(self, vrpnClient, device):
# Keep track of number of trackers created
DirectTracker.trackerCount += 1
# Create a new tracker node for the given device
TrackerNode.__init__(self, vrpnClient, device)
# Create a unique name for this tracker object
self.name = 'DirectTracker-' + repr(DirectTracker.trackerCount)
# Attach node to data graph
try:
self._base = base
except:
self._base = simbase
self.nodePath = self._base.dataRoot.attachNewNode(self)
[docs] def enable(self):
self.nodePath.reparentTo(self._base.dataRoot)
[docs] def disable(self):
self.nodePath.reparentTo(self._base.dataUnused)
[docs] def getName(self):
return self.name
[docs] def getNodePath(self):
return self.nodePath
def __repr__(self):
return self.name
[docs]class DirectDials(DialNode, DirectObject):
dialCount = 0
[docs] def __init__(self, vrpnClient, device):
# Keep track of number of dials created
DirectDials.dialCount += 1
# Create a new dial node for the given device
DialNode.__init__(self, vrpnClient, device)
# Create a unique name for this dial object
self.name = 'DirectDials-' + repr(DirectDials.dialCount)
# Attach node to data graph
try:
self._base = base
except:
self._base = simbase
self.nodePath = self._base.dataRoot.attachNewNode(self)
def __getitem__(self, index):
"""
if (index < 0) or (index >= self.getNumDials()):
raise IndexError
"""
return self.readDial(index)
def __len__(self):
return self.getNumDials()
[docs] def enable(self):
self.nodePath.reparentTo(self._base.dataRoot)
[docs] def disable(self):
self.nodePath.reparentTo(self._base.dataUnused)
[docs] def getName(self):
return self.name
[docs] def getNodePath(self):
return self.nodePath
def __repr__(self):
string = self.name + ': '
for i in range(self.getNumDials()):
string = string + '%.3f' % self[i] + ' '
return string
[docs]class DirectTimecodeReader(AnalogNode, DirectObject):
timecodeReaderCount = 0
[docs] def __init__(self, vrpnClient, device):
# Keep track of number of timecodeReader created
DirectTimecodeReader.timecodeReaderCount += 1
# Create a new dial node for the given device
AnalogNode.__init__(self, vrpnClient, device)
# Create a unique name for this dial object
self.name = ('DirectTimecodeReader-' +
repr(DirectTimecodeReader.timecodeReaderCount))
# Initialize components of timecode
self.frames = 0
self.seconds = 0
self.minutes = 0
self.hours = 0
# Attach node to data graph
try:
self._base = base
except:
self._base = simbase
self.nodePath = self._base.dataRoot.attachNewNode(self)
[docs] def enable(self):
self.nodePath.reparentTo(self._base.dataRoot)
[docs] def disable(self):
self.nodePath.reparentTo(self._base.dataUnused)
[docs] def getName(self):
return self.name
[docs] def getNodePath(self):
return self.nodePath
[docs] def getTime(self):
# Assume only one card, use channel 0
timeBits = int(self.getControlState(0))
self.frames = ((timeBits & 0xF) +
(((timeBits & 0xF0) >> 4) * 10))
self.seconds = (((timeBits & 0x0F00) >> 8) +
(((timeBits & 0xF000) >> 12) * 10))
self.minutes = (((timeBits & 0x0F0000) >> 16) +
(((timeBits & 0xF00000) >> 20) * 10))
self.hours = (((timeBits & 0xF000000) >> 24) +
(((timeBits & 0xF0000000) >> 28) * 10))
self.totalSeconds = ((self.hours * 3600) +
(self.minutes * 60) +
self.seconds +
(self.frames / 30.0))
return (self.hours, self.minutes, self.seconds, self.frames,
self.totalSeconds)
def __repr__(self):
string = ('%s: %d:%d:%d:%d' % ((self.name,) + self.getTime()[:-1]))
return string