SurfaceViewerImpl.mesa
James Rauen, August 26, 1986 2:14:07 am PDT
Last edited by: James Rauen January 13, 1988 6:17:51 pm PST
DIRECTORY
Atom USING [PutPropOnList],
Basics USING [Comparison],
CADTypes USING [Scad, ScadSequence, VariableRec, VisibleMask, VisibleMaskSequence],
Commander USING [CommandProc, Register],
Convert USING [CardFromRope, Error],
Geometry3dBasics USING [Triple, TripleSequence, TripleSequenceRep],
Geometry3dVector USING [Add, Cross, Mul],
Imager USING [Rectangle],
ImagerColor USING [RGB],
IO USING [BreakProc, Error, GetTokenRope, int, PutFR, RIS, STREAM],
LightingModels USING [Model, PointSourceSequence],
Rope USING [Compare, ROPE],
SceneUtilities USING [AddShape, AddShapeAt, CreateDefaultContext, DeleteShape, Hide, NameBackgroundColor, PlaceShape, Reveal, SetColor, SetFaceted, SetLight, SetViewPort, ShapeInstance],
ShadingModels USING [ShadingSequence],
SurfaceRender USING [MakeFrame],
SurfaceTracer,
SurfaceViewer USING [],
ThreeDBasics USING [Context, SetView],
ThreeDHacks USING [RegisterNewClasses, MakeFatPoint, MakeFatSeg, MakeTwoCell],
ThreeDViewer USING [ButtonDesc, MakeViewer],
ViewerClasses USING [Column, Viewer],
ViewerOps USING [ChangeColumn, OpenIcon];
SurfaceViewerImpl: CEDAR PROGRAM
IMPORTS Atom, Commander, Convert, IO, Rope, SurfaceRender, SurfaceTracer, ThreeDBasics, ThreeDHacks, ThreeDViewer, SceneUtilities, Geometry3dVector, ViewerOps
EXPORTS SurfaceViewer
~ BEGIN
Type declarations
SurfaceRec: TYPE ~ RECORD [
scad: CADTypes.Scad,
id: NAT,
mask: REF CADTypes.VisibleMask];
SurfaceSeq: TYPE ~ RECORD [
surfaces: SEQUENCE length: NAT OF SurfaceRec];
Global variables
context3d: REF ThreeDBasics.Context;
window3d: REF QuickViewer.QuickView;
noReDraw: BOOLEAN;
position: Geometry3dBasics.Triple;
forward: Geometry3dBasics.Triple;
up: Geometry3dBasics.Triple;
viewHasChanged: BOOLEAN;
surfaces: REF SurfaceSeq;
numberOfSurfaces: NAT;
maxNumberOfSurfaces: NAT ~ 3;
nextID: NAT;
Error: PUBLIC ERROR[why: ATOM] = CODE;
CreateSurfaceViewer: PUBLIC PROC[] ~ BEGIN
Note: This has some resemblance to Init in ThreeDDemoImpl
Declarations
workingDirectory: Rope.ROPE;
Menu Buttons
menuButtons: LIST OF ThreeDViewer.ButtonDesc ~ LIST [
[ proc: LoadPicture,
choices: LIST[
 [$Default, "Default Picture"], [NIL, NIL], [NIL, NIL],
 [NIL, NIL], [NIL, NIL], [NIL, NIL],
 [NIL, NIL], [NIL, NIL], [NIL, NIL]],
label: "Load Picture",
purpose: "Load a default picture"
]
];
Don't want ReDraw to do anything during the intitialization sequence.
noReDraw ← TRUE;
Initialize the global variables.
surfaces ← NEW[SurfaceSeq[maxNumberOfSurfaces]];
numberOfSurfaces ← 0;
nextID ← 0;
position ← [2.0, -10.0, 3.0];
forward ← [-2, 10, -3];
up ← [0., 0., 1.];
Initialize the context's graphics
context3d ← SceneUtilities.CreateDefaultContext[];
SceneUtilities.NameBackgroundColor[context3d, "Darkish Blue"];
SceneUtilities.SetLight [context3d, "Initial", position];
ThreeDBasics.SetView [
context: context3d,
eyePoint: position,
ptOfInterest: Geometry3dVector.Add[position, forward],
upDirection: up];
Register the $FatPoint and $FatSeg classes
ThreeDHacks.RegisterNewClasses[context3d];
Store away the working directory (motivated by ThreeDDemoImpl)
context3d.props ← Atom.PutPropOnList[context3d.props, $WDir, workingDirectory];
Create the viewer and move it to the color display
ThreeDViewer.MakeViewer[
context: context3d,
bannerName: "New and Improved Algebraic Surface Viewer",
menu: menuButtons,
mouseAction: NIL];
IF context3d.viewer.column # color THEN ViewerOps.ChangeColumn[context3d.viewer, color];
IF context3d.viewer.iconic THEN ViewerOps.OpenIcon[icon: context3d.viewer];
Draw it
noReDraw ← FALSE;
viewHasChanged ← TRUE;
SurfaceRender.MakeFrame[context3d];
END;
Private procedures for CreateSurfaceViewer
ReDraw: PROC [imagerCtx: Imager.Context, toDo: REF ANY] ~ BEGIN
Declarations
imagerContext: Imager.Context ← imagerCtx; -- 9/12/86 - arg name change in new QV
newContext: REF ThreeDScenes.Context;
oldContext: Imager.Context;
Don't do anything during Init sequence.
IF noReDraw THEN RETURN;
ReDraw according to the new parameters.
oldContext ← ThreeDMisc.GetImagerContext[context3d];
IF oldContext # NIL THEN imagerContext ← oldContext;
newContext ← ThreeDScenes.GetFromImagerContext[imagerContext, context3d.alphaBuffer, context3d.depthBuffer];
context3d.display ← newContext.display;
context3d.alphaBuffer ← newContext.alphaBuffer;
context3d.depthBuffer ← newContext.depthBuffer;
context3d.renderMode ← newContext.renderMode;
context3d.viewPort ← newContext.viewPort;
SetViewPort[context3d.viewPort];
ThreeDScenes.SetWindow[context3d, ThreeDScenes.WindowFromViewPort[context3d.viewPort]];
ENABLE UNWIND => NULL;
ThreeDScenes.DisplayFromImagerContext[context3d, imagerCtx];
IF context3d.depthBuffer THEN ThreeDScenes.AddDepthBuffer[context3d];
IF context3d.alphaBuffer THEN ThreeDScenes.AddAlphaBuffer[context3d];
viewHasChanged ← TRUE;
MakeFrame[];
END;
ShutDown: PROC [] ~ BEGIN
END;
LoadPicture: PROC[context: REF ThreeDBasics.Context, key: ATOM] ~ BEGIN
SceneUtilities.AddShapeAt[context, "Glass", "[Cedar]<CedarChest7.0>ThreeDWorld>ChampagneGlass.shape"];
SurfaceRender.MakeFrame[context];
END;
MenuHit: PROC [bttn, choice: ATOM, x, y: REAL] ~ BEGIN
END;
LoadSurface: PUBLIC PROC[surface: CADTypes.Scad] RETURNS[id: NAT] ~ BEGIN
Declarations
index, newID: NAT;
Assign the surface the next available position (index) in the surface sequence. If there is no room, signal so.
IF numberOfSurfaces >= maxNumberOfSurfaces THEN ERROR Error[$AllFilledUp];
index ← numberOfSurfaces;
numberOfSurfaces ← numberOfSurfaces + 1;
Assign the surface an ID number.
newID ← nextID;
nextID ← nextID + 1;
Construct a SurfaceViewer record for the surface.
surfaces[index] ← [
scad: surface,
id: newID,
mask: NEW[CADTypes.VisibleMask[surface.cells.nCells]]
];
FOR i: NAT IN [0..surface.cells.nCells) DO
surfaces[index].mask[i] ← TRUE;
ENDLOOP;
Add each cell as a different shape to the 3d context.
FOR cell: NAT IN [0..surface.cells.nCells) DO
newCell: REF SceneUtilities.ShapeInstance;
cellName: Rope.ROPE ← MakeShapeName[
newID,
surface.cells[cell].indexX,
surface.cells[cell].indexY,
surface.cells[cell].indexZ];
SELECT surface.cells[cell].dimension FROM
Zero-cell.
0 => BEGIN
newCell ← ThreeDHacks.MakeFatPoint[
name: cellName,
position: surface.cells[cell].vertices[0]];
SceneUtilities.AddShape[context3d, newCell];
SceneUtilities.PlaceShape[context3d, cellName, [0, 0, 0]];
SceneUtilities.SetColor[context3d, cellName, [0.1, 0.9, 0.9]];
SceneUtilities.SetFaceted[context3d, cellName];
END;
One-cell.
1 => BEGIN
vertexSequence: Geometry3dBasics.TripleSequence ← NEW[Geometry3dBasics.TripleSequenceRep[surface.cells[cell].vertices.nVertices]];
vertexSequence.length ← surface.cells[cell].vertices.nVertices;
FOR i: NAT IN [0..surface.cells[cell].vertices.nVertices) DO
vertexSequence[i] ← surface.cells[cell].vertices[i]
ENDLOOP;
newCell ← ThreeDHacks.MakeFatSeg[
name: cellName,
points: vertexSequence];
SceneUtilities.AddShape[context3d, newCell];
SceneUtilities.PlaceShape[context3d, cellName, [0, 0, 0]];
SceneUtilities.SetColor[context3d, cellName, [0.1, 0.9, 0.9]];
SceneUtilities.SetFaceted[context3d, cellName];
9/22/86 - check for empty shape
IF surface.cells[cell].vertices.nVertices < 2 THEN
SceneUtilities.Hide[context3d, cellName];
END;
Two-cell.
2 => BEGIN
newCell ← ThreeDHacks.MakeTwoCell[
name: cellName,
vertices: surface.cells[cell].vertices,
triangles: surface.cells[cell].polygons];
SceneUtilities.AddShape[context3d, newCell];
SceneUtilities.PlaceShape[context3d, cellName, [0, 0, 0]];
SceneUtilities.SetColor[context3d, cellName, [0.1, 0.9, 0.9]];
SceneUtilities.SetFaceted[context3d, cellName];
9/22/86 - check for empty shape
IF surface.cells[cell].polygons.nTriangles = 0 THEN
SceneUtilities.Hide[context3d, cellName];
END;
ENDCASE;
Do the next cell.
ENDLOOP;
viewHasChanged ← TRUE;
MakeFrame[];
RETURN[newID];
END;
MaskSurface: PUBLIC PROC[id: NAT, mask: REF CADTypes.VisibleMask] ~ BEGIN
index: NAT ← IndexFromID[id];  --verify ID
FOR i: NAT IN [0..surfaces[index].scad.cells.nCells) DO
nameToMask: Rope.ROPE ← MakeShapeName[
id,
surfaces[index].scad.cells[i].indexX,
surfaces[index].scad.cells[i].indexY,
surfaces[index].scad.cells[i].indexZ
];
IF mask[i] THEN SceneUtilities.Reveal[context3d, nameToMask]
ELSE SceneUtilities.Hide[context3d, nameToMask];
ENDLOOP;
surfaces[index].mask ← mask;
viewHasChanged ← TRUE;
MakeFrame[];
END;
HideSurface: PUBLIC PROC[id: NAT] ~ BEGIN
index: NAT ← IndexFromID[id];  --verify ID
namesToHide: LIST OF Rope.ROPE ← ShapesMatchingID[id];
UNTIL namesToHide = NIL DO
SceneUtilities.Hide[context3d, namesToHide.first];
namesToHide ← namesToHide.rest;
ENDLOOP;
viewHasChanged ← TRUE;
MakeFrame[];
END;
UnHideSurface: PUBLIC PROC[id: NAT] ~ BEGIN
index: NAT ← IndexFromID[id];  --verify ID
namesToHide: LIST OF Rope.ROPE ← ShapesMatchingID[id];
UNTIL namesToHide = NIL DO
SceneUtilities.Reveal[context3d, namesToHide.first];
namesToHide ← namesToHide.rest;
ENDLOOP;
viewHasChanged ← TRUE;
MakeFrame[];
END;
DeleteSurface: PUBLIC PROC[id: NAT] ~ BEGIN
index: NAT ← IndexFromID[id];
namesToDelete: LIST OF Rope.ROPE ← ShapesMatchingID[id];
FOR i: NAT IN [index..numberOfSurfaces - 1) DO
surfaces[i] ← surfaces[i + 1];
ENDLOOP;
numberOfSurfaces ← numberOfSurfaces - 1;
UNTIL namesToDelete = NIL DO
SceneUtilities.DeleteShape[context3d, namesToDelete.first];
namesToDelete ← namesToDelete.rest;
ENDLOOP;
viewHasChanged ← TRUE;
MakeFrame[];
END;
FlushSurfaces: PUBLIC PROC[] ~ BEGIN
numberOfSurfaces ← 0;
viewHasChanged ← TRUE;
MakeFrame[];
END;
IndexFromID: PROC [id: NAT] RETURNS [index: NAT] ~ BEGIN
Looks to see if there is a surface in the surface sequence with the given id. If there is, returns its index in the sequence. If not, raises Error[$InvalidID].
found: BOOLEANFALSE;
FOR i: NAT IN [0..numberOfSurfaces) DO
IF surfaces[i].id = id THEN {index ← i; found ← TRUE};
ENDLOOP;
IF ~found THEN ERROR Error[$InvalidID];
END;
ShapesMatchingID: PROC [id: NAT] RETURNS [shapes: LIST OF Rope.ROPE] ~ BEGIN
Returns a list of all the shape names in the 3d context that match id.
names: LIST OF Rope.ROPENIL;
FOR i: NAT IN [0..context3d.shapes.length) DO
name: Rope.ROPE ← context3d.shapes[i].name;
IF Rope.Compare[name, "Initial"] # equal AND Rope.Compare[name, "Torch"] # equal THEN BEGIN
surfaceID: NAT;
[id: surfaceID] ← ParseShapeName[name];
IF surfaceID = id THEN names ← CONS[name, names];
END;
ENDLOOP;
RETURN[names];
END;
MakeShapeName: PROC [id, indexX, indexY, indexZ: NAT] RETURNS [name: Rope.ROPE] ~ BEGIN
name ← IO.PutFR [
"S%gX%gY%gZ%g",
IO.int[id],
IO.int[indexX],
IO.int[indexY],
IO.int[indexZ]];
RETURN;
END;
ParseError: ERROR = CODE;
ParseShapeName: PROC [name: Rope.ROPE] RETURNS [id, indexX, indexY, indexZ: NAT] ~ BEGIN
BreakProc: IO.BreakProc ~ BEGIN
RETURN [SELECT char FROM
IN ['0..'9] => other,
ENDCASE => break];
END;
inStream: IO.STREAMIO.RIS[name];
token: Rope.ROPE;
[token: token] ← IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError];
IF Rope.Compare[token, "S"] # equal THEN ERROR ParseError;
[token: token] ← IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError];
id ← Convert.CardFromRope[token ! Convert.Error => ERROR ParseError];
[token: token] ← IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError];
IF Rope.Compare[token, "X"] # equal THEN ERROR ParseError;
[token: token] ← IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError];
indexX ← Convert.CardFromRope[token ! Convert.Error => ERROR ParseError];
[token: token] ← IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError];
IF Rope.Compare[token, "Y"] # equal THEN ERROR ParseError;
[token: token] ← IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError];
indexY ← Convert.CardFromRope[token ! Convert.Error => ERROR ParseError];
[token: token] ← IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError];
IF Rope.Compare[token, "Z"] # equal THEN ERROR ParseError;
[token: token] ← IO.GetTokenRope[inStream, BreakProc ! IO.Error => ERROR ParseError];
indexZ ← Convert.CardFromRope[token ! Convert.Error => ERROR ParseError];
END;
ChangePosition: PUBLIC PROC[newPosition: Geometry3dBasics.Triple] ~ BEGIN
position ← newPosition;
ThreeDBasics.SetView [
context: context3d,
eyePoint: position,
ptOfInterest: Geometry3dVector.Add[position, forward],
upDirection: up];
viewHasChanged ← TRUE;
END;
ChangeOrientation: PUBLIC PROC[newForward, newUp: Geometry3dBasics.Triple] ~ BEGIN
forward ← newForward;
up ← newUp;
ThreeDBasics.SetView [
context: context3d,
eyePoint: position,
ptOfInterest: Geometry3dVector.Add[position, forward],
upDirection: up];
viewHasChanged ← TRUE;
END;
ChangeScope: PUBLIC PROC[newScope: REAL] ~ BEGIN
viewHasChanged ← TRUE;
END;
DrawFrame: PUBLIC PROC [] ~ BEGIN
IF viewHasChanged THEN MakeFrame[];
MakeFrame[];
END;
InvokeRayTracer: PUBLIC PROC[variables: CADTypes.VariableRec, filename: Rope.ROPE, pixelsU, pixelsV: NAT] ~ BEGIN
Declarations.
scads: REF CADTypes.ScadSequence;
colors: REF ShadingModels.ShadingSequence;
masks: REF CADTypes.VisibleMaskSequence;
behind, right, behindAndRight, above, pointSourcePosition: Geometry3dBasics.Triple;
pointSources: REF LightingModels.PointSourceSequence;
lighting: LightingModels.Model;
Assemble the sequences needed by the ray tracer.
n: NAT ← numberOfSurfaces;
scads ← NEW[CADTypes.ScadSequence[n]];
colors ← NEW[ShadingModels.ShadingSequence[n]];
masks ← NEW[CADTypes.VisibleMaskSequence[n]];
FOR i: NAT IN [0..n) DO
scads[i] ← surfaces[i].scad;
colors[i] ← [
surfaceColor: surfaces[i].scad.color,
ambientReflectionCoefficient: 1.0,
diffuseReflectionCoefficient: 0.4,
specularReflectionCoefficient: 0.8];
masks[i] ← surfaces[i].mask;
ENDLOOP;
Assemble a lighting model.
behind ← Geometry3dVector.Mul[forward, -5];
right ← Geometry3dVector.Mul[Geometry3dVector.Cross[forward, up], 2];
above ← Geometry3dVector.Mul[up, 2];
behindAndRight ← Geometry3dVector.Add[Geometry3dVector.Add[behind, right], above];
pointSourcePosition ← Geometry3dVector.Add[position, behindAndRight];
pointSources ← NEW[LightingModels.PointSourceSequence[1]];
pointSources[0] ← [position: pointSourcePosition, color: [1, 1, 1]];
lighting ← [background: [1, 1, 1], ambientSource: [0.2, 0.2, 0.2], pointSources: pointSources];
Invoke the ray tracer.
SurfaceTracer.TraceCells[
surfaces: scads,
variables: variables,
colors: colors,
masks: masks,
screenCenter: position,
screenU: up,
screenV: Geometry3dVector.Cross[up, forward],
pixelsU: pixelsU,
pixelsV: pixelsV,
lightingModel: lighting,
filename: filename];
END;
Private procedures
Reset: PROC [] ~ BEGIN
Shamelessly stolen from ThreeDDemoImpl
imagerContext: Imager.Context ← ThreeDMisc.GetImagerContext[context3d];
QuickViewer.Reset[imagerContext];
ThreeDSurfaces.EnableDisplay[];
ThreeDMisc.SetViewPortFromImager[context3d, imagerContext];
ThreeDScenes.SetWindow[context3d, ThreeDScenes.WindowFromViewPort[context3d.viewPort]];
context3d.shapes ← context3d.lights;   -- delete all the objects
ThreeDScenes.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
DoReDraw: PROCEDURE [imagerCtx: Imager.Context] ~ { ReDraw[imagerCtx, NIL]; };
context3d.stopMe ← FALSE;       -- get stop flag unstuck, if necessary
ThreeDScenes.SetWindow[context3d,ThreeDScenes.WindowFromViewPort[context3d.viewPort]];
context3d.shapes ← context3d.lights;   -- delete all the objects
ThreeDScenes.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
QuickViewer.DrawInViewer[window3d, DoReDraw];   -- gets a new imager context
END;
MakeFrame: PROC [] ~ BEGIN
ThreeDSurfaces.EnableDisplay[];
ThreeDBasics.SetView[      -- get new screen dimensions into transformations
context:  context3d,
eyePoint:  context3d.eyePoint,
ptOfInterest: context3d.ptOfInterest,
fieldOfView: context3d.fieldOfView,
rollAngle: context3d.rollAngle,
upDirection: context3d.upDirection,
hitherLimit: context3d.hitherLimit,
yonLimit:  context3d.yonLimit
];
SceneUtilities.SetLight[
context:  context3d,
name:   "Torch",
position:  context3d.eyePoint,
color:   [1, 1, 1]
];
SurfaceRender.MakeFrame[context3d]; 
viewHasChanged ← FALSE;
END;
SetViewPort: PROC [size: Imager.Rectangle] ~ BEGIN
SceneUtilities.SetViewPort[context3d, size];   -- set clippers etc.
FOR i: NAT IN [0..context3d.shapes.length) DO
context3d.shapes[i].vtcesInValid ← TRUE;
context3d.shapes[i].shadingInValid ← TRUE;
ENDLOOP;
ThreeDBasics.SetView[      -- get new screen dimensions into transformations
context:  context3d,
eyePoint:  context3d.eyePoint,
ptOfInterest: context3d.ptOfInterest,
fieldOfView: context3d.fieldOfView,
rollAngle: context3d.rollAngle,
upDirection: context3d.upDirection,
hitherLimit: context3d.hitherLimit,
yonLimit:  context3d.yonLimit
];
END;
Foo: Commander.CommandProc ~ BEGIN
CreateSurfaceViewer[];
END;
Commander.Register["MakeSurfaceViewer", Foo];
END.