GGShapesImpl.mesa
Contents: Predefined shapes for use in Gargoyle (e.g. squares for control points).
Copyright Ó 1988, 1992 by Xerox Corporation. All rights reserved.
Pier, January 18, 1988 3:48:16 pm PST
Eisenman, July 24, 1987 5:21:27 pm PDT
Bier, October 24, 1988 3:46:50 pm PDT
DIRECTORY
Draw2d, GGBasicTypes, GGCaret, GGCoreTypes, GGModelTypes, GGSegmentTypes, GGShapes, GGStoragePools, Imager, ImagerTransformation, Lines2d, Real, SF, Vectors2d;
GGShapesImpl:
CEDAR
PROGRAM
IMPORTS Draw2d, GGStoragePools, Lines2d, Vectors2d, Imager, Real
EXPORTS GGShapes = BEGIN
BoundBox: TYPE = GGCoreTypes.BoundBox;
Circle: TYPE = GGBasicTypes.Circle;
Line: TYPE = GGCoreTypes.Line;
Point: TYPE = GGBasicTypes.Point;
Ray: TYPE = GGBasicTypes.Ray;
Vector: TYPE = GGBasicTypes.Vector;
SelectionClass: TYPE = GGSegmentTypes.SelectionClass;
jointSize: INTEGER = Real.Round[GGModelTypes.jointSize];
halfJointSize: REAL = GGModelTypes.halfJointSize;
hotJointSize: INTEGER = Real.Round[GGModelTypes.hotJointSize];
halfHotJointSize: REAL = GGModelTypes.halfHotJointSize;
Generic
DrawWhiteRectangle:
PUBLIC
PROC [dc: Imager.Context, loX, loY, hiX, hiY:
REAL, strokeWidth:
REAL ¬ 1.0] = {
Draws the indicated rectangle (not filled).
DoDrawRect:
PROC = {
strokeWidth2: REAL ¬ strokeWidth+strokeWidth;
width: REAL ¬ hiX-loX;
height: REAL ¬ hiY-loY;
Imager.SetColor[dc, Imager.black];
Imager.MaskRectangle[dc,
[loX-strokeWidth, loY-strokeWidth, width+strokeWidth2, height+strokeWidth2]];
Imager.SetColor[dc, Imager.white];
Imager.MaskRectangle[dc, [loX, loY, width, height]];
};
Imager.DoSave[dc, DoDrawRect];
};
DrawRectangle:
PUBLIC
PROC [dc: Imager.Context, loX, loY, hiX, hiY:
REAL, strokeWidth:
REAL ¬ 1.0] = {
Draws the indicated rectangle (not filled).
DoDrawRect:
PROC = {
RectanglePath: Imager.PathProc = {
moveTo[[loX, loY]];
lineTo[[loX, hiY]];
lineTo[[ hiX, hiY]];
lineTo[[ hiX, loY]];
lineTo[[loX, loY]];
};
Imager.SetStrokeWidth[dc, strokeWidth];
Imager.SetStrokeEnd[dc, square];
Imager.SetStrokeJoint[dc, round];
Imager.MaskStroke[dc, RectanglePath, TRUE];
};
Imager.DoSave[dc, DoDrawRect];
};
DrawFilledRectangle:
PUBLIC PROC [dc: Imager.Context, loX, loY, hiX, hiY:
REAL] = {
DoDrawFilledRectangle:
PROC = {
Imager.SetXY[dc, [loX, loY]]; -- set the current position
Imager.Move[dc]; -- move the origin to the current position
Imager.SetColor[dc, Imager.black];
Imager.MaskRectangle[dc, [0, 0, hiX-loX, hiY-loY]];
};
Imager.DoSave[dc, DoDrawFilledRectangle];
};
DrawSquare:
PUBLIC
PROC [dc: Imager.Context, center: Point, side:
REAL, strokeWidth:
REAL ¬ 1.0] = {
halfSide: REAL ¬ side/2.0;
DoDrawSquare:
PROC = {
SquarePath: Imager.PathProc = {
moveTo[[- halfSide, - halfSide]];
lineTo[[- halfSide, halfSide]];
lineTo[[ halfSide, halfSide]];
lineTo[[ halfSide, - halfSide]];
lineTo[[- halfSide, - halfSide]];
};
Imager.SetXY[dc, [center.x, center.y]];
Imager.Trans[dc];
Imager.Move[dc];
Imager.SetStrokeWidth[dc, strokeWidth];
Imager.SetStrokeEnd[dc, square];
Imager.SetStrokeJoint[dc, round];
Imager.MaskStroke[dc, SquarePath, TRUE];
};
Imager.DoSave[dc, DoDrawSquare];
};
DrawPlus:
PUBLIC
PROC [dc: Imager.Context, center: Point] = {
halfSide: REAL = 5.0;
DoDrawPlus:
PROC = {
Horiz: Imager.PathProc = {
moveTo[[-halfSide, 0.0]];
lineTo[[halfSide, 0.0]];
};
Vert: Imager.PathProc = {
moveTo[[0.0, -halfSide]];
lineTo[[0.0, halfSide]];
};
Imager.SetXY[dc, [center.x, center.y]];
Imager.Trans[dc];
Imager.Move[dc];
Imager.SetStrokeWidth[dc, 2.0];
Imager.SetStrokeEnd[dc, square];
Imager.MaskStroke[dc, Horiz, FALSE];
Imager.MaskStroke[dc, Vert, FALSE];
};
Imager.DoSave[dc, DoDrawPlus];
};
DrawBoundBox:
PUBLIC
PROC [dc: Imager.Context, bBox: BoundBox, strokeWidth:
REAL ¬ 1.0] = {
DrawRectangle[dc, bBox.loX, bBox.loY, bBox.hiX, bBox.hiY, strokeWidth];
};
DrawFilledSquare:
PUBLIC PROC [dc: Imager.Context, center: Point, side:
REAL] = {
halfSide: REAL ¬ side/2.0;
DoDrawFilledSquare:
PROC = {
Imager.SetXY[dc, [center.x, center.y]]; -- set the current position
Imager.Move[dc]; -- move the origin to the current position
Imager.SetColor[dc, Imager.black];
Imager.MaskRectangle[dc, [- halfSide, - halfSide, side, side]];
};
Imager.DoSave[dc, DoDrawFilledSquare];
};
DrawEmptySquare:
PROC [dc: Imager.Context, center: Point, side:
REAL, strokeWidth:
REAL] = {
Draws an outer black square of length "side",
and an inner white square of length "side-2*strokeWidth"
DoDrawEmptySquare:
PROC = {
Imager.SetXY[dc, [center.x, center.y]];
Imager.Move[dc]; -- move the origin to the current position
Imager.SetColor[dc, Imager.black];
Imager.MaskRectangle[dc, [-halfSide, -halfSide, side, side]];
Imager.SetColor[dc, Imager.white];
Imager.MaskRectangle[dc, [-halfSide+strokeWidth, -halfSide+strokeWidth, side-width2, side-width2]];
};
halfSide, width2: REAL;
side ¬ side;
halfSide ¬ side/2.0;
width2 ¬ 2.0*strokeWidth;
Imager.DoSave[dc, DoDrawEmptySquare];
};
DrawSpot:
PUBLIC
PROC [dc: Imager.Context, point: Point] = {
DoDrawSpot:
PROC = {
Imager.SetXY[dc, [point.x, point.y]];
Imager.Move[dc];
Imager.MaskRectangle[dc, [0.0, 0.0, 1.0, 1.0]];
};
Imager.DoSave[dc, DoDrawSpot];
};
DrawLine:
PUBLIC
PROC [dc: Imager.Context, line: Line, clippedBy: Imager.Rectangle, strokeWidth:
REAL ¬ 1.0, zip: Draw2d.Zip ¬
NIL] = {
count: NAT;
ray: Ray;
params: ARRAY[1..2] OF REAL;
p1, p2, basePoint: Point;
direction: Vector;
p1 ¬ [clippedBy.x, clippedBy.y];
p2 ¬ [clippedBy.x + clippedBy.w, clippedBy.y + clippedBy.h];
basePoint ¬ Lines2d.PointOnLine[line];
direction ¬ Lines2d.DirectionOfLine[line];
ray ¬ AllocateRay[basePoint, direction];
[count, params] ¬ Lines2d.LineRayMeetsBox[ray, p1.x, p1.y, p2.x, p2.y];
IF count = 2
THEN {
p1 ¬ Lines2d.EvalRay[ray, params[1]];
p2 ¬ Lines2d.EvalRay[ray, params[2]];
Imager.SetStrokeWidth[dc, strokeWidth];
Draw2d.Line[dc, p1, p2, solid, zip];
};
DoFreeRay[rayPool, ray];
};
DrawLittleLine:
PUBLIC
PROC [dc: Imager.Context, line: Line, point: Point] = {
Draw a short line (1 inch) centered on point, parallel to line.
};
DrawFilledLoLeftSquare:
PUBLIC
PROC [dc: Imager.Context, loLeft: Point, side:
REAL] = {
DoDrawRect:
PROC = {
Imager.SetXY[dc, [loLeft.x, loLeft.y]];
Imager.Move[dc];
Imager.MaskRectangle[dc, [0.0, 0.0, side, side]];
};
Imager.DoSave[dc, DoDrawRect];
};
DrawCircle:
PUBLIC
PROC [dc: Imager.Context, circle: Circle] = {
leftSide, rightSide: Point;
DoDrawCircle:
PROC = {
CirclePath: Imager.PathProc = {
moveTo[[leftSide.x, leftSide.y]];
arcTo[[rightSide.x, rightSide.y], [ leftSide.x, leftSide.y]];
};
Imager.SetStrokeWidth[dc, 1.0];
Imager.SetStrokeEnd[dc, round];
Imager.MaskStroke[dc, CirclePath, TRUE];
};
leftSide ¬ [circle.origin.x - circle.radius, circle.origin.y];
rightSide ¬ [circle.origin.x + circle.radius, circle.origin.y];
Imager.DoSave[dc, DoDrawCircle];
};
starBurstsOn: BOOL ¬ TRUE;
starBurstWidth: REAL ¬ 0.0;
tryDraw2d: BOOL ¬ TRUE;
R: REAL ¬ 7.0;
cos30: REAL = 0.8660254;
sin30: REAL = 0.5;
yR: REAL ¬ cos30*R;
xR: REAL ¬ sin30*R;
DrawStarburst:
PUBLIC
PROC [dc: Imager.Context, point: Point, scale:
REAL ¬ 1.0, zip: Draw2d.Zip ¬
NIL] = {
Draw a six pointed starburst of outer radius R and inner radius r.
DoDrawGlow:
PROC = {
r: REAL ← 3.0;
xr: REAL ← cos30*r;
yr: REAL ← sin30*r;
leftX, loX, hiX, rightX, loY, hiY: REAL;
scaledR, scaledXR, scaledYR: REAL;
Imager.SetColor[dc, Imager.black];
Imager.SetStrokeWidth[dc, starBurstWidth];
scaledR ¬ R*scale;
scaledXR ¬ xR*scale;
scaledYR ¬ yR*scale;
leftX ¬ point.x-scaledR;
loX ¬ point.x-scaledXR;
hiX ¬ point.x+scaledXR;
rightX ¬ point.x+scaledR;
loY ¬ point.y-scaledYR;
hiY ¬ point.y+scaledYR;
IF tryDraw2d
THEN {
Draw2d.Line[dc, [leftX, point.y], [rightX, point.y], solid, zip];
Draw2d.Line[dc, [loX, loY], [hiX, hiY], solid, zip];
Draw2d.Line[dc, [hiX, loY], [loX, hiY], solid, zip];
Draw2d.Line[dc, [0.0, r], [0.0, R], solid, zip];
Draw2d.Line[dc, [0.0, -r], [0.0, -R], solid, zip];
Draw2d.Line[dc, [xr, yr], [xR, yR], solid, zip];
Draw2d.Line[dc, [-xr, -yr], [-xR, -yR], solid, zip];
Draw2d.Line[dc, [-xr, yr], [-xR, yR], solid, zip];
Draw2d.Line[dc, [xr, -yr], [xR, -yR], solid, zip];
}
ELSE {
Imager.MaskVector[dc, [leftX, point.y], [rightX, point.y]];
Imager.MaskVector[dc, [loX, loY], [hiX, hiY]];
Imager.MaskVector[dc, [hiX, loY], [loX, hiY]];
Imager.MaskVector[dc, [0.0, r], [0.0, R]];
Imager.MaskVector[dc, [0.0, -r], [0.0, -R]];
Imager.MaskVector[dc, [xr, yr], [xR, yR]];
Imager.MaskVector[dc, [-xr, -yr], [-xR, -yR]];
Imager.MaskVector[dc, [-xr, yr], [-xR, yR]];
Imager.MaskVector[dc, [xr, -yr], [xR, -yR]];
};
};
IF starBurstsOn THEN Imager.DoSave[dc, DoDrawGlow];
};
DrawArrow:
PUBLIC
PROC [dc: Imager.Context, tip: Point, base: Point, strokeWidth:
REAL] = {
OPEN Vectors2d;
DrawArrowAux: Imager.PathProc = {
moveTo[tip];
lineTo[Sub[tip, Add[Scale[axis, height], Scale[perp, halfWidth]]]];
lineTo[Sub[tip, Add[Scale[axis, height], Scale[perp, -halfWidth]]]];
lineTo[tip];
};
axis: Vector;
perp: Vector;
height: REAL ¬ strokeWidth+5.0;
halfWidth: REAL ¬ strokeWidth+3.0;
IF tip = base THEN RETURN;
axis ¬ Normalize[Vectors2d.Sub[tip, base]];
perp ¬ [axis.y, -axis.x];
Imager.MaskFill[context: dc, path: DrawArrowAux, oddWrap: FALSE]
};
ArrowSize:
PUBLIC
PROC [strokeWidth:
REAL]
RETURNS [height, halfWidth:
REAL] = {
The arrowhead will fit in a box of size height by (halfWidth*2).
height ¬ strokeWidth+5.0;
halfWidth ¬ strokeWidth+3.0;
};
Gargoyle-specified
DrawCaret:
PUBLIC
PROC [dc: Imager.Context, point: Point, normal: Vector, scale:
REAL ¬ 1.0] = {
Scale the caret by "scale" about "point" before drawing it.
halfWidth: REAL = GGCaret.caretWidth/2.0;
fullHeight: REAL = GGCaret.caretHeight;
caretDirection: Vector;
point1, point2, midPoint: Point;
DoDrawCaret:
PROC = {
CaretPath: Imager.PathProc = {
moveTo[point1];
lineTo[point];
lineTo[point2];
};
Imager.SetStrokeWidth[dc, 0.0];
Imager.MaskStroke[dc, CaretPath, FALSE];
};
caretDirection ¬ Vectors2d.Normalize[normal];
midPoint ¬ Vectors2d.Add[point, Vectors2d.Scale[caretDirection, fullHeight*scale]];
caretDirection ¬ Vectors2d.Scale[caretDirection, halfWidth*scale];
point1.x ¬ midPoint.x + caretDirection.y;
point1.y ¬ midPoint.y - caretDirection.x;
point2.x ¬ midPoint.x - caretDirection.y;
point2.y ¬ midPoint.y + caretDirection.x;
Imager.DoSave[dc, DoDrawCaret];
};
DrawAnchor:
PUBLIC
PROC [dc: Imager.Context, point: Point, normal: Vector, scale:
REAL ¬ 1.0] = {
DoDrawAnchor:
PROC = {
ArmPath: Imager.PathProc = {
moveTo[[nearX, stroke]];
lineTo[[farX, stroke]];
lineTo[[midX, bottom]];
lineTo[[midX, -stroke]];
lineTo[[nearX, -stroke]];
};
side: REAL ¬ 3.5*scale;
insideWing: REAL ¬ 7.0*scale;
width: REAL ¬ 6.0*scale;
height: REAL ¬ 8.0*scale;
bottom: REAL ¬ -8.5*scale;
stroke: REAL ¬ 0.5*scale;
nearX, midX, farX: REAL;
degrees: REAL;
epsilon: REAL = 0.1;
Imager.SetXY[dc, [point.x, point.y]]; -- set the current position
degrees ¬ Vectors2d.AngleFromVector[normal] + 90.0;
Imager.Move[dc];
IF ABS[degrees] > epsilon THEN Imager.RotateT[dc, degrees];
Imager.SetColor[dc, Imager.black];
Imager.SetStrokeEnd[dc, square];
Imager.SetStrokeWidth[dc, scale];
Draw the square in the middle.
Imager.MaskVector[dc, [-side, -side], [-side, side]];
Imager.MaskVector[dc, [-side, side], [side, side]];
Imager.MaskVector[dc, [side, side], [side, -side]];
Imager.MaskVector[dc, [side, -side], [-side, -side]];
Draw the two wings.
nearX ¬ side; midX ¬ insideWing; farX ¬ insideWing+width;
Imager.MaskFill[dc, ArmPath];
nearX ¬ -side; midX ¬ -insideWing; farX ¬ -insideWing-width;
Imager.MaskFill[dc, ArmPath];
};
Imager.DoSave[dc, DoDrawAnchor];
};
DrawCP:
PUBLIC
PROC [dc: Imager.Context, point: Point, scale:
REAL ¬ 1.0] = {
<<IF useBitmaps THEN ImagerBackdoor.DrawBits[context: dc, base: LOOPHOLE[cpBits], wordsPerLine: 1, sMin: 0, fMin: 0, sSize: jointSize, fSize: jointSize, tx: Real.Round[point.x-halfJointSize], ty: Real.Round[point.y+halfJointSize] ]
ELSE>> DrawEmptySquare[dc, point, jointSize*scale, scale];
};
DrawJoint:
PUBLIC
PROC [dc: Imager.Context, point: Point, scale:
REAL ¬ 1.0] = {
<<IF useBitmaps THEN ImagerBackdoor.DrawBits[context: dc, base: LOOPHOLE[jointBits], wordsPerLine: 1, sMin: 0, fMin: 0, sSize: jointSize, fSize: jointSize , tx: Real.Round[point.x-halfJointSize], ty: Real.Round[point.y+halfJointSize] ]
ELSE>> {
DrawEmptySquare[dc, point, jointSize*scale, scale];
DrawFilledSquare[dc, point, 2.0*scale];
};
};
DrawSelectedJoint:
PUBLIC
PROC [dc: Imager.Context, point: Point, selectClass: SelectionClass, scale:
REAL ¬ 1.0] = {
<<IF useBitmaps
THEN
IF selectClass=hot THEN ImagerBackdoor.DrawBits[context: dc, base: LOOPHOLE[hotBits], wordsPerLine: 1, sMin: 0, fMin: 0, sSize: hotJointSize, fSize: hotJointSize , tx: Real.Round[point.x-halfHotJointSize], ty: Real.Round[point.y+halfHotJointSize] ]
ELSE IF selectClass=normal THEN ImagerBackdoor.DrawBits[context: dc, base: LOOPHOLE[normalBits], wordsPerLine: 1, sMin: 0, fMin: 0, sSize: jointSize, fSize: jointSize, tx: Real.Round[point.x-halfJointSize], ty: Real.Round[point.y+halfJointSize] ]
ELSE ImagerBackdoor.DrawBits[context: dc, base: LOOPHOLE[jointBits], wordsPerLine: 1, sMin: 0, fMin: 0, sSize: jointSize, fSize: jointSize , tx: Real.Round[point.x-halfJointSize], ty: Real.Round[point.y+halfJointSize] ]
ELSE>>
IF selectClass=hot THEN DrawEmptySquare[dc, point, hotJointSize*scale, scale]
ELSE IF selectClass=normal THEN DrawFilledSquare[dc, point, jointSize*scale]
ELSE ERROR;
};
DrawQuickSelectedJoint:
PUBLIC
PROC [dc: Imager.Context, point: Point, selectClass: SelectionClass, scale:
REAL] = {
IF selectClass=normal
THEN {
scaleJOver2: REAL ¬ scale*halfJointSize;
DrawFilledRectangle[dc, point.x-3.0*scaleJOver2, point.y-scaleJOver2, point.x+scaleJOver2, point.y+scaleJOver2];
}
ELSE
IF selectClass=hot
THEN {
scaleHJOver2: REAL ¬ scale*halfHotJointSize;
Imager.SetColor[dc, Imager.black];
DrawWhiteRectangle[dc, point.x-scale*(halfHotJointSize+jointSize), point.y-scaleHJOver2, point.x+scaleHJOver2, point.y+scaleHJOver2, scale];
}
ELSE ERROR;
};
The Pool of Rays
rayPool: GGStoragePools.StoragePool;
rayPoolClass: GGStoragePools.StoragePoolClass;
RayPool: TYPE = REF RayPoolObj;
RayPoolObj:
TYPE =
RECORD [
index: NAT ¬ 0,
rays: SEQUENCE len: NAT OF Ray
];
maxRays: NAT = 6;
AllocateRay:
PROC [base: Point, direction: Vector]
RETURNS [ray: Ray] = {
ray ¬ NARROW[DoAllocateRay[rayPool]];
ray.p ¬ base;
ray.d ¬ direction;
};
MakeRayStorage:
PROC [pool: GGStoragePools.StoragePool] = {
realPool: RayPool ¬ NEW[RayPoolObj[maxRays]];
FOR i:
NAT
IN [0..5]
DO
realPool[i] ¬ Lines2d.CreateRay[[0,0], [0,0]];
ENDLOOP;
realPool.index ¬ maxRays;
pool.data ¬ realPool;
};
DoAllocateRay:
PROC [pool: GGStoragePools.StoragePool]
RETURNS [item:
REF
ANY] = {
realPool: RayPool ¬ NARROW[pool.data];
IF realPool.index = 0 THEN item ¬ Lines2d.CreateRay[[0,0], [0,0]] -- pool is empty, allocate one
ELSE {
realPool.index ¬ realPool.index - 1;
item ¬ realPool[realPool.index];
};
};
DoFreeRay:
PROC [pool: GGStoragePools.StoragePool, item:
REF
ANY] = {
realPool: RayPool ¬ NARROW[pool.data];
realItem: Ray ¬ NARROW[item];
IF realPool.index = maxRays THEN RETURN; -- pool is full. Toss ray on the floor.
realPool[realPool.index] ¬ realItem;
realPool.index ¬ realPool.index + 1;
};
Init:
PROC = {
rayPoolClass ¬ GGStoragePools.CreateClass[$RayPool, DoAllocateRay, DoFreeRay];
rayPool ¬ GGStoragePools.CreatePool[rayPoolClass, MakeRayStorage];
};
Init[];
END.