<> <> 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 <> 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; <> 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.STREAM _ NARROW[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; }; <> 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 ~{ <> }; MakePage: ContextProc ~ { fileName: Rope.ROPE _ IF 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 RGB _ NARROW[ 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]; }; <> 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 ~ { <> 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: ROPE _ NIL] ~ { <> 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 ~ { <> 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]; }; <> 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] ]; }; <> MakeInterpressPage: PUBLIC PROC[context: REF Context, fileName: Rope.ROPE] ~ { <> 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.