Preliminary Version

December 14, 1995

Microsoft Corporation

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:

- (i) the object code may be reproduced and used without restriction or
charge; and

(ii) the source code may be reproduced and used for educational, non-commercial and internal use without charge, and for commercial use at a commercially reasonable charge.

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.

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.

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).

if expression1 then expression2 else expression3is 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 declaration1; . . . declarationn[;] in expressionis 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.

identifier = expressionOr, they declare a function:

identifier(identifier, ..., identifier) = expressionThe 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) + seedThis 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 swizitNotice 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.

successor(nat) = nat + 1ActiveVRML 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) = valWhen 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.

In deference to the typographically-challenged ASCII character set, the type assigned to nada is actually written ‘a->‘a. In typeset ActiveVRML documents, including this one, Greek letters are often used instead of the ASCII syntax for type identifiers in order to improve readability.

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

- The unit type is a trivial type containing only one member. This type is
often used for functions that take or return uninteresting data, similar to
the way that the void type is used in C++ programs.

()

- The unique member of the unit type, pronounced "trivial".

type -> type

- The function type a -> b represents mappings from type a to type b.
Functions in ActiveVRML may be higher-order, meaning that a function can take
another function as an argument or return a function as its result. For
example, function f might have type (number->number)->number. This means
that f can be applied to a function with type number->number to produce a
result of type number. Another function g might have type
number->(number->number). This means that g will take a number as an
argument, and produce a function with type number->number as its result.

**Constructors**

function pattern . expression

- This constructor is used to create anonymous function values. The pattern
part of this constructor (described in the "Pattern Matching" section) may be
thought of as a list for formal arguments. For example, the declaration

f (x, y) = x * y + 1can be thought of as an abbreviation for:

f = function (x, y). x * y + 1Function declarations are value declarations where the value is a function value.

**Functions**

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

- The expression f o g is the composition of the functions f and g. The
notation infix o means that o is an infix operator (like the familiar + as in
14 + 3). The value of (f o g)(x) is f(g(x)). Note that o has a type like a
higher-order function. It takes two functions as arguments and returns a
function as its result. Its type can be written as ((a -> b) * (b -> g))
-> (a -> g) since * has higher precedence than -> in ActiveVRML
types. (See ActiveVRML Type Precedence later in this document.)

type * type

- The product type a * b represents pairs of elements: for example, (e1, e2)
where e1 has type a and e2 has type b.

**Constructors**

expression, expression

- The pairing constructor is a comma. The precedence of the comma is
extremely low in ActiveVRML, so it is usually desirable (and visually clearer)
to write pairing with parentheses: (3, "hello"). The pairing operator
associates to the right. Thus, (3,

**Functions**

first: a * b -> a

- The first function computes the first element of a pair.

- The second function computes the second element of a pair.

a list

- The type a list is a list (or finite sequence). Each element is of type a.
For example, number list is a list of numbers, and (string list) list is a
list where each element is a list of strings.

**Constructors**

[expression-list]

- A list of expressions (zero or more) separated by commas. For example, []
is the null list (of type a list) and [1, 2, 3] is a number list.

**Functions**

head: a list -> a

- The head(list) function returns the first element of the list list. It is
illegal to apply head to an empty list.

- The tail(list) function computes a list comprising all but the first
element of the original list. It is illegal to apply tail to an empty list.

- The operator :: is read as "cons". The expression elt:: list computes a
new list formed by prepending ("cons’ing") elt to list.

- The empty(list) function is true if and only if list is an empty list.

- The length(list) function computes the length of list.

- The map(fun, list) function computes a list by applying fun to each
element of list.

- The reduce([e1,...,en], base, fun) function computes:

fun(e1, fun(e2, fun(...,fun(en-1, fun(en, base))...)))nth: a list * number -> a

- The nth(list, n) function computes the nth element of list, where the
first element of the list is 1.

boolean

- The boolean type represents true and false values in ActiveVRML.

**Constructors**

true

false

**Functions**

infix and: boolean * boolean -> boolean

infix or: boolean * boolean -> boolean

infix not: boolean -> boolean

infix =: a * a -> boolean

- Equality may be used to compare any two values with the same type.
Equality in ActiveVRML is structural: pairs are equal if each side of the pair
is equal; lists are equal if their lengths are the same and corresponding
elements of each list are equal. Equality applied to functions and modeling
types is not defined (since it is not always possible to determine equality on
these types).

infix <>: a * a -> boolean

- The expression x <> y is true if x is not equal to y.

number

- The type of numbers in ActiveVRML. ActiveVRML does not distinguish between
"fixed point" and "floating point" numbers; both are considered numbers. The
implementation will choose an appropriate representation.

**Constructors**

number-literal

- The number-literal constructor is any sequence of characters satisfying
the following regular expression:

digit+ (‘.’ digit*)? ([’e’ ‘E’] [’+’ ‘-‘]?digit+)?

time

- A time-varying number representing the local time of a behavior. This
important constructor is the basis for many interesting time-varying
behaviors.

random

- A pseudo-random number in [0, 1] that is time-varying. All instances of
random that start at the same global time have the same time-varying value.

pi

- A constant number representing pi.

**Functions**

infix +: number * number -> number

infix *: number * number -> number

infix -: number * number -> number

infix /: number * number -> number

- Division by zero is an error.

prefix -: number -> number

- The notation "prefix -" means that "-" is a unary operator: for example,
-x where x is type number.

prefix +: number -> number

- The prefix operator + does not change the value of a number.

infix <: number * number -> boolean

infix <=: number * number -> boolean

infix >: number * number -> boolean

infix >=: number * number -> boolean

abs: number -> number

- Absolute value.

sqrt: number -> number

- Square root.

mod: number * number -> number

- Arithmetic modulus (remainder upon division).

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

- The atan(h, w) function returns the arctangent of h/w in radians.

atan: number -> number

cos: number -> number

- Cosine of radians.

sin: number -> number

- Sine of radians.

tan: number -> number

- Tangent of radians.

infix ^: number * number -> number

- The x^y function computes xy.

exp: number -> number

- The exp(x) function computes ex.

ln: number -> number

- The ln(x) function computes the natural logarithm of x.

log10: number -> number

- The log10(x) function computes the base 10 logarithm of x.

seededRandom: number -> number

- Pseudorandom behavior is parameterized by a random seed. SeededRandom
returns x in [0, 1], implicitly parameterized by time.

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 are constructed from one of the following types of events.

u leftButtonPress: unit event u rightButtonPress: unit event u keyPress: character event

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.

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.)

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.

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.

