SurfaceViewerImpl.mesa
James Rauen, August 26, 1986 2:14:07 am PDT
DIRECTORY
Atom USING [PutPropOnList],
Basics USING [Comparison],
CADTypes USING [Scad, ScadSequence, VariableRec, VisibleMask, VisibleMaskSequence],
Commander USING [CommandProc, Register],
Convert USING [CardFromRope, Error],
Imager USING [Context, Rectangle],
ImagerColor USING [RGB],
IO USING [BreakProc, Error, GetTokenRope, int, PutFR, RIS, STREAM],
LightingModels USING [Model, PointSourceSequence],
QuickViewer,
QuickViewer USING [BuildViewer, QuickView, Reset],
Rope USING [Compare, ROPE],
ShadingModels USING [ShadingSequence],
SurfaceTracer,
SurfaceViewer USING [],
ThreeDBasics,
ThreeDHacks USING [RegisterNewClasses, MakeFatPoint, MakeFatSeg, MakeTwoCell],
ThreeDMisc,
ThreeDScenes,
ThreeDSurfaces,
Vector3d USING [Add, Cross, Mul, Triple, TripleSequence, TripleSequenceRec],
ViewerClasses USING [Viewer],
ViewerOps;
SurfaceViewerImpl: CEDAR PROGRAM
IMPORTS Atom, Commander, Convert, IO, QuickViewer, Rope, SurfaceTracer, ThreeDHacks, ThreeDMisc, ThreeDScenes, Vector3d, 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: Vector3d.Triple;
forward: Vector3d.Triple;
up: Vector3d.Triple;
viewHasChanged: BOOLEAN;
surfaces: REF SurfaceSeq;
numberOfSurfaces: NAT;
maxNumberOfSurfaces: NAT ~ 3;
nextID: NAT;
Error: PUBLIC ERROR[why: ATOM] = CODE;
CreateSurfaceViewer: PUBLIC PROC[] ~ BEGIN
Declarations
workingDirectory: Rope.ROPE;
Start out with no surfaces.
surfaces ← NEW[SurfaceSeq[maxNumberOfSurfaces]];
numberOfSurfaces ← 0;
nextID ← 0;
Don't want ReDraw to do anything during the intitialization sequence.
noReDraw ← TRUE;
Build a color QuickViewer
context3d ← ThreeDScenes.Create[];
context3d.renderMode ← context3d.preferredRenderMode ← $Dorado24;
9/25/86 - unclear why this is needed; should happen automatically
context3d.viewer ← window3d ← QuickViewer.BuildViewer[
menuLabels: NIL,
reDrawProc: ReDraw,
quitProc: ShutDown,
buttonProc: MenuHit,
viewerTitle: "Algebraic Surface Viewer"];
Move it to the color display
IF window3d.outer.column # color THEN ViewerOps.ChangeColumn[window3d.outer, color];
IF window3d.outer.iconic THEN ViewerOps.OpenIcon[icon: window3d.outer];
Save the working directory (stolen from ThreeDDemoImpl.mesa)
context3d.props ← Atom.PutPropOnList[context3d.props, $WDir, workingDirectory];
Initiialize the 3d context.
position ← [2.0, -10.0, 3.0];
forward ← [-2, 10, -3];
up ← [0, 0,1];
ThreeDMisc.SetBackground [context3d, "Darkish Blue"];
[] ← ThreeDScenes.SetLight [context3d, "Initial", [-100., -200., 50.] ];
[] ← ThreeDScenes.SetLight [context3d, "Initial", position];
ThreeDHacks.RegisterNewClasses[context3d];
ThreeDScenes.SetView [
context: context3d,
eyePoint: position,
ptOfInterest: Vector3d.Add[position, forward],
upDirection: up];
noReDraw ← FALSE;
viewHasChanged ← TRUE;
MakeFrame[];
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;
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 ThreeDScenes.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]];
ThreeDScenes.PlaceShape[newCell, [0, 0, 0]];
context3d.shapes ← ThreeDScenes.AddShape[context3d.shapes, newCell];
ThreeDMisc.SetFacetedColor[context3d, cellName, [0.1, 0.9, 0.9]];
END;
One-cell.
1 => BEGIN
vertexSequence: Vector3d.TripleSequence ← NEW[Vector3d.TripleSequenceRec[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];
ThreeDScenes.PlaceShape[newCell, [0, 0, 0]];
context3d.shapes ← ThreeDScenes.AddShape[context3d.shapes, newCell];
ThreeDMisc.SetFacetedColor[context3d, cellName, [0.1, 0.9, 0.9]];
9/22/86 - check for empty shape
IF surface.cells[cell].vertices.nVertices < 2 THEN
ThreeDMisc.Hide[context3d, cellName];
END;
Two-cell.
2 => BEGIN
newCell ← ThreeDHacks.MakeTwoCell[
name: cellName,
vertices: surface.cells[cell].vertices,
triangles: surface.cells[cell].polygons];
ThreeDScenes.PlaceShape[newCell, [0, 0, 0]];
context3d.shapes ← ThreeDScenes.AddShape[context3d.shapes, newCell];
ThreeDMisc.SetFacetedColor[context3d, cellName, [0.1, 0.9, 0.9]];
9/22/86 - check for empty shape
IF surface.cells[cell].polygons.nTriangles = 0 THEN
ThreeDMisc.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 ThreeDMisc.Reveal[context3d, nameToMask]
ELSE ThreeDMisc.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
ThreeDMisc.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
ThreeDMisc.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
context3d.shapes ← ThreeDScenes.DeleteShape[context3d.shapes, 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: Vector3d.Triple] ~ BEGIN
position ← newPosition;
ThreeDScenes.SetView [
context: context3d,
eyePoint: position,
ptOfInterest: Vector3d.Add[position, forward],
upDirection: up];
viewHasChanged ← TRUE;
END;
ChangeOrientation: PUBLIC PROC[newForward, newUp: Vector3d.Triple] ~ BEGIN
forward ← newForward;
up ← newUp;
ThreeDScenes.SetView [
context: context3d,
eyePoint: position,
ptOfInterest: Vector3d.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[];
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: Vector3d.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 ← Vector3d.Mul[forward, -5];
right ← Vector3d.Mul[Vector3d.Cross[forward, up], 2];
above ← Vector3d.Mul[up, 2];
behindAndRight ← Vector3d.Add[Vector3d.Add[behind, right], above];
pointSourcePosition ← Vector3d.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: Vector3d.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[];
ThreeDScenes.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
];
[] ← ThreeDScenes.SetLight[
context:  context3d,
name:   "Torch",
position:  context3d.eyePoint,
color:   [1, 1, 1]
];
ThreeDMisc.MakeFrame[context3d]; 
viewHasChanged ← FALSE;
END;
SetViewPort: PROC [size: Imager.Rectangle] ~ BEGIN
ThreeDScenes.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;
ThreeDScenes.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.