RenderWithImagerImpl.mesa
Last Edited by: Crow, October 30, 1987 3:27:12 pm PST
DIRECTORY
Atom     USING [ DottedPair, PropList, GetPropFromList, PutPropOnList ],
IO      USING [ Flush, STREAM ] ,
Rope     USING [ ROPE ] ,
Real     USING [ Float, Round ],
ViewerClasses  USING [ Viewer ],
Imager    USING [ Context, Font, MaskFill, MaskRectangle, MaskStroke,
         MaskVector, PathProc,
         Rectangle, SetColor, SetFont, SetStrokeJoint, SetStrokeWidth,
         SetXY, ShowRope ],
ImagerColor   USING [ ColorFromRGB, RGB ],
ImagerColorFns  USING [ RGBFromHSL ],
ImagerColorMap  USING [ GetGamma, SetStandardColorMap, SetStandardGrayMap ],
ImagerFont   USING [ Find, Scale ],
ImagerInterpress  USING [ Close, Create, DoPage, Ref ],
NamedColors   USING [ RopeToHSL ],
G3dVector USING [ Add ],
ThreeDBasics  USING [ ClipState, Context, ContextClass, ContextProc, Create, Error,
         ImagerProc, ImagerProcRec, IntegerPair, IntegerPairSequence,
         LoadDisplayType, NatSequence, Pair, PairSequence, Patch,
         PatchProc, Pixel, Quad, Rectangle, RegisterDisplayType, RGB,
         ShadingValue, ShapeInstance, Triple, Vertex ],
