DIRECTORY Atom, BasicTime, CedarProcess,  Commander,  FS, G3dAnimationSupport, G3dBasic, G3dColorDisplaySupport, G3dRender,  G3dRenderWithImager, G3dRenderWithPixels, G3dShape, G3dSortandDisplay,  Imager, ImagerBackdoor, ImagerPixel, ImageTwiddle, List, Process, Random, Real, Rope, Terminal, ThreeDViewer, ViewerTools;
ThreeDDemoImpl: CEDAR MONITOR
IMPORTS  Atom, BasicTime, CedarProcess, Commander, FS, G3dAnimationSupport, G3dColorDisplaySupport, G3dRender, G3dRenderWithImager, G3dRenderWithPixels, G3dShape, G3dSortandDisplay, Imager, ImagerBackdoor, ImagerPixel, ImageTwiddle, List, Process, Random, Real, Rope, Terminal, ThreeDViewer, ViewerTools  

~ BEGIN

PropList:			TYPE ~ Atom.PropList;
ROPE:				TYPE ~ Rope.ROPE;
Context:			TYPE ~ G3dRender.Context;
RealSequence:	TYPE ~ G3dBasic.RealSequenceRep;
IntegerPair:		TYPE ~ G3dRender.IntegerPair;
Pair:				TYPE ~ G3dRender.Pair;
Pixel:				TYPE ~ G3dRender.Pixel;
Triple:			TYPE ~ G3dRender.Triple;
Rectangle:		TYPE ~ G3dRender.Rectangle;
RGB:				TYPE ~ G3dRender.RGB;
Patch:				TYPE ~ G3dRender.Patch;
ShadingClass:	TYPE ~ G3dRender.ShadingClass;
Shape:			TYPE ~ G3dRender.Shape;
ShapeRep:		TYPE ~ G3dShape.ShapeRep;
ShapeSequence:	TYPE ~ G3dRender.ShapeSequence;
ShapeSequenceRep:	TYPE ~ G3dShape.ShapeSequenceRep;
RenderData:		TYPE ~ G3dRender.RenderData;
ImagerProc:		TYPE ~ G3dRender.ImagerProc;
ImagerProcRec:	TYPE ~ G3dRender.ImagerProcRec;
LORA:				TYPE ~ LIST OF REF ANY;
context3d, offScreenCtx: Context;  	-- , onScreenCtx
currentScene, scene1, scene2, scene3: ShapeSequence _ NIL;
lastDemoCalled: PROC[makeFrame: BOOLEAN _ FALSE];
frames: REF G3dAnimationSupport.FrameSequence;
xNow, yNow, zNow: REAL _ 3.0;
xPosition, yPosition: REAL _ 0.0;
cursorRange: REAL _ 64.0;
quadTime: REAL;
animation: BOOLEAN _ FALSE;
movieProcess: CedarProcess.Process _ NIL;
cachedViewPort: Rectangle _ [0.0, 0.0, 0.0, 0.0];
windowStack, viewPortStack:  LIST OF Rectangle _ NIL;
mouseWaiting: ATOM _ NIL;
mouseRectangle: REF Rectangle _ NIL;
FindShape: PROC [context: Context, shapeName: ROPE] RETURNS [Shape] 
~ G3dRender.FindShape;

GetAIS: PROC[ context: Context, fileRoot: ROPE, xOffset, yOffset: INTEGER _ 0, 
				 center: BOOLEAN _ TRUE, labeled: BOOLEAN _ FALSE ] 
		 RETURNS[ xSize, ySize: INTEGER] 
~ G3dColorDisplaySupport.GetAIS;

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;
demoMenu: LIST OF ThreeDViewer.ButtonDesc ~ LIST [
[  proc: SetScene, 											-- Select Scene
choices: LIST[
[$Scene1, "Polyhedra on Checkerboard"], 						-- left mouse button
[$Scene2, "Glass, Banana and Egg"], 							-- middle mouse button
[$Scene3, "Bezier Teapot"],										-- right mouse button
[$OneShape, "Show One Shape (name from selection)"],		-- shift left
[NIL, NIL],															-- shift middle
[NIL, NIL],															-- shift right
[$Window, "2 Clicks in image define part of scene to display"], 		-- control left
[$ViewPort, "2 Clicks in image define new screen area"], 			-- control middle
[NIL, NIL],																	-- control right
[$RestoreW, "Restore previous window"], 						-- control shift left
[$RestoreVP, "Restore previous viewport"] 						-- control shift middle
], 
label: "Select Shapes",
purpose: "Chooses scene to manipulate"
],
[  proc: SetShading, 										-- Shading Style
choices: LIST[
[$Lines, "Line Drawing"], 							-- left mouse button
[$Facets, "Faceted Shading"], 							-- middle mouse button
[$Smooth, "Smooth Shading"], 						-- right mouse button
[$Hidden, "Hidden Line Drawing"],					-- shift left
[$Shiny, "Faceted Shading with Highlights"], 		-- shift middle
[$Hilights, "Smooth Shading with Highlights"], 	-- shift right
[$SmoLines, "Smooth Shading on Lines"], 			-- control left
[$Normals, "Line drawing with normal vectors"] 	-- control middle
], 
label: "Shading Style",
purpose: "Chooses shading for surfaces in current scene"
],
[  proc: SetRenderingStyle, 								-- Rendering Style
choices: LIST[
[$Jaggy, "Aliased"], 										-- left mouse button
[$NoJaggy, "AntiAliased"],								-- middle mouse button
[NIL, NIL],													-- right mouse button
[$Visible, "Build Image On Display"], 					-- shift left
[$Buffered, "Double Buffer Image"], 						-- shift middle
[NIL, NIL],													-- shift right
[$Imager, "Use Imager (faceted only)"], 					-- control left
[$Pixels, "Use fancier tiler/shaders"], 					-- control middle
[NIL, NIL],													-- control right
[$ZBuffer, "Use depth buffer for hidden surfaces"], 	-- control shift left
[$Sorted, "Use sorting for hidden surfaces"], 			-- control shift middle
[NIL, NIL],													-- control shift right
[$PseudoClr, "8-bit mapped color"],
[$Gray, "8-bit grayscale"],
[$FullClr, "24-bit full color"]  
], 
label: "Rendering Style",
purpose: "Chooses display type, antialising, and double buffering"
],
[  proc: DoCommands, 										-- Commands
choices: LIST[
[$Display, "Make a frame with the current scene"], 			-- left mouse button
[$StoreImage, "Store image in file (name from selection)"], 	-- middle mouse button
[$Interpress, "Interpress file (name from selection)"] 			-- right mouse button
], 
label: "Do It",
purpose: "Make/Store images"
],
[  proc: ShowMotion, 										-- Animation
choices: LIST[
[$Orbit, "Orbit about scene computing frames on the fly"], 	-- left mouse button
[$SelectDthr, "Take 8-bit dithered sequence name from selection"],  -- middle button
[$SelectGray, "Take 8-bit grayscale sequence name from selection"],  -- right button
[$Shapes, "Play back Banana, Glass, Egg scene"], 				-- shift left
[$Terrain, "Play back Terrain flyover"], 						-- shift middle
[NIL, NIL],															-- shift right
[$Web1, "Play back front view Paper web"],					-- control left
[$Web2, "Play back top view Paper web"], 						-- control middle
], 
label: "Animation",
purpose: "Chooses various animations"
],
[  proc: ShowPicture, 										-- Gallery
choices: LIST[	
[$SelectColor, "Take rgb file name from selection"], 			 -- left mouse button
[$SelectDthr, "Take 8-bit dithered file name from selection"],  -- middle mouse button
[$SelectGray, "Take 8-bit grayscale file name from selection"], -- right mouse button
[$Spoon, "Newell's Bezier patch spoon"], 			-- shift left
[$Bananas, "A field of bananas"],						-- shift middle
[$Compare, "Shading Comparison on Eggs"], 		-- shift right
[$Bowl, "Sugar bowl with spoon"], 					-- control left
[$TeaPot, "Teapot with Perlin texture"],		 		-- control middle
[$Eggtxtr, "Multi-textured Egg"],  					-- control right
[$Heart, "Heart from two Bezier patches"], 			-- control shift left
[$Objects, "Five objects on a checkerboard"], 		-- control shift middle
[$Extents, "Five objects with bounding boxes"], 	-- control shift right
[$HexTiles, "Objects on hex-tiled plane"],
[$MikHead, "Mik leers over the curvaceous glass"],
[$MikTrees, "Mik leers over scanned-in trees"],
[$BallPage, "A sphere with a page of text"],
[$BallPages, "A sphere with many pages of text"],
[$TeaPages, "Teapot with pages of text"],  
[$SolidLines, "Drawing with fat lines"]  
], 
label: "Gallery",
purpose: "Chooses a pretty picture to display"
],
[  proc: StopEverything, 									-- STOP! button
choices: LIST[], 					-- No Choices, just calls proc
label: "STOP!",
purpose: "Panic button, stops all 3D activity"
],
[  proc: DoReset, 											-- Reset button
choices: LIST[], 					-- No Choices, just calls proc
label: "Reset",
purpose: "Reset button, can often get things unwedged"
]
];

SetScene: ENTRY PROC[context: Context, key: ATOM] ~ {
context.stopMe^ _ FALSE;						-- unstick stop button
{  ENABLE UNWIND => NULL;	-- get out and release lock if error, etc.
SELECT key FROM
$Scene1 => SetScene1[];
$Scene2 => SetScene2[];
$Scene3 => SetScene3[];
$OneShape => OneShape[];
$Window => GetWindow[];
$ViewPort => GetViewPort[];
$RestoreW => RestoreWindow[];
$RestoreVP => RestoreViewPort[];
ENDCASE;
};
};  

SetShading: ENTRY PROC[context: Context, key: ATOM] ~ {
context.stopMe^ _ FALSE;						-- unstick stop button
{  ENABLE UNWIND => NULL;
SELECT key FROM
$Lines	=> LinesDemo[];
$Facets	=> {   SetDull[];    FacetedDemo[];   };
$Smooth	=> {   SetDull[];    SmoothDemo[];   };
$Hidden	=> HiddenLinesDemo[];
$Shiny	=> {   SetShiny[];    FacetedDemo[];   };
$Hilights	=> {   SetShiny[];    SmoothDemo[];   };
$SmoLines	=> IF context.class.displayType # $PseudoColor
OR ThreeDViewer.SwitchDisplayTo[context, $FullColor] 
THEN {							-- only for $Gray and $FullColor
context.preferredRenderMode _ $Pixels;
ShadedLinesDemo[];
};
$Normals	=> NormaledLinesDemo[];
ENDCASE;
};
};

SetRenderingStyle: ENTRY PROC[context: Context, key: ATOM] ~ {
context.stopMe^ _ FALSE;						-- unstick stop button
SELECT key FROM
$Jaggy		=> G3dRenderWithPixels.AntiAliasing[context, FALSE];
$Visible		=> G3dRenderWithPixels.BufferRendering[context, FALSE];
$Imager		=> context.preferredRenderMode _ $Imager;
$NoJaggy		=> IF context.class.displayType # $PseudoColor 
OR ThreeDViewer.SwitchDisplayTo[context, $FullColor] 
THEN {
context.preferredRenderMode _ $Pixels;
G3dRenderWithPixels.AntiAliasing[context, TRUE];
};
$Buffered	=> G3dRenderWithPixels.BufferRendering[context, TRUE];
$Pixels		=> context.preferredRenderMode _ $Pixels;
$ZBuffer	=> G3dRenderWithPixels.DepthBuffering[context, TRUE];
$Sorted		=> G3dRenderWithPixels.DepthBuffering[context, FALSE];
$PseudoClr	=> [] _ ThreeDViewer.SwitchDisplayTo[context, $PseudoColor];
$Gray			=> [] _ ThreeDViewer.SwitchDisplayTo[context, $Gray];
$FullClr		=> [] _ ThreeDViewer.SwitchDisplayTo[context, $FullColor];
ENDCASE;
{  ENABLE UNWIND => NULL;
ThreeDViewer.DrawInViewer[context, NIL];
};
};

