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: SchCDUtils.Shell] ~ {
Build a shell structure based on parameters and defaults extracted from the design.
pinWidth: CD.Number = CDLayers.LayerWidth[design, pinLayer];
shell ←
NEW [SchCDUtils.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: SchCDUtils.Shell]
RETURNS [obj:
CD.Object] ~ {
Create a ChipNDale object for this shell. This function is extremely tricky due to the geometry manipulation (for which CD does not really help..). If you want to modify it, get brand new pad, pencil and eraser for a few drawings...
The following basic invariants should help the intrepid:
- 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
GridUp:
PROC [x:
CD.Number]
RETURNS [v:
CD.Number] ~
Round a number up to the grid
{v ← IF x>=0 THEN ((x+grid-1)/grid)*grid ELSE (x/grid)*grid};
GridDown:
PROC [x:
CD.Number]
RETURNS [v:
CD.Number] ~
Round a number down to the grid
{v ← IF x>=0 THEN (x/grid)*grid ELSE ((x-grid+1)/grid)*grid};
GridRectUp:
PROC [r:
CD.Rect]
RETURNS [v:
CD.Rect] ~
Return a gridded rectangle surrounding the argument
{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] ~ {
Create the instance for the title centered around the origin and return an inflated size
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]~ {
Create geometry (pins and names) along an edge of the icon. The size is extended to the grid size.
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] ~ {
Move instances and insert them into the cell
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] ~ {
Center a block between two others taking the height of texts... (grumble)
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;
Create the object into which instances will be later added
obj ← CDCells.CreateEmptyCell[];
Compute the instances for the 4 sides and the title (not well placed yet)
[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];
Compute a reasonable size for the nice box and prepare reposition.
width ← sLeft.x+sRight.x+MAX[sBottom.x, sTop.x, titleSize.x];
height ← MAX[sLeft.y+margin, sRight.y+margin, sBottom.y+sTop.y+titleSize.y];
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)];
Move the sides at their final locations, add box and title if present
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]];
Update the object
CDCells.ToSequenceMode[obj];
[] ← CDCells.ResizeCell[design: NIL, cell: obj]};
CellTypePins:
PUBLIC
PROC
[cell: Core.CellType] RETURNS [pins: ARRAY CoreGeometry.Side OF LIST OF Rope.ROPE] ~ {
Return list of publics on each side, sorted in increasing coordinates. A name may appear on multiple sides depending on the source.
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
pins[side] ← CONS [list.first.name, pins[side]];
list ← list.rest;
ENDLOOP;
ENDLOOP;
};
OldCellTypePins:
PROC
[cell: Core.CellType] RETURNS [pins: ARRAY CoreGeometry.Side OF LIST OF Rope.ROPE] ~ {
Return list of publics on each side, sorted in increasing coordinates. A public having pin geometry on multiple places get allocated to a single side & position arbitrarily. Unnamed publics are blissfully ignored...
EachWire: CoreOps.EachWireProc ~ {
EachPin: CoreGeometry.EachPinProc ~ {
elemsOn[side] ← CONS [NEW[ElemRep ← [name, min]], elemsOn[side]];
quit ← TRUE}; -- enumerate only the first pin of each wire...
name: Rope.ROPE = CoreOps.GetShortWireName[wire];
IF Rope.IsEmpty[name] THEN RETURN; -- ignore unnamed wires
[] ← CoreGeometry.EnumerateSides[decoration, cell, wire, EachPin]};
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];
decoration: CoreGeometry.Decoration = Sisyph.mode.decoration;
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
pins[side] ← CONS [list.first.name, pins[side]];
list ← list.rest;
ENDLOOP;
ENDLOOP};
CreateIconCommand:
PROC [comm: CDSequencer.Command] ~ {
User interface to create icon from schematic
schName, shortName, iconName: Rope.ROPE;
icon: CD.Object;
cell: Core.CellType;
shell: SchCDUtils.Shell;
nms: ARRAY CoreGeometry.Side OF LIST OF Rope.ROPE;
selected: CD.Instance = CDOps.TheInstance[comm.design, "CreateIcon\n"];
Get name of source and target
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};
Get the source cell type
cell ← ExtractOps.ExtractCDInstanceCellTypeAndReport[selected, comm.design, Sisyph.mode];
IF cell=NIL THEN RETURN; -- message already printed, service of ExtractOps
Get the names
nms ← CellTypePins[cell];
Build the object, instantiate it & return
shell ← NewShell[shortName, nms, comm.design];
shell.grid ← CDOps.GetGrid[comm.design, comm]; -- default might not be perfect...
icon ← ShellObject[shell];
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]]};