SurfaceViewerImpl.mesa
James Rauen, August 26, 1986 2:14:07 am PDT
Last edited by: James Rauen January 18, 1988 3:14:59 pm PST
DIRECTORY
Atom USING [PutPropOnList],
Basics USING [Comparison],
CADTypes USING [Scad, ScadSequence, VariableRec, VisibleMask, VisibleMaskSequence],
Convert USING [CardFromRope, Error],
Geometry3dBasics USING [Triple, TripleSequence, TripleSequenceRep],
Geometry3dVector USING [Add, Cross, Mul],
Imager USING [Rectangle],
ImagerColor USING [RGB],
IO USING [BreakProc, Error, GetRope, GetTokenRope, int, PutFR, RIS, STREAM],
LightingModels USING [Model, PointSourceSequence],
Rope USING [Compare, IsPrefix, ROPE],
SceneUtilities USING [AddShape, AddShapeAt, CreateDefaultContext, DeleteShape, Hide, NameBackgroundColor, PlaceShape, Reveal, SetColor, SetFaceted, SetLight, SetViewPort, ShapeInstance],
ShadingModels USING [ShadingSequence],
SurfaceRender USING [MakeFrame],
SurfaceTracer,
SurfaceViewer,
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, Convert, IO, Rope, SurfaceRender, SurfaceTracer, ThreeDBasics, ThreeDHacks, ThreeDViewer, SceneUtilities, Geometry3dVector, ViewerOps
EXPORTS SurfaceViewer
~ BEGIN
Constants and type declarations
initialPosition: Geometry3dBasics.Triple ← [2.0, -10.0, 3.0];
initialForward: Geometry3dBasics.Triple ← [-2.0, 10.0, -3.0];
initialUp: Geometry3dBasics.Triple ← [0., 0., 1.];
maxNumberOfSurfaces: NAT ~ 3;
Error: PUBLIC ERROR[why: ATOM] = CODE;
CreateSurfaceViewer: PUBLIC PROC[] RETURNS[sviewer: REF SurfaceViewer.SurfaceViewer] ~ BEGIN
Note: This has some resemblance to Init in ThreeDDemoImpl
workingDirectory: Rope.ROPE;
Create the sviewer and initialize its slots.
sviewer ← NEW[SurfaceViewer.SurfaceViewer];
sviewer.surfaces ← NEW[SurfaceViewer.SurfaceSeq[maxNumberOfSurfaces]];
sviewer.numberOfSurfaces ← 0;
sviewer.nextID ← 0;
sviewer.context3d ← SceneUtilities.CreateDefaultContext[];
Initialize the context's graphics
SceneUtilities.NameBackgroundColor[sviewer.context3d, "Darkish Blue"];
SceneUtilities.SetLight [sviewer.context3d, "Initial", initialPosition];
ThreeDBasics.SetView [
context: sviewer.context3d,
eyePoint: initialPosition,
ptOfInterest: Geometry3dVector.Add[initialPosition, initialForward],
upDirection: initialUp];
Register the $FatPoint and $FatSeg classes
ThreeDHacks.RegisterNewClasses[sviewer.context3d];
Store away the working directory (motivated by ThreeDDemoImpl)
sviewer.context3d.props ← Atom.PutPropOnList[sviewer.context3d.props, $WDir, workingDirectory];
Create the actual viewer, move it to the color display, and open it.
ThreeDViewer.MakeViewer[
context: sviewer.context3d,
bannerName: "ThreeDWorld -- Algebraic Surface Viewer",
menu: NIL,
mouseAction: NIL];
IF sviewer.context3d.viewer.column # color THEN ViewerOps.ChangeColumn[sviewer.context3d.viewer, color];
IF sviewer.context3d.viewer.iconic THEN ViewerOps.OpenIcon[icon: sviewer.context3d.viewer];
Draw it
DrawFrame[sviewer];
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;
GetThreeDContext: PUBLIC PROC[sviewer: REF SurfaceViewer.SurfaceViewer] RETURNS[context3d: REF ThreeDBasics.Context] ~ BEGIN
context3d ← sviewer.context3d;
END;
LoadSurface: PUBLIC PROC[surface: CADTypes.Scad, sviewer: REF SurfaceViewer.SurfaceViewer] RETURNS[id: NAT] ~ BEGIN
index, newID: NAT;
Assign the surface the next available position (index) in the surface sequence. If there is no room, signal so.
IF sviewer.numberOfSurfaces >= maxNumberOfSurfaces THEN ERROR Error[$AllFilledUp];
index ← sviewer.numberOfSurfaces;
sviewer.numberOfSurfaces ← sviewer.numberOfSurfaces + 1;
Assign the surface an ID number.
newID ← sviewer.nextID;
sviewer.nextID ← sviewer.nextID + 1;
Construct a SurfaceViewer record for the surface.
sviewer.surfaces[index] ← [
scad: surface,
id: newID,
mask: NEW[CADTypes.VisibleMask[surface.cells.nCells]]
];
FOR i: NAT IN [0..surface.cells.nCells) DO
sviewer.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[sviewer.context3d, newCell];
SceneUtilities.PlaceShape[sviewer.context3d, cellName, [0, 0, 0]];
SceneUtilities.SetColor[sviewer.context3d, cellName, [0.1, 0.9, 0.9]];
SceneUtilities.SetFaceted[sviewer.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[sviewer.context3d, newCell];
SceneUtilities.PlaceShape[sviewer.context3d, cellName, [0, 0, 0]];
SceneUtilities.SetColor[sviewer.context3d, cellName, [0.1, 0.9, 0.9]];
SceneUtilities.SetFaceted[sviewer.context3d, cellName];
9/22/86 - check for empty shape
IF surface.cells[cell].vertices.nVertices < 2 THEN
SceneUtilities.Hide[sviewer.context3d, cellName];
END;
Two-cell.
2 => BEGIN
newCell ← ThreeDHacks.MakeTwoCell[
name: cellName,
vertices: surface.cells[cell].vertices,
triangles: surface.cells[cell].polygons];
SceneUtilities.AddShape[sviewer.context3d, newCell];
SceneUtilities.PlaceShape[sviewer.context3d, cellName, [0, 0, 0]];
SceneUtilities.SetColor[sviewer.context3d, cellName, [0.1, 0.9, 0.9]];
SceneUtilities.SetFaceted[sviewer.context3d, cellName];
9/22/86 - check for empty shape
IF surface.cells[cell].polygons.nTriangles = 0 THEN
SceneUtilities.Hide[sviewer.context3d, cellName];
END;
ENDCASE;
Do the next cell.
ENDLOOP;
DrawFrame[sviewer];
RETURN[newID];
END;
MaskSurface: PUBLIC PROC[id: NAT, mask: REF CADTypes.VisibleMask, sviewer: REF SurfaceViewer.SurfaceViewer] ~ BEGIN
index: NAT ← IndexFromID[id, sviewer];  --verify ID
FOR i: NAT IN [0..sviewer.surfaces[index].scad.cells.nCells) DO
nameToMask: Rope.ROPE ← MakeShapeName[
id,
sviewer.surfaces[index].scad.cells[i].indexX,
sviewer.surfaces[index].scad.cells[i].indexY,
sviewer.surfaces[index].scad.cells[i].indexZ
];
IF mask[i] THEN SceneUtilities.Reveal[sviewer.context3d, nameToMask]
ELSE SceneUtilities.Hide[sviewer.context3d, nameToMask];
ENDLOOP;
sviewer.surfaces[index].mask ← mask;
DrawFrame[sviewer];
END;
HideSurface: PUBLIC PROC[id: NAT, sviewer: REF SurfaceViewer.SurfaceViewer] ~ BEGIN
index: NAT ← IndexFromID[id, sviewer];  --verify ID
namesToHide: LIST OF Rope.ROPE ← ShapesMatchingID[id, sviewer];
UNTIL namesToHide = NIL DO
SceneUtilities.Hide[sviewer.context3d, namesToHide.first];
namesToHide ← namesToHide.rest;
ENDLOOP;
DrawFrame[sviewer];
END;
UnHideSurface: PUBLIC PROC[id: NAT, sviewer: REF SurfaceViewer.SurfaceViewer] ~ BEGIN
index: NAT ← IndexFromID[id, sviewer];  --verify ID
namesToHide: LIST OF Rope.ROPE ← ShapesMatchingID[id, sviewer];
UNTIL namesToHide = NIL DO
SceneUtilities.Reveal[sviewer.context3d, namesToHide.first];
namesToHide ← namesToHide.rest;
ENDLOOP;
DrawFrame[sviewer];
END;
DeleteSurface: PUBLIC PROC[id: NAT, sviewer: REF SurfaceViewer.SurfaceViewer] ~ BEGIN
index: NAT ← IndexFromID[id, sviewer];
namesToDelete: LIST OF Rope.ROPE ← ShapesMatchingID[id, sviewer];
FOR i: NAT IN [index..sviewer.numberOfSurfaces - 1) DO
sviewer.surfaces[i] ← sviewer.surfaces[i + 1];
ENDLOOP;
sviewer.numberOfSurfaces ← sviewer.numberOfSurfaces - 1;
UNTIL namesToDelete = NIL DO
SceneUtilities.DeleteShape[sviewer.context3d, namesToDelete.first];
namesToDelete ← namesToDelete.rest;
ENDLOOP;
DrawFrame[sviewer];
END;
FlushSurfaces: PUBLIC PROC[sviewer: REF SurfaceViewer.SurfaceViewer] ~ BEGIN
sviewer.numberOfSurfaces ← 0;
DrawFrame[sviewer];
END;
IndexFromID: PROC [id: NAT, sviewer: REF SurfaceViewer.SurfaceViewer] 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..sviewer.numberOfSurfaces) DO
IF sviewer.surfaces[i].id = id THEN {index ← i; found ← TRUE};
ENDLOOP;
IF ~found THEN ERROR Error[$InvalidID];
END;
ShapesMatchingID: PROC [id: NAT, sviewer: REF SurfaceViewer.SurfaceViewer] 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..sviewer.context3d.shapes.length) DO
name: Rope.ROPE ← sviewer.context3d.shapes[i].name;
IF Rope.IsPrefix["Cell", name] 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 [
"CellS%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 ← IO.GetRope[inStream, 4, FALSE];
IF Rope.Compare[token, "Cell"] # equal THEN ERROR ParseError;
[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, sviewer: REF SurfaceViewer.SurfaceViewer] ~ 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 ← sviewer.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] ← sviewer.surfaces[i].scad;
colors[i] ← [
surfaceColor: sviewer.surfaces[i].scad.color,
ambientReflectionCoefficient: 1.0,
diffuseReflectionCoefficient: 0.4,
specularReflectionCoefficient: 0.8];
masks[i] ← sviewer.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;
DrawFrame: PUBLIC PROC [sviewer: REF SurfaceViewer.SurfaceViewer] ~ BEGIN
ThreeDSurfaces.EnableDisplay[];
ThreeDBasics.SetView[      -- get new screen dimensions into transformations
context:  sviewer.context3d,
eyePoint:  sviewer.context3d.eyePoint,
ptOfInterest: sviewer.context3d.ptOfInterest,
fieldOfView: sviewer.context3d.fieldOfView,
rollAngle: sviewer.context3d.rollAngle,
upDirection: sviewer.context3d.upDirection,
hitherLimit: sviewer.context3d.hitherLimit,
yonLimit:  sviewer.context3d.yonLimit
];
SceneUtilities.SetLight[
context:  sviewer.context3d,
name:   "Torch",
position:  sviewer.context3d.eyePoint,
color:   [1, 1, 1]
];
SurfaceRender.MakeFrame[sviewer.context3d]; 
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;
END.