DoCommands: ENTRY PROC[context: Context, key: ATOM] ~ {
context.stopMe^ _ FALSE;						-- unstick stop button
{  ENABLE UNWIND => NULL;
SELECT key FROM
$Display => {
IF DemoErrorMessages[] THEN RETURN;
SELECT lastDemoCalled FROM
LinesDemo			=> MakeFrame[];
ShadedLinesDemo	=> MakeFrame[];
HiddenLinesDemo	=> HiddenLinesDemo[TRUE];
NormaledLinesDemo	=> NormaledLinesDemo[TRUE];
FacetedDemo	=> MakeFrame[];
SmoothDemo	=> MakeFrame[];
ENDCASE		=> MakeFrame[];
};
$StoreImage => G3dColorDisplaySupport.PutAIS[ 
											context3d, ViewerTools.GetSelectionContents[] ];
$Interpress	=> InterpressDemo[];
ENDCASE;
};
};  

ShowMotion: ENTRY PROC[context: Context, key: ATOM] ~ {
SetSingleBuffer: PROC[] ~ {
G3dRenderWithPixels.AntiAliasing[context, FALSE];
G3dRenderWithPixels.DepthBuffering[context, FALSE];
};
context.stopMe^ _ FALSE;						-- unstick stop button
{  ENABLE UNWIND => NULL;
IF key = $Orbit OR context.class.displayType # $FullColor 
OR ThreeDViewer.SwitchDisplayTo[context, $PseudoColor]	-- ensure 8-bit display
THEN SELECT key FROM
$Orbit => Orbit[];
$SelectDthr => {  
[] _ ThreeDViewer.SwitchDisplayTo[context, $PseudoColor];    SetSingleBuffer[];
SetUpMovie[ fileRoot: ViewerTools.GetSelectionContents[] ];
};
$SelectGray => {  
[] _ ThreeDViewer.SwitchDisplayTo[context, $Gray];    SetSingleBuffer[];
SetUpMovie[ fileRoot: ViewerTools.GetSelectionContents[] ];
};
$Shapes => { 
[] _ ThreeDViewer.SwitchDisplayTo[context, $PseudoColor];    SetSingleBuffer[];
SetUpMovie[
fileRoot: "/Pixel/Crow/Animation/StillCloser.ais", numFiles: 40, start: 1
];
};
$Terrain => {
[] _ ThreeDViewer.SwitchDisplayTo[context, $PseudoColor];    SetSingleBuffer[];
SetUpMovie[ fileRoot: "/Cyan/AIS/Animation/LakeSceneTest.ais", numFiles: 64 ];
ImageTwiddle.SetUpTerrainColors[context.terminal];
};
$Web1 => {  
[] _ ThreeDViewer.SwitchDisplayTo[context, $Gray];    SetSingleBuffer[];
SetUpMovie[ fileRoot: "/Pixel/Crow/Animation/Paper4Web.ais", numFiles: 40 ];
};
$Web2 => {  
[] _ ThreeDViewer.SwitchDisplayTo[context, $Gray];    SetSingleBuffer[];
SetUpMovie[ fileRoot: "/Pixel/Crow/Animation/Paper5Web.ais", numFiles: 40 ];
};
ENDCASE;
IF key # $Orbit THEN movieProcess _ CedarProcess.Fork[PlayBackbyFrame];
};
};

ShowPicture: ENTRY PROC[context: Context, key: ATOM] ~ {
context.stopMe^ _ FALSE;						-- unstick stop button
{  ENABLE UNWIND => NULL;
SELECT key FROM
$SelectDthr  => IF context.class.displayType # $PseudoColor THEN 
IF ThreeDViewer.SwitchDisplayTo[context, $PseudoColor] THEN -- switch to dithered
{ [] _ CedarProcess.Fork[ WaitThenShowPicture, LIST[context, key] ];  RETURN; };
$SelectGray  => IF context.class.displayType # $Gray THEN
IF ThreeDViewer.SwitchDisplayTo[context, $Gray] THEN -- switch to 8-bit gray
{ [] _ CedarProcess.Fork[ WaitThenShowPicture, LIST[context, key] ];  RETURN; };
ENDCASE  => IF context.class.displayType # $FullColor THEN
IF ThreeDViewer.SwitchDisplayTo[context, $FullColor] THEN	-- switch to full color
{ [] _ CedarProcess.Fork[ WaitThenShowPicture, LIST[context, key] ];  RETURN; };
SELECT key FROM
$SelectDthr, $SelectGray, $SelectColor  => 
				   [] _ GetAIS[context3d, ViewerTools.GetSelectionContents[] ];
$Spoon		=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/Spoon.ais" ]; 
$Bananas 	=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/FieldOfBanana.ais" ];  
$Compare		=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/ComparisonEggs.ais" ];	 
$Bowl 		=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/BowlAndSpoon.ais" ]; 
$TeaPot 		=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/ZebraBurlTeaPot.ais" ]; 
$Eggtxtr		=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/MultiTxtrdEgg2.ais" ];
$Heart		=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/Heart.ais" ]; 
$Objects		=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/TestScene2.ais" ]; 
$Extents		=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/FiveObjWBoxes.ais" ]; 
$HexTiles		=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/HexTiles.ais" ]; 
$MikHead	=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/Lamming6Scene.ais" ]; 
$MikTrees	=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/Lamming7Scene.ais" ]; 
$BallPage 	=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/SphereWithPage.ais" ]; 
$BallPages 	=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/SphereWithPages.ais" ]; 
$TeaPages	=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/PagedTeaPot.ais" ]; 
$SolidLines	=>	[] _ GetAIS[context3d, "/cyan/AIS/Crow/StillLines.ais" ]; 
ENDCASE
};
};

WaitThenShowPicture: CedarProcess.ForkableProc ~ {
list: LORA _ NARROW[data];
context: Context _ NARROW[list.first];
key: ATOM _ NARROW[list.rest.first];
WHILE GetProp[ context.displayProps, $ViewerAdjusted ] # NIL DO
Process.PauseMsec[ 500 ];  ENDLOOP;				-- wait for viewer paint proc to complete
ShowPicture[context, key];
};

StopEverything: PROC[context: Context, key: ATOM] ~ {				-- panic stop
bkgrdContext: Context _ NIL;

WITH GetProp[context.props, $BackGround] SELECT FROM
bkGrdCtx: Context => bkgrdContext _ bkGrdCtx;
ENDCASE;

context.stopMe^ _ TRUE;
WHILE bkgrdContext # NIL DO  			-- stop chain of background contexts
tmpCtx: Context _ bkgrdContext;
tmpCtx.stopMe^ _ TRUE; 
bkgrdContext _ NARROW[GetProp[tmpCtx.props, $BackGround]];
ENDLOOP;  
IF movieProcess # NIL THEN {
Process.Yield[];
CedarProcess.Abort[movieProcess];
movieProcess _ NIL;
Process.Yield[];
G3dRender.SetViewPort[ context, cachedViewPort ];
};
};

DoReset: PROC [context: Context, key: ATOM] ~ { -- get out of stuckness, start over
StopEverything[context, NIL];
Process.PauseMsec[ 3000 ];							-- wait 3 seconds for processes to stop
context3d.stopMe^ _ FALSE;							-- get stop flag unstuck, if necessary
context3d.window _ NIL;	
context3d.shapes _ context3d.visibleShapes _ NIL;	
context3d.lightSources _ NIL;
G3dRender.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
G3dRender.AddLight[context3d, "Light0", [-100., -200., 50.] ]; 
G3dRender.SetViewPort[context3d, [0.0, 0.0, 65536.0, 65536.0] ]; 
windowStack _ viewPortStack _ NIL;
mouseWaiting _ NIL;
mouseRectangle _ NIL;
currentScene _ NIL;
frames _ NIL;
context3d.props _ Atom.RemPropFromList[ context3d.props, $SinglePatch ];
context3d.props _ Atom.RemPropFromList[ context3d.props, $SingleVtx ];
};

ThreeDDemoCommand: Commander.CommandProc ~ {
context3d _ G3dRender.Create[];
context3d.preferredRenderMode _ $Pixels;		-- $Pixels, Imager;
ThreeDViewer.MakeViewer[
context: context3d, 
displayType: $PseudoColor, 			-- $Gray, ImagerDithered
bannerName: "ThreeDWorld Demonstration", 
menu: demoMenu,
mouseAction: MouseControl,
verticalMenu: FALSE
];
G3dRender.NameBackgroundColor[ context3d, "Darkish Blue" ];	-- set background color
G3dRender.NameAmbientLight[ context3d, "Darkish Gray" ];	-- set ambient light
G3dRender.AddLight[context3d, "Light0", [-100., -200., 50.] ]; 
G3dRender.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
};

InitNoDisplay: PROC[] ~ {
context3d _ G3dRender.Create[];
G3dRender.NameBackgroundColor[ context3d, "Darkish Blue" ];	-- set background color
G3dRender.NameAmbientLight[ context3d, "Darkish Gray" ];	-- set ambient light
G3dRender.AddLight[context3d, "Light0", [-100., -200., 50.] ]; 
G3dRender.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
};

InitNoViewer: PROC[] ~ {
context3d _ G3dRender.Create[];
G3dRender.LoadDisplayClass[context3d, $PseudoColor];
G3dRender.NameBackgroundColor[ context3d, "Darkish Blue" ];	-- set background color
G3dRender.NameAmbientLight[ context3d, "Darkish Gray" ];	-- set ambient light
G3dRender.AddLight[context3d, "Light0", [-100., -200., 50.] ]; 
G3dRender.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
};

InitNoViewerFullColor: PROC[] ~ {
context3d _ G3dRender.Create[];
G3dRender.LoadDisplayClass[context3d, $FullColor];
G3dRender.NameBackgroundColor[ context3d, "Darkish Blue" ];	-- set background color
G3dRender.NameAmbientLight[ context3d, "Darkish Gray" ];	-- set ambient light
G3dRender.AddLight[context3d, "Light0", [-100., -200., 50.] ]; 
G3dRender.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
};

Reset: PROCEDURE [] ~ {
DoReset[context3d, NIL];
};

MouseControl: ThreeDViewer.MouseProc ~ {
width, height: INTEGER;
divisor: REAL;
IF mouseWaiting # NIL  
THEN {  IF bttn = $LeftButton THEN GetMouseRectangle[x, y];    RETURN;  };
IF context.viewer # NIL 
THEN  {  width _ context.viewer.ww;    height _ context.viewer.wh;  } 
ELSE   {  width _ context.pixels.box.max.f - context.pixels.box.min.f;
			height _ context.pixels.box.max.s - context.pixels.box.min.s;    };
divisor _ ((width/2.0) * (width/2.0)) / cursorRange;
x _ x - width / 2.0;				-- center mouse coords
y _ y - height / 2.0;
SELECT bttn FROM
$LeftButton, $LeftHeld	=>	IF movieProcess # NIL 
THEN frameRate _ INTEGER[Real.Fix[x / 10.]]
ELSE  {
xNow _ (x * ABS[x] / divisor) + context3d.lookAt.x;
yNow _ (y * ABS[y] / divisor) + context3d.lookAt.y;
G3dRender.SetView[ context, [xNow, yNow, zNow], context.lookAt, context.fieldOfView, context.rollAngle, context.upDirection, context.hitherLimit, context.yonLimit ];
DoCommands[context, $Display];
};
$MiddleButton, $MiddleHeld	=>	{
xLight: REAL _ x * 1.0;			-- roughly plus or minus 300-500
yLight: REAL _ y * 1.0;
[] _ G3dRender.AddLight[ context, "Light0", [xLight, yLight, 50.0] ];
DoCommands[context, $Display];
};
$RightButton, $RightHeld	=> {
zNow _ (y * ABS[y] / divisor) + context3d.lookAt.z;
G3dRender.SetView[ context, [xNow, yNow, zNow], context.lookAt, context.fieldOfView, context.rollAngle, context.upDirection, context.hitherLimit, context.yonLimit ];
DoCommands[context, $Display];
};
ENDCASE;
};
MoveToCursor: PROC[] ~ {
width, height, offset: INTEGER;
sPos: IntegerPair;
IF context3d.viewer # NIL 
THEN {  
width _ context3d.viewer.ww;   height _ context3d.viewer.wh;   offset _ 28; 
} 
ELSE  {  
width _ context3d.pixels.box.max.f - context3d.pixels.box.min.f;	
height _ context3d.pixels.box.max.s - context3d.pixels.box.min.s;
offset _ context3d.pixels.box.min.s  
};
[[sPos.x, sPos.y]] _ Terminal.GetColorCursorPosition[context3d.terminal];
xPosition _ 1.0 * sPos.x  / width;
yPosition _ 1.0 * (height - sPos.y + offset)  / height;
};

