```"""
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
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
"""

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

# some vector ops, including scalar multiplication

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))

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().
"""

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),

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)  # make snakeywakey red

for i in xrange(130):
graph1.next()

Edge.color = (0,1,0)  # make derivative green

for i in xrange(130):
graph2.next()

Edge.color = (0,1,1)  # make 2nd derivative cyan

for i in xrange(130):
graph3.next()

if __name__ == '__main__':
testmemore()

```
`# code highlighted using py2html.py version 0.8`