G3dToolViewerImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bloomenthal, April 8, 1993 0:04 am PDT
Heckbert, August 9, 1988 5:51:51 pm PDT
Glassner, July 18, 1990 2:25:48 pm PDT
Shoemake, November 6, 1989 11:57:54 pm PST
3d Tool: Creation and Miscellany
DIRECTORY BasicTime, CedarProcess, Commander, CommanderOps, Controls, Cursors, FS, FileNames, G2dBasic, G3dBasic, G3dControl, G3dLight, G3dMatrix, G3dRender, G3dTimeTrees, G3dTool, G3dView, Icons, ImagerSample, IO, Process, ProcessProps, RawViewers, Real, Rope, TEditSelection, TiogaMenuOps, UnixSpawnTCP, ViewerClasses, ViewerOps, ViewerSpecs, ViewersWorldInstance;
G3dToolViewerImpl: CEDAR PROGRAM
IMPORTS BasicTime, CedarProcess, CommanderOps, Controls, Cursors, FileNames, FS, G3dControl, G3dLight, G3dMatrix, G3dRender, G3dTool, G3dView, Icons, ImagerSample, IO, Process, ProcessProps, RawViewers, Rope, TEditSelection, TiogaMenuOps, UnixSpawnTCP, ViewerOps, ViewerSpecs, ViewersWorldInstance
EXPORTS G3dTool
~ BEGIN
Types
ForkableProc:   TYPE ~ CedarProcess.ForkableProc;
ButtonList:    TYPE ~ Controls.ButtonList;
ClickProc:    TYPE ~ Controls.ClickProc;
ControlList:   TYPE ~ Controls.ControlList;
ControlProc:   TYPE ~ Controls.ControlProc;
ControlSizes:   TYPE ~ Controls.ControlSizes;
DestroyProc:   TYPE ~ Controls.DestroyProc;
GraphicsData:   TYPE ~ Controls.GraphicsData;
MouseProc:    TYPE ~ Controls.MouseProc;
Viewer:     TYPE ~ Controls.Viewer;
Box2d:     TYPE ~ G2dBasic.Box;
Pair:     TYPE ~ G3dBasic.Pair;
Triple:    TYPE ~ G3dBasic.Triple;
CameraControl:  TYPE ~ G3dControl.CameraControl;
Matrix:     TYPE ~ G3dMatrix.Matrix;
Viewport:   TYPE ~ G3dMatrix.Viewport;
Context3d:    TYPE ~ G3dRender.Context3d;
Client:     TYPE ~ G3dTool.Client;
NamedBuffer:   TYPE ~ G3dTool.NamedBuffer;
Operations:    TYPE ~ G3dTool.Operations;
ScreenSequence:  TYPE ~ G3dTool.ScreenSequence;
Tool:      TYPE ~ G3dTool.Tool;
ToolRep:   TYPE ~ G3dTool.ToolRep;
View:     TYPE ~ G3dTool.View;
ViewProc:    TYPE ~ G3dTool.ViewProc;
ViewRep:    TYPE ~ G3dTool.ViewRep;
ViewSequence:  TYPE ~ G3dTool.ViewSequence;
TTNode:    TYPE ~ G3dTimeTrees.Node;
Camera:     TYPE ~ G3dView.Camera;
CameraRep:   TYPE ~ G3dView.CameraRep;
SampleMap:   TYPE ~ ImagerSample.SampleMap;
ROPE:     TYPE ~ Rope.ROPE;
IconFlavor:   TYPE ~ ViewerClasses.IconFlavor;
Tool Creation and Control
defaultIcon: IconFlavor ¬ Icons.NewIconFromFile["G3dUser.icons", 0];
MakeTool: PUBLIC PROC [
name:    ROPE,
extraButtons:  ButtonList ¬ NIL,
extraControls: ControlList ¬ NIL,
controlSizes:  ControlSizes ¬ [],
ops:    Operations ¬ [],
client:    Client ¬ [],
nViews:   NAT ¬ 1,
noOpen:   BOOL ¬ FALSE,
useArcBalls:  BOOL ¬ TRUE,
arcBallSize:  INTEGER ¬ 136,
graphicsHeight: INTEGER ¬ 475,
icon:    IconFlavor ¬ document,
useExistingTool: BOOL ¬ FALSE,
camera:   CameraRep ¬ [[0.0, 2.0, 0.0], [], 1.0, 60.0],
background:  Triple ¬ [1.0, 1.0, 1.0],
typescriptHeight: INT ¬ 36,
toolSettings:  ToolRep ¬ []]
RETURNS [t: Tool]
~ {
IF useExistingTool AND activeTool # NIL
THEN {
t ¬ NEW[ToolRep ¬ activeTool^];
t.outerData ¬ Controls.OuterViewer[name: name, column: left, buttons: extraButtons, controls: extraControls, controlSizes: controlSizes, destroyProc: OuterDestroy, icon: icon];
IF client # [] THEN {
activeClients ¬ CONS[client, activeClients];
ViewerOps.AddProp[t.outerData.parent, $ToolClient, NEW[Client ¬ client]];
};
IF NOT noOpen THEN ViewerOps.OpenIcon[t.outerData.parent];
}
ELSE {
Enumerate: ViewerOps.EnumProc ~ {ViewerOps.PaintViewer[v, caption]};
NewButton: PROC [name: ROPE, proc: ClickProc, guarded: BOOL ¬ FALSE] ~ {
t.buttons ¬ CONS[Controls.ClickButton[name, proc, t, 0,,,,,, guarded], t.buttons];
};
RawViewers.SetColorWorld[ViewersWorldInstance.GetWorld[], $dense];
ViewerOps.EnumerateViewers[Enumerate]; -- fix up caption and icon colors
SetActiveTool[t ¬ NEW[ToolRep ¬ toolSettings]];
IF t.ipStrokeWidth = 2.0 THEN t.ipStrokeWidth ¬ 1.0; -- better default
(actually, add another field: strokeWidth, for use with screen display)
t.activeViews ¬ LIST[0];
t.ops ¬ ops;
t.client ¬ client;
IF t.cmd = NIL THEN
t.cmd ¬ NARROW[ProcessProps.GetProp[$CommanderHandle], Commander.Handle];
IF NOT Rope.IsEmpty[name] THEN t.name ¬ name;
t.camera ¬ G3dControl.InitCameraControl[
proc: G3dTool.Controller,
clientData: t,
fieldOfView: camera.fieldOfView,
proxyMode: eye,
scale: camera.scale,
move: camera.move,
rotate: camera.rotate];
G3dControl.SetArcBallSize[t.camera.arcBall, arcBallSize];
Context3dOk[t];
t.background ¬ background;
t.buttons ¬ extraButtons;
IF ops.io   THEN NewButton["IO",    G3dTool.IOOpsButton];
IF ops.lines  THEN NewButton["Draw",   G3dTool.DrawOpsButton];
IF ops.light  THEN NewButton["Light",   G3dTool.LightOpsButton];
IF ops.shape  THEN NewButton["Shapes",  G3dTool.ShapeOpsButton];
IF ops.render  THEN NewButton["Render",  G3dTool.RenderOpsButton];
IF ops.render  THEN NewButton["Render",  G3dTool.RenderButton];
IF ops.animation THEN NewButton["Animate",  G3dTool.AnimateOpsButton];
IF ops.scene  THEN NewButton["Scene",   G3dTool.SceneOpsButton];
IF ops.texture THEN NewButton["Texture",  G3dTool.TextureOpsButton];
IF ops.render THEN NewButton["Abort Render", G3dTool.AbortButton, TRUE];
IF ops.help  THEN NewButton["Help",   G3dTool.HelpButton];
IF ops.stop  THEN NewButton["STOP",   G3dTool.StopButton, TRUE];
t.controls ¬ G3dControl.AddCameraControl[extraControls, t.camera, useArcBalls];
IF nViews > 0 THEN {
t.outerData ¬ Controls.OuterViewer[
name: name,
column: left,
buttons: t.buttons,
controls: t.controls,
controlSizes: controlSizes,
typescriptHeight: typescriptHeight,
graphicsHeight: graphicsHeight,
mouseProc: G3dTool.Mouse,
destroyProc: Destroy,
drawProc: DoDraw,
noOpen: TRUE,
icon: IF icon # document THEN icon ELSE defaultIcon,
clientData: t];
t.outer ¬ t.outerData.parent;
t.outer.label ¬ name;
t.typescript ¬ t.outerData.typescript;
t.graphics ¬ t.outerData.graphics;
t.views ¬ MakeViews[t, nViews];
IF NOT noOpen THEN [] ¬ CedarProcess.Fork[OpenViewer, t];
Only upon return does the application have a Tool, which it needs if it is to draw.
Thus, we give the client some time with a Tool before drawing the viewer:
};
};
t.directory ¬ FileNames.CurrentWorkingDirectory[];
};
DoDraw: Controls.DrawProc ~ {
t: Tool ¬ NARROW[clientData];
v: G3dTool.View ¬ G3dTool.GetView[viewer, t];
G3dTool.Draw[context, viewer, whatChanged, v.camera.matrix, v.viewport, v, t, t];
};
OpenViewer: ForkableProc ~ {
Process.PauseMsec[250]; -- should be plenty
ViewerOps.OpenIcon[NARROW[data, Tool].outer];
};
OuterDestroy: Controls.DestroyProc ~ {
ref: REF ANY ¬ ViewerOps.FetchProp[viewer, $ToolClient];
IF ref # NIL THEN {
client: Client ¬ NARROW[ref, REF Client]^;
IF activeClients.first = client
THEN activeClients ¬ activeClients.rest
ELSE FOR c: LIST OF Client ¬ activeClients, c.rest WHILE c.rest # NIL DO
IF c.rest.first = client THEN {c.rest ¬ c.rest.rest; EXIT};
ENDLOOP;
};
};
Destroy: Controls.DestroyProc ~ {
t: Tool ¬ NARROW[clientData];
IF t = NIL THEN RETURN;
IF t.unixSpawn # NIL THEN UnixSpawnTCP.Kill[t.unixSpawn];
IF t.client.destroy # NIL THEN t.client.destroy[viewer, $ToolKilled, t.client.data];
IF t = activeTool THEN {
FOR c: LIST OF Client ¬ activeClients, c.rest WHILE c # NIL DO
IF c.first.destroy # NIL THEN c.first.destroy[viewer, $ToolKilled, c.first.data];
IF c.first.change # NIL THEN c.first.change[activeTool, NIL, c.first.data];
ENDLOOP;
activeTool ¬ NIL;
};
IF t.views # NIL THEN
FOR i: INTEGER IN [0..t.views.length) DO
FOR l: LIST OF G3dTool.NamedBuffer ¬ t.views[i].idBuffer, l.rest WHILE l # NIL DO
ImagerSample.ReleaseScratchMap[l.first.map];
ENDLOOP;
ENDLOOP;
};
Context3dOk: PUBLIC PROC [tool: Tool] ~ {
IF tool.context3d = NIL THEN {
tool.context3d ¬ G3dRender.Create[];
tool.lights ¬ G3dLight.AddLight[tool.lights, "Light1", [-.2, .2, .3], [.2, -.2, -.3]];
G3dTool.SetRenderView[tool.context3d, tool.camera];
};
};
activeTool, previousTool: Tool;
activeClients: LIST OF Client ¬ NIL;
SetActiveTool: PUBLIC PROC [tool: Tool] ~ {
IF tool = activeTool THEN RETURN;
previousTool ¬ activeTool;
activeTool ¬ tool;
FOR c: LIST OF Client ¬ activeClients, c.rest WHILE c # NIL DO
IF c.first.change # NIL THEN c.first.change[previousTool, activeTool, c.first.data];
ENDLOOP;
};
GetActiveTool: PUBLIC PROC RETURNS [Tool] ~ {RETURN[activeTool]};
GetActiveClients: PUBLIC PROC RETURNS [LIST OF Client] ~ {RETURN[activeClients]};
ToolBusy: PUBLIC PROC [tool: Tool] RETURNS [BOOL] ~ {
RETURN[tool.process # NIL AND CedarProcess.GetStatus[tool.process] = busy];
};
ForceFork: PUBLIC PROC [tool: Tool, proc: ForkableProc] ~ {
IF ToolBusy[tool] THEN Stop[tool, NIL, TRUE];
tool.process ¬ CedarProcess.Fork[proc, tool, [background, TRUE]]
};
MaybeFork: PUBLIC PROC [tool: Tool, proc: ForkableProc, data: REF ANY ¬ NIL]
RETURNS [forked: BOOL]
~ {
IF (forked ¬ NOT ToolBusy[tool])
THEN tool.process ¬
CedarProcess.Fork[proc, IF data = NIL THEN tool ELSE data, [background, TRUE]]
ELSE TSWrite[tool, "Tool is busy!\n"];
};
Stop: PUBLIC PROC [tool: Tool, reason: ROPE ¬ NIL, waitTilAborted: BOOL ¬ FALSE] ~ {
IF tool.unixSpawn # NIL THEN {
UnixSpawnTCP.Kill[tool.unixSpawn];
tool.unixSpawn ¬ NIL;
};
IF tool.process # NIL AND NOT tool.process.abortRequested THEN {
CedarProcess.Abort[tool.process];
IF waitTilAborted THEN [] ¬ CedarProcess.Join[tool.process];
IF tool.client.stop # NIL THEN tool.client.stop[tool.client.data, reason];
};
IF reason # NIL THEN TSWrite[tool, reason];
};
View Operations
MakeViews: PUBLIC PROC [tool: Tool, nViews: NAT] RETURNS [views: ViewSequence] ~ {
g: Viewer ¬ tool.graphics;
w: NAT ¬ SELECT g.column FROM
left => ViewerSpecs.openLeftWidth,
right => ViewerSpecs.openRightWidth,
ENDCASE => ViewerSpecs.colorScreenWidth;
views ¬ NEW[G3dTool.ViewSequenceRep[nViews]];
ViewerOps.AddProp[g, $Width, NEW[INTEGER ¬ w]]; -- redundant over ControlsOuter
ViewerOps.AddProp[g, $Height, NEW[INTEGER ¬ g.ch]];
FOR n: NAT IN [0..nViews) DO
MakeViewer: PROC [x, y, w, h: NAT] RETURNS [vwr: Viewer] ~ {
data: GraphicsData ¬ NEW[Controls.GraphicsDataRep ¬ NARROW[g.data, GraphicsData]^];
vwr ¬ data.viewer ¬ ViewerOps.CreateViewer[flavor: $Graphics, paint: FALSE, info: [wx: x, wy: y, ww: w, wh: h, data: data, scrollable: FALSE, border: nViews > 1, parent: g]];
ViewerOps.AddProp[vwr, $View, v]; -- property not there on initial opening!?
ViewerOps.AddProp[vwr, $CreatedInOuterViewer, $True]; -- avoid Controls.AdjustProc
};
v: View ¬ views[n] ¬ NEW[ViewRep];
v.screens ¬ NEW[G3dTool.ShapeScreensRep[10]];
v.tool ¬ tool;
v.index ¬ n;
IF nViews = 1
THEN v.viewer ¬ MakeViewer[0, 0, w, g.ch]
ELSE {
miniW: NAT ¬ w/MIN[nViews, 3];
v.viewer ¬ MakeViewer[n*miniW, 0, miniW, g.wh]
};
G3dControl.SaveState[tool.camera, v.camera ¬ NEW[CameraRep]];
ENDLOOP;
};
DoWithViews: PUBLIC PROC [tool: Tool, action: ViewProc, activeOnly: BOOL ¬ TRUE] ~ {
IF tool = NIL THEN RETURN;
IF activeOnly
THEN FOR l: LIST OF NAT ¬ tool.activeViews, l.rest WHILE l # NIL DO
IF NOT action[tool.views[l.first]] THEN EXIT;
ENDLOOP
ELSE FOR i: NAT IN [0..tool.views.length) DO
IF NOT action[tool.views[i]] THEN EXIT;
ENDLOOP;
};
GetFirstActiveView: PUBLIC PROC [tool: Tool] RETURNS [v: View] ~ {
IF tool # NIL AND tool.views # NIL AND tool.activeViews # NIL
THEN v ¬ tool.views[tool.activeViews.first];
};
GetView: PUBLIC PROC [viewer: Viewer, tool: Tool ¬ NIL] RETURNS [v: View] ~ {
IF viewer # NIL THEN {
ref: REF ¬ ViewerOps.FetchProp[viewer, $View];
IF ref # NIL THEN v ¬ NARROW[ref];
};
IF v = NIL AND tool # NIL THEN v ¬ tool.views[tool.activeViews.first];
IF viewer = NIL AND v # NIL THEN viewer ¬ v.viewer;
IF v # NIL THEN v.viewport ¬ G3dView.GetViewport[viewer];
};
GetViewTransform: PUBLIC PROC [
viewer: Viewer ¬ NIL,
tool: Tool ¬ NIL,
out: Matrix ¬ NIL]
RETURNS [m: Matrix]
~ {
v: View ¬ GetView[viewer, tool];
IF v # NIL THEN m ¬ G3dMatrix.CopyMatrix[v.camera.matrix, out];
};
GetBufferedSampleMap: PUBLIC PROC [view: View, id: ATOM ¬ $Frame]
RETURNS [map: SampleMap]
~ {
IF view # NIL THEN
FOR l: LIST OF NamedBuffer ¬ view.idBuffer, l.rest WHILE l # NIL DO
IF l.first.id = id THEN RETURN[l.first.map];
ENDLOOP;
};
SetBufferedSampleMap: PUBLIC PROC [map: SampleMap, view: View, id: ATOM ¬ $Frame]~ {
nb: NamedBuffer ¬ [id, map];
IF view = NIL THEN RETURN;
FOR l: LIST OF NamedBuffer ¬ view.idBuffer, l.rest WHILE l # NIL DO
IF l.first.id = id THEN {l.first ¬ nb; RETURN};
ENDLOOP;
view.idBuffer ¬ CONS[nb, view.idBuffer];
};
InvalidateViewScreens: PUBLIC PROC [view: View] ~ {
IF view # NIL AND view.screens # NIL THEN
FOR i: INTEGER IN [0..view.screens.length) DO
view.screens[i].screensValid ¬ FALSE;
ENDLOOP;
};
Stop, Finish, and Help
FinishButton: PUBLIC ClickProc ~ {
t: Tool ¬ NARROW[clientData];
IF t.cmd = NIL
THEN G3dTool.TSWrite[t, "No command handle!\n"]
ELSE {
prompt: ROPE ¬ "When done with a forked process, do: ";
IF t.command # NIL THEN prompt ¬ Rope.Cat[prompt, "(presently ", t.command, ") "];
t.command ¬ Controls.TypescriptRead[t.typescript, prompt];
};
};
Finish: PUBLIC PROC [tool: Tool, reason: ROPE] ~ {
IF tool.client.stop # NIL THEN tool.client.stop[tool.client.data, reason];
IF tool.command # NIL AND tool.cmd # NIL
THEN [] ¬ CommanderOps.DoCommand[tool.command, tool.cmd];
};
HelpButton: PUBLIC ClickProc ~ {OpenHelp["A tool for line drawings or shaded images"]};
StopButton: PUBLIC ClickProc ~ {Stop[NARROW[clientData], " . . . aborted\n"]};
Miscellany
doc: ROPE ¬ "Graphics3dDoc.tioga";
docProp: REF ROPE ¬ NEW[ROPE ¬ "Yo!"];
OpenHelp: PUBLIC PROC [key: ROPE] ~ {
name: ROPE ¬ doc;
vHelp: Viewer;
PropProc: ViewerOps.EnumProc
~ {
WITH ViewerOps.FetchProp[v, $G3dHelp] SELECT FROM
r: REF ROPE => IF r = docProp THEN {vHelp ¬ v; RETURN[FALSE]};
ENDCASE => RETURN[TRUE];
};
name ¬ FileNames.StripVersionNumber[FS.FileInfo[doc ! FS.Error => CONTINUE].fullFName];
vHelp ¬ ViewerOps.FindViewer[name];
IF vHelp = NIL THEN ViewerOps.EnumerateViewers[PropProc];
IF vHelp # NIL
THEN {IF vHelp.column # right THEN ViewerOps.ChangeColumn[vHelp, right]}
ELSE {
vHelp ¬ ViewerOps.CreateViewer[
flavor: $Text, paint: FALSE, info: [iconic: TRUE, column: right, openHeight: 140]];
TiogaMenuOps.Load[viewer: vHelp, fileName: doc];
ViewerOps.AddProp[viewer: vHelp, prop: $G3dHelp, val: docProp];
};
ViewerOps.SetOpenHeight[viewer: vHelp, clientHeight: 140];
ViewerOps.OpenIcon[icon: vHelp, bottom: FALSE, paint: FALSE]; -- must do Open before Top
ViewerOps.TopViewer[viewer: vHelp, paint: FALSE];
ViewerOps.ComputeColumn[right];
TEditSelection.FindRope[vHelp, key];
};
SetCursor: PUBLIC PROC [tool: Tool, waiting: BOOL] ~ {
IF waiting
THEN Cursors.SetCursor[tool.outer.class.cursor ¬ tool.graphics.class.cursor ¬ hourGlass]
ELSE Cursors.SetCursor[tool.outer.class.cursor ¬ tool.graphics.class.cursor ¬ textPointer];
};
StartTimer: PUBLIC PROC [tool: Tool] ~ {
tool.clock ¬ BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]];
};
ElapsedTime: PUBLIC PROC [tool: Tool] RETURNS [seconds: REAL] ~ {
RETURN[BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]]-tool.clock];
};
PrintElapsedTime: PUBLIC PROC [tool: Tool, elapsedTime: REAL ¬ 0.0, action: ROPE ¬ NIL] ~ {
IF action = NIL THEN action ¬ "elapsed";
IF elapsedTime = 0.0 THEN elapsedTime ¬ ElapsedTime[tool];
TSWrite[tool, IO.PutFR["%g time: %6.3f seconds.", IO.rope[action], IO.real[elapsedTime]]]
};
TSWrite: PUBLIC PROC [tool: Tool, message: ROPE] ~ {
IF tool.outer # NIL
THEN Controls.TypescriptWrite[tool.typescript, Rope.Concat[message, "\n"]]
ELSE IO.PutRope[tool.cmd.out, message];
};
END.