DrawToCursor: PROC[ color: RGB _ [1.0,1.0,1.0] ] ~ {
width, height, offset: INTEGER;
sPos: IntegerPair;
newPos: Pair;
clrPxl: Pixel _ [Real.Fix[color.R*255], Real.Fix[color.G*255], Real.Fix[color.B*255], 0, 0];
IF context3d.viewer # NIL 
THEN {  
width _ context3d.viewer.ww;   height _ context3d.viewer.wh;   offset _ 28; 
} 
ELSE  {  
width _ context3d.pixels.box.max.f - context3d.pixels.box.min.f;	
height _ context3d.pixels.box.max.s - context3d.pixels.box.min.s;
offset _ context3d.pixels.box.min.s  
};
[[sPos.x, sPos.y]] _ Terminal.GetColorCursorPosition[context3d.terminal];
newPos _ [1.0 * sPos.x  / width, 1.0 * (height - sPos.y + offset)  / height];
context3d.class.draw2DLine[ context3d, [xPosition, yPosition], newPos, clrPxl ];
xPosition _ newPos.x;
yPosition _ newPos.y;
};

TextAtCursor: PROC[ rope: ROPE, color: RGB _ [1.0,1.0,1.0], size: REAL _ 20.0, 
						  font: ROPE _ NIL ] ~ {
sPos: IntegerPair;
pos: Pair;
width, height, offset: INTEGER;
clrPxl: Pixel _ [Real.Fix[color.R*255], Real.Fix[color.G*255], Real.Fix[color.B*255], 0, 0];
IF context3d.viewer # NIL 
THEN {  
width _ context3d.viewer.ww;   height _ context3d.viewer.wh;   offset _ 28; 
} 
ELSE  {  
width _ context3d.pixels.box.max.f - context3d.pixels.box.min.f;	
height _ context3d.pixels.box.max.s - context3d.pixels.box.min.s;
offset _ context3d.pixels.box.min.s  
};
[[sPos.x, sPos.y]] _ Terminal.GetColorCursorPosition[context3d.terminal];
pos _ [1.0 * sPos.x  / width, 1.0 * (height - sPos.y + offset)  / height];
IF font = NIL THEN font _ "Xerox/Pressfonts/TimesRoman-MRR";
context3d.class.draw2DRope[context3d, rope, pos, clrPxl, size, font];
};

GetOffScreenCtx: PROC[context: Context, width, height: NAT] ~ {
offScreenCtx _ G3dRender.GetTmpContext[context];
offScreenCtx.viewer _ NIL;
offScreenCtx.viewPort _ NEW[ 				-- set viewport directly to define pixelMap size
Rectangle  _ [ x: 0.0, y: 0.0, w: Real.Float[width], h: Real.Float[height] ]
];
G3dRenderWithPixels.AllocatePixelMemory[offScreenCtx];					-- get display memory
G3dRender.SetViewPort[ offScreenCtx, [0.0, 0.0, Real.Float[width], Real.Float[height]] ];
IF context.antiAliasing THEN G3dRenderWithPixels.AntiAliasing[offScreenCtx];-- get state straight
IF context.depthBuffering THEN G3dRenderWithPixels.DepthBuffering[offScreenCtx];

[] _ G3dRender.StartLog[offScreenCtx];
offScreenCtx.changed _ TRUE;
G3dSortandDisplay.ValidateContext[offScreenCtx];
G3dSortandDisplay.ValidateContext[context];			-- just in case it hasn't been used yet
};

GetOnScreenCtx: PROC[context: Context, width, height: NAT] ~ {
onScreenCtx: Context _ G3dRender.GetTmpContext[context];
G3dRender.SetViewPort[ onScreenCtx, [0.0, 0.0, Real.Float[width], Real.Float[height]] ];
onScreenCtx.class.render _ G3dRenderWithPixels.MakeFrame;		-- load direct memory procs
onScreenCtx.class.displayPolygon _ G3dRenderWithPixels.PolygonTiler;
IF context.antiAliasing THEN G3dRenderWithPixels.AntiAliasing[onScreenCtx];
IF context.depthBuffering THEN G3dRenderWithPixels.DepthBuffering[onScreenCtx];
onScreenCtx.changed _ TRUE;
G3dSortandDisplay.ValidateContext[onScreenCtx];
G3dSortandDisplay.ValidateContext[context];			-- just in case it hasn't been used yet
};

DemoErrorMessages:  PROCEDURE [] RETURNS[ error: BOOLEAN _ FALSE ] ~ {
NoSurfaces:  PROCEDURE [shapes: ShapeSequence] RETURNS[BOOLEAN]~{
FOR i: NAT IN [0..shapes.length) DO  
IF shapes[i].vertices # NIL AND shapes[i].surfaces # NIL THEN RETURN[FALSE];  
ENDLOOP;
RETURN[TRUE];
};
IF context3d.shapes = NIL OR NoSurfaces[context3d.shapes] THEN SetScene1[];
IF context3d.shapes = NIL OR NoSurfaces[context3d.shapes] THEN {
context3d.class.draw2DRope[context3d, "No objects, nothing to display", [60, 340] ];
error _ TRUE;
};
IF context3d.antiAliasing 
AND NOT List.Memb[ context3d.class.displayType, LIST[$Gray, $FullColor] ]
THEN {
context3d.class.draw2DRope[context3d, 
							 "NoJaggy needs GrayScale or 24-bit Color", [60, 300] ];
error _ TRUE;
};
};

LinesDemo:  PROCEDURE [makeFrame: BOOLEAN _ FALSE] ~ {  
IF DemoErrorMessages[] THEN RETURN;
FOR i: NAT IN [0..context3d.shapes.length) DO 
G3dRender.SetRenderStyle[ FindShape[context3d, context3d.shapes[i].name], lines ];
ENDLOOP;
context3d.changed _ TRUE;
IF makeFrame THEN context3d.class.render[context3d];  
lastDemoCalled _ LinesDemo;
};

ShadedLinesDemo:  PROCEDURE [makeFrame: BOOLEAN _ FALSE] ~ {  
IF DemoErrorMessages[] THEN RETURN;
FOR i: NAT IN [0..context3d.shapes.length) DO 
G3dRender.SetRenderStyle[ FindShape[context3d, context3d.shapes[i].name], shadedLines ];
ENDLOOP;
context3d.changed _ TRUE;
IF makeFrame THEN context3d.class.render[context3d];  
lastDemoCalled _ ShadedLinesDemo;
};

HiddenLinesDemo:  PROCEDURE [makeFrame: BOOLEAN _ FALSE] ~ {  
tmpContext: Context _ G3dRender.GetTmpContext[context3d];	-- get modifiable context
IF DemoErrorMessages[] THEN RETURN;
G3dRender.NameBackgroundColor[ tmpContext, "White" ];
G3dRender.CopyContextShapes[tmpContext, context3d];	-- insulate shapes from changes
FOR i: NAT IN [0..tmpContext.shapes.length) DO 
G3dRender.SetRenderStyle[
FindShape[tmpContext, tmpContext.shapes[i].name],  hiddenLines
];
ENDLOOP;
context3d.changed _ TRUE;
IF makeFrame THEN tmpContext.class.render[tmpContext];  
lastDemoCalled _ HiddenLinesDemo;
};

NormaledLinesDemo:  PROCEDURE [makeFrame: BOOLEAN _ FALSE] ~ {  
tmpContext: Context _ G3dRender.GetTmpContext[context3d];	-- get modifiable context
IF DemoErrorMessages[] THEN RETURN;
G3dRender.NameBackgroundColor[ tmpContext, "White" ];
G3dRender.CopyContextShapes[tmpContext, context3d];	-- insulate shapes from changes
FOR i: NAT IN [0..tmpContext.shapes.length) DO 
G3dRender.SetColor[ FindShape[tmpContext, tmpContext.shapes[i].name], [1.0, 1.0, 1.0] ];
G3dRender.SetRenderStyle[
FindShape[tmpContext, tmpContext.shapes[i].name],  hiddenLines
];
ENDLOOP;
context3d.changed _ TRUE;
IF makeFrame THEN tmpContext.class.render[tmpContext];  
lastDemoCalled _ NormaledLinesDemo;
};

FacetedDemo:  PROCEDURE [makeFrame: BOOLEAN _ FALSE] ~ {  
IF DemoErrorMessages[] THEN RETURN;
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetRenderStyle[ FindShape[context3d, context3d.shapes[i].name], faceted ];
ENDLOOP;
context3d.changed _ TRUE;
IF makeFrame THEN context3d.class.render[context3d];  
lastDemoCalled _ FacetedDemo;
};

SmoothDemo:  PROCEDURE [makeFrame: BOOLEAN _ FALSE] ~ {    
IF DemoErrorMessages[] THEN RETURN;
FOR i: NAT IN [0..context3d.shapes.length) DO 
G3dRender.SetRenderStyle[ FindShape[context3d, context3d.shapes[i].name], smooth ];
ENDLOOP;
context3d.changed _ TRUE;
lastDemoCalled _ SmoothDemo;
IF makeFrame THEN context3d.class.render[context3d];  
};

SetShiny:  PROCEDURE [] ~ {      
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetShininess[ FindShape[context3d, context3d.shapes[i].name], 50.0 ];
ENDLOOP;
context3d.changed _ TRUE;
};

SetDull:  PROCEDURE [] ~ {      
FOR i: NAT IN [0..context3d.shapes.length) DO 
G3dRender.SetShininess[ FindShape[context3d, context3d.shapes[i].name], 0.0 ];
ENDLOOP;
context3d.changed _ TRUE;
};

InterpressDemo:  PROCEDURE [] ~ {  
fileName: Rope.ROPE _ ViewerTools.GetSelectionContents[];
IF Rope.Length[fileName] < 1 THEN {  
SIGNAL G3dRender.Error[$MisMatch, "No selection for file name"];
RETURN;  
};
IF DemoErrorMessages[] THEN RETURN;
IF lastDemoCalled # HiddenLinesDemo AND lastDemoCalled # NormaledLinesDemo
THEN 
G3dRenderWithImager.MakeInterpressPage[context3d, fileName ]
ELSE {
tmpContext: Context _ G3dRender.GetTmpContext[context3d];	-- get mod. context
context3d.changed _ TRUE;
G3dRender.NameBackgroundColor[ tmpContext, "White" ];
G3dRender.CopyContextShapes[tmpContext, context3d]; -- insulate shapes from change
FOR i: NAT IN [0..context3d.shapes.length) DO 
G3dRender.SetColor[ FindShape[tmpContext, tmpContext.shapes[i].name], [1.0,1.0,1.0] ];
IF G3dRender.ShapeClassFrom[tmpContext.shapes[i]].type # $Light 	-- avoid lights
THEN IF lastDemoCalled # NormaledLinesDemo
THEN G3dRender.SetRenderStyle[ 
			FindShape[tmpContext, tmpContext.shapes[i].name], hiddenLines ]
ELSE G3dRender.SetRenderStyle[ 
		  FindShape[tmpContext, tmpContext.shapes[i].name], linesWnormals ];
ENDLOOP;
G3dRenderWithImager.MakeInterpressPage[tmpContext, fileName ]
};  
};

