Output.mesa
Last edited: November 1, 1982 7:21 PM
Modified by TRS for compat with new Parser/Interp December 9, 1980 2:47 PM
Modified by AWP on May 14, 1982 for CMOS.
WARNING: LAYERS MAPPED INTO '100' GO BYE-BYE!!!
Edited by AWP, Nov 1, 82 to use latest MAGIC interpreter
(parent vis parameter added at Visible:)
Modified by PJM on 14-Nov-83 11:15:42
Fixed Polygon compensation to be Total not by edge
Modified by PJM on April 6, 1984 fixed NSIL3 ability
Last Edited by: McCreight, June 13, 1985 4:15:59 pm PDT
.. to scale stretches returned by the ID routine to CIF units instead of microns
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
OPEN OutputDefs;
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;
END; -- of ID
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.