<div dir="ltr"><div>I think the code below is short enough and didactic / general purpose <br>enough to archive / share here on edu-sig, (c) MIT License, versus<br>say Github. <br><br>It runs with only visual (Visual Python) as a dependency, which makes <br>
it a great demo and provides assets out of the box (simple Vector <br>and Edge classes) to use with that excellent package. <br><br>Then I'm going to post another demo which in turns depends on this <br>module, and on this one:<br>
<br><a href="https://mail.python.org/pipermail/edu-sig/2013-August/010872.html">https://mail.python.org/pipermail/edu-sig/2013-August/010872.html</a><br><br></div><div>This demo will further show how to use stickworks.py (below) as a<br>
resource.<br><br></div><div>NOTE: some code may have word-wrapped during transport. <br>Perform standard maintenance before first use.<br><br></div><div>Kirby<br><br></div><div><br>"""<br>Some infrastructure for working with Vectors and Edges, including<br>
an xyplotter generator and axes maker.<br><br>(c) By Kirby Urner, Sept 13, 2006, 2013, MIT License<br># no warranty, "as is" (feel free to bring up to your own<br></div><div># standards before using it to build bridges). Originally<br>
# for learning purposes.<br></div><div><br>Updated Sept 29, 2006:<br>make Edge color a class-level attribute<br>add funky derivative demo<br>refactor a bit<br>Updated Sept 27, 2013: made Python 3.2 compatible<br><br>Original Code:<br>
<a href="http://www.4dsolutions.net/ocn/python/stickworks.py">http://www.4dsolutions.net/ocn/python/stickworks.py</a><br><br>Video:<br><a href="http://youtu.be/4iMSGKXJ_Ds">http://youtu.be/4iMSGKXJ_Ds</a><br></div><div><br>
Some relevant discussion:<br><a href="http://mail.python.org/pipermail/edu-sig/2006-September/007145.html">http://mail.python.org/pipermail/edu-sig/2006-September/007145.html</a><br><a href="http://mail.python.org/pipermail/edu-sig/2006-September/007149.html">http://mail.python.org/pipermail/edu-sig/2006-September/007149.html</a><br>
<a href="http://mail.python.org/pipermail/edu-sig/2006-September/007150.html">http://mail.python.org/pipermail/edu-sig/2006-September/007150.html</a><br><a href="http://mail.python.org/pipermail/edu-sig/2006-September/007312.html">http://mail.python.org/pipermail/edu-sig/2006-September/007312.html</a><br>
"""<br><br>from visual import vector, cylinder, cross, dot, diff_angle<br>import visual<br><br>class Vector (object):<br><br> """<br> A wrapper for visual.vector that expresses a cylinder via draw(),<br>
always pegged to the origin<br> """<br><br> radius = 0.03<br><br> def __init__(self, xyz, color=(0,0,1)):<br> self.v = vector(*xyz)<br> self.xyz = xyz<br> self.color = color<br>
self.cyl = None<br><br> def draw(self):<br> """define and render the cylinder"""<br> self.cyl = cylinder(pos = (0,0,0), axis = self.v, radius = self.radius, color = self.color)<br>
<br> def erase(self):<br> """toss the cylinder"""<br> if self.cyl:<br> self.cyl.visible = 0<br> self.cyl = None<br><br> def __repr__(self):<br> return 'Vector @ (%s,%s,%s)' % self.xyz<br>
<br> # some vector ops, including scalar multiplication<br><br> def diff_angle(self, other):<br> return self.v.diff_angle(other.v)<br><br> def cross(self, other):<br> temp = cross(self.v, other.v)<br>
return Vector((temp.x, temp.y, temp.z))<br><br> def dot(self, other):<br> return dot(self.v, other.v)<br><br> def __sub__(self, other):<br> temp = self.v - other.v<br> return Vector((temp.x, temp.y, temp.z))<br>
<br> def __add__(self, other):<br> temp = self.v + other.v<br> return Vector((temp.x, temp.y, temp.z))<br><br> def __mul__(self, scalar):<br> temp = self.v * scalar<br> return Vector((temp.x, temp.y, temp.z))<br>
<br> __rmul__ = __mul__<br><br> def __neg__(self):<br> return Vector((-self.v.x, -self.v.y, -self.v.z))<br><br> def _length(self):<br> return pow(self.v.x ** 2 + self.v.y ** 2 + self.v.z ** 2, 0.5)<br>
<br> length = property(_length)<br><br>class Edge (object):<br><br> """<br> Edges are defined by two Vectors (above) and express as cylinder via draw().<br> """<br><br> radius = 0.03<br>
color = (1,0,0)<br><br> def __init__(self, v0, v1, color=None):<br> if not isinstance(v0, Vector) or not isinstance(v1, Vector):<br> raise TypeError("Wrong input types")<br> if not color==None:<br>
self.color = color<br> self.v0 = v0<br> self.v1 = v1<br> self.cyl = None<br><br> def draw(self):<br> """define and render the cylinder"""<br> temp = (self.v1 - self.v0).xyz<br>
self.cyl = cylinder(pos = self.v0.xyz, axis = vector(*temp),<br> radius = self.radius, color = self.color)<br><br> def erase(self):<br> """toss the cylinder"""<br>
if self.cyl:<br> self.cyl.visible = 0<br> self.cyl = None<br><br> def _length(self):<br> return (self.v1 - self.v0).length<br><br> length = property(_length)<br><br> def __repr__(self):<br>
return 'Edge from %s to %s' % (self.v0, self.v1)<br><br><br>def xyplotter(domain, f):<br> """<br> domain should be an initialized generator, ready for next() triggering.<br> f is any function of x. Consecutive Vectors trace connected edges.<br>
"""<br> x0 = next(domain)<br> y0 = f(x0)<br> while True:<br> x1 = next(domain)<br> y1 = f(x1)<br> e = Edge( Vector((x0, y0, 0)), Vector((x1, y1, 0)) )<br> e.draw()<br>
yield None<br> x0, y0 = x1, y1<br><br>def axes(x=0,y=0,z=0):<br> """<br> Draw some axes on the VPython canvas<br> """<br> v0 = Vector((x,0,0))<br> v0.draw()<br> v0 = Vector((-x,0,0))<br>
v0.draw()<br><br> v0 = Vector((0,y,0))<br> v0.draw()<br> v0 = Vector((0,-y,0))<br> v0.draw()<br><br> v0 = Vector((0,0,z))<br> v0.draw()<br> v0 = Vector((0,0,-z))<br> v0.draw()<br><br><br>def dgen(start, step):<br>
"""<br> generic domain generator<br> """<br> while True:<br> yield start<br> start += step<br><br><br>def testme():<br> """<br> >>> from stickworks import testme<br>
Visual 2005-01-08<br> >>> testme()<br><br> See:<br> <a href="http://www.4dsolutions.net/ocn/graphics/cosines.png">http://www.4dsolutions.net/ocn/graphics/cosines.png</a><br> """<br>
<br> from math import cos<br><br> def f(x): return cos(x)<br><br> d = dgen(-5, 0.1)<br> axes(-5,1,0)<br> graph = xyplotter(d, f)<br><br> for i in xrange(100):<br> graph.next()<br><br>def testmemore():<br>
"""<br> See:<br> <a href="http://www.4dsolutions.net/ocn/graphics/pycalculus.png">http://www.4dsolutions.net/ocn/graphics/pycalculus.png</a><br> """<br><br> def snakeywakey(x):<br>
"""<br> Polynomial with x-axis crossings at 3,2,-3,-7, with scaler<br> to keep y-values under control (from a plotting point of view)<br> """<br> return 0.01 * (x-3)*(x-2)*(x+3)*(x+7)<br>
<br> def deriv(f, h=1e-5):<br> """<br> Generic df(x)/dx approximator (discrete h)<br> """<br> def funk(x):<br> return (f(x+h)-f(x))/h<br> return funk<br>
<br><br> d1 = dgen(-8, 0.1)<br> d2 = dgen(-8, 0.1)<br> d3 = dgen(-8, 0.1)<br><br> axes(-8,5,3)<br><br> deriv_snakeywakey = deriv(snakeywakey)<br> second_deriv = deriv(deriv_snakeywakey)<br><br> graph1 = xyplotter(d1, snakeywakey)<br>
graph2 = xyplotter(d2, deriv_snakeywakey)<br> graph3 = xyplotter(d3, second_deriv)<br><br> Edge.color = (1,0,0) # make snakeywakey red<br><br> for i in range(130):<br> next(graph1)<br><br> Edge.color = (0,1,0) # make derivative green<br>
<br> for i in range(130):<br> next(graph2)<br><br> Edge.color = (0,1,1) # make 2nd derivative cyan<br><br> for i in range(130):<br> next(graph3)<br><br>if __name__ == '__main__':<br> testmemore()<br>
<br></div></div>