RandomQuads:  PROCEDURE [context: Context] ~ {
x, y: REAL;
time: REAL _ BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]];
width: REAL _ context.viewPort.w;    height: REAL _ context.viewPort.h;
aspectRatio: REAL _ height / width;
rs: Random.RandomStream _ Random.Create[Real.Round[width]];  -- limit to display width
p: REF Patch _ NEW[ Patch[4] ];
shape: Shape _ NEW[ShapeRep];
renderData: REF RenderData _ G3dRender.RenderDataFrom[shape];
renderData.shadingClass _ NEW[ShadingClass]; 
renderData.shadingClass.renderMethod _ $Smooth;
p.props _ PutProp[ p.props, $Shape, shape ];
p.nVtces _ 4;
FOR i: NAT IN [0..1000) DO  
IF context.stopMe^ THEN EXIT;
x _ Random.NextInt[rs];    y _ Random.NextInt[rs] * aspectRatio;
FOR j: NAT IN [0..4) DO
xSgn, ySgn: INTEGER;
xSgn _ IF  j < 1 OR j > 3 THEN -1 ELSE 1;    ySgn _ IF  j < 2  THEN 1 ELSE -1;
p[j].coord.sx _ MAX[0, MIN[ width-1, x + xSgn * Random.NextInt[rs] * 0.1]];
p[j].coord.sy _ MAX[0, MIN[ height-1, y+ySgn * Random.NextInt[rs] *0.1 *aspectRatio]];
p[j].shade.er _ Random.NextInt[rs] / width;    
p[j].shade.eg _ Random.NextInt[rs] / width;    
p[j].shade.eb _ Random.NextInt[rs] / width;
ENDLOOP;
[] _ context.class.displayPolygon[context, p];
ENDLOOP;
quadTime _  BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]] - time;
};

SetScene1:  PROCEDURE [] ~ {  
IF scene1 = NIL THEN {
Reset[];
G3dRender.AddShapeFromFile[ context3d,
"CheckerBoard", 							-- internal name
"CheckerBoard.shape", 						-- file name
[-8.0, -8.0, -2.0]								-- position
];
G3dRender.SetColor[ FindShape[context3d, "CheckerBoard"], [.8, .4, .2] ];	-- add shading

G3dRender.AddShapeFromFile[ context3d, 
											"Icosahedron", "Icosahedron.shape", [2.0, 0.0, 0.0] ];
G3dRender.SetColor[ FindShape[context3d, "Icosahedron"], [1., 1., 1.] ];

G3dRender.AddShapeFromFile[ context3d, "SoccerBall", "SoccerBall.shape", [-2.0, 0.0, 0.0] ];
G3dRender.SetColor[ FindShape[context3d, "SoccerBall"], [1., 1., 1.] ];

G3dRender.AddShapeFromFile[ context3d, "CutCube", "CutCube.shape", [0.0, 3.0, 0.0] ];
G3dRender.SetColor[ FindShape[context3d, "CutCube"], [.9, .8, .5] ];

scene1 _ NEW[ShapeSequenceRep[context3d.shapes.length]];
FOR i: NAT IN [0..context3d.shapes.length) DO  
scene1[i] _ context3d.shapes[i];  
ENDLOOP;
scene1.length _ context3d.shapes.length;
}
ELSE {
context3d.shapes _ NEW[ShapeSequenceRep[scene1.length]];
FOR i: NAT IN [0..scene1.length) DO 
context3d.shapes[i] _ scene1[i];
ENDLOOP;
context3d.shapes.length _ scene1.length;
};
G3dRenderWithPixels.DepthBuffering[context3d, FALSE];
currentScene _ scene1;
};

SetScene2:  PROCEDURE [] ~ {  
IF scene2 = NIL THEN {
Reset[];
G3dRender.AddShapeFromFile[context3d, "ChampagneGlass", "ChampagneGlass.shape", 
																					 [2.0, 0.0, 0.0] ];
G3dRender.SetColor[ FindShape[context3d, "ChampagneGlass"], [1.,0.,1.] ];
G3dRender.SetTransmittance[ FindShape[context3d, "ChampagneGlass"], 0.8 ];
G3dRender.AddShapeFromFile[context3d, "UtahEgg", "UtahEgg.shape" ];
G3dShape.TransformShape[ shape: FindShape[context3d, "UtahEgg"], translate: [-2.,.0,.0], 
							   axis: [[0.0, 0.0, 0.0], [1.0, 1.0, 0.0]], rotation: 90.0 ];
G3dRender.SetColor[ FindShape[context3d, "UtahEgg"], [1.0, .8, .5] ];
G3dRender.AddShapeFromFile[context3d, "Banana", "Banana.shape" ];
G3dRender.SetColor[ FindShape[context3d, "Banana"], [1.0, .9, .1] ];
scene2  _ NEW[ShapeSequenceRep[context3d.shapes.length]];
FOR i: NAT IN [0..context3d.shapes.length) DO  
scene2[i] _ context3d.shapes[i];  
ENDLOOP;
scene2.length _ context3d.shapes.length;
}
ELSE {
context3d.shapes _ NEW[ShapeSequenceRep[scene2.length]];
FOR i: NAT IN [0..scene2.length) DO 
context3d.shapes[i] _ scene2[i];
ENDLOOP;
context3d.shapes.length _ scene2.length;
};
G3dRenderWithPixels.DepthBuffering[context3d, FALSE];
currentScene _ scene2;
};

