G3dRenderWithImagerImpl.mesa
Last Edited by: Crow, September 21, 1989 1:52:37 pm PDT
DIRECTORY Atom, G3dRender, G3dRenderWithImager, G3dClipXfmShade, G3dSortandDisplay, G3dVector, Imager, ImagerColor, ImagerColorFns, ImagerColorMap, ImagerFont, ImagerInterpress, ImagerPixel, IO, NamedColors, Real, Rope, ViewerClasses;
G3dRenderWithImagerImpl: CEDAR MONITOR
IMPORTS Atom, G3dRender, G3dClipXfmShade, G3dSortandDisplay, G3dVector, Imager, ImagerColor, ImagerColorFns, ImagerColorMap, ImagerFont, ImagerInterpress, ImagerPixel, IO, NamedColors, Real
EXPORTS G3dRenderWithImager
~ BEGIN
Types
Context:     TYPE ~ G3dRender.Context;
ContextProc:    TYPE ~ G3dRender.ContextProc;
ContextClass:    TYPE ~ G3dRender.ContextClass;
ImagerProc:    TYPE ~ G3dRender.ImagerProc;
ImagerProcRec:   TYPE ~ G3dRender.ImagerProcRec;
Triple:     TYPE ~ G3dRender.Triple;
RGB:      TYPE ~ G3dRender.RGB;
Pixel:      TYPE ~ G3dRender.Pixel;
Rectangle:    TYPE ~ G3dRender.Rectangle;
Pair:      TYPE ~ G3dRender.Pair;
PairSequence:   TYPE ~ G3dRender.PairSequence;
IntegerPair:    TYPE ~ G3dRender.IntegerPair;
IntegerPairSequence: TYPE ~ G3dRender.IntegerPairSequence;
IntRGB:     TYPE ~ RECORD [r, g, b: CARDINAL];
IntRGBSequence:   TYPE ~ RECORD [SEQUENCE length: NAT OF IntRGB];
NatSequence:    TYPE ~ G3dRender.NatSequence;
Patch:      TYPE ~ G3dRender.Patch;
PatchProc:    TYPE ~ G3dRender.PatchProc;
Shape:      TYPE ~ G3dRender.Shape;
RenderStyle:    TYPE ~ G3dRender.RenderStyle;
ClipState:     TYPE ~ G3dRender.ClipState;
ROPE:      TYPE ~ Rope.ROPE;
RopeProc:     TYPE ~ G3dRender.RopeProc;
PixelMap:     TYPE ~ ImagerPixel.PixelMap;
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: Context] ~ {
log: IO.STREAMNARROW[Atom.GetPropFromList[context.props, $Log]];
IF log # NIL THEN IO.Flush[log];
};
PairToScreen: PROC[context: 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: G3dSortandDisplay.MakeFrame,
loadBackground: ClearViewPort,
draw2DLine: Imager2DLine,
draw2DPolygon: Imager2DPoly,
draw2DRope: Draw2DRope,
displayPolygon: ImagerPolygon 
];
G3dRender.RegisterDisplayClass[ standardClass, $Bitmap ];
standardClass.displayType ← $ImagerGray;
G3dRender.RegisterDisplayClass[ standardClass, $ImagerGray ];
standardClass.displayType ← $ImagerDithered;
G3dRender.RegisterDisplayClass[ standardClass, $ImagerDithered ];
standardClass.displayType ← $ImagerFullClr;
G3dRender.RegisterDisplayClass[ standardClass, $ImagerFullClr ];
standardClass.displayType ← $Interpress;
standardClass.render ← MakePage;
standardClass.loadBackground ← InterpressLoadBackground;
G3dRender.RegisterDisplayClass[ 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
}
ELSE context.pixels ← ImagerPixel.MakePixelMap[NIL]; -- this keeps other procs happy
};
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[
G3dRender.PrependWorkingDirectory[
context,
G3dRender.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 G3dRender.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^ ];
G3dSortandDisplay.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: Context =>            -- background image
SIGNAL G3dRender.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: 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: G3dRender.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 ];
};
SetQuickLines: PUBLIC PROC[ imagerCtx: Imager.Context, color: Pixel] ~ {
Imager.SetStrokeWidth[ imagerCtx, 1.0 ];
Imager.SetColor[
imagerCtx,
ImagerColor.ColorFromRGB[[color[r]/255.0, color[g]/255.0, color[b]/255.0]]
];
};
DoQuickLine: PUBLIC PROC[ imagerCtx: Imager.Context, p1, p2: Pair] ~ {
Imager.MaskVector[ imagerCtx, p1, p2 ];
};
PolyDesc: TYPE ~ RECORD[ poly: PairSequence, color: Pixel];
Imager2DPoly: PUBLIC PROC[ context: Context, poly: 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: G3dRender.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: 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: Context, patch: REF Patch, data: REF ANYNIL ] RETURNS[REF Patch]
doProc: G3dRender.ImagerProc;
WITH patch.renderData.shadingClass.renderMethod SELECT FROM
style: REF RenderStyle => {
renderStyle: RenderStyle ← style^;
SELECT renderStyle FROM
lines, shadedLines => doProc ← DoOutline;
hiddenLines => doProc ← DoBoth;
linesWnormals => doProc ← DoBothAndNormals;
ENDCASE => doProc ← DoFill;
};
ENDCASE =>
SIGNAL G3dRender.Error[$Unimplemented, "Only REF RenderStyle works here"];
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: G3dRender.ImagerProc ~ {
patch: REF Patch ← NARROW[data];
patch.ctlPt[0].shade.er ← patch.ctlPt[0].shade.eg ← patch.ctlPt[0].shade.eb ← 1.0;
DoFill[ context, imagerCtx, data ];
patch.ctlPt[0].shade.er ← patch.ctlPt[0].shade.eg ← patch.ctlPt[0].shade.eb ← 0.0;
DoOutline[ context, imagerCtx, data ];
};
DoBothAndNormals: G3dRender.ImagerProc ~ {
patch: REF Patch ← NARROW[data];
IF patch.dir = back THEN DoNormals[ context, imagerCtx, data ];
patch.ctlPt[0].shade.er ← patch.ctlPt[0].shade.eg ← patch.ctlPt[0].shade.eb ← 1.0;
DoFill[ context, imagerCtx, data ];
patch.ctlPt[0].shade.er ← patch.ctlPt[0].shade.eg ← patch.ctlPt[0].shade.eb ← 0.0;
DoOutline[ context, imagerCtx, data ];
IF patch.dir # back THEN DoNormals[ context, imagerCtx, data ];
};
DoFill: G3dRender.ImagerProc ~ {
Path: Imager.PathProc ~ {
moveTo[[ patch.ctlPt[patch.nVtces-1].coord.sx, patch.ctlPt[patch.nVtces-1].coord.sy ]];
FOR i: NAT IN [0..patch.nVtces) DO
lineTo[[ patch.ctlPt[i].coord.sx, patch.ctlPt[i].coord.sy ]];
ENDLOOP;
};
patch: REF Patch ← NARROW[data];
Imager.SetColor[ imagerCtx, ImagerColor.ColorFromRGB[
[ patch.ctlPt[0].shade.er, patch.ctlPt[0].shade.eg, patch.ctlPt[0].shade.eb ]
] ];
Imager.MaskFill[imagerCtx, Path];
};
DoOutline: G3dRender.ImagerProc ~ {
Path: Imager.PathProc ~ {
moveTo[[ patch.ctlPt[0].coord.sx, patch.ctlPt[0].coord.sy ]];
FOR i: NAT IN [1..patch.nVtces) DO
lineTo[[ patch.ctlPt[i].coord.sx, patch.ctlPt[i].coord.sy ]];
ENDLOOP;
};
patch: REF Patch ← NARROW[data];
Imager.SetStrokeWidth[ imagerCtx, 1.0 ];
Imager.SetStrokeJoint[ imagerCtx, round ];
Imager.SetColor[ imagerCtx, ImagerColor.ColorFromRGB[
[ patch[0].shade.er, patch[0].shade.eg, patch[0].shade.eb ]
] ];
Imager.MaskStroke[context: imagerCtx, path: Path, closed: patch.type # $PolyLine];
};
DoNormals: G3dRender.ImagerProc ~ {
Path: Imager.PathProc ~ {
FOR i: NAT IN [0..patch.nVtces) DO
ep2: Triple ← G3dClipXfmShade.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 RopeProc ~ {
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: G3dRender.ImagerProc ~ {
PROC[ context: 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: 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: Context, fileName: ROPE] ~ {
Makes an interpress master, given an active 3D Context
ipCtxt: Context ← G3dRender.Create[];
G3dRender.CopyContextData[ipCtxt, context];
ipCtxt.props ← Atom.PutPropOnList[ipCtxt.props, $OutputFile, fileName];
G3dRender.LoadDisplayClass[ipCtxt, $Interpress];
ipCtxt.viewer ← NIL;     -- just in case
ipCtxt.displayInValid ← TRUE;
ipCtxt.class.render[ipCtxt];    -- should call MakePage
};
Init[];
END.