<> <> <> <> DIRECTORY Atom USING [ GetPropFromList, PutPropOnList, PropList, RemPropFromList ], Commander USING [ PrependWorkingDir ], G3dVector USING [ Length, Sub ], G3dMatrix USING [ Identity, MakeRotate, Mul ], ImagerColorMap USING [ SetStandardColorMap ], Rope USING [ Length, ROPE, Substr ], Real USING [ Fix, Float ], RealFns USING [ SqRt ], RuntimeError USING [ BoundsFault ], ThreeDBasics USING [ Box, Context, ContextClass, ErrorDesc, Patch, Rectangle, RGB, ShadingClass, ShapeClass, ShapeInstance, Triple, Xfm3DRep ]; ThreeDBasicsImpl: CEDAR PROGRAM IMPORTS Atom, Commander, G3dMatrix, G3dVector, ImagerColorMap, Real, RealFns, Rope, RuntimeError EXPORTS ThreeDBasics ~ BEGIN <> Error: PUBLIC SIGNAL [reason: ThreeDBasics.ErrorDesc] = CODE; Context: TYPE ~ ThreeDBasics.Context; RGB: TYPE ~ ThreeDBasics.RGB; Triple: TYPE ~ ThreeDBasics.Triple; Xfm3DRep: TYPE ~ ThreeDBasics.Xfm3DRep; Rectangle: TYPE ~ ThreeDBasics.Rectangle; Box: TYPE ~ ThreeDBasics.Box; Patch: TYPE ~ ThreeDBasics.Patch; ShapeInstance: TYPE ~ ThreeDBasics.ShapeInstance; ContextClass: TYPE ~ ThreeDBasics.ContextClass; ShadingClass: TYPE ~ ThreeDBasics.ShadingClass; ShapeClass: TYPE ~ ThreeDBasics.ShapeClass; <> registeredDisplayTypes: Atom.PropList _ NIL; -- keeps active display types registeredSurfaceTypes: Atom.PropList _ NIL; -- keeps active surface types registeredShadingClasses: Atom.PropList _ NIL; -- keeps active shading classes <> Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { RETURN[number * number]; }; RectangleFromBox: PUBLIC PROC[box: Box] RETURNS[ Rectangle] ~ { RETURN [[ x: Real.Float[box.min.f], y: Real.Float[box.min.s], w: Real.Float[box.max.f - box.min.f], h: Real.Float[box.max.s - box.min.s] ]]; }; BoxFromRectangle: PUBLIC PROC[ rect: Rectangle ] RETURNS[ Box ] ~ { RETURN [[ min: [ f: Real.Fix[rect.x], s: Real.Fix[rect.y] ], max: [ f: Real.Fix[rect.x + rect.w], s: Real.Fix[rect.y + rect.h] ] ]]; }; IntersectRectangles: PUBLIC PROC[ rect1, rect2: Rectangle ] RETURNS[ Rectangle ] ~ { intersection: Rectangle; intersection.x _ MAX[rect1.x, rect2.x]; intersection.y _ MAX[rect1.y, rect2.y]; intersection.w _ MIN[rect1.w - (intersection.x - rect1.x), rect2.w - (intersection.x - rect2.x)]; intersection.h _ MIN[rect1.h - (intersection.y - rect1.y), rect2.h - (intersection.y - rect2.y)]; RETURN [intersection]; }; <> RegisterDisplayType: PUBLIC PROC[ class: ContextClass, type: ATOM ] ~ { registeredDisplayTypes _ Atom.PutPropOnList[registeredDisplayTypes, type, NEW[ContextClass _ class] ]; }; GetDisplayType: PUBLIC PROC[ type: ATOM ] RETURNS[ class: ContextClass ] ~ { refClass: REF ContextClass _ NARROW[Atom.GetPropFromList[registeredDisplayTypes, type]]; IF refClass # NIL THEN class _ refClass^ ELSE SIGNAL Error[[$Unimplemented, "Unregistered display type"]] }; LoadDisplayType: PUBLIC PROC[ context: REF Context, type: ATOM ] ~ { class: REF ContextClass _ NARROW[Atom.GetPropFromList[registeredDisplayTypes, type]]; IF class = NIL THEN Error[[$Unimplemented, "Unregistered display type"]]; context.class _ NEW[ ContextClass _ class^ ]; context.pixels _ NIL; --this and next line for saving VM context.displayProps _ Atom.RemPropFromList[context.displayProps, $FullDisplayMemory]; context.class.setUpDisplayType[context]; -- maps in pixels, sets up color map IF Atom.GetPropFromList[context.props, $BufferContext] # NIL THEN { SIGNAL Error[[$Warning, "Dropping Buffer context"]]; -- insufficient info to update context.props _ Atom.RemPropFromList[context.props, $BufferContext]; }; WITH Atom.GetPropFromList[context.props, $BackGround] SELECT FROM backGrdCtx: REF Context => { SIGNAL Error[[$Warning, "Dropping BackGround context"]]; -- no info for update context.props _ Atom.RemPropFromList[context.props, $BackGround]; }; ENDCASE; }; RegisterSurfaceType: PUBLIC PROC[ class: ShapeClass, type: ATOM ] ~ { registeredSurfaceTypes _ Atom.PutPropOnList[registeredSurfaceTypes, type, NEW[ShapeClass _ class] ]; }; GetSurfaceType: PUBLIC PROC[ type: ATOM ] RETURNS[ class: ShapeClass ] ~ { refClass: REF ShapeClass _ NARROW[Atom.GetPropFromList[registeredSurfaceTypes, type]]; IF refClass # NIL THEN class _ refClass^ ELSE SIGNAL Error[[$Unimplemented, "Unregistered surface type"]]; }; LoadSurfaceType: PUBLIC PROC[shape: REF ShapeInstance, type: ATOM _ $ConvexPolygon] ~{ class: REF ShapeClass _ NARROW[Atom.GetPropFromList[registeredSurfaceTypes, type]]; IF class = NIL THEN SIGNAL Error[[$Unimplemented, "Unregistered surface type"]]; IF shape.class = NIL THEN shape.class _ NEW[ ShapeClass _ class^ ] ELSE { new: REF ShapeClass _ NEW[ ShapeClass _ class^ ]; IF new.validate = NIL THEN new.validate _ shape.class.validate; IF new.display = NIL THEN new.display _ shape.class.display; IF new.displayPatch = NIL THEN new.displayPatch _ shape.class.displayPatch; IF new.doBeforeFrame = NIL THEN new.doBeforeFrame _ shape.class.doBeforeFrame; shape.class _ new; }; IF shape.shadingClass = NIL THEN LoadShadingClass[shape]; -- load default class shape.shadingInValid _ TRUE; -- in case we're changing the type on an existing shape shape.vtcesInValid _ TRUE; shape.props _ Atom.RemPropFromList[ shape.props, $LinesList ]; -- list may not be valid }; RegisterShadingClass: PUBLIC PROC[ class: ShadingClass, type: ATOM ] ~ { <> registeredShadingClasses _ Atom.PutPropOnList[registeredShadingClasses, type, NEW[ShadingClass _ class] ]; }; GetShadingClass: PUBLIC PROC[ type: ATOM ] RETURNS[ class: ShadingClass ] ~ { <> ref: REF ShadingClass _ NARROW[Atom.GetPropFromList[registeredShadingClasses, type]]; IF ref # NIL THEN class _ ref^ ELSE SIGNAL Error[[$Unimplemented, "Unregistered shading class"]]; }; LoadShadingClass: PUBLIC PROC[ shape: REF ShapeInstance, type: ATOM _ $Default ] ~ { <> class: REF ShadingClass _ NARROW[Atom.GetPropFromList[registeredShadingClasses, type]]; IF class = NIL THEN SIGNAL Error[[$Unimplemented, "Unregistered shading class"]]; IF shape.shadingClass = NIL THEN shape.shadingClass _ NEW[ ShadingClass _ class^ ] ELSE { new: REF ShadingClass _ NEW[ ShadingClass _ class^ ]; IF new.shadingType = NIL THEN new.shadingType _ shape.shadingClass.shadingType; new.color _ shape.shadingClass.color; new.shininess _ shape.shadingClass.shininess; new.transmittance _ shape.shadingClass.transmittance; IF new.patchShade = NIL THEN new.patchShade _ shape.shadingClass.patchShade; IF new.texture = NIL THEN new.texture _ shape.shadingClass.texture; IF new.cnvrtVtx = NIL THEN new.cnvrtVtx _ shape.shadingClass.cnvrtVtx; IF new.getColor = NIL THEN new.getColor _ shape.shadingClass.getColor; IF new.loadShapeAux = NIL THEN new.loadShapeAux _ shape.shadingClass.loadShapeAux; IF new.loadVtxAux = NIL THEN new.loadVtxAux _ shape.shadingClass.loadVtxAux; IF new.shadeVtx = NIL THEN new.shadeVtx _ shape.shadingClass.shadeVtx; shape.shadingClass _ new; }; shape.shadingProps _ NIL; -- WHY??? }; <> Create: PUBLIC PROC[] RETURNS [REF Context] ~ { context: REF Context _ NEW[Context]; <> wDir: Rope.ROPE _ Commander.PrependWorkingDir[" "]; -- add needed space (wierdness) wDir _ Rope.Substr[ base: wDir, len: Rope.Length[wDir] - 1 ]; -- drop space context.props _ Atom.PutPropOnList[context.props, $WDir, wDir]; -- keep directory context.eyeSpaceXfm _ G3dMatrix.Identity[]; -- can't do this in initialization, so do it here context.stopMe _ NEW[ BOOLEAN _ FALSE ]; RETURN[context]; }; SetView: PUBLIC PROC[context: REF Context, eyePoint, ptOfInterest: Triple, fieldOfView: REAL _ 40.0, rollAngle: REAL _ 0.0, upDirection: Triple _ [ 0., 0., 1.], hitherLimit: REAL _ .01, yonLimit: REAL _ 1000.0] ~ { context.eyePoint _ eyePoint; context.ptOfInterest _ ptOfInterest; context.fieldOfView _ fieldOfView; context.rollAngle _ rollAngle; context.upDirection _ upDirection; context.hitherLimit _ hitherLimit; context.yonLimit _ yonLimit; context.viewInValid _ TRUE; }; SetPosition: PUBLIC PROC[shape: REF ShapeInstance, concat: BOOLEAN _ FALSE] ~ { <> hypotenuse: REAL _ RealFns.SqRt[ Sqr[shape.orientation.x] + Sqr[shape.orientation.y] ]; IF NOT concat THEN shape.position _ G3dMatrix.Identity[]; -- clear to identity transform shape.position _ G3dMatrix.Mul[ shape.position, -- rotation about arbitrary axis G3dMatrix.MakeRotate[ axis: G3dVector.Sub[shape.axisEnd, shape.axisBase], theta: shape.rotation, base: shape.axisBase ] ]; IF hypotenuse > 0.0 -- orientation THEN { length: REAL _ G3dVector.Length[shape.orientation]; cosA, sinA, cosB, sinB: REAL; cosA _ shape.orientation.x / hypotenuse; sinA _ shape.orientation.y / hypotenuse; shape.position _ G3dMatrix.Mul[ shape.position, -- longitudinal rotation into x-z plane, left handed about z-up NEW[ Xfm3DRep _ [ [cosA,-sinA,0.,0.], [sinA,cosA,0.,0.], [0.,0.,1.,0.], [0.,0.,0.,1.] ] ] ]; cosB _ shape.orientation.z / length; sinB _ hypotenuse / length; shape.position _ G3dMatrix.Mul[ shape.position, -- latitudinal rotation, right-handed about y-north NEW[ Xfm3DRep _ [ [cosB,0.,-sinB,0.], [0.,1.,0.,0.], [sinB,0.,cosB,0.], [0.,0.,0.,1.] ] ] ]; shape.position _ G3dMatrix.Mul[ shape.position, -- longitudinal rotation from x-z plane, right handed about z-up NEW[ Xfm3DRep _ [ [cosA,sinA,0.,0.], [-sinA,cosA,0.,0.], [0.,0.,1.,0.], [0.,0.,0.,1.] ] ] ]; } ELSE IF shape.orientation.z < 0.0 THEN shape.position _ G3dMatrix.Mul[ shape.position, -- turn upside down NEW[ Xfm3DRep _ [ [-1.,0.,0.,0.], [0.,1.,0.,0.], [0.,0.,-1.,0.], [0.,0.,0.,1.] ] ] ]; shape.position _ G3dMatrix.Mul[ shape.position, -- translation NEW[ Xfm3DRep _ [[1.,0.,0.,0.], [0.,1.,0.,0.], [0.,0.,1.,0.], [shape.location.x, shape.location.y, shape.location.z, 1.]] ] ]; shape.positionInValid _ FALSE; }; CloseDisplay: PUBLIC PROC[context: REF Context] ~ { <> context.pixels _ NIL; -- throw away buffer bits context.displayProps _ Atom.RemPropFromList[context.displayProps, $FullDisplayMemory]; ImagerColorMap.SetStandardColorMap[ context.terminal -- restore standard color map ! RuntimeError.BoundsFault => CONTINUE]; }; END.