a event

**Constructors** done : unit event

- The done constructor detects the termination of a behavior.

**Functions**

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

- The expression e1 | e2 is an alternative event. The first of the two
events is chosen, and the data associated with that event becomes the data for
the alternative event.

predicate: boolean -> unit event

- The predicate(b) function turns a boolean value into an event with trivial
(unit) data. The event occurs the first time after local time 0 that the
predicate b is true.

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

- The expression e => h is a handler event. It occurs the same time that
e does, and returns as the function h applied to the data produced by e.

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

- This second form of e => b is a syntactic convenience, valid only when
b is not a function. It is roughly equivalent to e => function x. b and is
useful when the handler does not need the value of the data produced by the
event. This is a special form and does not immediately evaluate the second
argument.

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

- The suchThat(e, p) function is a filter event that occurs when e does,
producing the data that e would, but only if the predicate p is true on that
data.

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

- The andEvent(e1, e2) function occurs when e1 and e2 occur simultaneously.
The data returned is the pair of data from e1 and e2.

snapshot: a * unit event -> a event

- The snapshot(b, e) function creates a new event that happens at the same
time as the e event, and associates a static snapshot of the b behavior with
the event. When the e occurs, b is sampled. A new event with the static
sampled value of b associated with the e event is created. Snapshot is a
special form that does not immediately evaluate the second argument.

end : a

- The end constructor causes the behavior to finish immediately.

infix until : a * a event -> a

- The expression b until e is equivalent to the behavior is b until the
event e occurs, in which case, the behavior becomes b’.

repeat : a -> a

- The repeat(b) function is equal to b until b ends. Then it restarts with b
at that time.

timeTransform: a * number -> a

- The timeTransform(b, timeTrans) function adjusts the local time line for
behavior b to follow the (time-varying) number timeTrans. For example,
timeTrans(b, time * 2) makes a behavior that runs twice as fast as b normally
would. See the "Behaviors and Time" section above for more information.

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.

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

vector2

**Constructors**

xVector2: vector2

yVector2: vector2

zeroVector2: vector2

**Functions**

vector2Xy: number * number -> vector2

vector2Polar: number * number -> vector2

- vector2Polar(theta, rho)

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

transform2

- 2D transformations represent a mapping between 2D-space and 2D-space and
are used to transform various 2D entities, including point2, vector2, and
image, all by the overloaded apply function listed in their relevant sections.

**Constructors** identityTransform2: transform2

**Functions**

translate: number * number -> transform2

translate: vector2 -> transform2

scale: number * number -> transform2

scale: vector2 -> transform2

scale2: number -> transform2

- uniform scale

rotate2: number -> transform2

- rotate n radians ccw

shear2: number -> transform2

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

inverse: transform2 -> transform2

isSingular: transform2 -> boolean

- A value of type image is a spatially continuous image behavior of infinite
spatial extent. Operations on it include the application of 2D transforms and
opacity, and overlaying images. Continuous images are constructed by importing
bitmap files, projecting 3D geometry, or by rendering text into an image.

