-- GFigToJaM.mesa, Cedar version -- Last changed by M. Stone, 9-Sep-81 18:14:37 DIRECTORY PairList USING [Relation,NotFound,CreateRelation,AddPair, Right,ForAllPairs,DestroyRelation, EqualProc], Real USING [RoundI], Rope USING [Ref,Concat,Match], IOStream, Convert USING [ValueToRope], GriffinFig, Vector USING[Vec], Cubic USING[Bezier]; GFigToJaM: PROGRAM IMPORTS PairList, Rope, Convert, IOStream, Real EXPORTS GriffinFig = {OPEN GriffinFig, PairList, Vector; NoFileOpen: PUBLIC SIGNAL = CODE; FileNotClosed: PUBLIC SIGNAL[name: Rope.Ref] = CODE; NotAValidColorID: PUBLIC SIGNAL = CODE; NotAValidFontID: PUBLIC SIGNAL = CODE; NotAValidStyleID: PUBLIC SIGNAL[type: StyleType] = CODE; NotAValidObjectID: PUBLIC SIGNAL = CODE; NotAValidPathID: PUBLIC SIGNAL = CODE; NoCurrentPath: PUBLIC SIGNAL = CODE; PathAlreadyStarted: PUBLIC SIGNAL = CODE; NotAValidClusterID: PUBLIC SIGNAL = CODE; NoCurrentCluster: PUBLIC SIGNAL = CODE; ClusterAlreadyStarted: PUBLIC SIGNAL = CODE; Stream: IOStream.Handle ← NIL; FileName: Rope.Ref ← NIL; -- Conversion constants: Press -> Alto PressToAltoScaleFactor: REAL = 0.03125; --files NewFile: PUBLIC PROC[name: Rope.Ref] = { IF Stream#NIL THEN SIGNAL FileNotClosed[FileName]; InitDataStructures[]; FileName ← NIL; FileName ← Rope.Concat[FileName,name]; IF ~Rope.Match["*.jam", FileName, FALSE] THEN FileName ← Rope.Concat[FileName,".jam"]; Stream ← IOStream.CreateFileStream[FileName,overwrite]; WName[name]; WS[" /begin"]; WS["\n% The following definition includes a circumlocution for LineWidth"]; WS["\n% to always have the value 0. Remove the .pop when CGStrokeImpl"]; WS["\n% accepts non-unit line widths."]; WS["\n(linewidth) (.pop 0 (LineWidth) .exch .store).cvx .def"]; }; CloseFile: PUBLIC PROC = { wn: PROC[leftPart, rightPart: REF ANY] = {WS[NARROW[rightPart]]; WS[" "]}; IF Stream=NIL THEN SIGNAL NoFileOpen; --ForAllPairs gives inverse chronological order. We need the opposite ReverseObjects[]; WS["\n(refresh) ("]; ForAllPairs[ObjectRelation,wn]; WS[") .cvx .def\n"]; IOStream.Close[Stream,TRUE]; Stream ← NIL; }; --to call from debugger DebugCloseFile: PUBLIC PROC = { IOStream.Close[Stream,TRUE]; Stream ← NIL; }; Comment: PUBLIC PROC[string: Rope.Ref] = { WName[string]; WS[" .pop\n"]; }; --style information Color: PUBLIC PROC[h,s,b: [0..255], texture: CARDINAL] RETURNS [ColorID] = { --relation will have a pointer to this string id: CARDINAL ← TestAndAdd[color]; WN[h]; WN[s]; WN[b]; WS[" .sethsb"]; WUN[texture]; WS[" .setcolor) .cvx .def"]; RETURN[id]; }; Font: PUBLIC PROC[name: Rope.Ref, charOrientation: REAL] RETURNS [FontID] = { id: CARDINAL ← TestAndAdd[font]; WName[name]; WS[" .setfont"]; WR[charOrientation]; WS[" .charorientation) .cvx .def"]; RETURN[id]; }; LineStyle: PUBLIC PROC[colorID: ColorID, width: REAL] RETURNS [LineStyleID] = { id: CARDINAL ← TestAndAdd[linestyle]; colorName: Rope.Ref ← CheckID[colorID,color]; WS[colorName]; WR[width]; WS[" linewidth) .cvx .def"]; RETURN[id]; }; FillStyle: PUBLIC PROC[colorID: ColorID] RETURNS [FillStyleID] = { id: CARDINAL ← TestAndAdd[fillstyle]; colorName: Rope.Ref ← CheckID[colorID,color]; WS[colorName]; WS[") .cvx .def"]; RETURN[id]; }; TextStyle: PUBLIC PROC[colorID: ColorID, fontID: FontID] RETURNS [TextStyleID] = { id: CARDINAL ← TestAndAdd[textstyle]; colorName: Rope.Ref ← CheckID[colorID,color]; fontName: Rope.Ref ← CheckID[fontID,font]; WS[colorName]; WS[" "]; WS[fontName]; WS[") .cvx .def"]; RETURN[id]; }; --paths newBoundary: BOOLEAN ← FALSE; CurrentPathID: CARDINAL ←0; StartPath: PUBLIC PROC = { IF CurrentPathID#0 THEN SIGNAL PathAlreadyStarted; CurrentPathID ← TestAndAdd[path]; newBoundary ← TRUE; }; EnterPoint: PUBLIC PROC[p: Vec] = { IF Stream=NIL THEN SIGNAL NoFileOpen; IF CurrentPathID=0 THEN SIGNAL NoCurrentPath; WP[p]; }; EnterCubic: PUBLIC PROC[c: Cubic.Bezier] = { IF Stream=NIL THEN SIGNAL NoFileOpen; IF CurrentPathID=0 THEN SIGNAL NoCurrentPath; WC[c]; }; NewBoundary: PUBLIC PROC = { IF Stream=NIL THEN SIGNAL NoFileOpen; IF CurrentPathID=0 THEN SIGNAL NoCurrentPath; newBoundary ← TRUE; }; --To use the path in the file, need .linepath, .areapath or .closedlinepath --following in the stream EndPath: PUBLIC PROC RETURNS [PathID] = { return: PathID; IF Stream=NIL THEN SIGNAL NoFileOpen; IF CurrentPathID=0 THEN SIGNAL NoCurrentPath; WS[") .cvx .def"]; return ← CurrentPathID; CurrentPathID ← 0; RETURN[return]; }; --objects LineObject: PUBLIC PROC[lineStyleID: LineStyleID, pathID: PathID] RETURNS [ObjectID] = { id: CARDINAL ← TestAndAdd[object]; styleName: Rope.Ref ← CheckID[lineStyleID,linestyle]; pathName: Rope.Ref ← CheckID[pathID,path]; WS[styleName]; WS[" "]; WS[pathName]; WS[ " LineWidth .drawpath) .cvx .def"]; RETURN[id]; }; AreaObject: PUBLIC PROC[fillStyleID: FillStyleID, lineStyleID: LineStyleID, pathID: PathID] RETURNS [ObjectID] = { id: CARDINAL ← TestAndAdd[object]; pathName: Rope.Ref ← CheckID[pathID,path]; styleName: Rope.Ref; IF fillStyleID#NullStyle THEN { styleName ← CheckID[fillStyleID,fillstyle]; WS[styleName]; WS[" "]; WS[pathName]; WS[" "]; WS[".drawarea "]; }; IF lineStyleID#NullStyle THEN { styleName ← CheckID[lineStyleID,linestyle]; WS[styleName]; WS[" "]; WS[pathName]; WS[" LineWidth .drawpath"]; }; WS[ ") .cvx .def"]; RETURN[id]; }; --this is complicated because of the left, right, center anchor --on griffin text. TextObject: PUBLIC PROC[styleID: TextStyleID, text: Rope.Ref, anchorType: AnchorType, anchor: Vec, rot: REAL] RETURNS [ObjectID] = { id: CARDINAL ← TestAndAdd[object]; styleName: Rope.Ref ← CheckID[styleID,textstyle]; WS[".pushdc "]; WS[styleName]; WP[anchor]; WS[" .moveto"]; WR[rot]; WS[" .rotate "]; WName[text]; WS[" altotoobject"]; --these procedures should use the .textbox function to recompute --the origin for the .drawtext SELECT anchorType FROM left => WS[" left"]; right => WS[" right"]; center => WS[" center"]; ENDCASE => ERROR; WS[" .drawtext .popdc) .cvx .def"]; RETURN[id]; }; --clusters CurrentClusterID: CARDINAL ←0; StartCluster: PUBLIC PROC = { IF CurrentClusterID#0 THEN SIGNAL ClusterAlreadyStarted; CurrentClusterID ← TestAndAdd[cluster]; }; EnterObject: PUBLIC PROC[objectID: ObjectID] = { IF Stream=NIL THEN SIGNAL NoFileOpen; WS[CheckID[objectID, object]]; WS[" "]; }; EnterCluster: PUBLIC PROC[clusterID: ClusterID] = { IF Stream=NIL THEN SIGNAL NoFileOpen; WS[CheckID[clusterID, cluster]]; }; --finishes current cluster Cluster: PUBLIC PROC RETURNS [ClusterID] = { return: ClusterID; IF Stream=NIL THEN SIGNAL NoFileOpen; IF CurrentClusterID=0 THEN SIGNAL NoCurrentCluster; WS[") .cvx .def"]; return ← CurrentClusterID; CurrentClusterID ← 0; RETURN[return]; }; --private procedures InitDataStructures: PROC = { DestroyRelation[ColorRelation]; DestroyRelation[FontRelation]; DestroyRelation[LineStyleRelation]; DestroyRelation[FillStyleRelation]; DestroyRelation[TextStyleRelation]; DestroyRelation[PathRelation]; DestroyRelation[ObjectRelation]; DestroyRelation[ClusterRelation]; ColorRelation ← FontRelation ← LineStyleRelation ← FillStyleRelation ← TextStyleRelation ← PathRelation ← ObjectRelation ← ClusterRelation ← NIL; ColorNum ← FontNum ← LStyleNum ← FStyleNum ← TStyleNum ← PathNum ← ObjectNum ← ClusterNum ← 1; FileName ← NIL; }; --relation handling procs RelType: TYPE = {color,font,linestyle,fillstyle,textstyle,path,object,cluster}; ColorRelation: Relation ← NIL; FontRelation: Relation ← NIL; LineStyleRelation: Relation ← NIL; FillStyleRelation: Relation ← NIL; TextStyleRelation: Relation ← NIL; PathRelation: Relation ← NIL; ObjectRelation: Relation ← NIL; ClusterRelation: Relation ← NIL; --so ForAllPairs in generating refresh command gives chronological order ReverseObjects: PROC= { new: Relation ← CreateRelation[]; add: PROC [leftPart, rightPart: REF ANY] = { AddPair[new,leftPart,rightPart]}; IF ObjectRelation=NIL THEN RETURN; ForAllPairs[ObjectRelation,add]; ObjectRelation ← new; }; TestAndAdd: PROC [type: RelType] RETURNS [id: CARDINAL] = { --relation will have a pointer to this string name: Rope.Ref; refID: REF CARDINAL; relation: Relation ← NIL; IF Stream=NIL THEN SIGNAL NoFileOpen; SELECT type FROM color => relation ← ColorRelation ← EnsureNotNil[ColorRelation]; font => relation ← FontRelation ← EnsureNotNil[FontRelation]; linestyle => relation ← LineStyleRelation ← EnsureNotNil[LineStyleRelation]; fillstyle => relation ← FillStyleRelation ← EnsureNotNil[FillStyleRelation]; textstyle => relation ← TextStyleRelation ← EnsureNotNil[TextStyleRelation]; path => relation ← PathRelation ← EnsureNotNil[PathRelation]; object => relation ← ObjectRelation ← EnsureNotNil[ObjectRelation]; cluster => relation ← ClusterRelation ← EnsureNotNil[ClusterRelation]; ENDCASE => ERROR; [name,id] ← NextID[type]; refID ← NEW[CARDINAL ← id]; AddPair[relation,refID,name]; WS["\n"]; WName[name]; WS[" ("]; }; EnsureNotNil: PROC [r: Relation] RETURNS [Relation] = { RETURN [IF r = NIL THEN CreateRelation[] ELSE r]; }; CheckID: PROC [id: CARDINAL, type: RelType] RETURNS [Rope.Ref] = { name: Rope.Ref; refID: REF CARDINAL ← NEW[CARDINAL ← id]; equalCardinal: PairList.EqualProc = { IF ISTYPE[right, REF CARDINAL] THEN IF NARROW[right,REF CARDINAL]↑ = NARROW[left,REF CARDINAL]↑ THEN RETURN[TRUE] ELSE RETURN[FALSE]; }; SELECT type FROM color => {ENABLE NotFound => {SIGNAL NotAValidColorID; CONTINUE}; name ← NARROW[Right[ColorRelation,equalCardinal,refID]]}; font => {ENABLE NotFound => {SIGNAL NotAValidFontID; CONTINUE}; name ← NARROW[Right[FontRelation,equalCardinal,refID]]}; linestyle => {ENABLE NotFound => {SIGNAL NotAValidStyleID[line]; CONTINUE}; name ← NARROW[Right[LineStyleRelation,equalCardinal,refID]]}; fillstyle => {ENABLE NotFound => {SIGNAL NotAValidStyleID[fill]; CONTINUE}; name ← NARROW[Right[FillStyleRelation,equalCardinal,refID]]}; textstyle => {ENABLE NotFound => {SIGNAL NotAValidStyleID[text]; CONTINUE}; name ← NARROW[Right[TextStyleRelation,equalCardinal,refID]]}; path => {ENABLE NotFound => {SIGNAL NotAValidPathID; CONTINUE}; name ← NARROW[Right[PathRelation,equalCardinal,refID]]}; object => {ENABLE NotFound => {SIGNAL NotAValidObjectID; CONTINUE}; name ← NARROW[Right[ObjectRelation,equalCardinal,refID]]}; cluster => {ENABLE NotFound => {SIGNAL NotAValidClusterID; CONTINUE}; name ← NARROW[Right[ClusterRelation,equalCardinal,refID]]}; ENDCASE => ERROR; RETURN[name]; }; --generate id's. Keep it simple for now ColorName: Rope.Ref ← "c"; FontName: Rope.Ref ← "f"; LineStyleName: Rope.Ref ← "ls"; FillStyleName: Rope.Ref ← "fs"; TextStyleName: Rope.Ref ← "ts"; PathName: Rope.Ref ← "p"; ObjectName: Rope.Ref ← "obj"; ClusterName: Rope.Ref ← "clu"; ColorNum,FontNum,LStyleNum,FStyleNum,TStyleNum: CARDINAL ← 1; PathNum,ObjectNum,ClusterNum: CARDINAL ← 1; NextID: PROC [type: RelType] RETURNS [name: Rope.Ref, id: CARDINAL] = { typeName: Rope.Ref; SELECT type FROM color => {id ← ColorNum; ColorNum ← ColorNum+1}; font => {id ← FontNum; FontNum ← FontNum+1}; linestyle => {id ← LStyleNum; LStyleNum ← LStyleNum+1}; fillstyle => {id ← FStyleNum; FStyleNum ← FStyleNum+1}; textstyle => {id ← TStyleNum; TStyleNum ← TStyleNum+1}; path => {id ← PathNum; PathNum ← PathNum+1}; object => {id ← ObjectNum; ObjectNum ← ObjectNum+1}; cluster => {id ← ClusterNum; ClusterNum ← ClusterNum+1}; ENDCASE => ERROR; typeName ← (SELECT type FROM color => ColorName, font => FontName, linestyle => LineStyleName, fillstyle => FillStyleName, textstyle => TextStyleName, path => PathName, object => ObjectName, cluster => ClusterName, ENDCASE => ERROR); name ← typeName; name ← Rope.Concat[name,Convert.ValueToRope[[signed[id,10]]]]; }; --file writing procs WS: PROC [s: Rope.Ref] = { IOStream.PutRope[Stream,s]; }; WName: PROC [s: Rope.Ref] = {OPEN IOStream; Put[Stream,char['(], rope[s], char[')]]; }; --write it unsigned, base 8 WUN: PROC [n: CARDINAL] = {OPEN IOStream; PutF[Stream," %bB",card[n]]; }; --write it signed, base 10 WN: PROC [n: INTEGER] = {OPEN IOStream; Put[Stream,char[SP],int[n] ]; }; WR: PROC [r: REAL] = {OPEN IOStream; Put[Stream,char[SP],int[Real.RoundI[r*PressToAltoScaleFactor]] ]; }; WP: PROC [p: Vec] = { WR[p.x]; WR[p.y]; IF newBoundary THEN WS[" .moveto\n"] ELSE WS[" .lineto\n"]; newBoundary ← FALSE; }; WriteBB: PROC [ll,ur: Vec] = { WR[ll.x]; WR[ll.y]; WR[ur.x]; WR[ur.y]; WS[" bb"]; }; WC: PROC [c: Cubic.Bezier] = { IF newBoundary THEN WP[c.b0]; WR[c.b1.x]; WR[c.b1.y]; WR[c.b2.x]; WR[c.b2.y]; WR[c.b3.x]; WR[c.b3.y]; WS[ " .curveto\n"]; }; }.