DIRECTORY
Atom, List, MPCDefs, PartitionDefs, ParserTypeDefs, NStripeDefs, PrincOps, Real, Rope, IntStorageDefs, IntTransDefs, IODefs, PolygonDefs, ParserErrorDefs, OutputDefs, OutputInternalDefs;
Output:
CEDAR
PROGRAM
IMPORTS
List, PartitionDefs, NStripeDefs, Real, IntTransDefs, IODefs,
PolygonDefs, ParserErrorDefs
EXPORTS OutputDefs, OutputInternalDefs =
BEGIN
Point: TYPE = ParserTypeDefs.Point;
PointList: TYPE = ParserTypeDefs.PointList;
Path: TYPE = ParserTypeDefs.Path;
one: REAL ← 1.0;
half: REAL ← 0.5;
small: REAL ← 0.0001; --used relative to 1 mebes unit
w: PUBLIC REF MPCDefs.WaferDescriptor;
c: PUBLIC REF MPCDefs.ChipDescriptor;
p: PUBLIC REF MPCDefs.ProjectDescriptor;
InitOutput:
PUBLIC
PROC [chip:
REF MPCDefs.ChipDescriptor]
RETURNS [
BOOL] =
BEGIN
PartitionDefs.InitPartitions["Pfile$"];
c ← chip;
w ← c.inWafer;
NStripeDefs.InitStripe[c.chipSizeX*w.outputUnitsPerMicron, c.chipSizeY*w.outputUnitsPerMicron];
Assign partition file layer indices
FOR pl: Atom.PropList ← w.layerFileList, pl.rest
WHILE pl#
NIL
DO
WITH pl.first.val
SELECT
FROM
lfi: MPCDefs.LayerFileItem =>
TRUSTED { lfi.pFileLayer ← List.Length[LOOPHOLE[pl]] };
ENDCASE => ERROR;
ENDLOOP;
RETURN[TRUE];
END;
InitProject:
PUBLIC
PROC [project:
REF MPCDefs.ProjectDescriptor]
RETURNS [
BOOL] =
BEGIN
p ← project;
IF p.inChip # c THEN ERROR;
Assign Cif layer indices and set up a map
TRUSTED { cifMap ← NEW[OutputInternalDefs.CIFMap[List.Length[LOOPHOLE[p.layerMap]]+1]] };
cifMap[0] ← NIL;
FOR pl: Atom.PropList ← p.layerMap, pl.rest
WHILE pl#
NIL
DO
WITH pl.first.val
SELECT
FROM
lg: MPCDefs.LayerGroup =>
BEGIN
TRUSTED {lg.cifIndex ← List.Length[LOOPHOLE[pl]] };
cifMap[lg.cifIndex] ← lg;
END;
ENDCASE => ERROR;
ENDLOOP;
RETURN[TRUE];
END;
RPoint: TYPE = RECORD [x, y: REAL];
cifMap: PUBLIC REF OutputInternalDefs.CIFMap ← NIL;
ID:
PROC [n:
CARDINAL, index:
INTEGER]
RETURNS [fileLayer: CARDINAL, stretch: REAL -- diameter, CIF units --, nextindex: INTEGER] =
Looks up fileLayer and stretch (in CIF units) from string layer name
Multiple entries are possible. On first entry, "index" should be set to 0.
On subsequent entries "index" should be set to the last returned
"nextindex".
When nextindex is returned negative then no layer was found and no
more entries should be made.
BEGIN
IF cifMap[n] =
NIL
THEN
{ParserErrorDefs.Report["No Such Layer", Advisory]; RETURN[0, 0, -1]}
ELSE
BEGIN
r: REF ANY;
TRUSTED { r ← List.NthTail[LOOPHOLE[cifMap[n].maskLayers], index] };
IF r = NIL THEN RETURN[0, 0, -1]
ELSE
WITH r
SELECT
FROM
pl: Atom.PropList =>
WITH pl.first.val
SELECT
FROM
la: MPCDefs.LayerAssoc =>
RETURN[
fileLayer: la.toLayer.pFileLayer,
stretch: 100*la.stretch, -- express stretch in CIF units
nextindex: index+1];
ENDCASE => ERROR;
ENDCASE => ERROR;
END;
OutputWire:
PUBLIC
PROC [
visible: VisibleType, layerName: CARDINAL, width: LONG CARDINAL,
a: Path] =
BEGIN
ENABLE
NStripeDefs.OutOfBoundsNumber =>
BEGIN ParserErrorDefs.Report["Wire coordinate out of bounds", Fatal]; CONTINUE; END;
layer: CARDINAL;
stretch, length, Width, halfw, offset: REAL;
A, b, c: PointList;
ab, bc: Point;
lengthab, lengthbc: REAL;
aExt, bExt: REAL;
inline: REAL;
index: INTEGER ← 0;
Stepbc:
PROC =
BEGIN --given old c, compute b, new c, bc, lengthbc
If no distinct new c then b will be a.Last
Filters out verticies giving rise to zero length segments
b ← c;
UNTIL c = a.last
DO
c ← c.rest;
bc ← Point[c.first.x - b.first.x, c.first.y - b.first.y];
lengthbc ← Real.SqRt[Dot[bc, bc]];
IF lengthbc = 0 THEN b ← c ELSE EXIT;
ENDLOOP;
END;
IF a.length = 0 OR layerName = 100 THEN RETURN;
DO
--once per definition of this layer
[layer, stretch, index] ← ID[layerName, index];
IF index < 0 THEN RETURN;
Width ← width + stretch;
halfw ← Width*half;
c ← a.first;
Stepbc[];
IF b = a.last
THEN
BEGIN OutputFlash[visible, layerName, width, b.first]; RETURN; END;
bExt ← halfw;
UNTIL b = a.last
DO
A ← b;
aExt ← bExt;
ab ← bc;
lengthab ← lengthbc;
Stepbc[];
IF b = a.last THEN bExt ← halfw
ELSE
BEGIN
IF (inline ← Dot[ab, bc]) < 0
THEN
BEGIN bExt ← 0; OutputFlash[visible, layerName, width, b.first]; END
ELSE
bExt ← halfw*ABS[Dot[[ab.y, -ab.x], bc]]/(inline + lengthab*lengthbc);
END;
length ← lengthab + aExt + bExt;
offset ← (lengthab - aExt + bExt)/2;
SELECT
TRUE
FROM
ab.y = 0 =>
ROutputBox[
layer, length, Width, [
IF ab.x >= 0 THEN A.first.x + offset ELSE A.first.x - offset,
A.first.y]];
ab.x = 0 =>
ROutputBox[
layer, width, length, [
A.first.x,
IF ab.y >= 0 THEN A.first.y + offset ELSE A.first.y - offset]];
ENDCASE =>
BEGIN
IntTransDefs.Push[];
IntTransDefs.Translate[Real.Round[offset], 0]; --'A' to origin
IntTransDefs.Rotate[ab.x, ab.y];
IntTransDefs.Translate[A.first.x, A.first.y]; --'A' to correct place
ROutputBox[layer, length, Width, [0, 0]];
IntTransDefs.Pop[];
END;
ENDLOOP;
ENDLOOP; --multi-layers loop
END;
Dot:
PROC [p, q: Point]
RETURNS [dotProd:
REAL] =
BEGIN RETURN[Real.Float[p.x]*Real.Float[q.x] + Real.Float[p.y]*Real.Float[q.y]]; END;
twoMR2: REAL = 2.0 - Real.SqRt[2];
R2M1: REAL = Real.SqRt[2] - 1.0;
OutputFlash:
PUBLIC
PROC [
visible: VisibleType, layerName: CARDINAL, diameter: LONG CARDINAL,
center: Point] =
Approximate circle with an octagon aligned with axes
BEGIN
ENABLE
NStripeDefs.OutOfBoundsNumber =>
BEGIN ParserErrorDefs.Report["Flash coordinate out of bounds", Fatal]; CONTINUE; END;
layer: CARDINAL;
stretch, h1, h2, cX, cY, Diameter, radius: REAL;
index: INTEGER ← 0;
IF layerName = 100 THEN RETURN;
DO
--once per definition of this layer
[layer, stretch, index] ← ID[layerName, index];
IF index < 0 THEN RETURN;
Diameter ← (diameter + stretch)*w.outputUnitsPerMicron/100;
radius ← Diameter/2;
h1 ← twoMR2*radius;
h2 ← R2M1*radius;
[cX, cY] ← IntTransDefs.RTransformPoint[center.x, center.y];
NStripeDefs.WriteTrapezoid[layer, Diameter, h1, cX - radius, cY + h2, 1, -1];
NStripeDefs.WriteRectangle[layer, Diameter, h2 + h2, cX - radius, cY - h2];
NStripeDefs.WriteTrapezoid[layer, h2 + h2, h1, cX - h2, cY - radius, -1, 1];
ENDLOOP;
END;
OutputPolygon:
PUBLIC
PROC [
visible: VisibleType, layerName: CARDINAL, a: Path] =
BEGIN
ENABLE
NStripeDefs.OutOfBoundsNumber =>
BEGIN ParserErrorDefs.Report["Polygon coordinate out of bounds", Fatal]; CONTINUE; END;
xT, yT, stretch, R2M1s: REAL;
p, prev: PointList;
layer: CARDINAL;
poly: PolygonDefs.PolygonDescriptor;
index: INTEGER ← 0;
Angle:
TYPE = {
lessthan45, between45and90, perp, between90and135, between135and180};
StretchedTrapezoid:
PROC [llx, lrx, ly, ulx, urx, uy:
REAL] =
BEGIN
h: REAL = uy - ly;
IF h < small THEN RETURN;
NStripeDefs.WriteTrapezoid[layer, lrx - llx, h, llx, ly, (ulx - llx)/h, (urx - lrx)/h];
END;
UnStretchedTrapezoid:
PROC [llx, lrx, ly, ulx, urx, uy:
REAL] =
BEGIN --Stretch each trapezoid and reconvert it
polygon: PolygonDefs.PolygonDescriptor ← PolygonDefs.PolyCreate[];
p, p1, p2, q, q1, q2, r, r1, r2: RPoint;
case: Angle;
Generate new corners and build new polygon
Right edge
[case, p1, p, p2, q1, q, q2, r1, r, r2] ← ExtendCorners[
[urx, uy], [lrx, ly], stretch];
IF case = lessthan45 THEN PolygonDefs.PolyVertex[polygon, urx + p.x, uy + p.y]
ELSE
BEGIN
PolygonDefs.PolyVertex[polygon, urx + p1.x, uy + p1.y];
PolygonDefs.PolyVertex[polygon, urx + p2.x, uy + p2.y]
END;
SELECT case
FROM
lessthan45 =>
BEGIN
PolygonDefs.PolyVertex[polygon, lrx + q1.x, ly + q1.y];
PolygonDefs.PolyVertex[polygon, lrx + q2.x, ly + q2.y];
END;
between45and90 => PolygonDefs.PolyVertex[polygon, lrx + q.x, ly + q.y];
between90and135 => PolygonDefs.PolyVertex[polygon, urx + q.x, uy + q.y];
between135and180 =>
BEGIN
PolygonDefs.PolyVertex[polygon, urx + q1.x, uy + q1.y];
PolygonDefs.PolyVertex[polygon, urx + q2.x, uy + q2.y];
END;
ENDCASE;
IF case = between135and180 THEN PolygonDefs.PolyVertex[polygon, lrx + r.x, ly + r.y]
ELSE
BEGIN
PolygonDefs.PolyVertex[polygon, lrx + r1.x, ly + r1.y];
PolygonDefs.PolyVertex[polygon, lrx + r2.x, ly + r2.y];
END;
Left edge - turn through 180 degrees
[case, p1, p, p2, q1, q, q2, r1, r, r2] ← ExtendCorners[
[-llx, -ly], [-ulx, -uy], stretch];
IF case = lessthan45 THEN PolygonDefs.PolyVertex[polygon, llx - p.x, ly - p.y]
ELSE
BEGIN
PolygonDefs.PolyVertex[polygon, llx - p1.x, ly - p1.y];
PolygonDefs.PolyVertex[polygon, llx - p2.x, ly - p2.y]
END;
SELECT case
FROM
lessthan45 =>
BEGIN
PolygonDefs.PolyVertex[polygon, ulx - q1.x, uy - q1.y];
PolygonDefs.PolyVertex[polygon, ulx - q2.x, uy - q2.y];
END;
between45and90 => PolygonDefs.PolyVertex[polygon, ulx - q.x, uy - q.y];
between90and135 => PolygonDefs.PolyVertex[polygon, llx - q.x, ly - q.y];
between135and180 =>
BEGIN
PolygonDefs.PolyVertex[polygon, llx - q1.x, ly - q1.y];
PolygonDefs.PolyVertex[polygon, llx - q2.x, ly - q2.y];
END;
ENDCASE;
IF case = between135and180 THEN PolygonDefs.PolyVertex[polygon, ulx - r.x, uy - r.y]
ELSE
BEGIN
PolygonDefs.PolyVertex[polygon, ulx - r1.x, uy - r1.y];
PolygonDefs.PolyVertex[polygon, ulx - r2.x, uy - r2.y];
END;
Convert it again
PolygonDefs.PolyGenerate[polygon, StretchedTrapezoid];
END;
ExtendCorners:
PUBLIC
PROC [tr, br: RPoint, s:
REAL]
RETURNS [case: Angle, P1, P, P2, Q1, Q, Q2, R1, R, R2: RPoint] =
BEGIN --return case and displacements of extended points for right edge
bc: RPoint;
upSideDown: BOOL = tr.x > br.x;
s ← s/2; -- Halve stretch so get TOTAL compensation, PJM
IF upSideDown THEN BEGIN t: REAL = tr.x; tr.x ← br.x; br.x ← t; END;
bc ← [br.x - tr.x, br.y - tr.y];
IF bc.x = 0
THEN
RETURN[
perp, [R2M1s, s], [s, s], [s, R2M1s], [s, 0], [s, 0], [s, 0], [
s, -R2M1s], [s, -s], [R2M1s, -s]]
ELSE
BEGIN
p1, p, p2, q1, q, q2, r1, r, r2: RPoint;
lengthbc: REAL = Real.SqRt[bc.x*bc.x + bc.y*bc.y];
unitbc: RPoint = [bc.x/lengthbc, bc.y/lengthbc];
bcrot: RPoint = [-unitbc.y, unitbc.x];
pdir: RPoint = [bcrot.x, bcrot.y + 1];
qdir: RPoint = [bcrot.x + 1, bcrot.y];
p ← [s*pdir.x/pdir.y, s];
p1 ← [R2M1*p.x, s];
p2 ← [s*bcrot.x - p1.x*unitbc.x, s*bcrot.y - p1.x*unitbc.y];
q ← [s, s*qdir.y/qdir.x];
q2 ← [s, R2M1*q.y];
q1 ← [s*bcrot.x + q2.y*unitbc.x, s*bcrot.y + q2.y*unitbc.y];
r ← [s, -s];
r1 ← [s, -R2M1s];
r2 ← [R2M1s, -s];
IF upSideDown
THEN
RETURN[
IF bc.x > -bc.y
THEN between135and180
ELSE between90and135, [
r2.x, -r2.y], [r.x, -r.y], [r1.x, -r1.y], [q2.x, -q2.y], [
q.x, -q.y], [q1.x, -q1.y], [p2.x, -p2.y], [p.x, -p.y], [
p1.x, -p1.y]]
ELSE
RETURN[
IF bc.x > -bc.y
THEN lessthan45
ELSE between45and90, p1, p, p2, q1, q,
q2, r1, r, r2];
END;
END;
Polygon code starts here
IF a.length = 0 OR layerName = 100 THEN RETURN;
DO
--once per definition of this layer
[layer, stretch, index] ← ID[layerName, index];
IF index < 0 THEN RETURN;
IF stretch < 0 THEN ParserErrorDefs.Report["Negative polygon stretch", Advisory];
stretch ← stretch*w.outputUnitsPerMicron/100; --stretch in CIF units
R2M1s ← R2M1*stretch;
poly ← PolygonDefs.PolyCreate[];
prev ← NIL;
FOR p ← a.first, p.rest
UNTIL prev = a.last
DO
[xT, yT] ← IntTransDefs.RTransformPoint[p.first.x, p.first.y];
PolygonDefs.PolyVertex[poly, xT, yT];
prev ← p;
ENDLOOP;
PolygonDefs.PolyGenerate[
poly, IF stretch = 0 THEN StretchedTrapezoid ELSE UnStretchedTrapezoid];
ENDLOOP;
END;
OutputBox:
PUBLIC
PROC [
visible: VisibleType, layerName: CARDINAL, length, width: LONG CARDINAL,
center: Point, xRotation, yRotation: INT] =
BEGIN
ENABLE
NStripeDefs.OutOfBoundsNumber =>
BEGIN ParserErrorDefs.Report["Box coordinate out of bounds", Fatal]; CONTINUE; END;
layer: CARDINAL;
Length, Width, stretch: REAL;
centerX: REAL ← center.x;
centerY: REAL ← center.y;
index: INTEGER ← 0;
IF layerName = 100 THEN RETURN;
DO
--once per definition of this layer
[layer, stretch, index] ← ID[layerName, index];
IF index < 0 THEN RETURN;
Length ← length + stretch;
Width ← width + stretch;
SELECT
TRUE
FROM
yRotation = 0 => ROutputBox[layer, Length, Width, [centerX, centerY]];
xRotation = 0 => ROutputBox[layer, Width, Length, [centerX, centerY]];
ENDCASE =>
BEGIN --do rotation AND translation via transformations
IntTransDefs.Push[];
IntTransDefs.Rotate[xRotation, yRotation];
IntTransDefs.Translate[center.x, center.y];
ROutputBox[layer, Length, Width, [0, 0]];
IntTransDefs.Pop[];
END;
ENDLOOP;
END;
ROutputBox:
PROC [layer:
CARDINAL, length, width:
REAL, center: RPoint] =
BEGIN
Internal procedure for really dealing with boxes
It assumes that stretches have been applied to the input data
Rotations must have been dealt with either by interchanging coords or
by setting up the appropriate transformation.
lenR: REAL ← length*half;
widR: REAL ← width*half;
lenT, widT: RPoint; --Transformed half Length and Width
[lenT.x, lenT.y] ← IntTransDefs.RTransformVector[lenR, 0];
[widT.x, widT.y] ← IntTransDefs.RTransformVector[0, widR];
Check for "Manhattan" rectangle (common case)
IF widT.x = 0
OR widT.y = 0
THEN
BEGIN
ax, ay, bx, by: REAL;
[ax, ay] ← IntTransDefs.RTransformPoint[center.x - lenR, center.y - widR];
[bx, by] ← IntTransDefs.RTransformPoint[center.x + lenR, center.y + widR];
NStripeDefs.WriteRectangle[layer, ABS[bx - ax], ABS[by - ay], MIN[ax, bx], MIN[ay, by]];
END
ELSE
BEGIN
top, nextA, nextB: RPoint;
lenSl, widSl, widTx2, widTy2, cX, cY: REAL;
widH: REAL; --horizontal Width
Arrange rectangle axes to have positive y, with L higher
IF lenT.y < 0 THEN lenT ← [-lenT.x, -lenT.y];
IF widT.y < 0 THEN widT ← [-widT.x, -widT.y];
IF lenT.y < widT.y THEN BEGIN T: RPoint ← lenT; lenT ← widT; widT ← T; END;
We now have one of two similar configurations mirrored about y axis
lenSl ← lenT.x/lenT.y;
widSl ← widT.x/widT.y;
widH ← 2*(widT.x + widT.y/widSl);
top ← [lenT.x + widT.x, lenT.y + widT.y];
nextA ← [lenT.x - widT.x, lenT.y - widT.y];
nextB ← [nextA.x + widH, nextA.y];
widTx2 ← widT.x + widT.x;
widTy2 ← widT.y + widT.y;
[cX, cY] ← IntTransDefs.RTransformPoint[center.x, center.y];
IF lenT.x > 0
THEN
BEGIN --L in first quadrant, widT.x & widH negative
NStripeDefs.WriteTrapezoid[
layer, -widH, widTy2, cX + nextB.x, cY + nextB.y, lenSl, widSl];
IF nextA.y # 0
THEN
NStripeDefs.WriteTrapezoid[
layer, -widH, nextA.y + nextA.y, cX - nextA.x, cY - nextA.y, lenSl,
lenSl];
NStripeDefs.WriteTrapezoid[layer, 0, widTy2, cX - top.x, cY - top.y, widSl, lenSl];
END
ELSE
BEGIN --L in second quadrant, widT.x & widH positive
NStripeDefs.WriteTrapezoid[
layer, widH, widTy2, cX + nextA.x, cY + nextA.y, widSl, lenSl];
IF nextA.y # 0
THEN
NStripeDefs.WriteTrapezoid[
layer, widH, nextA.y + nextA.y, cX - nextB.x, cY - nextB.y, lenSl,
lenSl];
NStripeDefs.WriteTrapezoid[layer, 0, widTy2, cX - top.x, cY - top.y, lenSl, widSl];
END;
END;
END;
OutputUserCommand:
PUBLIC
PROC [command: [0..9], userText: Rope.
ROPE] =
BEGIN
ParserErrorDefs.Report["User Extension ignored", Advisory];
END;
OutputUserObject:
PUBLIC
PROC [visible: VisibleType, layerName:
CARDINAL,
data: REF ANY] = { NULL };
in chip coordinates, not obvious how to call this yet
OutputBoundingBox:
PUBLIC
PROC [left, right, bottom, top:
INT] =
BEGIN IODefs.WriteLine["OutputBoundingBox Called"]; END;
establish ordering
Relation:
PUBLIC
PROC [
left1, right1, bottom1, top1, left2, right2, bottom2, top2: INT]
RETURNS [RelationType] =
BEGIN
this version orders left within bottom
RETURN[
dontcare
SELECT bottom1 FROM
<bottom2 => rel,
=bottom2 => same,
ENDCASE => norel
];
END;
determine whether an item should be shown
Visible:
PUBLIC
PROC [
kind: IntStorageDefs.Object, level: CARDINAL, parentVis: VisibleType,
left, right, bottom, top: INT] RETURNS [VisibleType] =
BEGIN RETURN[yes]; END;
returns the bounding box of the primitive interpreted in the current context
BBWire:
PUBLIC
PROC [
layerName: CARDINAL, width: LONG CARDINAL, a: Path]
RETURNS [left, right, bottom, top: INT] =
BEGIN
halfw, xT, yT: REAL;
p, prev: PointList;
IF a.length = 0 THEN RETURN[0, 0, 0, 0];
minmaxStarted ← FALSE;
halfw ← width/2;
prev ← NIL;
FOR p ← a.first, p.rest
UNTIL prev = a.last
DO
[xT, yT] ← IntTransDefs.RTransformPoint[p.first.x, p.first.y];
UpdateMinMax[xT - halfw, xT + halfw, yT - halfw, yT + halfw];
prev ← p;
ENDLOOP;
RETURN[FloorI[XMin], CeilingI[XMax], FloorI[YMin], CeilingI[YMax]];
END;
BBFlash:
PUBLIC
PROC [
layerName: CARDINAL, diameter: LONG CARDINAL, center: Point]
RETURNS [left, right, bottom, top: INT] =
BEGIN
radius: REAL = diameter*half;
xT, yT: REAL;
[xT, yT] ← IntTransDefs.RTransformPoint[center.x, center.y];
RETURN[
FloorI[xT - radius], CeilingI[xT + radius], FloorI[yT - radius], CeilingI[
yT + radius]];
END;
BBPolygon:
PUBLIC
PROC [layerName:
CARDINAL, a: Path]
RETURNS [left, right, bottom, top: INT] =
BEGIN
xT, yT: REAL;
p, prev: PointList;
IF a.length = 0 THEN RETURN[0, 0, 0, 0];
minmaxStarted ← FALSE;
prev ← NIL;
FOR p ← a.first, p.rest
UNTIL prev = a.last
DO
[xT, yT] ← IntTransDefs.RTransformPoint[p.first.x, p.first.y];
UpdateMinMax[xT, xT, yT, yT];
prev ← p;
ENDLOOP;
RETURN[FloorI[XMin], CeilingI[XMax], FloorI[YMin], CeilingI[YMax]];
END;
BBBox:
PUBLIC
PROC [
layerName: CARDINAL, length, width: LONG CARDINAL,
center: Point, xRotation, yRotation: INT]
RETURNS [left, right, bottom, top: INT] =
BEGIN
SELECT
TRUE
FROM
yRotation = 0 =>
[left, right, bottom, top] ← RBBBox[length, width, [center.x, center.y]];
xRotation = 0 =>
[left, right, bottom, top] ← RBBBox[width, length, [center.x, center.y]];
ENDCASE =>
BEGIN --accomplish rotation AND translation via transformations
IntTransDefs.Push[];
IntTransDefs.Rotate[xRotation, yRotation];
IntTransDefs.Translate[center.x, center.y];
[left, right, bottom, top] ← RBBBox[length, width, [0, 0]];
IntTransDefs.Pop[];
END;
END;
BBUserObject:
PUBLIC
PROC [
layerName: CARDINAL, data: REF ANY]
RETURNS [left, right, bottom, top: INT] =
BEGIN left ← right ← bottom ← top ← 0; END;
RBBBox:
PROC [length, width:
REAL, center: RPoint]
RETURNS [left, right, bottom, top: INT] =
BEGIN
Internal procedure for really dealing with boxes
Rotations must have been dealt with either by interchanging coords or
by setting up the appropriate transformation.
lenR: REAL ← length*half; --depends on length and width being positive
widR: REAL ← width*half;
cx, cy: REAL;
lenT, widT: RPoint; --Transformed half length and Width
[cx, cy] ← IntTransDefs.RTransformPoint[center.x, center.y];
[widT.x, widT.y] ← IntTransDefs.RTransformVector[0, widR];
Check for "Manhattan" rectangles (common case)
SELECT
TRUE
FROM
widT.x = 0 =>
RETURN[
FloorI[cx - lenR], CeilingI[cx + lenR], FloorI[cy - widR], CeilingI[
cy + widR]];
widT.y = 0 =>
RETURN[
FloorI[cx - widR], CeilingI[cx + widR], FloorI[cy - lenR], CeilingI[
cy + lenR]];
ENDCASE =>
BEGIN
w, h: REAL;
[lenT.x, lenT.y] ← IntTransDefs.RTransformVector[lenR, 0];
Arrange rectangle axes to have positive y
IF lenT.y < 0 THEN lenT ← [-lenT.x, -lenT.y];
IF widT.y < 0 THEN widT ← [-widT.x, -widT.y];
We now have one of two similar configurations mirrored about y axis
w ← ABS[lenT.x - widT.x];
h ← ABS[lenT.y + widT.y];
RETURN[
FloorI[cx - w], CeilingI[cx + w], FloorI[cy - h], CeilingI[cy + h]];
END;
END;
Private procedure
minmaxStarted: BOOL;
XMin, XMax, YMin, YMax: REAL;
UpdateMinMax:
PROC [xmin, xmax, ymin, ymax:
REAL] =
INLINE
BEGIN
IF minmaxStarted
THEN
BEGIN
IF xmin < XMin THEN XMin ← xmin;
IF xmax > XMax THEN XMax ← xmax;
IF ymin < YMin THEN YMin ← ymin;
IF ymax > YMax THEN YMax ← ymax;
END
ELSE
BEGIN
XMin ← xmin;
XMax ← xmax;
YMin ← ymin;
YMax ← ymax;
minmaxStarted ← TRUE;
END;
END;
FloorI:
PROC [r:
REAL]
RETURNS [i:
INT] =
INLINE
BEGIN i ← Real.Fix[r]; IF r < 0 THEN i ← Real.Fix[r - i + 1] + i - 1; END;
CeilingI:
PROC [r:
REAL]
RETURNS [i:
INT] =
INLINE
BEGIN i ← Real.Fix[r]; IF r > 0 THEN i ← Real.Fix[r - i - 1] + i + 1; END;
END.