**Constructors**

emptyImage: image

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

- The parameter image is the imported image, vector2 is the size of the
imported image in meters, and number is the resolution of the image in pixels
per meter.

**Functions**

renderedImage: geometry * camera -> image

- The renderedImage(geometry, viewingCamera) function is the primary 3D to
2D interface. The viewingCamera parameter determines the projection by which
the geometry will be imaged. The resultant image is spatially infinite, and
the camera performs no cropping. Cropping, if desired, can be achieved by
using the crop function below.

infix over: image * image -> image

- The renderedImage(top, bottom) function produces a new image with top
overlaid above bottom.

opacity2: number -> (image -> image)

- The opacity2(value)(img) function, given a value from 0.0 to 1.0, creates
a new image identical to img, but (value * 100) percent opaque. These compose
multiplicatively; thus opacity2(0.5)(opacity2(0.2)(myOpaqueImg) results in an
image with opacity of 0.1 (or 90% transparent).

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

- The crop(min, max)(img) function creates an image identical to img inside
of the box defined by min and max, and a transparent layer outside of this
box.

apply: transform2 * image -> image

- A montage is a set of images with associated depth values. Montages are
useful for creating multilayered, image-based (cel) animation.

**Constructors**

emptyMontage

**Functions**

imageMontage: image * number -> montage

- The imageMontage(image, depth) function builds a 2.5D image set with a
single image at depth.

infix union: montage * montage -> montage

- The union function combines the contents of two image sets into a single
collection.

renderedImage: montage -> image

- The renderedMontage function converts the set of images and depths
encapsulated in a montage into a flattened image. Image elements with larger
depths will be layered underneath.

point3

**Constructors**

origin3

**Functions**

point3Xyz: number * number * number -> point3

point3Spherical: number * number * number -> point3

- point3Spherical(theta, phi, rho)

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

vector3

- Direction and magnitude.

**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

transform3

- 3D transformations represent a mapping between 3D-space and 3D-space and
are used to transform various 3D entities, including point3, vector3,
geometry, microphone, and camera, all by using the overloaded apply function
listed in their relevant sections.

**Constructors**

identityTransform3

**Functions**

translateXyz: number * number * number -> transform3

translate: vector3 -> transform3

scale: number * number * number -> transform3

scale: vector3 -> transform3

scale3: number -> transform3

- Uniform scale.

rotate: vector3 * number -> transform3

- Construct a rotation number radians about the axis specified by the
provided vector.

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

- The lookAtFrom(from, to, up) function creates a transformation when
applied to an object centered at the origin, with +Y up and directed toward
-Z, moves the object to from, pointing towards to, with its up direction as
close to up as possible.

inverse: transform3 -> transform3

isSingular: transform3 -> boolean

geometry

- A value of type geometry is a spatially continuous behavior of infinite
spatial extent in three dimensions. A geometry value is constructed by
importing other geometry formats (such as VRML), applying modeling
transformations, material properties, aggregating multiple geometries,
positioning sound in 3D, and specifying other attributes.

**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

- The spotLight function has arguments color, fullcone, cutoff, and
exponent.

**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

**Constructors**

simpleCamera: number -> camera

- The simpleCamera(viewDistance) constructor’s eye-point is at the origin,
looking along the +Z axis with +Y up. The viewing plane is Z = viewDistance.
(Hither and yon clipping planes are not exposed, but may be determined by the
implementation as follows: hither is viewDistance, and yon is determined from
the implementation-determined bounding volume of the geometry being rendered.)

**Functions**

apply: transform3 * camera -> camera

sound

- A sound value is constructed by importing primitive sounds, mixing,
rendering of geometry with embedded sounds, and by the application of audio
attributes, such as gain.

Note that sound is always considered to be single channel. Stereo is supported by constructing two separate sounds.

Certain audio effects can be achieved by using the general time transformation mechanism. For example, both phase shift and rate control can be achieved by time transformations.

**Constructors**

silence

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

- Importing a .wav, .au, or .aiff file constructs a pair of sounds, one for
the left and right channels of the sound. If the imported file is monophonic,
the two returned sounds are identical. When a sound is finished, it
terminates. Sounds can be looped by using the repeat facility.

**Functions**

infix mix: sound * sound -> sound

- The mix function combines two sounds into a single sound, with each
component sound contributing equally.

renderedSound: geometry * microphone -> sound

- The renderedSound function takes a geometry and a microphone, and produces
an audio rendering of the sounds embedded within the geometry (by using
soundSource3), with respect to the microphone.

gain: number -> (sound -> sound)

- The gain function multiplicatively adjusts the gain of a sound. Thus,
gain(0.3)(gain(5.0)(origSound)) results in a sound 1.5 times as loud as the
original sound.

- The microphone type represents an audio perceiver in 3D. ActiveVRML 1.0
supports a simple microphone that may be spatially transformed by using
modeling transformations. Future versions will add other attributes to the
microphone.

**Constructors**

defaultMicrophone

**Functions**

apply: transform3 * microphone -> microphone

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

char

**Constructors**

’c’

- In the ‘c’ constructor, c is an ASCII character or one of the following
escape forms:

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

**Functions**

ord: char -> number

- The Ord(c) function is the ASCII code for character c.

chr: number -> char

- The Chr(n) function is the ASCII character corresponding to n.

string

**Constructors**

"string-literal"

- In the "string-literal" constructor, string-literal is a sequence of
characters or escape characters.

**Functions**

infix &: string * string -> string

implode: char list -> string

explode: string -> char list

numberToString: number * number -> string

- The numberToString(num, precision) function formats num to a string with
precision digits after the decimal point. If precision is zero, then the
decimal point is elided. It is illegal for precision to be negative.

fontFamily

**Constructors**

serifProportional: fontFamily

sansSerifProportional: fontFamily

serifMonospaced: fontFamily

text

- ActiveVRML supports the construction of simply formatted text, which can
then be rendered into an image. ActiveVRML 1.0 supports simple scaling,
coloring, emboldening, italicizing, and choosing from a fixed set of
typefaces.

**Functions**

simpleText: string -> text

- The text created by simpleText has a default color (black), family (serif
proportional), and is neither bold nor italic. It has a nominal scale of 1
point.

textScale: number -> (text -> text)

- The textScale function composes with other textScale functions
multiplicatively.

textColor: color -> (text -> text)

textFamily: fontFamily -> (text -> text)

- The outermost application of these functions wins.

bold: text -> text

italic: text -> text

renderedImage: text -> image * vector

- The renderedImage function takes text and returns an image, as well as a
size (in meters) of the nontransparent region of the resultant image (starting
at the origin). The resultant image may subsequently be scaled by applying
image transformations.

The resultant image is transparent in all places other than where the text is actually rendered.

There are explicitly no alignment operators (align left, right, center, etc.), as these can be achieved by transformation of the resultant image.

Type Derivative number number point2 vector2 vector2 vector2 point3 vector3 vector3 vector3 derivative: T -> DT integral: DT -> DTNote that the derivative of a point is a vector, but the integral of a vector is a vector.

blendLinear: T * T * number -> T

leftButtonDown: boolean

- Tracks the state of the left button of the mouse.

leftButtonPress: unit event

- Occurs each time the user presses the left mouse button.

leftButtonRelease: unit event

- Occurs each time the user releases the left mouse button.

rightButtonDown: boolean

rightButtonPress: unit event

rightButtonRelease: unit event

- Analogous to the corresponding left button behaviors.

keyDown: character -> boolean

- For keyDown(c), the value is true if key c is presently depressed.

keyPress: character event

- Occurs when a key is pressed. It produces the pressed character in the
event data.

keyRelease: character event

- Occurs when a key is released. It produces the released character in the
event data.

mousePosition: point2

- Tracks the current position of the mouse in world image coordinates.

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 = 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:

()

- Matches the trivial value, (), of the type unit.

identifier

- Matches any value and effectively binds the value to the identifier in the
right hand side of the declaration. All of the identifiers in a pattern must
be distinct.

pattern1, pattern2

- Matches a pair value if pattern1 and pattern2 match the left and right
components of the pair. The comma pattern associates right.

(pattern)

- Groups pattern syntax for readability and precedence. The form matches if
pattern matches the value.

pattern: type

- Matches the value if pattern does, and constrains the pattern to have a
particular type.

The following are a few example declarations:

x = 3

- This declares an identifier x as a variable with a value of 3.

(x, y) = (4, 17)

- This pattern matches since the pattern (x, y) and value (4, 17) are both
pairs. Effectively, x is bound to 4, and y is bound to 17.

(x, y) = p

- This declaration matches x to the first component of p (which must be a
pair) and y to the second component of p.

(x, y) = 3

- This declaration will cause an error to be reported since 3 is not a pair.

f() = 5

- This declares a constant function. The application expression f() will
always produce a value of 5.

g(x) = x + 1

- This declares a function g that takes an argument x.

h(x, y) = x + y

- This declares a function h that takes a single argument that must be a
pair. The function may be applied either as h(3, 4) or as h(p) where p is a
pair of numbers.

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

- This declares a function k that takes a single argument that is a pair,
the second component of the pair being also a pair. Because comma patterns
associate right, the argument takes the following form:

(int, (int, int))

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 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.

<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.

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.

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.

/* 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.

and else event function if import in let list mix not o or over then union until

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

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.