ActiveVRML Reference Manual
Preliminary Version
December 14, 1995
Microsoft Corporation


Notice

This draft specification is published by Microsoft for the purpose of helping to create an open standard. Independent implementations of this specification are expressly permitted by Microsoft irrespective of whether the specification contains, constitutes or reflects Microsoft patents or copyrights.

Microsoft anticipates that it will release a reference implementation in object and source code form. Microsoft expects to license this code in the following manner:


Introduction

This paper presents a technical summary of the Active Virtual Reality Modeling Language (ActiveVRML). It provides a brief but reasonably precise definition of ActiveVRML version 1.0. Those seeking an introduction to ActiveVRML should consult the companion paper, An Introduction To ActiveVRML.

ActiveVRML is intended to provide a framework for constructing models that manipulate media including sound, 2D images, and 3D geometry. There are two characteristics that make ActiveVRML unique and especially well suited for this task: all values in ActiveVRML potentially vary with time, and values may change in reaction to events.

Every value in ActiveVRML may change with time. For example, there is an image type in ActiveVRML. An image object is not like a static photograph, but more like a video, continuously changing with time. Similarly, geometry in ActiveVRML is not like a static geometry model, but is (potentially) animated, moving, and reacting to events. This is an important principle in ActiveVRML; every value may change with time. Even simple objects, like numbers, may change with time. Values that can vary with time are called behaviors in ActiveVRML. A reactive behavior is one that (potentially) varies in response to events.

One way that values change with time is in response to particular events. For example, a user input event, or a mouse event. Events may be caused by internal events (to the ActiveVRML model) as well. For example, a particular number value being zero may cause an event.

Finally, ActiveVRML is a language for describing media via reactive behaviors. The language part of ActiveVRML is actually very small. The bulk of this paper is spent describing a large collection of useful library functions for manipulating media types.


Expressions and Declarations

ActiveVRML is fundamentally very simple; it has just a handful of expression forms and only two forms for declaring identifiers. This section describes these forms. The power of ActiveVRML comes from the underlying model which includes time varying values, reactivity, and a rich set of media types. These are described in subsequent sections.


Literal and Constructor Expressions

Associated with most types in ActiveVRML is a literal or constructor form for each.

Examples include 17 which represents a literal number, and [1, 2, 3], which uses the constructor form for lists to build a list of numbers. Allowable constructor forms are defined below in the sections defining each type.


Variable Expressions

An identifier in ActiveVRML is an arbitrarily long string of alpha-numeric characters beginning with a letter. Identifiers are case sensitive and there are some keywords (listed in Appendix A) that may not be used as identifiers. Variables in ActiveVRML are statically scoped.


Application Expressions

An expression of the form expression1 expression2 is an application expression and represents the value of applying the function value of expression1 to the value of expression2.

ActiveVRML is strict; that is, it obeys call by value semantics for argument evaluation. The order of the evaluation of arguments is not specified. Argument application associates left; for example, f(x)(y) equates to (f(x))(y).


Parenthetical Expressions

Parentheses may be used to group expressions and override operator precedence. Parentheses are also useful for improving the readability and presentation of ActiveVRML models. Operator precedence in ActiveVRML is listed in Appendix A.


If Expressions

A construct of the form
 
if expression1 then expression2 else expression3 
is an IF expression. It represents the value computed by evaluating the boolean test of expression1 and selecting expression2 or expression3 depending upon the true value of expression1. The types of the two branches of the IF expression are required to match (or unify). There is no corresponding IF THEN statement; all IF statements have both branches. Since ActiveVRML is functional (operations do not have side effects), such one-armed IF statements would not be very useful.


Let Expressions

A construct of the form
let 
	declaration1;
		.
		.
		.
	declarationn[;]
in
	expression
is a LET expression. It represents the value of an expression when evaluated in a context that includes the bindings for declaration1 through declarationn. The LET expression is used to introduce a local scope. The declarations are evaluated simultaneously. The names are in scope of the right hand sides of the declarations, allowing for forward declarations and mutual recursion automatically. All of the declared identifiers are required to be distinct. The scope of the declarations is limited to the LET expression itself. The semicolon following the last declaration is optional.

Declarations

