CDStretchCommands.mesa (part of ChipNDale)
Copyright © 1984 by Xerox Corporation. All rights reserved.
Created by Christian Jacobi, July 11, 1983 3:42 pm
Last edited by: Christian Jacobi, October 31, 1986 10:22:00 am PST
DIRECTORY
CD,
CDAtomicObjects,
CDBasics,
CDInstances,
CDOps,
CDProperties,
CDRects,
CDSequencer,
CDStretchyBackdoor,
CDValue,
CDViewer,
IO,
TerminalIO,
ViewerClasses,
ViewerOps;
CDStretchCommands:
CEDAR
PROGRAM
IMPORTS CD, CDAtomicObjects, CDBasics, CDInstances, CDOps, CDProperties, CDRects, CDSequencer, CDStretchyBackdoor, CDValue, CDViewer, IO, TerminalIO, ViewerOps =
BEGIN
--common
StepValue:
PROC[design:
CD.Design]
RETURNS [n:
CD.Number] = {
n ← CDValue.FetchInt[boundTo: design, key: $CDxStepValue, propagation: global];
IF n<=0 THEN n ← design.technology.lambda
};
WireTyped:
PROC [ob:
CD.Object]
RETURNS [
BOOL] =
INLINE {
RETURN [ob.class.wireTyped]
};
--stretch an edge in any direction
FiddlePos:
PROC [inst:
CD.Instance, ir:
CD.Rect] = {
MoreZero:
PROC [a, b:
INT]
RETURNS [
INT𡤀] = {
IF a>=0 AND b>=0 THEN RETURN [MIN[a, b]];
IF a<=0 AND b<=0 THEN RETURN [MAX[a, b]];
};
r: CD.Rect ← CDInstances.InstRectI[inst];
inst.trans.off.x ← inst.trans.off.x + MoreZero[ir.x1-r.x1, ir.x2-r.x2];
inst.trans.off.y ← inst.trans.off.y + MoreZero[ir.y1-r.y1, ir.y2-r.y2];
};
ExactNewSize:
PROC [ob:
CD.Object, sz:
CD.Position]
RETURNS [ob1:
CD.Object] = {
NewSize:
PROC [ob:
CD.Object, sz:
CD.Position]
RETURNS [ob1:
CD.Object] = {
SELECT
TRUE
FROM
ob.class.wireTyped =>
ob1 ← CDRects.CreateRect[sz, ob.layer];
CDAtomicObjects.IsAtomicOb[ob] =>
ob1 ← CDAtomicObjects.CreateAtomicOb[ob.class.objectType, sz, ob.class.technology, ob.layer];
ENDCASE => ob1 ← CDStretchyBackdoor.MakeSimilar[ob, CDBasics.RectAt[[0, 0], sz]];
IF ob1=NIL THEN ob1 ← ob;
};
ob1 ← NewSize[ob, sz];
IF CD.InterestSize[ob1]#sz THEN ob1 ← ob
};
AddDelta:
PROC [r1, r2:
CD.Rect]
RETURNS [r:
CD.Rect] = {
r.x1 ← r1.x1+r2.x1;
r.x2 ← r1.x2+r2.x2;
r.y1 ← r1.y1+r2.y1;
r.y2 ← r1.y2+r2.y2;
};
StretchIt:
PROC [design:
CD.Design, deltaR:
CD.Rect] = {
count: INT ← 0;
amount: CD.Position ← [deltaR.x2-deltaR.x1, deltaR.y2-deltaR.y1];
FOR list:
CD.InstanceList ← CDOps.InstList[design], list.rest
WHILE list#
NIL
DO
IF list.first.selected
THEN {
inst: CD.Instance ← list.first;
stretchSize: CD.Position ← CDBasics.OrientedSize[amount, inst.trans.orient];
goal: CD.Rect ← AddDelta[CDInstances.InstRectI[inst], deltaR];
CDOps.RedrawInstance[design, inst, TRUE];
inst.ob ← ExactNewSize[inst.ob, CDBasics.AddPoints[stretchSize, CD.InterestSize[inst.ob]]];
FiddlePos[inst, goal];
CDOps.RedrawInstance[design, inst, FALSE];
count ← count+1;
}
ENDLOOP;
TerminalIO.PutF1["tried to stretch %g objects\n", IO.int[count]];
};
PosDelta:
PROC [comm: CDSequencer.Command, dir:
ATOM]
RETURNS [delta:
CD.Rect←[0, 0, 0, 0]] = {
pointedInst: CD.Instance ← CDInstances.InstanceAt[il: CDOps.InstList[comm.design], pos: comm.pos, selectedOnly: TRUE];
IF pointedInst=NIL THEN CDSequencer.Quit[" no selected object pointed\n"]
ELSE {
r: CD.Rect ← CDInstances.InstRectI[pointedInst];
pos: CD.Position ← comm.pos;
amount: CD.Number ← StepValue[comm.design];
mouseRight: BOOL = ABS[r.x1-pos.x] > ABS[r.x2-pos.x];
mouseUp: BOOL = ABS[r.y1-pos.y] > ABS[r.y2-pos.y];
IF pointedInst=NIL THEN CDSequencer.Quit[" no selected object pointed\n"];
SELECT dir
FROM
$west => IF mouseRight THEN delta.x2 ← -amount ELSE delta.x1 ← -amount;
$east => IF mouseRight THEN delta.x2 ← amount ELSE delta.x1 ← amount;
$south =>IF mouseUp THEN delta.y2 ← -amount ELSE delta.y1 ← -amount;
$north => IF mouseUp THEN delta.y2 ← amount ELSE delta.y1 ← amount;
ENDCASE => ERROR;
}
};
StretchCommand:
PROC [comm: CDSequencer.Command] = {
GetDelta:
PROC [r:
CD.Rect, from, to:
CD.Position]
RETURNS [dr:
CD.Rect ← [0, 0, 0, 0]] = {
--given an interest rect and a vector,
--compute a delta for a stretch (rect to add)
IF
ABS[to.x-from.x]>
ABS[to.y-from.y]
THEN {
--horizontal stretch vector
IF ABS[r.x1-from.x] < ABS[r.x2-from.x] THEN dr.x1 ← to.x-from.x --left edge
ELSE dr.x2 ← to.x-from.x --right edge
}
ELSE {
--vertical stretch vector
IF ABS[r.y1-from.y] < ABS[r.y2-from.y] THEN dr.y1 ← to.y-from.y --bottom edge
ELSE dr.y2 ← to.y-from.y --top edge
};
};
VectorStretch:
PROC [design:
CD.Design, from, to:
CD.Position] = {
deltaR: CD.Rect;
pointedInst: CD.Instance ← CDInstances.InstanceAt[il: CDOps.InstList[design], pos: from, selectedOnly: TRUE];
IF pointedInst=
NIL
THEN {
TerminalIO.PutRope["failed: must point to an instance\n"];
RETURN
};
deltaR ← GetDelta[CDInstances.InstRectI[pointedInst], from, to];
StretchIt[design, deltaR];
};
TerminalIO.PutRope["stretch selected\n"];
VectorStretch[design: comm.design, from: comm.sPos, to: comm.pos]
};
StretchStepLeftCommandS:
PROC [comm: CDSequencer.Command] = {
TerminalIO.PutRope["Stretch step selected left"];
StretchIt[comm.design, PosDelta[comm, $west]];
};
StretchStepRightCommandS:
PROC [comm: CDSequencer.Command] = {
TerminalIO.PutRope["Stretch step selected right"];
StretchIt[comm.design, PosDelta[comm, $east]];
};
StretchStepUpCommandS:
PROC [comm: CDSequencer.Command] = {
TerminalIO.PutRope["Stretch step selected up"];
StretchIt[comm.design, PosDelta[comm, $north]];
};
StretchStepDownCommandS:
PROC [comm: CDSequencer.Command] = {
TerminalIO.PutRope["Stretch step selected down"];
StretchIt[comm.design, PosDelta[comm, $south]];
};
--cut wires
CutInstance:
PROC [design:
CD.Design, inst:
CD.Instance, from, to:
CD.Position, gridding: CD.Number𡤁] = {
NearlyRoundToLambda:
PROC[x:
CD.Number]
RETURNS[r:
CD.Number] = {
r ← (x/gridding)*gridding;
};
--The mouse track defines the cut vector. Let's define the "spine" of a wire as the centerline, roughly in the middle (lambda) and following the visible length, i.e. the longer edge, not the CD length. Then we cut the wire at the position where the spine meets the cut vector. The cut always reduces the visible length of the wire. (Pff, not easy to describe without drawing!)
--A vertical wire freshly drawn has even (0) orientation.
cutPos: CD.Position;
L1, L2, W1, W2: CD.Number;
wireLeft, wireRight: CD.Object;
inter: CD.Position; -- the intersection of the two lines, if any, and the center of the wire
oldRect: CD.Rect ← CDInstances.InstRectI[inst]; -- the bounding box
oldSize: CD.Position ← CDBasics.SizeOfRect[oldRect];
vertical: BOOL ← oldSize.y > oldSize.x;
dx: CD.Number ← to.x-from.x; dy: CD.Number ← to.y-from.y;
ctr: CD.Position ← CDBasics.Center[oldRect];
--Test if vector parallel to wire
IF vertical AND (dx=0) OR (NOT vertical) AND (dy=0) THEN RETURN;
--Now we can divide and find the intersection of lines (not yet segments)
gridding ← MAX[1, gridding];
inter ←
IF vertical
THEN [ctr.x, (from.y+(dy*(ctr.x-from.x)/dx))/gridding*gridding]
ELSE [(dx*(ctr.y-from.y)/dy+from.x)/gridding*gridding, ctr.y];
--Monier-Sindhu theorem: if inter is in cursorBox and in oldRect, then it is the intersection of segments
IF NOT (CDBasics.InsidePos[inter, CDBasics.ToRect[from, to]] AND CDBasics.InsidePos[inter, CDBasics.Extend[oldRect, -1]]) THEN RETURN;
cutPos ← IF vertical THEN [oldRect.x1, inter.y] ELSE [inter.x, oldRect.y1];
IF vertical
THEN {
L1 ← cutPos.y - oldRect.y1;
L2 ← oldSize.y - L1;
W1 ← W2 ← oldSize.x;
}
ELSE {
L1 ← L2 ← oldSize.y;
W1 ← cutPos.x - oldRect.x1;
W2 ← oldSize.x - W1;
};
wireLeft ← CDRects.CreateRect[CDBasics.OrientedSize[[W1, L1], inst.trans.orient], inst.ob.layer];
wireRight ← CDRects.CreateRect[CDBasics.OrientedSize[[W2, L2], inst.trans.orient], inst.ob.layer];
IF CDBasics.IncludesOddRot90[inst.trans.orient]
THEN {
CDOps.IncludeInstance[design,
CDInstances.NewInst[wireLeft,
[CDBasics.AddPoints[CDBasics.BaseOfRect[oldRect], [CD.InterestSize[wireLeft].y, 0]], rotate90],
CDProperties.DCopyProps[inst.properties],
TRUE],
FALSE];
CDOps.IncludeInstance[design,
CDInstances.NewInst[wireRight,
[CDBasics.AddPoints[cutPos, [CD.InterestSize[wireRight].y, 0]], rotate90],
CDProperties.DCopyProps[inst.properties],
TRUE],
FALSE];
}
ELSE {
CDOps.IncludeInstance[design, CDInstances.NewInst[wireLeft, [CDBasics.BaseOfRect[oldRect], original], CDProperties.DCopyProps[inst.properties], TRUE], FALSE];
CDOps.IncludeInstance[design, CDInstances.NewInst[wireRight, [cutPos, original], CDProperties.DCopyProps[inst.properties], TRUE], FALSE];
};
CDOps.RemoveInstance[design, inst];
};
CutWireComm:
PROC [comm: CDSequencer.Command] = {
GetGrid:
PROC [comm: CDSequencer.Command]
RETURNS [g:
CD.Number𡤀] = {
--figures out grid used for a particular design or viewer
v: ViewerClasses.Viewer ← CDViewer.GetViewer[comm];
IF v#
NIL
THEN {
WITH ViewerOps.GetViewer[v, $Grid]
SELECT
FROM
ri: REF INT => g ← ri^;
ENDCASE => NULL;
};
IF g<=0 THEN g ← comm.design.technology.lambda;
};
DoAllCuts:
PROC [design:
CD.Design, from, to:
CD.Position, gridding:
CD.Number ← 1] = {
FOR list:
CD.InstanceList ← CDOps.InstList[design], list.rest
WHILE list#
NIL
DO
IF list.first.selected
AND list.first.ob.class.wireTyped
THEN
CutInstance[design, list.first, from, to, gridding];
ENDLOOP;
};
grid: CD.Number ← GetGrid[comm];
TerminalIO.PutRopes["cut selected with grid ", CDOps.LambdaRope[grid, comm.design.technology.lambda], "\n"];
DoAllCuts[design: comm.design, from: comm.sPos, to: comm.pos, gridding: grid]
};
--stretchy moves
StretchyMoveSelected:
PROC [design:
CD.Design, offset:
CD.Position] = {
selIL, otherIL: CD.InstanceList ← NIL;
[selIL, otherIL] ← CDInstances.SplitSelected[CDOps.InstList[design]];
StretchyMove[design, offset, selIL, otherIL]
};
Primality:
TYPE = {primary, secondary};
if a wire is fiddled primary:
its area might be stretched in direction of wire length
its area might be moved in direction of wire width
if a wire is fiddled secondary:
its area might be stretched in direction of wire length
StretchList: TYPE = LIST OF StretchRef;
StretchRef: TYPE = REF StretchRec;
StretchRec:
TYPE =
RECORD [
inst: CD.Instance,
conductRect: CD.Rect, -- hint only
near: BOOL ← FALSE, -- match at small edge of conductRect
far: BOOL ← FALSE, -- match at large edge of conductRect
horizontal: BOOL ← FALSE
];
StretchyMove:
PROC[design:
CD.Design, offset:
CD.Position, selIL, otherIL:
CD.InstanceList] = {
primInsts: CD.InstanceList ← NIL;
primList, secondList, nonPrimList: StretchList ← NIL;
rect: CD.Rect ← CDInstances.BoundingRectO[selIL];
FOR l:
CD.InstanceList ← otherIL, l.rest
WHILE l#
NIL
DO
IF WireTyped[l.first.ob]
THEN {
me: StretchRef ←
NEW[StretchRec←[
inst: l.first,
conductRect: CDInstances.InstRectI[l.first],
near: FALSE,
far: FALSE,
horizontal: CDBasics.IncludesOddRot90[l.first.trans.orient] -- y dir is length !
]];
IF CDBasics.Intersect[rect, me.conductRect]
AND HasMatch[me, selIL, primary]
THEN {
primList ← CONS[me, primList];
primInsts ← CONS[me.inst, primInsts]
}
ELSE nonPrimList ← CONS[me, nonPrimList]
};
ENDLOOP;
FOR l: StretchList ← nonPrimList, l.rest
WHILE l#
NIL
DO
IF HasMatch[l.first, primInsts, secondary] THEN secondList ← CONS[l.first, secondList]
ENDLOOP;
FOR l: StretchList ← secondList, l.rest
WHILE l#
NIL
DO
FiddleWire[design, l.first, offset, secondary]
ENDLOOP;
FOR l: StretchList ← primList, l.rest
WHILE l#
NIL
DO
FiddleWire[design, l.first, offset, primary]
ENDLOOP;
FOR l:
CD.InstanceList ← selIL, l.rest
WHILE l#
NIL
DO
MoveInst[design, l.first, offset]
ENDLOOP;
};
FiddleWire:
PROC [design:
CD.Design, segment: StretchRef, offset:
CD.Position, class: Primality] = {
ChangeWireLength:
PROC [inst:
CD.Instance, amount:
CD.Number] = {
sz: CD.Position = CD.InterestSize[inst.ob];
newOb: CD.Object ← CDRects.CreateRect[CD.Position[x: sz.x, y: sz.y+amount], inst.ob.layer];
IF newOb#NIL THEN inst.ob ← newOb
};
r: CD.Rect ← segment.conductRect;
stretch: CD.Number ← (IF segment.horizontal THEN offset.x ELSE offset.y);
move: CD.Position ← (IF class=primary THEN offset ELSE [0, 0]); --of conductRect, not inst
IF segment.horizontal THEN move.x ← 0 ELSE move.y ← 0;
IF ~segment.near AND ~segment.far THEN RETURN;
CDOps.RedrawInstance[design, segment.inst];
IF segment.near
THEN {
ChangeWireLength[segment.inst, -stretch];
IF segment.horizontal THEN r.x1 ← r.x1+stretch ELSE r.y1 ← r.y1+stretch
};
IF segment.far
THEN {
ChangeWireLength[segment.inst, stretch];
IF segment.horizontal THEN r.x2 ← r.x2+stretch ELSE r.y2 ← r.y2+stretch
};
r ← CDBasics.MoveRect[r, move];
segment.inst.trans ← CDOps.FitObjectI[segment.inst.ob, CDBasics.BaseOfRect[r], segment.inst.trans.orient];
CDOps.RedrawInstance[design, segment.inst, FALSE];
};
HasMatch:
PROC [me: StretchRef, list:
CD.InstanceList, prim: Primality]
RETURNS [
BOOL] = {
-- checks weather me has some match with any of list
-- list:
near, far: BOOL;
nearEdge, farEdge: CD.Rect;
nearEdge ← farEdge ← me.conductRect; -- edges at near or far end of wire, parallel to width
IF me.horizontal
THEN {
farEdge.x1 ← me.conductRect.x2;
nearEdge.x2 ← me.conductRect.x1;
}
ELSE {
farEdge.y1 ← me.conductRect.y2;
nearEdge.y2 ← me.conductRect.y1;
};
FOR l:
CD.InstanceList ← list, l.rest
WHILE l#
NIL
DO
r: CD.Rect = CDInstances.InstRectI[l.first];
near ← CDBasics.Intersect[nearEdge, r];
far ← CDBasics.Intersect[farEdge, r];
IF near
OR far
THEN {
SELECT
TRUE
FROM
l.first.ob.class.symbolic => near ← far ← FALSE;
WireTyped[l.first.ob] => {
IF l.first.ob.layer#me.inst.ob.layer THEN near ← far ← FALSE
ELSE
IF prim#primary
THEN
IF me.horizontal=CDBasics.IncludesOddRot90[l.first.trans.orient]
THEN
near ← far ← FALSE
};
CDStretchyBackdoor.HasMatchProc[l.first.ob] => {
IF ~CDStretchyBackdoor.Match[l.first.ob, me.conductRect, me.inst.ob.layer, prim=primary, me.horizontal] THEN near ← far ← FALSE
};
l.first.ob.class.inDirectory => NULL;
l.first.ob.layer=me.inst.ob.layer => NULL;
ENDCASE => near ← far ← FALSE;
me.near ← me.near OR near;
me.far ← me.far OR far;
};
ENDLOOP;
RETURN [me.near OR me.far]
};
MoveInst:
PROC [design:
CD.Design, inst:
CD.Instance, offset:
CD.Position] = {
CDOps.RedrawInstance[design, inst];
inst.trans.off ← CDBasics.AddPoints[inst.trans.off, offset];
CDOps.RedrawInstance[design, inst, FALSE];
};
StretchyMoveSCommand:
PROC [comm: CDSequencer.Command] = {
TerminalIO.PutRope["move selected stretchy\n"];
StretchyMoveSelected[design: comm.design, offset: CD.Position[comm.pos.x-comm.sPos.x, comm.pos.y-comm.sPos.y]]
};
StretchyMoveStepUpS:
PROC [comm: CDSequencer.Command] = {
TerminalIO.PutRope["stretchy move selected step up\n"];
StretchyMoveSelected[comm.design, CD.Position[0, StepValue[comm.design]]]
};
StretchyMoveStepDownS:
PROC [comm: CDSequencer.Command] = {
TerminalIO.PutRope["stretchy move selected step down\n"];
StretchyMoveSelected[comm.design, CD.Position[0, -StepValue[comm.design]]]
};
StretchyMoveStepLeftS:
PROC [comm: CDSequencer.Command] = {
TerminalIO.PutRope["stretchy move step left\n"];
StretchyMoveSelected[comm.design, CD.Position[-StepValue[comm.design], 0]]
};
StretchyMoveStepRightS:
PROC [comm: CDSequencer.Command] = {
TerminalIO.PutRope["stretchy move step right\n"];
StretchyMoveSelected[comm.design, CD.Position[StepValue[comm.design], 0]]
};
--stretches
CDSequencer.ImplementCommand[$StretchS, StretchCommand];
CDSequencer.ImplementCommand[$StretchStepLeftS, StretchStepLeftCommandS];
CDSequencer.ImplementCommand[$StretchStepRightS, StretchStepRightCommandS];
CDSequencer.ImplementCommand[$StretchStepUpS, StretchStepUpCommandS];
CDSequencer.ImplementCommand[$StretchStepDownS, StretchStepDownCommandS];
--cut wires
CDSequencer.ImplementCommand[$CutWireS, CutWireComm];
--stretchy moves
CDSequencer.ImplementCommand[$StretchyMoveS, StretchyMoveSCommand];
CDSequencer.ImplementCommand[$StretchyMoveStepUpS, StretchyMoveStepUpS];
CDSequencer.ImplementCommand[$StretchyMoveStepDownS, StretchyMoveStepDownS];
CDSequencer.ImplementCommand[$StretchyMoveStepLeftS, StretchyMoveStepLeftS];
CDSequencer.ImplementCommand[$StretchyMoveStepRightS, StretchyMoveStepRightS];
END.