77906fc7a2
Add pure-python integrator to verlet chain example
127 lines
3.2 KiB
Python
127 lines
3.2 KiB
Python
"""
|
|
Mechanical simulation of a chain using verlet integration.
|
|
|
|
Use the mouse to interact with one of the chains.
|
|
|
|
By default, this uses a slow, pure-python integrator to solve the chain link
|
|
positions. Unix users may compile a small math library to speed this up by
|
|
running the `examples/verlet_chain/make` script.
|
|
|
|
"""
|
|
|
|
import initExample ## Add path to library (just for examples; you do not need this)
|
|
|
|
import pyqtgraph as pg
|
|
from pyqtgraph.Qt import QtCore, QtGui
|
|
import numpy as np
|
|
|
|
import verlet_chain
|
|
|
|
sim = verlet_chain.ChainSim()
|
|
|
|
if verlet_chain.relax.COMPILED:
|
|
# Use more complex chain if compiled mad library is available.
|
|
chlen1 = 80
|
|
chlen2 = 60
|
|
linklen = 1
|
|
else:
|
|
chlen1 = 10
|
|
chlen2 = 8
|
|
linklen = 8
|
|
|
|
npts = chlen1 + chlen2
|
|
|
|
sim.mass = np.ones(npts)
|
|
sim.mass[int(chlen1 * 0.8)] = 100
|
|
sim.mass[chlen1-1] = 500
|
|
sim.mass[npts-1] = 200
|
|
|
|
sim.fixed = np.zeros(npts, dtype=bool)
|
|
sim.fixed[0] = True
|
|
sim.fixed[chlen1] = True
|
|
|
|
sim.pos = np.empty((npts, 2))
|
|
sim.pos[:chlen1, 0] = 0
|
|
sim.pos[chlen1:, 0] = 10
|
|
sim.pos[:chlen1, 1] = np.arange(chlen1) * linklen
|
|
sim.pos[chlen1:, 1] = np.arange(chlen2) * linklen
|
|
# to prevent miraculous balancing acts:
|
|
sim.pos += np.random.normal(size=sim.pos.shape, scale=1e-3)
|
|
|
|
links1 = [(j, i+j+1) for i in range(chlen1) for j in range(chlen1-i-1)]
|
|
links2 = [(j, i+j+1) for i in range(chlen2) for j in range(chlen2-i-1)]
|
|
sim.links = np.concatenate([np.array(links1), np.array(links2)+chlen1, np.array([[chlen1-1, npts-1]])])
|
|
|
|
p1 = sim.pos[sim.links[:,0]]
|
|
p2 = sim.pos[sim.links[:,1]]
|
|
dif = p2-p1
|
|
sim.lengths = (dif**2).sum(axis=1) ** 0.5
|
|
sim.lengths[(chlen1-1):len(links1)] *= 1.05 # let auxiliary links stretch a little
|
|
sim.lengths[(len(links1)+chlen2-1):] *= 1.05
|
|
sim.lengths[-1] = 7
|
|
|
|
push1 = np.ones(len(links1), dtype=bool)
|
|
push1[chlen1:] = False
|
|
push2 = np.ones(len(links2), dtype=bool)
|
|
push2[chlen2:] = False
|
|
sim.push = np.concatenate([push1, push2, np.array([True], dtype=bool)])
|
|
|
|
sim.pull = np.ones(sim.links.shape[0], dtype=bool)
|
|
sim.pull[-1] = False
|
|
|
|
# move chain initially just to generate some motion if the mouse is not over the window
|
|
mousepos = np.array([30, 20])
|
|
|
|
|
|
def display():
|
|
global view, sim
|
|
view.clear()
|
|
view.addItem(sim.makeGraph())
|
|
|
|
def relaxed():
|
|
global app
|
|
display()
|
|
app.processEvents()
|
|
|
|
def mouse(pos):
|
|
global mousepos
|
|
pos = view.mapSceneToView(pos)
|
|
mousepos = np.array([pos.x(), pos.y()])
|
|
|
|
def update():
|
|
global mousepos
|
|
#sim.pos[0] = sim.pos[0] * 0.9 + mousepos * 0.1
|
|
s = 0.9
|
|
sim.pos[0] = sim.pos[0] * s + mousepos * (1.0-s)
|
|
sim.update()
|
|
|
|
app = pg.mkQApp()
|
|
win = pg.GraphicsLayoutWidget()
|
|
win.show()
|
|
view = win.addViewBox()
|
|
view.setAspectLocked(True)
|
|
view.setXRange(-100, 100)
|
|
#view.autoRange()
|
|
|
|
view.scene().sigMouseMoved.connect(mouse)
|
|
|
|
#display()
|
|
#app.processEvents()
|
|
|
|
sim.relaxed.connect(relaxed)
|
|
sim.init()
|
|
sim.relaxed.disconnect(relaxed)
|
|
|
|
sim.stepped.connect(display)
|
|
|
|
timer = pg.QtCore.QTimer()
|
|
timer.timeout.connect(update)
|
|
timer.start(16)
|
|
|
|
|
|
## Start Qt event loop unless running in interactive mode or using pyside.
|
|
if __name__ == '__main__':
|
|
import sys
|
|
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
|
|
QtGui.QApplication.instance().exec_()
|