diff --git a/MANIFEST.in b/MANIFEST.in index c6667d04..86ae0f60 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,6 @@ recursive-include pyqtgraph *.py *.ui *.m README *.txt recursive-include tests *.py *.ui -recursive-include examples *.py *.ui +recursive-include examples *.py *.ui *.gz *.cfg recursive-include doc *.rst *.py *.svg *.png *.jpg recursive-include doc/build/html * recursive-include tools * diff --git a/examples/verlet_chain/chain.py b/examples/verlet_chain/chain.py index 896505ac..6eb3501a 100644 --- a/examples/verlet_chain/chain.py +++ b/examples/verlet_chain/chain.py @@ -1,7 +1,7 @@ import pyqtgraph as pg import numpy as np import time -from .relax import relax +from . import relax class ChainSim(pg.QtCore.QObject): @@ -52,7 +52,7 @@ class ChainSim(pg.QtCore.QObject): self.mrel1[self.fixed[l2]] = 0 self.mrel2 = 1.0 - self.mrel1 - for i in range(100): + for i in range(10): self.relax(n=10) self.initialized = True @@ -75,6 +75,10 @@ class ChainSim(pg.QtCore.QObject): else: dt = now - self.lasttime self.lasttime = now + + # limit amount of work to be done between frames + if not relax.COMPILED: + dt = self.maxTimeStep if self.lastpos is None: self.lastpos = self.pos @@ -103,8 +107,9 @@ class ChainSim(pg.QtCore.QObject): def relax(self, n=50): - # speed up with C magic - relax(self.pos, self.links, self.mrel1, self.mrel2, self.lengths, self.push, self.pull, n) + # speed up with C magic if possible + relax.relax(self.pos, self.links, self.mrel1, self.mrel2, self.lengths, self.push, self.pull, n) self.relaxed.emit() + diff --git a/examples/verlet_chain/maths.so b/examples/verlet_chain/maths.so deleted file mode 100755 index 62aff321..00000000 Binary files a/examples/verlet_chain/maths.so and /dev/null differ diff --git a/examples/verlet_chain/relax.py b/examples/verlet_chain/relax.py index 95a2b7b3..22c54d62 100644 --- a/examples/verlet_chain/relax.py +++ b/examples/verlet_chain/relax.py @@ -2,22 +2,69 @@ import ctypes import os so = os.path.join(os.path.dirname(__file__), 'maths.so') -lib = ctypes.CDLL(so) +try: + lib = ctypes.CDLL(so) + COMPILED = True +except OSError: + COMPILED = False -lib.relax.argtypes = [ - ctypes.c_void_p, - ctypes.c_void_p, - ctypes.c_void_p, - ctypes.c_void_p, - ctypes.c_void_p, - ctypes.c_void_p, - ctypes.c_void_p, - ctypes.c_int, - ctypes.c_int, - ] -def relax(pos, links, mrel1, mrel2, lengths, push, pull, iters): - nlinks = links.shape[0] - lib.relax(pos.ctypes, links.ctypes, mrel1.ctypes, mrel2.ctypes, lengths.ctypes, push.ctypes, pull.ctypes, nlinks, iters) - +if COMPILED: + lib.relax.argtypes = [ + ctypes.c_void_p, + ctypes.c_void_p, + ctypes.c_void_p, + ctypes.c_void_p, + ctypes.c_void_p, + ctypes.c_void_p, + ctypes.c_void_p, + ctypes.c_int, + ctypes.c_int, + ] + def relax(pos, links, mrel1, mrel2, lengths, push, pull, iters): + nlinks = links.shape[0] + lib.relax(pos.ctypes, links.ctypes, mrel1.ctypes, mrel2.ctypes, lengths.ctypes, push.ctypes, pull.ctypes, nlinks, iters) + +else: + def relax(pos, links, mrel1, mrel2, lengths, push, pull, iters): + lengths2 = lengths**2 + for i in range(iters): + #p1 = links[:, 0] + #p2 = links[:, 1] + #x1 = pos[p1] + #x2 = pos[p2] + + #dx = x2 - x1 + + #dist = (dx**2).sum(axis=1)**0.5 + + #mask = (npush & (dist < lengths)) | (npull & (dist > lengths)) + ##dist[mask] = lengths[mask] + #change = (lengths-dist) / dist + #change[mask] = 0 + + #dx *= change[:, np.newaxis] + #print dx + + ##pos[p1] -= mrel2 * dx + ##pos[p2] += mrel1 * dx + #for j in range(links.shape[0]): + #pos[links[j,0]] -= mrel2[j] * dx[j] + #pos[links[j,1]] += mrel1[j] * dx[j] + + + for l in range(links.shape[0]): + p1, p2 = links[l]; + x1 = pos[p1] + x2 = pos[p2] + + dx = x2 - x1 + dist2 = (dx**2).sum() + + if (push[l] and dist2 < lengths2[l]) or (pull[l] and dist2 > lengths2[l]): + dist = dist2 ** 0.5 + change = (lengths[l]-dist) / dist + dx *= change + pos[p1] -= mrel2[l] * dx + pos[p2] += mrel1[l] * dx diff --git a/examples/verlet_chain_demo.py b/examples/verlet_chain_demo.py index 6ed97d48..1197344d 100644 --- a/examples/verlet_chain_demo.py +++ b/examples/verlet_chain_demo.py @@ -1,26 +1,38 @@ """ 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 -from verlet_chain import ChainSim +import verlet_chain -sim = ChainSim() +sim = verlet_chain.ChainSim() - -chlen1 = 80 -chlen2 = 60 +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[chlen1-15] = 100 +sim.mass[int(chlen1 * 0.8)] = 100 sim.mass[chlen1-1] = 500 sim.mass[npts-1] = 200 @@ -31,8 +43,10 @@ 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) -sim.pos[chlen1:, 1] = np.arange(chlen2) +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)] @@ -55,7 +69,8 @@ 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 -mousepos = sim.pos[0] +# move chain initially just to generate some motion if the mouse is not over the window +mousepos = np.array([30, 20]) def display():