<> <> <> <> 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_0] = { 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_1] = { 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_0] = { <<--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}; <> <> <> <> <> 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.