<> <> <> <> <<>> DIRECTORY Basics, CD, CDBasics, CDCells, CDDirectory, CDInstances, CDLayers, CDOps, CDPanelFonts, CDProperties, CDRects, CDSatellites, CDSequencer, CDTexts, Core, CoreOps, CoreGeometry, ExtractOps, GList, IO, Rope, RopeList, IconConstruction, Sisyph, TerminalIO, PWCore, Convert, RuntimeError; IconConstructionImpl: CEDAR PROGRAM IMPORTS Basics, CD, CDBasics, CDCells, CDDirectory, CDInstances, CDLayers, CDOps, CDPanelFonts, CDProperties, CDRects, CDSatellites, CDSequencer, CDTexts, CoreOps, CoreGeometry, ExtractOps, GList, IO, Rope, RopeList, Sisyph, TerminalIO, PWCore, Convert, RuntimeError EXPORTS IconConstruction ~ BEGIN <> NewShell: PUBLIC PROC [name: Rope.ROPE, pins: ARRAY CoreGeometry.Side OF LIST OF Rope.ROPE, design: CD.Design, pinLayer: CD.Layer _ CD.commentLayer, boxLayer: CD.Layer _ CD.commentLayer] RETURNS [shell: IconConstruction.Shell] ~ { <> pinWidth: CD.Number = CDLayers.LayerWidth[design, pinLayer]; shell _ NEW [IconConstruction.ShellRep _ [ name: name, pinLayer: pinLayer, boxLayer: boxLayer, boxWidth: CDLayers.LayerWidth[design, boxLayer], pinRect: [x: pinWidth, y: 4*pinWidth], guard: 4, grid: CDOps.GetGrid[design], font: CDPanelFonts.CurrentFont[design], pins: pins]]}; ShellObject: PUBLIC PROC [shell: IconConstruction.Shell] RETURNS [obj: CD.Object] ~ { <> <> <<- the outermost left/bottom point of pins are grid aligned (+pin width on top & right)>> <<- BuildSideGeometry creates the instances in such a way that the first pin has its lower-left point at (0,0) and that texts may be edited by CD and still stick to the pin>> <<- CD texts need some massaging so that a horizontal text is exactly on the middle of the horizontal axis (c.f. fontCorrection...)>> <<- the numerous orientations used are critical as they allow easier positionning & permit the correct edition of texts>> obj _ ShellObjectInner[shell]}; ShellObjectInner: PROC [shell: IconConstruction.Shell, minSize: CD.Position _ [0,0]] RETURNS [obj: CD.Object] ~ { GridUp: PROC [x: CD.Number] RETURNS [v: CD.Number] ~ <> {v _ IF x>=0 THEN ((x+grid-1)/grid)*grid ELSE (x/grid)*grid}; GridDown: PROC [x: CD.Number] RETURNS [v: CD.Number] ~ <> {v _ IF x>=0 THEN (x/grid)*grid ELSE ((x-grid+1)/grid)*grid}; GridRectUp: PROC [r: CD.Rect] RETURNS [v: CD.Rect] ~ <> {v _ [x1: GridDown[r.x1], x2: GridUp[r.x2], y1: GridDown[r.y1], y2: GridUp[r.y2]]}; MakeTitle: PROC [text: Rope.ROPE, font: CDTexts.CDFont] RETURNS [inst: CD.Instance, size: CD.Position] ~ { <> fName: Rope.ROPE = font.supposedName; obj: CD.Object; IF Rope.IsEmpty[text] THEN RETURN [NIL, [0,0]]; IF Rope.Find[fName, "I", Rope.Length[fName]-1, FALSE]=-1 THEN font _ CDTexts.MakeFont[fName.Concat["I"], font.scaleI]; obj _ CDTexts.Create[text, font]; size _ CDBasics.SizeOfRect[obj.bbox]; inst _ CDInstances.NewInst[obj, [[-size.x/2, font.origin.y - font.height/2], original]]; size.x _ GridUp[(3*size.x)/2]; -- increase a bit the area taken by the title size.y _ GridUp[2*size.y]}; -- increase a bit the area taken by the title BuildSideGeometry: PROC [names: LIST OF Rope.ROPE, pinOrient, textOrient: CD.Orientation, textOffset: CD.Position] RETURNS [iList: CD.InstanceList, size: CD.Position]~ { <> pin: CD.Object = CDRects.CreateRect[shell.pinRect, shell.pinLayer]; -- all pins are the same step: CD.Position = CDBasics.OrientedSize[[GridUp[shell.font.height], 0], pinOrient]; -- between pins pinPos: CD.Position _ [0, 0]; iList _ NIL; WHILE names#NIL DO pinInst: CD.Instance = CDInstances.NewInst[pin, [pinPos, pinOrient]]; textInst: CD.Instance = CDInstances.NewInst[CDTexts.Create[names.first, shell.font], [CDBasics.AddPoints[pinPos, textOffset], textOrient]]; pinPos _ CDBasics.AddPoints[pinPos, step]; CDSatellites.Associate[master: pinInst, text: textInst]; iList _ CONS[pinInst, CONS[textInst, iList]]; names _ names.rest; ENDLOOP; IF iList#NIL THEN size _ CDBasics.SizeOfRect[GridRectUp[CDInstances.BoundingRectO[iList]]] ELSE size _ [0, 0]}; AddBoxSide: PROC [length: CD.Number, at: CD.Position, orient: CD.Orientation] ~ { CDProperties.PutInstanceProp[ CDCells.IncludeOb[cell: obj, ob: CDRects.CreateRect[[shell.boxWidth, length], shell.boxLayer], trans: [at, orient], mode: dontResize].newInst, Sisyph.mode.extractProcProp, $ExtractNull]}; AddInstances: PROC [insts: CD.InstanceList, moveBy: CD.Position] ~ { <> WHILE insts#NIL DO insts.first.trans.off _ CDBasics.AddPoints[insts.first.trans.off, moveBy]; [] _ CDCells.IncludeInstance[cell: obj, inst: insts.first, mode: dontResize]; insts _ insts.rest; ENDLOOP}; Center: PROC [mid, left, right, tot: CD.Number] RETURNS [v: CD.Number] ~ { <
> v _ GridUp[(tot+left-mid-right+shell.font.height)/2]}; grid: CD.Number = shell.grid; fontCorrection: CD.Number = shell.font.origin.y - shell.font.height/2; -- CDTexts are crazy... guard: CD.Number = shell.guard+shell.boxWidth; -- the real guard margin: CD.Number = GridUp[2*(shell.pinRect.y+shell.boxWidth)]; -- left/right safety margin iLeft, iRight, iTop, iBottom: CD.InstanceList; -- list of instances for pins on all 4 sides sLeft, sRight, sTop, sBottom: CD.Position; -- bbox for the 4 sides title: CD.Instance; titleSize: CD.Position; box: CD.Rect; width, height: CD.Number; <> obj _ CDCells.CreateEmptyCell[]; <> [iTop, sTop] _ BuildSideGeometry[names: shell.pins[top], pinOrient: original, textOrient: rotate270, textOffset: [fontCorrection, -guard]]; [iBottom, sBottom] _ BuildSideGeometry[names: shell.pins[bottom], pinOrient: rotate180X, textOrient: rotate90X, textOffset: [fontCorrection, guard]]; [iRight, sRight] _ BuildSideGeometry[names: shell.pins[right], pinOrient: rotate90X, textOrient: mirrorX, textOffset: [-guard, fontCorrection]]; [iLeft, sLeft] _ BuildSideGeometry[names: shell.pins[left], pinOrient: rotate90, textOrient: original, textOffset: [guard, fontCorrection]]; [title, titleSize] _ MakeTitle[shell.name, shell.font]; <> width _ MAX[sLeft.x+sRight.x+MAX[sBottom.x, sTop.x, titleSize.x], minSize.x]; height _ MAX[sLeft.y+margin, sRight.y+margin, sBottom.y+sTop.y+titleSize.y, minSize.y]; width _ GridUp[width]; height _ GridUp[height]; box _ [ -- touching all pins on the inside x1: IF iLeft=NIL THEN 0 ELSE shell.pinRect.y, x2: shell.pinRect.x + (IF iRight=NIL THEN width ELSE width-shell.pinRect.y), y1: IF iBottom=NIL THEN 0 ELSE shell.pinRect.y, y2: shell.pinRect.x + (IF iTop=NIL THEN height ELSE height-shell.pinRect.y)]; <> AddInstances[iBottom, [Center[sBottom.x, sLeft.x, sRight.x, width], box.y1]]; AddInstances[iTop, [Center[sTop.x, sLeft.x, sRight.x, width], box.y2]]; AddInstances[iLeft, [box.x1, Center[sLeft.y, 0, 0, height]]]; AddInstances[iRight, [box.x2, Center[sRight.y, 0, 0, height]]]; AddBoxSide[box.y2-box.y1, [box.x1, box.y1], original]; -- left AddBoxSide[box.y2-box.y1, [box.x2, box.y2], rotate180]; -- right AddBoxSide[box.x2-box.x1, [box.x2, box.y1], rotate90]; -- bot AddBoxSide[box.x2-box.x1, [box.x1, box.y2], rotate270]; -- top IF title#NIL THEN -- reposition the title in the center of the inside of the icon AddInstances[LIST[title], [(width+sLeft.x-sRight.x)/2, (height+sBottom.y-sTop.y)/2]]; <> CDCells.ToSequenceMode[obj]; [] _ CDCells.ResizeCell[design: NIL, cell: obj]}; CellTypePinsSrcPos: PUBLIC PROC [cell: Core.CellType] RETURNS [pins: ARRAY CoreGeometry.Side OF LIST OF Rope.ROPE] ~ { <> EachWire: CoreOps.EachWireProc ~ { EachPin: CoreGeometry.EachPinProc ~ {elemsOn[side] _ CONS [NEW[ElemRep _ [name, min]], elemsOn[side]]}; name: Rope.ROPE = CoreOps.GetShortWireName[wire]; IF Rope.IsEmpty[name] THEN TerminalIO.PutRope["*** Warning: Unnamed public.\n"] ELSE [] _ CoreGeometry.EnumerateSides[Sisyph.mode.decoration, cell, wire, EachPin]; RETURN[FALSE, FALSE]}; Increasing: GList.CompareProc ~ { -- Inverted order because of later list copy !!! RETURN [Basics.CompareInt[NARROW [ref2, Elem].at, NARROW [ref1, Elem].at]]}; Elem: TYPE ~ REF ElemRep; Elems: TYPE ~ LIST OF Elem; ElemRep: TYPE ~ RECORD [name: Rope.ROPE, at: INT]; elemsOn: ARRAY CoreGeometry.Side OF Elems _ ALL [NIL]; [] _ CoreOps.VisitWireSeq[cell.public, EachWire]; -- build list for each side FOR side: CoreGeometry.Side IN CoreGeometry.Side DO -- sort each side & build icon list: Elems _ NARROW[GList.Sort[elemsOn[side], Increasing]]; pins[side] _ NIL; WHILE list#NIL DO IF NOT RopeList.Memb[pins[side], list.first.name] THEN pins[side] _ CONS [list.first.name, pins[side]]; list _ list.rest; ENDLOOP; ENDLOOP}; CellTypePinsAlpha: PUBLIC PROC [cell: Core.CellType] RETURNS [pins: ARRAY CoreGeometry.Side OF LIST OF Rope.ROPE] ~ { <> <> EachWire: CoreOps.EachWireProc ~ { EachPin: CoreGeometry.EachPinProc ~ {pins[side] _ CONS [name, pins[side]]}; name: Rope.ROPE = CoreOps.GetShortWireName[wire]; IF Rope.IsEmpty[name] THEN TerminalIO.PutRope["*** Warning: Unnamed public.\n"] ELSE { [] _ CoreGeometry.EnumerateSides[Sisyph.mode.decoration, cell, wire, EachPin]; RETURN[FALSE, FALSE]}}; -- use highest level name only [] _ CoreOps.VisitWireSeq[cell.public, EachWire]; -- build list for each side FOR side: CoreGeometry.Side IN CoreGeometry.Side DO -- sort and remove multiple entries pins[side] _ RopeList.Sort[pins[side], RopeList.IgnoreCase]; pins[side] _ RopeList.Reverse[pins[side]]; FOR list: LIST OF Rope.ROPE _ pins[side], list.rest WHILE list#NIL DO WHILE list.rest#NIL AND list.first.Equal[list.rest.first] DO list.rest _ list.rest.rest; ENDLOOP; ENDLOOP; ENDLOOP}; CellTypePinsLayout: PUBLIC PROC [cell: Core.CellType] RETURNS [pins: ARRAY CoreGeometry.Side OF LIST OF Rope.ROPE] ~ { <> [] _ PWCore.Layout[cell]; FOR i: INT IN [0..cell.public.size) DO EachWire: CoreOps.EachWireProc ~ { EachPin: CoreGeometry.EachPinProc ~{ IF pins[side]=NIL OR pins[side].first#name THEN pins[side] _ CONS [name, pins[side]]}; IF wire.size=0 THEN [] _ CoreGeometry.EnumerateSides [PWCore.extractMode.decoration, cell, wire, EachPin]}; name: Rope.ROPE = CoreOps.GetShortWireName[cell.public[i]]; [] _ CoreOps.VisitWire[cell.public[i], EachWire] ENDLOOP; FOR side: CoreGeometry.Side IN CoreGeometry.Side DO -- sort and remove multiple entries pins[side] _ RopeList.Sort[pins[side], RopeList.IgnoreCase]; pins[side] _ RopeList.Reverse[pins[side]]; FOR list: LIST OF Rope.ROPE _ pins[side], list.rest WHILE list#NIL DO WHILE list.rest#NIL AND list.first.Equal[list.rest.first] DO list.rest _ list.rest.rest; ENDLOOP; ENDLOOP; ENDLOOP}; CreateIconCommand: PROC [comm: CDSequencer.Command] ~ { <> schName, shortName, iconName: Rope.ROPE; icon: CD.Object; cell: Core.CellType; shell: IconConstruction.Shell; minSize: CD.Position; nms: ARRAY CoreGeometry.Side OF LIST OF Rope.ROPE; selected: CD.Instance = CDOps.TheInstance[comm.design, "CreateIcon\n"]; <> IF selected=NIL THEN RETURN; -- nothing to do schName _ CDDirectory.Name[selected.ob, comm.design]; IF schName.IsEmpty[] THEN {TerminalIO.PutF["*** Selected schematic has no name.\n"]; RETURN}; IF NOT Rope.Match["*.sch", schName] THEN TerminalIO.PutF["*** Convention for schematics is to suffix them with '.sch'.\n"]; shortName _ TerminalIO.RequestRope["Type icon short name: "]; IF shortName.IsEmpty[] THEN shortName _ IF Rope.Match["*.sch", schName] THEN schName.Substr[0, schName.Length[]-4] ELSE schName; iconName _ shortName.Concat[".icon"]; IF CDDirectory.Fetch[comm.design, iconName]#NIL THEN { -- maybe offer replace ? TerminalIO.PutF["*** The icon %g already exists!\n", IO.rope[iconName]]; RETURN}; <> cell _ ExtractOps.ExtractCDInstanceCellTypeAndReport[selected, comm.design, Sisyph.mode]; IF cell=NIL THEN RETURN; -- message already printed, service of ExtractOps <> nms _ SELECT comm.key FROM $CreateIconAlpha => CellTypePinsAlpha[cell], $CreateIconSrcPos => CellTypePinsSrcPos[cell], $CreateIconLayout => CellTypePinsLayout[cell], ENDCASE => ERROR; <> SELECT comm.key FROM $CreateIconLayout => { scale: INT _ RequestIntDefault["Type icon scale factor (default 100): ", 100]; minSize _ CD.InterestSize[PWCore.Layout[cell]]; minSize.x _ minSize.x/scale; minSize.y _ minSize.y/scale}; ENDCASE => minSize _ [0,0]; <> shell _ NewShell[shortName, nms, comm.design]; shell.grid _ CDOps.GetGrid[comm.design, comm]; -- default might not be perfect... icon _ ShellObjectInner[shell, minSize]; CDProperties.PutObjectProp[icon, Sisyph.mode.extractProcProp, $SisyphExtractCellIcon]; CDProperties.PutObjectProp[icon, $IconFor, schName]; CDCells.SetSimplificationTreshhold[cell: icon, val: 30, inPixels: TRUE]; IF NOT CDDirectory.Include[comm.design, icon, iconName] THEN ERROR; -- Abnormal [] _ CDOps.IncludeObjectI[comm.design, icon, comm.pos]; TerminalIO.PutF["%g generated\n", IO.rope[iconName]]}; RequestIntDefault: PROC[prompt: IO.ROPE, default: INT] RETURNS[i: INT] = { DO ok: BOOL _ TRUE; r: Rope.ROPE _ TerminalIO.RequestRope[prompt, 0]; IF r.Length[]=0 THEN RETURN[default]; i _ Convert.IntFromRope[r ! RuntimeError.UNCAUGHT => {ok_FALSE; CONTINUE}]; IF ok THEN RETURN; TerminalIO.PutRope[" ?? (integer), please repeat: \n"]; ENDLOOP}; <> IsWire: PROC [inst: CD.Instance] RETURNS [BOOL] ~ { <> RETURN [inst.ob.layer=CD.commentLayer AND CDRects.IsBareRect[inst.ob]]}; <<>> 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]}; SwitchWireSizeCommand: PROC [comm: CDSequencer.Command] ~ { <> shrunk: INT _ 0; enlarged: INT _ 0; smallW: CD.Number = CDLayers.LayerWidth[comm.design, CD.commentLayer]; largeW: CD.Number = 3*smallW; TerminalIO.PutRope["switch size of selected wires\n"]; FOR list: CD.InstanceList _ CDOps.InstList[comm.design], list.rest WHILE list#NIL DO inst: CD.Instance _ list.first; -- instance may be modified ! IF inst.selected AND IsWire[inst] THEN { size: CD.Position = CD.InterestSize[inst.ob]; delta: CD.Number; SELECT MIN [size.x, size.y] FROM smallW => delta _ smallW; largeW => delta _ -smallW; ENDCASE => delta _ 0; IF delta#0 THEN { -- modify the instance goal: CD.Rect = CDBasics.Extend[CDInstances.InstRectI[inst], delta]; oldRect: CD.Rect = CDInstances.InstRectO[inst]; inst.ob _ CDRects.CreateRect[CDBasics.AddPoints[size, [2*delta, 2*delta]], inst.ob.layer]; inst.trans.off _ CDBasics.SubPoints[inst.trans.off, [delta, delta]]; FiddlePos[inst, goal]; -- move instance to fit goal: transf are the plague of CD CDOps.Redraw[comm.design, CDBasics.Surround[oldRect, CDInstances.InstRectO[inst]]]; IF delta>0 THEN enlarged _ enlarged + 1 ELSE shrunk _ shrunk + 1 } }; ENDLOOP; TerminalIO.PutF ["Enlarged %g wire(s), shrunk %g wire(s)\n", IO.int[enlarged], IO.int[shrunk]]}; <> CDSequencer.ImplementCommand[$CreateIconAlpha, CreateIconCommand]; CDSequencer.ImplementCommand[$CreateIconSrcPos, CreateIconCommand]; CDSequencer.ImplementCommand[$CreateIconLayout, CreateIconCommand]; CDSequencer.ImplementCommand[$SwitchWireSize, SwitchWireSizeCommand]; END.