ImagerClipperImpl.mesa
Created April 28, 1983
Last Edited by: Crow, June 18, 1983 10:34 am
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;
Client-called Procedures
Client clipping areas are first transformed to fix them on the viewer
SetClipArea:
PUBLIC PROC [context: Context, area: Path] = {
-- Establishes clipped area
context.clientClipper ← TransformClipper[ClipperFromPath[area], context.clientTransform];
ValidateCompositeClipper[context];
};
ConcatArea:
PUBLIC PROC [context: Context, area: Path, exclude:
BOOLEAN ←
FALSE] = {
Modifies old clipping region by supplied area.
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).
TestArea:
PUBLIC PROC [context: Context, area: Path]
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;
};
ClipArea:
PUBLIC PROC [context: Context, area: Path]
RETURNS [Path] = {
clipper: ClipperRecord ← ClipClipper[context.clipper, ClipperFromPath[area]];
RETURN[ PathFromClipper[clipper] ];
};
Fast Track procedures
SetClipIntRectangle:
PUBLIC PROC [context: Context, area: IntRectangle] = {
context.clientClipper ← TransformClipper[ClipperFromRectangle[area],
context.clientTransform];
ValidateCompositeClipper[context];
};
ConcatIntRectangle:
PUBLIC PROC [context: Context, area: IntRectangle] = {
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: IntVec]
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;
};
ClipIntRectangle:
PUBLIC PROC [context: Context, area: IntRectangle]
RETURNS [IntRectangle] = {
Returns subrectangle which lies entirely inside clipping area
SELECT context.clipper.type FROM
rectangle => RETURN [ RectangleFromClipper[
IntersectClipBoxes[ClipperFromRectangle[area], context.clipper]] ];
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;
};
ConcatRectangle:
PUBLIC PROC [context: Context, clipSpace: ImagingSpace, area: IntRectangle] = {
Intersect rectangle with existing clipper to define new clipper
SELECT clipSpace FROM
client =>
SELECT context.clientClipper.type FROM
rectangle => context.clientClipper ← IntersectClipBoxes[context.clientClipper,
ClipperFromRectangle[area]];
path => context.clientClipper ← ClipClipper[context.clientClipper,
ClipperFromRectangle[area]];
ENDCASE => ERROR;
viewer =>
SELECT context.viewerClipper.type FROM
rectangle => context.clientClipper ← IntersectClipBoxes[context.viewerClipper,
ClipperFromRectangle[area]];
path => context.clientClipper ← ClipClipper[context.viewerClipper,
ClipperFromRectangle[area]];
ENDCASE => ERROR;
device =>
SELECT context.deviceClipper.type FROM
rectangle => context.clientClipper ← IntersectClipBoxes[context.deviceClipper,
ClipperFromRectangle[area]];
path => context.clientClipper ← ClipClipper[context.deviceClipper,
ClipperFromRectangle[area]];
ENDCASE => ERROR;
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.