File: StretchLinesImpl.mesa   
Copyright © 1984 by Xerox Corporation. All rights reserved.
Created by: Mayo, June 8, 1984 12:40:57 pm PDT
Last Edited by: Mayo, July 27, 1984 11:10:26 pm PDT
DIRECTORY
StretchLines,
TilerMenu USING [Register],
TerminalIO USING [WriteRope, WriteInt, RequestRope, UserSaysYes],
CDSequencer USING [Command, ImplementCommand],
CD, CDPanel USING [FetchDefaultLevel],
TokenIO, CDInline,
CDOps USING [AddAnObject],
CDCallSpecific USING [CallProc, Register],
CDOrient, Rope USING [ROPE, Cat, Equal],
Atom, Graphics;
StretchLinesImpl: CEDAR PROGRAM    
IMPORTS Atom, TilerMenu, CD, TerminalIO, CDOps, CDInline, CDCallSpecific, Rope, CDSequencer, TokenIO, CDOrient, CDPanel, Graphics EXPORTS StretchLines =  
BEGIN OPEN StretchLines;
StretchObj: TYPE = REF StretchObjRec;  -- an object that can be put in a cell or design
StretchObjRec: TYPE = RECORD [
label: Rope.ROPE,
type: LineTypes,
level: CD.Level  -- for blunt lines, the level to be stretched.
];
objAtom: PUBLIC ATOM ← $StretchLine;
objsProcs: REF CD.ObjectProcs;
myLevel: CD.Level;
objHeight: INT = 12;
defaultObjWidth: INT = 45;
minObjWidth: INT = 2 * CD.lambda;
objBaseline: INT = 3;
-- Utilities
-- Given a ChipNDale application that points to a stretch mark, return its label.
FindLabel: PUBLIC PROC [aptr: CD.ApplicationPtr] RETURNS [Rope.ROPE, LineTypes] = BEGIN
st: StretchObj ← NARROW[aptr.ob.specificRef, StretchObj];
RETURN[st.label, st.type];
END;
-- find the origin of a stretch line relative to the containing cell's origin
Fetch: PUBLIC PROC [aptr: CD.ApplicationPtr] RETURNS [LineData] = BEGIN
markObj: StretchObj = NARROW[aptr.ob.specificRef];
line: LineData;
orient: CD.Orientation ← aptr.orientation;
IF CDOrient.IncludesMirrorX[orient] THEN {
orient ← CDOrient.MirrorX[orient]; -- compiler can't nest these calls!!!
orient ← CDOrient.Rotate90[orient];
orient ← CDOrient.Rotate90[orient];
};
line.point ← CDInline.BaseOfAnyRect[CDOrient.MapRect[
itemInCell: [x1: 0, y1: objBaseline, x2: aptr.ob.size.x, y2: objBaseline],
cellSize: aptr.ob.size, cellInstOrient: aptr.orientation, cellInstPos: aptr.location]];
line.length ← aptr.ob.size.x;
SELECT orient FROM
CDOrient.original => line.direction ← up;
CDOrient.rotate90 => line.direction ← left;
CDOrient.rotate180 => line.direction ← down;
CDOrient.rotate270 => line.direction ← right;
ENDCASE => ERROR;
line.type ← markObj.type;
line.label ← markObj.label;
line.level ← markObj.level;
RETURN[line];
END;
-- check a command for wierdness as it is possible to screw up the TIP table.
OKCommand: PROC [comm: CDSequencer.Command] RETURNS[BOOL] = BEGIN
IF comm.pos.x > 10000 OR comm.pos.y > 10000 OR comm.pos.x < -10000 OR comm.pos.y < -10000 THEN BEGIN
TerminalIO.WriteRope["Warning: Huge position passed to AlignmentMarks from ChipNDale, maybe the TIP table is screwed up?\n"];
TerminalIO.WriteRope["comm.pos.x ="]; TerminalIO.WriteInt[comm.pos.x];
TerminalIO.WriteRope[" comm.pos.y ="]; TerminalIO.WriteInt[comm.pos.y];
[] ← TerminalIO.RequestRope["\nOperation will be aborted, type return ......"];
RETURN[FALSE];
END;
RETURN[TRUE];
END;
-- routines for interactive editing of stretch line
-- Place mark. Given point is the left (or lower) endpoint of the mark.r
Place: PUBLIC PROC [into: CD.Design, line: LineData, shift: BOOLTRUE] = BEGIN
orient: CD.Orientation;
obj: CD.ObPtr ← NEW[CD.ObjectDefinition];
p: CD.Position ← [line.point.x, line.point.y];
len: INT ← MAX[minObjWidth, line.length];
markObj: StretchObj ← NEW[StretchObjRec];
markObj.label ← line.label;
markObj.type ← line.type;
obj.p ← objsProcs;
SELECT line.direction FROM
up => {
orient ← CDOrient.original;
IF shift THEN p.y ← p.y - objBaseline;
};
down => {
orient ← CDOrient.rotate180;
IF shift THEN p.y ← p.y - (objHeight - objBaseline);
};
left => {
orient ← CDOrient.rotate90;
IF shift THEN p.x ← p.x - (objHeight - objBaseline);
};
right => {
orient ← CDOrient.rotate270;
IF shift THEN p.x ← p.x - objBaseline;
};
ENDCASE => ERROR;
IF line.type = blunt THEN {
IF line.level # CD.combined THEN
markObj.level ← line.level
ELSE
markObj.level ← CDPanel.FetchDefaultLevel[into];
}
ELSE
markObj.level ← CD.combined;
obj.level ← myLevel;
obj.size ← [len, objHeight];
obj.specificRef ← markObj;
CDOps.AddAnObject[design: into, ob: obj, location: p, orientation: orient];
END;
DrawStretchMark: CD.DrawProc = BEGIN
-- PROC [aptr: ApplicationPtr, pos: DesignPosition, orient: Orientation, pr: REF DrawInformation];
markObj: StretchObj = NARROW[aptr.ob.specificRef];
-- move to a specified point, with or without drawing a line
MoveTo: PROC [x, y: INT, draw: BOOL, aptr: CD.ApplicationPtr, pos: CD.DesignPosition, orient: CD.Orientation, context: Graphics.Context] = BEGIN
p: CD.Position;
p ← CDInline.AddPoints[CDOrient.MapPosition[
itemInCell: [x1: x, y1: y, x2: x, y2: y],
cellSize: aptr.ob.size, cellInstOrient: orient], pos];
IF draw THEN
Graphics.DrawTo[context, p.x, p.y]
ELSE
Graphics.SetCP[context, p.x, p.y];
END;
Color: PROC [context: Graphics.Context] = BEGIN
MapToReal: PROC[x, y: INT] RETURNS [REAL, REAL] = INLINE BEGIN
MapPoint: PROC [p: CD.Position, cellSize: CD.Position, orient: CD.Orientation] RETURNS [CD.Position] = INLINE BEGIN
RETURN[CDOrient.MapPosition[
itemInCell: [x1: p.x, y1: p.y, x2: p.x, y2: p.y],
cellSize: cellSize, cellInstOrient: orient]];
END;
orig: CD.Position;
orig ← CDInline.AddPoints[MapPoint[[x, y], aptr.ob.size, orient], pos];
RETURN[orig.x, orig.y];
END;
mid: INT ← aptr.ob.size.x / 2;
x, y: REAL;
path: Graphics.Path ← Graphics.NewPath[size: 8];
IF context = NIL THEN RETURN;
[] ← Graphics.SetFat[context, FALSE];
-- fill in the arrow, a hack since we can't do color except in rectangles
[x, y] ← MapToReal[mid - 2, objBaseline + 0];
Graphics.MoveTo[path, x, y, TRUE];
[x, y] ← MapToReal[mid - 2, objBaseline + 4];
Graphics.LineTo[path, x, y];
[x, y] ← MapToReal[mid - 4, objBaseline + 4];
Graphics.LineTo[path, x, y];
[x, y] ← MapToReal[mid - 0, objBaseline + 8];
Graphics.LineTo[path, x, y];
[x, y] ← MapToReal[mid + 4, objBaseline + 4];
Graphics.LineTo[path, x, y];
[x, y] ← MapToReal[mid + 2, objBaseline + 4];
Graphics.LineTo[path, x, y];
[x, y] ← MapToReal[mid + 2, objBaseline + 0];
Graphics.LineTo[path, x, y];
Graphics.DrawArea[self: context, path: path];
rect ← CDInline.MoveRect[CDOrient.MapRect[
itemInCell: [x1: mid - 1, y1: objBaseline + 4, x2: mid + 1, y2: objBaseline + 7],
cellSize: aptr.ob.size, cellInstOrient: orient], pos];
pr.drawRect[rect, aptr.ob.level, pr];
rect ← CDInline.MoveRect[CDOrient.MapRect[
itemInCell: [x1: mid - 2, y1: objBaseline, x2: mid + 2, y2: objBaseline + 6],
cellSize: aptr.ob.size, cellInstOrient: orient], pos];
pr.drawRect[rect, aptr.ob.level, pr];
rect ← CDInline.MoveRect[CDOrient.MapRect[
itemInCell: [x1: mid - 3, y1: objBaseline + 4, x2: mid + 3, y2: objBaseline + 5],
cellSize: aptr.ob.size, cellInstOrient: orient], pos];
pr.drawRect[rect, aptr.ob.level, pr];
END;
Outline: PROC [context: Graphics.Context] = BEGIN
right: INT ← aptr.ob.size.x;
left: INT ← 0;
mid: INT ← aptr.ob.size.x / 2;
IF context = NIL THEN RETURN;
[] ← Graphics.SetFat[context, FALSE];
-- draw the stretch line
MoveTo[left, objBaseline, FALSE, aptr, pos, orient, context];
MoveTo[right, objBaseline, TRUE, aptr, pos, orient, context];
{ -- use our `Fetch' procedure so we know that it works the same as drawing
line: LineData ← Fetch[aptr];
pos1, pos2: CD.Position;
pos1 ← pos2 ← line.point;
IF line.direction = up OR line.direction = down THEN
pos2.x ← pos1.x + line.length
ELSE
pos2.y ← pos1.y + line.length;
Graphics.SetCP[context, pos1.x, pos1.y];
Graphics.DrawTo[context, pos2.x, pos2.y];
};
-- draw direction arrow
MoveTo[mid - 2, objBaseline + 0, FALSE, aptr, pos, orient, context];
MoveTo[mid - 2, objBaseline + 4, TRUE, aptr, pos, orient, context];
MoveTo[mid - 4, objBaseline + 4, TRUE, aptr, pos, orient, context];
MoveTo[mid - 0, objBaseline + 8, TRUE, aptr, pos, orient, context];
MoveTo[mid + 4, objBaseline + 4, TRUE, aptr, pos, orient, context];
MoveTo[mid + 2, objBaseline + 4, TRUE, aptr, pos, orient, context];
MoveTo[mid + 2, objBaseline + 0, TRUE, aptr, pos, orient, context];
IF NARROW[aptr.ob.specificRef, StretchObj].type = pointed THEN {
-- draw arrowheads
MoveTo[left + 2, objBaseline + 2, FALSE, aptr, pos, orient, context];
MoveTo[left + 0, objBaseline + 0, TRUE, aptr, pos, orient, context];
MoveTo[left + 2, objBaseline - 2, TRUE, aptr, pos, orient, context];
MoveTo[right - 2, objBaseline + 2, FALSE, aptr, pos, orient, context];
MoveTo[right - 0, objBaseline + 0, TRUE, aptr, pos, orient, context];
MoveTo[right - 2, objBaseline - 2, TRUE, aptr, pos, orient, context];
}
ELSE {
-- draw square ends
MoveTo[left + 1, objBaseline + 2, FALSE, aptr, pos, orient, context];
MoveTo[left + 0, objBaseline + 2, TRUE, aptr, pos, orient, context];
MoveTo[left + 0, objBaseline - 2, TRUE, aptr, pos, orient, context];
MoveTo[left + 1, objBaseline - 2, TRUE, aptr, pos, orient, context];
MoveTo[right - 1, objBaseline + 2, FALSE, aptr, pos, orient, context];
MoveTo[right - 0, objBaseline + 2, TRUE, aptr, pos, orient, context];
MoveTo[right - 0, objBaseline - 2, TRUE, aptr, pos, orient, context];
MoveTo[right - 1, objBaseline - 2, TRUE, aptr, pos, orient, context];
};
END;
CD.DrawToContext[pr, Color, markObj.level];
CD.DrawToContext[pr, Outline, aptr.ob.level];
END;
AddStretchLine: PROC [comm: CDSequencer.Command] = BEGIN
line: LineData;
IF ~OKCommand[comm] THEN RETURN[];
line.label ← TerminalIO.RequestRope["Enter label for new stretch line: "];
IF TerminalIO.UserSaysYes["Pointed?", "Make this a pointed stretch line?", TRUE] THEN
line.type ← pointed
ELSE
line.type ← blunt;
line.point ← comm.pos;
line.length ← defaultObjWidth;
Place[comm.design, line, FALSE];
END;
AddCellLine: PROC [comm: CDSequencer.Command] = BEGIN
line: LineData;
IF ~OKCommand[comm] THEN RETURN[];
line.label ← TerminalIO.RequestRope["Enter label for new cell stretch line: "];
line.type ← pointed;
line.point ← comm.pos;
line.length ← defaultObjWidth;
Place[comm.design, line, FALSE];
END;
AddLayerLine: PROC [comm: CDSequencer.Command] = BEGIN
line: LineData;
layer: Rope.ROPE ← Atom.GetPName[CD.LevelKey[CDPanel.FetchDefaultLevel[comm.design]]];
line.type ← blunt;
IF ~OKCommand[comm] THEN RETURN[];
line.label ← TerminalIO.RequestRope[Rope.Cat["Enter label for new '", layer, "' stretch line: "]];
line.type ← blunt;
line.point ← comm.pos;
line.length ← defaultObjWidth;
Place[comm.design, line, FALSE];
END;
WriteStretchLine: CD.InternalWriteProc -- PROC [me: ObPtr] -- = BEGIN
markObj: StretchObj = NARROW[me.specificRef];
TokenIO.WriteRope[markObj.label];
IF markObj.type = pointed THEN
TokenIO.WriteRope["P"]
ELSE
TokenIO.WriteRope["B"];
TokenIO.WriteInt[me.size.x];
TokenIO.WriteInt[markObj.level];
END;
ReadStretchLine: CD.InternalReadProc -- PROC [] RETURNS [ObPtr] -- = BEGIN
obj: CD.ObPtr ← NEW[CD.ObjectDefinition];
markObj: StretchObj ← NEW[StretchObjRec];
obj.p ← objsProcs;
obj.specificRef ← markObj;
markObj.label ← TokenIO.ReadRope[];
IF Rope.Equal[TokenIO.ReadRope[], "P"] THEN
markObj.type ← pointed
ELSE
markObj.type ← blunt;
obj.size.x ← TokenIO.ReadInt[];
markObj.level ← TokenIO.ReadInt[];
obj.level ← myLevel;
obj.size.y ← objHeight;
RETURN[obj];
END;
DescribeStretchLine: CD.DescribeProc -- PROC [me: ObPtr] RETURNS [Rope.ROPE] -- = BEGIN
markObj: StretchObj = NARROW[me.specificRef];
IF markObj.type = pointed THEN
RETURN[Rope.Cat["Cell stretch line labeled '", markObj.label, "'"]]
ELSE
RETURN[Rope.Cat["Layer '", Atom.GetPName[CD.LevelKey[markObj.level]], "' stretch line labeled '", markObj.label, "'"]];
END;
SetSize: CDCallSpecific.CallProc = BEGIN
newLength: CD.DesignNumber ← NARROW[x, REF CD.DesignNumber]^;
aptr.ob.size.x ← MAX[minObjWidth, newLength];
repaintMe ← TRUE;
END;
AdjustSize: CDCallSpecific.CallProc = BEGIN
amount: INT;
IF x=NIL THEN
amount ← CD.lambda
ELSE IF ISTYPE [x, REF CD.DesignPosition] THEN {
p: CD.DesignPosition ← NARROW[x, REF CD.DesignPosition]^;
IF p.x = 0 THEN
amount ← p.y
ELSE
amount ← p.x;
}
ELSE {
done←FALSE;
RETURN;
};
aptr.ob.size.x ← MAX[minObjWidth, MAX[CD.lambda, aptr.ob.size.x + amount]];
repaintMe ← TRUE;
END;
-- Top-level command routines
Init: PROC[] = BEGIN
error: BOOLFALSE;
tmp: REF CD.ObjectProcs;
error ← FALSE;
tmp ← CD.RegisterObjectType[objAtom ! CD.Error => {error ← TRUE; CONTINUE}];
IF tmp = NIL THEN error ← TRUE;
IF ~error THEN {
myLevel ← CD.NewLevel[NIL, $StretchLineLevel];
objsProcs ← tmp;
}
ELSE {
error ← FALSE;
objsProcs ← CD.FetchObjectProcs[objAtom! CD.Error => {error ← TRUE; CONTINUE}];
myLevel ← CD.FetchLevel[NIL, $StretchLineLevel ! CD.Error => {error ← TRUE; CONTINUE}];
IF error THEN objsProcs ← NIL;
};
IF objsProcs = NIL THEN {
TerminalIO.WriteRope["Error: Can't register stretch-line object procs.\n"];
RETURN;
};
objsProcs.drawMe ← DrawStretchMark;
objsProcs.internalWrite ← WriteStretchLine;
objsProcs.internalRead ← ReadStretchLine;
objsProcs.describe ← DescribeStretchLine;
objsProcs.hasChildren ← FALSE;
objsProcs.wireTyped ← FALSE;
CDCallSpecific.Register[$Lengthen, objsProcs, AdjustSize];
CDCallSpecific.Register[$Widen, objsProcs, AdjustSize];
CDCallSpecific.Register[$SetLength, objsProcs, SetSize];
CDCallSpecific.Register[$SetWidth, objsProcs, SetSize];
CDSequencer.ImplementCommand[$AddStretchLine, AddStretchLine];
CDSequencer.ImplementCommand[$AddCellLine, AddCellLine];
CDSequencer.ImplementCommand[$AddLayerLine, AddLayerLine];
TilerMenu.Register["Add Cell Stretch Line", $AddCellLine];
TilerMenu.Register["Add Layer Stretch Line", $AddLayerLine];
END;
-- Main body
Init[];
END.