[Edu-sig] Going Graphical: Where Python Meets Descartes

Kirby Urner pdx4d@teleport.com
Wed, 09 Feb 2000 18:01:35 -0800


Here's another good approximation of where I'm thinking to go
with "Python notation" in the context of a prototypical math 
class.

In USA K-12, we're mostly working with calculators in early 
2000, with relatively few kids having access to an interactive 
command line (aka an REPL environment).  The text books don't
have much beyond obsolete BASIC, if that, and nothing about
the object oriented paradigm.

Whereas some language gurus posting here have advocated forming 
a united front against the hegemony of C/C++ (nevermind for 
the moment that Python is written in C++), I have a slightly 
different, perhaps complementary focus:  helping math teachers 
move beyond their graphing calculators into the world of 
full-fledged computers.  

This move from calculators to computers is likewise a move 
"beyond flatland" into fully spatial geometry, and to a 
broader treatment of symbolic operations, with a greater
range of data structures and operations making it into the 
standard curriculum.[1]

These lesson plans are about showing how one can leverage Python 
and other tools (e.g. Povray), along with pre-existing K-12 
content (in this example coordinate geometry), to achieve a 
usefully synergetic result.  

Students learning enough Python to scan/write code such as 
included below will be gaining insight into both computing 
and mathematics at the same time, in the context of an inter-
active, self-reinforcing environment conducive to further 
play and self-directed exploration.

The teacher in such a classroom will need to know enough 
Python to keep students moving along towards their goals, 
but need not be a top-level Python guru.  A good grasp of 
the underlying mathematics remains the more important 
qualification for successful communication of this content, 
plus experience in various time-tested methods of pedagogy.

Kirby
4D Solutions

[1] See: http://www.inetarena.com/~pdx4d/ocn/trends2000.html

===========

Another Lesson Plan (first draft):  

        Going Graphical: Where Python Meets Descartes
        by Kirby Urner, Oregon Curriculum Network
        Ver. 1.0, Feb 09, 2000        
        Originally posted to edu-sig@python.org
        Coming soon:  web version

Concepts typically introduced in K-12 include many relating to
coordinate geometry.  Renaissance painters figured out a lot
of this stuff, in their efforts to master the art of painting 
in perspective.

Descartes brought a lot of their methods together in the 
context of his "I think therefore I am" philosophy, giving 
us the Cartesian coordinate system. 

Considering the contributions by the art schools to this 
heritage,  it's appropriate that we will be bringing more color 
into our graphics than 1900s text books typically permitted 
(more perspective, too).

A vector is an object with magnitude and direction, typically
shown as an arrow in Cartesian space.  Vector addition involves
placing two such arrows tip-to-tail, to get a third:  their
vector sum.  (a,b,c) + (d,e,f) = (a+d,b+e,c+f).

    def __add__(self,addvector):
        # add a vector to this object (self)
        # return a new vector
        newcoords=[0,0,0]
        for i in range(3):
            newcoords[i]=addvector.coords[i] + self.coords[i]
        return Vector(newcoords)

Likewise, we can scale a vector, meaning cause it to grow or
shrink without changing its orientation.  Or, if we apply a
negative scalar, the operation of negation will cause our 
arrow to flip, and point 180 degrees oppositely to its original
direction.

So to scale by 2 is to double a vector's length, whereas to
scale it by -0.5 is to flip it by 180 degrees, and shrink
it to 1/2 its original magnitude.  (a,b,c) * s = (as,bs,cs).

    def __mul__(self,scalar):
        # scale and this vector (self) by scalar
        # return a new vector
        newcoords=[0,0,0]
        for i in range(3):
            newcoords[i]=scalar * self.coords[i]
        return Vector(newcoords)

Let's look at Vectors as a class of object.  Here's a complete 
class template for our vector objects:

class Vector:

    def __init__(self,arg):
        # initialize a vector at an (x,y,z) tuple (= arg)
        self.coords = arg
    
    def __mul__(self,scalar):
        # scale and this vector (self) by scalar
        # return a new vector
        newcoords=[0,0,0]
        for i in range(3):
            newcoords[i]=scalar * self.coords[i]
        return Vector(newcoords)
    
    def __add__(self,addvector):
        # add a vector object to this object (self)
        # return a new vector        
        newcoords=[0,0,0]
        for i in range(3):
            newcoords[i]=addvector.coords[i] + self.coords[i]
        return Vector(newcoords)
    
    def length(self):
        # return this vector's length
        sum = 0
        for i in range(3):
            sum = sum + self.coords[i]**2
        return sum ** 0.5
        
[ Note that in saying "complete" I don't mean we can't further 
add to and enhance our vectors.  Dot and cross products are 
examples of additional operations we might usefully define. 
"Complete" means "in working condition" -- even if we have
plans to brainstorm lots of enhancements ]

