Interactive3DImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last Edited by: Crow, June 10, 1989 1:47:12 pm PDT
DIRECTORY
Atom    USING [ GetPropFromList ],
Real    USING [ Round, LargestNumber ],
ThreeDBasics USING [ Context, RealSequence, Rectangle, ShapeInstance ],
Interactive3D USING [ CloseVertex, CloseVertexSequence, FacingDirection ];
Interactive3DImpl: CEDAR PROGRAM
IMPORTS Atom, Real
EXPORTS Interactive3D
~ BEGIN
Basic Types
Context: TYPE ~ ThreeDBasics.Context;
Rectangle: TYPE ~ ThreeDBasics.Rectangle;
RealSequence: TYPE ~ ThreeDBasics.RealSequence;
FacingDirection: TYPE ~ Interactive3D.FacingDirection;
CloseVertex: TYPE ~ Interactive3D.CloseVertex;
RECORD[ vtx: NAT, shape: REF ThreeDBasics.ShapeInstance ];
CloseVertexSequence: TYPE ~ Interactive3D.CloseVertexSequence;
RECORD[ SEQUENCE length: CARDINAL OF CloseVertex ];
Procedures for hit testing
NearbyVertices: PUBLIC PROC[ context: Context, cursorX, cursorY: REAL,
          numberToReturn: NAT ← 1, maxDistance: REAL ← Real.LargestNumber ]
      RETURNS[ closeVtx: REF CloseVertexSequence ] ~ {
closeDist: REF RealSequence ← NEW[ RealSequence[numberToReturn] ];
FOR i: NAT IN [0.. closeDist.length) DO closeDist[i] ← Real.LargestNumber; ENDLOOP;
closeVtx ← NEW[ CloseVertexSequence[numberToReturn] ];
FOR i: NAT IN [0.. context.shapes.length) DO
IF Atom.GetPropFromList[context.shapes[i].props, $Invisible] = NIL
AND context.shapes[i].clipState # out
AND Inside[context.shapes[i], cursorX, cursorY]
THEN {
shape: REF ThreeDBasics.ShapeInstance ← context.shapes[i];
FOR i: NAT IN [0.. shape.vertex.length) DO
distance: REAL ABS[ cursorX - shape.vertex[i].sx ]
    + ABS[ cursorY - shape.vertex[i].sy ];
IF distance < maxDistance AND distance < closeDist[0] THEN {
closeDist[0] ← distance;   -- replace most distant close vertex found so far
closeVtx[0] ← [i, shape];
FOR j: NAT IN [1 .. closeDist.length) DO-- bubble into sorted position
IF closeDist[i] > closeDist[i-1] THEN {
tmpVtx: CloseVertex; tmpDst: REAL;
tmpDst ← closeDist[i-1]; closeDist[i-1] ← closeDist[i]; closeDist[i] ← tmpDst;
tmpVtx ← closeVtx[i-1]; closeVtx[i-1] ← closeVtx[i]; closeVtx[i] ← tmpVtx;
}
ELSE EXIT;
ENDLOOP; -- do for close Vertices
};
ENDLOOP; -- do for all vertices
};
ENDLOOP; -- do for all shapes
};
Inside: PROC[ shape: REF ThreeDBasics.ShapeInstance, x, y: REAL ] RETURNS[ BOOLEAN ] ~ {
ix: NAT ← Real.Round[x]; iy: NAT ← Real.Round[y];
IF shape.screenExtent.min.f > ix OR shape.screenExtent.max.f < ix
OR shape.screenExtent.min.s > iy OR shape.screenExtent.max.s < iy
THEN RETURN[ FALSE ]
ELSE RETURN[ TRUE ];
};
BestPolygon: PUBLIC PROC[ closeVtx: REF CloseVertexSequence,
         facingDir: FacingDirection ← front, closest: BOOLEANTRUE ] ~ {
};
Procedures for erasable drawing
PasteOn: PUBLIC PROC[context: Context, viewPort: Rectangle, action: PROC[Context]] ~ {
Stores pixels underlying viewPort and saves them on context.props to RipOff the area later, if something is already stored on context.props it is RippedOff before action is executed. Action is executed within context using supplied viewPort.
};
RipOff: PUBLIC PROC[ context: Context ] ~ {
Undoes the last PasteOn
srcExtent, dstExtent: Pixels.Extent;
srcBuf: REF PixelBuffer ← NARROW[ Atom.GetPropFromList[context.props, $PasteOnBuffer] ];
IF srcBuf = NIL THEN RETURN[];
srcExtent ← Pixels.GetExtent[srcBuf^]; dstExtent ← Pixels.GetExtent[context.display];
Pixels.Clip[context.display, srcExtent];
Pixels.Transfer[context.display, srcBuf^];
Pixels.Clip[context.display, dstExtent];      -- restore buffer size
context.props ← Atom.RemPropFromList[context.props, $PasteOnBuffer];
};
END.