The simplest declarations declare an identifier to have a value:
identifier = expression
Or, they declare a function:
identifier(identifier, ..., identifier) = expression
The following is an example function declaration:
swizzle(seed) = 
	if seed = 1 then
		1
	else if odd(seed) then
		swizzle(seed * 3 + 1) + seed
	else
		swizzle(seed / 2) + seed
This declares a function, swizzle, that takes one formal argument, seed. The function is recursive. All function declarations are assumed to be recursive. When you use the name of the function you are declaring within the expression, you are referring to the function itself, not a new or different function.

The following is an example variable declaration:

swizit = swizzle(27)
This declares the variable swizit to be the evaluation of the expression swizzle(27). We can illustrate scoping in LET expressions by combining these declarations along with a declaration for the predicate odd used in the declaration of swizzle:
let 
	swizzle(seed) = 
		if seed = 1 then
			1
		else if odd(seed) then
			swizzle(seed * 3 + 1) + seed
		else
			swizzle(seed / 2) + seed;
	odd(i) = (mod(i, 2) = 1);
	swizit = swizzle(27)
in
	swizit
Notice that the declaration for odd comes after its use in the declaration of swizzle. Since all of the declarations within a LET expression are assumed to be mutually recursive, this is legal. However, for better readability and because the declarations are not truly mutually recursive, the definition of odd should probably appear first.

Within the scope of the LET expression, three identifiers are declared, swizzle, odd, and swizit. Beyond the scope of this expression, the three declarations are not available. The value of the LET expression is the value of swizit which evaluates to 101440.

In addition to these simple forms of variable and function declarations, it is also possible to use pattern matching to specify the destructuring of values within a declaration. This is described later in this document.


Basic Types

ActiveVRML includes a very powerful and useful typing system. Each expression and declaration in ActiveVRML is given a type either explicitly by the user, or implicitly by the system. Consider the following example declaration:
successor(nat) = nat + 1
ActiveVRML assigns successor the type number->number, meaning it will map any value of type number to another value of type number. This typing is strong in the sense that ActiveVRML will catch all type errors during authoring. It is also convenient; the user did not have to explicitly give the type as the system inferred it.

Finally, types are polymorphic, meaning that a given type may stand for many different type instances. Consider the following declaration:

nada(val) = val
When applied, nada will return its actual argument unchanged. Thus nada(3) evaluates to the number 3 and nada("hello") evaluates to the string "hello". The type that ActiveVRML infers for nada is polymorphic: a->a. Here a is a type identifier and may be replaced everywhere uniformly to create an instance of a polymorphic type. Thus, number->number is an instance of a->a, and so is string->string.

Note that number->string is not an instance of a->a, since one a was replaced by a number and the other by a string (not uniformly). A polymorphic type can contain more than one type identifier, for example, a->b. In this case, each identifier can be replaced separately. Thus, number->b, a->string, number->string, number->number, and g->string are all instances of the polymorphic type a->b.

Every expression and declaration in ActiveVRML is assigned a type by the system using a standard Milner-Damas polymorphic type checker. Except to improve exposition and occasionally to resolve ambiguity with an overloaded operator, it is not necessary for the programmer to explicitly give types. An expression may be qualified with a type using the syntax like the following:

expression: type-expression

For example, the following syntax can be used to restrict nada to a particular type (desirable for clarity):

nada(val: string) = val

This will assign nada the monomorphic type string->string. The following sections define the basic types for ActiveVRML and list the constructor forms and functions for these types. Later sections define types for reactivity and for modeling (geometry, images, and associated types).


Unit Type: unit

Type
unit Constructors
()


Function Type: type -> type

Type
type -> type

Constructors
function pattern . expression

Functions
infix o: (a -> b) * (b -> g) -> (a -> g)


Product Type: type * type

Type
type * type

Constructors
expression, expression

Functions
first: a * b -> a

second: a * b -> b


List Type: list

Type
a list

Constructors
[expression-list]

Functions
head: a list -> a

tail: a list -> a list infix::: a * a list -> a list empty: a list -> boolean length: a list -> number map: (a -> b) * (a list) -> b list reduce: a list * b * (a * b -> b) -> b nth: a list * number -> a


Boolean Type: boolean

Type
boolean

Constructors
true

false

