-- 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"];
};
}.