SetScene3:  PROCEDURE [] ~ {  
IF scene3 = NIL THEN {
Reset[];
G3dRender.AddShapeFromFile[context3d, "TeaPot", "TeaPotWithBot.shape", [0.0, 0.0, -1.25]];
scene3  _ NEW[ShapeSequenceRep[context3d.shapes.length]];
FOR i: NAT IN [0..context3d.shapes.length) DO  
scene3[i] _ context3d.shapes[i];  
ENDLOOP;
scene3.length _ context3d.shapes.length;
G3dRender.SetView[context3d, [3.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
}
ELSE {
context3d.shapes _ NEW[ShapeSequenceRep[scene3.length]];
FOR i: NAT IN [0..scene3.length) DO 
context3d.shapes[i] _ scene3[i];
ENDLOOP;
context3d.shapes.length _ scene3.length;
};
currentScene _ scene3;
};

OneShape:  PROCEDURE [] ~ {  
Reset[];
G3dRender.AddShapeFromFile[context3d, "Shape0", ViewerTools.GetSelectionContents[] ];
G3dRender.SetView[context3d, [0.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
};

GetMouseRectangle:  PROCEDURE[x, y: REAL] ~ {
IF mouseRectangle # NIL 
THEN {
mouseRectangle.w _  x - mouseRectangle.x;
IF mouseRectangle.w < 0.0 THEN {
mouseRectangle.x _  mouseRectangle.x + mouseRectangle.w;   
mouseRectangle.w _ -mouseRectangle.w; 
};
mouseRectangle.h _  y - mouseRectangle.y;
IF mouseRectangle.h < 0.0 THEN {
mouseRectangle.y _  mouseRectangle.y + mouseRectangle.h;   
mouseRectangle.h _ -mouseRectangle.h; 
};
SELECT mouseWaiting FROM
$Window => {
mouseRectangle.x _ mouseRectangle.x - context3d.viewPort.x;
mouseRectangle.y _ mouseRectangle.y - context3d.viewPort.y;
mouseRectangle.w _ MIN[mouseRectangle.w, context3d.viewPort.w];
mouseRectangle.x _ 2.0 * (mouseRectangle.x / context3d.viewPort.w - 0.5); -- center
mouseRectangle.y _ 
2.0 * (mouseRectangle.y - context3d.viewPort.h/2.0) / context3d.viewPort.w;
mouseRectangle.w _ 2.0 * mouseRectangle.w / context3d.viewPort.w;
mouseRectangle.h _ mouseRectangle.w * context3d.viewPort.h/context3d.viewPort.w;

windowStack _ CONS[ context3d.window^, windowStack ];
G3dRender.SetWindow[ context3d, mouseRectangle^ ];
mouseRectangle _ NIL;
mouseWaiting _ NIL;
};
$ViewPort => {
viewPortStack _ CONS[ context3d.viewPort^, viewPortStack ];
G3dRender.SetViewPort[ context3d, mouseRectangle^ ];
mouseRectangle _ NIL;
mouseWaiting _ NIL;
};
ENDCASE => SIGNAL G3dRender.Error[$MisMatch, "Unknown Mouse Atom"];
}
ELSE mouseRectangle _ NEW[ Rectangle _ [x, y, 0.0, 0.0] ];
};

GetWindow:  PROCEDURE[] ~ {
mouseWaiting _ $Window
};

RestoreWindow:  PROCEDURE[] ~ {
IF windowStack # NIL 
THEN {
G3dRender.SetWindow[ context3d, windowStack.first ];
windowStack _ windowStack.rest;
}
ELSE SIGNAL G3dRender.Error[$MisMatch, "No previous window"];
};

GetViewPort:  PROCEDURE [] ~ {
mouseWaiting _ $ViewPort
};

RestoreViewPort:  PROCEDURE[] ~ {
IF viewPortStack # NIL 
THEN {
G3dRender.SetViewPort[ context3d, viewPortStack.first ];
viewPortStack _ viewPortStack.rest;
}
ELSE SIGNAL G3dRender.Error[$MisMatch, "No previous viewport"];
};

ShowSinglePatch:  PROCEDURE[ shapeNumber, patchNumber: NAT, keep: BOOL _ FALSE ] ~ {
context3d.props _ PutProp[ 
context3d.props, $SinglePatch, NEW[ IntegerPair _ [shapeNumber, patchNumber] ] 
];
DoCommands[context3d, $Display];
IF NOT keep THEN context3d.props _ Atom.RemPropFromList[ context3d.props, $SinglePatch ];
};

ShowPatchesOnVtx:  PROCEDURE[ shapeNumber, vtxNumber: NAT, keep: BOOL _ FALSE ] ~ {
context3d.props _ PutProp[ 
context3d.props, $SingleVtx, NEW[ IntegerPair _ [shapeNumber, vtxNumber] ] 
];
DoCommands[context3d, $Display];
IF NOT keep THEN context3d.props _ Atom.RemPropFromList[ context3d.props, $SingleVtx ];
};

sumTable: REF RealSequence _ NIL;
discreteTable: BOOLEAN _ FALSE;				-- TRUE if table made from discrete samples
testPatNumStripes, testPatRatio: REAL;

MakeCurves: PROC[numStripes: NAT, ratio: REAL] ~ {
testPatNumStripes _ numStripes;    testPatRatio _ ratio;
IF context3d.viewer # NIL THEN {										 	-- do through viewer
context3d.class.drawInViewer[context3d, NEW[ImagerProcRec _ [ViewerMakeCurves, NIL]]];
};
};

ViewerMakeCurves: ImagerProc ~ {
DoMakeCurves: PROC[pixelMap: ImagerPixel.PixelMap] ~ {
Ceiling: PROC[number: REAL] RETURNS[ result: INTEGER ] ~ {
result _  Real.RoundI[number];
IF result < number THEN result _ result + 1;
};
DrawFilter: PROC[] ~ {
IF sumTable # NIL THEN {				-- draw convolution filter and pixel fiducials
by: NAT _ Real.RoundC[viewPort.h/12.0];
x: NAT _ Real.RoundC[viewPort.w/2.0 - 128.0];
Imager.SetColor[displayContext, Imager.black];
FOR i: NAT IN [0..4] DO								-- draw pixel fiducials
Imager.MaskVector[displayContext, [ x, by+10 ], [ x, by-10 ]];
Imager.MaskVector[displayContext, [ x+10, by ], [ x-10, by ]];
x _ x + 64;
ENDLOOP;
FOR i: NAT IN ( 0..sumTable.length ) DO			-- draw convolution filter
xx: NAT _ Real.RoundC[viewPort.w/2.0 + viewPort.x] + i - sumTable.length/2;
yy: INTEGER _ Real.FixI[ (sumTable[i] - sumTable[i-1]) * 35.0 * viewPort.h / 3.0 ];
IF yy # 0.0 AND i # 192 AND i # 64 THEN {
IF discreteTable THEN yy _ Real.FixI[ MIN[ .8 * viewPort.h/4.0, yy/15.0 ]]; 
yy _ by + yy;
Imager.MaskVector[displayContext, [ xx-1, by ], [ xx-1, yy ]];
Imager.MaskVector[displayContext, [ xx, by ], [ xx, yy ]];
Imager.MaskVector[displayContext, [ xx+1, by ], [ xx+1, yy ]];
};
ENDLOOP;
};

};
numStripes: REAL _ testPatNumStripes;    ratio: REAL _ testPatRatio;
viewPort: Imager.Rectangle _ ImagerBackdoor.GetBounds[ imagerCtx ];
factor: REAL _ numStripes / viewPort.w;
pos: REF RealSequence _ NEW[ RealSequence[numStripes] ];
inc1: REF RealSequence _ NEW[ RealSequence[numStripes] ];
inc2: REF RealSequence _ NEW[ RealSequence[numStripes] ];
line: REF RealSequence _ NEW[ RealSequence[Real.Round[viewPort.w]+4] ];
scanLIne: ImagerPixel.PixelBuffer _ ImagerPixel.NewPixels[ pixelMap, 1, viewPort.w ];
maxValue: REAL _ 255.0; 
viewPort.h _ viewPort.h * 3.0 / 4.0;		-- use just 3/4 of viewport

FOR i: NAT IN [ 0..numStripes ) DO			-- draw spacing curve
pos[i] _ ( 1.0 - ratio/(i+ratio) ) * viewPort.w * 1.0 / (1.0 - ratio / (numStripes+ratio));
inc1[i] _ Real.Float[(numStripes - i)] / numStripes;
inc2[i] _ -2 * inc1[i] / viewPort.h;
ENDLOOP;

Imager.DoSaveAll[imagerCtx, DrawFilter];

FOR y: NAT IN [ Real.RoundC[viewPort.h/3.0] 
				 .. Real.RoundC[viewPort.h*4.0/3.0] ) DO
lastX, thisX: REAL _ 0;
white: BOOLEAN _ TRUE;
IF sumTable # NIL THEN FOR i: NAT IN [0..line.length) DO  line[i] _ 0.0;  ENDLOOP;
FOR i: NAT IN [ 0 .. numStripes ) DO
pxlValue: CARDINAL;
thisX _ pos[i];
IF white THEN pxlValue _ Real.FixC[maxValue] ELSE pxlValue _ 0;
IF sumTable = NIL THEN			-- no table, output plain
FOR x: NAT IN [ Real.FixC[lastX] .. Real.FixC[thisX] ) DO
IF x < Real.RoundC[viewPort.w] 
THEN scanLine[x] _ pxlValue;
ENDLOOP
ELSE IF sumTable.length = 128 THEN { 	-- 2-pixel table
tX: REAL _ thisX + 2;
lX: REAL _ lastX + 2; 
IF white THEN {
lastIndex: NAT _ Real.FixC[lX * 64.0 - (Real.FixC[lX] * 64)];
thisIndex: NAT _ Real.FixC[tX * 64.0 - (Real.FixC[tX] * 64)];
loopStart: NAT _ Real.FixC[lX]-1;
loopEnd: NAT _ Real.FixC[tX];
FOR x: NAT IN [ loopStart .. loopEnd ] DO
SELECT x FROM
loopStart => {
line[x] _ line[x] + 1.0 - sumTable[lastIndex+64];
IF loopStart = loopEnd-1
THEN line[x] _ line[x] - (1.0 - sumTable[thisIndex+64]);
};
loopStart+1 => {
line[x] _ line[x] + 1.0 - sumTable[lastIndex];
IF loopStart = loopEnd-1
THEN line[x] _ line[x] - (1.0 - sumTable[thisIndex]);
IF loopStart = loopEnd-2
THEN line[x] _ line[x] - (1.0 - sumTable[thisIndex+64]);
};
loopEnd-1 => {
line[x] _ line[x] + sumTable[thisIndex+64];
};
loopEnd => {
line[x] _ line[x] + sumTable[thisIndex];
};
ENDCASE => line[x] _ 1.0;			  
ENDLOOP;
}
}
ELSE IF sumTable.length = 256 THEN { 	-- 4-pixel negative lobe table
tX: REAL _ thisX + 2;
lX: REAL _ lastX + 2; 
IF white THEN {
lastIndex: NAT _ Real.FixC[lX * 64.0 - (Real.FixC[lX] * 64)];
thisIndex: NAT _ Real.FixC[tX * 64.0 - (Real.FixC[tX] * 64)];
loopStart: NAT _ Real.FixC[lX]-2;
loopEnd: NAT _ Real.FixC[tX]+1;
FOR x: NAT IN [ loopStart .. loopEnd ] DO
SELECT x FROM
loopStart => {
line[x] _ line[x] + sumTable[255] - sumTable[lastIndex+192];
IF loopStart = loopEnd-3				-- start and end in same pixel span
THEN line[x] _ line[x] - (sumTable[255] - sumTable[thisIndex+192]);
};
loopStart+1 => {
line[x] _ line[x] + sumTable[255] 
					+ sumTable[191] - sumTable[lastIndex+128];
IF loopStart = loopEnd-3				-- start and end in same pixel span
THEN line[x] _ line[x] - ( sumTable[255] 
					+ sumTable[191] - sumTable[thisIndex+128] );
IF loopStart = loopEnd-4				-- start and end in adjacent pixel spans
THEN line[x] _ line[x] - ( sumTable[255] - sumTable[thisIndex+192] );
};
loopStart+2 => {
line[x] _ line[x] + sumTable[255] 
					+ sumTable[191] - sumTable[lastIndex+64];
IF loopStart = loopEnd-3				-- start and end in same pixel span
THEN line[x] _ line[x] - ( sumTable[255] 
							 + sumTable[191] - sumTable[thisIndex+64] );
IF loopStart = loopEnd-4				-- start and end in adjacent pixel spans
THEN line[x] _ line[x] - ( sumTable[255] 
							 + sumTable[191] - sumTable[thisIndex+128] );
IF loopStart = loopEnd-5	-- start and end in pixels separated by one span
THEN line[x] _ line[x] - ( sumTable[255] - sumTable[thisIndex+192] );
};
loopStart+3 => {
line[x] _ line[x] +  sumTable[255] + sumTable[191]
					+ sumTable[63] - sumTable[lastIndex];
IF loopStart = loopEnd-3				-- start and end in same pixel span
THEN line[x] _ line[x] - ( sumTable[255] + sumTable[191]
							 + sumTable[63] - sumTable[thisIndex] );
IF loopStart = loopEnd-4				-- start and end in adjacent pixel spans
THEN line[x] _ line[x] - ( sumTable[255] 
							 + sumTable[191] - sumTable[thisIndex+64] );
IF loopStart = loopEnd-5	-- start and end in pixels separated by one span
THEN line[x] _ line[x] - ( sumTable[255] 
							 + sumTable[191] - sumTable[thisIndex+128] );
IF loopStart = loopEnd-6	-- start and end in pixels separated by two spans
THEN line[x] _ line[x] - ( sumTable[255] - sumTable[thisIndex+192] );
};
loopEnd-3 => {
line[x] _ line[x] + sumTable[63] + sumTable[191] 
					+ sumTable[thisIndex+192];
};
loopEnd-2 => {
line[x] _ line[x] + sumTable[63] + sumTable[thisIndex+128];
};
loopEnd-1 => {
line[x] _ line[x] + sumTable[63] + sumTable[thisIndex+64];
};
loopEnd => {
line[x] _ line[x] + sumTable[thisIndex];
};
ENDCASE => line[x] _ 1.0;			  
ENDLOOP;
}
};
white _ NOT white;
pos[i] _ pos[i] + inc1[i];
inc1[i] _ inc1[i] + inc2[i];
lastX _ thisX;
ENDLOOP;
IF sumTable # NIL THEN FOR i: NAT IN [2..Real.RoundC[viewPort.w]+2) DO
pixelValue: INTEGER _ MAX[ 0, Real.FixI[ MIN[maxValue, line[i]*(maxValue)] ] ];
scanLine[i-2] _ pixelValue;
ENDLOOP;
ImagerPixel.PutPixels[ self: pixelMap, pixels: scanLine, 
						 initIndex: [f: xStart, s: y], count: viewPort.w ];
ENDLOOP;
};
ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoMakeCurves, context.viewPort^];
};

MakeNilTable: PROC[] ~ {
sumTable _ NIL; 
};

MakeImpulseTable: PROC[] ~ {
sumTable _ NEW[ RealSequence[128] ];
FOR i: NAT IN [0 .. 64)   DO sumTable[i] _ 0.0;  ENDLOOP;
FOR i: NAT IN [64 .. 128) DO sumTable[i] _ 1.0;  ENDLOOP;
discreteTable _ TRUE;
};

Make2ImpulseTable: PROC[] ~ {
sumTable _ NEW[ RealSequence[128] ];
FOR i: NAT IN [0 .. 48)   DO sumTable[i] _ 0.0;  ENDLOOP;
FOR i: NAT IN [48 .. 80)   DO sumTable[i] _ 0.5;  ENDLOOP;
FOR i: NAT IN [80 .. 128) DO sumTable[i] _ 1.0;  ENDLOOP;
discreteTable _ TRUE;
};

Make4ImpulseTable: PROC[] ~ {
sumTable _ NEW[ RealSequence[128] ];
FOR i: NAT IN [0 .. 40)   DO sumTable[i] _ 0.0;  ENDLOOP;
FOR i: NAT IN [40 .. 56)   DO sumTable[i] _ 0.25;  ENDLOOP;
FOR i: NAT IN [56 .. 72)   DO sumTable[i] _ 0.50;  ENDLOOP;
FOR i: NAT IN [72 .. 88)   DO sumTable[i] _ 0.75;  ENDLOOP;
FOR i: NAT IN [88 .. 128) DO sumTable[i] _ 1.0;  ENDLOOP;
discreteTable _ TRUE;
};

Make8ImpulseTable: PROC[] ~ {
sumTable _ NEW[ RealSequence[128] ];
FOR i: NAT IN [0 .. 36)     DO sumTable[i] _  0.000;  ENDLOOP;
FOR i: NAT IN [36 .. 44)    DO sumTable[i] _  0.125;  ENDLOOP;
FOR i: NAT IN [44 .. 52)    DO sumTable[i] _  0.250;  ENDLOOP;
FOR i: NAT IN [52 .. 60)    DO sumTable[i] _  0.375;  ENDLOOP;
FOR i: NAT IN [60 .. 68)    DO sumTable[i] _  0.500;  ENDLOOP;
FOR i: NAT IN [68 .. 76)    DO sumTable[i] _  0.625;  ENDLOOP;
FOR i: NAT IN [76 .. 84)    DO sumTable[i] _  0.750;  ENDLOOP;
FOR i: NAT IN [84 .. 92)    DO sumTable[i] _  0.875;  ENDLOOP;
FOR i: NAT IN [92 .. 128)   DO sumTable[i] _  1.000;  ENDLOOP;
discreteTable _ TRUE;
};

