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