Functions
infix and: boolean * boolean -> boolean

infix or: boolean * boolean -> boolean

infix not: boolean -> boolean

infix =: a * a -> boolean

infix <>: a * a -> boolean


Number Type: number

Type
number

Constructors
number-literal

time

random

pi

Functions
infix +: number * number -> number

infix *: number * number -> number

infix -: number * number -> number

infix /: number * number -> number

prefix -: number -> number

prefix +: number -> number

infix <: number * number -> boolean

infix <=: number * number -> boolean

infix >: number * number -> boolean

infix >=: number * number -> boolean

abs: number -> number

sqrt: number -> number

mod: number * number -> number

ceiling: number -> number

floor: number -> number

round: number -> number

radiansToDegrees: number -> number

degreesToRadians: number -> number

asin: number -> number

acos: number -> number

atan: number * number -> number

atan: number -> number

cos: number -> number

sin: number -> number

tan: number -> number

infix ^: number * number -> number

exp: number -> number

ln: number -> number

log10: number -> number

seededRandom: number -> number


Reactive Behaviors

Recall that all values in ActiveVRML are potentially time-varying. Variation is achieved by specifying the time explicitly (for example, 3 + time), input such as mouse motion, or by reactivity. This section defines reactivity and the constructs used to build reactive behaviors.

Reactivity

A reactive behavior is one that can potentially react to an event. The following is a very simple reactive behavior:
red until leftButtonPress => green

In this expression, UNTIL is the main operator. The expression is parsed into the following:

red until (leftButtonPress => green)

The reactive behavior changes the color from red to green when the mouse button is pressed.

The subexpression leftButtonPress => green is called a handler. It pairs up an event, leftButtonPress, with a value, green. The value is the action taken when the event occurs.

The UNTIL construct can also be used to watch for more than one event, reacting to the first one that occurs. For example:

red until leftButtonPress => green | rightButtonPress => yellow

This is parsed into the following:

red until ( (leftButtonPress => green) 
		| (rightButtonPress => yellow) )

The color remains red until either the left or right mouse buttons are pressed. If the left button is pressed, the color changes to green. If the right button is pressed, the color changes to yellow.

In general, the logic of the UNTIL operator follows this pattern:

b0 until e1 => b1
	| e2 => b2
	.
	.
	.
	| en => bn

The reactive behavior is b0 until any one of the events occurs, e1 through en. The first event to occur, ei, results in the behavior, bi.

A more advanced form of events uses event data to produce the next behavior. For example:

0 until numEvent => function x. x + 1

In this case, numEvent is an event that produces a number. The value of this behavior is zero until the event occurs, and then becomes the value associated with the event plus 1.

The type checking of an UNTIL expression is as follows: If b is an a behavior and e is an a event, then b until e is a reactive behavior with type a behavior.

The next section describes events in more detail.

Events and Reactivity

An event can trigger a discrete change in a behavior. In addition to marking the occurrence of an event at a particular time, an event may also produce data. An a event produces some data of type a that can be used as new behavior after the event.

Events are constructed from one of the following types of events.

System Events

System events represent user input events. All of the following are system events:
u	leftButtonPress: unit event
u	rightButtonPress: unit event
u	keyPress: character event

Boolean Events

Events corresponding to a boolean behavior occur as soon as the boolean value is evaluated as true. The predicate function is used to construct an event from a boolean behavior. For example, predicate(x = 0) is a unit event (one that returns a unit as its data) that occurs the first time that behavior x is zero.

Simple Handler Events

New events may be constructed from other events. If e is a unit event (an event that does not produce interesting data) and b is an a behavior, then e=>b is a simple handler. In this example, the behavior is to wait for event e and before becoming b.

Handler Events

More complex handler events use the data produced by an event to construct the resulting behavior. If e is an a event and f is a function with type a->b, then e=>f is a b event. In this example, the behavior is to wait for event e, and then use the data produced by the event to run function f. The result is an event, e=>f, that occurs at the same time that e does and produces f(x) as the data, where x is the data produces by event e.

Alternative Events

If e and e are a events, then e | e is an a event. This means to choose whichever event, e or e, happens first, and then return the corresponding data for the event. If e and e happen at the same time, it is undefined which will be chosen.

