ImagerClipperImpl.mesa
Created April 28, 1983
Last Edited by: Crow, June 20, 1983 8:46 pm
DIRECTORY
Real     USING [Float],
Imager    USING [PathFromIntRectangle, Context],
ImagerBasic   USING [Path, Vec, IntVec, IntRectangle, Visibility],
ImagerInternalDefs USING [ImagingSpace, Clipper, ClipperRecord, TransformRecord],
ImagerTransform USING [IntTransform, TransformIntVec],
ImagerClipper;
ImagerClipperImpl: CEDAR PROGRAM
IMPORTS Imager, ImagerTransform, Real
EXPORTS Imager, ImagerClipper
= BEGIN OPEN ImagerBasic;
Client-called Procedures
Client clipping areas are first transformed to fix them on the viewer
ClipPath: PUBLIC PROC [context: Context, outline: Path, exclude: BOOLEANFALSE] = {
Modifies old clipping region by supplied outline. Exclude sets clipping outside path rather than inside.
context.clientClipper ← ClipClipper[context.clientClipper,
       TransformClipper[ClipperFromPath[area], context.clientTransform]];
ValidateCompositeClipper[context];
};
The Composite clipper is held in the device space, where clipping actually takes place. Therefore, incoming data is expected in device space (pretransformed).
ClipRectangle: PROC [context: Context, outline: Rectangle, exclude: BOOLEANFALSE];
Modifies old clipping region by supplied outline. Exclude sets clipping outside path rather than inside.
TestRectangle: PUBLIC PROC [context: Context, area: Rectangle] RETURNS [Visibility] = {
state: Visibility;
clipper: ClipperRecord ← ClipperFromPath[area];
SELECT context.clipper.type FROM
rectangle => { state ← TestClipperRectangle[clipper, context.clipper];
    IF state = visible THEN context.noClipArmed ← TRUE;
    RETURN [ state ]; };
path  => ERROR;   -- until M. Plass works here
ENDCASE => ERROR;
};
TestPoint: PUBLIC PROC [context: Context, point: Vec] RETURNS [BOOLEAN] = {
SELECT context.clipper.type FROM
rectangle => IF (point.x <= Real.Float[context.clipper.xMax]) AND
     (point.x >= Real.Float[context.clipper.xMin]) AND
     (point.y <= Real.Float[context.clipper.yMax]) AND
     (point.y >= Real.Float[context.clipper.yMin])
    THEN RETURN[ TRUE ]
    ELSE RETURN[ FALSE ];
path  => ERROR;   -- until M. Plass works here
ENDCASE => ERROR;
};
Fast Track procedures
ClipIntRectangle: PUBLIC PROC [context: Context, outline: IntRectangle, exclude: BOOLEANFALSE] = {
Modifies old clipping region by supplied outline. Exclude sets clipping outside path rather than inside.
IF context.clientTransform.type = hard
THEN ConcatArea[context, Imager.PathFromIntRectangle[area]]
ELSE ConcatRectangle[context, client, EZTfmRectangle[area, context.clientTransform]];
ValidateCompositeClipper[context];
};
TestIntRectangle: PUBLIC PROC [context: Context, area: IntRectangle] RETURNS [Visibility] = {
state: Visibility;
SELECT context.clipper.type FROM
rectangle => { state ← TestClipperRectangle[ClipperFromRectangle[area], context.clipper];
    IF state = visible THEN context.noClipArmed ← TRUE;
    RETURN [ state ]; };
path  => RETURN [ TestArea[context, Imager.PathFromIntRectangle[area]] ];
ENDCASE => ERROR;
};
TestIntPoint: PUBLIC PROC [context: Context, point: IntPair] RETURNS [BOOLEAN] = {
SELECT context.clipper.type FROM
rectangle => IF (point.x <= context.clipper.xMax) AND (point.x >= context.clipper.xMin) AND
     (point.y <= context.clipper.yMax) AND (point.y >= context.clipper.yMin)
    THEN RETURN[ TRUE ]
    ELSE RETURN[ FALSE ];
path  => ERROR;   -- until M. Plass works here
ENDCASE => ERROR;
};
DoWithoutClipping: PUBLIC PROC [context: Context, callBack: PROC[Context]] = {
Each call must be preceded by a successful call to TestArea or TestIntRectangle
IF context.noClipArmed THEN context.noClip ← TRUE ELSE ERROR;
callBack[ context ];
context.noClipArmed ← FALSE; context.noClip ← FALSE;
};
Unpublicized Procedures
SetClipper: PUBLIC PROC [context: Context, clipSpace: ImagingSpace, clipper: ClipperRecord] = {
SELECT clipSpace FROM
client   => context.clientClipper ← clipper;
viewer => context.viewerClipper ← clipper;
device => context.deviceClipper ← clipper;
ENDCASE => ERROR;
ValidateCompositeClipper[context];
};
GetClipper: PUBLIC PROC [context: Context, clipSpace: ImagingSpace, clipper: ClipperRecord] = {
SELECT clipSpace FROM
client   => clipper ← context.clientClipper;
viewer => clipper ← context.viewerClipper;
device => clipper ← context.deviceClipper;
ENDCASE => ERROR;
};
Internal Procedures
IntersectClipBoxes: PROC [clpr1, clpr2: ClipperRecord] RETURNS [ClipperRecord] = {
clipper: ClipperRecord;
clipper.xMin ← MAX[clpr1.xMin, clpr2.xMin];
clipper.xMax ← MIN[clpr1.xMax, clpr2.xMax];
clipper.yMin ← MAX[clpr1.yMin, clpr2.yMin];
clipper.yMax ← MIN[clpr1.yMax, clpr2.yMax];
IF (clipper.xMin >= clipper.xMax) OR (clipper.yMin >= clipper.yMax)  -- disjoint areas?
THEN { clipper.xMax ← clipper.xMin; clipper.yMax ← clipper.yMin; }; -- make null
clipper.type ← rectangle;
RETURN[clipper];
};
TestClipperRectangle: PROC [clippee, clipper: ClipperRecord] RETURNS [Visibility] = {
IF   (clippee.xMax <= clipper.xMin) OR (clippee.xMin >= clipper.xMax) OR
   (clippee.yMax <= clipper.yMin) OR (clippee.yMin >= clipper.yMax)
THEN RETURN[ invisible ]
ELSE IF (clippee.xMax <= clipper.xMax) AND (clippee.xMin >= clipper.xMax) AND
   (clippee.yMax <= clipper.yMax) AND (clippee.yMin >= clipper.yMin)
THEN RETURN[ visible ]
ELSE RETURN[ partlyVisible ];
};
ClipClipper: PROC [clipper1, clipper2: ClipperRecord] RETURNS [ClipperRecord] = {
IF (clipper1.type = rectangle) AND (clipper2.type = rectangle)
THEN RETURN [ IntersectClipBoxes[clipper1, clipper2] ]
ELSE ERROR;          -- M. Plass needed here
};
ClipperFromPath: PROC [area: Path] RETURNS [ClipperRecord] = {
clipper: ClipperRecord;
clipper.type ← none;     -- until M. Plass works here
RETURN[clipper];
};
PathFromClipper: PROC [clipper: ClipperRecord] RETURNS [Path] = {
path: Path;
RETURN[path];       -- until M. Plass works here
};
ClipperFromRectangle: PROC [area: IntRectangle] RETURNS [ClipperRecord] = {
clipper: ClipperRecord;
{ OPEN clipper;
type ← rectangle;
xMin ← MIN[area.x, area.x + area.w];
xMax ← MAX[area.x, area.x + area.w];
yMin ← MIN[area.y, area.y + area.h];
yMax ← MAX[area.y, area.y + area.h];
clipper ← NIL;
};
RETURN[clipper];
};
RectangleFromClipper: PROC [clipper: ClipperRecord] RETURNS [IntRectangle] = {
OPEN clipper;
RETURN [ IntRectangle[xMin, yMin, xMax - xMin, yMax - yMin] ];
};
EZTfmRectangle: PROC [area: IntRectangle, transform: TransformRecord]
       RETURNS [IntRectangle] = {
Does rectangle-preserving easy transforms only
rect: IntRectangle;
IF (transform.type = hard) OR (transform.type = none) THEN ERROR;
[[rect.x, rect.y]] ← ImagerTransform.IntTransform[transform, IntVec[area.x, area.y]];
[[rect.w, rect.h]] ← ImagerTransform.TransformIntVec[transform, IntVec[area.w, area.h]];
RETURN[ rect ];
};
TransformClipper: PROC [clipper: ClipperRecord, transform: TransformRecord]
      RETURNS [ClipperRecord] = {
IF (transform.type # hard) AND (clipper.type = rectangle)
THEN RETURN [ ClipperFromRectangle[
        EZTfmRectangle[RectangleFromClipper[clipper], transform]] ]
ELSE ERROR;          -- M. Plass needed here
};
ValidateCompositeClipper: PROC [context: Context] = {
Assemble composite clipper, leave result in device space
context.clipper ←
ClipClipper[
TransformClipper[
ClipClipper[
TransformClipper[context.clientClipper, context.viewerTransform],
context.viewerClipper],
context.deviceTransform],
context.deviceClipper];
};
END.