In this document you will learn to
Numeric Python is used throughout the OpenGLContext project, and you will likely want to make use of the package in your own code. This document focuses primarily on Numeric Python itself, rather than on PyOpenGL. Mastering the features shown here will be useful when doing non-trivial programming in PyOpenGL, but it's not necessary to know this material before you start playing with OpenGLContext.
Numeric Python makes it easy to work with (potentially large) homogenous arrays of a given data type. It provides extended slicing semantics for dealing with multidimensional arrays, utility functions for processing all elements of an array, and mechanisms for storing and converting to/from arrays and native Python data structures.
Before it can be used, Numeric Python needs to be imported
into your module. Depending on how many of the functions in the
module you'll be wanting to use, you may use either from Numeric
import *
or import Numeric
to gain access.
Creating a Numeric array is done with one of a set of functions, each of which has a particular purpose (note, these are just the commonly used functions):
zeros, ones | Generate an array filled with either 0 or 1 values of a given size and data type. |
zeros( (2,3), 'd') |
---|---|---|
array, asarray | Convert a sequence (often a
nested sequence) into an equivalently structured array of a given data
type (or of an automatically determined type if none is
specified).
asarray skips copying if the passed argument is already an array of the appropriate type. |
array( [[2,3,4],[5,6,7]],'i') |
identity | Generate an x by x integer identity matrix for the given x. |
identity ( 4 ) |
arange | An advanced form of the standard Python range, allowing for floating point ranges as well as integer ranges, generating an array as the result. |
arange( 3.0, 0.0, -.1, 'd') |
The Numeric type codes mentioned in the above table include a considerable number of exotic data types. For OpenGL work, here are the significant type codes and their equivalent OpenGL types:
double | Python float | d | GL_DOUBLE |
---|---|---|---|
float | No native Python equivalent | f | GL_FLOAT |
32-bit integer | Python integer | i | GL_INT |
16-bit integer | No native Python equivalent | s | GL_SHORT |
unsigned byte | Python string | b (c) | GL_UNSIGNED_BYTE |
signed byte | No native Python equivalent | 1 | GL_BYTE |
Notably absent from the list above are the unsigned integer and unsigned short types. As of yet, there is no satisfactory representation of these types in the Numeric module.
Array dimensions are specified as tuples, in the order of
indexing. The current shape of an array can be retrieved using
the shape( some_array )
function. You can use the reshape
,resize
,transpose
,
and ravel
functions to get a copy of an array with
different dimensions. Note: most Numeric functions allow for in-place
copying through an optional extra argument specifying the destination
for the operation. See the Numeric Python manual for details.
Mathematical operators tend to work element-wise for arrays, and allow for a fairly wide variety of second arguments.
For instance, to multiply an entire array by 3.0,
you would write result = myarray * 3.0
with
similar approaches for addition, subtraction, and division.
If, instead, you wanted to multiply each element in one array
by the corresponding element in another array, you would write result
= firstarray * secondarray
again, with the same
approach working for most basic math operations.
Numeric Python uses an extended slicing notation, which allows for flexible and powerful manipulation of multidimensional arrays (though at the price of making the slice specifications somewhat less intuitive looking).
The syntax for the extended slicing notation goes like this:
[ start : stop : step, start : stop : step, ... ]
with one start, stop, step set allowed (but not required beyond the first) for each dimension of the array. The step argument and the preceding colon can be left off unless actually needed. As with standard slicing notation, if you leave off a start value, zero is assumed, and if you leave off a stop value, the end of the array is assumed.
Generally it is easiest to understand the slicing notation through examples. Consider this interactive session:
>>> m = array( [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]], 'd')
>>> m
array([[ 1., 2., 3., 4.],
[ 5., 6., 7., 8.],
[ 9., 10., 11., 12.],
[ 13., 14., 15., 16.]])
# item 0 of the array in first dimension
>>> m[0]
array([ 1., 2., 3., 4.])
# item 1 of the array in first dimension
>>> m[1]
array([ 5., 6., 7., 8.])
# item 0 of the array in second dimension
>>> m[:,0]
array([ 1., 5., 9., 13.])
# item 1 of the array in second dimension
>>> m[:,1]
array([ 2., 6., 10., 14.])
# all items in first dimension taking every
# second item
>>> m[::2]
array([[ 1., 2., 3., 4.],
[ 9., 10., 11., 12.]])
# as previous, but now take item 1 in the
# second dimension for each row
>>> m[::2,1]
array([ 2., 10.])
# as previous, but starting the slice of first dimension
# at the second item (i.e. take 1 and 3 instead of 0 and 2)
>>> m[1::2,1]
array([ 6., 14.])
>>>
Common slices you'll see include:
# x-coordinate of an i*3 array
xes = points[:,0]
yes = points[:,1]
# first points of i*3 set of points describing triangles
firstPoints = trianglePoints[::3]
secondPoints = trianglePoints[1::3]
# r-values of an x*y*3 r,g,b image
reds = image[:,:,0]
greens = image[:,:,1]
There are three different types of multiplication commonly used in OpenGLContext. Element-wise multiplication was covered in Basic Mathematics above. The other two common multiplication types are dot product and cross product. Numeric Python provides a dot product function:
result = dot( first, second )
Unfortunately, it doesn't seem to provide any cross product function, so the OpenGLContext utility module provides its own cross product function (currently implemented in Python, potentially implemented in C in the future):
from OpenGLContext.utilities import crossprod
result = crossprod( (ux, uy, uz, uw), (vx, vy, vz, vw) )
When working with arrays of vectors, the triangleutilities function crossProduct should be used, as it will process the entire array of vectors using Numeric Python mechanisms (instead of calling a Python function for every vector).
Array Processing Functions
The basic manipulations above are primarily useful for selecting a particular subset of an array for processing, while the array processing functions do the bulk of the calculations in most algorithms. Because of the number of functions, you should use the Numeric Python Handbook for descriptions of the functions.
These function-like objects provide for extremely flexible application of common mathematical operations on arguments of varying type.
absolute, add, arccos, arcsin, arctan, arctan2,
bitwise_and, bitwise_or, bitwise_xor, ceil, conjugate, cos, cosh,
divide, divide_safe, equal, exp, fabs, floor, fmod,
greater, greater_equal, hypot, invert, left_shift, less,
less_equal, log, log10, logical_and, logical_not, logical_or,
logical_xor, maximum, minimum, multiply, negative, not_equal,
power, remainder, right_shift, sin, sinh, sqrt, subtract, tan, tanh
These functions deal with index arrays or indexed arrays, that is, arrays which refer to other arrays, either by having equivalent structure, or including actual indices into the other array. These functions can provide efficient operation by not requiring you to copy the arrays being processed. For an example, see OpenGLContext.scenegraph.polygonsort's use of argsort to generate the indices required for rendering sorted transparent geometry without needing to copying/rearranging the point array.
argmax, argmin, argsort, choose, take, where, put,
putmask, compress
These functions deal with two "data arrays", combining their values in some way to create a new array. The most commonly used function of this group is dot.
convolve, cross_correlate, dot, outerproduct,
innerproduct, sum
These functions produce single value outputs from an entire array, checking, for instance, that the entire array is true, or calculating the sum of the entire array.
alltrue, cumproduct, cumsum, product, sometrue, trace
These functions produce a copy of an array with each value modified by the given function, or the array's ordering/contents modified as dictated by the function's rules of operation.
around, clip, sign, sort, compress
The OpenGLContext Utility Modules
The vectorutilities module provides similar functions for operating on entire arrays of vectors (they can also work with individual vectors, but are not generally convenient for such operation).
Note, the current set of utility modules is not ideal, it is likely that the "utility" module will largely go away with the explicit utility modules (vector, triangle) replacing its functionality.
Array-based Geometry
With C code, the difference between the array-based code and individual calls to transfer data values was noticeable, but it is still quite practical to use the individual calls in many instances. With Python code, however, it is generally impractical to use individual data transfer calls, as the overhead for the API call is enormous compared to the actual work being done with such low level functions.
The basic approach of the array-based geometry is to specify a pointer for a number of different array types: vertex, color, normal and textureCoordinate (potentially multiple coordinates if using multi-texturing).
glVertexPointerd( self.verticies )
glColorPointerd ( self.colours )
glNormalPointerd ( self.normals )
glTexCoordPointerd( self.textures )
This tells the OpenGL engine that these are the active arrays for each of these types. For all of the arrays save texture coordinates, only one active array is possible. Where multi-texturing is enabled (multi-texturing is an OpenGL extension not available on all platforms), you can specify one active texture coordinate array for each texture engine.
Once you have specified the active arrays, you enable or disable each array type you wish to use for a given call.
glEnable( GL_VERTEX_ARRAY )
glEnable( GL_COLOR_ARRAY )
glEnable( GL_NORMAL_ARRAY )
glEnable( GL_TEXTURE_COORD_ARRAY )
The array drawing calls such as glDrawArrays and glDrawElements are called in place of a glBegin, glEnd construct (including the glVertex, glColor, glNormal, glTexCoord, etc. calls that normally go between them). The array drawing call will specify the primitive type to draw (the same values as for glBegin), and potentially a subset of the arrays which is to be drawn (for instance, a start position, a count of items to draw, or a list of indices into the arrays which are to be drawn).
glDrawArrays( GL_TRIANGLES, 0, 32 )
glDrawElementsui( GL_TRIANGLES, indices )
For information on using Numeric for image manipulation, see Images and Textures
For further information on Numeric, see the Numeric Python Handbook and/or the Numeric Python Homepage
OpenGLContext.scenegraph.arraygeometry module for a simple implementation of array-based geometry rendering.
OpenGLContext.vectorutilities, OpenGLContext.triangleutilities for examples of array manipulation.
The following programs in the tests directory test array geometry functionality (and therefore include example drawing code): gldrawarrays, gldrawarrays_string, gldrawelements, glarrayelement, glinterleavedarrays.