SceneUtilities  USING [ CopyContextData, PrependWorkingDirectory, TackOnExtension ],
ShapeUtilities  USING [ XfmPtToDisplay ],
SurfaceRender  USING [ MakeFrame, ShowShapes ],
RenderWithImager USING [ ];
RenderWithImagerImpl: CEDAR MONITOR
IMPORTS Atom, G3dVector, Imager, ImagerColor, ImagerColorFns, ImagerColorMap, ImagerFont, ImagerInterpress, IO, NamedColors, Real, SceneUtilities, ShapeUtilities, SurfaceRender, ThreeDBasics
EXPORTS RenderWithImager
~ BEGIN
Types
Context: TYPE ~ ThreeDBasics.Context;
ContextProc: TYPE ~ ThreeDBasics.ContextProc;
ContextClass: TYPE ~ ThreeDBasics.ContextClass;
ImagerProc: TYPE ~ ThreeDBasics.ImagerProc;
ImagerProcRec: TYPE ~ ThreeDBasics.ImagerProcRec;
Triple: TYPE ~ ThreeDBasics.Triple;
RGB: TYPE ~ ThreeDBasics.RGB;
Pixel: TYPE ~ ThreeDBasics.Pixel;
Rectangle: TYPE ~ ThreeDBasics.Rectangle;
Pair: TYPE ~ ThreeDBasics.Pair;
PairSequence: TYPE ~ ThreeDBasics.PairSequence;
IntegerPair: TYPE ~ ThreeDBasics.IntegerPair;
IntegerPairSequence: TYPE ~ ThreeDBasics.IntegerPairSequence;
IntRGB: TYPE ~ RECORD[ r, g, b: CARDINAL];
IntRGBSequence: TYPE ~RECORD [ SEQUENCE length: NAT OF IntRGB ];
NatSequence: TYPE ~ ThreeDBasics.NatSequence;
Patch: TYPE ~ ThreeDBasics.Patch;
PatchProc: TYPE ~ ThreeDBasics.PatchProc;
ShapeInstance: TYPE ~ ThreeDBasics.ShapeInstance;
ClipState: TYPE ~ ThreeDBasics.ClipState;
ROPE: TYPE ~ Rope.ROPE;
Utility Procedures
Ceiling: PROC[number: REAL] RETURNS[result: INTEGER] ~ {
result ← Real.Round[number];
IF result < number THEN result ← result + 1;
};
Floor: PROC[ in: REAL ] RETURNS[ out: INTEGER ] ~ {
out ← Real.Round[in];
IF Real.Float[out] > in THEN out ← out - 1;
};
FlushLog: PUBLIC PROC [context: REF Context] ~ {
log: IO.STREAMNARROW[Atom.GetPropFromList[context.props, $Log]];
IF log # NIL THEN IO.Flush[log];
};
PairToScreen: PROC[context: REF Context, p: Pair] RETURNS[outP: Pair] ~ {
outP.x ← context.ndcToPixels.scaleX * p.x + context.ndcToPixels.addX;
outP.y ← context.ndcToPixels.scaleY * p.y + context.ndcToPixels.addY;
};
Initialize Standard Displays
Init: PROC[] ~ {       -- register Imager-based drawing types
standardClass: ContextClass ← [
displayType: $Bitmap,
setUpDisplayType: SetUp,
validateDisplay: ValidateDisplay,
render: SurfaceRender.MakeFrame,
loadBackground: ClearViewPort,
draw2DLine: Imager2DLine,
draw2DPolygon: Imager2DPoly,
draw2DRope: Draw2DRope,
displayPolygon: ImagerPolygon 
];
ThreeDBasics.RegisterDisplayType[ standardClass, $Bitmap ];
standardClass.displayType ← $ImagerGray;
ThreeDBasics.RegisterDisplayType[ standardClass, $ImagerGray ];
standardClass.displayType ← $ImagerDithered;
ThreeDBasics.RegisterDisplayType[ standardClass, $ImagerDithered ];
standardClass.displayType ← $ImagerFullClr;
ThreeDBasics.RegisterDisplayType[ standardClass, $ImagerFullClr ];
standardClass.displayType ← $Interpress;
standardClass.render ← MakePage;
standardClass.loadBackground ← InterpressLoadBackground;
ThreeDBasics.RegisterDisplayType[ standardClass, $Interpress ];
};
SetUp: ContextProc ~{   -- null procedure, Viewers & Imager do it all
SELECT context.class.displayType FROM
$ImagerGray => ImagerColorMap.SetStandardGrayMap[
context.terminal,
ImagerColorMap.GetGamma[context.terminal]
];
$ImagerDithered => ImagerColorMap.SetStandardColorMap[
context.terminal,
ImagerColorMap.GetGamma[context.terminal]
];
ENDCASE;   -- don't do anything for $Bitmap, $ImagerFullClr, $Interpress
};
ValidateDisplay: ContextProc ~{   -- update viewPort, etc.
IF context.viewer # NIL THEN {
IF Atom.GetPropFromList[ context.displayProps, $ViewerAdjusted ] # NIL
THEN context.class.drawInViewer[context, NIL];
context.class.updateViewer[context];
context.pixelAspectRatio ← 1.0;
context.stopMe^ ← FALSE;    -- make sure stop button is released
};
};
InterpressLoadBackground: ContextProc ~{       
No backgrounds with Interpress output. It is assumed that backgrounds can be added with other tools more efficiently
};
MakePage: ContextProc ~ {
fileName: Rope.ROPEIF Atom.GetPropFromList[context.props, $OutputFile] # NIL
THEN NARROW[Atom.GetPropFromList[context.props, $OutputFile]]
ELSE "ThreeDImage.interpress";
ipRef: ImagerInterpress.Ref ← ImagerInterpress.Create[
SceneUtilities.PrependWorkingDirectory[
context,
SceneUtilities.TackOnExtension[fileName, "interpress"]
]
];
scale: REAL ← 1.0 / (72.0 * 39.37);       -- meters per pt.
Action: PROC[imagerCtx: Imager.Context] ~ {
rect: Rectangle;
bkgrdClr: REF RGBNARROW[
Atom.GetPropFromList[context.props, $BackGround]
! ANY => SIGNAL ThreeDBasics.Error[[$MisMatch,
           "Interpress needs constant background"]]
];
context.props ← Atom.PutPropOnList[context.props, $ImagerCtx, imagerCtx];
Imager.SetColor[ imagerCtx, ImagerColor.ColorFromRGB[bkgrdClr^] ];
IF context.viewPort = NIL     -- set default resolution
THEN context.viewPort ← NEW[Imager.Rectangle ← [0, 0, 1024, 768]];
rect ← context.viewPort^;
context.ndcToPixels ← [       -- render with origin at bottom left
rect.w-1.0, rect.h-1.0, REAL[context.depthResolution-1], -- scaleX, scaleY, scaleZ
rect.x, rect.y, 0.0             -- addX, addY, addZ
];
Imager.MaskRectangle[ imagerCtx, context.viewPort^ ];
SurfaceRender.ShowShapes[ context ];
};
ImagerInterpress.DoPage[ipRef, Action, scale];   -- imager has to provide imagerCtx
ImagerInterpress.Close[ipRef];
};
Low-level drawing
ClearViewPort: PUBLIC ContextProc ~ {
context.class.drawInViewer[ context, NEW[ImagerProcRec ← [DoClear, NIL] ] ];
};
DoClear: ImagerProc ~ {
clr: RGB;
WITH Atom.GetPropFromList[context.props, $BackGround] SELECT FROM
bkgrdClr: REF RGB => clr ← bkgrdClr^;         -- constant color
bkgrdImage: REF Context =>            -- background image
SIGNAL ThreeDBasics.Error[[$MisMatch, "No background images with imager"]];
ENDCASE => clr ← [ 0.0, 0.0, 0.0 ];           -- NIL, clear to black
Imager.SetColor[ imagerCtx, ImagerColor.ColorFromRGB[clr] ];
Imager.MaskRectangle[ imagerCtx, context.viewPort^ ];
};
LineDesc: TYPE ~ RECORD[ p1, p2: Pair, color: Pixel];
Imager2DLine: PUBLIC PROC[ context: REF Context, p1, p2: Pair, color: Pixel] ~ {
lineData: REF LineDesc ← NEW[ LineDesc ← [p1, p2, color] ];
IF context.class.displayType = $Interpress
THEN DoLine[
context, NARROW[Atom.GetPropFromList[context.props, $ImagerCtx]], lineData
]
ELSE context.class.drawInViewer[ context, NEW[ImagerProcRec ← [DoLine, lineData]] ];
};
DoLine: ThreeDBasics.ImagerProc ~ {
lineData: REF LineDesc ← NARROW[data];
color: Pixel ← lineData.color;
p1: Pair ← PairToScreen[context, lineData.p1]; p2: Pair ← PairToScreen[context, lineData.p2];
Imager.SetStrokeWidth[ imagerCtx, 1.0 ];
Imager.SetColor[
imagerCtx,
ImagerColor.ColorFromRGB[[color[r]/255.0, color[g]/255.0, color[b]/255.0]]
];
Imager.MaskVector[ imagerCtx, p1, p2 ];
};
PolyDesc: TYPE ~ RECORD[ poly: REF PairSequence, color: Pixel];
Imager2DPoly: PUBLIC PROC[ context: REF Context, poly: REF PairSequence,
         color: Pixel] ~ {
polyData: REF PolyDesc ← NEW[ PolyDesc ← [poly, color] ];
IF context.class.displayType = $Interpress
THEN DoPoly[
context, NARROW[Atom.GetPropFromList[context.props, $ImagerCtx]], polyData
]
ELSE context.class.drawInViewer[ context, NEW[ImagerProcRec ← [DoPoly, polyData]] ];
};
DoPoly: ThreeDBasics.ImagerProc ~ {
Path: Imager.PathProc ~ {
moveTo[ PairToScreen[context, poly[poly.length-1]] ];
FOR i: NAT IN [0..poly.length) DO
lineTo[ PairToScreen[context, poly[i]] ];
ENDLOOP;
};
polyData: REF PolyDesc ← NARROW[data];
poly: REF PairSequence ← polyData.poly;
color: Pixel ← polyData.color;
Imager.SetColor[
imagerCtx,
ImagerColor.ColorFromRGB[[color[r]/255.0, color[g]/255.0, color[b]/255.0]]
];
Imager.MaskFill[imagerCtx, Path];
};
ImagerPolygon: PUBLIC PatchProc ~ {
PROC[ context: REF Context, patch: REF Patch, data: REF ANYNIL ] RETURNS[REF Patch]
shape: REF ShapeInstance ← NARROW[ Atom.GetPropFromList[patch.props, $Shape] ];
doProc: ThreeDBasics.ImagerProc ← SELECT shape.shadingClass.shadingType FROM
$Lines, $ShadedLines => DoOutline,
$HiddenLines => DoBoth,
$NormaledLines => DoBothAndNormals,
ENDCASE => DoFill;
IF context.class.displayType = $Interpress
THEN doProc[context, NARROW[ Atom.GetPropFromList[context.props, $ImagerCtx]], patch]
ELSE context.class.drawInViewer[ context, NEW[ImagerProcRec ← [doProc, patch]] ];
RETURN[NIL];
};
DoBoth: ThreeDBasics.ImagerProc ~ {
patch: REF Patch ← NARROW[data];
patch.vtx[0].shade.er ← patch.vtx[0].shade.eg ← patch.vtx[0].shade.eb ← 1.0;
DoFill[ context, imagerCtx, data ];
patch.vtx[0].shade.er ← patch.vtx[0].shade.eg ← patch.vtx[0].shade.eb ← 0.0;
DoOutline[ context, imagerCtx, data ];
};
DoBothAndNormals: ThreeDBasics.ImagerProc ~ {
patch: REF Patch ← NARROW[data];
IF patch.dir = back THEN DoNormals[ context, imagerCtx, data ];
patch.vtx[0].shade.er ← patch.vtx[0].shade.eg ← patch.vtx[0].shade.eb ← 1.0;
DoFill[ context, imagerCtx, data ];
patch.vtx[0].shade.er ← patch.vtx[0].shade.eg ← patch.vtx[0].shade.eb ← 0.0;
DoOutline[ context, imagerCtx, data ];
IF patch.dir # back THEN DoNormals[ context, imagerCtx, data ];
};
DoFill: ThreeDBasics.ImagerProc ~ {
Path: Imager.PathProc ~ {
moveTo[[ patch.vtx[patch.nVtces-1].coord.sx, patch.vtx[patch.nVtces-1].coord.sy ]];
FOR i: NAT IN [0..patch.nVtces) DO
lineTo[[ patch.vtx[i].coord.sx, patch.vtx[i].coord.sy ]];
ENDLOOP;
};
patch: REF Patch ← NARROW[data];
Imager.SetColor[ imagerCtx, ImagerColor.ColorFromRGB[
[ patch.vtx[0].shade.er, patch.vtx[0].shade.eg, patch.vtx[0].shade.eb ]
] ];
Imager.MaskFill[imagerCtx, Path];
};
DoOutline: ThreeDBasics.ImagerProc ~ {
Path: Imager.PathProc ~ {
moveTo[[ patch.vtx[0].coord.sx, patch.vtx[0].coord.sy ]];
FOR i: NAT IN [1..patch.nVtces) DO
lineTo[[ patch.vtx[i].coord.sx, patch.vtx[i].coord.sy ]];
ENDLOOP;
};
patch: REF Patch ← NARROW[data];
Imager.SetStrokeWidth[ imagerCtx, 1.0 ];
Imager.SetStrokeJoint[ imagerCtx, round ];
Imager.SetColor[ imagerCtx, ImagerColor.ColorFromRGB[
[ patch.vtx[0].shade.er, patch.vtx[0].shade.eg, patch.vtx[0].shade.eb ]
] ];
Imager.MaskStroke[context: imagerCtx, path: Path, closed: patch.type # $PolyLine];
};
DoNormals: ThreeDBasics.ImagerProc ~ {
Path: Imager.PathProc ~ {
FOR i: NAT IN [0..patch.nVtces) DO
ep2: Triple ← ShapeUtilities.XfmPtToDisplay[ context, G3dVector.Add[
[patch[i].coord.ex, patch[i].coord.ey, patch[i].coord.ez],
[patch[i].shade.exn/2, patch[i].shade.eyn/2, patch[i].shade.ezn/2]
] ];
p2: IntegerPair ← [ Real.Round[ep2.x], Real.Round[ep2.y] ];
moveTo[[ patch[i].coord.sx, patch[i].coord.sy ]];
lineTo[ [p2.x, p2.y] ];
ENDLOOP;
};
patch: REF Patch ← NARROW[data];
Imager.SetStrokeWidth[ imagerCtx, 1.0 ];
Imager.SetStrokeJoint[ imagerCtx, round ];
Imager.SetColor[ imagerCtx, ImagerColor.ColorFromRGB[ [ 0.0, 0.0, 0.0 ] ] ];
Imager.MaskStroke[context: imagerCtx, path: Path, closed: patch.type # $PolyLine];
};
RopeDesc: TYPE ~ RECORD[rope: ROPE, position: Pair, color: Pixel, size: REAL, font: ROPE];
Draw2DRope: PUBLIC PROC[context: REF Context, rope: ROPE, position: Pair,
       color: Pixel ← [255,255,255,0,0], size: REAL ← 20, font: ROPENIL] ~ {
Put a string of characters on the screen
ropeData: REF RopeDesc ← NEW[ RopeDesc ← [rope, position, color, size, font] ];
IF context.class.displayType = $Interpress
THEN DoRope[
context: context,
imagerCtx: NARROW[Atom.GetPropFromList[context.props, $ImagerCtx]],
data: ropeData
]
ELSE context.class.drawInViewer[ context, NEW[ImagerProcRec ← [DoRope, ropeData]] ];
};
DoRope: ThreeDBasics.ImagerProc ~ {
PROC[ context: REF Context, imagerCtx: Imager.Context, data: REF ANY ]
ropeData: REF RopeDesc ← NARROW[data];
color: Pixel ← ropeData.color;
theFont: Imager.Font;
Imager.SetColor[
imagerCtx,
ImagerColor.ColorFromRGB[[color[r]/255.0, color[g]/255.0, color[b]/255.0]]
];
IF ropeData.font = NIL THEN ropeData.font ← "Xerox/Pressfonts/TimesRoman-MRR";
theFont ← ImagerFont.Find[ropeData.font];
theFont ← ImagerFont.Scale[theFont, ropeData.size];
Imager.SetFont[imagerCtx, theFont];
Imager.SetXY[ imagerCtx, PairToScreen[context, ropeData.position] ];
Imager.ShowRope[imagerCtx, ropeData.rope];
};
Colors
RGBtoPixelValue: PROC[context: REF Context, clr: RGB]
      RETURNS[ value: Pixel ] ~ {
value[r] ← Real.Round[255.0 * clr.R];
value[g] ← Real.Round[255.0 * clr.G];
value[b] ← Real.Round[255.0 * clr.B];
value[a] ← 0;
value[z] ← 0;
};
SetNamedColor: PROC [imagerCtx: Imager.Context, color: Rope.ROPE] ~ {
clr: RGB ← ImagerColorFns.RGBFromHSL[ NamedColors.RopeToHSL[color] ];
Imager.SetColor[ imagerCtx, ImagerColor.ColorFromRGB[clr] ];
};
Frame Generation and Animation
MakeInterpressPage: PUBLIC PROC[context: REF Context, fileName: Rope.ROPE] ~ {
Makes an interpress master, given an active 3D Context
ipCtxt: REF Context ← ThreeDBasics.Create[];
SceneUtilities.CopyContextData[ipCtxt, context];
ipCtxt.props ← Atom.PutPropOnList[ipCtxt.props, $OutputFile, fileName];
ThreeDBasics.LoadDisplayType[ipCtxt, $Interpress];
ipCtxt.class.render[ipCtxt];    -- should call MakePage
};
Init[];
END.