Jabka Graphics Programming Reference
Donald H. House, June 28, 1996
This document gives a brief introduction to the special features of Jabka's version of the Scheme programming language that provide support needed for graphics programming. Central to providing this support is the addition to Scheme of two new atoms transforms and objects. This document deals with transforms, and also details a variety of procedures and definitions that facilitate dealing with geometric entities and with colors. All of these are explained below. Objects are the subject of a separate document entitled the Jabka Object System.
Contents
Points, vertices and polygons are the basic building-blocks from which geometric models are built in Jabka. They are all specified using Scheme's list structure. More complex structures are built up from these simple structures using procedures that are defined in some of the following sections. The conventions for defining these geometric structures within Jabka are listed below.
- point (x y) A two-element list giving the x and y coordinates of a point in two-dimensional space. x and y must have numeric values.
- point-list (p1 p2 ...) A list of any number of points. p1, p2, etc. must evaluate to points.
- vertex (x y z) A three-element list giving the x, y, and z coordinates of a point in three-dimensional space. x, y, and z must have numeric values.
- vertex-list (v1 v2 ...) A list of any number of vertices (note: the plural of vertex is vertices.). v1, v2, etc. must evaluate to vertices.
- polygon vertex-list A polygon is represented by a consecutive list of its vertices. There should be exactly one entry in this list for each vertex. The order in which the vertices are specified is very important, since their order is used to determine which side of the polygon is its "front" -- renderers generally ignore polygons facing away from the camera, or at most just paint them black. The left hand rule should be used to determine the correct order for the vertices, i.e. the vertices should be specified in a clockwise direction, when viewing the polygon from its "front" side.
- poly-list (p1 p2 ...) A list of any number of polygons. p1, p2, etc. must evaluate to polygons.
- vector (dx dy dz) A three-element list giving the x, y, and z components of a vector in three- dimensional space. dx, dy, and dz must have numeric values. For example, the unit vector pointing along the positive x axis is (1 0 0). Note that vector and vertex have identical formats and in most cases can be used interchangeably.
- vector-list (v1 v2 ...) A list of any number of vectors. v1, v2, etc. must evaluate to vectors.
The following procedures generate or operate on vertices, polygons, and points. Each function definition is followed by one or more examples. An example usually consists of a function call, followed by an arrow symbol ==>, followed by an S-expression. This indicates that the call would return the value to the right of the arrow.
- (circle radius numsamples) The arguments must both evaluate to positive non-zero numbers. numsamples must be 3 or greater. Returns a point-list of numsamples consecutive points equally spaced in a clockwise direction around the circumference of a circle centered at the origin, and of radius radius. The first point will lie on the positive x axis. Note that this ordering was chosen for natural use with the extrude procedure -- extruding a circle in the positive z direction results in a faceted cylinder whose sides and ends all face out. example: (circle 10 4) ==> ((10 0) (0 -10) (-10 0) (0 10))
- (semi-circle radius numsamples) The arguments must both evaluate to positive non- zero numbers. numsamples must be 3 or greater. Returns a point-list of numsamples consecutive points equally spaced in a counter-clockwise direction around the circumference of a semi-circle centered at the origin, lying in the right-half plane (i.e. to the right of the y axis), and of radius radius. The first point will lie on the negative y axis. Note that this ordering was chosen for natural use with the sweep procedure -- sweeping a semi-circle results in a faceted sphere whose facets face out. example: (semi-circle 10 4) ==> ((0 -10) (8.6600 -5) (8.660 5) (0 10))
- (2to3d point-list) The argument must evaluate to a point-list. Returns a vertex-list (i.e. a polygon) formed by appending a z coordinate of 0 to each point in the point-list. example: (2to3d '((0 0) (100 0) (100 100) (0 100))) ==> ((0 0 0) (100 0 0) (100 100 0) (0 100 0)), example: (2to3d (circle 10 4)) ==> ((10 0 0) (0 -10 0) (-10 0 0) (0 10 0))
- (x-coord vertex) The argument must evaluate to a vertex or a vector. Returns the x coordinate. example: (x-coord '(100 200 300)) ==> 100
- (y-coord vertex) The argument must evaluate to a vertex or a vector. Returns the y coordinate. example: (y-coord '(100 200 300)) ==> 200
- (z-coord vertex) The argument must evaluate to a vertex or a vector. Returns the z coordinate. example: (z-coord '(100 200 300)) ==> 300
- (reflect-yz polygon) The argument must evaluate to a polygon. Returns a copy of the polygon as if it had been reflected in a mirror located in the y-z plane. The order of the vertices of the mirrored polygon is reversed from that of the original polygon, thus preserving the same front-face/back-face ordering as in the original polygon. example: (reflect-yz '((0 0 100) (100 0 100) (100 100 100) (0 100 100))) ==> ((0 100 100) (-100 100 100) (-100 0 100) (0 0 100))
- (reflect-xz polygon) The argument must evaluate to a polygon. Returns a copy of the polygon as if it had been reflected in a mirror located in the x-z plane. The order of the vertices of the mirrored polygon is reversed from that of the original polygon, thus preserving the same front-face/back-face ordering as in the original polygon. example: (reflect-xz '((0 0 100) (100 0 100) (100 100 100) (0 100 100))) ==> ((0 -100 100) (100 -100 100) (100 0 100) (0 0 100))
- (reflect-xy polygon) The argument must evaluate to a polygon. Returns a copy of the polygon as if it had been reflected in a mirror located in the x-y plane. The order of the vertices of the mirrored polygon is reversed from that of the original polygon, thus preserving the same front-face/back-face ordering as in the original polygon. example: (reflect-xy '((0 0 100) (100 0 100) (100 100 100) (0 100 100))) ==> ((0 100 -100) (100 100 -100) (100 0 -100) (0 0 -100))
A transform is an atom that can be used in Jabka's version of Scheme to specify a transformation that is to be done on a geometric entity. The basic transformations that are directly supported in Jabka are translations, scales, and rotations. These basic transformations can also be composed together to form more complex transformations. The functions that actually make use of these transforms to operate on vertices and polygons are given below. Some of the methods that operate on geometric objects also make use of transforms (see the description of Xformables under the section on objects).
A transform is created by a call to one of the routines shown below. This transform is stored internally as 4 by 4 array of numbers. Simple transforms are composed together to make more complex transforms by matrix multiplication (an explanation of matrix multiplication is beyond the scope of this reference guide but can be found in any book on linear algebra). Since routines that print out or display transforms use this 4 by 4 notation, it will be helpful to know at least how the various simple transforms are stored. This is given below with the description of each transform creating routine.
- (identity) Returns the identity transform:. This is the transform that transforms a geometric object into itself (i.e. it effectively does nothing!). example:
(identity) ==> #<TRANSFORM 1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1>
- (translate delta-x delta-y delta-z) The three arguments must evaluate to numbers. Returns a transform that will cause a geometric object to be translated by an amount delta-x in the x direction, delta-y in the y direction, and delta-z in the z direction. example:
(translate 100 -200 50) ==>
#<TRANSFORM 1 0 0 0
0 1 0 0
0 0 1 0
100 -200 50 1>
i.e., a transform that will shift a vertex 100 in the x
direction (right), -200 in the y direction (down), and 50
in the z direction (into the screen).
- (x-rot angle) The argument must evaluate to a number that represents an angle specified in degrees (usually a number between -360 and 360). Returns a transform that will cause a geometric object to rotate around the x-axis by the specified number of degrees. The left hand rule determines the direction of positive rotation. example:
(x-rot 30) ==> #<TRANSFORM 1 0 0 0
0 0.87 0.5 0
0 -0.5 0.87 0
0 0 0 1>
i.e., a transform that will rotate a vertex about the x-axis
by 30 degrees. The rotation will be counter-clockwise if
viewed standing at the origin, sighting along the x-axis.
- (y-rot angle) The argument must evaluate to a number that represents an angle specified in degrees (usually a number between -360 and 360). Returns a transform that will cause a geometric object to rotate around the y-axis by the specified number of degrees. The left hand rule determines the direction of positive rotation. example:
(y-rot 30) ==> #<TRANSFORM 0.87 0 -0.5 0
0 1 0 0
0.5 0 0.87 0
0 0 0 1>
i.e., a transform that will rotate a vertex about the y-axis
by 30 degrees. The rotation will be counter-clockwise if
viewed standing at the origin, sighting along the y-axis.
- (z-rot angle) The argument must evaluate to a number that represents an angle specified in degrees (usually a number between -360 and 360). Returns a transform that will cause a geometric object to rotate around the z-axis by the specified number of degrees. The left hand rule determines the direction of positive rotation. example:
(z-rot 30) ==> #<TRANSFORM 0.87 0.5 0 0
-0.5 0.87 0 0
0 0 1 0
0 0 0 1>
i.e., a transform that will rotate a vertex about the z-axis
by 30 degrees. The rotation will be counter-clockwise if
viewed standing at the origin, sighting along the z-axis.
- (scale x-scale y-scale z-scale) The three arguments must each evaluate to numbers. Returns a transform that will cause a geometric object to be scaled by the scale factors x-scale in the x direction, y-scale in the y direction, and z-scale in the z direction. A given scale factor is simply a multiplier on the specified coordinate value, so a scale factor of 1 leaves the value unchanged, 2 doubles it, and 0.5 halves it. Using a scale factor of 0 is dangerous, since it can cause vectors (i.e. sides of objects) to reduce to length 0. Using negative scale factors can be useful in turning polygons inside out, and reflecting them about one of the coordinate axes, but this should only be used when you are sure you know what you are doing, since it can produce some pretty strange results. example:
(scale 1 0.25 3) ==>
#<TRANSFORM 1 0 0 0
0 0.25 0 0
0 0 3 0
0 0 0 1>
i.e., a transform that will scale a vertex by leaving its
x-coordinate unchanged, reducing its y-coordinate to
one-quarter of its original value, and tripling its z-coordinate.
- (compose transform1 transform2 ...) The each arguments must evaluate to a transform. Returns a transform that is the composition of all of the argument transforms. This transform will have the same effect as doing each of the individual transforms one at a time, in left to right order. Note that transforms are not, in general, commutative so the order in which the arguments are specified is very important. example:
(compose (translate 100 0 0) (z-rot 30)) ==>
#<TRANSFORM 0.87 0.5 0 0
-0.5 0.87 0 0
0 0 1 0
100 0 0 1>
i.e., a transform that will first shift a vertex by 100 in the
x direction, then rotate it about the z-axis 30 degrees.
The following procedures allow use of transforms to operate directly on vertices and polygons.
- (vertxform transform vertex) The first argument must evaluate to a transform, and the second argument to a vertex. Returns a new vertex that is a copy of the original vertex after being operated on by the transform. examples:
(vertxform (identity) '(10 20 30)) ==> (10 20 30)
(vertxform (translate 10 -20 5) '(10 20 30)) ==> (20 0 35)
(vertxform (x-rot 30) '(10 20 30)) ==> (10 2.321 35.98)
(vertxform (y-rot 30) '(10 20 30)) ==> (23.66 20 20.98)
(vertxform (z-rot 30) '(10 20 30)) ==> (-1.34 22.32 30)
(vertxform (scale 1 0.25 3) '(10 20 30)) ==> (10 5 90)
(vertxform (compose (translate 10 0 0) (z-rot 30)) '(10 20 30)) ==>
(7.32 27.32 30)
- (polyxform transform polygon) The first argument must evaluate to a transform, and the second argument to a polygon. Returns a new polygon that is a copy of the original polygon after all of its vertices have been operated on by the transform. examples:
(polyxform (identity) '((0 0 10) (10 0 10) (0 10 10))) ==>
((0 0 10) (10 0 10) (0 10 10))
(polyxform (translate 10 -20 5) '((0 0 10) (10 10 10) (0 10 10))) ==>
((10 -20 15) (20 -20 15) (10 -10 15))
(polyxform (x-rot 30) '((0 0 10) (10 0 10) (0 10 10))) ==>
((0 -5 8.66) (10 -5 8.66) (0 3.66 13.66))
(polyxform (y-rot 30) '((0 0 10) (10 0 10) (0 10 10))) ==>
((5 0 8.66) (13.66 0 3.66) (5 10 8.66))
(polyxform (z-rot 30) '((0 0 10) (10 0 10) (0 10 10))) ==>
((0 0 10) (8.66 5 10) (-5 8.66 10))
(polyxform (scale 1 0.25 3) '((0 0 10) (10 0 10) (0 10 10))) ==>
((0 0 30) (10 0 30) (0 2.5 30))
(polyxform (compose (translate 10 0 0) (z-rot 30))
'((0 0 10) (10 0 10) (0 10 10))) ==>
((8.66 5 10) (17.32 10 10) (3.66 13.66 10))
Colors are specified using Scheme's list structure. They are used in defining the colors of lights, backgrounds and foregrounds, and in defining the surface colors of materials. The conventions for defining colors within Jabka are listed below.
- rgb-color (r g b) A three-element list giving, respectively, the red, green and blue primary components of a color specified in the RGB system. r, g, and b must have numeric values between 0 and 1, where 0 indicates none of the corresponding primary, and 1 indicates full intensity of the primary.
- hsv-color (h s v) A three-element list giving, respectively, the hue, saturation, and value components of a color specified in the HSV system. Hue determines the chromatic content of the color, and is specified as an angle in degrees around the perimeter of a color wheel. The primary hues of red, green, and blue are at angles of 0, 120, and 240 degrees respectively, and the secondary hues of yellow, cyan, and magenta are at 60, 180, and 300 degrees. h can be any numeric value, but by convention is usually specified as a number between 0 and 360. Saturation is a measure of the purity of the color -- thinking of paint, a saturated color is made from pure colored pigment, and is desaturated by adding white pigment. s must have a numeric value between 0 and 1, where 0 indicates a completely desaturated color (i.e. white) and 1 indicates a completely saturated or pure color. Value is a measure of the intensity or brightness of the color -- again, thinking of paint, a color of high value has no black added to it and is reduced in value by the addition of black paint. v must have a numeric value between 0 and 1, where 0 indicates pure black, no matter what the hue or saturation, and 1 indicates full brightness. Note, that in the HSV system, a color reaches maximum chromatic intensity when both value and saturation are 1.
- hls-color (h l s) A three-element list giving, respectively, the hue, lightness, and saturation of a color specified in the HLS system. The HLS color system is very similar to the HSV system described above, but with lightness playing the role of value, and with the parameters ordered slightly differently. The main difference between HLS and HSV is that in the HLS system a color of maximum lightness is pure white, regardless of its hue and saturation. This system recognizes the fact that there is a coupling between saturation and perceived brightness of a color -- the more white pigment is in the color, the brighter it appears. Thus, in the HLS system, maximum chromatic intensity for a color is reached when lightness is 0.5 and saturation is 1. h and s are specified as for hsv-colors. l must be a number between 0 and 1, with 0 indicating pure black, and 1 indicating pure white.
All routines in Jabka that expect a color as an argument require that color to be specified in the RGB system. The following procedures provide the color system conversions required if you want to work in either the HSV or HLS system.
- (hsv2rgb hsv-color) The argument must evaluate to an hsv-color. Returns the equivalent rgb-color. example: (hsv2rgb '(240 0.6 0.8)) ==> (0.32 0.32 0.8) a light gray-blue
- (hls2rgb hls-color) The argument must evaluate to an hls-color. Returns the equivalent rgb-color. example: (hls2rgb '(60 0.5 1)) ==> (1 1 -1) a bright yellow
- (pick-color rgb-color) Currently operates on the Macintosh only. The argument may be omitted, but if present, it must evaluate to an rgb-color. Calls the Macintosh operating-system's color picker routine, placing a dialog box on the screen that allows you to use the mouse to interactively choose a color. When you click the OK button, pick-color returns the rgb-color corresponding with the color you have chosen. If you click the Cancel button it returns the empty list. Hue and saturation are selected by moving the mouse around a color wheel. A slider is provided for chosing value. The color currently chosen is displayed in the upper left-hand corner of the dialog box. If the rgb-color argument is present, the color picker will start out with this color, and keep this initial color displayed just below the interactively chosen color. This is handy if you would like to refine a color that is close to the one you want. If the argument is not present, the color picker starts out with white. example: (pick-color '(1 1 0)) ==> (0.9 0.7 0.1) the user supplies pure yellow as a starting color, and then interactively modifies that color towards a yellow brown -- the returned value is an example only, it could have been any valid rgb-color.
The following named colors are predefined in Jabka. You can use these names wherever an rgb-color is called for.
- black (0 0 0)
- dkgray or dkgrey (0.25 0.25 0.25)
- gray or grey (0.5 0.5 0.5)
- ltgray or ltgrey (0.75 0.75 0.75)
- white (1 1 1)
- red (1 0 0)
- green (0 1 0)
- blue (0 0 1)
- yellow (1 1 0)
- cyan (0 1 1)
- magenta (1 0 1)