"""
Some infrastructure for working with Vectors and Edges, including
an xyplotter generator and axes maker.
By Kirby Urner, Sept 13, 2006
Updated Sept 29, 2006:
make Edge color a class-level attribute
add funky derivative demo
refactor a bit
Code:
http://www.4dsolutions.net/ocn/python/stickworks.py
For colorized source:
http://www.4dsolutions.net/cgi-bin/py2html.cgi?script=/ocn/python/stickworks.py
Some relevant discussion:
http://mail.python.org/pipermail/edu-sig/2006-September/007145.html
http://mail.python.org/pipermail/edu-sig/2006-September/007149.html
http://mail.python.org/pipermail/edu-sig/2006-September/007150.html
http://mail.python.org/pipermail/edu-sig/2006-September/007312.html
"""
from visual import vector, cylinder, cross, dot, diff_angle
import visual
class Vector (object):
"""
A wrapper for visual.vector that expresses a cylinder via draw(),
always pegged to the origin
"""
radius = 0.03
def __init__(self, xyz, color=(0,0,1)):
self.v = vector(*xyz)
self.xyz = xyz
self.color = color
self.cyl = None
def draw(self):
"""define and render the cylinder"""
self.cyl = cylinder(pos = (0,0,0), axis = self.v, radius = self.radius, color = self.color)
def erase(self):
"""toss the cylinder"""
if self.cyl:
self.cyl.visible = 0
self.cyl = None
def __repr__(self):
return 'Vector @ (%s,%s,%s)' % self.xyz
def diff_angle(self, other):
return self.v.diff_angle(other.v)
def cross(self, other):
temp = cross(self.v, other.v)
return Vector((temp.x, temp.y, temp.z))
def dot(self, other):
return dot(self.v, other.v)
def __sub__(self, other):
temp = self.v - other.v
return Vector((temp.x, temp.y, temp.z))
def __add__(self, other):
temp = self.v + other.v
return Vector((temp.x, temp.y, temp.z))
def __mul__(self, scalar):
temp = self.v * scalar
return Vector((temp.x, temp.y, temp.z))
__rmul__ = __mul__
def __neg__(self):
return Vector((-self.v.x, -self.v.y, -self.v.z))
def _length(self):
return pow(self.v.x ** 2 + self.v.y ** 2 + self.v.z ** 2, 0.5)
length = property(_length)
class Edge (object):
"""
Edges are defined by two Vectors (above) and express as cylinder via draw().
"""
radius = 0.03
color = (1,0,0)
def __init__(self, v0, v1, color=None):
if not color==None:
self.color = color
self.v0 = v0
self.v1 = v1
self.cyl = None
def draw(self):
"""define and render the cylinder"""
temp = (self.v1 - self.v0).xyz
self.cyl = cylinder(pos = self.v0.xyz, axis = vector(*temp),
radius = self.radius, color = self.color)
def erase(self):
"""toss the cylinder"""
if self.cyl:
self.cyl.visible = 0
self.cyl = None
def __repr__(self):
return 'Edge from %s to %s' % (self.v0, self.v1)
def xyplotter(domain, f):
"""
domain should be an initialized generator, ready for next() triggering.
f is any function of x. Consecutive Vectors trace connected edges.
"""
x0 = domain.next()
y0 = f(x0)
while True:
x1 = domain.next()
y1 = f(x1)
e = Edge( Vector((x0, y0, 0)), Vector((x1, y1, 0)) )
e.draw()
yield None
x0, y0 = x1, y1
def axes(x=0,y=0,z=0):
"""
Draw some axes on the VPython canvas
"""
v0 = Vector((x,0,0))
v0.draw()
v0 = Vector((-x,0,0))
v0.draw()
v0 = Vector((0,y,0))
v0.draw()
v0 = Vector((0,-y,0))
v0.draw()
v0 = Vector((0,0,z))
v0.draw()
v0 = Vector((0,0,-z))
v0.draw()
def dgen(start, step):
"""
generic domain generator
"""
while True:
yield start
start += step
def testme():
"""
>>> from stickworks import testme
Visual 2005-01-08
>>> testme()
See:
http://www.4dsolutions.net/ocn/graphics/cosines.png
"""
from math import cos
def f(x): return cos(x)
d = dgen(-5, 0.1)
axes(-5,1,0)
graph = xyplotter(d, f)
for i in xrange(100):
graph.next()
def testmemore():
"""
See:
http://www.4dsolutions.net/ocn/graphics/pycalculus.png
"""
def snakeywakey(x):
"""
Polynomial with x-axis crossings at 3,2,-3,-7, with scaler
to keep y-values under control (from a plotting point of view)
"""
return 0.01 * (x-3)*(x-2)*(x+3)*(x+7)
def deriv(f, h=1e-5):
"""
Generic df(x)/dx approximator (discrete h)
"""
def funk(x):
return (f(x+h)-f(x))/h
return funk
d1 = dgen(-8, 0.1)
d2 = dgen(-8, 0.1)
d3 = dgen(-8, 0.1)
axes(-8,5,3)
deriv_snakeywakey = deriv(snakeywakey)
second_deriv = deriv(deriv_snakeywakey)
graph1 = xyplotter(d1, snakeywakey)
graph2 = xyplotter(d2, deriv_snakeywakey)
graph3 = xyplotter(d3, second_deriv)
Edge.color = (1,0,0)
for i in xrange(130):
graph1.next()
Edge.color = (0,1,0)
for i in xrange(130):
graph2.next()
Edge.color = (0,1,1)
for i in xrange(130):
graph3.next()
if __name__ == '__main__':
testme()
# code highlighted using py2html.py version 0.8