Let's see our Vector objects in action:

>>> from coords import Vector
>>> v1 = Vector((1,0,0))
>>> v2 = Vector((0,2,1))
>>> v3 = v1 + v2
>>> v3.coords
[1, 2, 1]
>>> v1.length()
1.0
>>> v2.length()
2.2360679775
>>> v3.length()
2.44948974278
>>> v4 = v1 * 10.0
>>> v4.coords
[10.0, 0.0, 0.0]

Now we'd like to "paint" our vectors.  We'll use the freeware ray 
tracing program known as POV-ray (www.povray.org) for this 
purpose.

Once again, we can choose to look at a Povray file (.pov extension) 
as an object.  You instantiate it with a file name... 

>>> from povray import Povray
>>> myfile = Povray("sine.pov")

then feed vectors to its two write methods:  writecyl and writepoint.  
For example: 

>>> i = 0.75
>>> point = Vector((i,math.sin(i),0))
>>> myfile.writepoint(point)

>>> posx = Vector((3,0,0))
>>> myfile.cylcolor = 'Green'
>>> myfile.writecyl(posx)
>>> myfile.writecyl(posx * -1.0)

myfile.writecyl(vector) draws a slender cylinder from (0,0,0) 
to our vector tip:


 def writecyl(self,v1):
     # write cylinder from (0,0,0) to v1.coords
     tip = tuple(v1.coords)        
     self.file.write(("cylinder{<0, 0, 0>" 
         + ",<%s, %s, %s>," % tip
         + " %s pigment {color %s} no_shadow}\n" 
         % (self.cylradius, self.cylcolor)))


[ Here we're looking inside the povray.py module (gives the 
class template for a Povray object) ]

myfile.writesph(vector) just puts a little sphere at the vector 
tip, a graphical point.

 def writepoint(self,v1):
     # write sphere at v1.coords
     center = tuple(v1.coords)
     self.file.write(("sphere{<%s, %s, %s>, " % center
         + " %s pigment {color %s} no_shadow }\n" 
         % (self.sphradius, self.sphcolor)))

All these write operations end generating a povray text file 
with a lot of instructions for the ray tracing engine.  Here's 
an excerpt from 'parabola.pov':

  cylinder{<0, 0, 0>,<0, 0, 3>, 0.02 pigment {color Cyan} no_shadow}
  cylinder{<0, 0, 0>,<0.0, 0.0, -3.0>, 0.02 pigment {color Cyan} no_shadow}
  sphere{<-1.5, 2.25, 0>, 0.02 pigment {color Red} no_shadow }
  sphere{<-1.49, 2.2201, 0>, 0.02 pigment {color Red} no_shadow }

Now that we have all this infrastructure in place, we have the 
wherewithall to start doing some serious graphics -- more 
sophisticated than any graphing calculator can do.  

For example, we might want to start building a little "functions 
library" for showing favorite text book graphs, such as 

parabola(x) = sin(x) or 
sinewave(x) = x^2

Here's some Python for doing both:

def sinewave():
    # graph a sine wave from -pi to pi
    myfile = Povray("sine.pov")
    xyzaxes(myfile)    # include multi-color xyz axes
    i = - math.pi
    while (i < math.pi):
        point = Vector((i,math.sin(i),0))
        myfile.writepoint(point)
        i = i+0.01
    myfile.close()
    
def parabola():
    # graph a parabola from -2.0 to 2.0
    myfile = Povray("parabola.pov")
    xyzaxes(myfile)    # include multi-color xyz axes
    i = - 1.5
    while (i < 1.5):
        point = Vector((i,i**2,0))
        myfile.writepoint(point)
        i = i+0.01
    myfile.close()    

Note that both functions invoke an xyz axis function, to draw 
green/orange/cyan xyz axes (part of our functions library):

def xyzaxes(file):
    
    posx = Vector((3,0,0))
    file.cylcolor = 'Green'
    file.writecyl(posx)
    file.writecyl(posx * -1.0)
    
    posy = Vector((0,3,0))
    file.cylcolor = 'Orange'
    file.writecyl(posy)
    file.writecyl(posy * -1.0)
    
    posz = Vector((0,0,3))
    file.cylcolor = 'Cyan'
    file.writecyl(posz)
    file.writecyl(posz * -1.0)

Below is a command line session, wherein we simply execute these 
functions. 

Python 1.5.2 (#0, Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)] on win32
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> import functions
>>> functions.sinewave()
>>> functions.parabola()

The output of these two functions may be viewed with your web 
browser at:

http://www.inetarena.com/~pdx4d/ocn/graphics/sinewave.gif
http://www.inetarena.com/~pdx4d/ocn/graphics/parabola.gif

Kirby