Filtered Events

If e is an a event, and pis a function that maps a values to booleans, then suchThat(e, p) is an a event that allows through only occurrences of e whose data satisfies the predicate p.

Snapshot Events

The snapshot construct may be used to make a time varying behavior into a static one (i.e., no longer varying with time). This is useful when one wishes to record the instantaneous value of a time varying behavior for some other use. For example,
0 until snapshot(xComponent(mousePosition), leftButtonPress,)

is the static behavior 0 until the left mouse button is pressed, and then becomes the static value of the x coordinate of the mouse position at the time of the press. The behavior

0 until leftButtonPress => xComponent(mousePosition) 
is different in that it produces a behavior that continues to vary with time and track the x coordinate of the mouse after the button event occurs. The following subsections define the types and operators used in constructing reactive values.

Behavior Termination

Behaviors can terminate. The end construct is of type a (any type) and means to immediately terminate the behavior. For example:
b = time until leftButtonPress => end 

This behavior varies with time until the leftButtonPress event occurs. Then it terminates.

A behavior will also terminate if one of its defining behaviors terminates. For example, consider

b = f(b, 3)

If behavior b terminates, then b terminates at the same time.

The DONE construct is a unit event that can be used to detect when a behavior has terminated and react accordingly. Consider the following:

repeat(b) =	b until	done => repeat(b)

This function takes behavior b and runs it until it terminates. Then it starts b again. (Note: This a built-in function in ActiveVRML.)

Behaviors and Time

To improve modularity, all behaviors are defined to begin at local time zero. That is, when a behavior begins, no matter what the system time is, the time for the behavior starts at zero. Consider the following:
b until e => b

When b is performed, there are two times to consider, the system time and the local time for b. Let the system time be tg. Whatever the value of tg is, at the time b is performed, it represents time zero for b. Event e occurs some time after tg. Let this time be te. This behavior can then be broken down by time: Do behavior b from time tg to te. Then do behavior b. The local time for b is zero to (te - tg). When b starts, its local time is zero as well.

Here is a simple behavior that uses the local time as its events:

green until time = 2 => (blue until time = 1 => red)

This behavior makes the color green for 2 seconds, blue for 1 second, and then red.

The TimeTransform Construct

The timeTransform construct can be used to change the interpretation of local time. The function has the type timeTransform: a * number -> a. It uses the number to redefine how local time is interpreted within a behavior. Consider the following:
doubleSpeed = time * 2;
b = playVideo(video);

doubleVideo = timeTransform(b, doubleSpeed)

In this example, the video is played at twice its original speed. From the perspective of global time, each 1 second interval corresponds to 2 seconds of local time. The effects of time transformation are cumulative. For example:

timeTransform(doubleVideo, doubleSpeed)

This line would play the video at four times its original speed.

To be consistent and predictable, the number argument (n) for the time transformation must satisfy two rules:

u	Monotone -- For all times t0 and t1 when t0 is less than t1, n at time t0 must be less than n at time t1.
u	Nonnegative -- For all times t, n at time t is nonnegative.

Monotonicity is required to make event reaction sensible (event transitions cannot be undone). Nonnegativity is required to prevent definition of a behavior before local time zero; that is, it may not make sense to sample a behavior like a video before local zero.

Certain behaviors, principally those defined by system or user input devices, may not be transformed in time in the same way that artificial behaviors can be. Such devices ignore user-defined time transformations when they are sampled.


Reactive Types

Following are definitions for the basic types for events, reactivity, and time.

Event Type: event

Type
a event

Constructors done : unit event

Functions
infix | : a event * a event -> a event

predicate: boolean -> unit event

infix => : a event * (a -> b) -> b event

infix => : a event * b -> b event

suchThat : a event * (a -> boolean) -> a event

andEvent : a event * b event -> a*b event

snapshot: a * unit event -> a event

Reactivity Type

Constructors
end : a

infix until : a * a event -> a

repeat : a -> a

Time Type

Functions
timeTransform: a * number -> a


Modeling Types

This section defines a broad range of types and associated functions that are useful for manipulating media types. This includes types for 2D images, 3D geometry, 2D and 3D vectors, points, cameras, and colors.

ActiveVRML uses the following conventions:

