-- GriffinToTAImpl.mesa
-- Read Griffin files and convert to Tioga Artwork files
-- borrowed liberally from ReadGriffinImpl.mesa and GriffinFile.mesa
-- Rick Beach, April 15, 1983 11:34 am
-- Maureen Stone May 5, 1983 4:52 pm
DIRECTORY
Cubic USING [Bezier, CoeffsToBezier, Coeffs],
Environment USING [charsPerPage, Byte],
GFileFormatDefs,
GriffinToTA,
GriffinToTANodes,
GriffinToTAPrivate,
GriffinToJaM,
Inline USING [LongCOPY, BytePair],
Graphics USING [Path, MoveTo, LineTo, CurveTo, NewPath],
CGPath USING [Empty],
IO,
FileIO,
PairList,
RealConvert USING [Mesa5ToIeee],
Rope,
SplineDefs,
StyleDefs,
Vector USING [Vec];
GriffinToTAImpl: PROGRAM
IMPORTS Cubic, GriffinToTANodes, GriffinToTAPrivate, Inline, IO, PairList, RealConvert, Rope, SplineDefs, FileIO, Graphics, CGPath, GriffinToJaM
EXPORTS GriffinToTA = {
ROPE: TYPE = Rope.ROPE;
GriffinFileError: PUBLIC SIGNAL[why: ROPE] = CODE;
ViewType: TYPE = GriffinToTA.ViewType;
FigureNumber: TYPE = CARDINAL[1 .. 100];
FontRef: TYPE = GriffinToTA.FontRef;
StyleRef: TYPE = GriffinToTA.StyleRef;
ObjectRef: TYPE = GriffinToTA.ObjectRef;
PathRef: TYPE = GriffinToTA.PathRef;
CaptionRef: TYPE = GriffinToTA.CaptionRef;
CaptionRec: TYPE = GriffinToTA.CaptionRec;
ColorRef: TYPE = GriffinToTA.ColorRef;
PathStyleRef: TYPE = GriffinToTA.PathStyleRef;
PathStyleRec: TYPE = GriffinToTA.PathStyleRec;
CaptionStyleRef: TYPE = GriffinToTA.CaptionStyleRef;
CaptionStyleRec: TYPE = GriffinToTA.CaptionStyleRec;
IntegerVec: TYPE = RECORD[
x, y: INTEGER];
-- now our own data structures
fontMap: PUBLIC PairList.Relation; -- <fontNumber, FontRef>
styleMap: PUBLIC PairList.Relation; -- <styleNumber, StyleRef>
objectMap: PUBLIC PairList.Relation; -- <objectNumber, ObjectRef>
pathMap: PUBLIC PairList.Relation; -- <objectNumber, PathRef>
captionMap: PUBLIC PairList.Relation; -- <objectNumber, CaptionRef>
clusterMap: PUBLIC PairList.Relation; -- <clusterNumber, objectNumber>
objectsReversed: BOOLEAN ← FALSE;
firstCluster, lastCluster: PUBLIC CARDINAL ← 0;
colors: PUBLIC ColorRef ← NIL;
colorNumber: PUBLIC CARDINAL ← 0;
pathStyles: PUBLIC PathStyleRef ← NIL;
pathStyleNumber: PUBLIC CARDINAL ← 0;
captionStyles: PUBLIC CaptionStyleRef ← NIL;
captionStyleNumber: PUBLIC CARDINAL ← 0;
objectNumber: CARDINAL ← 1; -- count of objects in this view
inThisView: BOOLEAN ← FALSE;
diskHandle: IO.STREAM;
maxCPairCount: CARDINAL = 2000; -- just for safety
majorVersion: Environment.Byte = 1;
minorVersion: Environment.Byte = 4;
realConvert: BOOLEAN ← FALSE; -- convert reals if older than Griffin version 1.4
ConvertFile: PUBLIC PROCEDURE[fileName: ROPE, view: ViewType] = {
IF view = both THEN view ← main;
fileName ← FixFileName[fileName, ".Griffin"];
diskHandle ← FileIO.Open[fileName ! FileIO.OpenFailed => CHECKED {GOTO BadCreate}];
InitDataStructures[];
ReadFigure[1, view];
IF ~objectsReversed THEN {
clusterMap ← ReverseRelation[clusterMap];
objectsReversed ← TRUE};
GriffinToTANodes.PutClusters[FixFileName[fileName, ".Artwork"]];
GriffinToTANodes.PutStyles[FixFileName[fileName, ".Style"]];
DestroyDataStructures[];
IO.Close[diskHandle];
EXITS BadCreate => {SIGNAL GriffinFileError[Rope.Cat["file ", fileName, " not found"]]};
};
ConvertFileToJaM: PUBLIC PROCEDURE[fileName: ROPE, view: ViewType] = {
IF view = both THEN view ← main;
fileName ← FixFileName[fileName, ".Griffin"];
diskHandle ← FileIO.Open[fileName ! FileIO.OpenFailed => CHECKED {GOTO BadCreate}];
InitDataStructures[];
ReadFigure[1, view];
IF ~objectsReversed THEN {
clusterMap ← ReverseRelation[clusterMap];
objectsReversed ← TRUE};
GriffinToJaM.PutClusters[FixFileName[fileName, ".JaM"]];
DestroyDataStructures[];
IO.Close[diskHandle];
EXITS BadCreate => {SIGNAL GriffinFileError[Rope.Cat["file ", fileName, " not found"]]};
};
InitDataStructures: PROCEDURE = {
colors ← GriffinToTAPrivate.InitColors[];
fontMap ← PairList.CreateRelation[];
styleMap ← PairList.CreateRelation[];
objectMap ← PairList.CreateRelation[];
pathMap ← PairList.CreateRelation[];
captionMap ← PairList.CreateRelation[];
clusterMap ← PairList.CreateRelation[];
objectsReversed ← FALSE;
firstCluster ← 0;
lastCluster ← 0;
pathStyles ← NIL;
pathStyleNumber ← 0;
captionStyles ← NIL;
captionStyleNumber ← 0;
};
DestroyDataStructures: PROCEDURE = {
PairList.DestroyRelation[fontMap];
PairList.DestroyRelation[styleMap];
PairList.DestroyRelation[objectMap];
PairList.DestroyRelation[pathMap];
PairList.DestroyRelation[captionMap];
PairList.DestroyRelation[clusterMap];
pathStyles ← NIL;
captionStyles ← NIL;
colors ← NIL;
};
ReadFigure: PROCEDURE[fignum: FigureNumber, view: ViewType] = {
fileheader: GFileFormatDefs.GFileHeader;
hcontrol: GFileFormatDefs.GFileHardcopyController;
dcontrol: GFileFormatDefs.GFileDisplayController;
figurename: GFileFormatDefs.GFileFigureName;
fontCount, styleCount, objectCount, f, s: CARDINAL;
ReadHeader[@fileheader];
MoveToSector[fileheader.figure[fignum]]; -- position to the right sector
ReadStructure[@figurename, GFileFormatDefs.lGFileFigureName];
ReadStructure[@hcontrol, GFileFormatDefs.lGFileHardcopyController];
ReadStructure[@dcontrol, GFileFormatDefs.lGFileDisplayController];
--control pairs not implemented
IF dcontrol.numcontrolpairs > maxCPairCount THEN SIGNAL GriffinFileError["too many control pairs"];
fontCount ← ReadWord[];
FOR f IN [1 .. fontCount] DO
ReadFont[f, fontMap];
ENDLOOP;
styleCount ← ReadWord[];
FOR s IN [1 .. styleCount] DO
ReadStyle[s, styleMap];
ENDLOOP;
objectCount ← ReadWord[];
THROUGH [1 .. objectCount] DO
ReadObject[objectMap, view];
ENDLOOP;
};
ReadHeader: PROCEDURE[h: POINTER TO GFileFormatDefs.GFileHeader] = {
IO.SetIndex[diskHandle,0];
ReadStructure[h, GFileFormatDefs.lGFileHeader];
realConvert ← FALSE;
IF h.majversion#majorVersion OR h.minversion#minorVersion THEN {
--version 1.4 is the first with the Ieee floating point format.
--we need to set up to convert the reals for older versions
IF h.majversion=1 AND h.minversion IN [0..3]
THEN realConvert ← TRUE;
};
};
ReadFont: PROCEDURE[fontNumber: CARDINAL, fontMap: PairList.Relation] = {
font: FontRef ← NEW[GFileFormatDefs.GFileFont];
refFontId: REF CARDINAL ← NEW[CARDINAL ← fontNumber];
ReadStructure[LOOPHOLE[font], GFileFormatDefs.lGFileFont];
PairList.AddPair[fontMap, refFontId, font];
};
ReadStyle: PROCEDURE[styleNumber: CARDINAL, styleMap: PairList.Relation] = {
style: StyleRef ← NEW[GFileFormatDefs.GFileStyle];
refStyleId: REF CARDINAL ← NEW[CARDINAL ← styleNumber];
ReadStructure[LOOPHOLE[style], GFileFormatDefs.lGFileStyle];
style.thickness ← GetReal[style.thickness];
PairList.AddPair[styleMap, refStyleId, style];
};
ReadObject: PROCEDURE[objectMap: PairList.Relation, view: ViewType] = {
fileObjectRef: ObjectRef ← NEW[GFileFormatDefs.GFileObject];
topScreenCoord: INTEGER = 808;
origin: IntegerVec;
path: PathRef ← Graphics.NewPath[];
refCaption: CaptionRef ← NIL;
ReadStructure[LOOPHOLE[fileObjectRef], GFileFormatDefs.lGFileObject];
inThisView ← ((view=main) AND (~fileObjectRef^.hidewindow)) OR ((view=alternate) AND (fileObjectRef^.hidewindow));
origin.x ← fileObjectRef^.bleft;
origin.y ← topScreenCoord - fileObjectRef^.bbottom;
SELECT fileObjectRef^.objtype FROM
GFileFormatDefs.typeCurveObject,
GFileFormatDefs.typeAreaObject => {
nlinks: CARDINAL ← ReadWord[];
splinetype: SplineDefs.SplineType ← SELECT fileObjectRef^.splinetype FROM
GFileFormatDefs.typeNUMSpline => naturalUM,
GFileFormatDefs.typeCUMSpline => cyclicUM,
GFileFormatDefs.typeNALSpline => naturalAL,
GFileFormatDefs.typeCALSpline => cyclicAL,
GFileFormatDefs.typeBEZSpline => bezier,
GFileFormatDefs.typeBSISpline => bsplineInterp,
GFileFormatDefs.typeBSpline => bspline,
GFileFormatDefs.typeCRSpline => crspline,
ENDCASE => bspline;
SELECT fileObjectRef^.trajtype FROM
GFileFormatDefs.typeLinkedTraj =>
AppendPathLinks[nlinks, splinetype, FALSE, origin, path];
GFileFormatDefs.typeCSTraj => {
IF nlinks#1 THEN SIGNAL GriffinFileError["imposible cyclic spline"];
splinetype ← (SELECT splinetype FROM
naturalUM => cyclicUM,
naturalAL => cyclicAL,
ENDCASE => splinetype);
AppendPathLinks[1, splinetype, TRUE, origin, path];
};
ENDCASE => SIGNAL GriffinFileError["invalid trajectory"];
};
GFileFormatDefs.typeCaptionObject => {
fcaptiontrailer: GFileFormatDefs.GFileCaptionTrailer;
captionText: ROPE;
pt: Vector.Vec;
ReadStructure[@fcaptiontrailer, GFileFormatDefs.lGFileCaptionTrailer];
pt.x ← GetReal[fcaptiontrailer.xanchor];
pt.y ← GetReal[fcaptiontrailer.yanchor];
pt.x ← GriffinToTAPrivate.ScalePressToScreenCoord[pt.x];
pt.y ← GriffinToTAPrivate.ScalePressToScreenCoord[pt.y];
captionText ← ReadString[];
refCaption ← NEW[CaptionRec ← [pt, captionText]];
};
ENDCASE => SIGNAL GriffinFileError["bad object"];
IF inThisView THEN {
refObjectId: REF CARDINAL ← NEW[CARDINAL ← objectNumber];
refClusterId: REF CARDINAL ← NEW[CARDINAL ← fileObjectRef^.cluster];
PairList.AddPair[objectMap, refObjectId, fileObjectRef];
objectNumber ← objectNumber+1;
SELECT fileObjectRef^.objtype FROM
GFileFormatDefs.typeCurveObject,
GFileFormatDefs.typeAreaObject =>
PairList.AddPair[pathMap, refObjectId, path];
GFileFormatDefs.typeCaptionObject =>
PairList.AddPair[captionMap, refObjectId, refCaption];
ENDCASE;
firstCluster ← MIN[firstCluster, fileObjectRef^.cluster];
lastCluster ← MAX[lastCluster, fileObjectRef^.cluster];
PairList.AddPair[clusterMap, refClusterId, refObjectId];
};
};
AppendPathLinks: PROCEDURE[nlinks: CARDINAL, splinetype: SplineDefs.SplineType, cyclic: BOOLEAN, origin: IntegerVec, path: PathRef] = {
THROUGH [1 .. nlinks] DO
AddLink[splinetype, cyclic, origin, path];
ENDLOOP;
};
AddLink: PROCEDURE[splinetype: SplineDefs.SplineType, cyclic: BOOLEAN, origin: IntegerVec, path: PathRef] = {
knotword: GFileFormatDefs.GFileKnotWord;
knots: SplineDefs.KnotSequence;
coeffs: SplineDefs.CoeffsSequence;
CoeffsLength: PROCEDURE [coeffs: SplineDefs.CoeffsSequence] RETURNS [CARDINAL] = {
RETURN[IF coeffs=NIL THEN 0 ELSE coeffs.length]
};
num, j: CARDINAL;
fpoint: GFileFormatDefs.GFilePoint;
ReadStructure[@knotword, GFileFormatDefs.lGFileKnotWord];
num ← knotword.knotcount;
IF num=0 THEN SIGNAL GriffinFileError["bad link"];
SELECT knotword.knottype FROM
GFileFormatDefs.typeFD3Knot => {
knots ← NEW[SplineDefs.KnotSequenceRec[num]];
FOR j IN [0 .. num) DO
ReadStructure[@fpoint, GFileFormatDefs.lGFilePoint];
knots[j] ← SplineDefs.FPCoords[GetReal[fpoint.x], GetReal[fpoint.y]];
ENDLOOP;
IF cyclic THEN knots ← MakeCyclicKnots[knots,splinetype];
coeffs ← SplineDefs.MakeSpline[knots,splinetype];
FOR j IN [0..CoeffsLength[coeffs]) DO
c: Cubic.Coeffs;
c.c0 ← [coeffs[j].t0[SplineDefs.X],coeffs[j].t0[SplineDefs.Y]];
c.c1 ← [coeffs[j].t1[SplineDefs.X],coeffs[j].t1[SplineDefs.Y]];
c.c2 ← [coeffs[j].t2[SplineDefs.X],coeffs[j].t2[SplineDefs.Y]];
c.c3 ← [coeffs[j].t3[SplineDefs.X],coeffs[j].t3[SplineDefs.Y]];
IF inThisView THEN
AppendCubic[Cubic.CoeffsToBezier[c], origin, path];
ENDLOOP;
};
GFileFormatDefs.typeFD0Knot,
GFileFormatDefs.typeFD1Knot,
GFileFormatDefs.typeFD2Knot =>
FOR j IN [0 .. num) DO
ReadStructure[@fpoint, GFileFormatDefs.lGFilePoint];
IF inThisView THEN
AppendPoint[[GetReal[fpoint.x], GetReal[fpoint.y]], origin, path];
ENDLOOP;
ENDCASE => SIGNAL GriffinFileError["bad link"];
};
MakeCyclicKnots: PROCEDURE[knots: SplineDefs.KnotSequence, type: SplineDefs.SplineType] RETURNS[SplineDefs.KnotSequence] = {
cycknots: SplineDefs.KnotSequence;
numknots: INTEGER ← IF knots=NIL THEN 0 ELSE knots.length;
newLength,i: INTEGER;
IF numknots <= 0 THEN RETURN[NIL];
SELECT type FROM
cyclicUM,
cyclicAL,
bezier=> newLength ← numknots+1;
bspline,
crspline=> newLength ← numknots+3;
ENDCASE;
cycknots ← NEW[SplineDefs.KnotSequenceRec[newLength]];
FOR i IN [0..numknots) DO
cycknots[i] ← knots[i];
ENDLOOP;
cycknots[numknots] ← cycknots[0];
SELECT type FROM
naturalUM => type ← cyclicUM;
naturalAL => type ← cyclicAL;
bspline => {
cycknots[numknots+1] ← cycknots[1];
cycknots[numknots+2] ← cycknots[2];
};
crspline => {
cycknots[numknots+1] ← cycknots[1];
cycknots[numknots+2] ← cycknots[2];
};
ENDCASE;
RETURN[cycknots];
};
AppendCubic: PROCEDURE[c: Cubic.Bezier, origin: IntegerVec, path: PathRef] = {
b0, b1, b2, b3: Vector.Vec;
b0 ← XFormPt[c.b0,origin];
b1 ← XFormPt[c.b1,origin];
b2 ← XFormPt[c.b2,origin];
b3 ← XFormPt[c.b3,origin];
IF CGPath.Empty[path] THEN Graphics.MoveTo[path,b0.x,b0.y];
Graphics.CurveTo[path,b1.x,b1.y,b2.x,b2.y,b3.x,b3.y];
};
AppendPoint: PROCEDURE[p: Vector.Vec, origin: IntegerVec, path: PathRef] = {
v: Vector.Vec ← XFormPt[p,origin];
IF CGPath.Empty[path] THEN Graphics.MoveTo[path,v.x,v.y]
ELSE Graphics.LineTo[path,v.x,v.y];
};
XFormPt: PROC [p: Vector.Vec, origin: IntegerVec] RETURNS [Vector.Vec] = {
RETURN[[GriffinToTAPrivate.ScalePressToScreenCoord[p.x]-origin.x,
GriffinToTAPrivate.ScalePressToScreenCoord[p.y]-origin.y]];
};
--so ForAllPairs in generating objects does so in chronological order
ReverseRelation: PROCEDURE[map: PairList.Relation] RETURNS[PairList.Relation] = {
new: PairList.Relation ← PairList.CreateRelation[];
add: PROC[leftPart, rightPart: REF ANY] = {
PairList.AddPair[new, leftPart, rightPart]};
IF map=NIL THEN RETURN[map];
PairList.ForAllPairs[map, add];
map ← new;
PairList.DestroyRelation[new];
RETURN[map];
};
-- private low-level disk access procedures
ReadWord: PROCEDURE RETURNS[CARDINAL] = {
high, low: CHARACTER;
word: Inline.BytePair;
high ← IO.GetChar[diskHandle];
low ← IO.GetChar[diskHandle];
word.high ← LOOPHOLE[high]; word.low ← LOOPHOLE[low];
RETURN[LOOPHOLE[word]];
};
ReadString: PROCEDURE RETURNS[r: ROPE] = {
t: REF TEXT;
length: CARDINAL;
length ← LOOPHOLE[IO.GetChar[diskHandle], CARDINAL];
t ← NEW[TEXT[length]];
IF IO.GetBlock[diskHandle,t,0,length] # length
THEN SIGNAL GriffinFileError["Possible disk problem"];
r ← Rope.FromRefText[t];
--always read an even number of bytes: string length + string chars MOD 2 = 0
IF (length+1) MOD 2 # 0 THEN [] ← IO.GetChar[diskHandle];
};
MoveToSector: PROCEDURE[si: GFileFormatDefs.SectorIndex] = {
IO.SetIndex[diskHandle, INTEGER[si*Environment.charsPerPage]];
};
ReadStructure: PROCEDURE[p: LONG POINTER, l: CARDINAL] = {
from: LONG POINTER;
b: REF TEXT ← NEW[TEXT[l*2]];
IF IO.GetBlock[diskHandle, b, 0, 2*l] # 2*l
THEN SIGNAL GriffinFileError["Possible disk problem"];
from ← LOOPHOLE[b,LONG POINTER]+2; --start of bytes
Inline.LongCOPY[from,l,p];
};
GetReal: PROCEDURE[r: LONG UNSPECIFIED] RETURNS[out: REAL] = {
IF realConvert THEN RETURN[RealConvert.Mesa5ToIeee[r]] ELSE RETURN[r];
};
FixFileName: PROCEDURE[oldname, extension: ROPE] RETURNS [newname:ROPE] = {
dotPosition: INTEGER ← Rope.Find[oldname, "."];
IF dotPosition < 0 THEN
newname ← Rope.Cat[oldname, extension]
ELSE
newname ← Rope.Cat[Rope.Substr[oldname, 0, dotPosition], extension];
};
}.