GriffinToTAImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Read Griffin files and convert to Tioga Artwork files
borrowed liberally from ReadGriffinImpl.mesa and GriffinFile.mesa
Rick Beach, February 9, 1985 9:57:42 pm PST
Maureen Stone May 5, 1983 4:52 pm
DIRECTORY
Basics USING [Byte, BytePair],
Cubic USING [Bezier, CoeffsToBezier, Coeffs],
GFileFormatDefs,
GriffinToTA,
GriffinToTANodes,
GriffinToTAPrivate,
GriffinToJaM,
Graphics USING [Path, MoveTo, LineTo, CurveTo, NewPath],
CGPath USING [Empty],
IO,
FS USING [Error, StreamOpen],
PairList,
PrincOps USING [bytesPerPage],
PrincOpsUtils USING [LongCOPY],
RealConvert USING [Mesa5ToIeee],
Rope,
SplineDefs,
StyleDefs,
Vector USING [Vec];
GriffinToTAImpl:
CEDAR PROGRAM
IMPORTS Cubic, GriffinToTANodes, GriffinToTAPrivate, IO, PairList, PrincOpsUtils, RealConvert, Rope, SplineDefs, FS, 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: Basics.Byte = 1;
minorVersion: Basics.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 ← FS.StreamOpen[fileName ! FS.Error => 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 ← FixFileNIme[fileName, ".Griffin"];
diskHandle ← FS.StreamOpen[fileName ! FS.Error => CHECKED {GOTO BqdCreate}];
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;
lastCìuster ← 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] =
TRUSTED {
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:
LONG
POINTER
TO GFileFormatDefs.GFileHeader] =
TRUSTED {
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];
TRUSTED { style.thickness ← GetReal[LOOPHOLE[style.thickness]] };
PairList.AddPair[styleMap, refStyleId, style];
};
ReadObject:
PROCEDURE[objectMap: PairList.Relation, view: ViewType] =
TRUSTED {
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] =
TRUSTED {
knotword: GFileFormatDefs.GFileKnotWord;
knots: SplineDefs.KnotSequence;
coeffs: SplineDefs.CoeffsSequence;
CoeffsLength:
PROCEDURE [coeffs: SplineDefs.CoeffsSequence]
RETURNS [
CARDINAL] =
TRUSTED {
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: Basics.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*PrincOps.bytesPerPage]];
};
ReadStructure:
PROCEDURE[p:
LONG
POINTER, l:
CARDINAL] =
TRUSTED {
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
PrincOpsUtils.LongCOPY[from,l,p];
};
GetReal:
PROCEDURE[r:
LONG
UNSPECIFIED]
RETURNS[out:
REAL] =
TRUSTED {
IF realConvert THEN RETURN[RealConvert.Mesa5ToIeee[r]] ELSE RETURN[LOOPHOLE[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];
};
}.