pyqtgraph/examples/relativity/relativity.py

777 lines
28 KiB
Python
Raw Permalink Normal View History

import numpy as np
import collections
import sys, os
2014-06-14 17:35:00 +00:00
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore
from pyqtgraph.parametertree import Parameter, ParameterTree
from pyqtgraph.parametertree import types as pTypes
import pyqtgraph.configfile
from pyqtgraph.python2_3 import xrange
2014-06-14 17:35:00 +00:00
class RelativityGUI(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.animations = []
self.animTimer = QtCore.QTimer()
self.animTimer.timeout.connect(self.stepAnimation)
self.animTime = 0
self.animDt = .016
self.lastAnimTime = 0
self.setupGUI()
self.objectGroup = ObjectGroupParam()
self.params = Parameter.create(name='params', type='group', children=[
dict(name='Load Preset..', type='list', values=[]),
#dict(name='Unit System', type='list', values=['', 'MKS']),
dict(name='Duration', type='float', value=10.0, step=0.1, limits=[0.1, None]),
dict(name='Reference Frame', type='list', values=[]),
dict(name='Animate', type='bool', value=True),
dict(name='Animation Speed', type='float', value=1.0, dec=True, step=0.1, limits=[0.0001, None]),
dict(name='Recalculate Worldlines', type='action'),
dict(name='Save', type='action'),
dict(name='Load', type='action'),
self.objectGroup,
])
self.tree.setParameters(self.params, showTop=False)
self.params.param('Recalculate Worldlines').sigActivated.connect(self.recalculate)
self.params.param('Save').sigActivated.connect(self.save)
self.params.param('Load').sigActivated.connect(self.load)
self.params.param('Load Preset..').sigValueChanged.connect(self.loadPreset)
self.params.sigTreeStateChanged.connect(self.treeChanged)
## read list of preset configs
presetDir = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'presets')
if os.path.exists(presetDir):
presets = [os.path.splitext(p)[0] for p in os.listdir(presetDir)]
self.params.param('Load Preset..').setLimits(['']+presets)
def setupGUI(self):
self.layout = QtGui.QVBoxLayout()
self.layout.setContentsMargins(0,0,0,0)
self.setLayout(self.layout)
self.splitter = QtGui.QSplitter()
self.splitter.setOrientation(QtCore.Qt.Horizontal)
self.layout.addWidget(self.splitter)
self.tree = ParameterTree(showHeader=False)
self.splitter.addWidget(self.tree)
self.splitter2 = QtGui.QSplitter()
self.splitter2.setOrientation(QtCore.Qt.Vertical)
self.splitter.addWidget(self.splitter2)
self.worldlinePlots = pg.GraphicsLayoutWidget()
self.splitter2.addWidget(self.worldlinePlots)
self.animationPlots = pg.GraphicsLayoutWidget()
self.splitter2.addWidget(self.animationPlots)
self.splitter2.setSizes([int(self.height()*0.8), int(self.height()*0.2)])
self.inertWorldlinePlot = self.worldlinePlots.addPlot()
self.refWorldlinePlot = self.worldlinePlots.addPlot()
self.inertAnimationPlot = self.animationPlots.addPlot()
self.inertAnimationPlot.setAspectLocked(1)
self.refAnimationPlot = self.animationPlots.addPlot()
self.refAnimationPlot.setAspectLocked(1)
self.inertAnimationPlot.setXLink(self.inertWorldlinePlot)
self.refAnimationPlot.setXLink(self.refWorldlinePlot)
def recalculate(self):
## build 2 sets of clocks
clocks1 = collections.OrderedDict()
clocks2 = collections.OrderedDict()
for cl in self.params.param('Objects'):
clocks1.update(cl.buildClocks())
clocks2.update(cl.buildClocks())
## Inertial simulation
dt = self.animDt * self.params['Animation Speed']
sim1 = Simulation(clocks1, ref=None, duration=self.params['Duration'], dt=dt)
sim1.run()
sim1.plot(self.inertWorldlinePlot)
self.inertWorldlinePlot.autoRange(padding=0.1)
## reference simulation
ref = self.params['Reference Frame']
dur = clocks1[ref].refData['pt'][-1] ## decide how long to run the reference simulation
sim2 = Simulation(clocks2, ref=clocks2[ref], duration=dur, dt=dt)
sim2.run()
sim2.plot(self.refWorldlinePlot)
self.refWorldlinePlot.autoRange(padding=0.1)
## create animations
self.refAnimationPlot.clear()
self.inertAnimationPlot.clear()
self.animTime = 0
self.animations = [Animation(sim1), Animation(sim2)]
self.inertAnimationPlot.addItem(self.animations[0])
self.refAnimationPlot.addItem(self.animations[1])
## create lines representing all that is visible to a particular reference
#self.inertSpaceline = Spaceline(sim1, ref)
#self.refSpaceline = Spaceline(sim2)
self.inertWorldlinePlot.addItem(self.animations[0].items[ref].spaceline())
self.refWorldlinePlot.addItem(self.animations[1].items[ref].spaceline())
def setAnimation(self, a):
if a:
self.lastAnimTime = pg.ptime.time()
self.animTimer.start(self.animDt*1000)
else:
self.animTimer.stop()
def stepAnimation(self):
now = pg.ptime.time()
dt = (now-self.lastAnimTime) * self.params['Animation Speed']
self.lastAnimTime = now
self.animTime += dt
if self.animTime > self.params['Duration']:
self.animTime = 0
for a in self.animations:
a.restart()
for a in self.animations:
a.stepTo(self.animTime)
def treeChanged(self, *args):
clocks = []
for c in self.params.param('Objects'):
clocks.extend(c.clockNames())
#for param, change, data in args[1]:
#if change == 'childAdded':
self.params.param('Reference Frame').setLimits(clocks)
self.setAnimation(self.params['Animate'])
def save(self):
2018-06-23 00:31:58 +00:00
filename = pg.QtGui.QFileDialog.getSaveFileName(self, "Save State..", "untitled.cfg", "Config Files (*.cfg)")
if isinstance(filename, tuple):
filename = filename[0] # Qt4/5 API difference
if filename == '':
2014-06-14 17:35:00 +00:00
return
state = self.params.saveState()
2018-06-23 00:31:58 +00:00
pg.configfile.writeConfigFile(state, str(filename))
2014-06-14 17:35:00 +00:00
def load(self):
2018-06-23 00:31:58 +00:00
filename = pg.QtGui.QFileDialog.getOpenFileName(self, "Save State..", "", "Config Files (*.cfg)")
if isinstance(filename, tuple):
filename = filename[0] # Qt4/5 API difference
if filename == '':
2014-06-14 17:35:00 +00:00
return
2018-06-23 00:31:58 +00:00
state = pg.configfile.readConfigFile(str(filename))
2014-06-14 17:35:00 +00:00
self.loadState(state)
def loadPreset(self, param, preset):
if preset == '':
return
path = os.path.abspath(os.path.dirname(__file__))
fn = os.path.join(path, 'presets', preset+".cfg")
state = pg.configfile.readConfigFile(fn)
self.loadState(state)
def loadState(self, state):
if 'Load Preset..' in state['children']:
del state['children']['Load Preset..']['limits']
del state['children']['Load Preset..']['value']
self.params.param('Objects').clearChildren()
self.params.restoreState(state, removeChildren=False)
self.recalculate()
class ObjectGroupParam(pTypes.GroupParameter):
def __init__(self):
pTypes.GroupParameter.__init__(self, name="Objects", addText="Add New..", addList=['Clock', 'Grid'])
def addNew(self, typ):
if typ == 'Clock':
self.addChild(ClockParam())
elif typ == 'Grid':
self.addChild(GridParam())
class ClockParam(pTypes.GroupParameter):
def __init__(self, **kwds):
defs = dict(name="Clock", autoIncrementName=True, renamable=True, removable=True, children=[
dict(name='Initial Position', type='float', value=0.0, step=0.1),
#dict(name='V0', type='float', value=0.0, step=0.1),
AccelerationGroup(),
dict(name='Rest Mass', type='float', value=1.0, step=0.1, limits=[1e-9, None]),
dict(name='Color', type='color', value=(100,100,150)),
dict(name='Size', type='float', value=0.5),
dict(name='Vertical Position', type='float', value=0.0, step=0.1),
])
#defs.update(kwds)
pTypes.GroupParameter.__init__(self, **defs)
self.restoreState(kwds, removeChildren=False)
def buildClocks(self):
x0 = self['Initial Position']
y0 = self['Vertical Position']
color = self['Color']
m = self['Rest Mass']
size = self['Size']
prog = self.param('Acceleration').generate()
c = Clock(x0=x0, m0=m, y0=y0, color=color, prog=prog, size=size)
return {self.name(): c}
def clockNames(self):
return [self.name()]
pTypes.registerParameterType('Clock', ClockParam)
class GridParam(pTypes.GroupParameter):
def __init__(self, **kwds):
defs = dict(name="Grid", autoIncrementName=True, renamable=True, removable=True, children=[
dict(name='Number of Clocks', type='int', value=5, limits=[1, None]),
dict(name='Spacing', type='float', value=1.0, step=0.1),
ClockParam(name='ClockTemplate'),
])
#defs.update(kwds)
pTypes.GroupParameter.__init__(self, **defs)
self.restoreState(kwds, removeChildren=False)
def buildClocks(self):
clocks = {}
template = self.param('ClockTemplate')
spacing = self['Spacing']
for i in range(self['Number of Clocks']):
c = list(template.buildClocks().values())[0]
2014-06-14 17:35:00 +00:00
c.x0 += i * spacing
clocks[self.name() + '%02d' % i] = c
return clocks
def clockNames(self):
return [self.name() + '%02d' % i for i in range(self['Number of Clocks'])]
pTypes.registerParameterType('Grid', GridParam)
class AccelerationGroup(pTypes.GroupParameter):
def __init__(self, **kwds):
defs = dict(name="Acceleration", addText="Add Command..")
pTypes.GroupParameter.__init__(self, **defs)
self.restoreState(kwds, removeChildren=False)
def addNew(self):
nextTime = 0.0
if self.hasChildren():
nextTime = self.children()[-1]['Proper Time'] + 1
self.addChild(Parameter.create(name='Command', autoIncrementName=True, type=None, renamable=True, removable=True, children=[
dict(name='Proper Time', type='float', value=nextTime),
dict(name='Acceleration', type='float', value=0.0, step=0.1),
]))
def generate(self):
prog = []
for cmd in self:
prog.append((cmd['Proper Time'], cmd['Acceleration']))
return prog
pTypes.registerParameterType('AccelerationGroup', AccelerationGroup)
class Clock(object):
nClocks = 0
def __init__(self, x0=0.0, y0=0.0, m0=1.0, v0=0.0, t0=0.0, color=None, prog=None, size=0.5):
Clock.nClocks += 1
self.pen = pg.mkPen(color)
self.brush = pg.mkBrush(color)
self.y0 = y0
self.x0 = x0
self.v0 = v0
self.m0 = m0
self.t0 = t0
self.prog = prog
self.size = size
def init(self, nPts):
## Keep records of object from inertial frame as well as reference frame
self.inertData = np.empty(nPts, dtype=[('x', float), ('t', float), ('v', float), ('pt', float), ('m', float), ('f', float)])
self.refData = np.empty(nPts, dtype=[('x', float), ('t', float), ('v', float), ('pt', float), ('m', float), ('f', float)])
## Inertial frame variables
self.x = self.x0
self.v = self.v0
self.m = self.m0
self.t = 0.0 ## reference clock always starts at 0
self.pt = self.t0 ## proper time starts at t0
## reference frame variables
self.refx = None
self.refv = None
self.refm = None
self.reft = None
self.recordFrame(0)
def recordFrame(self, i):
f = self.force()
self.inertData[i] = (self.x, self.t, self.v, self.pt, self.m, f)
self.refData[i] = (self.refx, self.reft, self.refv, self.pt, self.refm, f)
def force(self, t=None):
if len(self.prog) == 0:
return 0.0
if t is None:
t = self.pt
ret = 0.0
for t1,f in self.prog:
if t >= t1:
ret = f
return ret
def acceleration(self, t=None):
return self.force(t) / self.m0
def accelLimits(self):
## return the proper time values which bound the current acceleration command
if len(self.prog) == 0:
return -np.inf, np.inf
t = self.pt
ind = -1
for i, v in enumerate(self.prog):
t1,f = v
if t >= t1:
ind = i
if ind == -1:
return -np.inf, self.prog[0][0]
elif ind == len(self.prog)-1:
return self.prog[-1][0], np.inf
else:
return self.prog[ind][0], self.prog[ind+1][0]
def getCurve(self, ref=True):
if ref is False:
data = self.inertData
else:
data = self.refData[1:]
x = data['x']
y = data['t']
curve = pg.PlotCurveItem(x=x, y=y, pen=self.pen)
#x = self.data['x'] - ref.data['x']
#y = self.data['t']
step = 1.0
#mod = self.data['pt'] % step
#inds = np.argwhere(abs(mod[1:] - mod[:-1]) > step*0.9)
inds = [0]
pt = data['pt']
for i in range(1,len(pt)):
diff = pt[i] - pt[inds[-1]]
if abs(diff) >= step:
inds.append(i)
inds = np.array(inds)
#t = self.data['t'][inds]
#x = self.data['x'][inds]
pts = []
for i in inds:
x = data['x'][i]
y = data['t'][i]
if i+1 < len(data):
dpt = data['pt'][i+1]-data['pt'][i]
dt = data['t'][i+1]-data['t'][i]
else:
dpt = 1
if dpt > 0:
c = pg.mkBrush((0,0,0))
else:
c = pg.mkBrush((200,200,200))
pts.append({'pos': (x, y), 'brush': c})
points = pg.ScatterPlotItem(pts, pen=self.pen, size=7)
return curve, points
class Simulation:
def __init__(self, clocks, ref, duration, dt):
self.clocks = clocks
self.ref = ref
self.duration = duration
self.dt = dt
@staticmethod
def hypTStep(dt, v0, x0, tau0, g):
## Hyperbolic step.
## If an object has proper acceleration g and starts at position x0 with speed v0 and proper time tau0
## as seen from an inertial frame, then return the new v, x, tau after time dt has elapsed.
if g == 0:
return v0, x0 + v0*dt, tau0 + dt * (1. - v0**2)**0.5
v02 = v0**2
g2 = g**2
tinit = v0 / (g * (1 - v02)**0.5)
B = (1 + (g2 * (dt+tinit)**2))**0.5
v1 = g * (dt+tinit) / B
dtau = (np.arcsinh(g * (dt+tinit)) - np.arcsinh(g * tinit)) / g
tau1 = tau0 + dtau
x1 = x0 + (1.0 / g) * ( B - 1. / (1.-v02)**0.5 )
return v1, x1, tau1
@staticmethod
def tStep(dt, v0, x0, tau0, g):
## Linear step.
## Probably not as accurate as hyperbolic step, but certainly much faster.
gamma = (1. - v0**2)**-0.5
dtau = dt / gamma
return v0 + dtau * g, x0 + v0*dt, tau0 + dtau
@staticmethod
def tauStep(dtau, v0, x0, t0, g):
## linear step in proper time of clock.
## If an object has proper acceleration g and starts at position x0 with speed v0 at time t0
## as seen from an inertial frame, then return the new v, x, t after proper time dtau has elapsed.
## Compute how much t will change given a proper-time step of dtau
gamma = (1. - v0**2)**-0.5
if g == 0:
dt = dtau * gamma
else:
v0g = v0 * gamma
dt = (np.sinh(dtau * g + np.arcsinh(v0g)) - v0g) / g
#return v0 + dtau * g, x0 + v0*dt, t0 + dt
v1, x1, t1 = Simulation.hypTStep(dt, v0, x0, t0, g)
return v1, x1, t0+dt
@staticmethod
def hypIntersect(x0r, t0r, vr, x0, t0, v0, g):
## given a reference clock (seen from inertial frame) has rx, rt, and rv,
## and another clock starts at x0, t0, and v0, with acceleration g,
## compute the intersection time of the object clock's hyperbolic path with
## the reference plane.
## I'm sure we can simplify this...
if g == 0: ## no acceleration, path is linear (and hyperbola is undefined)
#(-t0r + t0 v0 vr - vr x0 + vr x0r)/(-1 + v0 vr)
t = (-t0r + t0 *v0 *vr - vr *x0 + vr *x0r)/(-1 + v0 *vr)
return t
gamma = (1.0-v0**2)**-0.5
sel = (1 if g>0 else 0) + (1 if vr<0 else 0)
sel = sel%2
if sel == 0:
#(1/(g^2 (-1 + vr^2)))(-g^2 t0r + g gamma vr + g^2 t0 vr^2 -
#g gamma v0 vr^2 - g^2 vr x0 +
#g^2 vr x0r + \[Sqrt](g^2 vr^2 (1 + gamma^2 (v0 - vr)^2 - vr^2 +
#2 g gamma (v0 - vr) (-t0 + t0r + vr (x0 - x0r)) +
#g^2 (t0 - t0r + vr (-x0 + x0r))^2)))
t = (1./(g**2 *(-1. + vr**2)))*(-g**2 *t0r + g *gamma *vr + g**2 *t0 *vr**2 - g *gamma *v0 *vr**2 - g**2 *vr *x0 + g**2 *vr *x0r + np.sqrt(g**2 *vr**2 *(1. + gamma**2 *(v0 - vr)**2 - vr**2 + 2 *g *gamma *(v0 - vr)* (-t0 + t0r + vr *(x0 - x0r)) + g**2 *(t0 - t0r + vr* (-x0 + x0r))**2)))
else:
#-(1/(g^2 (-1 + vr^2)))(g^2 t0r - g gamma vr - g^2 t0 vr^2 +
#g gamma v0 vr^2 + g^2 vr x0 -
#g^2 vr x0r + \[Sqrt](g^2 vr^2 (1 + gamma^2 (v0 - vr)^2 - vr^2 +
#2 g gamma (v0 - vr) (-t0 + t0r + vr (x0 - x0r)) +
#g^2 (t0 - t0r + vr (-x0 + x0r))^2)))
t = -(1./(g**2 *(-1. + vr**2)))*(g**2 *t0r - g *gamma* vr - g**2 *t0 *vr**2 + g *gamma *v0 *vr**2 + g**2* vr* x0 - g**2 *vr *x0r + np.sqrt(g**2* vr**2 *(1. + gamma**2 *(v0 - vr)**2 - vr**2 + 2 *g *gamma *(v0 - vr) *(-t0 + t0r + vr *(x0 - x0r)) + g**2 *(t0 - t0r + vr *(-x0 + x0r))**2)))
return t
def run(self):
nPts = int(self.duration/self.dt)+1
for cl in self.clocks.values():
2014-06-14 17:35:00 +00:00
cl.init(nPts)
if self.ref is None:
self.runInertial(nPts)
else:
self.runReference(nPts)
def runInertial(self, nPts):
clocks = self.clocks
dt = self.dt
tVals = np.linspace(0, dt*(nPts-1), nPts)
for cl in self.clocks.values():
2014-06-14 17:35:00 +00:00
for i in xrange(1,nPts):
nextT = tVals[i]
while True:
tau1, tau2 = cl.accelLimits()
x = cl.x
v = cl.v
tau = cl.pt
g = cl.acceleration()
v1, x1, tau1 = self.hypTStep(dt, v, x, tau, g)
if tau1 > tau2:
dtau = tau2-tau
cl.v, cl.x, cl.t = self.tauStep(dtau, v, x, cl.t, g)
cl.pt = tau2
else:
cl.v, cl.x, cl.pt = v1, x1, tau1
cl.t += dt
if cl.t >= nextT:
cl.refx = cl.x
cl.refv = cl.v
cl.reft = cl.t
cl.recordFrame(i)
break
def runReference(self, nPts):
clocks = self.clocks
ref = self.ref
dt = self.dt
dur = self.duration
## make sure reference clock is not present in the list of clocks--this will be handled separately.
clocks = clocks.copy()
for k,v in clocks.items():
2014-06-14 17:35:00 +00:00
if v is ref:
del clocks[k]
break
ref.refx = 0
ref.refv = 0
ref.refm = ref.m0
## These are the set of proper times (in the reference frame) that will be simulated
ptVals = np.linspace(ref.pt, ref.pt + dt*(nPts-1), nPts)
for i in xrange(1,nPts):
## step reference clock ahead one time step in its proper time
nextPt = ptVals[i] ## this is where (when) we want to end up
while True:
tau1, tau2 = ref.accelLimits()
dtau = min(nextPt-ref.pt, tau2-ref.pt) ## do not step past the next command boundary
g = ref.acceleration()
v, x, t = Simulation.tauStep(dtau, ref.v, ref.x, ref.t, g)
ref.pt += dtau
ref.v = v
ref.x = x
ref.t = t
ref.reft = ref.pt
if ref.pt >= nextPt:
break
#else:
#print "Stepped to", tau2, "instead of", nextPt
ref.recordFrame(i)
## determine plane visible to reference clock
## this plane goes through the point ref.x, ref.t and has slope = ref.v
## update all other clocks
for cl in clocks.values():
2014-06-14 17:35:00 +00:00
while True:
g = cl.acceleration()
tau1, tau2 = cl.accelLimits()
##Given current position / speed of clock, determine where it will intersect reference plane
#t1 = (ref.v * (cl.x - cl.v * cl.t) + (ref.t - ref.v * ref.x)) / (1. - cl.v)
t1 = Simulation.hypIntersect(ref.x, ref.t, ref.v, cl.x, cl.t, cl.v, g)
dt1 = t1 - cl.t
## advance clock by correct time step
v, x, tau = Simulation.hypTStep(dt1, cl.v, cl.x, cl.pt, g)
## check to see whether we have gone past an acceleration command boundary.
## if so, we must instead advance the clock to the boundary and start again
if tau < tau1:
dtau = tau1 - cl.pt
cl.v, cl.x, cl.t = Simulation.tauStep(dtau, cl.v, cl.x, cl.t, g)
cl.pt = tau1-0.000001
continue
if tau > tau2:
dtau = tau2 - cl.pt
cl.v, cl.x, cl.t = Simulation.tauStep(dtau, cl.v, cl.x, cl.t, g)
cl.pt = tau2
continue
## Otherwise, record the new values and exit the loop
cl.v = v
cl.x = x
cl.pt = tau
cl.t = t1
cl.m = None
break
## transform position into reference frame
x = cl.x - ref.x
t = cl.t - ref.t
gamma = (1.0 - ref.v**2) ** -0.5
vg = -ref.v * gamma
cl.refx = gamma * (x - ref.v * t)
cl.reft = ref.pt # + gamma * (t - ref.v * x) # this term belongs here, but it should always be equal to 0.
cl.refv = (cl.v - ref.v) / (1.0 - cl.v * ref.v)
cl.refm = None
cl.recordFrame(i)
t += dt
def plot(self, plot):
plot.clear()
for cl in self.clocks.values():
2014-06-14 17:35:00 +00:00
c, p = cl.getCurve()
plot.addItem(c)
plot.addItem(p)
class Animation(pg.ItemGroup):
def __init__(self, sim):
pg.ItemGroup.__init__(self)
self.sim = sim
self.clocks = sim.clocks
self.items = {}
for name, cl in self.clocks.items():
item = ClockItem(cl)
self.addItem(item)
self.items[name] = item
#self.timer = timer
#self.timer.timeout.connect(self.step)
#def run(self, run):
#if not run:
#self.timer.stop()
#else:
#self.timer.start(self.dt)
def restart(self):
for cl in self.items.values():
cl.reset()
def stepTo(self, t):
for i in self.items.values():
i.stepTo(t)
class ClockItem(pg.ItemGroup):
def __init__(self, clock):
pg.ItemGroup.__init__(self)
self.size = clock.size
self.item = QtGui.QGraphicsEllipseItem(QtCore.QRectF(0, 0, self.size, self.size))
self.item.translate(-self.size*0.5, -self.size*0.5)
self.item.setPen(pg.mkPen(100,100,100))
self.item.setBrush(clock.brush)
self.hand = QtGui.QGraphicsLineItem(0, 0, 0, self.size*0.5)
self.hand.setPen(pg.mkPen('w'))
self.hand.setZValue(10)
self.flare = QtGui.QGraphicsPolygonItem(QtGui.QPolygonF([
QtCore.QPointF(0, -self.size*0.25),
QtCore.QPointF(0, self.size*0.25),
QtCore.QPointF(self.size*1.5, 0),
QtCore.QPointF(0, -self.size*0.25),
]))
self.flare.setPen(pg.mkPen('y'))
self.flare.setBrush(pg.mkBrush(255,150,0))
self.flare.setZValue(-10)
self.addItem(self.hand)
self.addItem(self.item)
self.addItem(self.flare)
self.clock = clock
self.i = 1
self._spaceline = None
def spaceline(self):
if self._spaceline is None:
self._spaceline = pg.InfiniteLine()
self._spaceline.setPen(self.clock.pen)
return self._spaceline
def stepTo(self, t):
data = self.clock.refData
while self.i < len(data)-1 and data['t'][self.i] < t:
self.i += 1
while self.i > 1 and data['t'][self.i-1] >= t:
self.i -= 1
self.setPos(data['x'][self.i], self.clock.y0)
t = data['pt'][self.i]
self.hand.setRotation(-0.25 * t * 360.)
self.resetTransform()
v = data['v'][self.i]
gam = (1.0 - v**2)**0.5
self.scale(gam, 1.0)
f = data['f'][self.i]
self.flare.resetTransform()
if f < 0:
self.flare.translate(self.size*0.4, 0)
else:
self.flare.translate(-self.size*0.4, 0)
self.flare.scale(-f * (0.5+np.random.random()*0.1), 1.0)
if self._spaceline is not None:
self._spaceline.setPos(pg.Point(data['x'][self.i], data['t'][self.i]))
self._spaceline.setAngle(data['v'][self.i] * 45.)
def reset(self):
self.i = 1
#class Spaceline(pg.InfiniteLine):
#def __init__(self, sim, frame):
#self.sim = sim
#self.frame = frame
#pg.InfiniteLine.__init__(self)
#self.setPen(sim.clocks[frame].pen)
#def stepTo(self, t):
#self.setAngle(0)
#pass
if __name__ == '__main__':
pg.mkQApp()
#import pyqtgraph.console
#cw = pyqtgraph.console.ConsoleWidget()
#cw.show()
#cw.catchNextException()
win = RelativityGUI()
win.setWindowTitle("Relativity!")
win.show()
win.resize(1100,700)
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
#win.params.param('Objects').restoreState(state, removeChildren=False)