-- File CIFOutput.mesa -- Device independent Output module for CIF 2.0 Parser -- Written by Martin Newell, March 1980. -- Last updated: June 16, 1981 10:43 AM DIRECTORY AuxIntDefs: FROM "AuxIntDefs" USING [IStop], CIFDevicesDefs: FROM "CIFDevicesDefs" USING [LookupLayer, SetLayer, OutputText, LayerVisible], CIFOutputDefs: FROM "CIFOutputDefs" USING[SortType], CIFUtilitiesDefs: FROM "CIFUtilitiesDefs" USING[DrawRectangleOutline, GetDisplayContext, GetBaseContext, EnableClipping, DisableClipping], Graphics: FROM "Graphics" USING [DrawRectangle, DisplayContext, PushContext, PopContext, StartAreaPath, EnterPoint, Map, DrawArea, Translate], IODefs: FROM "IODefs" USING [SP, TAB, NUL, WriteLine, WriteString], ParserErrorDefs: FROM "ParserErrorDefs" USING [ErrorType, Report], OutputDefs: FROM "OutputDefs" USING [RelationType, VisibleType], IntDefs: FROM "IntDefs" USING [IUserObject, IScaleLongInt], IntStorageDefs: FROM "IntStorageDefs" USING [ObjectType], IntTransDefs: FROM "IntTransDefs" USING[Push, Pop, Rotate, Translate, RTransformPoint, RTransformVector], JaMFnsDefs: FROM "JaMFnsDefs" USING [GetJaMBreak], ParserTypeDefs: FROM "ParserTypeDefs" USING [Point, Path, LinkedPoint], Real: FROM "Real" USING [Fix, Float], RealFns: FROM "RealFns" USING [SqRt], StringDefs: FROM "StringDefs" USING [AppendChar, StringToLongNumber, InvalidNumber], SystemDefs: FROM "SystemDefs" USING [AllocateHeapNode, FreeHeapNode], Vector USING [Vec]; CIFOutput: PROGRAM IMPORTS AuxIntDefs, CIFDevicesDefs, CIFUtilitiesDefs, RealFns, Graphics, IODefs, IntDefs, IntTransDefs, JaMFnsDefs, ParserErrorDefs, Real, StringDefs, SystemDefs EXPORTS OutputDefs, CIFOutputDefs = BEGIN OPEN AuxIntDefs, CIFDevicesDefs, CIFOutputDefs, CIFUtilitiesDefs, RealFns, Graphics, IODefs, IntDefs, IntStorageDefs, IntTransDefs, OutputDefs, ParserErrorDefs, ParserTypeDefs, JaMFnsDefs, Real, StringDefs, SystemDefs, Vector; RPoint: TYPE = RECORD[x,y: REAL]; UserObjectType: TYPE = {User9, User94, other}; UserObject: TYPE = POINTER TO UserObjectRecord; UserObjectRecord: TYPE = RECORD[ SELECT type: UserObjectType FROM User9 => [ --Symbol name name: PACKED ARRAY [0..0) OF CHARACTER], User94 => [ --Named point x: REAL, y: REAL, name: PACKED ARRAY [0..0) OF CHARACTER], ENDCASE]; InitOutput: PUBLIC PROCEDURE RETURNS [BOOLEAN] = BEGIN RETURN [TRUE]; END; FinishOutput: PUBLIC PROCEDURE RETURNS [BOOLEAN] = BEGIN EnableClipping[]; RETURN[TRUE]; END; SendMessage: PUBLIC PROCEDURE [type: ParserErrorDefs.ErrorType, message: STRING, wantCR: BOOLEAN _ TRUE] = BEGIN IF wantCR THEN IODefs.WriteLine[message] ELSE IODefs.WriteString[message]; END; MessageFile: PUBLIC PROCEDURE [fileName: STRING] RETURNS [BOOLEAN] = BEGIN RETURN[FALSE]; END; OutputToFile: PUBLIC PROCEDURE [fileName: STRING] RETURNS [BOOLEAN] = BEGIN RETURN[FALSE]; END; Map: PUBLIC PROCEDURE [layerName: PACKED ARRAY [0..4) OF CHARACTER] RETURNS [CARDINAL] = BEGIN RETURN[LookupLayer[layerName]]; END; OutputWire: PUBLIC PROCEDURE [visible: VisibleType, layerName: CARDINAL, width: LONG CARDINAL, a: ParserTypeDefs.Path] = BEGIN length,halfw,offset: REAL; ap,bp,cp: POINTER TO LinkedPoint; ab,bc: Point; lengthab,lengthbc: REAL; aExt,bExt: REAL; inline: REAL; Stepbc: PROCEDURE = 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 bp _ cp; UNTIL cp=a.last DO cp _ cp.next; bc _ Point[cp.value.x-bp.value.x, cp.value.y-bp.value.y]; lengthbc _ SqRt[Dot[bc,bc]]; IF lengthbc=0 THEN bp _ cp ELSE EXIT; ENDLOOP; END; --body starts here IF GetJaMBreak[] THEN { IStop[]; WriteLine["***Aborted***"]; RETURN; }; IF a.length=0 OR ~LayerVisible[layerName] THEN RETURN; IF visible=yes THEN DisableClipping[] ELSE EnableClipping[]; SetLayer[layerName]; halfw _ width/2; cp _ a.first; Stepbc[]; IF bp=a.last THEN BEGIN ROutputFlash[width, bp.value]; RETURN; END; bExt _ halfw; UNTIL bp=a.last DO ap _ bp; aExt _ bExt; ab _ bc; lengthab _ lengthbc; Stepbc[]; IF bp=a.last THEN bExt _ halfw ELSE BEGIN IF (inline _ Dot[ab,bc])<0 THEN BEGIN bExt _ 0; ROutputFlash[width, bp.value]; 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[length, width, [IF ab.x>=0 THEN ap.value.x+offset ELSE ap.value.x-offset, ap.value.y]]; ab.x=0 => ROutputBox[width, length, [ap.value.x, IF ab.y>=0 THEN ap.value.y+offset ELSE ap.value.y-offset]]; ENDCASE => BEGIN Push[]; IntTransDefs.Translate[Round[offset],0]; --'a' to origin Rotate[ab.x,ab.y]; IntTransDefs.Translate[ap.value.x,ap.value.y]; --'a' to correct place ROutputBox[length, width, [0,0]]; Pop[]; END; ENDLOOP; END; Dot: PROCEDURE [p,q: Point] RETURNS[dotProd: REAL] = BEGIN RETURN[ Float[p.x]*Float[q.x] + Float[p.y]*Float[q.y]]; END; twoMR2: REAL = 2 - SqRt[2]; R2M1: REAL = SqRt[2] - 1; OutputFlash: PUBLIC PROCEDURE [visible: VisibleType, layerName: CARDINAL, diameter: LONG CARDINAL, center: ParserTypeDefs.Point] = --Approximate circle with an octagon BEGIN IF GetJaMBreak[] THEN { IStop[]; WriteLine["***Aborted***"]; RETURN; }; IF ~LayerVisible[layerName] THEN RETURN; IF visible=yes THEN DisableClipping[] ELSE EnableClipping[]; SetLayer[layerName]; ROutputFlash[diameter, center]; END; ROutputFlash: PUBLIC PROCEDURE [diameter: REAL, center: ParserTypeDefs.Point] = --Internal procedure for really dealing with Flashes BEGIN h2,cX,cY,radius: REAL; dc: DisplayContext _ GetDisplayContext[]; radius _ diameter/2; h2 _ R2M1*radius; [cX,cY] _ RTransformPoint[center.x, center.y]; PushContext[dc]; Graphics.Translate[dc, [cX,cY]]; StartAreaPath[dc, FALSE]; EnterPoint[dc, [radius,h2]]; EnterPoint[dc, [h2,radius]]; EnterPoint[dc, [-h2,radius]]; EnterPoint[dc, [-radius,h2]]; EnterPoint[dc, [-radius,-h2]]; EnterPoint[dc, [-h2,-radius]]; EnterPoint[dc, [h2,-radius]]; EnterPoint[dc, [radius,-h2]]; DrawArea[dc]; PopContext[dc]; END; OutputPolygon: PUBLIC PROCEDURE [visible: VisibleType, layerName: CARDINAL, a: ParserTypeDefs.Path] = BEGIN xT,yT: REAL; p,prev: POINTER TO LinkedPoint; dc: DisplayContext _ GetDisplayContext[]; IF GetJaMBreak[] THEN { IStop[]; WriteLine["***Aborted***"]; RETURN; }; IF a.length=0 OR ~LayerVisible[layerName] THEN RETURN; IF visible=yes THEN DisableClipping[] ELSE EnableClipping[]; SetLayer[layerName]; StartAreaPath[dc, FALSE]; prev _ NIL; FOR p _ a.first, p.next UNTIL prev=a.last DO [xT,yT] _ RTransformPoint[p.value.x,p.value.y]; EnterPoint[dc, [xT, yT]]; prev _ p; ENDLOOP; DrawArea[dc]; END; OutputBox: PUBLIC PROCEDURE [visible: VisibleType, layerName: CARDINAL, length, width: LONG CARDINAL, center: ParserTypeDefs.Point, xRotation, yRotation: LONG INTEGER] = BEGIN centerX: REAL _ center.x; centerY: REAL _ center.y; IF GetJaMBreak[] THEN { IStop[]; WriteLine["***Aborted***"]; RETURN; }; IF ~LayerVisible[layerName] THEN RETURN; IF visible=yes THEN DisableClipping[] ELSE EnableClipping[]; SetLayer[layerName]; SELECT TRUE FROM yRotation=0 => ROutputBox[length, width, [centerX,centerY]]; xRotation=0 => ROutputBox[width, length, [centerX,centerY]]; ENDCASE => BEGIN --accomplish rotation AND translation via transformations Push[]; Rotate[xRotation,yRotation]; IntTransDefs.Translate[center.x,center.y]; ROutputBox[length, width, [0,0]]; Pop[]; END; END; ROutputBox: PROCEDURE [length,width: REAL, center: RPoint] = 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/2; widR: REAL _ width/2; ax,ay,bx,by,cx,cy: REAL; dc: DisplayContext _ GetDisplayContext[]; [ax,ay] _ RTransformVector[lenR,widR]; [bx,by] _ RTransformVector[lenR,-widR]; [cx,cy] _ RTransformPoint[center.x, center.y]; -- Check for "Manhattan" rectangle (common case) IF ax=bx OR ay=by THEN DrawRectangle[dc, [cx-ax,cy-ay],[cx+ax,cy+ay]] ELSE BEGIN StartAreaPath[dc, FALSE]; EnterPoint[dc, [cx+ax,cy+ay]]; EnterPoint[dc, [cx+bx,cy+by]]; EnterPoint[dc, [cx-ax,cy-ay]]; EnterPoint[dc, [cx-bx,cy-by]]; DrawArea[dc]; END; END; OutputUserCommand: PUBLIC PROCEDURE [command: [0..9], userText: STRING] = BEGIN s: STRING _ [2]; -- SendMessage[Other, "User Command: "]; -- AppendDecimal[s,command]; -- AppendChar[s,SP]; -- SendMessage[Other, s, FALSE]; -- SendMessage[Other, userText]; IF command=9 AND userText.length>1 THEN SELECT userText[0] FROM SP => --9 SymbolName { ind: CARDINAL _ 1; string: STRING _ [100]; ind _ GetName[userText,ind,string]; IF string.length=0 THEN BEGIN Report["Invalid syntax for User Command 9 - expects ;", Other]; RETURN; END; -- WriteString["Name:"]; WriteString[string]; -- WriteLine[""]; OutputSymbolName[string]; }; '4 => --94 name x y IF userText[1]=' THEN { ENABLE InvalidNumber => { Report["Invalid syntax for User Command 94 - expects ;", Other]; CONTINUE; }; ind: CARDINAL _ 2; string: STRING _ [100]; number: STRING _ [50]; x,y: LONG INTEGER; ind _ GetName[userText,ind,string]; IF string.length=0 THEN InvalidNumber; ind _ GetName[userText,ind,number]; x _ StringToLongNumber[number,10]; ind _ GetName[userText,ind,number]; y _ StringToLongNumber[number,10]; -- WriteString["Name:"]; WriteString[string]; -- WriteString[" x:"]; WriteFloat[x]; -- WriteString[" y:"]; WriteFloat[y]; -- WriteLine[""]; OutputNamedPoint[string, x,y]; }; ENDCASE; END; OutputSymbolName: PUBLIC PROCEDURE [name: STRING] = BEGIN object9: POINTER TO User9 UserObjectRecord; i,sizeobject9: CARDINAL; sizeobject9 _ SIZE[User9 UserObjectRecord]+(name.length+1)/2; object9 _ AllocateHeapNode[sizeobject9]; object9^ _ UserObjectRecord[User9[name:]]; FOR i IN [0..name.length) DO object9.name[i] _ name[i]; ENDLOOP; IF (name.length MOD 2) # 0 THEN object9.name[name.length] _ NUL; IUserObject[sizeobject9,object9]; FreeHeapNode[object9]; END; OutputNamedPoint: PUBLIC PROCEDURE [name: STRING, x,y: LONG INTEGER] = BEGIN object94: POINTER TO User94 UserObjectRecord; i,sizeobject94: CARDINAL; xx,yy: REAL; sizeobject94 _ SIZE[User94 UserObjectRecord]+(name.length+1)/2; object94 _ AllocateHeapNode[sizeobject94]; xx _ Float[IScaleLongInt[x]]; yy _ Float[IScaleLongInt[y]]; object94^ _ UserObjectRecord[User94[x: xx, y: yy, name:]]; FOR i IN [0..name.length) DO object94.name[i] _ name[i]; ENDLOOP; IF (name.length MOD 2) # 0 THEN object94.name[name.length] _ NUL; IUserObject[sizeobject94,object94]; FreeHeapNode[object94]; END; GetName: PROCEDURE[string: STRING, ind: CARDINAL, name: STRING] RETURNS[newind: CARDINAL] = BEGIN ch: CHARACTER; newind _ ind; name.length _ 0; --leading space WHILE newind NULL; User94 => { string: STRING _ [100]; i,stringlength: INTEGER; xs,ys: REAL; ps: Vec; dc: DisplayContext _ GetDisplayContext[]; bc: DisplayContext _ GetBaseContext[]; stringlength _ (size-SIZE[User94 UserObjectRecord])*2; FOR i IN [0..stringlength) DO AppendChar[string,name[i]]; ENDLOOP; IF stringlength>0 AND name[stringlength-1]=NUL THEN string.length _ string.length - 1; [xs,ys] _ RTransformPoint[x,y]; ps _ Graphics.Map[dc,bc, [xs,ys]]; OutputText[string,ps.x,ps.y]; }; ENDCASE => WriteLine["unknown user object"]; END; -- in chip coordinates, not obvious how to call this yet OutputBoundingBox: PUBLIC PROCEDURE [left,right,bottom,top: LONG INTEGER] = BEGIN DrawRectangleOutline[[left,bottom,right,top]]; END; -- begin a new page of output NewPlot: PUBLIC PROCEDURE = BEGIN END; -- finish the current page of output DonePlot: PUBLIC PROCEDURE = BEGIN EnableClipping[]; END; -- specify window limits OutWindow: PUBLIC PROCEDURE [left, right, bottom, top: LONG INTEGER] = BEGIN WindowLeft _ left; WindowRight _ right; WindowBottom _ bottom; WindowTop _ top; END; SetSorting: PUBLIC PROCEDURE [sorting: SortType] = BEGIN Sorting _ sorting; END; Sorting: SortType _ none; -- establish ordering Relation: PUBLIC PROCEDURE [left1, right1, bottom1, top1, left2, right2, bottom2, top2: LONG INTEGER] RETURNS [RelationType] = BEGIN RETURN[SELECT Sorting FROM incx => SELECT left1 FROM rel, =left2 => same, ENDCASE => norel, decx => SELECT right1 FROM >right2 => rel, =right2 => same, ENDCASE => norel, incy => SELECT bottom1 FROM rel, =bottom2 => same, ENDCASE => norel, decy => SELECT top1 FROM >top2 => rel, =top2 => same, ENDCASE => norel, ENDCASE => dontcare]; END; -- determine whether an item should be shown Visible: PUBLIC PROCEDURE [kind: IntStorageDefs.ObjectType, level: CARDINAL, parentVis: VisibleType, left,right,bottom,top: LONG INTEGER] RETURNS [VisibleType] = BEGIN RETURN[ SELECT TRUE FROM left>right OR bottom>top => no, parentVis=yes => yes, left>WindowLeft AND rightWindowBottom AND top yes, leftWindowLeft AND bottomWindowBottom => maybe, ENDCASE => no]; END; -- returns the bounding box of the primitive interpreted in the current context BBWire: PUBLIC PROCEDURE [layerName: CARDINAL, width: LONG CARDINAL, a: ParserTypeDefs.Path] RETURNS [left,right,bottom,top: LONG INTEGER] = BEGIN xT,yT: REAL; p,prev: POINTER TO LinkedPoint; IF a.length=0 THEN RETURN[0,0,0,0]; minmaxStarted _ FALSE; prev _ NIL; FOR p _ a.first, p.next UNTIL prev=a.last DO [xT,yT] _ RTransformPoint[p.value.x,p.value.y]; UpdateMinMax[xT-width, xT+width, yT-width, yT+width]; -- +-halfw not enough prev _ p; ENDLOOP; RETURN[FloorI[XMin],CeilingI[XMax],FloorI[YMin],CeilingI[YMax]]; END; BBFlash: PUBLIC PROCEDURE [layerName: CARDINAL, diameter: LONG CARDINAL, center: ParserTypeDefs.Point] RETURNS [left,right,bottom,top: LONG INTEGER] = BEGIN radius: REAL = diameter/2; xT,yT: REAL; [xT,yT] _ RTransformPoint[center.x,center.y]; RETURN[FloorI[xT-radius],CeilingI[xT+radius], FloorI[yT-radius],CeilingI[yT+radius]]; END; BBPolygon: PUBLIC PROCEDURE [layerName: CARDINAL, a: ParserTypeDefs.Path] RETURNS [left,right,bottom,top: LONG INTEGER] = BEGIN xT,yT: REAL; p,prev: POINTER TO LinkedPoint; IF a.length=0 THEN RETURN; minmaxStarted _ FALSE; prev _ NIL; FOR p _ a.first, p.next UNTIL prev=a.last DO [xT,yT] _ RTransformPoint[p.value.x,p.value.y]; UpdateMinMax[xT, xT, yT, yT]; prev _ p; ENDLOOP; RETURN[FloorI[XMin],CeilingI[XMax],FloorI[YMin],CeilingI[YMax]]; END; BBBox: PUBLIC PROCEDURE [layerName: CARDINAL, length, width: LONG CARDINAL, center: ParserTypeDefs.Point, xRotation, yRotation: LONG INTEGER] RETURNS [left,right,bottom,top: LONG INTEGER] = 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 Push[]; Rotate[xRotation,yRotation]; Translate[center.x,center.y]; [left,right,bottom,top] _ RBBBox[length, width, [0,0]]; Pop[]; END; END; RBBBox: PROCEDURE [length,width: REAL, center: RPoint] RETURNS [left,right,bottom,top: LONG INTEGER] = 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/2; --depends on length and width being positive widR: REAL _ width/2; cx,cy: REAL; lenT,widT: RPoint; --Transformed half length and Width [cx,cy] _ RTransformPoint[center.x,center.y]; [widT.x,widT.y] _ 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] _ 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; BBUserObject: PUBLIC PROCEDURE [layerName: CARDINAL, size: CARDINAL, data: POINTER TO UNSPECIFIED] RETURNS [left,right,bottom,top: LONG INTEGER] = BEGIN userObject: UserObject _ data; WITH userObject SELECT FROM User9 => --Meaningless BEGIN RETURN[1,0,1,0]; --no bounding box END; User94 => BEGIN xi: LONG INTEGER _ Fix[x]; yi: LONG INTEGER _ Fix[y]; RETURN[xi,xi,yi,yi]; END; ENDCASE => BEGIN WriteLine["Unknown user object in BBUserObject"]; RETURN[0,0,0,0]; END; END; WindowLeft, WindowRight, WindowBottom, WindowTop: LONG INTEGER; --the following procedure is exported to CIFDevicesDefs for use in CIFDevices --should be done some other way in new CGraphics --GetLastBB: PUBLIC PROCEDURE RETURNS [left,right,bottom,top: LONG INTEGER] = -- BEGIN -- RETURN[MAX[ObjectLeft,WindowLeft], MIN[ObjectRight,WindowRight], -- MAX[ObjectBottom,WindowBottom], MIN[ObjectTop,WindowTop]]; -- END; --Private procedures minmaxStarted: BOOLEAN; XMin,XMax,YMin,YMax: REAL; UpdateMinMax: PROCEDURE [xmin,xmax,ymin,ymax: REAL] = INLINE BEGIN IF minmaxStarted THEN BEGIN IF xminXMax THEN XMax _ xmax; IF yminYMax THEN YMax _ ymax; END ELSE BEGIN XMin _ xmin; XMax _ xmax; YMin _ ymin; YMax _ ymax; minmaxStarted _ TRUE; END; END; Round: PROCEDURE [r: REAL] RETURNS[i: LONG INTEGER] = INLINE BEGIN RETURN[Fix[IF r<0 THEN r-0.5 ELSE r+0.5]]; END; FloorI: PROCEDURE [r: REAL] RETURNS[i: LONG INTEGER] = INLINE BEGIN i _ Fix[r]; IF r<0 THEN i _ Fix[r-i+1] + i-1; END; CeilingI: PROCEDURE [r: REAL] RETURNS[i: LONG INTEGER] = INLINE BEGIN i _ Fix[r]; IF r>0 THEN i _ Fix[r-i-1] + i+1; END; END. (635)\161b9B1282b9B443b6B28b14B33b10B38b16B231b10B69b12B87b11B191b11B88b12B88b3B133b10B1953b3B176b11B409b12B664b13B614b9B812b10B804b17B188i5I28i5I1148b16B464b16B587b7B418b16B832b17B151b7B72b8B79b9B160b10B124b8B617b7B537b6B535b7B337b9B458b5B635b6B1202b12B704b11B292b12B345b5B114b6B118b8B