u	Time is specified in seconds.
u	Angles are specified in radians.
u	Distances, when necessary, are specified in meters.
u	A right-handed coordinate system is used with positive x to the right, 
        positive y up, and negative z into the screen.

2D Point Type: point2

Type
point2

Constructors
origin2: point2

Functions
point2Xy: number * number -> point2

point2Polar: number * number -> point2

point2Polar(theta, rho)

addVector: point2 * vector2 -> point2

subtractVector: point2 * vector2 -> point2

infix -: point2 * point2 -> vector2

distance: vector2 * vector2 -> number

distanceSquared: vector2 * vector2 -> number

xComponent: point2 -> number

yComponent: point2 -> number

apply: transform2 * point2 -> point2

thetaComponent: point2 -> number

phiComponent: point2 -> number

2D Vector Type: vector2

Type
vector2

Constructors
xVector2: vector2

yVector2: vector2

zeroVector2: vector2

Functions
vector2Xy: number * number -> vector2

vector2Polar: number * number -> vector2

normal: vector2 -> vector2

length: vector2 -> number

lengthSquared: vector2 -> number

infix +: vector2 * vector2 -> vector2

infix -: vector2 * vector2 -> vector2

scaleVector2: vector2 * number -> vector2

dot: vector2 * vector2 -> number

xComponent: vector2 -> number

yComponent: vector2 -> number

apply: transform2 * vector2 -> vector2

thetaComponent: vector2 -> number

rhoComponent: vector2 -> number

2D Transformation Type: transform2

Type
transform2

Constructors identityTransform2: transform2

Functions
translate: number * number -> transform2

translate: vector2 -> transform2

scale: number * number -> transform2

scale: vector2 -> transform2

scale2: number -> transform2

rotate2: number -> transform2

shear2: number -> transform2

transform3x2: number * number * number * number * number * number -> transform2

inverse: transform2 -> transform2

isSingular: transform2 -> boolean

Image Type: image

Type image

Constructors
emptyImage: image

import(pathname.[bmp | jpeg | gif]): image * vector2 * number

Functions
renderedImage: geometry * camera -> image

infix over: image * image -> image

opacity2: number -> (image -> image)

crop: point2 * point2 -> (image -> image)

apply: transform2 * image -> image

Composite 2.5D Image Type: montage

Type montage

Constructors
emptyMontage

Functions
imageMontage: image * number -> montage

infix union: montage * montage -> montage

renderedImage: montage -> image

3D Point Type: point3

Type
point3

Constructors
origin3

Functions
point3Xyz: number * number * number -> point3

point3Spherical: number * number * number -> point3

infix -: point3 * point3 -> vector3

distance: point3 * point3 -> number

addVector: point3 * vector3 -> number

subtractVector: point3 * vector3 -> number

distanceSquared: point3 * point3 -> number

apply: transform3 * point3 -> point3

xComponent: point3 -> number

yComponent: point3 -> number

zComponent: point3 -> number

thetaComponent: point3 -> number

phiComponent: point3 -> number

rhoComponent: point3 -> number

3D Vector Type: vector3

Type
vector3

Constructors
xVector3: vector3

yVector3: vector3

zVector3: vector3

zeroVector3: vector3

Functions
vector3Xyz: number * number * number -> vector3

vector3Spherical: number * number * number -> vector3

vector3Spherical(theta, phi, rho)

normal: vector3 -> vector3

length: vector3 -> number

lengthSquared: vector3 -> number

infix +: vector3 * vector3 -> vector3

infix -: vector3 * vector3 -> vector3

scaleVector3: vector3 * number -> vector3

dot: vector3 * vector3 -> number

cross: vector3 * vector3 -> vector3

apply: transform3 * vector3 -> vector3

xComponent: vector3 -> number

yComponent: vector3 -> number

zComponent: vector3 -> number

thetaComponent: vector3 -> number

phiComponent: vector3 -> number

rhoComponent: vector3 -> number

3D Transformation Type: transform3

Type
transform3

Constructors
identityTransform3

Functions
translateXyz: number * number * number -> transform3

translate: vector3 -> transform3

scale: number * number * number -> transform3

scale: vector3 -> transform3

scale3: number -> transform3

rotate: vector3 * number -> transform3

