GGShapesImpl.mesa
Contents: Predefined shapes for use in Gargoyle (e.g. squares for control points).
Copyright Ó 1988 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: BOOLTRUE;
starBurstWidth: REAL ← 0.0;
tryDraw2d: BOOLTRUE;
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.