<> <> <> <> DIRECTORY CGCubic USING [Bezier, CoeffsToBezier, Coeffs], GFileFormatDefs, PrincOpsUtils USING [LongCOPY], Basics USING [BytePair], Graphics USING [Path, MoveTo, LineTo, CurveTo, NewPath], CGPath USING [Empty], IO, FS, PairList, ReadGriffin, RealConvert USING [Mesa5ToIeee], Rope, SplineDefs, StyleDefs, GraphicsBasic USING [Vec]; ReadGriffinImpl: CEDAR PROGRAM IMPORTS CGCubic, PrincOpsUtils, IO, PairList, ReadGriffin, RealConvert, Rope, SplineDefs, FS, Graphics, CGPath EXPORTS ReadGriffin = { OPEN ReadGriffin; ROPE: TYPE = Rope.ROPE; GriffinFileError: PUBLIC SIGNAL[why: ROPE] = CODE; FigureNumber: TYPE = CARDINAL[1 .. 100]; IntegerVec: TYPE = RECORD[ x, y: INTEGER]; <> 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: GFileFormatDefs.BYTE = 1; minorVersion: GFileFormatDefs.BYTE = 4; realConvert: BOOLEAN _ FALSE; -- convert reals if older than Griffin version 1.4 ConvertFile: PUBLIC PROCEDURE[fileName: ROPE, view: ViewType _ main] = { 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}; <> <> <> IO.Close[diskHandle]; EXITS BadCreate => {SIGNAL GriffinFileError[Rope.Cat["file ", fileName, " not found"]]}; }; InitDataStructures: PROCEDURE = { colors _ 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] = 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]; <> 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] = TRUSTED { IO.SetIndex[diskHandle,0]; ReadStructure[h, GFileFormatDefs.lGFileHeader]; realConvert _ FALSE; IF h.majversion#majorVersion OR h.minversion#minorVersion THEN { <> <> IF h.majversion=1 AND h.minversion IN [0..3] THEN realConvert _ TRUE; }; }; ReadFont: PROCEDURE[fontNumber: CARDINAL, fontMap: PairList.Relation] = TRUSTED { 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] = TRUSTED { 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] = 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: GraphicsBasic.Vec; ReadStructure[@fcaptiontrailer, GFileFormatDefs.lGFileCaptionTrailer]; pt.x _ GetReal[fcaptiontrailer.xanchor]; pt.y _ GetReal[fcaptiontrailer.yanchor]; pt.x _ ScalePressToScreenCoord[pt.x]; pt.y _ 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: CGCubic.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[CGCubic.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: CGCubic.Bezier, origin: IntegerVec, path: PathRef] = { b0, b1, b2, b3: GraphicsBasic.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: GraphicsBasic.Vec, origin: IntegerVec, path: PathRef] = { v: GraphicsBasic.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: GraphicsBasic.Vec, origin: IntegerVec] RETURNS [GraphicsBasic.Vec] = { RETURN[[ScalePressToScreenCoord[p.x]-origin.x, ScalePressToScreenCoord[p.y]-origin.y]]; }; <> ReverseRelation: PROCEDURE[map: PairList.Relation] RETURNS[PairList.Relation] = { new: PairList.Relation _ PairList.CreateRelation[]; AddPair: PROCEDURE[leftPart, rightPart: REF ANY _ NIL] = { PairList.AddPair[new, leftPart, rightPart]}; IF map=NIL THEN RETURN[map]; PairList.ForAllPairs[map, AddPair]; map _ new; PairList.DestroyRelation[new]; RETURN[map]; }; <> 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]; <> IF (length+1) MOD 2 # 0 THEN [] _ IO.GetChar[diskHandle]; }; charsPerPage: CARDINAL ~ 512; MoveToSector: PROCEDURE[si: GFileFormatDefs.SectorIndex] = { IO.SetIndex[diskHandle, INTEGER[si*charsPerPage]]; }; 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: REAL] RETURNS[out: REAL] = TRUSTED { IF realConvert THEN RETURN[RealConvert.Mesa5ToIeee[r]] ELSE RETURN[r]; }; FixFileName: PUBLIC 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]; }; InitColors: PROCEDURE[] RETURNS[ColorRef] = { -- create the Griffin color table: "name", h, s, b colors _ NEW[ColorRec _ [NIL, "black", 0, 0, 0]]; colors _ NEW[ColorRec _ [colors, "darkBrown", 7, 255, 59]]; colors _ NEW[ColorRec _ [colors, "brown", 7, 255, 118]]; colors _ NEW[ColorRec _ [colors, "tan", 0, 131, 217]]; colors _ NEW[ColorRec _ [colors, "maroon", 234, 255, 79]]; colors _ NEW[ColorRec _ [colors, "darkRed", 0, 255, 160]]; colors _ NEW[ColorRec _ [colors, "red", 0, 255, 255]]; colors _ NEW[ColorRec _ [colors, "orange", 10, 255, 255]]; colors _ NEW[ColorRec _ [colors, "darkYellow", 25, 255, 255]]; colors _ NEW[ColorRec _ [colors, "yellow", 40, 255, 255]]; colors _ NEW[ColorRec _ [colors, "lightYellow", 40, 190, 255]]; colors _ NEW[ColorRec _ [colors, "darkGreen", 71, 255, 59]]; colors _ NEW[ColorRec _ [colors, "green", 76, 255, 255]]; colors _ NEW[ColorRec _ [colors, "lightGreen", 71, 193, 255]]; colors _ NEW[ColorRec _ [colors, "darkBlue", 150, 255, 170]]; colors _ NEW[ColorRec _ [colors, "blue", 148, 255, 255]]; colors _ NEW[ColorRec _ [colors, "lightBlue", 141, 150, 255]]; colors _ NEW[ColorRec _ [colors, "darkAqua", 107, 255, 98]]; colors _ NEW[ColorRec _ [colors, "aqua", 107, 224, 255]]; colors _ NEW[ColorRec _ [colors, "cyan", 120, 255, 255]]; colors _ NEW[ColorRec _ [colors, "darkPurple", 178, 255, 178]]; colors _ NEW[ColorRec _ [colors, "purple", 170, 224, 255]]; colors _ NEW[ColorRec _ [colors, "violet", 170, 131, 255]]; colors _ NEW[ColorRec _ [colors, "magenta", 200, 255, 255]]; colors _ NEW[ColorRec _ [colors, "pink", 206, 170, 255]]; colors _ NEW[ColorRec _ [colors, "darkGrey", 0, 0, 40]]; colors _ NEW[ColorRec _ [colors, "grey", 0, 0, 120]]; colors _ NEW[ColorRec _ [colors, "lightGrey", 0, 0, 200]]; colors _ NEW[ColorRec _ [colors, "paleGrey", 0, 0, 230]]; colors _ NEW[ColorRec _ [colors, "white", 0, 0, 255]]; RETURN[colors]; }; }.