-- 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; -- styleMap: PUBLIC PairList.Relation; -- objectMap: PUBLIC PairList.Relation; -- pathMap: PUBLIC PairList.Relation; -- captionMap: PUBLIC PairList.Relation; -- clusterMap: PUBLIC PairList.Relation; -- 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]; }; }. ΚR˜J˜Iprocš‰ΟcβœΟk œžœ0žœvžœižœ#žœ8žœžœžœ6žœTžœžœžœžœžœžœžœžœ žœ'žœžœžœ"žœ$žœ#žœ$žœ'žœ%žœ'žœ+žœ.žœ1žœ-žœžœ žœœ žœœ žœœ žœœ žœœ žœœ žœ!œžœžœžœžœžœ žœžœžœžœžœžœžœžœžœžœžœžœ!œ žœžœžœžœžœ œUžœžœ3œΟn œžœž œ žœžœ žœwžœžœ<žœžœCžœžžœžœžœDŸœžœž œ žœžœ žœwžœžœ<žœžœCžœXžœžœžœDŸœž œ°žœ5žœ)žœ Ÿœž œτžœžœ žœŸ œž œ•žœG œΙ œžœ*žœžœGžœžœžœžœžœžœžœžœžœžœ#žœŸ œž œžœžœ#žœYžœžœžœžœ@œ<œžœžœžœ žœžœ Ÿœž œ žœ4žœ*žœžœžœžœ!žœWŸ œž œžœ6žœ,žœžœžœžœ"žœ‰Ÿ œž œPžœ0žœ\žœžœKžœžœžœwžœžœUžœ6žœžœώžœžœžœPžœ:žœ žœžœAžœ žœAžœ5žœžœžœ’žœήžœ*žœžœ"žœ žœžœžœžœžœ"žœžœžœžœzžœžœβžœžœ8žœlŸœž œ žœ-žœ*žœžœ0žœŸœž œ,žœ™Οb œž œ%žœžœžœžœžœžœΟsœžœ~žœžœžœ žœžœ/žœ&žœžœ žœˆžœžœžœdžœžœžœ:žœžœ(žœžœ(žœžœ(žœžœžœ žœ>žœkžœžœ žœ>žœ žœMžœžœžœ%Ÿœž œ>žœMžœžœ‘žœžœžœžœžœžœžœžœžœnžœžœ*žœžœžœžœ&žœžœŒžœžœŸ œž œΗžœžœ^Ÿ œž œ2žœ)žœžœœFœŸœž œžœSžœžœžœ6žœžœžœžœUžœ ,œŸœž œžœžœž œ"žœžœ#žœžœžœžœŸ œž œžœžœ žœžœ žœ žœžœžœžœžœ žœžœ,žœžœGNœžœ žœžœžœŸ œž œ'žœžœ%Ÿ œž œžœžœžœ žœžœžœžœžœžœ žœžœ)žœžœ4žœžœžœœ!Ÿœž œžœž œžœžœžœ žœžœžœžœ Ÿ œž œžœžœ žœžœžœžœ+žœP˜wJ˜—…—;’Aκ