MakeWtd8ImpulseTable: PROC[] ~ {
sumTable _ NEW[ RealSequence[128] ];
FOR i: NAT IN [0 .. 8)     DO sumTable[i] _  0.0/32.0;  ENDLOOP;
FOR i: NAT IN [8 .. 24)    DO sumTable[i] _  1.0/32.0;  ENDLOOP;
FOR i: NAT IN [24 .. 40)   DO sumTable[i] _  4.0/32.0;  ENDLOOP;
FOR i: NAT IN [40 .. 56)   DO sumTable[i] _  9.0/32.0;  ENDLOOP;
FOR i: NAT IN [56 .. 72)   DO sumTable[i] _ 16.0/32.0;  ENDLOOP;
FOR i: NAT IN [72 .. 88)   DO sumTable[i] _ 23.0/32.0;  ENDLOOP;
FOR i: NAT IN [88 .. 104)  DO sumTable[i] _ 28.0/32.0;  ENDLOOP;
FOR i: NAT IN [104 .. 120) DO sumTable[i] _ 31.0/32.0;  ENDLOOP;
FOR i: NAT IN [120 .. 128) DO sumTable[i] _ 32.0/32.0;  ENDLOOP;
discreteTable _ TRUE;
};

MakeWtd16ImpulseTable: PROC[] ~ {
sum: REAL _ 0;
lastPos: NAT _ 0;
thisPos: NAT _ 4;
sumTable _ NEW[ RealSequence[128] ];
FOR i: NAT IN [0..16) DO 
j: NAT _ 8 - ABS[8 - i];    IF i > 7 THEN j _ j -1;
FOR k: NAT IN [lastPos .. thisPos) DO  sumTable[k] _  sum;  ENDLOOP;
sum _ sum + (j*2 + 1)/128.0;
lastPos _ thisPos;
thisPos _ thisPos + 8;
ENDLOOP;
FOR k: NAT IN [lastPos .. 128) DO  sumTable[k] _  1.0;  ENDLOOP;
discreteTable _ TRUE;
};

MakeBoxTable: PROC[] ~ {
sumTable _ NEW[ RealSequence[128] ];
FOR i: NAT IN [0 .. 32)   DO sumTable[i] _ 0.0;  ENDLOOP;
FOR i: NAT IN [32 .. 96)  DO sumTable[i] _ (i-32) / 64.0;  ENDLOOP;
FOR i: NAT IN [96 .. 128) DO sumTable[i] _ 1.0;  ENDLOOP;
discreteTable _ FALSE;
};

MakeTriangleTable: PROC[] ~ {
sumTable _ NEW[ RealSequence[128] ];
FOR i: NAT IN [0..64) DO  
t: REAL _ i * 1.0 / 64.0;
sumTable[i] 				_ Sqr[t] / 2.;
sumTable[i + 64] _ 1. - Sqr[1. - t] / 2.;
ENDLOOP;
discreteTable _ FALSE;
};

MakeGaussTable: PROC[spread: REAL] ~ {
sumTable _ NEW[ RealSequence[128] ];
FOR i: NAT IN [0..64) DO  
t: REAL _ (i * 1.0 / 64.0);
sumTable[64 + i] _ RealFns.Exp[-spread*t*t];
sumTable[63 - i] _ sumTable[64 + i];
ENDLOOP;
FOR i: NAT IN (0..128) DO  
sumTable[i] _ sumTable[i-1] + sumTable[i];
ENDLOOP;
FOR i: NAT IN [0..128) DO  
sumTable[i] _ sumTable[i] / sumTable[127];
ENDLOOP;
discreteTable _ FALSE;
};

MakeSinc2Table: PROC[] ~ {
sumTable _ NEW[ RealSequence[128] ];
FOR i: NAT IN [0..64) DO  
t: REAL _ (1.0 - i * 1.0 / 64.0) * 3.1416;
sumTable[i] _ RealFns.Sin[t] / t;
sumTable[127 - i] _ sumTable[i];
ENDLOOP;
FOR i: NAT IN (0..128) DO  
sumTable[i] _ sumTable[i-1] + sumTable[i];
ENDLOOP;
FOR i: NAT IN [0..128) DO  
sumTable[i] _ sumTable[i] / sumTable[127];
ENDLOOP;
discreteTable _ FALSE;
};

MakeSinc4Table: PROC[weighted, sincWeighting: BOOLEAN _ TRUE] ~ {
completeSum: REAL;
sumTable _ NEW[ RealSequence[256] ];
FOR i: NAT IN [0..128) DO  
t: REAL _ (2.0 - i * 1.0 / 64.0) * 3.1416;
sumTable[i] _ RealFns.Sin[t] / t;
IF weighted THEN 
IF sincWeighting THEN sumTable[i] _ (RealFns.Sin[t/2.0] / (t/2.0)) * sumTable[i]
					 ELSE sumTable[i] _  (i * 1.0 / 128.0) * sumTable[i];	-- triangle weight
sumTable[255 - i] _ sumTable[i];
ENDLOOP;
FOR i: NAT IN (0..64) DO  
sumTable[i] _ sumTable[i-1] + sumTable[i];
sumTable[i+192] _ sumTable[i+192-1] + sumTable[i+192];
ENDLOOP;
FOR i: NAT IN (0..128) DO  
sumTable[i+64] _ sumTable[i+64-1] + sumTable[i+64];
ENDLOOP;
completeSum _ sumTable[63] + sumTable[191] + sumTable[255];
FOR i: NAT IN [0..256) DO  
sumTable[i] _ sumTable[i] / completeSum;
ENDLOOP;
sumTable[0] _ sumTable[0];
discreteTable _ FALSE;
};

Orbit:  PROCEDURE [] ~ { 
IF DemoErrorMessages[] THEN RETURN;
animation _ TRUE;
G3dAnimationSupport.Orbit[
context: context3d, 
lookingFrom: context3d.eyePoint,
lookingAt: context3d.lookAt, 
axis: [0.3, 0.0, 1.0], 
base: context3d.lookAt,
framesPerRev: 53 
! UNWIND => animation _ FALSE
]; 
animation _ FALSE;
};

MakeFrame: PROC ~ {
context3d.class.render[context3d  ];						-- clear and make new frame
};

ShowPixels: PROC ~ {
context3d.class.drawInViewer[ context3d, 
							       NEW[ImagerProcRec _ [G3dColorDisplaySupport.StuffBuf, NIL]] 
							    ];
};

ShowShapes: PROC ~ {
G3dSortandDisplay.ShowShapes[context3d  ];					-- show shapes without clearing
};

movieFileRoot: Rope.ROPE;
movieFileLog: Rope.ROPE;
movieFileLogTime: BasicTime.GMT;
movieNumFiles: NAT;
movieStart: NAT;
movieLabeledFrames: BOOLEAN;
SetUpMovie: PROC[fileRoot: Rope.ROPE, numFiles, start: NAT _ 0, labels: BOOLEAN _ FALSE] ~ {
fileUpdated: BOOLEAN _ FALSE;
IF frames # NIL AND Rope.Equal[movieFileRoot, fileRoot] 
THEN {
latestTime: BasicTime.GMT;
[created: latestTime] _ FS.FileInfo[ G3dRender.GetLogFileName[fileRoot] ];
IF BasicTime.Period[from: movieFileLogTime, to: latestTime] > 0 
THEN fileUpdated _ TRUE;
}
ELSE frames _ NIL;
IF frames = NIL OR fileUpdated THEN { 
movieFileRoot _ fileRoot;
[fullFName: movieFileLog, created: movieFileLogTime] _ FS.FileInfo[
G3dRender.GetLogFileName[fileRoot] 
];
movieNumFiles _ numFiles;
movieStart _ start;
movieLabeledFrames _ labels;
frames _ NIL;
};
};

frameRate: INTEGER _ 6;		-- playback rate in frames/second, negative plays backwards
PlayBackbyFrame: CedarProcess.ForkableProc ~ {
CleanUp: PROC ~ {
G3dRender.SetViewPort[ context3d, cachedViewPort ];
IF context3d.class.displayType = $PseudoColor 
THEN G3dColorDisplaySupport.LoadStd8BitClrMap[context3d.terminal];
};
size, posn: Pair;
G3dSortandDisplay.ValidateContext[context3d];
cachedViewPort _ context3d.viewPort^;
context3d.class.loadBackground[context3d];
IF frames = NIL THEN {
frames _ G3dAnimationSupport.CacheNumberedAISFiles[
context3d, movieFileRoot, movieNumFiles, movieStart, movieLabeledFrames
];
};
IF context3d.stopMe^ THEN {  CleanUp[];    RETURN[];  };
size _ [frames[0].box.max.f - frames[0].box.min.f, frames[0].box.max.s - frames[0].box.min.s];
posn _ IF context3d.viewer # NIL 
THEN [ context3d.viewer.cx + (context3d.viewer.cw/2 - size.x/2), 
        context3d.viewer.cy + (context3d.viewer.ch/2 - size.y/2) ]
ELSE  [ context3d.pixels.box.max.f/2 - size.x/2, context3d.pixels.box.max.s/2 - size.y/2 ];
G3dRender.SetViewPort[ context3d, [ posn.x, posn.y, size.x, size.y ] ];
IF context3d.viewer # NIL THEN ThreeDViewer.ViewerUpdate[context3d]; -- install viewport

context3d.class.draw2DRope[context3d, "Control frame rate using left button", [140, 50] ];
WHILE TRUE DO
ENABLE UNWIND => CleanUp[];
IF context3d.stopMe^ THEN {  CleanUp[];     RETURN[];  };
G3dAnimationSupport.ShowNextAISCacheFrame[context3d, frames, frameRate];
CedarProcess.CheckAbort[];
ENDLOOP;
};

Commander.Register["ThreeDDemo", ThreeDDemoCommand, "\nDemonstrate ThreeDWorld"];

END. 
��"��ThreeDDemoImpl.mesa 
Last Edited by: Crow, December 8, 1989 11:19:46 pm PST
Bloomenthal, September 26, 1988 12:04:57 pm PDT
Types
Global Variables
Renamed Procedures
Menu and Button Procs
PROC [data: REF] RETURNS [results: REF _ NIL];
Forking this process frees button, reducing likelihood of deadlocks, at least I think that's why this is done this way
Context and Interactive Control
PROC[context: Context, bttn, ctlShft: ATOM, x, y: REAL]
bttn in { $LeftButton, $LeftHeld, $MiddleButton, $MiddleHeld, $RightButton, $RightHeld }
ctlShft in { NIL, $Shift, $Control, $ControlShift }
This will be called whenever the mouse is moved or a mouse button is pushed.

