G3dRenderImpl.mesa
Copyright Ó 1985, 1989 by Xerox Corporation. All rights reserved.
Bloomenthal, September 10, 1989 1:48:21 pm PDT
Heckbert, August 9, 1988 4:50:32 pm PDT
Crow, September 21, 1989 4:19:55 pm PDT
Glassner, July 19, 1989 12:03:35 pm PDT
DIRECTORY Atom, BasicTime, CedarProcess, Commander, Convert, FS, G3dBasic, G3dColorDisplaySupport, G3dMappedAndSolidTexture, G3dMatrix, G3dLight, G3dRender, G3dRenderWithPixels, G3dShade, G3dShape, G3dSortandDisplay, G3dVector, G3dView, ImagerColorFns, ImagerColorMap, IO, NamedColors, Real, Rope, RuntimeError, SafeStorage, ViewerClasses, ViewerIO, ViewerOps;
G3dRenderImpl: CEDAR MONITOR
IMPORTS Atom, BasicTime, CedarProcess, Commander, Convert, FS, G3dColorDisplaySupport, G3dMappedAndSolidTexture, G3dLight, G3dMatrix, G3dRenderWithPixels, G3dShade, G3dShape, G3dSortandDisplay, G3dVector, G3dView, ImagerColorFns, ImagerColorMap, IO, NamedColors, Real, Rope, RuntimeError, SafeStorage, ViewerIO, ViewerOps
EXPORTS G3dRender
~ BEGIN
Errors
Error:     PUBLIC SIGNAL [code: ATOM, reason: ROPE] = CODE;
Types
Miscellaneous Types
PropList:    TYPE ~ Atom.PropList;
STREAM:    TYPE ~ IO.STREAM;
ROPE:     TYPE ~ Rope.ROPE;
Process:    TYPE ~ CedarProcess.Process;
Light:     TYPE ~ G3dLight.Light;
LightRep:    TYPE ~ G3dLight.LightRep;
Imported Sequences
NatSequence:   TYPE ~ G3dBasic.NatSequence;
NatSequenceRep:  TYPE ~ G3dBasic.NatSequenceRep;
PairSequence:  TYPE ~ G3dBasic.PairSequence;
TripleSequence:  TYPE ~ G3dBasic.TripleSequence;
LightSequence:  TYPE ~ G3dLight.LightSequence;
Pixel Definitions
RGB:      TYPE ~ G3dRender.RGB;
Geometric Definitions
Pair:     TYPE ~ G3dBasic.Pair;
Triple:    TYPE ~ G3dBasic.Triple;
Quad:     TYPE ~ G3dBasic.Quad;     -- RECORD [x, y, z, w: REAL];
Matrix:    TYPE ~ G3dMatrix.Matrix;    -- REF 4 by 4 ARRAY OF REAL
Image Definitions
SampleMap:   TYPE ~ G3dRender.SampleMap;
PixelMap:    TYPE ~ G3dRender.PixelMap;
Box:     TYPE ~ G3dRender.Box;    -- [min, max: Pair]
Rectangle:    TYPE ~ G3dRender.Rectangle; -- RECORD [x, y, w, h: REAL]
Polygon Definitions
SurfaceSequence: TYPE ~ G3dBasic.SurfaceSequence;
SurfaceSequenceRep: TYPE ~ G3dBasic.SurfaceSequenceRep;
Texture Definitions
TextureFunction: TYPE ~ G3dRender.TextureFunction;
TextureMap:   TYPE ~ G3dRender.TextureMap;
SummedTexture:  TYPE ~ G3dRender.SummedTexture;
TextureStyle:   TYPE ~ G3dRender.TextureStyle;
TextureInfo:   TYPE ~ G3dRender.TextureInfo;
Control Point Definitions
CtlPoint:    TYPE ~ G3dRender.CtlPoint;
Shape Definitions
Shape:     TYPE ~ G3dShape.Shape;
ShapeRep:    TYPE ~ G3dShape.ShapeRep;
ShapeSequence:  TYPE ~ G3dShape.ShapeSequence;
ShapeSequenceRep: TYPE ~ G3dShape.ShapeSequenceRep;
Vertex:    TYPE ~ G3dShape.Vertex;
VertexRep:   TYPE ~ G3dShape.VertexRep;
VertexSequenceRep: TYPE ~ G3dShape.VertexSequenceRep;
PatchSequence:  TYPE ~ G3dRender.PatchSequence;
ShapeClass:   TYPE ~ G3dRender.ShapeClass;
ShadingClass:  TYPE ~ G3dRender.ShadingClass;    
ShapeProc:   TYPE ~ G3dRender.ShapeProc;
RenderStyle:   TYPE ~ G3dRender.RenderStyle;
Context Definition
Context:    TYPE ~ G3dRender.Context;
ContextRep:   TYPE ~ G3dRender.ContextRep;
ContextClass:   TYPE ~ G3dRender.ContextClass;
RenderData:   TYPE ~ G3dRender.RenderData;
DisplayMode:  TYPE ~ G3dRender.DisplayMode;
Renamed Procedures
Transform: PROC [p: Triple, mat: Matrix] RETURNS [Triple] ~ G3dMatrix.Transform;
TransformVec: PROC [vec: Triple, mat: Matrix] RETURNS [Triple] ~ G3dMatrix.TransformVec;
GetProp: PROC [propList: PropList, prop: REF ANY] RETURNS [REF ANY]
~ Atom.GetPropFromList;
PutProp: PROC [propList: PropList, prop: REF ANY, val: REF ANY] RETURNS [PropList]
~ Atom.PutPropOnList;
Global Variables
registeredDisplayTypes:  PropList ← NIL;   -- keeps active display types
registeredSurfaceTypes:  PropList ← NIL;   -- keeps active surface types
registeredShadingClasses: PropList ← NIL;   -- keeps active shading classes
Class Registration and Installation
RegisterDisplayClass: PUBLIC PROC [class: ContextClass, type: ATOM] ~ {
registeredDisplayTypes ← PutProp[registeredDisplayTypes, type, NEW[ContextClass ← class]]; 
};
GetDisplayClass: PUBLIC PROC [type: ATOM] RETURNS [class: ContextClass] ~ {
refClass: REF ContextClass ← NARROW[GetProp[registeredDisplayTypes, type]];
IF refClass # NIL
THEN class ← refClass^
ELSE Error[$Unimplemented, "Unregistered display type"]
};
LoadDisplayClass: PUBLIC PROC [context: Context, type: ATOM] ~ {
class: REF ContextClass ← NARROW[GetProp[registeredDisplayTypes, type]];
IF class = NIL THEN Error[$Unimplemented, "Unregistered display type"];
context.class ← NEW[ContextClass ← class^];
context.pixels ← NIL;          --this and next line for saving VM
context.displayProps ← Atom.RemPropFromList[context.displayProps, $FullDisplayMemory];
context.class.setUpDisplayType[context];   -- maps in pixels, sets up color map
IF GetProp[context.props, $BufferContext] # NIL THEN {
Error[$Warning, "Dropping Buffer context"];  -- insufficient info to update
context.props ← Atom.RemPropFromList[context.props, $BufferContext];
};
WITH GetProp[context.props, $BackGround] SELECT FROM
backGrdCtx: Context => {
Error[$Warning, "Dropping BackGround context"]; -- no info for update
context.props ← Atom.RemPropFromList[context.props, $BackGround];
};
ENDCASE;
};
RegisterShapeClass: PUBLIC PROC [class: ShapeClass, type: ATOM] ~ {
registeredSurfaceTypes ← PutProp[registeredSurfaceTypes, type, NEW[ShapeClass ← class]]; 
};
GetShapeClass: PUBLIC PROC [type: ATOM] RETURNS [class: ShapeClass] ~ {
refClass: REF ShapeClass ← NARROW[GetProp[registeredSurfaceTypes, type]];
IF refClass # NIL
THEN class ← refClass^
ELSE Error[$Unimplemented, "Unregistered surface type"];
};
LoadShapeClass: PUBLIC PROC [shape: Shape, type: ATOM] ~{
renderData: REF RenderData;
class: REF ShapeClass ← NARROW[GetProp[registeredSurfaceTypes, type]];
IF class = NIL THEN Error[$Unimplemented, "Unregistered surface type"];
renderData ← RenderDataFrom[shape];
IF renderData.class = NIL
THEN renderData.class ← NEW[ShapeClass ← class^]
ELSE {     -- inherit previous procedures if no replacements
new: REF ShapeClass ← NEW[ShapeClass ← class^];
IF new.validate = NIL THEN new.validate ← renderData.class.validate;
IF new.display = NIL THEN new.display ← renderData.class.display;
IF new.displayPatch = NIL THEN new.displayPatch ← renderData.class.displayPatch;
IF new.doBeforeFrame = NIL
THEN new.doBeforeFrame ← renderData.class.doBeforeFrame;
renderData.class ← new;
};
IF ShadingClassFrom[shape] = NIL THEN G3dShade.LoadShadingClass[shape]; -- load default class
shape.renderValid ← FALSE;  -- in case we're changing the type on an existing shape
shape.screenValid ← FALSE;
shape.props ← Atom.RemPropFromList[shape.props, $LinesList];  -- list may not be valid
shape.type ← type;
};
InitStandardShapeClasses: PROC[] ~ {  -- register procedures for basic surface types
standardClass: ShapeClass ← [
type: $Light,
validate: G3dSortandDisplay.DummyValidate,
display: NIL,
displayPatch: NIL
];
RegisterShapeClass[standardClass, $Light]; -- placeholder class for light source
standardClass.type ← $ConvexPolygon;
standardClass.validate ← G3dSortandDisplay.ValidatePolyhedron;
standardClass.displayPatch ← G3dSortandDisplay.OutputPolygon;
RegisterShapeClass[standardClass, $ConvexPolygon]; -- ConvexPolygon procs
Non-convex polygon type, $Poly, registered from G3dStandardPatchProcs.mesa
standardClass.type ← $RopeShape;
standardClass.displayPatch ← G3dSortandDisplay.RopeDisplay;
RegisterShapeClass[standardClass, $RopeShape]; -- RopeShape procs
};
Context
Create: PUBLIC PROC [] RETURNS [Context] ~ {
context: Context ← NEW[ContextRep];
wDir: ROPE ← Commander.PrependWorkingDir[" "];  -- add needed space (wierdness)
wDir ← Rope.Substr[base: wDir, len: Rope.Length[wDir] - 1];   -- drop space
context.props ← PutProp[context.props, $WDir, wDir]; -- keep directory
context.eyeSpaceXfm ← G3dMatrix.Identity[]; -- can't do this in initialization, so do it here
context.stopMe ← NEW[BOOLEANFALSE];
RETURN[context];
};
CreateUndisplayedContext: PUBLIC PROC [
oldContext: Context ← NIL,
width: NAT ← 1024,
height: NAT ← 768,
displayMode: DisplayMode ← fullColor,
keepLog: BOOLEANFALSE ]
RETURNS [context: Context] ~ {
Return a Context with memory allocated for the indicated number of pixels. If oldContext is not NIL then copy existing scene, background, lights, etc. from it, otherwise initialize context to standard defaults and set RGB background to [0.2, 0.2, 0.7].
context ← Create[];
context.preferredRenderMode ← $Pixels;
IF oldContext # NIL THEN CopyContextData[context, oldContext];
context.viewer ← NIL;
context.viewPort ← NEW[      -- set viewport directly to define pixelMap size
Rectangle ← [ x: 0.0, y: 0.0, w: Real.Float[width], h: Real.Float[height] ]
];
context.preferredViewPort ← context.viewPort^;
SELECT displayMode FROM
gray  => LoadDisplayClass[context, $GrayInVM];
dither  => LoadDisplayClass[context, $PseudoColorInVM];
fullColor => LoadDisplayClass[context, $FullColorInVM];
ENDCASE;        -- can't get here without type error
IF keepLog THEN [] ← StartLog[context];
context.changed ← TRUE;       -- ensure screen coordinates get updated
IF oldContext = NIL THEN SetBackgroundColor[context, [0.2, 0.2, 0.7]];
};
InitializeRawColorDisplayContext: PUBLIC PROC [
antiAliasing: BOOLTRUE,
background: RGB ← [0.2, 0.2, 0.7],
displayMode: DisplayMode ← gray]
RETURNS [context: Context]
~ {
context ← Create[];
context.preferredRenderMode ← $Pixels;
LoadDisplayClass[context, AtomFromDisplayMode[displayMode]];
G3dRenderWithPixels.BufferRendering[context, FALSE]; -- , displayType = $FullColor];
SetAntiAliasing[context, antiAliasing];
SetBackgroundColor[context, background];
AddLight[context, "Light0", [-100.0, -200.0, 20.0]];    -- Jules' desired defaults
SetView[context, [0.0, -10.0, 0.0], []];
};
StoreImage: PUBLIC PROC [
context: Context,
fileName: ROPE,
sequenceNo: NATLAST[NAT],
log: BOOLFALSE,
abekasFormat: BOOLFALSE]
~ {
cp: FS.ComponentPositions;
fullFName: ROPE;
IF log OR sequenceNo # LAST[NAT] THEN {
out: STREAMFS.StreamOpen[
fileName: GetLogFileName[PrependWorkingDirectory[context, fileName]],
accessOptions: $append,
wDir: NARROW[GetProp[context.props, $WDir] ]
];
IO.PutRope[out, Rope.Cat[
" Frame no. ", Convert.RopeFromInt[sequenceNo], " written at ",
Convert.RopeFromTime[from: BasicTime.Now[], start: months, end: seconds], "\n"]];
IO.Close[out];
};
[fullFName, cp] ← FS.ExpandName[fileName];
IF abekasFormat
THEN fullFName ← Rope.Replace[fullFName, cp.ext.start, cp.ext.length, "rgb"]
ELSE IF Rope.Equal[Rope.Substr[fullFName, cp.ext.start, cp.ext.length], "rgb", FALSE]
THEN abekasFormat ← TRUE;
fileName ← PrependWorkingDirectory[context, fileName];
IF sequenceNo # LAST[NAT] THEN fileName ← PasteInSequenceNo[fileName, sequenceNo];
IF abekasFormat   
THEN G3dColorDisplaySupport.PutRGB[context, fileName]
ELSE G3dColorDisplaySupport.PutAIS[context, fileName];
};
CloseDisplay: PUBLIC PROC [context: Context] ~ {
context.pixels ← NIL;           -- throw away buffer bits
context.displayProps ← Atom.RemPropFromList[context.displayProps, $FullDisplayMemory];
ImagerColorMap.SetStandardColorMap[context.terminal  -- restore standard color map
! RuntimeError.BoundsFault => CONTINUE];
}; 
CloseColorViewers: PUBLIC PROC ~ {
EnumProc: ViewerOps.EnumProc ~ {
IF v.column = color THEN {
ViewerOps.CloseViewer[v];
ViewerOps.ChangeColumn[v, left];
};
};
ViewerOps.EnumerateViewers[EnumProc];
};
KillUntitledColorViewers: PUBLIC PROC ~ {
EnumProc: ViewerOps.EnumProc ~ {
IF v.column = color AND v.name = NIL THEN ViewerOps.DestroyViewer[v];
};
ViewerOps.EnumerateViewers[EnumProc];
};
GetTmpContext: PUBLIC PROC [srcCtx: Context] RETURNS[dstCtx: Context] ~ {
copies context so temporary modifications can be made (don't change shapes however)
dstCtx ← Create[];
CopyContextData[dstCtx, srcCtx];
dstCtx.pixels ← srcCtx.pixels;
};
CopyContextData: PUBLIC PROC [dstCtx, srcCtx: Context] ~ {
copies REFed data to dstCtx to insulate srcCtx from changes (except for shapes)
dstCtx.class ← IF srcCtx.class # NIL THEN NEW[ ContextClass ← srcCtx.class^ ] ELSE NIL;
dstCtx.stopMe ← srcCtx.stopMe;   -- inheriting REF so Stop signals will propagate
dstCtx.frameNumber ← srcCtx.frameNumber;
dstCtx.shapes ← srcCtx.shapes;
dstCtx.visibleShapes ← srcCtx.visibleShapes;
dstCtx.lightSources ← srcCtx.lightSources;
dstCtx.environment ← srcCtx.environment;
dstCtx.changed ← srcCtx.changed;
dstCtx.eyePoint ← srcCtx.eyePoint;
dstCtx.lookAt ← srcCtx.lookAt;
dstCtx.rollAngle ← srcCtx.rollAngle;
dstCtx.upDirection ← srcCtx.upDirection;
dstCtx.fieldOfView ← srcCtx.fieldOfView;
dstCtx.window ← srcCtx.window;
dstCtx.hitherLimit ← srcCtx.hitherLimit;
dstCtx.yonLimit ← srcCtx.yonLimit;
dstCtx.clippingPlanes ← srcCtx.clippingPlanes;
dstCtx.eyeSpaceXfm ← srcCtx.eyeSpaceXfm;
dstCtx.eyeToNdc ← srcCtx.eyeToNdc;
dstCtx.ndcToPixels ← srcCtx.ndcToPixels;
IF srcCtx.viewer # NIL THEN {
dstCtx.viewer ← NEW[ViewerClasses.ViewerRec ← srcCtx.viewer^];
FOR list: Atom.PropList ← srcCtx.viewer.props, list.rest UNTIL list = NIL DO-- new proplist
element: Atom.DottedPair ← NEW[Atom.DottedPairNode ← list.first^];
dstCtx.viewer.props ← CONS[element, dstCtx.viewer.props];
ENDLOOP;
dstCtx.viewer.props ← PutProp[ dstCtx.viewer.props, $Context3D, dstCtx ];
};
dstCtx.terminal ← srcCtx.terminal;
dstCtx.displayInValid ← srcCtx.displayInValid;
Don't copy pixels, some uses want their own
dstCtx.viewPort ← IF srcCtx.viewPort # NIL THEN NEW[ Rectangle ← srcCtx.viewPort^]
             ELSE NIL;
dstCtx.preferredViewPort ← srcCtx.preferredViewPort;
dstCtx.screenExtent ← srcCtx.screenExtent;
dstCtx.preferredRenderMode ← srcCtx.preferredRenderMode;
dstCtx.displayProps ← NIL;
FOR list: Atom.PropList ← srcCtx.displayProps, list.rest UNTIL list = NIL DO-- new proplist
element: Atom.DottedPair ← NEW[Atom.DottedPairNode ← list.first^];
dstCtx.displayProps ← CONS[element, dstCtx.displayProps];
ENDLOOP;
dstCtx.autoRedraw ← srcCtx.autoRedraw;
dstCtx.delayClear ← srcCtx.delayClear;
dstCtx.doVisibly ← srcCtx.doVisibly;
dstCtx.antiAliasing ← srcCtx.antiAliasing;
dstCtx.depthBuffering ← srcCtx.depthBuffering;
dstCtx.depthResolution ← srcCtx.depthResolution;
dstCtx.sortSequence ← srcCtx.sortSequence;
dstCtx.props ← NIL;
FOR list: Atom.PropList ← srcCtx.props, list.rest UNTIL list = NIL DO-- make new proplist
element: Atom.DottedPair ← NEW[Atom.DottedPairNode ← list.first^];
dstCtx.props ← CONS[element, dstCtx.props];
ENDLOOP;
};
CopyContextShapes: PUBLIC PROC [dstCtx, srcCtx: Context] ~ {
copies shape data to dstCtx to insulate srcCtx shapes from changes
dstCtx.shapes ← NEW[ ShapeSequenceRep[srcCtx.shapes.length] ];
FOR i: NAT IN [0..srcCtx.shapes.length) DO
dstCtx.shapes[i] ← NEW[ ShapeRep ← srcCtx.shapes[i]^ ];
RenderDataFrom[dstCtx.shapes[i]].shadingClass ← NEW[
ShadingClass ← RenderDataFrom[srcCtx.shapes[i]].shadingClass^
];
ENDLOOP;
dstCtx.shapes.length ← srcCtx.shapes.length;
};
View
SetViewFromParameters: PUBLIC PROC [
context: Context,
fieldOfView: REAL ← 40.0,
scale: REAL ← 1.0,
moves, rotates: Triple ← []]
~ {
fov: REALIF fieldOfView = 0.0 THEN 40.0 ELSE fieldOfView;
eyePoint, lookAt, upDirection: Triple;
[eyePoint, lookAt, upDirection] ← G3dView.FromScaleMovesRots[scale, moves, rotates];
SetView[context, eyePoint, lookAt, fov, 0.0, upDirection];
};
SetView: PUBLIC PROC [
context: Context,
eyePoint: Triple,
lookAt: Triple,
fieldOfView: REAL ← 40.0,
rollAngle: REAL ← 0.0,
upDirection: Triple ← [0., 0., 1.],
hitherLimit: REAL ← .01,
yonLimit: REAL ← 1000.0]
~ {
context.eyePoint ← eyePoint;
context.lookAt ← lookAt;
context.fieldOfView ← fieldOfView;
context.rollAngle ← rollAngle;
context.upDirection ← upDirection;
context.hitherLimit ← hitherLimit;
context.yonLimit ← yonLimit;
context.changed ← TRUE;
};
SetViewPort: PUBLIC PROC [context: Context, size: Rectangle] ~{
IF size.w <= 0.0 OR size.h <= 0.0 THEN SIGNAL Error[$MisMatch, "Null rectangle"];
context.preferredViewPort ← size;
context.viewPort ← NIL;    -- computed by ValidateView GetViewportFromViewer
context.window ← NIL;    -- resizing viewport forces update of window
context.displayInValid ← TRUE;
};
SetWindow: PUBLIC PROC[context: Context, size: Rectangle] ~{
context.window ← NEW[ Rectangle ← size ];
context.changed ← TRUE;
};
Lighting
SetAmbientLight: PUBLIC PROC [context: Context, rgb: RGB] ~ {
IF context # NIL THEN {
ref: REF RGBNEW[RGB ← rgb];
context.environment ← PutProp[context.environment, $AmbientLight, ref];
context.changed ← TRUE;
};
};
NameAmbientLight: PUBLIC PROC [context: Context, color: ROPE] ~ {
clr: RGB ← ImagerColorFns.RGBFromHSL[NamedColors.RopeToHSL[color]];
SetAmbientLight[context, clr];
};
GetAmbientLight: PUBLIC PROC [context: Context] RETURNS [rgb: RGB] ~ {
IF context # NIL THEN {
refAny: REF ANY ← GetProp[context.environment, $AmbientLight];
rgb ← IF refAny = NIL
THEN [0.0, 0.0, 0.0]
ELSE NARROW[refAny, REF RGB]^;
};
};
AddLight: PUBLIC PROC [context: Context, name: ROPE, position: Triple, color: RGB [1,1,1],
        type: ATOM ← $Default] ~ {
light: Light;
light ← G3dLight.GetLightType[type];   -- make new light of given type
light.name ← name;
light.position ← position;
light.color ← color;
context.lightSources ← G3dLight.AddToLightSequence[context.lightSources, light];
context.changed ← TRUE;
};
DeleteLight: PUBLIC PROC [context: Context, name: ROPE] ~ {
G3dLight.DeleteLight[context.lightSources, name];
context.changed ← TRUE;
};
DeleteAllLights: PUBLIC PROC [context: Context] ~ {
context.lightSources.length ← 0;
};
LightsFromContext: PUBLIC PROC [context: Context] RETURNS [l: LightSequence] ~ {
Was:
IF context # NIL AND context.shapes # NIL THEN
FOR n: NAT IN [0..context.shapes.length) DO
s: Shape ← context.shapes[n];
IF ShapeClassFrom[s].type = $Light THEN {
rgb: RGB ← ShadingClassFrom[s].color;
i: Light ← NEW[G3dLight.LightRep ←
[s.name, s.position, G3dVector.Normalize[s.position], rgb]];
l ← G3dLight.AddToLightSequence[l, i];
};
ENDLOOP;
RETURN[context.lightSources];
};
LightsToContext: PUBLIC PROC [lights: LightSequence, context: Context] ~ {
Was:
IF context # NIL AND lights # NIL THEN
FOR n: NAT IN [0..lights.length) DO
l: Light ← lights[n];
AddLight[context, l.name, l.position, l.color];
ENDLOOP;
context.lightSources ← lights;
};
Background
NameBackgroundColor: PUBLIC PROC [context: Context, color: ROPE] ~ {
Set background color using color naming scheme
bkgrdColor: RGB ← ImagerColorFns.RGBFromHSL[NamedColors.RopeToHSL[color]];
SetBackgroundColor[context, bkgrdColor]; -- set color
};
SetBackgroundColor: PUBLIC PROC [context: Context, color: RGB] ~ {
Set background color
context.props ← PutProp[context.props, $BackGround, NEW[RGB ← color]];
};
GetBackgroundColor: PUBLIC PROC [context: Context] RETURNS [rgb: RGB] ~ {
Return the background for context.
WITH GetProp[context.props, $BackGround] SELECT FROM
x: REF RGB => rgb ← x^;
ENDCASE;
};
SetBackgroundImage: PUBLIC PROC [context: Context, aisFile: ROPE] ~ {
Use named AIS file as background image for scene instead of solid color
bkGrdCtx: Context ← Create[];
bkGrdCtx.depthBuffering ← context.depthBuffering;
bkGrdCtx.antiAliasing ← context.antiAliasing;
bkGrdCtx.class ← context.class;
bkGrdCtx.props ← PutProp[bkGrdCtx.props, $BackGrdImage, aisFile];
SetBackgroundContext[context, bkGrdCtx];
};
GetBackgroundImage: PUBLIC PROC [context: Context] RETURNS [r: ROPENIL] ~ {
Return the name of the background image (NIL if none).
WITH GetProp[context.props, $BackGround] SELECT FROM
bkGrdCtx: Context => {
name: REF ANY ← GetProp[bkGrdCtx.props, $BackGrdImage];
IF name # NIL THEN r ← NARROW[name];
};
ENDCASE;
};
SetBackgroundContext: PUBLIC PROC [context, bkGrdCtx: Context] ~ {
Use named 3d context as background image for scene (contexts may be stacked arbitrarily).
context.props ← PutProp[context.props, $BackGround, bkGrdCtx];
};
EnableClear: PUBLIC PROC [context: Context, on: BOOL] ~ {
IF on
THEN context.props ← Atom.RemPropFromList[context.props, $DisableClear]
ELSE context.props ← Atom.PutPropOnList[context.props, $DisableClear, $DoIt];
};
DontMatteBackground: PUBLIC PROC [context: Context] ~ {
Remove the background, images will be rendered with no background. Use for images to be matted over others using alpha channel
context.props ← Atom.RemPropFromList[context.props, $BackGround];
};
Shapes
AddShape: PUBLIC PROC [context: Context, shape: Shape] ~ {
IF shape = NIL THEN RETURN;
IF shape.renderData = NIL THEN shape.renderData ← NEW[RenderData]; -- JB
DeleteShape[context, shape.name ! Error => CONTINUE];
context.shapes ← G3dShape.AddToShapeSequence[context.shapes, shape];
};
AddShapeFromFile: PUBLIC PROC [
context: Context,
shapeName: ROPE,
fileName: ROPE,
position: Triple ← [0., 0., 0.]]
~ {
shape, copyShape: Shape ← NIL;
fileName ← PrependWorkingDirectory[context, fileName];
IF context.shapes # NIL THEN FOR i: NAT IN [0..context.shapes.length) DO
IF Rope.Equal[fileName, context.shapes[i].fileName]  -- same data as another shape?
THEN copyShape ← context.shapes[i];
ENDLOOP;
IF copyShape # NIL        -- save data reads if previously read
THEN shape ← G3dShape.CopyShape[copyShape]
ELSE shape ← G3dShape.ShapeFromFile[fileName];
shape.name ← shapeName;
shape.fileName ← fileName;
AddShape[context, shape];
G3dShape.TransformShape[shape: shape, translate: position];
};
FindShape: PUBLIC PROC [context: Context, shapeName: ROPE]
RETURNS [shape: Shape ← NIL]
~ {
IF context # NIL THEN
shape ← G3dShape.FindShape[context.shapes, shapeName ! G3dShape.Error => CONTINUE];
IF shape = NIL THEN SIGNAL Error[$MisMatch, Rope.Cat[shapeName, " - not found"]];
};
ShapeFromRope: PUBLIC PROC [
name: ROPENIL,
message: ROPE,
color: ROPENIL,
size: REAL ← 0.5,
font: ROPENIL]
RETURNS [Shape] ~ {
shape: Shape ← NEW[ShapeRep ← [name: name, matrix: G3dMatrix.Identity[]]];
renderData: REF RenderData ← NEW[RenderData];
renderData.props ← PutProp[renderData.props, $RopeMessage, message];
renderData.props ← PutProp[renderData.props, $RopeFont, font];
shape.renderData ← renderData;
LoadShapeClass[shape, $RopeShape];
G3dShade.LoadShadingClass[shape, $NoShading];
renderData.shadingClass.color ← ImagerColorFns.RGBFromHSL[NamedColors.RopeToHSL[color]];
Build dummy patch structure to pass through ShapeUtilities.ShapePatchToPatch
shape.vertices ← NEW[VertexSequenceRep[2]];  
shape.vertices[0] ← NEW[VertexRep];
shape.vertices[1] ← NEW[VertexRep];    shape.vertices[1].point.z ← size;
shape.vertices.length ← 2;
shape.surfaces ← NEW[SurfaceSequenceRep[1]];
shape.surfaces[0].vertices ← NEW[NatSequenceRep[3]];
shape.surfaces[0].vertices[0] ← shape.surfaces[0].vertices[2] ← 0;
shape.surfaces[0].vertices[1] ← 1;
shape.surfaces[0].vertices.length ← 3;
shape.surfaces.length ← 1;
RETURN[shape];
};
ChangeRopeMessage: PUBLIC PROC [context: Context, shapeName: ROPE, newMessage: ROPE]
~ {
shape: Shape ← FindShape[context, shapeName];
NARROW[shape.renderData, REF RenderData].props ← PutProp[
NARROW[shape.renderData, REF RenderData].props, $RopeMessage, newMessage
];
};
DeleteShape: PUBLIC PROC [context: Context, shapeName: ROPE] ~ {
found: BOOLFALSE;
IF context.shapes = NIL THEN {
SIGNAL Error[$MisMatch, "No shapes to delete from"]; RETURN[];
};
FOR i: CARDINAL IN [0..context.shapes.length) DO
IF NOT found
THEN found ← Rope.Equal[shapeName, context.shapes[i].name, FALSE]
ELSE context.shapes[i-1] ← context.shapes[i];
ENDLOOP;
IF found
THEN {
context.shapes.length ← context.shapes.length - 1;
context.shapes[context.shapes.length] ← NIL;
}
ELSE SIGNAL Error[$MisMatch, Rope.Cat["Can't delete", shapeName, "- not there"]];
};
DeleteAllShapes: PUBLIC PROC [context: Context] ~ {
IF context # NIL THEN {
FOR n: NAT IN [0..context.shapes.length) DO context.shapes[n] ← NIL; ENDLOOP;
context.shapes ← NIL;
};
};
SetRenderStyle: PUBLIC PROC [shape: Shape, renderStyle: RenderStyle] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade = NIL THEN RETURN;
shade.renderMethod ← NEW[RenderStyle ← renderStyle];
shape.renderValid ← FALSE;
};
SetColor: PUBLIC PROC [shape: Shape, color: RGB] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade = NIL THEN RETURN;
shade.color ← color;
shape.renderValid ← FALSE;
};
SetDiffuse: PUBLIC PROC [shape: Shape, diffuseReflectivity: REAL] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade = NIL THEN RETURN;
shade.diffuseReflectivity ← diffuseReflectivity;
shape.renderValid ← FALSE;
};
SetSpecular: PUBLIC PROC [shape: Shape, specularReflectivity: REAL] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade = NIL THEN RETURN;
shade.specularReflectivity ← specularReflectivity;
shape.renderValid ← FALSE;
};
SetMetallicity: PUBLIC PROC [shape: Shape, metallicity: REAL] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade = NIL THEN RETURN;
shade.metallicity ← metallicity;
shape.renderValid ← FALSE;
};
SetShininess: PUBLIC PROC [shape: Shape, shininess: REAL] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade = NIL THEN RETURN;
shade.shininess ← shininess;
shape.renderValid ← FALSE;
};
SetTransmittance: PUBLIC PROC [shape: Shape, transmittance: REAL] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade = NIL THEN RETURN;
shade.transmittance ← transmittance;
shape.renderValid ← FALSE;
};
SetInvisible: PUBLIC PROC [shape: Shape] ~ {IF shape # NIL THEN shape.visible ← FALSE};
SetVisible: PUBLIC PROC [shape: Shape] ~ {IF shape # NIL THEN shape.visible ← TRUE};
ShowBackfaces: PUBLIC PROC [shape: Shape] ~ {
IF shape = NIL THEN RETURN;
shape.showBackfaces ← TRUE;
RenderDataFrom[shape].patch ← NIL;
};
HideBackfaces: PUBLIC PROC [shape: Shape] ~ {
IF shape = NIL THEN RETURN;
shape.showBackfaces ← FALSE;
RenderDataFrom[shape].patch ← NIL;
};
RenderDataFrom: PUBLIC PROC [shape: Shape] RETURNS [data: REF RenderData] ~ {
IF shape = NIL THEN RETURN[NIL];
IF shape.renderData = NIL THEN shape.renderData ← NEW[RenderData];
data ← NARROW[shape.renderData];
};
ShapeClassFrom: PUBLIC PROC [shape: Shape] RETURNS [REF ShapeClass] ~ {
Return the shapeClass for this shape.
data: REF RenderData;
IF shape = NIL THEN RETURN[NIL];
data ← RenderDataFrom[shape];
IF data.class = NIL THEN LoadShapeClass[shape, shape.type];
RETURN[data.class];
};
ShadingClassFrom: PUBLIC PROC [shape: Shape] RETURNS [REF ShadingClass] ~ {
Return the shadingClass for this shape.
data: REF RenderData;
IF shape = NIL THEN RETURN[NIL];
data ← RenderDataFrom[shape];
IF data.shadingClass = NIL THEN G3dShade.LoadShadingClass[shape];
RETURN[data.shadingClass];
};
PatchesFrom: PUBLIC PROC [shape: Shape] RETURNS [PatchSequence] ~ {
Get shape.renderData.patch, the patch-by-patch description, of the surface, conveniently.
data: REF RenderData ← RenderDataFrom[shape];
RETURN[IF data # NIL THEN data.patch ELSE NIL];
};
AddAxes: PUBLIC PROC [
context: Context,
origin: Triple ← [0, 0, 0],
size: REAL ← 1.0,
scale: Triple ← [1, 1, 1],
nReticles: NAT ← 20]
~ {
Don't create shapes, just transform axes endpoints and draw smooth lines to Imager context?
AddAxis: PROC [name: ROPE, p, v0, v1: Triple] ~ {
AddName: PROC [name: ROPE, position: Triple] ~ {
s: Shape ← ShapeFromRope[name, name, "white"];
s.position ← position;
AddShape[context, s];
};
SetRect: PROC [id, n0, n1, n2, n3: NAT] ~ {
poly: G3dShape.Surface ← s.surfaces[id] ← [NIL, NEW[NatSequenceRep[4]]];
poly.vertices.length ← 4;
poly.vertices[0] ← n0;
poly.vertices[1] ← n1;
poly.vertices[2] ← n2;
poly.vertices[3] ← n3;
};
SetVertex: PROC [id: NAT, p: Triple] ~ {s.vertices[id] ← NEW[VertexRep ← [point: p]]};
s: Shape ← NEW[ShapeRep ← [name: Rope.Cat[name, "-axis"], matrix: G3dMatrix.Identity[]]];
SetRenderStyle[s, lines];
s.surfaces ← NEW[SurfaceSequenceRep[4]]; s.surfaces.length ← 4;
s.vertices ← NEW[VertexSequenceRep[8]]; s.vertices.length ← 8;
SetVertex[0, origin];
SetVertex[1, G3dVector.Add[origin, v0]];
SetVertex[2, G3dVector.Add[G3dVector.Add[origin, v0], v1]];
SetVertex[3, G3dVector.Add[origin, v1]];
SetVertex[4, p];
SetVertex[5, G3dVector.Add[p, v0]];
SetVertex[6, G3dVector.Add[G3dVector.Add[p, v0], v1]];
SetVertex[7, G3dVector.Add[p, v1]];
SetRect[0, 0, 1, 5, 4];
SetRect[1, 1, 2, 6, 5];
SetRect[2, 2, 3, 7, 6];
SetRect[3, 3, 0, 4, 7];
FOR n: NAT IN [0..nReticles) DO
p1: Triple ← G3dVector.Interp[REAL[n]/REAL[nReticles], origin, direction];
p2: Triple ← G3dVector.Add[p1, offset];
s.vertices[2*n] ← NEW[VertexRep ← [point: p1]];
s.vertices[2*n+1] ← NEW[VertexRep ← [point: p2]];
s.surfaces[n].vertices ← NewPoly[2*n, 2*n+1];
ENDLOOP;
SetRenderStyle[s, lines];
AddShape[context, s];
AddName[name, direction];
};
AddAxis["X", [origin.x+size, origin.y, origin.z], [0.0, 0.01, 0.0], [0.0, 0.0, 0.01]];
AddAxis["Y", [origin.x, origin.y+size, origin.z], [0.0, 0.0, 0.01], [0.01, 0.0, 0.0]];
AddAxis["Z", [origin.x, origin.y, origin.z+size], [0.01, 0.0, 0.0], [0.0, 0.01, 0.0]];
};
Textures
SetTextureMap: PUBLIC PROC [
context: Context,
shapeName: ROPE,
aisName: ROPE,
textureStyle: TextureStyle ← intensity,
textureFiltering: BOOLFALSE]
RETURNS [error: ROPE]
~ {
a: ATOM ~ AtomFromTextureStyle[textureStyle];
IF shapeName = NIL THEN RETURN;
Kill current texture map (if any) before adding new one, so only one texture at a time.
G3dMappedAndSolidTexture.RemoveAllTexture[context, shapeName];
IF textureStyle = none
THEN G3dMappedAndSolidTexture.RemoveAllTexture[context, shapeName]
ELSE IF aisName # NIL THEN {
ENABLE Error => {error ← reason; CONTINUE};
m: REF TextureMap ← G3dMappedAndSolidTexture.TextureFromAIS[context, aisName, a];
shape: Shape ← FindShape[context, shapeName];
Disable the bizarre uv wraparound code in G3dMappedAndSolidTexture.AdjustTexture:
IF shape # NIL THEN SetTextureRange[shape, [1000., 1000.]];
G3dMappedAndSolidTexture.AddMappedTexture[context, shapeName, m];
IF textureFiltering
THEN G3dMappedAndSolidTexture.SumAllMappedTextures[context, shapeName];
};
};
OffsetTextureCoords: PUBLIC PROC [shape: Shape, offset: Pair] ~ {
FOR n: NAT IN [0..shape.vertices.length) DO
v: Vertex ← shape.vertices[n];
v.texture ← [v.texture.x+offset.x, v.texture.y+offset.y];
ENDLOOP;
};
GetTextureInfo: PUBLIC PROC [s: Shape] RETURNS [textureInfo: LIST OF TextureInfo] ~ {
filtered: BOOLFALSE;
IF s = NIL OR ShadingClassFrom[s] = NIL THEN RETURN;
FOR l: LIST OF REF ANY ← ShadingClassFrom[s].texture, l.rest WHILE l # NIL DO
WITH l.first SELECT FROM
texture: REF TextureMap => {
ref: REF ANY ← GetProp[texture.props, $FileName];
name: ROPEIF ref # NIL THEN NARROW[ref] ELSE NIL;
type: TextureStyle ← SELECT texture.type FROM
$Intensity => intensity,
$Color  => color,
$Bump  => bump,
ENDCASE  => none;
WITH texture.pixels SELECT FROM
x: PixelMap => filtered ← FALSE;
x: REF SummedTexture => filtered ← TRUE;
ENDCASE;
textureInfo ← CONS[[name, type, filtered], textureInfo];
};
txtrFn: REF TextureFunction =>    -- solid or other function-based texture
textureInfo ← CONS[[NIL, function, FALSE], textureInfo];
ENDCASE;
ENDLOOP;
};
ScaleTexture: PUBLIC PROC [context: Context, shape: Shape, scale: Pair] ~ {
G3dMappedAndSolidTexture.ScaleTxtrCoords[context, shape.name, 1.0, scale.x, scale.y];
};
GetTextureScale: PUBLIC PROC [shape: Shape] RETURNS [scale: Pair ← [1.0, 1.0]] ~ {
IF shape # NIL THEN {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade # NIL THEN scale ← shade.textureScale;
};
};
SetTextureScale: PUBLIC PROC [shape: Shape, scale: Pair ← [1.0, 1.0]] ~ {
IF shape # NIL THEN {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade # NIL THEN shade.textureScale ← scale;
};
};
SetTextureRange: PUBLIC PROC [shape: Shape, textureRange: Pair] ~ {
IF shape # NIL THEN NARROW[shape.renderData, REF RenderData].shadingProps ← PutProp[
NARROW[shape.renderData, REF RenderData].shadingProps,
$TxtrCoordRange, NEW[Pair ← textureRange]
];
};
SetTextureFiltering: PUBLIC PROC [context: Context, shape: Shape, on: BOOL] ~ {
IF shape # NIL THEN {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade.texture = NIL THEN RETURN;
IF on
THEN G3dMappedAndSolidTexture.SumAllMappedTextures[context, shape.name]
ELSE FOR l: LIST OF TextureInfo ← GetTextureInfo[shape], l.rest WHILE l # NIL DO
[] ← SetTextureMap[context, shape.name, l.first.name, l.first.type];
ENDLOOP;
};
};
GetBumpScale: PUBLIC PROC [shape: Shape] RETURNS [scale: REAL ← 1.0] ~ {
IF shape # NIL THEN {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade # NIL THEN scale ← shade.bumpScale;
};
};
SetBumpScale: PUBLIC PROC [shape: Shape, scale: REAL ← 1.0] ~ {
IF shape # NIL THEN {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade # NIL THEN shade.bumpScale ← scale;
};
};
Rendering Control
renderDone: CONDITION;
StartLog: PUBLIC PROC [context: Context] RETURNS[IO.STREAM] ~ {
log: IO.STREAMNARROW[GetProp[context.props, $Log]];
IF log = NIL THEN {
[out: log] ← ViewerIO.CreateViewerStreams[
name: "ThreeDWorld.log",
backingFile: PrependWorkingDirectory[context, "ThreeDWorld.log"]
];
context.props ← PutProp[context.props, $Log, log];
};
RETURN[ log ];
};
FlushLog: PUBLIC PROC [context: Context] ~ {
log: IO.STREAMNARROW[GetProp[context.props, $Log]];
IF log # NIL THEN IO.Flush[log];
};
CloseLog: PUBLIC PROC [context: Context] ~ {
log: IO.STREAMNARROW[GetProp[context.props, $Log]];
IF log # NIL THEN IO.Close[log];
context.props ← Atom.RemPropFromList[context.props, $Log]
};
Render: PUBLIC PROC [context: Context, fork: BOOLTRUE] ~ {
IF fork
THEN {
process: Process ← CedarProcess.Fork[ReallyRender, context, [background, TRUE]];
context.props ← PutProp[context.props, $Process, process];
}
ELSE [] ← ReallyRender[context];
};
RendertoAISFile: PUBLIC PROC [context: Context, name: ROPE, fork: BOOLTRUE] ~ {
IF fork
THEN {
process: Process ← CedarProcess.Fork[ReallyRender, context, [background, TRUE]];
context.props ← PutProp[context.props, $Process, process];
}
ELSE [] ← ReallyRender[context];
StoreImage[context, name ];       -- store resulting image
};
WaitTilRenderDone: PUBLIC ENTRY PROC ~ {
ENABLE UNWIND => NULL;
WAIT renderDone;
};
NotRendering: PUBLIC PROC [context: Context] RETURNS [b: BOOL] ~ {
process: Process ← NARROW[GetProp[context.props, $Process]];
RETURN[process = NIL OR CedarProcess.GetStatus[process] # busy];
};
BroadcastRenderDone: ENTRY PROC ~ {
BROADCAST renderDone;
};
ReallyRender: CedarProcess.ForkableProc ~ {
context: Context ← NARROW[data];
IF context.shapes # NIL THEN context.class.render[context ! ABORTED => CONTINUE];
BroadcastRenderDone[];
If providing management alpha and depth buffers for three-d compositing:
SurfaceRender.ShowShapes[context ! ABORTED => CONTINUE];
};
AbortRender: PUBLIC PROC [context: Context] ~ {
IF context # NIL THEN context.stopMe^ ← TRUE;
};
GetDisplayMode: PUBLIC PROC [context: Context] RETURNS [DisplayMode] ~ {
RETURN[SELECT context.class.displayType FROM
$PseudoColor => dither, $Gray => gray, ENDCASE => fullColor];
};
SetAntiAliasing: PUBLIC PROC [context: Context, on: BOOLTRUE] ~ {
This will draw images using the alpha buffer; all texture and transparency are enabled.
G3dRenderWithPixels.AntiAliasing[context, on];
};
AntiAliasingNeeded: PUBLIC PROC [context: Context] RETURNS [BOOL] ~ {
Determine if anti-aliasing is needed to render the shapes in their respective shading modes.
For example, anti-aliasing is necessary for bump-mapping, transparency, solid texture.
IF context.shapes # NIL THEN FOR n: NAT IN [0..context.shapes.length) DO
FOR l: LIST OF TextureInfo GetTextureInfo[context.shapes[n]], l.rest WHILE l #NIL DO
IF l.first.type = bump OR l.first.type = function THEN RETURN[TRUE];
ENDLOOP;
IF ShadingClassFrom[context.shapes[n]].transmittance > 0.0 THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
GetBuffer: PUBLIC PROC [context: Context, type: ATOM] RETURNS [sm: SampleMap ← NIL]
~ {
r: REF ← GetProp[context.displayProps, type];
IF r # NIL AND context.pixels # NIL THEN sm ← context.pixels[NARROW[r, REF NAT]^]
};
GetAlphaBuffer: PUBLIC PROC [context: Context] RETURNS [SampleMap] ~ {
RETURN[GetBuffer[context, $Alpha]];
};
GetDepthBuffer: PUBLIC PROC [context: Context] RETURNS [SampleMap] ~ {
RETURN[GetBuffer[context, $Depth]];
};
Local Utilities and Miscellany
Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE {RETURN[number*number]; };
GetRenderData: PROCEDURE [context: Context, shapeName: ROPE] RETURNS [REF RenderData]
~ {
data: REF RenderData ← NIL;
IF context # NIL THEN {
shape: Shape ← FindShape[context, shapeName];
IF shape # NIL THEN IF shape.renderData # NIL THEN data ← NARROW[
shape.renderData
! SafeStorage.NarrowRefFault => CONTINUE
];
};
RETURN[data];
};
PrependWorkingDirectory: PUBLIC PROC [context: Context, file: ROPE] RETURNS [ROPE]~{
wDir: ROPENARROW[GetProp[context.props, $WDir]];
IF wDir = NIL
THEN RETURN[file]
ELSE IF file = NIL OR (Rope.Index[s1: file, s2: "/"] > 0 AND Rope.Index[s1: file, s2: "["] > 0)  
THEN file ← Rope.Cat[wDir, file];  -- if first char not / or [ then prepend wDir
RETURN[file];
};
TackOnExtension: PUBLIC PROC[file, extension: ROPE] RETURNS[ROPE] ~ {
cp: FS.ComponentPositions; fullFName, fName: ROPE;
[fullFName, cp, ] ← FS.ExpandName[file];
IF cp.ext.length = 0
THEN {
fName ← Rope.Substr[ fullFName, 0, cp.ext.start];
RETURN[ Rope.Cat[fName, ".", extension] ];
}
ELSE RETURN[ file ];
};
GetLogFileName: PUBLIC PROC[fileRoot: ROPE] RETURNS[ROPE] ~ {
gets name of log file for updating animation status
cp: FS.ComponentPositions;
fullFName, fName: ROPE;
[fullFName, cp] ← FS.ExpandName[fileRoot];
fName ← Rope.Substr[fullFName, 0, IF cp.ext.length = 0 THEN cp.ext.start ELSE cp.ext.start-1];
RETURN[Rope.Cat[fName, ".", "log"]];
};
PasteInSequenceNo: PUBLIC PROC[fileRoot: ROPE, number: NAT] RETURNS[ROPE] ~ {
adds sequence number before file name extension for animations
num: ROPE ← Convert.RopeFromInt[number, 10, FALSE];
SELECT Rope.Length[num] FROM
1 => num ← Rope.Cat["00", num];
2 => num ← Rope.Cat["0", num];
3 => {};
ENDCASE => SIGNAL Error[$MisMatch, "Sequence # out of range"];
RETURN[ PasteInLabel[fileRoot, num ] ];
};
PasteInLabel: PUBLIC PROC[fileRoot: ROPE, label: ROPE] RETURNS[ROPE] ~ {
puts arbitrary label before file name extension, for adding "-red, -grn, -blu" etc.
cp: FS.ComponentPositions; fullFName, fName, ext: ROPE;
[fullFName, cp, ] ← FS.ExpandName[fileRoot];
IF cp.ext.length = 0
THEN {
fName ← Rope.Substr[ fullFName, 0, cp.ext.start];  -- name for filling in numbers
ext ← "ais";
}
ELSE {
fName ← Rope.Substr[ fullFName, 0, cp.ext.start-1];  -- drop "." before ext
ext ← Rope.Substr[ fullFName, cp.ext.start, cp.ext.length];
};
RETURN[ Rope.Cat[fName, label, ".", ext] ];
};
IntersectRectangles: PUBLIC PROC [rect1, rect2: Rectangle] RETURNS [Rectangle] ~ {
intersection: Rectangle;
intersection.x ← MAX[rect1.x, rect2.x];
intersection.y ← MAX[rect1.y, rect2.y];
intersection.w ← MIN[rect1.w - (intersection.x - rect1.x), rect2.w - (intersection.x - rect2.x)];
intersection.h ← MIN[rect1.h - (intersection.y - rect1.y), rect2.h - (intersection.y - rect2.y)];
RETURN [intersection];
};
AtomFromTextureStyle: PUBLIC PROC [textureStyle: TextureStyle] RETURNS [a: ATOM] ~ {
a ← SELECT textureStyle FROM bump => $Bump, color => $Color, ENDCASE => $Intensity;
};
AtomFromDisplayMode: PUBLIC PROC [displayMode: DisplayMode] RETURNS [a: ATOM] ~ {
a SELECT displayMode FROM fullColor=>$FullColor, dither=>$PseudoColor, ENDCASE=>$Gray;
};
RopeFromDisplayMode: PUBLIC PROC [displayMode: DisplayMode] RETURNS [ROPE] ~ {
RETURN[SELECT displayMode FROM
gray => "Gray",
dither => "Dither",
fullColor => "FullColor",
ENDCASE => NIL];
};
RopeFromRenderStyle: PUBLIC PROC [renderStyle: RenderStyle] RETURNS [ROPE] ~ {
RETURN[SELECT renderStyle FROM
faceted => "Faceted",
smooth => "Smooth",
lines => "Lines",
shadedLines => "ShadedLines",
hiddenLines => "HiddenLines",
ENDCASE => NIL];
};
RopeFromTextureStyle: PUBLIC PROC [textureStyle: TextureStyle] RETURNS [ROPE] ~ {
RETURN[SELECT textureStyle FROM
intensity => "Intensity",
color => "Color",
bump => "Bump",
ENDCASE => "None"];
};
InitStandardShapeClasses[];
END.