<> <> <> <> <> <<>> DIRECTORY Atom, Convert, FileNames, GGBasicTypes, GGBoundBox, GGCircles, GGSlice, GGInterfaceTypes, GGLines, GGModelTypes, GGShapes, GGParseOut, GGParseIn, GGTransform, GGVector, Imager, ImagerFont, ImagerTransformation, IO, MessageWindow, NodeStyle, NodeStyleFont, Real, RealFns, Rope; GGSliceImpl: CEDAR PROGRAM IMPORTS Atom, Convert, FileNames, GGBoundBox, GGCircles, GGLines, GGParseIn, GGParseOut, GGShapes, GGTransform, GGVector, Imager, ImagerFont, IO, MessageWindow, NodeStyleFont, Real, RealFns, Rope, ImagerTransformation EXPORTS GGSlice = BEGIN Circle: TYPE = GGBasicTypes.Circle; Point: TYPE = GGBasicTypes.Point; PointGenerator: TYPE = GGModelTypes.PointGenerator; PointGeneratorObj: TYPE = GGModelTypes.PointGeneratorObj; PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator; PointPairGeneratorObj: TYPE = GGModelTypes.PointPairGeneratorObj; SliceObj: TYPE = GGModelTypes.SliceObj; SliceClass: TYPE = GGModelTypes.SliceClass; SliceClassObj: TYPE = GGModelTypes.SliceClassObj; SliceParts: TYPE = GGModelTypes.SliceParts; Slice: TYPE = GGModelTypes.Slice; SelectMode: TYPE = GGModelTypes.SelectMode; ExtendMode: TYPE = GGModelTypes.ExtendMode; BoundBox: TYPE = GGModelTypes.BoundBox; SelectedObjectData: TYPE = GGModelTypes.SelectedObjectData; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj; CameraData: TYPE = GGInterfaceTypes.CameraData; DisplayStyle: TYPE = GGSlice.DisplayStyle; Corner: TYPE = GGSlice.Corner; CornerArray: TYPE = ARRAY [0..4) OF BOOL; -- ll, ul, ur, lr. Clockwise order of corners. EdgeArray: TYPE = ARRAY [0..4) OF BOOL; -- left, top, right, bottom. Clockwise order of edges. SliceClassDef: TYPE = REF SliceClassDefObj; SliceClassDefObj: TYPE = RECORD[type: ATOM, class: SliceClass]; TextData: TYPE = REF TextDataObj; TextDataObj: TYPE = RECORD [ rope: Rope.ROPE _ NIL, worldPt: Point, fontName: Rope.ROPE _ NIL, -- font description: e.g. Helvetica11BI fontFamily: ATOM, fontFace: NodeStyle.FontFace _ Regular, fontSize: REAL _ 10.0, printFont: ImagerFont.Font _ NIL, screenFont: ImagerFont.Font _ NIL, color: Imager.Color _ NIL, colorName: Rope.ROPE _ NIL, feedbackBox: BoundBox _ NIL ]; TextHitData: TYPE = REF TextHitDataObj; TextHitDataObj: TYPE = RECORD [ bestSeg: NAT _ 0 -- segment number from ClosestSegementProc ]; IPData: TYPE = REF IPDataObj; IPDataObj: TYPE = RECORD [ file: Rope.ROPE, worldPt: Point, feedbackBox: BoundBox, transform: ImagerTransformation.Transformation ]; IPHitData: TYPE = REF IPHitDataObj; IPHitDataObj: TYPE = RECORD [ bestSeg: NAT _ 0 -- segment number from ClosestSegementProc ]; NotFound: PUBLIC SIGNAL = CODE; FontNameError: PUBLIC SIGNAL = CODE; Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = CODE; FetchSliceClass: PUBLIC PROC [type: ATOM] RETURNS [class: SliceClass] = { FOR l: LIST OF SliceClassDef _ sliceClasses, l.rest UNTIL l=NIL DO IF l.first.type=type THEN RETURN[l.first.class]; ENDLOOP; SIGNAL NotFound; RETURN[NIL]; }; <<>> <> NoOpPointsInDescriptor: PUBLIC PROC [sliceD: SliceDescriptor] RETURNS [pointGen: PointGenerator] = { pointGen _ NIL; }; NoOpPointPairsInDescriptor: PUBLIC PROC [sliceD: SliceDescriptor] RETURNS [pointPairGen: PointPairGenerator] = { pointPairGen _ NIL; }; NoOpNextPoint: PUBLIC PROC [pointGen: PointGenerator] RETURNS [pointAndDone: GGModelTypes.PointAndDone] = { pointAndDone.done _ TRUE; }; NoOpNextPointPair: PUBLIC PROC [pointPairGen: PointPairGenerator] RETURNS [pointPairAndDone: GGModelTypes.PointPairAndDone] = { pointPairAndDone.done _ TRUE; }; <<>> <> BuildTextSliceClass: PROC [] RETURNS [class: SliceClass] = { class _ NEW[SliceClassObj _ [ type: $Text, boundBox: TextBoundBox, copy: TextCopy, draw: TextDraw, drawTransform: TextDrawTransform, drawSelectionFeedback: TextDrawSelectionFeedback, transform: TextTransform, emptyParts: TextEmptyParts, newParts: TextNewParts, unionParts: TextUnionParts, differenceParts: TextDiffParts, pointsInDescriptor: NoOpPointsInDescriptor, pointPairsInDescriptor: NoOpPointPairsInDescriptor, nextPoint: NoOpNextPoint, nextPointPair: NoOpNextPointPair, closestPoint: TextClosestPoint, closestPointAndTangent: NIL, closestSegment: TextClosestSegment, fileout: TextFileout, filein: TextFilein, setStrokeWidth: TextSetStrokeWidth, <> setStrokeColor: TextSetStrokeColor, <> setFillColor: TextSetFillColor <> ]]; }; MakeTextSlice: PUBLIC PROC [text: Rope.ROPE, fontName: Rope.ROPE, colorName: Rope.ROPE, worldPt: Point] RETURNS [slice: Slice] = { feedbackBox: BoundBox _ GGBoundBox.CreateBoundBox[0,0,0,0]; textData: TextData _ NEW[TextDataObj _ [rope: text, worldPt: worldPt, color: Imager.black, colorName: colorName, feedbackBox: feedbackBox, fontFace: , fontSize: ]]; slice _ NEW[SliceObj _ [ class: FetchSliceClass[$Text], data: textData, children: NIL, parent: NIL, selectedInFull: [FALSE, FALSE, FALSE, FALSE], boundBox: GGBoundBox.CreateBoundBox[0,0,0,0], -- gets filled in later onOverlay: FALSE, hitData: NEW[TextHitDataObj _ [] ] ]]; SetTextFont[slice, fontName]; }; GetText: PUBLIC PROC [slice: Slice] RETURNS [text: Rope.ROPE] = { RETURN[IF slice.class.type#$Text THEN NIL ELSE NARROW[slice.data, TextData].rope]; }; AppendText: PUBLIC PROC [slice: Slice, text: Rope.ROPE] = { textData: TextData; IF slice.class.type#$Text THEN RETURN; textData _ NARROW[slice.data]; textData.rope _ Rope.Concat[textData.rope, text]; -- update text slice.class.boundBox[slice]; -- update bound box }; BackspaceText: PUBLIC PROC [slice: Slice] = { textData: TextData; IF slice.class.type#$Text THEN RETURN; textData _ NARROW[slice.data]; textData.rope _ Rope.Substr[base: textData.rope, len: Rope.Length[textData.rope]-1]; -- update text slice.class.boundBox[slice]; -- update bound box }; DigitBreak: IO.BreakProc = { RETURN[IF char IN ['0..'9] THEN break ELSE other]; }; FontParamsFromFontName: PUBLIC PROC [fontName: Rope.ROPE] RETURNS [fontFamily: ATOM, fontFace: NodeStyle.FontFace, fontSize: REAL] = { <> GetNumber: PROC [s: IO.STREAM] RETURNS [x: REAL _ 0.0] = { c: CHAR; UNTIL (c _ IO.GetChar[s ! IO.EndOfStream => {c _ 'A; CONTINUE;}; ]) NOT IN ['0..'9] DO x _ x*10+(c-'0); ENDLOOP; }; familyName, face: Rope.ROPE; bold, italic: BOOL _ FALSE; fontStream: IO.STREAM _ IO.RIS[fontName]; [familyName, ----] _ IO.GetTokenRope[stream: fontStream, breakProc: DigitBreak ! IO.EndOfStream => SIGNAL FontNameError]; fontSize _ GetNumber[fontStream ! IO.EndOfStream => SIGNAL FontNameError]; <> [face, ----] _ IO.GetTokenRope[stream: fontStream, breakProc: IO.TokenProc ! IO.EndOfStream => {face _ ""; CONTINUE; };]; fontFamily _ Atom.MakeAtom[familyName]; SELECT Rope.Length[face] FROM 0 => NULL; 1 => bold _ Rope.Fetch[base: face, index: 0]='B; 2 => { bold _ Rope.Fetch[base: face, index: 0]='B; italic _ Rope.Fetch[base: face, index: 1]='I; }; ENDCASE => NULL; fontFace _ SELECT TRUE FROM bold AND italic => BoldItalic, bold => Bold, italic => Italic, ENDCASE => Regular; }; SetTextFont: PUBLIC PROC [slice: Slice, fontName: Rope.ROPE] = { <> textData: TextData; font: Imager.Font; fontFamily: ATOM; fontFace: NodeStyle.FontFace; fontSize: REAL; IF slice.class.type#$Text OR fontName=NIL THEN RETURN; textData _ NARROW[slice.data]; [fontFamily, fontFace, fontSize] _ FontParamsFromFontName[fontName ! FontNameError => GOTO Abort; ]; font _ NodeStyleFont.FontFromStyleParams[prefix: printPrefix, family: fontFamily, face: fontFace, size: fontSize, alphabets: CapsAndLower]; IF Rope.Find[s1: font.name, s2: Atom.GetPName[fontFamily], case: FALSE]=-1 THEN GOTO Abort ELSE textData.printFont _ font; --May do a font substitution which we don't want. textData.screenFont _ NodeStyleFont.FontFromStyleParams[prefix: screenPrefix, family: fontFamily, face: fontFace, size: fontSize, alphabets: CapsAndLower]; IF Rope.Find[s1: textData.screenFont.name, s2: Atom.GetPName[fontFamily], case: FALSE]=-1 THEN textData.screenFont _ textData.printFont; --Did a font substitution which we don't want. textData.fontName _ fontName; textData.fontFamily _ fontFamily; textData.fontFace _ fontFace; textData.fontSize _ fontSize; slice.class.boundBox[slice]; -- update bound box EXITS Abort => { MessageWindow.Append["Gargoyle: Font Not Found", TRUE]; MessageWindow.Blink[]; }; }; GetTextFont: PUBLIC PROC [slice: Slice] RETURNS [font: ImagerFont.Font, fontName: Rope.ROPE] = { textData: TextData; IF slice.class.type#$Text THEN RETURN[NIL, NIL]; textData _ NARROW[slice.data]; RETURN[textData.printFont, textData.fontName]; }; <> <> <> <> <> <> <<};>> <<>> <> <> <> <> <> <<};>> <<>> TextBoundBox: PROC [slice: Slice] = { <> textData: TextData _ NARROW[slice.data]; printExtents, screenExtents: ImagerFont.Extents; halfCP: REAL = GGModelTypes.halfJointSize + 1; printExtents _ ImagerFont.RopeBoundingBox[textData.printFont, textData.rope]; screenExtents _ ImagerFont.RopeBoundingBox[textData.screenFont, textData.rope]; <> <> GGBoundBox.UpdateBoundBox[slice.boundBox, textData.worldPt.x - screenExtents.leftExtent - halfCP, textData.worldPt.y - screenExtents.descent - halfCP, textData.worldPt.x + screenExtents.rightExtent + halfCP, textData.worldPt.y + screenExtents.ascent + halfCP]; <> GGBoundBox.UpdateBoundBox[textData.feedbackBox, textData.worldPt.x - printExtents.leftExtent, textData.worldPt.y - printExtents.descent, textData.worldPt.x + printExtents.rightExtent, textData.worldPt.y + printExtents.ascent]; }; TextCopy: PROC [slice: Slice] RETURNS [copy: Slice] = { <> textData: TextData _ NARROW[slice.data]; copy _ MakeTextSlice[textData.rope, textData.fontName, textData.colorName, textData.worldPt]; <> copy.parent _ slice.parent; copy.children _ slice.children; }; TextDraw: PROC [slice: Slice, dc: Imager.Context, camera: CameraData] = { <> textData: TextData _ NARROW[slice.data]; Imager.SetFont[dc, IF camera.displayStyle=screen THEN textData.screenFont ELSE textData.printFont]; Imager.SetXY[dc, [textData.worldPt.x, textData.worldPt.y]]; Imager.ShowRope[dc, textData.rope]; }; TextDrawTransform: PROC [slice: Slice, parts: SliceParts, dc: Imager.Context, camera: CameraData, transform: ImagerTransformation.Transformation] = { <> <> textData: TextData _ NARROW[slice.data]; tempPoint: Point _ GGTransform.Transform[transform, textData.worldPt]; Imager.SetFont[dc, IF camera.displayStyle=screen THEN textData.screenFont ELSE textData.printFont]; Imager.SetXY[dc, [ tempPoint.x, tempPoint.y ] ]; Imager.ShowRope[dc, textData.rope]; }; TextTransform: PROC [slice: Slice, parts: SliceParts, transform: ImagerTransformation.Transformation] = { <> <> textData: TextData _ NARROW[slice.data]; textData.worldPt _ GGTransform.Transform[transform, textData.worldPt]; TextBoundBox[slice]; }; TextDrawSelectionFeedback: PROC [slice: Slice, parts: SliceParts, dc: Imager.Context, camera: CameraData, quick: BOOL _ FALSE] = { <> textData: TextData _ NARROW[slice.data]; GGBoundBox.DrawBoundBox[dc, textData.feedbackBox]; <> }; TextEmptyParts: PROC [slice: Slice, parts: SliceParts] RETURNS [BOOL] = { RETURN[TRUE] }; TextNewParts: PROC [slice: Slice, mode: SelectMode] RETURNS [parts: SliceParts] = { RETURN[NIL] }; TextUnionParts: PROC [slice: Slice, partsA: SliceParts, partsB: SliceParts] RETURNS [aPlusB: SliceParts] = { RETURN[NIL] }; TextDiffParts: PROC [slice: Slice, partsA: SliceParts, partsB: SliceParts] RETURNS [aMinusB: SliceParts] = { RETURN[NIL] }; TextClosestPoint: PROC [slice: Slice, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = { <> <> textData: TextData _ NARROW[slice.data]; <<[bestDist, ----, bestPoint] _ GGBoundBox.NearestPoint[textData.feedbackBox, testPoint];>> <> [bestDist, ----, bestPoint, success] _ GGBoundBox.NearestPoint[textData.feedbackBox, testPoint]; }; TextClosestSegment: PROC [slice: Slice, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = { <> <> textData: TextData _ NARROW[slice.data]; textHitData: TextHitData _ NARROW[slice.hitData]; [bestDist, textHitData.bestSeg, bestPoint, success] _ GGBoundBox.NearestSegment[textData.feedbackBox, testPoint, tolerance]; }; TextFileout: PROC [slice: Slice, f: IO.STREAM] = { <> <> textData: TextData _ NARROW[slice.data]; f.PutF["\"%g\" %g %g", [rope[textData.rope]], [rope[textData.fontName]], [real[textData.fontSize]] ]; f.PutF[" %g ", [rope[textData.colorName]] ]; GGParseOut.WritePoint[f, textData.worldPt]; }; TextFilein: PROC [f: IO.STREAM, version: REAL] RETURNS [slice: Slice] = { <> <> UnpackComplexFontName: PROC [fontName: Rope.ROPE, fontSize: REAL] RETURNS [new: Rope.ROPE] = { <> face: Rope.ROPE; new _ FileNames.GetShortName[fontName]; -- throw away "xerox/pressfonts/" face _ FileNames.Tail[new, '-]; -- get face component (MIR, BRR, ...) new _ Rope.Substr[base: new, start: 0, len: Rope.SkipTo[s: new, pos: 0, skip: "-"]]; -- throw away "-XXX" new _ Rope.Cat[new, Convert.RopeFromInt[Real.FixI[fontSize]], SELECT TRUE FROM Rope.Equal[face, "MRR", FALSE] => "", Rope.Equal[face, "BRR", FALSE] => "B", Rope.Equal[face, "MIR", FALSE] => "I", Rope.Equal[face, "BIR", FALSE] => "BI", ENDCASE => ERROR ]; }; rope, fontName, colorName: Rope.ROPE; fontSize: REAL; point: Point; <> IF version > 8601.22 THEN { rope _ f.GetRopeLiteral[]; } ELSE { GGParseIn.ReadBlankAndRope[f, "("]; rope _ GGParseIn.ReadBlankAndWord[f]; GGParseIn.ReadBlankAndRope[f, ")"]; }; <> IF version <= 8601.06 THEN { -- no font name at all fontName _ "Helvetica10"; fontSize _ 10.0; colorName _ "black"; } ELSE IF version <= 8601.27 THEN { -- a simple name like "Helvetica" or "Gacha" fontName _ GGParseIn.ReadBlankAndWord[f]; fontName _ Rope.Concat[fontName, "10"]; fontSize _ 10.0; colorName _ "black"; } ELSE IF version <= 8605.12 THEN { -- a complex name like "xerox/pressfonts/Helvetica-BIR" fontName _ GGParseIn.ReadBlankAndWord[f]; fontSize _ GGParseIn.ReadBlankAndReal[f]; colorName _ GGParseIn.ReadBlankAndWord[f]; fontName _ UnpackComplexFontName[fontName, fontSize]; } ELSE { -- a mixed mode name like Helvetica7BI or TimesRoman12 fontName _ GGParseIn.ReadBlankAndWord[f]; fontSize _ GGParseIn.ReadBlankAndReal[f]; colorName _ GGParseIn.ReadBlankAndWord[f]; }; GGParseIn.ReadBlank[f]; point _ GGParseIn.ReadPoint[f]; slice _ MakeTextSlice[rope, fontName, colorName, point]; }; TextSetStrokeWidth: PROC [slice: Slice, parts: SliceParts, strokeWidth: REAL] = {}; <> <> <<};>> TextSetStrokeColor: PROC [slice: Slice, parts: SliceParts, color: Imager.Color] = {}; <> <> <<};>> TextSetFillColor: PROC [slice: Slice, color: Imager.Color] = { textData: TextData _ NARROW[slice.data]; textData.color _ color; }; <> <> <> <<};>> <> BoxData: TYPE = REF BoxDataObj; BoxDataObj: TYPE = RECORD [ box: BoundBox, <> transform: ImagerTransformation.Transformation, inverse: ImagerTransformation.Transformation, -- inverse of transform strokeWidths: ARRAY [0..4) OF REAL, -- in order left, top, right, bottom strokeColors: ARRAY [0..4) OF Imager.Color, -- in order left, top, right, bottom fillColor: Imager.Color ]; BoxParts: TYPE = REF BoxPartsObj; BoxPartsObj: TYPE = RECORD [ corners: CornerArray, -- which corners of box are selected. edges: EdgeArray -- which edges of box are selected. ]; BoxHitData: TYPE = REF BoxHitDataObj; BoxHitDataObj: TYPE = RECORD [ <> corner: [-1..4), -- which corner of box is hit. edge: [-1..4) -- which edge of box is hit. ]; BuildBoxSliceClass: PUBLIC PROC [] RETURNS [class: SliceClass] = { class _ NEW[SliceClassObj _ [ type: $Box, boundBox: BoxBoundBox, copy: BoxCopy, draw: BoxDraw, drawTransform: BoxDrawTransform, drawSelectionFeedback: BoxDrawSelectionFeedback, transform: BoxTransform, emptyParts: BoxEmptyParts, newParts: BoxNewParts, unionParts: BoxUnionParts, differenceParts: BoxDiffParts, pointsInDescriptor: BoxPointsInDescriptor, pointPairsInDescriptor: BoxPointPairsInDescriptor, nextPoint: BoxNextPoint, nextPointPair: BoxNextPointPair, closestPoint: BoxClosestPoint, closestPointAndTangent: NIL, closestSegment: BoxClosestSegment, fileout: BoxFileout, filein: BoxFilein, setStrokeWidth: BoxSetStrokeWidth, <> setStrokeColor: BoxSetStrokeColor, <> setFillColor: BoxSetFillColor <> ]]; }; MakeBoxSlice: PUBLIC PROC [box: BoundBox, corner: Corner, transform: ImagerTransformation.Transformation _ GGTransform.Identity[], strokeWidth: REAL _ 1.0] RETURNS [sliceD: SliceDescriptor] = { <> boxSlice: Slice; boxData: BoxData _ NEW[BoxDataObj _ [box: box, transform: transform, inverse: ImagerTransformation.Invert[transform], strokeWidths: [strokeWidth, strokeWidth, strokeWidth, strokeWidth], strokeColors: [Imager.black, Imager.black, Imager.black, Imager.black], fillColor: NIL] ]; boxParts: BoxParts _ NEW[BoxPartsObj _ [corners: ALL[FALSE], edges: ALL[FALSE]] ]; cIndex: INTEGER _ SELECT corner FROM ll=>0, ul=>1, ur=>2, lr=>3,ENDCASE=>-1; boxParts.corners[cIndex] _ TRUE; IF box.loX > box.hiX THEN {t: REAL _ box.loX; box.loX _ box.hiX; box.hiX _ t}; IF box.loY > box.hiY THEN {t: REAL _ box.loY; box.loY _ box.hiY; box.hiY _ t}; boxSlice _ NEW[SliceObj _ [ class: FetchSliceClass[$Box], data: boxData, children: NIL, parent: NIL, selectedInFull: [FALSE, FALSE, FALSE, FALSE], boundBox: GGBoundBox.CreateBoundBox[0,0,0,0], hitData: NEW[BoxHitDataObj _ [corner: cIndex, edge: -1 ] ] ]]; BoxBoundBox[boxSlice]; sliceD _ NEW[SliceDescriptorObj _ [boxSlice, boxParts] ]; }; GetBox: PUBLIC PROC [slice: Slice] RETURNS [box: BoundBox] = { <> RETURN[IF slice.class.type#$Box THEN NIL ELSE NARROW[slice.data, BoxData].box]; }; <> BoxBoundBox: PROC [slice: Slice] = { <> halfJoint: REAL = GGModelTypes.halfJointSize + 1; boxData: BoxData _ NARROW[slice.data]; box: BoundBox _ GGBoundBox.BoundBoxOfBoundBox[boxData.box, boxData.transform]; GGBoundBox.UpdateBoundBox[slice.boundBox, box.loX-halfJoint, box.loY-halfJoint, box.hiX+halfJoint, box.hiY+halfJoint] }; BoxCopy: PROC [slice: Slice] RETURNS [copy: Slice] = { <> copyData: BoxData; boxData: BoxData _ NARROW[slice.data]; box: BoundBox _ GGBoundBox.CopyBoundBox[boxData.box]; transform: Imager.Transformation _ ImagerTransformation.Copy[boxData.transform]; copy _ MakeBoxSlice[box, ur, transform].slice; -- does ur work here ?? copyData _ NARROW[copy.data]; FOR i: NAT IN [0..4) DO copyData.strokeWidths[i] _ boxData.strokeWidths[i]; copyData.strokeColors[i] _ boxData.strokeColors[i]; ENDLOOP; copyData.fillColor _ boxData.fillColor; }; BoxDraw: PROC [slice: Slice, dc: Imager.Context, camera: GGInterfaceTypes.CameraData] = { <> DoDrawBoxOutline: PROC = { Imager.ConcatT[dc, boxData.transform]; BoxDrawOutline[dc, boxData, [boxData.box.loX, boxData.box.loY], [boxData.box.hiX, boxData.box.hiY]]; }; DoDrawBoxJoints: PROC = { Imager.ConcatT[dc, boxData.transform]; BoxDrawJointsAux[dc, [boxData.box.loX, boxData.box.loY], [boxData.box.hiX, boxData.box.hiY] ]; }; boxData: BoxData _ NARROW[slice.data]; Imager.DoSaveAll[dc, DoDrawBoxOutline]; IF camera.quality # quality THEN Imager.DoSaveAll[dc, DoDrawBoxJoints]; }; BoxDrawOutline: PROC [dc: Imager.Context, boxData: BoxData, from: Point, to: Point, width: REAL _ 1.0] = { <> <> <> strokeScale: REAL _ ImagerTransformation.Factor[boxData.inverse].s.x*width; IF boxData.fillColor#NIL THEN { Imager.SetColor[dc, boxData.fillColor]; Imager.MaskBox[dc, [from.x, from.y, to.x, to.y] ]; }; Imager.SetColor[dc, boxData.strokeColors[0]]; Imager.SetStrokeWidth[dc, strokeScale*boxData.strokeWidths[0]]; Imager.MaskVector[dc, [from.x, from.y], [from.x, to.y]]; Imager.SetColor[dc, boxData.strokeColors[1]]; Imager.SetStrokeWidth[dc, strokeScale*boxData.strokeWidths[1]]; Imager.MaskVector[dc, [from.x, to.y], [to.x, to.y]]; Imager.SetColor[dc, boxData.strokeColors[2]]; Imager.SetStrokeWidth[dc, strokeScale*boxData.strokeWidths[2]]; Imager.MaskVector[dc, [to.x, to.y], [to.x, from.y]]; Imager.SetColor[dc, boxData.strokeColors[3]]; Imager.SetStrokeWidth[dc, strokeScale*boxData.strokeWidths[3]]; Imager.MaskVector[dc, [to.x, from.y], [from.x, from.y]]; }; <> <> <> <> <> <> <> <> <> <> <> <> <> <<};>> <<>> BoxDrawJointsAux: PROC [dc: Imager.Context, from, to: Point] = { <> tLL: Point _ from; tUR: Point _ to; tUL: Point _ [from.x, to.y]; tLR: Point _ [to.x, from.y]; Imager.SetStrokeWidth[dc, 1.0]; GGShapes.DrawJoint[dc, tLL ]; GGShapes.DrawJoint[dc, tUL ]; GGShapes.DrawJoint[dc, tUR ]; GGShapes.DrawJoint[dc, tLR ]; }; MakeComplete: PROC [parts: SliceParts] = { WITH parts SELECT FROM boxParts: BoxParts => { boxParts.corners _ ALL[TRUE]; boxParts.edges _ ALL[TRUE]; }; circleParts: CircleParts => circleParts.cpArray _ ALL[TRUE]; ENDCASE => ERROR; }; IsComplete: PROC [parts: SliceParts] RETURNS [BOOL] = { WITH parts SELECT FROM boxParts: BoxParts => RETURN[ boxParts.corners=ALL[TRUE] AND boxParts.edges=ALL[TRUE] ]; circleParts: CircleParts => RETURN[ circleParts.cpArray=ALL[TRUE] ]; ENDCASE => ERROR; }; IsEmpty: PROC [parts: SliceParts] RETURNS [BOOL] = { WITH parts SELECT FROM boxParts: BoxParts => RETURN[ boxParts.corners=ALL[FALSE] AND boxParts.edges=ALL[FALSE] ]; circleParts: CircleParts => RETURN[ circleParts.cpArray=ALL[FALSE] ]; ENDCASE => ERROR; }; BoxDrawTransform: PROC [slice: Slice, parts: SliceParts, dc: Imager.Context, camera: GGInterfaceTypes.CameraData, transform: ImagerTransformation.Transformation] = { <> <> point, oppositePoint: ImagerTransformation.VEC; -- really points, not vectors boxData: BoxData _ NARROW[slice.data]; boxParts: BoxParts _ NARROW[parts]; box: BoundBox _ boxData.box; inverse, totalTransform: ImagerTransformation.Transformation; cornerCount, edgeCount: INTEGER _ 0; EasyDrawOutline: PROC = { Imager.ConcatT[dc, ImagerTransformation.Concat[boxData.transform, transform]]; BoxDrawOutline[dc, boxData, [box.loX, box.loY], [box.hiX, box.hiY] ]; }; EasyDrawJoints: PROC = { Imager.ConcatT[dc, ImagerTransformation.Concat[boxData.transform, transform]]; BoxDrawJointsAux[dc, [box.loX, box.loY], [box.hiX, box.hiY]]; -- does NO tranformations }; HardDrawOutline: PROC = { <> Imager.ConcatT[dc, boxData.transform]; BoxDrawOutline[dc, boxData, point, oppositePoint]; }; HardDrawJoints: PROC = { <> Imager.ConcatT[dc, boxData.transform]; BoxDrawJointsAux[dc, point, oppositePoint]; }; IF box.null OR box.infinite THEN ERROR; IF IsComplete[boxParts] THEN { Imager.DoSaveAll[dc, EasyDrawOutline]; IF camera.quality # quality THEN Imager.DoSaveAll[dc, EasyDrawJoints]; RETURN; }; IF IsEmpty[boxParts] THEN RETURN; -- no parts. Legal. IF (cornerCount _ CountCorners[boxParts.corners])+(edgeCount _ CountEdges[boxParts.edges]) >= 2 THEN { <> Imager.DoSaveAll[dc, EasyDrawOutline]; IF camera.quality # quality THEN Imager.DoSaveAll[dc, EasyDrawJoints]; RETURN; }; IF boxParts.corners#ALL[FALSE] AND boxParts.edges#ALL[FALSE] THEN ERROR; -- can't mix corners and edges while dragging. Doesn't make sense; must be a selection error. IF cornerCount=1 THEN { -- one corner. Transform it. SELECT TRUE FROM boxParts.corners[0] => { point _ [ box.loX, box.loY ]; oppositePoint _ [ box.hiX, box.hiY ]; }; boxParts.corners[1] => { point _ [ box.loX, box.hiY ]; oppositePoint _ [ box.hiX, box.loY ]; }; boxParts.corners[2] => { point _ [ box.hiX, box.hiY ]; oppositePoint _ [ box.loX, box.loY ]; }; boxParts.corners[3] => { point _ [ box.hiX, box.loY ]; oppositePoint _ [ box.loX, box.hiY ]; }; ENDCASE => ERROR; inverse _ boxData.inverse; totalTransform _ ImagerTransformation.Cat[boxData.transform, transform, inverse]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; -- transform the dragging point } ELSE IF edgeCount=1 THEN { -- one edge. Transform it. f: ImagerTransformation.FactoredTransformation _ ImagerTransformation.Factor[transform]; globalTranslate: ImagerTransformation.VEC _ f.t; mInverse: ImagerTransformation.Transformation _ boxData.inverse; localTranslate: ImagerTransformation.VEC _ ImagerTransformation.TransformVec[mInverse, globalTranslate]; totalTransform: ImagerTransformation.Transformation _ ImagerTransformation.Translate[localTranslate]; SELECT TRUE FROM boxParts.edges[0] => { -- left point _ [ box.loX, 0 ]; oppositePoint _ [ box.hiX, box.hiY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ point.x, box.loY]; -- result point is transformed in x only }; boxParts.edges[1] => { -- top point _ [ 0, box.hiY ]; oppositePoint _ [ box.loX, box.loY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ box.hiX, point.y]; -- result point is transformed in y only }; boxParts.edges[2] => { -- right point _ [ box.hiX, 0 ]; oppositePoint _ [ box.loX, box.loY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ point.x, box.hiY]; -- result point is transformed in x only }; boxParts.edges[3] => { -- bottom point _ [ 0, box.loY ]; oppositePoint _ [ box.hiX, box.hiY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ box.loX, point.y]; -- result point is transformed in y only }; ENDCASE => ERROR; } ELSE ERROR; -- a corner and an edge simultaneously Imager.DoSaveAll[dc, HardDrawOutline]; IF camera.quality # quality THEN Imager.DoSaveAll[dc, HardDrawJoints]; }; CountCorners: PROC [a: CornerArray] RETURNS [count: INTEGER] = { count _ 0; FOR corner: INTEGER IN [0..4) DO IF a[corner] THEN count _ count+1; ENDLOOP; }; CountEdges: PROC [a: EdgeArray] RETURNS [count: INTEGER] = { count _ 0; FOR edge: INTEGER IN [0..4) DO IF a[edge] THEN count _ count+1; ENDLOOP; }; BoxTransform: PROC [slice: Slice, parts: SliceParts, transform: ImagerTransformation.Transformation] = { <> <> point, oppositePoint: ImagerTransformation.VEC; -- really points, not vectors boxData: BoxData _ NARROW[slice.data]; boxParts: BoxParts _ NARROW[parts]; box: BoundBox _ boxData.box; inverse, totalTransform: ImagerTransformation.Transformation; cornerCount, edgeCount: INTEGER _ 0; IF box.null OR box.infinite THEN ERROR; IF IsComplete[boxParts] THEN { boxData.transform _ ImagerTransformation.Concat[boxData.transform, transform]; boxData.inverse _ ImagerTransformation.Invert[boxData.transform]; BoxBoundBox[slice]; RETURN; }; IF IsEmpty[boxParts] THEN RETURN; -- no parts. Legal. IF (cornerCount _ CountCorners[boxParts.corners])+(edgeCount _ CountEdges[boxParts.edges]) >= 2 THEN { -- more than one corner or more than one edge. Full transform. boxData.transform _ ImagerTransformation.Concat[boxData.transform, transform]; boxData.inverse _ ImagerTransformation.Invert[boxData.transform]; BoxBoundBox[slice]; RETURN; }; IF boxParts.corners#ALL[FALSE] AND boxParts.edges#ALL[FALSE] THEN ERROR; -- can't mix corners and edges while dragging. Doesn't make sense; must be a selection error. IF cornerCount=1 THEN { -- one corner. Transform it. SELECT TRUE FROM boxParts.corners[0] => { point _ [ box.loX, box.loY ]; oppositePoint _ [ box.hiX, box.hiY ]; }; boxParts.corners[1] => { point _ [ box.loX, box.hiY ]; oppositePoint _ [ box.hiX, box.loY ]; }; boxParts.corners[2] => { point _ [ box.hiX, box.hiY ]; oppositePoint _ [ box.loX, box.loY ]; }; boxParts.corners[3] => { point _ [ box.hiX, box.loY ]; oppositePoint _ [ box.loX, box.hiY ]; }; ENDCASE => ERROR; inverse _ ImagerTransformation.Invert[m: boxData.transform]; totalTransform _ ImagerTransformation.Cat[boxData.transform, transform, inverse]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; -- transform the dragging point } ELSE IF edgeCount=1 THEN { -- transform an edge f: ImagerTransformation.FactoredTransformation _ ImagerTransformation.Factor[transform]; globalTranslate: ImagerTransformation.VEC _ f.t; mInverse: ImagerTransformation.Transformation _ ImagerTransformation.Invert[boxData.transform]; localTranslate: ImagerTransformation.VEC _ ImagerTransformation.TransformVec[mInverse, globalTranslate]; totalTransform: ImagerTransformation.Transformation _ ImagerTransformation.Translate[localTranslate]; SELECT TRUE FROM boxParts.edges[0] => { point _ [ box.loX, 0 ]; oppositePoint _ [ box.hiX, box.hiY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ point.x, box.loY]; -- result point is transformed in x only }; boxParts.edges[1] => { point _ [ 0, box.hiY ]; oppositePoint _ [ box.loX, box.loY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ box.hiX, point.y]; -- result point is transformed in y only }; boxParts.edges[2] => { point _ [ box.hiX, 0 ]; oppositePoint _ [ box.loX, box.loY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ point.x, box.hiY]; -- result point is transformed in x only }; boxParts.edges[3] => { point _ [ 0, box.loY ]; oppositePoint _ [ box.hiX, box.hiY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ box.loX, point.y]; -- result point is transformed in y only }; ENDCASE => ERROR; } ELSE ERROR; -- a corner and an edge simultaneously box^ _ [MIN[point.x, oppositePoint.x], MIN[point.y, oppositePoint.y], MAX[point.x, oppositePoint.x], MAX[point.y, oppositePoint.y], FALSE, FALSE]; BoxBoundBox[slice]; }; BoxDrawSelectionFeedback: PROC [slice: Slice, parts: SliceParts, dc: Imager.Context, camera: GGInterfaceTypes.CameraData, quick: BOOL] = { <> DoDrawFeedback: PROC = { boxData: BoxData _ NARROW[slice.data]; boxParts: BoxParts _ NARROW[parts]; box: BoundBox _ boxData.box; t: ImagerTransformation.Transformation _ boxData.transform; tLL: Point _ ImagerTransformation.Transform[t, [box.loX, box.loY] ]; tUL: Point _ ImagerTransformation.Transform[t, [box.loX, box.hiY] ]; tUR: Point _ ImagerTransformation.Transform[t, [box.hiX, box.hiY] ]; tLR: Point _ ImagerTransformation.Transform[t, [box.hiX, box.loY] ]; lX: REAL _ MIN[tLL.x, tUL.x, tUR.x, tLR.x]; lY: REAL _ MIN[tLL.y, tUL.y, tUR.y, tLR.y]; hX: REAL _ MAX[tLL.x, tUL.x, tUR.x, tLR.x]; hY: REAL _ MAX[tLL.y, tUL.y, tUR.y, tLR.y]; tBox: ImagerTransformation.Rectangle _ [lX, lY, hX-lX, hY-lY]; <> <> <> <<};>> <> <> <> <<};>> FOR corner: INTEGER IN [0..4) DO IF boxParts.corners[corner] THEN SELECT corner FROM 0 => GGShapes.DrawSelectedJoint[dc, tLL ]; 1 => GGShapes.DrawSelectedJoint[dc, tUL ]; 2 => GGShapes.DrawSelectedJoint[dc, tUR ]; 3 => GGShapes.DrawSelectedJoint[dc, tLR ]; ENDCASE => ERROR; ENDLOOP; FOR edge: INTEGER IN [0..4) DO IF boxParts.edges[edge] THEN SELECT edge FROM 0 => GGShapes.DrawLine[dc: dc, line: GGLines.LineFromPoints[ tLL, tUL ], clippedBy: tBox, strokeWidth: 2.0*boxData.strokeWidths[0] ]; 1 => GGShapes.DrawLine[dc: dc, line: GGLines.LineFromPoints[ tUL, tUR ], clippedBy: tBox, strokeWidth: 2.0*boxData.strokeWidths[1] ]; 2 => GGShapes.DrawLine[dc: dc, line: GGLines.LineFromPoints[ tUR, tLR ], clippedBy: tBox, strokeWidth: 2.0*boxData.strokeWidths[2] ]; 3 => GGShapes.DrawLine[dc: dc, line: GGLines.LineFromPoints[ tLR, tLL ], clippedBy: tBox, strokeWidth: 2.0*boxData.strokeWidths[3] ]; ENDCASE => ERROR; ENDLOOP; }; IF NOT camera.quality=quality THEN Imager.DoSaveAll[dc, DoDrawFeedback]; }; BoxPointsInDescriptor: PROC [sliceD: SliceDescriptor] RETURNS [pointGen: PointGenerator] = { parts: BoxParts _ NARROW[sliceD.parts]; pointGen _ NEW[PointGeneratorObj _ [sliceD, 0, 0, NIL] ]; -- toGo and index now used FOR corner: INTEGER IN [0..4) DO IF parts.corners[corner] THEN pointGen.toGo _ pointGen.toGo + 1; ENDLOOP; }; BoxPointPairsInDescriptor: PROC [sliceD: SliceDescriptor] RETURNS [pointPairGen: PointPairGenerator] = { parts: BoxParts _ NARROW[sliceD.parts]; pointPairGen _ NEW[PointPairGeneratorObj _ [sliceD, 0, 0, NIL] ]; -- toGo and index now used FOR edge: INTEGER IN [0..4) DO IF parts.edges[edge] THEN pointPairGen.toGo _ pointPairGen.toGo + 1; ENDLOOP; }; BoxNextPoint: PROC [pointGen: PointGenerator] RETURNS [pointAndDone: GGModelTypes.PointAndDone] = { IF pointGen=NIL OR pointGen.toGo = 0 THEN { pointAndDone.done _ TRUE; RETURN; } ELSE { sliceD: SliceDescriptor _ pointGen.sliceD; slice: Slice _ sliceD.slice; boxData: BoxData _ NARROW[slice.data]; boxParts: BoxParts _ NARROW[sliceD.parts]; t: ImagerTransformation.Transformation _ boxData.transform; index: INTEGER _ -1; pointAndDone.done _ FALSE; FOR index _ pointGen.index, index+1 UNTIL index >=4 DO IF boxParts.corners[index] THEN EXIT; -- index will point to next availabe point ENDLOOP; SELECT index FROM -- index in [0..4) of next available point 0 => pointAndDone.point _ ImagerTransformation.Transform[t, [boxData.box.loX, boxData.box.loY]]; 1 => pointAndDone.point _ ImagerTransformation.Transform[t, [boxData.box.loX, boxData.box.hiY]]; 2 => pointAndDone.point _ ImagerTransformation.Transform[t, [boxData.box.hiX, boxData.box.hiY]]; 3 => pointAndDone.point _ ImagerTransformation.Transform[t, [boxData.box.hiX, boxData.box.loY]]; ENDCASE => SIGNAL Problem[msg: "Broken Invariant"]; pointGen.toGo _ pointGen.toGo-1; pointGen.index _ IF pointGen.toGo=0 THEN 99 ELSE index+1; -- bump to the next available point in the generator }; }; BoxNextPointPair: PROC [pointPairGen: PointPairGenerator] RETURNS [pointPairAndDone: GGModelTypes.PointPairAndDone] = { IF pointPairGen=NIL OR pointPairGen.toGo = 0 THEN { pointPairAndDone.done _ TRUE; RETURN; } ELSE { sliceD: SliceDescriptor _ pointPairGen.sliceD; slice: Slice _ sliceD.slice; boxData: BoxData _ NARROW[slice.data]; boxParts: BoxParts _ NARROW[sliceD.parts]; t: ImagerTransformation.Transformation _ boxData.transform; index: INTEGER _ -1; pointPairAndDone.done _ FALSE; FOR index _ pointPairGen.index, index+1 UNTIL index >=4 DO IF boxParts.edges[index] THEN EXIT; -- index will point to next availabe edge ENDLOOP; SELECT index FROM -- index in [0..4) of next available edge 0 => { pointPairAndDone.lo _ ImagerTransformation.Transform[t, [boxData.box.loX, boxData.box.loY]]; pointPairAndDone.hi _ ImagerTransformation.Transform[t, [boxData.box.loX, boxData.box.hiY]]; }; 1 => { pointPairAndDone.lo _ ImagerTransformation.Transform[t, [boxData.box.loX, boxData.box.hiY]]; pointPairAndDone.hi _ ImagerTransformation.Transform[t, [boxData.box.hiX, boxData.box.hiY]]; }; 2 => { pointPairAndDone.lo _ ImagerTransformation.Transform[t, [boxData.box.hiX, boxData.box.hiY]]; pointPairAndDone.hi _ ImagerTransformation.Transform[t, [boxData.box.hiX, boxData.box.loY]]; }; 3 => { pointPairAndDone.lo _ ImagerTransformation.Transform[t, [boxData.box.hiX, boxData.box.loY]]; pointPairAndDone.hi _ ImagerTransformation.Transform[t, [boxData.box.loX, boxData.box.loY]]; }; ENDCASE => SIGNAL Problem[msg: "Broken Invariant"]; pointPairGen.toGo _ pointPairGen.toGo - 1; pointPairGen.index _ IF pointPairGen.toGo=0 THEN 99 ELSE index+1; -- bump to the next available pair in the generator }; }; BoxEmptyParts: PROC [slice: Slice, parts: SliceParts] RETURNS [BOOL] = { <> boxParts: BoxParts _ NARROW[parts]; -- NARROW for type checking RETURN[IsEmpty[boxParts]]; }; BoxNewParts: PROC [slice: Slice, mode: SelectMode] RETURNS [parts: SliceParts] = { <> boxHitData: BoxHitData _ NARROW[slice.hitData]; boxParts: BoxParts _ NEW[BoxPartsObj _ [corners: ALL[FALSE], edges: ALL[FALSE] ] ]; IF boxHitData.corner=-1 AND boxHitData.edge=-1 THEN ERROR; SELECT mode FROM joint => boxParts.corners[boxHitData.corner] _ TRUE; segment => boxParts.edges[boxHitData.edge] _ TRUE; traj, slice, topLevel => MakeComplete[boxParts]; ENDCASE => ERROR; parts _ boxParts; -- RETURN[boxParts] }; BoxUnionParts: PROC [slice: Slice, partsA: SliceParts, partsB: SliceParts] RETURNS [aPlusB: SliceParts] = { <> boxPartsA: BoxParts _ NARROW[partsA]; boxPartsB: BoxParts _ NARROW[partsB]; newParts: BoxParts _ NEW[BoxPartsObj _ [corners: ALL[FALSE], edges: ALL[FALSE] ] ]; FOR i: INTEGER IN [0..4) DO newParts.corners[i] _ boxPartsA.corners[i] OR boxPartsB.corners[i]; newParts.edges[i] _ boxPartsA.edges[i] OR boxPartsB.edges[i]; ENDLOOP; aPlusB _ newParts; -- RETURN[newParts] }; BoxDiffParts: PROC [slice: Slice, partsA: SliceParts, partsB: SliceParts] RETURNS [aMinusB: SliceParts] = { boxPartsA: BoxParts _ NARROW[partsA]; boxPartsB: BoxParts _ NARROW[partsB]; newParts: BoxParts _ NEW[BoxPartsObj _ [corners: ALL[FALSE], edges: ALL[FALSE] ] ]; FOR i: INTEGER IN [0..4) DO newParts.corners[i] _ IF boxPartsB.corners[i] THEN FALSE ELSE boxPartsA.corners[i]; newParts.edges[i] _ IF boxPartsB.edges[i] THEN FALSE ELSE boxPartsA.edges[i]; ENDLOOP; aMinusB _ newParts; -- RETURN[newParts] }; BoxClosestPoint: PROC [slice: Slice, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = { <> index: NAT _ 99; scale: REAL; boxData: BoxData _ NARROW[slice.data]; boxHitData: BoxHitData _ NARROW[slice.hitData]; inverse: ImagerTransformation.Transformation _ boxData.inverse; testPoint _ GGTransform.Transform[inverse, testPoint]; [bestDist, index, bestPoint, success] _ GGBoundBox.NearestPoint[boxData.box, testPoint]; bestPoint _ GGTransform.Transform[boxData.transform, bestPoint]; scale _ ImagerTransformation.Factor[boxData.transform].s.x; bestDist _ bestDist*scale; boxHitData.corner _ IF success THEN index ELSE -1; }; BoxClosestSegment: PROC [slice: Slice, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = { <> seg: NAT _ 99; scale: REAL; boxData: BoxData _ NARROW[slice.data]; boxHitData: BoxHitData _ NARROW[slice.hitData]; inverse: ImagerTransformation.Transformation _ boxData.inverse; testPoint _ GGTransform.Transform[inverse, testPoint]; [bestDist, seg, bestPoint, success] _ GGBoundBox.NearestSegment[boxData.box, testPoint, tolerance]; bestPoint _ GGTransform.Transform[boxData.transform, bestPoint]; scale _ ImagerTransformation.Factor[boxData.transform].s.x; bestDist _ bestDist*scale; boxHitData.edge _ IF success THEN seg ELSE -1; }; BoxFileout: PROC [slice: Slice, f: IO.STREAM] = { <> <> boxData: BoxData _ NARROW[slice.data]; GGParseOut.WritePoint[f, [ boxData.box.loX, boxData.box.loY] ]; f.PutChar[IO.SP]; GGParseOut.WritePoint[f, [ boxData.box.hiX, boxData.box.hiY] ]; f.PutChar[IO.SP]; GGParseOut.WriteTransformation[f, boxData.transform ]; }; BoxFilein: PROC [f: IO.STREAM, version: REAL] RETURNS [slice: Slice] = { <> box: BoundBox; p1, p2: Point; transform: ImagerTransformation.Transformation; GGParseIn.ReadBlank[f]; p1 _ GGParseIn.ReadPoint[f]; GGParseIn.ReadBlank[f]; p2 _ GGParseIn.ReadPoint[f]; GGParseIn.ReadBlank[f]; transform _ GGParseIn.ReadTransformation[f]; box _ GGBoundBox.CreateBoundBox[ p1.x, p1.y, p2.x, p2.y ]; slice _ MakeBoxSlice[box, none, transform].slice; }; BoxSetStrokeColor: PROC [slice: Slice, parts: SliceParts, color: Imager.Color] = { boxParts: BoxParts _ NARROW[parts]; boxData: BoxData _ NARROW[slice.data]; FOR edge: INTEGER IN [0..4) DO IF boxParts.edges[edge] THEN boxData.strokeColors[edge] _ color; ENDLOOP; }; BoxSetFillColor: PROC [slice: Slice, color: Imager.Color] = { boxData: BoxData _ NARROW[slice.data]; boxData.fillColor _ color; }; <> BoxSetStrokeWidth: PROC [slice: Slice, parts: SliceParts, strokeWidth: REAL] = { boxParts: BoxParts _ NARROW[parts]; boxData: BoxData _ NARROW[slice.data]; FOR edge: INTEGER IN [0..4) DO IF boxParts.edges[edge] THEN boxData.strokeWidths[edge] _ strokeWidth; ENDLOOP; }; <<>> <> CircleData: TYPE = REF CircleDataObj; CircleDataObj: TYPE = RECORD [ <> transform: ImagerTransformation.Transformation, inverse: ImagerTransformation.Transformation, startPoint: Point, -- original point on circumference when circle was created strokeWidth: REAL, strokeColor: Imager.Color, fillColor: Imager.Color ]; MaxCirclePoints: INTEGER = 5; CirclePoints: TYPE = [0..MaxCirclePoints); -- origin, left, top, right, bottom CircleParts: TYPE = REF CirclePartsObj; CirclePartsObj: TYPE = RECORD [ cpArray: ARRAY CirclePoints OF BOOL -- used when origin or CPs are selected <> ]; CircleHitData: TYPE = REF CircleHitDataObj; CircleHitDataObj: TYPE = RECORD [ index: [-1..MaxCirclePoints) -- records index of which origin or CP is hit. -1 => no hit ]; BuildCircleSliceClass: PUBLIC PROC [] RETURNS [class: SliceClass] = { class _ NEW[SliceClassObj _ [ type: $Circle, boundBox: CircleBoundBox, copy: CircleCopy, draw: CircleDraw, drawTransform: CircleDrawTransform, drawSelectionFeedback: CircleDrawSelectionFeedback, transform: CircleTransform, emptyParts: CircleEmptyParts, newParts: CircleNewParts, unionParts: CircleUnionParts, differenceParts: CircleDiffParts, pointsInDescriptor: CirclePointsInDescriptor, pointPairsInDescriptor: NoOpPointPairsInDescriptor, nextPoint: CircleNextPoint, nextPointPair: NoOpNextPointPair, closestPoint: CircleClosestPoint, closestPointAndTangent: NIL, closestSegment: CircleClosestSegment, fileout: CircleFileout, filein: CircleFilein, setStrokeWidth: CircleSetStrokeWidth, <> setStrokeColor: CircleSetStrokeColor, <> setFillColor: CircleSetFillColor <> ]]; }; MakeCircleSlice: PUBLIC PROC [origin: Point, outerPoint: Point, strokeWidth: REAL _ 1.0, strokeColor: Imager.Color _ Imager.black, fillColor: Imager.Color _ NIL] RETURNS [sliceD: SliceDescriptor] = { circleData: CircleData _ NEW[CircleDataObj _ [----, ----, outerPoint, strokeWidth, strokeColor, fillColor]]; circleParts: CircleParts _ NEW[CirclePartsObj _ [ALL[FALSE] ] ]; slice: Slice _ NEW[SliceObj _ [ class: FetchSliceClass[$Circle], data: circleData, children: NIL, parent: NIL, selectedInFull: [FALSE, FALSE, FALSE, FALSE], boundBox: GGBoundBox.CreateBoundBox[0,0,0,0], hitData: NEW[CircleHitDataObj _ [index: -1] ] ]]; IF origin=outerPoint THEN circleData.startPoint _ [origin.x+20.0, origin.y+20.0]; -- prevent degenerate circle circleData.transform _ ImagerTransformation.PreScale[ImagerTransformation.Translate[origin], GGVector.Distance[origin, circleData.startPoint]]; circleData.inverse _ ImagerTransformation.Invert[circleData.transform]; CircleBoundBox[slice]; sliceD _ NEW[SliceDescriptorObj _ [slice, circleParts] ]; }; <> <> <> <> <> <<};>> <> CircleSetStrokeWidth: PROC [slice: Slice, parts: SliceParts, strokeWidth: REAL] = { circleData: CircleData _ NARROW[slice.data]; circleData.strokeWidth _ strokeWidth; }; CircleSetStrokeColor: PROC [slice: Slice, parts: SliceParts, color: Imager.Color] = { circleData: CircleData _ NARROW[slice.data]; circleData.strokeColor _ color; }; CircleSetFillColor: PROC [slice: Slice, color: Imager.Color] = { circleData: CircleData _ NARROW[slice.data]; circleData.fillColor _ color; }; CirclePointsInDescriptor: PROC [sliceD: SliceDescriptor] RETURNS [pointGen: PointGenerator] = { parts: CircleParts _ NARROW[sliceD.parts]; pointGen _ NEW[PointGeneratorObj _ [sliceD, 0, 0, NIL] ]; FOR index: CirclePoints IN CirclePoints DO IF parts.cpArray[index] THEN pointGen.toGo _ pointGen.toGo + 1; ENDLOOP; }; CircleNextPoint: PROC [pointGen: PointGenerator] RETURNS [pointAndDone: GGModelTypes.PointAndDone] = { sliceD: SliceDescriptor _ pointGen.sliceD; slice: Slice _ sliceD.slice; circleData: CircleData _ NARROW[slice.data]; t: ImagerTransformation.Transformation _ circleData.transform; IF pointGen=NIL OR pointGen.toGo = 0 THEN { pointAndDone.done _ TRUE; RETURN; } ELSE { parts: CircleParts _ NARROW[sliceD.parts]; index: NAT; pointAndDone.done _ FALSE; FOR index _ pointGen.index, index+1 UNTIL index>=LAST[CirclePoints] DO IF parts.cpArray[index] THEN EXIT; -- find the next included point ENDLOOP; SELECT index FROM 0 => pointAndDone.point _ ImagerTransformation.Transform[t, [0.0, 0.0]]; -- origin 1 => pointAndDone.point _ ImagerTransformation.Transform[t, [-1.0, 0.0]]; -- left 2 => pointAndDone.point _ ImagerTransformation.Transform[t, [0.0, 1.0]]; -- top 3 => pointAndDone.point _ ImagerTransformation.Transform[t, [1.0, 0.0]]; -- right 4 => pointAndDone.point _ ImagerTransformation.Transform[t, [0.0, -1.0]]; -- bottom ENDCASE => SIGNAL Problem[msg: "Broken Invariant"]; pointGen.index _ index+1; pointGen.toGo _ pointGen.toGo - 1; }; }; CircleDraw: PROC [slice: Slice, dc: Imager.Context, camera: GGInterfaceTypes.CameraData] = { <> DoDrawCircleOutline: PROC = { Imager.ConcatT[dc, circleData.transform]; CircleDrawOutline[dc, circleData]; }; DoDrawCircleJoints: PROC = { CircleDrawJointsAux[dc, circleData.transform]; }; circleData: CircleData _ NARROW[slice.data]; Imager.DoSaveAll[dc, DoDrawCircleOutline]; IF camera.quality # quality THEN Imager.DoSaveAll[dc, DoDrawCircleJoints]; }; CircleDrawOutline: PRIVATE PROC [dc: Imager.Context, circleData: CircleData, width: REAL _ 1.0] = { <> <> FillItIn: PROC = { Imager.SetColor[dc, circleData.fillColor]; Imager.MaskFill[dc, CirclePath, TRUE] }; CirclePath: Imager.PathProc = { -- code copied from GGShapesImpl.DrawCircle moveTo[[leftSide.x, leftSide.y]]; arcTo[[rightSide.x, rightSide.y], [ leftSide.x, leftSide.y]]; }; leftSide: Point _ [-1.0, 0.0]; rightSide: Point _ [1.0, 0.0]; scale: Imager.VEC _ ImagerTransformation.Factor[circleData.inverse].s; strokeScale: REAL _ MAX[scale.x, scale.y]; Imager.SetColor[dc, circleData.strokeColor]; Imager.SetStrokeWidth[dc, width*circleData.strokeWidth*strokeScale]; Imager.SetStrokeEnd[dc, round]; Imager.MaskStroke[dc, CirclePath, TRUE]; IF circleData.fillColor#NIL THEN Imager.DoSaveAll[dc, FillItIn]; }; CircleDrawJointsAux: PRIVATE PROC [dc: Imager.Context, t: ImagerTransformation.Transformation] = { <> Imager.SetStrokeWidth[dc, 1.0]; GGShapes.DrawJoint[dc, ImagerTransformation.Transform[t, [0.0, 0.0]] ]; -- center GGShapes.DrawJoint[dc, ImagerTransformation.Transform[t, [-1.0, 0.0]] ]; -- left GGShapes.DrawJoint[dc, ImagerTransformation.Transform[t, [0.0, 1.0]] ]; -- top GGShapes.DrawJoint[dc, ImagerTransformation.Transform[t, [1.0, 0.0]] ]; -- right GGShapes.DrawJoint[dc, ImagerTransformation.Transform[t, [0.0, -1.0]] ]; -- bottom }; CircleDrawTransform: PROC [slice: Slice, parts: SliceParts, dc: Imager.Context, camera: GGInterfaceTypes.CameraData, transform: ImagerTransformation.Transformation] = { <> <> isOrigin: BOOL _ FALSE; cpCount: INT _ 0; cpIndex: INT _ -1; originPoint, edgePoint, pointInUnitSpace: Point; newTransform: ImagerTransformation.Transformation; circleData: CircleData _ NARROW[slice.data]; circleParts: CircleParts _ NARROW[parts]; EasyDrawOutline: PROC = { Imager.ConcatT[dc, ImagerTransformation.Concat[circleData.transform, transform]]; CircleDrawOutline[dc, circleData]; }; EasyDrawJoints: PROC = { Imager.ConcatT[dc, transform]; CircleDrawJointsAux[dc, circleData.transform]; }; HardDrawOutline: PROC = { <> Imager.ConcatT[dc, newTransform]; CircleDrawOutline[dc, circleData]; }; HardDrawJoints: PROC = { <> CircleDrawJointsAux[dc, newTransform]; }; IF circleParts.cpArray[0] THEN isOrigin _ TRUE; FOR index: CirclePoints IN [1..MaxCirclePoints) DO IF circleParts.cpArray[index] THEN{ cpCount _ cpCount + 1; cpIndex _ index; }; ENDLOOP; IF isOrigin OR cpCount > 1 THEN { -- treat as complete circle selected Imager.DoSaveAll[dc, EasyDrawOutline]; IF camera.quality # quality THEN Imager.DoSaveAll[dc, EasyDrawJoints]; RETURN; }; <> originPoint _ ImagerTransformation.Transform[circleData.transform, [0.0, 0.0]]; IF cpCount = 0 AND NOT isOrigin THEN { <> edgePoint _ GGVector.Add[v1: GGVector.Sub[v1: circleData.startPoint, v2: originPoint], v2: ImagerTransformation.Factor[transform].t]; -- edgepoint relative to origin edgePoint _ GGVector.Add[originPoint, edgePoint]; -- make edgepoint absolute } ELSE { -- this is the normal case when a single CP is selected edgePoint _ ImagerTransformation.Transform[circleData.transform, SELECT cpIndex FROM 1 => [-1.0, 0.0], 2 => [0.0, 1.0], 3 => [1.0, 0.0], 4 => [0.0, -1.0], ENDCASE => ERROR]; edgePoint _ ImagerTransformation.Transform[transform, edgePoint]; -- move the edgepoint }; pointInUnitSpace _ ImagerTransformation.Transform[circleData.inverse, edgePoint]; newTransform _ ImagerTransformation.PreScale[circleData.transform, GGVector.Magnitude[pointInUnitSpace]]; Imager.DoSaveAll[dc, HardDrawOutline]; IF camera.quality # quality THEN Imager.DoSaveAll[dc, HardDrawJoints]; }; CircleTransform: PROC [slice: Slice, parts: SliceParts, transform: ImagerTransformation.Transformation] = { <> <> isOrigin: BOOL _ FALSE; cpCount: INT _ 0; cpIndex: INT _ -1; originPoint, edgePoint, pointInUnitSpace: Point; circleData: CircleData _ NARROW[slice.data]; circleParts: CircleParts _ NARROW[parts]; IF circleParts.cpArray[0] THEN isOrigin _ TRUE; FOR index: CirclePoints IN [1..MaxCirclePoints) DO IF circleParts.cpArray[index] THEN{ cpCount _ cpCount + 1; cpIndex _ index; }; ENDLOOP; IF isOrigin OR cpCount > 1 THEN { -- treat as though complete circle is selected circleData.transform _ ImagerTransformation.Concat[circleData.transform, transform]; circleData.inverse _ ImagerTransformation.Invert[circleData.transform]; CircleBoundBox[slice]; RETURN; }; <> originPoint _ ImagerTransformation.Transform[circleData.transform, [0.0, 0.0]]; IF cpCount = 0 AND NOT isOrigin THEN { <> edgePoint _ GGVector.Add[v1: GGVector.Sub[v1: circleData.startPoint, v2: originPoint], v2: ImagerTransformation.Factor[transform].t]; -- edgepoint relative to origin edgePoint _ GGVector.Add[originPoint, edgePoint]; -- make edgepoint absolute } ELSE { -- this is the normal case when a single CP is selected edgePoint _ ImagerTransformation.Transform[circleData.transform, SELECT cpIndex FROM 1 => [-1.0, 0.0], 2 => [0.0, 1.0], 3 => [1.0, 0.0], 4 => [0.0, -1.0], ENDCASE => ERROR]; edgePoint _ ImagerTransformation.Transform[transform, edgePoint]; -- move the edgepoint }; pointInUnitSpace _ ImagerTransformation.Transform[circleData.inverse, edgePoint]; circleData.transform _ ImagerTransformation.PreScale[circleData.transform, GGVector.Magnitude[pointInUnitSpace]]; circleData.inverse _ ImagerTransformation.Invert[circleData.transform]; CircleBoundBox[slice]; }; CircleBoundBox: PROC [slice: Slice] = { <> halfJoint: REAL = GGModelTypes.halfJointSize + 1; circleData: CircleData _ NARROW[slice.data]; <> origin: Point _ ImagerTransformation.Transform[circleData.transform, [0.0, 0.0]]; minor: Point _ ImagerTransformation.Transform[circleData.transform, [0.0, 1.0]]; major: Point _ ImagerTransformation.Transform[circleData.transform, [1.0, 0.0]]; radius: REAL _ RealFns.SqRt[MAX[GGVector.DistanceSquared[minor, origin], GGVector.DistanceSquared[major, origin]]]; GGBoundBox.UpdateBoundBox[slice.boundBox, origin.x-radius-halfJoint, origin.y-radius-halfJoint, origin.x+radius+halfJoint, origin.y+radius+halfJoint]; -- new values }; CircleCopy: PROC [slice: Slice] RETURNS [copy: Slice] = { <> circleData: CircleData _ NARROW[slice.data]; newData: CircleData; copy _ MakeCircleSlice [[0.0, 0.0], [1.0, 1.0], circleData.strokeWidth, circleData.strokeColor, circleData.fillColor].slice; <> newData _ NARROW[copy.data]; newData.transform _ ImagerTransformation.Copy[circleData.transform]; newData.inverse _ ImagerTransformation.Copy[circleData.inverse]; }; <> <> <> <> <> <> <> <> <> <> <<};>> <> <> <> <<0 => ImagerTransformation.Transform[t, [0.0, 0.0] ],>> <<1 => ImagerTransformation.Transform[t, [-1.0, 0.0] ],>> <<2 => ImagerTransformation.Transform[t, [0.0, 1.0] ],>> <<3 => ImagerTransformation.Transform[t, [1.0, 0.0] ],>> <<4 => ImagerTransformation.Transform[t, [0.0, -1.0] ],>> < ERROR];>> <> <<};>> <> <<};>> <<>> CircleDrawSelectionFeedback: PROC [slice: Slice, parts: SliceParts, dc: Imager.Context, camera: GGInterfaceTypes.CameraData, quick: BOOL] = { <> circleData: CircleData _ NARROW[slice.data]; circleParts: CircleParts _ NARROW[parts]; t: ImagerTransformation.Transformation _ circleData.transform; DoDrawFeedback: PROC = { DrawOutlineFeedback: PROC = { Imager.ConcatT[dc, circleData.transform]; CircleDrawOutline[dc, circleData, 2.0]; }; IF IsComplete[circleParts] THEN Imager.DoSaveAll[dc, DrawOutlineFeedback]; -- heavy circle outline FOR index: CirclePoints IN [0..MaxCirclePoints) DO IF circleParts.cpArray[index] THEN GGShapes.DrawSelectedJoint[dc, SELECT index FROM 0 => ImagerTransformation.Transform[t, [0.0, 0.0] ], 1 => ImagerTransformation.Transform[t, [-1.0, 0.0] ], 2 => ImagerTransformation.Transform[t, [0.0, 1.0] ], 3 => ImagerTransformation.Transform[t, [1.0, 0.0] ], 4 => ImagerTransformation.Transform[t, [0.0, -1.0] ], ENDCASE => ERROR]; ENDLOOP; }; IF camera.quality # quality THEN Imager.DoSaveAll[dc, DoDrawFeedback]; }; CircleEmptyParts: PROC [slice: Slice, parts: SliceParts] RETURNS [BOOL] = { <> circleParts: CircleParts _ NARROW[parts]; RETURN[IsEmpty[circleParts] ]; }; CircleNewParts: PROC [slice: Slice, mode: SelectMode] RETURNS [parts: SliceParts] = { <> circleHitData: CircleHitData _ NARROW[slice.hitData]; circleParts: CircleParts _ NEW[CirclePartsObj _ [cpArray: ALL[FALSE]] ]; SELECT mode FROM joint => IF circleHitData.index#-1 THEN circleParts.cpArray[circleHitData.index] _ TRUE; segment, traj, topLevel, slice => MakeComplete[circleParts]; ENDCASE => ERROR; parts _ circleParts; -- RETURN[boxParts] }; CircleUnionParts: PROC [slice: Slice, partsA: SliceParts, partsB: SliceParts] RETURNS [aPlusB: SliceParts] = { <> circlePartsA: CircleParts _ NARROW[partsA]; circlePartsB: CircleParts _ NARROW[partsB]; newParts: CircleParts _ NEW[CirclePartsObj _ [ALL[FALSE] ] ]; FOR i: CirclePoints IN CirclePoints DO newParts.cpArray[i] _ circlePartsA.cpArray[i] OR circlePartsB.cpArray[i]; ENDLOOP; aPlusB _ newParts; -- RETURN[newParts] }; CircleDiffParts: PROC [slice: Slice, partsA: SliceParts, partsB: SliceParts] RETURNS [aMinusB: SliceParts] = { <> circlePartsA: CircleParts _ NARROW[partsA]; circlePartsB: CircleParts _ NARROW[partsB]; newParts: CircleParts _ NEW[CirclePartsObj _ [ALL[FALSE] ] ]; FOR i: CirclePoints IN CirclePoints DO newParts.cpArray[i] _ IF circlePartsB.cpArray[i] THEN FALSE ELSE circlePartsA.cpArray[i]; ENDLOOP; aMinusB _ newParts; -- RETURN[newParts] }; useBBox: BOOL _ TRUE; PointIsInBox: PRIVATE PROC [test: Point, box: GGBasicTypes.BoundBoxObj] RETURNS [BOOL] = { RETURN[ NOT (test.x < box.loX OR test.x > box.hiX OR test.y < box.loY OR test.y > box.hiY) ]; }; CirclePointsFromData: PROC [circleData: CircleData] RETURNS [origin, left, top, right, bottom: Point] = { t: ImagerTransformation.Transformation _ circleData.transform; origin _ ImagerTransformation.Transform[t, [0.0, 0.0] ]; left _ ImagerTransformation.Transform[t, [-1.0, 0.0] ]; top _ ImagerTransformation.Transform[t, [0.0, 1.0] ]; right _ ImagerTransformation.Transform[t, [1.0, 0.0] ]; bottom _ ImagerTransformation.Transform[t, [0.0, -1.0] ]; }; CircleHitDataFromPoint: PRIVATE PROC [point: Point, circleData: CircleData, circleHitData: CircleHitData] = { <> nextDist, bestDist: REAL; nextPoint, origin, left, top, right, bottom: Point; foundIndex: INTEGER _ 0; [origin, left, top, right, bottom] _ CirclePointsFromData[circleData]; bestDist _ GGVector.Distance[origin, point]; FOR index: INTEGER IN [1..MaxCirclePoints) DO nextPoint _ SELECT index FROM 1 => left, 2 => top, 3 => right, 4 => bottom, ENDCASE => origin; IF (nextDist _ GGVector.Distance[point, nextPoint]) < bestDist THEN { bestDist _ nextDist; foundIndex _ index; }; ENDLOOP; circleHitData.index _ foundIndex; -- and mark the chosen CP }; CircleClosestSegment: PROC [slice: Slice, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL _ TRUE] = { circleData: CircleData _ NARROW[slice.data]; circleHitData: CircleHitData _ NARROW[slice.hitData]; pointOnCircle: Point _ [0.0, 0.0]; pointInUnitSpace: Point _ ImagerTransformation.Transform[circleData.inverse, testPoint]; dist: REAL _ GGVector.Magnitude[pointInUnitSpace]; <> IF dist > 0.0 THEN pointOnCircle _ [pointInUnitSpace.x/dist, pointInUnitSpace.y/dist]; <> bestPoint _ ImagerTransformation.Transform[circleData.transform, pointOnCircle]; bestDist _ GGVector.Distance[testPoint, bestPoint]; CircleHitDataFromPoint[bestPoint, circleData, circleHitData]; -- updates circleHitData in place RETURN; }; CircleClosestPoint: PROC [slice: Slice, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL _ TRUE] = { <> circleData: CircleData _ NARROW[slice.data]; circleHitData: CircleHitData _ NARROW[slice.hitData]; nextDist: REAL; nextPoint, origin, left, top, right, bottom: Point; foundIndex: INTEGER _ 0; [origin, left, top, right, bottom] _ CirclePointsFromData[circleData]; bestDist _ GGVector.DistanceSquared[origin, testPoint]; bestPoint _ origin; FOR index: INTEGER IN [1..MaxCirclePoints) DO nextPoint _ SELECT index FROM 1 => left, 2 => top, 3 => right, 4 => bottom, ENDCASE => origin; IF (nextDist _ GGVector.DistanceSquared[testPoint, nextPoint]) < bestDist THEN { bestDist _ nextDist; foundIndex _ index; bestPoint _ nextPoint; }; ENDLOOP; circleHitData.index _ foundIndex; -- and mark the chosen CP bestDist _ RealFns.SqRt[bestDist]; }; CircleFileout: PROC [slice: Slice, f: IO.STREAM] = { <> <> circleData: CircleData _ NARROW[slice.data]; GGParseOut.WriteTransformation[f, circleData.transform]; <> }; <<>> CircleFilein: PROC [f: IO.STREAM, version: REAL] RETURNS [slice: Slice] = { <> origin, outerPoint: Point; transform: ImagerTransformation.Transformation; GGParseIn.ReadBlank[f]; transform _ GGParseIn.ReadTransformation[f]; origin _ ImagerTransformation.Transform[m: transform, v: [0.0, 0.0] ]; outerPoint _ ImagerTransformation.Transform[m: transform, v: [0.0, 1.0] ]; <> slice _ MakeCircleSlice[origin, outerPoint].slice; }; <> BuildIPSliceClass: PROC [] RETURNS [class: SliceClass] = { class _ NEW[SliceClassObj _ [ type: $IP, boundBox: IPBoundBox, <> draw: IPDraw, drawTransform: IPDrawTransform, <> <> drawSelectionFeedback: IPDrawSelectionFeedback, transform: IPTransform, newParts: IPNewPartsProc, unionParts: IPUnionPartsProc, differenceParts: IPDiffPartsProc, pointsInDescriptor: NoOpPointsInDescriptor, pointPairsInDescriptor: NoOpPointPairsInDescriptor, nextPoint: NoOpNextPoint, nextPointPair: NoOpNextPointPair, closestPoint: IPClosestPoint, closestPointAndTangent: NIL, closestSegment: IPClosestSegment, fileout: IPFileout, filein: IPFilein, setStrokeWidth: IPSetStrokeWidth, setStrokeColor: IPSetStrokeColor, setFillColor: IPSetFillColor ]]; }; IPSetStrokeColor: PROC [slice: Slice, parts: SliceParts, color: Imager.Color] = {}; IPSetFillColor: PROC [slice: Slice, color: Imager.Color] = {}; IPSetStrokeWidth: PROC [slice: Slice, parts: SliceParts, strokeWidth: REAL] = { }; MakeIPSlice: PUBLIC PROC [fileName: Rope.ROPE, worldPt: Point] RETURNS [slice: Slice] = { ipData: IPData; feedbackBox: BoundBox; feedbackBox _ GGBoundBox.CreateBoundBox[0,0,0,0]; -- gets real values later in this proc. ipData _ NEW[IPDataObj _ [fileName, worldPt, feedbackBox]]; slice _ NEW[SliceObj _ [ class: FetchSliceClass[$IP], data: ipData, children: NIL, parent: NIL, selectedInFull: [FALSE, FALSE, FALSE, FALSE], boundBox: GGBoundBox.CreateBoundBox[0,0,0,0], -- gets set next line hitData: NEW[IPHitDataObj _ [] ] ]]; slice.class.boundBox[slice]; }; IPBoundBox: PROC [slice: Slice] = { <> ipData: IPData _ NARROW[slice.data]; rect: ImagerTransformation.Rectangle; halfCP: REAL = GGModelTypes.halfJointSize + 1; rect.x _ 0.0; rect.y _ 0.0; rect.w _ 300.0; rect.h _ 300.0; GGBoundBox.UpdateBoundBox[slice.boundBox, ipData.worldPt.x + rect.x - halfCP, ipData.worldPt.y + rect.y - halfCP, ipData.worldPt.x + rect.w + halfCP, ipData.worldPt.y + rect.h + halfCP]; GGBoundBox.UpdateBoundBox[ipData.feedbackBox, ipData.worldPt.x + rect.x, ipData.worldPt.y + rect.y, ipData.worldPt.x + rect.w, ipData.worldPt.y + rect.h]; }; IPDraw: PROC [slice: Slice, dc: Imager.Context, camera: CameraData] = { <> ipData: IPData _ NARROW[slice.data]; <> }; IPDrawTransform: PROC [slice: Slice, parts: SliceParts, dc: Imager.Context, camera: CameraData, transform: ImagerTransformation.Transformation] = { <> ipData: IPData _ NARROW[slice.data]; tempPoint: Point _ GGTransform.Transform[transform, ipData.worldPt]; }; IPDrawSelectionFeedback: PROC [slice: Slice, parts: SliceParts, dc: Imager.Context, camera: CameraData, quick: BOOL _ FALSE] = { <> ipData: IPData _ NARROW[slice.data]; GGBoundBox.DrawBoundBox[dc, ipData.feedbackBox]; }; IPTransform: PROC [slice: Slice, parts: SliceParts, transform: ImagerTransformation.Transformation] = { <> <> ipData: IPData _ NARROW[slice.data]; ipData.worldPt _ GGTransform.Transform[transform, ipData.worldPt]; IPBoundBox[slice]; }; IPNewPartsProc: GGModelTypes.SliceNewPartsProc = { <> }; IPUnionPartsProc: GGModelTypes.SliceUnionPartsProc = { <> }; IPDiffPartsProc: GGModelTypes.SliceDifferencePartsProc = { <> }; IPClosestPoint: PROC [slice: Slice, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = { <> <> ipData: IPData _ NARROW[slice.data]; <<[bestDist, ----, bestPoint] _ GGBoundBox.NearestPoint[ipData.feedbackBox, testPoint];>> <> [bestDist, ----, bestPoint, success] _ GGBoundBox.NearestPoint[ipData.feedbackBox, testPoint]; }; IPClosestSegment: PROC [slice: Slice, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = { <> <> ipData: IPData _ NARROW[slice.data]; ipHitData: IPHitData _ NARROW[slice.hitData]; [bestDist, ipHitData.bestSeg, bestPoint, success] _ GGBoundBox.NearestSegment[ipData.feedbackBox, testPoint, tolerance]; }; IPFileout: PROC [slice: Slice, f: IO.STREAM] = { <> <> ipData: IPData _ NARROW[slice.data]; f.PutF["\"%g\"", [rope[ipData.file]]]; GGParseOut.WritePoint[f, ipData.worldPt]; }; IPFilein: PROC [f: IO.STREAM, version: REAL] RETURNS [slice: Slice] = { <> <> fileName: Rope.ROPE; point: Point; fileName _ f.GetRopeLiteral[]; GGParseIn.ReadBlank[f]; point _ GGParseIn.ReadPoint[f]; slice _ MakeIPSlice[fileName, point]; }; sliceClasses: LIST OF SliceClassDef; printPrefix: ATOM; screenPrefix: ATOM; MaxCircles: NAT = 3; globalCirclePool: ARRAY [0..MaxCircles-1] OF Circle; globalCirclePoolIndex: NAT; GetCircleFromPool: PROC [] RETURNS [circle: Circle] = { <> IF globalCirclePoolIndex = MaxCircles THEN ERROR; circle _ globalCirclePool[globalCirclePoolIndex]; globalCirclePoolIndex _ globalCirclePoolIndex + 1; }; ReturnCircleToPool: PROC [circle: Circle] = { IF globalCirclePoolIndex = 0 THEN ERROR; globalCirclePoolIndex _ globalCirclePoolIndex - 1; globalCirclePool[globalCirclePoolIndex] _ circle; }; Init: PRIVATE PROC [] = { textDef: SliceClassDef _ NEW[SliceClassDefObj _ [type: $Text, class: BuildTextSliceClass[]]]; ipDef: SliceClassDef _ NEW[SliceClassDefObj _ [type: $IP, class: BuildIPSliceClass[]]]; boxDef: SliceClassDef _ NEW[SliceClassDefObj _ [type: $Box, class: BuildBoxSliceClass[]]]; circleDef: SliceClassDef _ NEW[SliceClassDefObj _ [type: $Circle, class: BuildCircleSliceClass[]]]; sliceClasses _ LIST[circleDef, boxDef, ipDef, textDef]; printPrefix _ Atom.MakeAtom["xerox/pressfonts/"]; screenPrefix _Atom.MakeAtom ["xerox/tiogafonts/"]; FOR i: NAT IN [0..MaxCircles) DO globalCirclePool[i] _ GGCircles.CreateEmptyCircle[]; ENDLOOP; globalCirclePoolIndex _ 0; }; Init[]; END.