Makes a new view of the supplied context, useful for using two portions of the screen differently, etc.
Demos
Remove Highlights
Scene Definition
Test Patterns for filter evaluation
Calculates the integral over the pyramid function, equal to the left half of a parabolic window or B-spline basis function
Calculates the integral over the central lobe of the sinc function
Calculates the integral over the central lobe of the sinc function
Calculates the integral over the inner 3 lobes of the sinc function weighted by the inner lobe stretched by 2 or weighted by a triangle
Frame Generation and Animation
Construct viewPort from cached frame size
Ê/¾��˜�head™J™6Icode™/IdefaultšÏk	œÏcœžœŠ˜¿—head2šœœ˜Jšœ¨ž˜±J˜�Jšœ˜J˜�—head3šÐbi™Mšœœ˜!Mšœœœ˜Mšœœ˜$Jšœœ˜.Mšœœ˜+Mšœ	œ˜Mšœ
œ˜!Mšœ
œ˜"Jšœœ˜'Jšœœ
œ˜Mšœ
œ˜!Jšœœ˜,Jšœ	œ˜ Jšœœ˜$Jšœœ˜.Jšœœ˜3Jšœœ˜)Mšœ
œ˜)Mšœœ˜.Mšœœœœœœ˜ —šÏb™Jšœœž˜4Jšœ6œ˜:Jšœœœœ˜1Jšœœ#˜.Jšœœ˜Jšœœ˜!Jšœ
œ˜Jšœ
œ˜Jšœœœ˜Mšœ%œ˜)M˜1Mšœœœ
œ˜5Mšœœœ˜Jšœœ
˜$—š ™šÏn	œœœœ	˜DJšœ˜—š¡œœœœœœœœœœ¡˜¬Jš¡œ˜!—š¡œÏsœ¢œ	¢œ¢¢œ¢¢œ¢œ˜CJšœ˜J˜�—š¡œœœœœœœ˜RJšœ˜——š ™defualtšœ
œœœ˜2šœž˜-šœ	œ˜Pšœ.ž˜BPšœ+ž˜APšœ%ž˜:Pšœ6ž
˜CPšœœœž˜)Pšœœœž˜(PšœBž˜QPšœ<ž˜MPšœœœž˜,Pšœ.ž˜CPšœ0ž˜GP˜—P˜P˜&P˜—šœž˜/šœ	œ˜Pšœ!ž˜5Pšœ%ž˜;Pšœ#ž˜8Pšœ"ž˜3Pšœ/ž˜>Pšœ0ž˜>Pšœ+ž˜:Pšœ0ž˜AP˜—P˜P˜8P˜—šœ$ž˜6šœ	œ˜Pšœž˜3Pšœž˜8Pšœœœž˜-Pšœ+ž
˜8Pšœ*ž˜9Pšœœœž˜&Pšœ-ž˜<Pšœ-ž˜>Pšœœœž˜(Pšœ5ž˜JPšœ1ž˜HPšœœœž˜.P˜#P˜P˜!P˜—P˜P˜BP˜—šœž˜*šœ	œ˜Pšœ6ž˜JPšœ=ž˜SPšœ7ž˜OP˜—P˜P˜P˜—šœž˜+šœ	œ˜Pšœ<ž˜PPšœDž˜TPšœEž˜TPšœ5ž
˜BPšœ/ž˜>Pšœœœž˜(Pšœ/ž˜>Pšœ/ž˜@P˜—P˜P˜%P˜—šœ ž
˜*šœ	œ˜Pšœ9ž˜MPšœ@ž˜VPšœ@ž˜UPšœ,ž
˜9Pšœ'ž˜6Pšœ,ž˜:Pšœ'ž˜6Pšœ-ž˜>Pšœ(ž˜8Pšœ.ž˜CPšœ0ž˜GPšœ1ž˜GP˜*P˜2P˜/P˜,P˜1P˜+P˜)P˜—P˜P˜.P˜—šœ"ž˜1Pšœ	œ	ž˜4P˜P˜.P˜—šœž˜,Pšœ	œ	ž˜4P˜P˜6P˜—P˜—š¡œ
œ
œœ˜5Jšœœž˜4šœœœœž*˜Dšœ˜J˜J˜J˜Jšœ˜P˜P˜P˜P˜ Jšœ˜—J˜—J˜—š¡
œ
œ
œœ˜7Jšœœž˜4šœœœœ˜šœ˜J˜J˜0J˜/J˜J˜0J˜2šœ
œ)˜8šœ3˜5šœ	ž ˜-J˜&J˜J˜———P˜ Jšœ˜—J˜—J˜—š¡œœœœ˜>Jšœœž˜4šœ˜Jšœ5œ˜<Jšœ:œ˜AJ˜2šœ
œ*˜9šœ3˜5šœ˜J˜&Jšœ*œ˜0J˜———Jšœ:œ˜@M˜2Mšœ8œ˜>Mšœ8œ˜?M˜GM˜=M˜DJšœ˜—šœœœœ˜Jšœ#œ˜(J˜—J˜—š¡
œ
œ
œœ˜7Jšœœž˜4šœœœœ˜šœ˜˜
Jšœœœ˜#šœ˜J˜J˜Jšœ#œ˜)Jšœ'œ˜-J˜J˜Jšœ˜—J˜—J˜jM˜ Jšœ˜—J˜—J˜—š¡
œœœœ˜7š¡œœ˜Jšœ*œ˜1Jšœ,œ˜3L˜—Jšœœž˜4šœœœœ˜šœœ(˜:Jšœ5ž˜Nšœœ˜J˜˜JšœO˜OJšœ;˜;J˜—˜JšœH˜HJ˜;J˜—˜
JšœO˜O˜J˜IJ˜—J˜—˜
JšœO˜OJšœ+Ïf
œ˜NJ˜2J˜—˜JšœH˜HJ˜LJ˜—˜JšœH˜HJ˜LJ˜—Jšœ˜——Jšœœ3˜GJ˜—J˜—š¡œ
œ
œœ˜8Mšœœž˜4šœœœœ˜šœ˜šœœ*˜Ašœ5œž˜QMšœ/œœ˜P——šœœ#˜9šœ.œž˜LMšœ/œœ˜P——šœœ(˜:šœ2œž˜QMšœ/œœ˜P———šœ˜M˜oM˜@MšœK˜KMšœL˜LMšœG˜GMšœL˜LMšœJ˜JMšœ@˜@MšœG˜GMšœJ˜JMšœF˜FMšœI˜IMšœJ˜JMšœL˜LMšœN˜NMšœH˜HMšœI˜IMš˜—M˜—M˜—š¡œ˜2Mš
œœœœœ™.M™vMšœœœ˜Mšœ	œœ
˜&Mšœœœ˜$šœ4œ˜?Mšœœž)˜P—M˜M˜—š	¡œœ
œœ	ž
˜FMšœœ˜šœ%œ˜4Mšœ
œ˜-Mšœ˜	—Mšœœ˜šœœœž$˜DMšœœ˜Mšœœ˜Mšœœ%˜:Mšœ˜
—šœœœ˜Mšœ˜Mšœ!˜!Mšœœ˜Mšœ˜M˜1M˜—M˜—š	¡œœœœž$˜SJšœœ˜Jšœ!ž'˜HJšœœž&˜GJšœœ˜Jšœ-œ˜2Jšœœ˜JšœC˜CJ˜?J˜AJšœœ˜"Jšœœ˜Jšœ˜Jšœœ˜Jšœ	œ˜
J˜HJ˜FJ˜L˜�——š ™š¡œ˜,Jšœ˜Iašœ*ž˜=˜J˜Jšœž˜6J˜)J˜J˜Jšœ˜J˜—Jšœ<ž˜SJšœ9ž˜MJ˜?JšœC˜CJ˜—š¡
œœ˜Jšœ˜Jšœ<ž˜SJšœ9ž˜MJ˜?JšœC˜CJ˜—š¡œœ˜Jšœ˜Jšœ4˜4Jšœ<ž˜SJšœ9ž˜MJ˜?JšœC˜CJ˜—š¡œœ˜!Jšœ˜Jšœ2˜2Jšœ<ž˜SJšœ9ž˜MJ˜?JšœC˜CJ˜—š¡œ	œ˜Jšœœ˜J˜—š¡œ˜(Mšœ
œœœ™7J™XJ™3J™LJšœœ˜Jšœ	œ˜šœœ˜Jšœœœœ˜J—šœœ˜JšœB˜FJšœB˜FJ˜F—J˜4Jšœž˜.J˜šœ˜šœœœ˜0Jšœ
œ˜+šœ˜Jšœœ$˜3Jšœœ$˜3Jšœ¥˜¥J˜J˜——˜Jšœœž ˜:Jšœœ˜J˜EJ˜J˜—˜Jšœœ$˜3Jšœ¥˜¥J˜J˜—Jšœ˜—J˜J™�—š¡œœ˜Jšœœ˜J˜šœœ˜šœ˜J˜LJ˜—šœ˜	J˜AJ˜AJ˜%J˜——J˜IJ˜"J˜7J˜—š¡œœ	œ˜4Jšœœ˜J˜J˜
J˜\šœœ˜šœ˜J˜LJ˜—šœ˜	J˜AJ˜AJ˜%J˜——J˜IJ˜MJ˜PJ˜J˜J˜—š¡œœœ	œœœœ˜nJ˜J˜
Jšœœ˜J˜\šœœ˜šœ˜J˜LJ˜—šœ˜	J˜AJ˜AJ˜%J˜——J˜IJ˜JJšœœœ*˜<J˜EJ˜—š¡œœ
œœ˜?Q˜0Qšœœ˜	šœœž0˜QQ˜LQ˜—Qšœ;ž˜PQ˜YQšœœ0ž˜aQšœœ3˜QJ˜&Jšœœ˜J˜0Jšœ.ž'˜UJ˜—š¡œœ
œœ˜>Q™gQšœ
œ$˜8Q˜XQšœ;ž˜VQšœD˜DQšœœ/˜KQšœœ1˜OQšœœ˜Jšœ/˜/Jšœ.ž'˜UJ˜——š ™š
¡œ	œœ	œœ˜Fš¡
œ	œœœ˜Ašœœœ˜%Jšœœœœœœœ˜NJš˜—Jšœœ˜
J˜—Jšœœœœ
˜Kšœœœœ˜@J˜TJšœœ˜
J˜—šœ˜Jšœœ)œ˜Išœ˜˜&J˜?—Jšœœ˜
J˜——J˜—š¡	œ	œ
œœ˜8Jšœœœ˜#šœœœœ˜.JšœR˜RJšœ˜—Jšœœ˜Jšœœ%˜6J˜J˜—š¡œ	œ
œœ˜>Jšœœœ˜#šœœœœ˜.JšœX˜XJšœ˜—Jšœœ˜Jšœœ%˜6J˜!J˜—š¡œ	œ
œœ˜>Jšœ:ž˜SJšœœœ˜#J˜5Jšœ4ž˜Sšœœœœ˜/šœ˜Jšœ>˜>Jšœ˜—Jšœ˜—Jšœœ˜Jšœœ'˜8J˜!J˜—š¡œ	œ
œœ˜@Jšœœ'ž˜SJšœœœ˜#J˜5Jšœ4ž˜Sšœœœœ˜/J˜Xšœ˜Jšœ>˜>Jšœ˜—Jšœ˜—Jšœœ˜Jšœœ'˜8J˜#J˜—š¡œ	œ
œœ˜:Jšœœœ˜#šœœœ˜-JšœT˜TJšœ˜—Jšœœ˜Jšœœ%˜6J˜J˜—š¡
œ	œ
œœ	˜;Jšœœœ˜#šœœœœ˜.JšœS˜SJšœ˜—Jšœœ˜J˜Jšœœ%˜6J˜—š¡œ	œ
˜!šœœœ˜-JšœO˜OJšœ˜—Jšœœ˜J˜—š¡œ	œ
˜ J™šœœœœ˜.JšœN˜NJšœ˜—Jšœœ˜J˜—š¡œ	œ	˜#Jšœœ&˜9šœœ˜%Jšœ:˜@Jšœ˜	J˜—Jšœœœ˜#šœ"œ#˜Jšœ˜J˜<—šœ˜Jšœœ'ž˜MJšœœ˜J˜5Jšœ3ž˜Ršœœœœ˜.J˜Všœ?ž˜Pšœœ#˜*šœ˜JšœB˜B—šœ˜JšœF˜F———Jšœ˜—J˜=J˜——J˜J˜�—š¡œ	œœ˜.Jšœœ˜Jšœœ9˜CJšœœ"œ˜GLšœ
œ˜#Lšœ=ž˜VJšœœ	œ
˜Jšœœ˜Jšœœ.˜=Jšœœ˜-Jšœ/˜/Jšœ,˜,Jšœ
˜
šœœœœ˜Jšœœœ˜Jšœ@˜@šœœœ˜Jšœœ˜Jšœœœœœœ	œœ˜NJšœœœ1˜KJšœœœ<˜VJšœ/˜/Jšœ/˜/Jšœ+˜+Jšœ˜—Jšœ.˜.Jšœ˜—JšœI˜IJšœ˜——š ™š¡	œ	œ	˜šœ
œœ˜J˜šœ&˜&Jšœž˜'Jšœž˜(Jšœž˜%J˜—JšœJž˜YJšœi˜iJšœI˜IJšœ\˜\JšœH˜HJšœU˜UJšœE˜EJšœ	œ,˜8šœœœœ˜/J˜"Jšœ˜—J˜(J˜—šœ˜Jšœœ"˜8šœœœœ˜$J˜ Jšœ˜—J˜(J˜—Jšœ.œ˜5J˜J˜—š¡	œ	œ	˜šœ
œ˜J˜šœy˜yJšœI˜IJšœJ˜J—šœC˜CJšœŸ˜ŸJšœE˜E—šœA˜AJšœD˜D—Jšœ
œ,˜9šœœœœ˜/J˜"Jšœ˜—J˜(J˜—šœ˜Jšœœ"˜8šœœœœ˜$J˜ Jšœ˜—J˜(J˜—Jšœ.œ˜5J˜J˜—š¡	œ	œ	˜šœ
œ˜J˜JšœZ˜ZJšœ
œ,˜9šœœœœ˜/J˜"Jšœ˜—J˜(JšœB˜BJ˜—šœ˜Jšœœ"˜8šœœœœ˜$J˜ Jšœ˜—J˜(J˜—J˜J˜—š¡œ	œ	˜J˜JšœU˜UJšœB˜BJ˜—š¡œ	œœ˜-šœœ˜šœ˜P˜)šœœ˜ P˜;P˜&P˜—P˜)šœœ˜ P˜;P˜&P˜—šœ˜˜P˜;P˜;Pšœœ)˜?JšœJž	˜S˜J˜K—J˜AP˜PP˜�Pšœœ#˜5P˜2Pšœœ˜Pšœœ˜P˜—˜Pšœœ'˜;P˜4Pšœœ˜Pšœœ˜P˜—Pšœœ2˜C—P˜—Pšœœ!˜:—P˜—š¡	œ	œ˜P˜P˜—š¡
œ	œ˜šœœ˜šœ˜P˜4P˜P˜—Pšœœ2˜=—P˜—š¡œ	œ˜P˜P˜—š¡œ	œ˜!šœœ˜šœ˜P˜8P˜#P˜—Pšœœ4˜?—P˜—š
¡œ	œœœœ˜Tšœ˜Pšœœ-˜OP˜—P˜ PšœœœI˜YP˜—š
¡œ	œœœœ˜Sšœ˜Pšœœ+˜KP˜—P˜ PšœœœG˜WP˜——š #™#˜�Jšœ
œœ˜!Jšœœœž+˜NJšœ!œ˜&J˜�—š¡
œœ
œ	œ˜2Qšœ8˜8	šœœœž˜@Qšœ(œ$œ˜VQ˜—J˜J˜�—š¡œ˜ š¡œœ$˜6	š
¡œœ	œœ
œ˜:Qšœ˜Qšœœ˜,Q˜—	š¡
œœ˜	šœœœž.˜JQšœœ ˜'Qšœœ'˜-Qšœ.˜.	šœœœž˜6Qšœ>˜>Qšœ>˜>Qšœ˜Qšœ˜—	šœœœž˜DQšœœD˜KQšœœH˜S	šœ
œ	œœ˜)Qšœœœ#˜LQšœ
˜
Qšœ>˜>Qšœ:˜:Qšœ>˜>Q˜—Qšœ˜—Q˜—Q˜�Q˜—Qšœœ œ˜DQšœC˜CQšœœ˜'Jšœœœ˜8Jšœœœ˜9Jšœœœ˜9Jšœœœ+˜GJšœU˜UJšœ
œ
˜Jšœ&ž˜AQ˜�	š	œœœœž˜:Qšœ[˜[Qšœ4˜4Qšœ$˜$Qšœ˜Q˜�—	šœ(˜(Q˜�—	šœœœJ˜YQšœœ˜Qšœœœ˜Qšœœœœœœœœ˜R	šœœœ˜$Qšœ
œ˜Qšœ˜Qšœœ œ˜?	šœœœž˜2	šœœœ*˜9	šœ˜Qšœ˜—Qš˜——	šœœœž˜6Qšœœ
˜Qšœœ˜	šœœ˜Qšœœ/˜=Qšœœ/˜=Qšœœ˜!Qšœ	œ˜	šœœœ˜)	šœ˜
	šœ˜Qšœ1˜1	šœ˜Qšœ4˜8—Qšœ˜—	šœ˜Qšœ.˜.	šœ˜Qšœ1˜5—	šœ˜Qšœ4˜8Qšœ˜——	šœ˜Qšœ+˜+Q˜—	šœ˜Qšœ(˜(Qšœ˜—Qšœ˜—Qšœ˜—Qšœ˜—Qšœ˜—	šœœœž˜DQšœœ
˜Qšœœ˜	šœœ˜Qšœœ/˜=Qšœœ/˜=Qšœœ˜!Qšœ	œ˜	šœœœ˜)	šœ˜
	šœ˜Qšœ<˜<	šœž#˜?Qšœ?˜C—Qšœ˜—	šœ˜QšœR˜R	šœž#˜?QšœW˜[—	šœž(˜DQšœA˜E—Qšœ˜—	šœ˜QšœQ˜Q	šœž#˜?QšœY˜]—	šœž(˜DQšœZ˜^—	šœž1˜IQšœA˜E—Qšœ˜—	šœ˜Qšœ]˜]	šœž#˜?Qšœd˜h—	šœž(˜DQšœY˜]—	šœž1˜IQšœZ˜^—	šœž2˜JQšœA˜E—Qšœ˜—	šœ˜QšœQ˜QQ˜—	šœ˜Qšœ;˜;Q˜—	šœ˜Qšœ:˜:Q˜—	šœ˜Qšœ(˜(Qšœ˜—Qšœ˜—Qšœ˜—Qšœ˜—Qšœ˜—Qšœœ˜Qšœ˜Qšœ˜Qšœ˜Qšœ˜—	š
œœœœœœ ˜FQšœœœœ#˜OQšœ˜Qšœ˜—Qšœs˜sQšœ˜—Qšœ˜—JšœQ˜QQ˜J˜�š¡œœ˜Jšœœ˜Jšœ˜J˜�—š¡œœ˜Jšœœ˜$Jš
œœœ
œœ˜9Jš
œœœ
œœ˜9Jšœœ˜Jšœ˜J˜�—š¡œœ˜Jšœœ˜$Jš
œœœ
œœ˜9Jš
œœœœœ˜:Jš
œœœ
œœ˜9Jšœœ˜Jšœ˜J˜�—š¡œœ˜Jšœœ˜$Jš
œœœ
œœ˜9Jš
œœœœœ˜;Jš
œœœœœ˜;Jš
œœœœœ˜;Jš
œœœ
œœ˜9Jšœœ˜Jšœ˜J˜�—š¡œœ˜Jšœœ˜$Jš
œœœœœ˜>Jš
œœœœœ˜>Jš
œœœœœ˜>Jš
œœœœœ˜>Jš
œœœœœ˜>Jš
œœœœœ˜>Jš
œœœœœ˜>Jš
œœœœœ˜>Jš
œœœœœ˜>Jšœœ˜Jšœ˜J˜�—š¡œœ˜ Jšœœ˜$Jš
œœœœœ˜@Jš
œœœœœ˜@Jš
œœœœœ˜@Jš
œœœœœ˜@Jš
œœœœœ˜@Jš
œœœœœ˜@Jš
œœœœœ˜@Jš
œœœœœ˜@Jš
œœœœœ˜@Jšœœ˜Jšœ˜J˜�—š¡œœ˜!Jšœœ˜Jšœ	œ˜Jšœ	œ˜Jšœœ˜$šœœœ	œ˜Jš	œœœœœ
˜3Jš
œœœœœ˜DJšœ˜Jšœ˜Jšœ˜Jšœ˜—Jš
œœœœœ˜@Jšœœ˜Jšœ˜J˜�—š¡œœ˜Jšœœ˜$Jš
œœœ
œœ˜9Jš
œœœ
œœ˜CJš
œœœ
œœ˜9Jšœœ˜Jšœ˜J˜�—š¡œœ˜J™zJšœœ˜$šœœœ	œ˜Jšœœ˜Jšœ˜Jšœ)˜)Jšœ˜—Jšœœ˜J˜J˜�—š¡œœ	œ˜&J™BJšœœ˜$šœœœ	œ˜Jšœœ˜Jšœ,˜,Jšœ$˜$Jšœ˜—šœœœ
œ˜Jšœ*˜*Jšœ˜—šœœœ
œ˜Jšœ*˜*Jšœ˜—Jšœœ˜Jšœ˜J˜�—š¡œœ˜J™BJšœœ˜$šœœœ	œ˜Jšœœ#˜*Jšœ!˜!Jšœ ˜ Jšœ˜—šœœœ
œ˜Jšœ*˜*Jšœ˜—šœœœ
œ˜Jšœ*˜*Jšœ˜—Jšœœ˜Jšœ˜J˜�—š¡œœœœ˜AJ™‡Jšœ
œ˜Jšœœ˜$šœœœ
œ˜Jšœœ#˜*Jšœ!˜!šœ
œ˜Jšœœ;˜PJšœœ1ž˜M—Jšœ ˜ Jšœ˜—šœœœ	œ˜Jšœ*˜*Jšœ6˜6Jšœ˜—šœœœ
œ˜Jšœ3˜3Jšœ˜—Jšœ;˜;šœœœ
œ˜Jšœ(˜(Jšœ˜—Jšœ˜Jšœœ˜J˜J˜�———š ™š¡œ	œ˜Jšœœœ˜#Jšœœ˜˜J˜J˜ Jšœ˜J˜Jšœ˜J˜Jšœœ˜J˜—Jšœœ˜J˜J˜�—š¡	œœ˜Jšœ*ž˜EJ˜—š¡
œœ˜Jšœ)œ3œ˜‚J˜—š¡
œœ˜Jšœ/ž˜NJ˜Jšœœ˜Jšœœ˜Jšœœ˜ Jšœœ˜Jšœœ˜Jšœœ˜—š¡
œœœœœœ˜\Qšœ
œœ˜	šœ
œœ%˜8	šœ˜Jšœœ˜Jšœœ0˜J	šœ>˜@Qšœœ˜—Q˜—Qšœ
œ˜—šœ
œœ
œ˜&J˜šœ7œ
˜CJšœ#˜#J˜—J˜J˜Jšœ˜Jšœ	œ˜
J˜—J˜Qšœœž;˜T—	š¡œ˜.š¡œœ˜Jšœ3˜3šœ,˜.Jšœ>˜B—J˜—J˜J˜-M˜%J˜*	šœ
œœ˜šœ3˜3JšœG˜GJ˜—Q˜Q™)—Jšœœœ˜8Q˜^	šœœœ˜!Qšœ€˜„QšœW˜[—QšœG˜GMšœœœ'ž˜YJ˜Zšœœ˜
Jšœœ˜Jšœœœ˜9JšœH˜HJ˜Jšœ˜—Q˜—Q˜�—šœQ˜QJ˜�Jšœ˜——�…—����Ä.��ù��