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] ~ {
Build a shell structure based on parameters and defaults extracted from the design.
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]]};
ShellObjectInner:
PROC [shell: IconConstruction.Shell, minSize:
CD.Position ← [0,0]]
RETURNS [obj: CD.Object] ~ {
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 ← 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)];
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]};
CellTypePinsSrcPos:
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
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] ~ {
Returns publics on each side sorted in alphabetic order.
A name may appear on multiple sides depending on the source.
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};
CreateIconCommand:
PROC [comm: CDSequencer.Command] ~ {
User interface to create icon from schematic
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"];
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 ←
SELECT comm.key
FROM
$CreateIconAlpha => CellTypePinsAlpha[cell],
$CreateIconSrcPos => CellTypePinsSrcPos[cell],
$CreateIconLayout => CellTypePinsLayout[cell],
ENDCASE => ERROR;
Get minSize
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];
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 ← 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]]};