Express Editor Release Notes

© Copyright 1996, ModelWorks Software

Introduction

What is Express? Express is a VRML preprocessor that lets you write VRML with expressions to generate VRML In Express almost all VRML field values can be defined using general expressions. For example in Express you can write:

   orientation =PointAt(10,5,0)       # Points the camera at (10,5,0) from the camera's position

rather than having to figure out what the rotation parameters should be. Expressions can use the usual arithmithic operators (e.g., +,-,*,/), conversion operators (e.g., 45'deg), fields names as variables, and many built-in functions. Expressions can define scalar, vector or matrix values.

Express also includes a number of specialized statements and nodes, such as INCLUDE and LOOP lets you write general programs using Express to generate VRML.

If you are new to VRML or 3D graphics

  1. Read the VRML 1.0 Specification
  2. Read VRML Browsing and Building Cyberspace by Mark Pesce (ISBN-1-56205-498-8), The VRML Sourcebook by Andrea Ames, David Nadeau, and John Moreland (ISBN 0-471-14159-3), or Special Edition Using VRML by Stephen Matsuba and Bernie Roehl (Que Publishing - to be published in February 1996).
  3. Read a graphics book to understand 3D graphics such as Fundamentals of Interactive Computer Graphics by Foley and Van Dam (ISBN 0-201-12110-7).


Table of Contents


Using Express

Figure 1 shows the main window of Express. It consists of an edit pane and a message pane. The upper panel is the editor and the lower panel displays messages generated while building a VRML world file. To build a world file use the Build command from the Express menu. Use the Insert and Function menu to insert templates into your file.

Figure 1. Express window.

For example, to build a simple world do the following:

  1. Choose the Header menu item from the Insert menu. This will insert the VRML header and a Separator node.
  2. Choose a shape to insert, such as a cone, from the Insert-Shapes menu

The resulting Express code - which is also legal VRML in this case - should look like this:

#VRML V1.0 ascii

Separator {
        # Options: ON OFF AUTO
        renderCulling AUTO

        Cone {
                parts ALL
                bottomRadius 1
                height 1
        }
}

Next, choose Build from the Express menu. This will create a world file "Output.wrl" in your default directory.

About the examples

Most of the examples discussed in this document are include the Examples folder inside your Express folder. Both Express and world files are included. A few of the examples do not work well in all browsers. Drive.wrl should be driven using WebFX. Stars.wrl should be viewed using VRScout.

Writing Programs

Express programs use a general expression together with special nodes and statements. Express programs can be as simple as using expressions to define values of fields or can be as complex as using loops, conditional statements and parameter based objects.

Using expressions

To use an expression in Express just enter an expression in place of a VRML field value. In the following example the orientation field is defined by the Express function pointAt and the heightAngle is defined an Express unit conversion expression.

PerspectiveCamera {
        position -10 5 10
        orientation =PointAt(0,5,0)       # Points the camera at (0,5,0) from (-10,5,10)
        heightAngle 45'deg                # Converts 45 degrees to radians
}

You can also use the values of other fields in an expression. If the field you want to use is in the current node or in a parent of the current node you can just use the name the field in the expression. If the field you want to use is not in the path of the current node or there is more than one field using the same name in the path you can refer to the field using the nodeName ':' fieldName. For example the first value of the translation field defined below is given the value of the loop's position field.

LOOP {
       fields [SFFloat start, SFFloat end, SFFloat position, SFFloat step]
       start 0
       end 5
       
       Translation {
           translation position 0 0  # The first element of the translation field is set to the
       }                             # the value of the LOOP's position field
}

See operators and functions for more information.

Placing objects

One of the easiest programs to write in Express is to use a loop to place objects in a world. The following simple example places 6 Spheres on the y-axis. By changing the start and end values you can easily generate more or fewer objects. By changing the translation field you can place the objects in different locations. Example files: Loop.vxr and Loop.wrl

LOOP {
        fields [SFFloat start, SFFloat end, SFFloat position, SFFloat step]
        start -3
        end 4
        
        Separator {
                Translation {
                        translation 0 position 0
                }
                Sphere {
                        radius .5
                }
        }      
}

The next example lays out the columns for a "greek" temple. Here we use a double loop together with an INCLUDE for the definition of the column. The SELECT is used to include the column only once. From then on a USE statement is used to reference the definition of the greekColumn. Note the use of a conditional expression to compute whichChild.

DEF xAxis LOOP {
        fields [SFFloat start, SFFloat end, SFFloat position, SFFloat step]
        start 0
        end 5
        
        DEF zAxis LOOP {
                fields [SFFloat start, SFFloat end, SFFloat position, SFFloat step]
                start 0
                end 5
                
                Separator {
                        Translation {
                                translation xAxis:position 0 zAxis:position
                        }
                        
                        SELECT {
                                fields [ SFLong whichChild ]
                                whichChild if(xAxis:position==0&&zAxis:position==0)then(0)else(1)
                                
                                INCLUDE Column.vxr  # Include greekColumn definition only once
                                
                                USE greekColumn     # Use a reference the rest of the time
                        }
                }
        }
}

Example files: Greek.vxr, Drive.vxr and SpiralStairs.vxr

Building objects

This example defines the points for a triangle located on the x-axis and then reflects the triangle across the x = 0 plane. The APPEND statement does most of the work. Its job is to apply the transformation matrix to the triangle data and then to append the results to the to the original triangle data. In 3D graphics we apply a transformation by multiplying the data by the transformation matrix. But before we can multiply we must first convert the 3D data to 4D homogeneous coordinates, then multiply 4D data points by the matrix, and then convert the results back to 3D (It's easier to do in Express than to say it!). The result of this program will be a Coordinate3 object with two triangles, each a mirror image of the other. Example files: Reflect.vxr. Reflect.wrl. Matrix.vxr and Matrix.wrl.

DEF object Coordinate3 {
        point [1 0 0, 2 0 0, 2 1 0 ] # MFVec3f
}

APPEND object:point convert3D(convert4D(object:point)*yzReflect())

The next example generates the data for a 10 by 10 meter grid on the plane y = 0. It uses the APPEND statement together with a loop to generate the grid.. Each time through the loop we generate one grid line parallel to the x-axis and another grid line parallel to the z-axis. Since the loop is repeated 10 times, this results in a 10 by 10 grid. Example files: Grix.vxr and Grid.wrl

# This is where we will put the grid data
DEF gridData Coordinate3 {
        point [] # MFVec3f
}

# This is where we will put the polyline indexes
DEF grid IndexedLineSet {
        coordIndex [] # MFLong
}

LOOP {
        fields [SFFloat start, SFFloat end, SFFloat step, SFFloat position ]
        start 0
        end 10+1
        step 1

        # Generate the grid points
        APPEND gridData:point (0,0,position) 
        APPEND gridData:point (10,0,position) 
        APPEND gridData:point (position,0,0) 
        APPEND gridData:point (position,0,10) 

        # Generate the polyline indexes
        APPEND grid:coordIndex (position-start)*4
        APPEND grid:coordIndex (position-start)*4+1
        APPEND grid:coordIndex -1 # end this polyline
        APPEND grid:coordIndex (position-start)*4+2
        APPEND grid:coordIndex (position-start)*4+3
        APPEND grid:coordIndex -1 # end this polyline
}

Using and Building Parameter Based Objects

Using parameter based objects is a way to speed up writing VRML and also helps you to organize your world. For example suppose that you want display an x-z grid in your world as a debugging aid. You could create a grid using the Express program described above, or you could INCLUDE a parameter based object. The file gridXZ.vxr is a parameter based object that lets you define its grid parameters. It can be used like this:

PARAMETERS {
        # These are the parameters you will "pass" to gridXZ.vxr
        fields [SFFloat fromX, SFFloat toX, SFFloat fromZ, SFFloat toZ, SFFloat gridSpacing]
        fromX -10 
        toX 10 
        fromZ 0 
        toZ 10 
        gridSpacing .5 
        INCLUDE gridXZ.vxr 
}

To build a parameter based object you write a program that uses inherited fields in its expressions and save it in an Express file. Example files: GridXZ.vxr and StreetLight.vxr.

Integrating Files

If you are building world files using multiple tools you may want to use Express to automate the integration of these files. For example to build a simple Express program to integrate two world files do the following:

  1. Insert a header
  2. Use the statement INCLUDE fileName.wrl for each file you want to include
  3. Use the statement OUTPUT_TO fileName.wrl to change the name of the default output file
  4. Add transforms as appropriate
#VRML V1.0 ascii

Separator {
        # Options: ON OFF AUTO
        renderCulling AUTO
        
        
        Separator {
              Transform {
                     # set transform fields as appropriate
              }
              INCLUDE file1.wrl
        }
        Separator {
              Transform {
                    # set transform fields as appropriate
              }
              INCLUDE file2.wrl
        }

        OUTPUT_TO integratedFile.wrl
}

Language Reference

The language used by Express is a superset of VRML. A VRML 1.0 file is legal input for Express 1.0. However the converse is not necessarily true. The reason for this is that Express allows general symbolic expressions for most field values and also includes new statements.

Expressions

The ability to use expressions to define value and to refer to values of fields is a key part of Express. Express allows the values of most VRML fields to be specified using an expression. Fields which cannot be defined using an expression are SFEnum, SFBitMask and SFImage.

General Syntax

Express expression syntax provides much of the capability of expressions that are provided in other languages. For detailed descriptions see the grammar and operator reference sections included in this document.

However because VRML uses spaces to separate values in fields, expressions must be written without spaces. For example:

(sqrt(x*x+y*y)) # is legal
(sqrt(x*x + y*y)) # is illegal

Vector and matrix expressions

Normally, Express reads an expression for each element of a field. However, reading a field element by element does not let a user use a function or expression that can define a field. To solve this problem Express uses an '=' character at the start of an expression to indicate the following expression is a field expression.

Examples:

MatrixTransform {
matrix =YRotate(45'deg)  # This function computes the 4x4 transform matrix for a 45 deg rotation 
                         #about the Y axis
}

DEF data Coordinate3 { 
        point[1 0 1, 0 1 1, 1 1 0]
}
Coordinate3 { 
        point =data:point # Defines the point field using a value from the data node above
}

DEF colorData PARAMETERS {
        fields [MFColor color ] 
        color [ =Color("red"), =Color("blue"),=Color("green") ]
        # The Color function returns a SFVec3f value
}
Material {
        ambientColor [ =colorData:color, 0 0 0 ] # MFColor
}

To define a vector from scalar values you enclose a list of scalars in parenthesis. Examples

(0,0,0) # Defines a SFVec3f
((1,0,0,0);(0,1,0,0);(0,0,1,0);(0,0,0,1)) # Defines the 4x4 Identity matrix

NOTE: You should be aware that operations on matrixes and vectors don't quite follow the rules of scalar operations (e.g., 4+7). Only certain operations on vectors and matrixes are defined. You can add and subtract vectors and matrixes so long as their dimensions are the same. You can multiply a vector and a matrix provided that the number of columns of the vector is equal to the number of rows of the matrix. You can also multiply two matrixes provided that the number of columns of the first matrix is equal to the number of rows of the second matrix..

Defining node names

You can use "VARNAME" in place of the VRML "DEF" keyword to define the name of a node using an expression. The syntax of VARNAME is:

VARNAME nodeName NODECLASS  

where nodeName is of the form name~`expr1`~`expr2`…. This expression concatenates name with the ASCII integer value of expr1 and expr2. For example: ball~`expr1`~`expr2` creates the names ball23 if expr1 evaluates to 2 and expr2 evaluates to 3. You can also use this form of nodeName in a USE statement.

See the Lattice.vxr file for an example of how to use VARNAME

Express Nodes

Express uses nodes to implement operations that are performed when you build a world file. In general, these nodes are not copied to world files and produce no output of their own. However if you include VRML nodes as children they will be copied to the world file. Express nodes are specified like the VRML Unknown Node - that is they require a fields […] definition after the bracket.

This release of Express includes the following nodes:

  1. PARAMETERS
  2. LOOP
  3. SELECT

PARAMETERS node. The PARAMETERS node performs no function other than allowing you to define fields and to include children. As with other nodes it is not copied to world files, but its children are copied if appropriate. Think of it as a place to store parameters or private data. Its syntax is:

PARAMETERS {
        fields [ <field type> <field name>]
        # set field values as required
        # note that it is not necessary to set the value of fields if
        # they are set before they are used
}

Example:

DEF parameters PARAMETERS {
        fields [ SFFloat param ]
        param 10   # The value of param is set to 10
                   # To use the value of param later refer to it as  "parameters:param" 
                   # If this node is a parent then you can refer to 
                   # the value as "param" and let 
                   # Express finds the value using inheritance
}

LOOP node. The LOOP node lets you repeat a group of nodes. Its syntax is:

LOOP {
        fields [SFFloat start, SFFloat end, SFFloat position, SFFloat step]
        start <expression>
        end <expression>
        step <expression>
        
        # Nodes included here will be executed each time the loop is repeated
}

The loop is repeated for start to end by step while position is less than end. The default value of step is 1. The field position contains the current value of the loop as it goes from start to end by step.

The following example places 7 spheres along the y axis from x equal -3 to x equal 3 The value of position in the translation field is determined using inheritance.

LOOP {
        fields [SFFloat start, SFFloat end, SFFloat position, SFFloat step]
        start -3
        end 4
        
        Separator {
                Translation {
                        translation position 0 0
                }
                Sphere {
                        radius .5
                }
        }
}

SELECT node. This node uses the same syntax as the VRML Switch node. Its syntax is:

SELECT {
        fields [ SFLong whichChild ]
        whichChild <expression>  
        
        <child node #0>
        
        <child node #n-2>
        <child node #n-1>
}

This node evaluates one, none, or all of its children. The whichChild field specifies the index of the child to traverse, where the first child has index 0. A value of -1 (the default) means do not evaluate any children. A value of -3 evaluates all the children.

The following example places 7 objects along the y axis from x equals -3 to x equals 3. If abs(position mod 2) is 0 then the Cube will be generated, if abs(position mod 2) is 1 then the Sphere will be generated

LOOP {
        fields [SFFloat start, SFFloat end, SFFloat position, SFFloat step] 
        start -3
        end 4
        
        SELECT {
                fields [ SFLong whichChild ]
                whichChild abs(position%2) 
        
                Separator {
                        Translation {
                                translation position 0 0
                        }
                        Cube { 
                                width .5
                                height .5
                                depth .5
                        }
                }
        
                Separator {
                        Translation {
                                translation position 0 0
                        }
                        Sphere {
                                radius .5
                        }
                }
        }
}

Express Statements

  1. APPEND
  2. DECLARE
  3. OUTPUT_TO
  4. PRECISION
  5. PRINT
  6. SET

APPEND. This statement lets you append a value to a MF field. The syntax is:

APPEND nodeName:fieldName value

The value may be one or more complete elements for a MF field. Note that you cannot append to part of field.

Examples:

DEF data Coordinate3 { 
        point[]
}
APPEND data:point (1,1,1) # Add one point
APPEND data:point ((1,1,1),(0,0,0),(1,1,0)) # Add three points
APPEND data:point 1 # Illegal - point requires three values

DECLARE. This node lets you declare the number of fields in a MFField. Its only purpose is to speed up world building when the size of a MFField is large. The syntax for this node is:

DECLARE nodeName:fieldName value

If value is a scalar then that value is used to pre-allocate fields for the specified MFField. If value is a vector then the first element is used to pre-allocate fields and the second element is the grow factor. The default grow factor for MFFields is 10

Examples:

DECLARE node:color  256 # Allocates 256 fields for the MFColor field
DECLARE node:point (400,50)  # Allocates 400 fields and sets the grow factor to 50

OUTPUT_TO. This statement lets you change the default output file. Its syntax is:

OUTPUT_TO fileName.wrl

Examples:

OUTPUT_TO  Cone.wrl
OUTPUT_TO  "Release/Cone.wrl"

PRECISION. This statement lets you set the value of significant number of digits output to a world file. Its syntax is:

PRECISION value

Value may be any scalar expression. Generally the value should be some power of 10 such as 10, 1, 0.1, 0.01.

Example:

PRECISION .0001 # This is the default

PRINT node. Use this statement to print out the value of an expression to help you debug an Express program. The output will be displayed in the message pane of the Express window. The expression should be a scalar. Its syntax is:

PRINT expression

Example:

PRINT position   # Will output "Line nn: position is X" 

SET. This statement lets you set the value of fields in other nodes. The syntax for this node is:

SET nodeName:fieldName value

Value may be any legal expression. This includes a whole field or some part of a field.

Examples:

SET node:point  (1,1) # Sets the value of a SFVec2f
SET node:color[1][2]  .5  # Sets the blue value of the second field of a MFColor
SET node:point  data:point  # Sets the value of node:point to the value of field data:point

Generator Nodes

This release of Express contains three generator nodes. Generator nodes are high level nodes used to describe a graphical object from a minimum amount of data. More generators will be added in future releases.

Ball Node

The Ball node is a simple generator that lets you place a sphere at a given location by just specifying the center field. Is syntax is:

Ball {
        fields [ SFVec3f center, SFFloat radius ] 
        center 0 0 0
        radius 1
}

Use this node with the Connect node described below. You can use this node to build a ball and stick chemical model. Example files: Connect.vxr, Lattice.vxr

Connect Node

The Connect node lets you connect two nodes together with a cylinder. Is syntax is:

Connect {
        fields [ SFNode first, SFNode second, SFFloat radius ] 
        first nodeName                # Name of a node with a SFVect3f center field
        second nodeName               # Name of another node with a SFVect3f center field
        radius 1'cm
}

Use this node with the Ball node described above. Example files: Connect.vxr , Lattice.vxr

Wall Node

The Wall node lets you describe a wall in terms width, its elevation, its height, and its inside or outside x-z coordinates. The Wall node uses the height and elevation field to locate the wall in the vertical direction. If there are more data elements than height or elevation elements, the Wall node will use the last height or elevation element. Its syntax is:

Wall {
        fields [ SFLong placement, SFFloat width, MFFloat height, 
                MFFloat elevation, MFVec2f data ] 
        placement 1 # -1: Inside, 1: Outside 
        width 6'in
        height [ 8'ft ] 
        elevation [ 0 ] 
        data [] # Describe the (x,y) coordinates of the wall here
}

Examples:

Wall {
        fields [ SFLong placement, SFFloat width, MFFloat height, 
                MFFloat elevation, MFVec2f data] 
        placement -1
        width 8'in
        height [ 8'ft ] 
        elevation [0] 
        data [ 0 0, 2 0, 4 0, 4 2, 8 2, 8 4, 8 6, 0 6, 0 4, 0 2, 0 0 ] 
}

Example files: Walls.vxr, Drive.vxr

Menu Commands

Edit menu

The Edit menu contains the standard edit items along with commands for indenting, commenting and reformating VRML. To indent or unindent a block of code select the block of code and choose indent or unindent. You can also use the tab key and the shift-tab keys to indent and unindent. The comment and uncomment can be used to comment and uncomment a block of code. The comment insert the VRML comment character '#' at the beginning of each line of code each time you use comment. Uncomment removes the first comment character at the beginning of each line of code. Reformat indents a whole file according the level of each node.

Express menu

Use the build command in the Express menu to generate a world file. Any errors detected or messages generated while Express is building your VRML file will be displayed in the message pane at the bottom the Express window. When you click on a message, Express high-lights and shows the corresponding VRML source related to the message.

By default Express places the output generated by the build command in the file "Output.wrl" in the current directory. To change the default use the OUTPUT_TO statement.

Note: Express always saves your file before building the world file. If the file you are compiling has not been saved before, Express will prompt you for a file name. This file will be saved as a .vxr file (Express file). If you are compiling a .wrl file, Express will also prompt you for a file name and will save the original file as a .vxr file before compiling.

View menu

The view menu contains commands that let set the font and tab size. To set these attributes click on the appropriate pane and then choose an attribute to set from the View menu.

Insert menu

The Insert menu contains Express and VRML templates. This make it easy to insert code without having to remember, look up or type the syntax for Express or VRML

Function menu

The function menu contains a list of all the built-in functions in Express. As with the Insert menu, selecting a function will insert the function template into your file.

Error messages

Most error messages should be easy to understand. However you may need some help with the message "Syntax error: found token - expecting token or token,…". To interpret syntax error message, first click on the error message to find out where the error occurred, then use the grammar to interpret the error. For example suppose you used the statement: "SET index 1". This statement will generate a error message of the form "Syntax error found NUMBER - expecting ':' or '~'". Since the error occurred in a SET statement look up the syntax for the SET statement in the grammar. There you will find that Express is expecting a "nodeName:fieldName expr" for a simple SET statement. In this case the error message is telling you that Express found the expr (the NUMBER token) but was expecting a ':' followed by a fieldName which it did not find.

Built-in Runtime Functions

Constants

e                       e()
                        Return the constant e (2.718281828459045)
pi                      pi()
                        Returns the constant pi (3.141592653589793)

General Math

abs, fabs               abs(x), fabs(x)
                        Computes the absolute value of x
arc cosine              acos(x)
                        Computes the arc cosine of x
arc sine                asin(x)
                        Computes the arc sine of x
arc tangent             atan(x)
                        Computes the arc tangent of x
                        atan2(x, y) 
                        Calculates the arctangent of y/x.
                        Atan2 returns a value in the range of -pi/2 to pi/2.
                        If x or x and y are zero then atan2 returns 0.
ceiling                 ceil(x)
                        Rounds the x to the next integer. 
cosine                  cos(x)
                        Computes the cosine of x
distance                dist(x1, y1, x2, y2), dist(x1, y1, z1, x2, y2, z2),
                        dist(point, point)
                        Computes the Euclidean distance between two points
exp                     exp(x)
                        Computes the e raised to x
floor                   floor(x)
                        Truncates the x to its integer part. 
float modulus           fmod(x, y) 
                        Returns the remainder of x/y.  This is same as x % y.
hyperbolic cosine       cosh(x)
                        Computes the hyperbolic cosine of x
hyperbolic sine         sinh(x)
                        Computes the hyperbolic sine of x
hyperbolic tangent      tanh(x)
                        Computes the hyperbolic tangent of x
hypotenuse              hypot(x, y)
                        Returns the square root of x*x + y*y.
interpolate             interpolate (point, point, amount),
                        interpolate (x, y, amount)
                        Returns the interpolated value between two points. 
                        Amount should be between 0 and 1.
log                     log(x)
                        Computes the log base e of x
log10                   log10(x)
                        Computes the log base 10 of x
maximum                 max(x, y)
                        Returns the maximum of x and y.
minimum                 min(x, y)
                        Returns the minimum of x and y.
power                   pow(x, y)
                        Returns the x raised to y.  This is same as x^y.
rand                    rand()
                        Returns a random number between 0.0 and 1.0
round                   round(x, precision)
                        Rounds x to precision
sine                    sin(x)
                        Computes the sine of x. 
square root             sqrt(x)
                        Computes the square root of the s. X must be greater than or equal to 0.0. 
tangent                 tan(x)
                        Computes the tangent of x. 

Special functions

pointAt                 pointAt(x,y,x), pointAt(point)
                        Computes the orientation value for the camera. Returns a SFRotation value. 
                        A position field must be defined in the same node in order for these 
                        versions of the function to work.
                        pointAt(fromPoint,toPoint) 
                        Same as above but can be used to set any SFRotation field
                        Note:  Both PointAt functions assume that the rotation is to be computed
                        from the vector going from (0,0,1) to (0,0,0)
rotate                  rotate(x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4)
                        rotate(point1,point2,point3,point4)
                        rotate(fromDirection,toDirection)
                        Like pointAt above, rotate computes a SFRotation field, but rotate lets 
                        you define the fromVector and the toVector The first two points define
                        the fromVector and the third and fourth define the toVector. The third
                        form assumes that each point is a unit vector

FractalSurfaceData      FractalSurfaceData(rows, cols, roughness, predefined)
FractalSurfaceIndex     FractalSurfaceIndex(rows, cols)
                        These two functions are used together to generate the data for a fractal
                        surface.  The rows and cols parameters determine the size of the surface
                        in meters.  For now the number of rows and cols should be equal. 
                        The neutral value of roughness is 1.0.  Values greater than one make the 
                        surface rougher.  The predefined parameter is a MFFloat value.
                        The first element specifies the height of the surface in the center.  
                        The next four elements specify the height for the midpoints from the
                        center.  Additional points are applied recursively until all the
                        predefined values are used. 

Matrix functions

determinate             determinate(matrix)
                        Computes the determinate of a square matrix
inverse                 inverse(matrix)
                        Computes the inverse of a square matrix
transpose               transpose(vector), transpose(matrix) 
                        Returns a transpose matrix

Homogeneous coordinates

convert4D               convert4D(vector) convert4D(matrix)
                        Converts a Nx3 array to a Nx4 homogeneous array
convert3D               convert3D(vector), convert3D(matrix) 
                        Converts a Nx4 homogeneous array to a Nx3 array

Transformation matrix operations

All these operations return a 4x4 transformation matrix

translate               translate(x,y,z)
                        Generates the 4x4 transformation matrix to translate points by x,y,z.
scale                   scale(overall)
                        Generates the 4x4 transformation matrix to scale points by amount overall.
scale                   scale(x,y,z);
                        Generates the 4x4 transformation matrix to scale points by x,y,z.
xyReflect               xyReflect()
                        Generates the 4x4 transformation matrix to reflect points across the z = 0 plane.
yzReflect               yzReflect()
                        Generates the 4x4 transformation matrix to reflect points across the x = 0 plane.
xzReflect               xzReflect()
                        Generates the 4x4 transformation matrix to reflect points across the y = 0 plane.
xRotate                 xRotate(angle)
                        Generates the 4x4 transformation matrix to rotate points around the x-axis by angle.
yRotate                 yRotate(angle)
                        Generates the 4x4 transformation matrix to rotate points around the y-axis by angle.
zRotate                 zRotate(angle) 
                        Generates the 4x4 transformation matrix to rotate points around the z-axis by angle.
rotate                  rotate(vector,vector,angle)
                        Generates the 4x4 transformation matrix to rotate points around the line
                        specified by the two vectors by angle.

Color Functions

color                   color(name)
                        Returns a predefined color by name.  The following colors are predefined
                        red, green, blue, yellow, cyan, magenta, black, white, gray, lightGray, darkGray
grayColor               grayColor(amount)
                        Returns a gray color with density equal to amount. 
                        Amount should be between 0 (white) and 1 (black).

Note:  You can use interpolate to get other colors (e.g., interpolate(color("red"),color("green"),.25))

Operator Summary

Operator               Notation                               Precedence (from low to high)

comma                  x, x                                   from left to right
conditional            if(x)then(y)else(z)                    from right to left
                       NOTE: the parenthesis are required

logical or             x || y                                 from left to right
logical and            x && y

equal                  x == y                                 from left to right
not equal              x != y > 
less than or equal     x <= y
less than              x < y 
greater than or equal  x >=y 
greater than           x > y

add                    x + y                                  from left to right
subtract               x - y

multiply               x * y                                  from left to right
divide                 x / y
remainder              x % y

power                  x ^ y                                  from right to left

unary logical not      !x                                     from right to left

unary plus             + x                                    from right to left
unary minus            - y

select field           nodeName:fieldName                     from right to left
                       :fieldName    inherited only
                       fieldName      local and inherited

subscript              fieldName [index]                     from left to right
                       fieldName [index] [index] 

grouping               (x + y) * z                            from left to right
array                  ((x1,y1);(x2,y2)) 

unit conversion        x ' unit name                          from left to right
     inch              x ' in         converts to meters
     foot              x ' ft         converts to meters
     yard              x ' yd         converts to meters
     centimeter        x ' cm         converts to meters
     degree            x ' deg        converts to radians
     gradient          x ' grad       converts to radians

Express Grammar

statement
        :       expr
        |       = expr
        |       USE stringExpr
        |       STRING stringExpr
        |       DECLARE stringExpr : stringExpr expr
        |       SET stringExpr : stringExpr expr {
        |       SET stringExpr : stringExpr [ expr ] expr
        |       SET stringExpr : stringExpr [ expr ] [ expr ] expr
        |       APPEND stringExpr : stringExpr expr
        ;
        
expr
        :       factor
        |       + expr 
        |       - expr 
        |       expr + expr                          // Addition operator
        |       expr - expr                          // Subtraction operator
        |       expr * expr                          // Multiplication operator
        |       expr / expr                          // Division operator
        |       expr % expr                          // Remainder operator
        |       expr ^ expr                          // Power operator
        |       ! expr                               // Logical NOT operator
        |       expr && expr                         // Logical AND operator
        |       expr || expr                         // Logical OR operator
        |       expr == expr                         // Equal operator 
        |       expr != expr                         // Not equal operator 
        |       expr < expr                          // Less than operator
        |       expr > expr                          // Greater than operator
        |       expr <= expr                         // Less than or equal operator    
        |       expr >=  expr                        // Greater than or equal operator
        |       expr ' NAME                          // Conversion operator
        |       if expr then expr else expr          // Conditional operator
        ;

factor 
        :       NUMBER
        |       TRUE
        |       FALSE
        |       stringExpr ( )                       // Function call
        |       stringExpr ( exprList )              // Function call
        |       fieldReference 
        |       fieldReference [ expr ]              // row or element reference
        |       fieldReference [ expr ] [ expr ]     // element reference
        |       cfactor 
        ;

fieldReference
        :       stringExpr                           // Local and inherited field reference
        |       : stringExpr                         // Inherited field reference
        |       stringExpr : stringExpr              // Node field reference
        ;

stringExpr
        :       NAME
        |       stringExpr ~ stringExpr              // String concatenation
        |       stringExpr ~ ` expr `                // String concatenation and evaluation
        ;

exprList
        :       expr
        |       exprList , expr                      // element separator
        ;

cfactor
        :       ( exprList )                         // Grouping *
        |       cfactor; ( exprList )                // row separator **
        ;

*  Generates a scalar if only one element is in exprList
   Generates a vector if more than one element is in exprList
** Generates a matrix if more than one row is in cfactor


Possible future enhancements - not in priority order

  1. More built in functions
  2. More data generation nodes
  3. Express extensions
  4. VRML tools
  5. VRML diagnostic tools
  6. Import of external data
  7. Stock object catalog
  8. Software development kit for defining your own object functions and nodes

Suggestions welcome.