xyShear: number -> transform3

zyShear: number -> transform3

xzShear: number -> transform3

transform4x4: number * number * number * number * number * number * number * number * number * number * number * number * number * number * number * number -> transform3

lookAtFrom: point3 * point3 * vector3 -> transform3

inverse: transform3 -> transform3

isSingular: transform3 -> boolean

3D Geometry Type: geometry

Type
geometry

Constructors
emptyGeometry: geometry

import(filename.[wrl]): geometry * point3 * point3 In the import constructor, geometry is the result of importing the specified file. The two points returned are the minimum and maximum extents of the tightest axis-aligned, rectangular bounding volume containing the geometry.

Functions
infix union: geometry * geometry -> geometry The union function aggregates two geometries into their geometric union.

soundSource3: sound -> geometry The soundSource3 function allows sounds to be embedded into a geometry. It creates a geometry with the specified sound positioned at the local coordinate system origin. The resultant geometry may be transformed in space, and has no visible presence when rendered. The function renderedSound, described in the Sound section below, takes a geometry and a microphone, and creates a sound by spatializing all of the sounds embedded into that geometry with respect to the microphone.

apply: transform3 * geometry -> geometry

opacity3: number -> (geometry -> geometry) The opacity3(value)(geo) function, given a value from 0.0 to 1.0, creates a new geometry identical to geo, but (value * 100) percent opaque. These compose multiplicatively, making opacity3(0.5)(opacity3(0.2)(myOpaqueGeo) result in a geometry with opacity of 0.1 (or 90% transparent).

texture: image -> (geometry -> geometry) The texture function is the means by which texture mapping onto geometry is specified. The coordinates of the image are mapped onto the texture map coordinates associated with the vertices of the primitive geometries comprising the geometry being mapped, resulting in textured geometry. If the primitive geometries have no texture coordinates, texturing is ignored.

Note
The following functions create light geometries, all of which having no visible appearance themselves; but, they do cast light onto other objects they are aggregated with.

ambientLight: color -> geometry

directionalLight: color -> geometry

pointLight: color -> geometry

spotLight: color * number * number * number -> geometry

Note
The following functions allow for attributing geometry with standard Lambertian shading characteristics. The outermost applied attribute overrides other attributes of the same kind; that is, diffuseColor(red)(diffuseColor(blue)(geo)) results in a red geometry.

diffuseColor: color -> (geometry -> geometry)

ambientColor: color -> (geometry -> geometry)

specularColor: color -> (geometry -> geometry)

emissiveColor: color -> (geometry -> geometry)

specularExponent: number -> (geometry -> geometry)

Camera Type: camera

Type
camera

Constructors
simpleCamera: number -> camera

Functions
apply: transform3 * camera -> camera

Sound Type: sound

Type
sound

Constructors
silence

import(pathname.[wav | au | aiff]): sound * sound

Functions
infix mix: sound * sound -> sound

renderedSound: geometry * microphone -> sound

gain: number -> (sound -> sound)

Microphone Type: microphone

Type microphone

Constructors
defaultMicrophone

Functions
apply: transform3 * microphone -> microphone

Color Type: color

Type
color

Constructors
red: color

green: color

blue: color

cyan: color

magenta: color

yellow: color

white: color

black: color

Functions
colorRgb: number * number * number -> color

colorHsl: number * number * number -> color

redComponent: color -> number

greenComponent: color -> number

blueComponent: color -> number

hueComponent: color -> number

saturationComponent: color -> number

lightnessComponent: color -> number

Character Type: char

Type
char

Constructors
c

Escape Code	Result

\n	Newline
\t	Tab
\	Apostrophe
\"	Quote
\\	Backslash
\integer	The ASCII character with this value

Functions
ord: char -> number

chr: number -> char

String Type: string

Type
string

Constructors
"string-literal"

Functions
infix &: string * string -> string

implode: char list -> string

explode: string -> char list

numberToString: number * number -> string

Font Family Type: fontFamily

Type
fontFamily

Constructors
serifProportional: fontFamily

sansSerifProportional: fontFamily

serifMonospaced: fontFamily

Text Type: text

Type
text

Functions
simpleText: string -> text

textScale: number -> (text -> text)

textColor: color -> (text -> text)

textFamily: fontFamily -> (text -> text)

bold: text -> text

italic: text -> text

renderedImage: text -> image * vector


Integration, Differentiation, and Interpolation

Derivatives, integrals, and linear interpolation apply to the following types:
Type		Derivative

number		number
point2		vector2
vector2		vector2
point3		vector3
vector3		vector3

derivative: T -> DT
integral: DT -> DT
Note that the derivative of a point is a vector, but the integral of a vector is a vector.
blendLinear: T * T * number -> T

Single User Interactivity

Interaction with a user is one way in which a behavior value can change over time. For ActiveVRML 1.0 models, a very simple user input model for a single user is provided. It provides behaviors that represent the users mouse and keyboard, and facilities for specifying reactions to picking (clicking on) geometry and images.

User Input

The keyboard and mouse of the user are modeled through a number of additional, pervasive library functions and values in ActiveVRML:

leftButtonDown: boolean

leftButtonPress: unit event

leftButtonRelease: unit event

rightButtonDown: boolean

rightButtonPress: unit event

rightButtonRelease: unit event

keyDown: character -> boolean

keyPress: character event

keyRelease: character event

mousePosition: point2


Picking Images and Geometry

ActiveVRML 1.0 provides a simple model of picking geometry and images within a model. Picking is based upon a continuously probing input device, the users mouse. The following probe functions take images (for 2D) or geometry (for 3D) and return events that occur when any ray, cast from the assumed eye point of the viewer, "touches" the specified image or geometry.

Occlusion is taken into account. That is, probing rays do not pass through one nontransparent image and into another image, or through one nontransparent geometry into another.

For a 2D probe, consider the following:

probe: image -> (point2 * vector2) event

The specified image is interpreted as being in local coordinates. The returned event data consists of pointPicked and offset,where pointPicked is the static point on the image that was picked, in local coordinates (local to the image), and offset is the 2D vector-valued behavior that tracks the probe as it moves relative to the picked point (also in coordinates local to the specified image).

For a 3D probe, consider the following:

probe: geometry -> (point3 * vector3) event

Similar to the 2D case, the event produces a static point on the geometry where the pick occurred and a vector-valued behavior that tracks the probe as it moves relative to this point. Both return values are local to the specified geometry.


Pattern Matching

This section describes a useful, somewhat more advanced feature of ActiveVRML: the ability to specify declarations using pattern matching. The general syntax for a value declaration is as follows:
pattern = expression

The general syntax for a function declaration is as follows:

identifier pattern = expression

Patterns may be used to destructure values and specify bindings for (potentially) more than one identifier. Patterns are denoted in one of the following forms:

()

identifier

pattern1, pattern2

(pattern)

pattern: type

The following are a few example declarations:

x = 3

(x, y) = (4, 17)

(x, y) = p

(x, y) = 3

f() = 5

g(x) = x + 1

h(x, y) = x + y

k(x, y, z) = x + y / z

An application of k could look like k(1, 2, 3), k(1, (2, 3)), k(1, p) where p is a pair, or k(t) where t is a triple of type int*int*int. The application k((1,2),3) will report an error.

Pattern matching can also be used to specify the formals for a function expression. The following is an anonymous addition function:

function (x, y). x + y

And, this function returns the first element of a pair:

function (x, y). x


ActiveVRML Models and World Wide Web Browsing

This section describes the conventions that must be followed to connect an ActiveVRML model to a World Wide Web (web) browser for single-user interactive animations.

ActiveVRML 1.0 models should contain the following comment as their first source line:

// ActiveVRML 1.0 ASCII

An ActiveVRML model consists of a list of top-level declarations.

An external entry point will be a declaration with type geometry or image*sound*sound. These are the only behaviors that may be indexed from a uniform resource locator (URL). The former will cause the browser to enable a 3D navigational user interface to allow the user to navigate the geometry, and have the embedded sounds rendered (played). The latter form allows the creator of the model to use their own camera, and to directly specify the sounds to play to the left and right speakers.

Embedding and Hyperlinking To ActiveVRML

To specify a URL from an HTML document to an ActiveVRML model, use the following syntax:
<a href="http://www.microsoft.com/model.av#mainEntry">

This will create an active link to the model myModel in the file model.av on the server www.microsoft.com. The entry point for the model, mainEntry, must be type geometry or image*sound*sound. The former is for geometries that will use the default camera from the view (including embedded sound). The latter is for images with stereo sound.

When the user clicks the URL, an ActiveVRML 1.0 capable viewer will begin executing the ActiveVRML (at local time 0).

It is also possible to embed an ActiveVRML model within an HTML page. To do so, use the following syntax:

<embed clsid=ActiveVRML.ActiveVRMLView1 width=300 height=300 props="URL=http://www.microsoft.com/model.av#mainEntry">

This instructs the viewer to display the model in a window within the HTML page. The height and width are specified in pixels to be consistent with other embedded types in HTML.

Hyperlinking from ActiveVRML

The hyperlinking interfaces in ActiveVRML are very basic:
hyperlink3: string -> (geometry -> geometry)
hyperlink2: string -> (image -> image)

These act as attributers for geometries and images. For example:

im2 = hyperlink2("http://www.microsoft.com")(im1)

The variable im2 is now an image that when selected will be noticed by the browser, causing a jump to the specified URL. Note that the URL can be any valid web content type, not just ActiveVRML.


Viewer Information

The following information is available to the model in ActiveVRML 1.0:
viewerResolution: number

The resolution of the view in pixels per meter. In general, this number will be an approximation, as, among other things, monitor size varies.

viewerDimensions: vector2

The dimensions of the view in meters. This will be an approximation of the display dimensions.


ActiveVRML Grammar and Lexical Conventions

Identifiers

An ActiveVRML identifier is an alphanumeric string beginning with a letter. Identifiers are case sensitive. No length limit is imposed.

Type identifiers

A type identifier is an apostrophe () followed by an identifier. In typeset documents, including this one, Greek letters are often used instead of ASCII for type identifiers to improve readability.

Comments

Comments are of the form
/* This is a comment. */
// This is a comment.
where the first form encloses any characters up to the first */ pattern and the latter form ignores everything until the end of line. Nested comments are handled correctly.

White Space

Spaces, tabs, carriage-returns, line-feeds, and comments are considered white space and are ignored other than as token separators.

ActiveVRML Keywords

Following are the keywords of ActiveVRML. These words are reserved and may not be used as identifiers:
and
else
event
function
if
import
in
let
list
mix
not
o
or
over
then
union
until

ActiveVRML Precedence Table

Operator			Associativity

->				Right
* (product type)		Left
list event			Non Associative
, (comma)			Right
.(dot in function) else in 	Non Associative
until				Right
| (or event)			Left
=> (event handler)		Left
o union over mix 		Left
:: (list cons)			Right
or				Left
and				Left
not				Non Associative
= < <= > >= <>			Left
+ - (binary) &			Left
* /				Left
^ 				Right
+ - (unary)			Non Associative
: (type qualification)		Non Associative

ActiveVRML Syntax

program:
declarations 
| epsilon

declarations:
declaration 
| declaration ;
| declaration ; declarations

declaration:
pattern = commaexpression
| identifier pattern = commaexpression
pattern:
 ()
| identifier
| pattern , pattern
| (pattern)
| pattern: typeexp

expressionlist:
nonemptyexpressionlist
| epsilon
nonemptyexpressionlist:
expression , nonemptyexpressionlist
| expression

commaexpression:
expression , commaexpression
| expression
expression:
if expression then expression else expression
| let declarations in expression
| function pattern . expression
| expression binaryoperator expression
| unaryoperator expression
| applyterm

binaryoperator:
 + | - | * | / | ^ | = | > | >= | < | <= |:: | & | and | or | until | | | union | over | mix | o 

unaryoperator:
 + | - | not

applyterm:
applyterm term 
| term
term:
 numberliteral
 | characterliteral
 | stringliteral
| identifier
| ()
| (commaexpression )
| [ expressionlist]
| term: typeexp

typeexp:
typeidentifier
| identifier
| typeexp * typeexp 
| typeexp -> typeexp
| typeexp identifier
| (typeexp)

epsilon:
/* empty */

This document was converted from MS WORD to HTML by Robert M. Free.
Contact
specs@graphcomp.com for corrections and changes.