GGClusterImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last edited by Pier on May 22, 1986 7:57:20 pm PDT
Last edited by Bier on January 3, 1986 3:56:21 pm PST
Contents: Implements various cluster classes in Gargoyle.
DIRECTORY
Atom, Convert, FileNames, GGBasicTypes, GGBoundBox, GGBoxCluster, GGCluster, GGError, GGInterfaceTypes, GGModelTypes, GGParseOut, GGParseIn, GGTransform, Imager, ImagerFont, ImagerTransformation, IO, NodeStyle, NodeStyleFont, Real, Rope;
GGClusterImpl: CEDAR PROGRAM
IMPORTS Atom, Convert, FileNames, GGBoundBox, GGBoxCluster, GGError, GGParseIn, GGParseOut, GGTransform, Imager, ImagerFont, IO, NodeStyleFont, Real, Rope EXPORTS GGCluster = BEGIN
Point: TYPE = GGBasicTypes.Point;
ClusterObj: TYPE = GGModelTypes.ClusterObj;
ClusterClass: TYPE = GGModelTypes.ClusterClass;
ClusterClassObj: TYPE = GGModelTypes.ClusterClassObj;
ClusterParts: TYPE = GGModelTypes.ClusterParts;
Cluster: TYPE = GGModelTypes.Cluster;
SelectMode: TYPE = GGModelTypes.SelectMode;
ExtendMode: TYPE = GGModelTypes.ExtendMode;
BoundBox: TYPE = GGModelTypes.BoundBox;
SelectedObjectData: TYPE = GGModelTypes.SelectedObjectData;
CameraData: TYPE = GGInterfaceTypes.CameraData;
DisplayStyle: TYPE = GGCluster.DisplayStyle;
ClusterClassDef: TYPE = REF ClusterClassDefObj;
ClusterClassDefObj: TYPE = RECORD[type: ATOM, class: ClusterClass];
TextData: TYPE = REF TextDataObj;
TextDataObj: TYPE = RECORD [
rope: Rope.ROPE,
worldPt: Point,
lots of font info, duplicated in various forms
fontStyle: DisplayStyle, -- the desired font style
trueFontStyle: DisplayStyle, -- the actual font style
fontString: Rope.ROPENIL, -- the desired font string
trueFontString: Rope.ROPENIL, -- the actual font string
fontFamily: ATOM,
fontFace: NodeStyle.FontFace,
fontSize: REAL,
font: ImagerFont.Font,
validFontInfo: BOOLFALSE, -- TRUE if desires match actual
color: Imager.Color,
colorName: Rope.ROPE,
feedbackBox: BoundBox
];
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;
FontStringError: PUBLIC SIGNAL = CODE;
FetchClusterClass: PUBLIC PROC [type: ATOM] RETURNS [class: ClusterClass] = {
FOR l: LIST OF ClusterClassDef ← clusterClasses, l.rest UNTIL l=NIL DO
IF l.first.type=type THEN RETURN[l.first.class];
ENDLOOP;
SIGNAL NotFound;
RETURN[NIL];
};
Text cluster class procs
BuildTextClusterClass: PROC [] RETURNS [class: ClusterClass] = {
class ← NEW[ClusterClassObj ← [
type: $Text,
boundBox: TextBoundBox,
copy: TextCopy,
draw: TextDraw,
drawTransform: TextDrawTransform,
drawSelectionFeedback: TextDrawSelectionFeedback,
transform: TextTransform,
emptyParts: TextEmptyParts,
newParts: TextNewParts,
addParts: TextAddParts,
removeParts: TextRemoveParts,
closestPoint: TextClosestPoint,
closestPointAndTangent: NIL,
closestSegment: TextClosestSegment,
fileout: TextFileout,
filein: TextFilein
]];
};
MakeTextCluster: PUBLIC PROC [text: Rope.ROPE, fontStyle: DisplayStyle, fontString: Rope.ROPE, colorName: Rope.ROPE, worldPt: Point] RETURNS [clus: Cluster] = {
feedbackBox: BoundBox ← GGBoundBox.CreateBoundBox[0,0,0,0];
textData: TextData ← NEW[TextDataObj ← [text, worldPt, , , , , , , , , FALSE, Imager.black, colorName, feedbackBox]];
clus ← NEW[ClusterObj ← [
class: FetchClusterClass[$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[clus, fontStyle, fontString];
SetTextFont now does an UpdateFontInfo of its own
UpdateFontInfo[clus, fontStyle];
clus.class.boundBox[clus];
};
GetText: PUBLIC PROC [clus: Cluster] RETURNS [text: Rope.ROPE] = {
RETURN[IF clus.class.type#$Text THEN NIL ELSE NARROW[clus.data, TextData].rope];
};
AppendText: PUBLIC PROC [clus: Cluster, text: Rope.ROPE] = {
textData: TextData;
IF clus.class.type#$Text THEN RETURN;
textData ← NARROW[clus.data];
textData.rope ← Rope.Concat[textData.rope, text]; -- update text
clus.class.boundBox[clus]; -- update bound box
};
BackspaceText: PUBLIC PROC [clus: Cluster] = {
textData: TextData;
IF clus.class.type#$Text THEN RETURN;
textData ← NARROW[clus.data];
textData.rope ← Rope.Substr[base: textData.rope, len: Rope.Length[textData.rope]-1]; -- update text
clus.class.boundBox[clus]; -- update bound box
};
DigitBreak: IO.BreakProc = {
RETURN[IF char IN ['0..'9] THEN break ELSE other];
};
FontParamsFromFontString: PUBLIC PROC [fontStyle: DisplayStyle, fontString: Rope.ROPE] RETURNS [fontPrefix: ATOM, fontFamily: ATOM, fontFace: NodeStyle.FontFace, fontSize: REAL] = {
A fontstring is a font designation with family, size, and face, like Helvetica23BI or TimesRoman6.
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: BOOLFALSE;
fontStream: IO.STREAMIO.RIS[fontString];
[familyName, ----] ← IO.GetTokenRope[stream: fontStream, breakProc: DigitBreak ! IO.EndOfStream => SIGNAL FontStringError];
fontSize ← GetNumber[fontStream ! IO.EndOfStream => SIGNAL FontStringError];
can't user IO.GetInt because a string like Helvetica9BI causes it to fail!
[face, ----] ← IO.GetTokenRope[stream: fontStream, breakProc: IO.TokenProc ! IO.EndOfStream => {face ← ""; CONTINUE; };];
fontPrefix ← Atom.MakeAtom[IF fontStyle IN [print..wysiwyg] THEN "xerox/pressfonts/" ELSE "xerox/tiogafonts/"];
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;
};
UpdateFontInfo: PRIVATE PROC [clus: Cluster, displayStyle: DisplayStyle] = {
textData: TextData;
font: Imager.Font;
fontPrefix, fontFamily: ATOM;
fontFace: NodeStyle.FontFace;
fontSize: REAL ← 0.0;
IF clus.class.type#$Text THEN ERROR; -- shouldn't happen
textData ← NARROW[clus.data];
IF textData.fontString=NIL THEN ERROR; -- shouldn't happen
IF displayStyle= textData.trueFontStyle AND Rope.Equal[textData.fontString, textData.trueFontString, FALSE] THEN {
textData.fontStyle ← displayStyle;
clus.class.boundBox[clus]; -- update bound box
textData.validFontInfo ← TRUE; -- last thing to set
RETURN;
};
[fontPrefix, fontFamily, fontFace, fontSize] ← FontParamsFromFontString[displayStyle, textData.fontString ! FontStringError => GOTO Abort; ];
font ← NodeStyleFont.FontFromStyleParams[prefix: fontPrefix, family: fontFamily, face: fontFace, size: fontSize, alphabets: CapsAndLower];
IF Rope.Find[s1: font.name, s2: Atom.GetPName[fontFamily], case: FALSE]=-1 THEN {
Did a font substitution which we don't want. Retry with press fonts
font ← NodeStyleFont.FontFromStyleParams[prefix: Atom.MakeAtom["xerox/pressfonts/"], family: fontFamily, face: fontFace, size: fontSize, alphabets: CapsAndLower];
IF Rope.Find[s1: font.name, s2: Atom.GetPName[fontFamily], case: FALSE]=-1 THEN
GOTO Abort;
};
textData.fontStyle ← textData.trueFontStyle ← displayStyle;
textData.trueFontString ← textData.fontString;
textData.fontFamily ← fontFamily;
textData.fontFace ← fontFace;
textData.fontSize ← fontSize;
textData.font ← font;
clus.class.boundBox[clus]; -- update bound box
textData.validFontInfo ← TRUE; -- last thing to set
EXITS
Abort => GGError.AppendHerald["Gargoyle Font not Found", TRUE];
};
SetTextFont: PUBLIC PROC [clus: Cluster, fontStyle: DisplayStyle, fontString: Rope.ROPE] = {
A fontstring is a font designation with family, size, and face, like Helvetica23BI or TimesRoman6
textData: TextData;
IF clus.class.type#$Text OR fontString=NIL THEN RETURN;
textData ← NARROW[clus.data];
This ONLY sets parameters in the textData. It does NOT make a new font. The new font will be made the next time the text cluster is displayed for any reason.
NOTE: this could get us into bound box problems since the bound box will be obsolete for a short while.
textData.validFontInfo ← FALSE;
textData.fontStyle ← fontStyle;
textData.fontString ← fontString;
};
SetTextFont: PUBLIC PROC [clus: Cluster, fontStyle: DisplayStyle, fontString: Rope.ROPE] = {
A fontstring is a font designation with family, size, and face, like Helvetica23BI or TimesRoman6
textData: TextData;
IF clus.class.type#$Text OR fontString=NIL THEN RETURN;
textData ← NARROW[clus.data];
textData.validFontInfo ← FALSE;
textData.fontStyle ← fontStyle;
textData.fontString ← fontString;
NOTE: The following call makes the style caching stuff superfluous but makes the refreshing work, so we do it until we either rip it out or live with it
UpdateFontInfo[clus, fontStyle];
};
GetTextFont: PUBLIC PROC [clus: Cluster] RETURNS [font: ImagerFont.Font, fontString: Rope.ROPE] = {
textData: TextData;
IF clus.class.type#$Text THEN RETURN[NIL, NIL];
textData ← NARROW[clus.data];
RETURN[textData.font, textData.trueFontString];
};
SetTextColor: PUBLIC PROC [clus: Cluster, color: Imager.Color, colorName: Rope.ROPE] = {
textData: TextData;
IF clus.class.type#$Text THEN RETURN;
textData ← NARROW[clus.data];
textData.color ← color;
textData.colorName ← colorName;
};
GetTextColor: PUBLIC PROC [clus: Cluster] RETURNS [color: Imager.Color, colorName: Rope.ROPE] = {
textData: TextData;
IF clus.class.type#$Text THEN RETURN[NIL, NIL];
textData ← NARROW[clus.data];
RETURN[textData.color, textData.colorName];
};
TextBoundBox: PROC [cluster: Cluster] = {
GGModelTypes.ClusterBoundBoxProc
textData: TextData ← NARROW[cluster.data];
extents: ImagerFont.Extents;
halfCP: REAL = GGModelTypes.halfJointSize + 1;
extents ← ImagerFont.RopeBoundingBox[textData.font, textData.rope];
ASSERT: extent elements are all positive, so subtract left and descent, add right and ascent
GGBoundBox.UpdateBoundBox[cluster.boundBox,
textData.worldPt[1] - extents.leftExtent - halfCP,
textData.worldPt[2] - extents.descent - halfCP,
textData.worldPt[1] + extents.rightExtent + halfCP,
textData.worldPt[2] + extents.ascent + halfCP];
GGBoundBox.UpdateBoundBox[textData.feedbackBox,
textData.worldPt[1] - extents.leftExtent,
textData.worldPt[2] - extents.descent,
textData.worldPt[1] + extents.rightExtent,
textData.worldPt[2] + extents.ascent];
};
TextCopy: PROC [cluster: Cluster] RETURNS [copy: Cluster] = {
GGModelTypes.ClusterCopyProc
textData: TextData ← NARROW[cluster.data];
copy ← MakeTextCluster[textData.rope, textData.fontStyle, textData.fontString, textData.colorName, textData.worldPt];
IS THIS RIGHT: ??
copy.parent ← cluster.parent;
copy.children ← cluster.children;
};
TextDraw: PROC [cluster: Cluster, dc: Imager.Context, camera: CameraData] = {
GGModelTypes.ClusterDrawProc
textData: TextData ← NARROW[cluster.data];
IF NOT textData.validFontInfo THEN UpdateFontInfo[cluster, camera.displayStyle];
Imager.SetFont[dc, textData.font];
Imager.SetXY[dc, [textData.worldPt[1], textData.worldPt[2]]];
Imager.ShowRope[dc, textData.rope];
IF selected THEN GGBoundBox.DrawBoundBox[dc, textData.feedbackBox];
IF selected THEN DrawTextJoints[cluster, dc];
};
DrawTextJoints: PROC [cluster: Cluster, dc: Imager.Context] = {
halfCP: REAL = GGModelTypes.halfJointSize + 1;
bBox: BoundBox ← cluster.boundBox;
GGShapes.DrawSelectedJoint[dc, [bBox.loX + halfCP, bBox.loY + halfCP]];
GGShapes.DrawSelectedJoint[dc, [bBox.loX + halfCP, bBox.hiY - halfCP]];
GGShapes.DrawSelectedJoint[dc, [bBox.hiX - halfCP, bBox.loY + halfCP]];
GGShapes.DrawSelectedJoint[dc, [bBox.hiX - halfCP, bBox.hiY - halfCP]];
};
TextDrawTransform: PROC [cluster: Cluster, parts: ClusterParts, dc: Imager.Context, camera: CameraData, transform: ImagerTransformation.Transformation] = {
GGModelTypes.ClusterDrawTransformProc
Translates the origin. Rotation is not currently possible.
textData: TextData ← NARROW[cluster.data];
tempPoint: Point ← GGTransform.Transform[transform, textData.worldPt];
IF NOT textData.validFontInfo THEN UpdateFontInfo[cluster, camera.displayStyle];
Imager.SetFont[dc, textData.font];
Imager.SetXY[dc, [ tempPoint[1], tempPoint[2] ] ];
Imager.ShowRope[dc, textData.rope];
};
TextDrawSelectionFeedback: PROC [cluster: Cluster, parts: ClusterParts, dc: Imager.Context, camera: CameraData, quick: BOOLFALSE] = {
GGModelTypes.ClusterDrawSelectionFeedbackProc
textData: TextData ← NARROW[cluster.data];
IF NOT textData.validFontInfo THEN UpdateFontInfo[cluster, camera.displayStyle];
GGBoundBox.DrawBoundBox[dc, textData.feedbackBox];
DrawTextJoints[cluster, dc];
};
TextTransform: PROC [cluster: Cluster, parts: ClusterParts, transform: ImagerTransformation.Transformation] = {
GGModelTypes.ClusterTransformProc
Translates the origin. Rotation is not currently possible.
textData: TextData ← NARROW[cluster.data];
textData.worldPt ← GGTransform.Transform[transform, textData.worldPt];
TextBoundBox[cluster];
};
TextEmptyParts: PROC [cluster: Cluster, parts: ClusterParts] RETURNS [BOOL] = {
RETURN[TRUE]
};
TextNewParts: PROC [cluster: Cluster, mode: SelectMode] RETURNS [parts: ClusterParts] = {
RETURN[NIL]
};
TextAddParts: PROC [cluster: Cluster, parts: ClusterParts, mode: ExtendMode] RETURNS [newParts: ClusterParts] = {
RETURN[NIL]
};
TextRemoveParts: PROC [cluster: Cluster, parts: ClusterParts, mode: ExtendMode] RETURNS [newParts: ClusterParts] = {
RETURN[NIL]
};
TextClosestPoint: PROC [cluster: Cluster, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = {
GGModelTypes.ClusterClosestPointProc
Used for hit testing. See comments in GGCluster.mesa
textData: TextData ← NARROW[cluster.data];
[bestDist, ----, bestPoint] ← GGBoundBox.NearestPoint[textData.feedbackBox, testPoint];
success ← TRUE;
[bestDist, ----, bestPoint, success] ← GGBoundBox.NearestPoint[textData.feedbackBox, testPoint];
};
TextClosestSegment: PROC [cluster: Cluster, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = {
GGModelTypes.ClusterClosestSegmentProc
Used for hit testing. See comments in GGCluster.mesa
textData: TextData ← NARROW[cluster.data];
textHitData: TextHitData ← NARROW[cluster.hitData];
[bestDist, textHitData.bestSeg, bestPoint, success] ← GGBoundBox.NearestSegment[textData.feedbackBox, testPoint, tolerance];
};
TextFileout: PROC [cluster: Cluster, f: IO.STREAM] = {
GGModelTypes.ClusterFileoutProc
Write a description of yourself onto stream f.
textData: TextData ← NARROW[cluster.data];
f.PutF["\"%g\" %g %g", [rope[textData.rope]], [rope[textData.fontString]], [real[textData.fontSize]] ];
f.PutF[" %g ", [rope[textData.colorName]] ];
GGParseOut.WritePoint[f, textData.worldPt];
};
TextFilein: PROC [f: IO.STREAM, version: REAL] RETURNS [clus: Cluster] = {
GGModelTypes.ClusterFileinProc
Read a description of yourself from stream f.
UnpackComplexFontName: PROC [fontName: Rope.ROPE, fontSize: REAL] RETURNS [new: Rope.ROPE] = {
Takes a complex name like "xerox/pressfonts/Helvetica-MIR" and a font size and returns a simple name like Helvetica10I
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;
Strings now surronded by quotes. Formerly surrounded by parens.
IF version > 8601.22 THEN {
rope ← f.GetRopeLiteral[];
}
ELSE {
GGParseIn.ReadBlankAndRope[f, "("];
rope ← GGParseIn.ReadBlankAndWord[f];
GGParseIn.ReadBlankAndRope[f, ")"];
};
We keep evoloving font names
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];
clus ← MakeTextCluster[rope, print, fontName, colorName, point];
};
Interpress cluster class procs
BuildIPClusterClass: PROC [] RETURNS [class: ClusterClass] = {
class ← NEW[ClusterClassObj ← [
type: $IP,
boundBox: IPBoundBox,
listBoxes: NIL,
draw: IPDraw,
drawTransform: IPDrawTransform,
select: IPSelect,
deselect: IPDeselect,
drawSelectionFeedback: IPDrawSelectionFeedback,
transform: IPTransform,
newParts: IPEndSelectProc,
addParts: IPEndExtendProc,
closestPoint: IPClosestPoint,
closestPointAndTangent: NIL,
closestSegment: IPClosestSegment,
fileout: IPFileout,
filein: IPFilein
]];
};
MakeIPCluster: PUBLIC PROC [fileName: Rope.ROPE, worldPt: Point] RETURNS [clus: Cluster] = {
ipData: IPData;
feedbackBox: BoundBox;
feedbackBox ← GGBoundBox.CreateBoundBox[0,0,0,0]; -- gets real values later in this proc.
ipData ← NEW[IPDataObj ← [fileName, worldPt, feedbackBox]];
clus ← NEW[ClusterObj ← [
class: FetchClusterClass[$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 ← [] ]
]];
clus.class.boundBox[clus];
};
IPBoundBox: PROC [cluster: Cluster] = {
GGModelTypes.ClusterBoundBoxProc
ipData: IPData ← NARROW[cluster.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[cluster.boundBox,
ipData.worldPt[1] + rect.x - halfCP,
ipData.worldPt[2] + rect.y - halfCP,
ipData.worldPt[1] + rect.w + halfCP,
ipData.worldPt[2] + rect.h + halfCP];
GGBoundBox.UpdateBoundBox[ipData.feedbackBox,
ipData.worldPt[1] + rect.x,
ipData.worldPt[2] + rect.y,
ipData.worldPt[1] + rect.w,
ipData.worldPt[2] + rect.h];
};
IPDraw: PROC [cluster: Cluster, dc: Imager.Context, camera: CameraData] = {
GGModelTypes.ClusterDrawProc
ipData: IPData ← NARROW[cluster.data];
IF selected THEN GGBoundBox.DrawBoundBox[dc, ipData.feedbackBox];
};
IPDrawTransform: PROC [cluster: Cluster, parts: ClusterParts, dc: Imager.Context, camera: CameraData, transform: ImagerTransformation.Transformation] = {
GGModelTypes.ClusterDrawTransformProc
ipData: IPData ← NARROW[cluster.data];
tempPoint: Point ← GGTransform.Transform[transform, ipData.worldPt];
};
IPSelect: PROC [cluster: Cluster] = {
GGModelTypes.ClusterSelectProc
};
IPDeselect: PROC [cluster: Cluster] = {
GGModelTypes.ClusterDeselectProc
};
IPDrawSelectionFeedback: PROC [cluster: Cluster, parts: ClusterParts, dc: Imager.Context, camera: CameraData, quick: BOOLFALSE] = {
GGModelTypes.ClusterDrawSelectionFeedbackProc
ipData: IPData ← NARROW[cluster.data];
GGBoundBox.DrawBoundBox[dc, ipData.feedbackBox];
};
IPTransform: PROC [cluster: Cluster, parts: ClusterParts, transform: ImagerTransformation.Transformation] = {
GGModelTypes.ClusterTransformProc
Store the transformation in the cluster. (For now, just allow translation)
ipData: IPData ← NARROW[cluster.data];
ipData.worldPt ← GGTransform.Transform[transform, ipData.worldPt];
IPBoundBox[cluster];
};
IPEndSelectProc: GGModelTypes.ClusterNewPartsProc = {
GGModelTypes.ClusterNewPartsProc
};
IPEndExtendProc: GGModelTypes.ClusterAddPartsProc = {
GGModelTypes.ClusterAddPartsProc
};
IPClosestPoint: PROC [cluster: Cluster, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = {
GGModelTypes.ClusterClosestPointProc
Used for hit testing. See comments in GGCluster.mesa
ipData: IPData ← NARROW[cluster.data];
[bestDist, ----, bestPoint] ← GGBoundBox.NearestPoint[ipData.feedbackBox, testPoint];
success ← TRUE;
[bestDist, ----, bestPoint, success] ← GGBoundBox.NearestPoint[ipData.feedbackBox, testPoint];
};
IPClosestSegment: PROC [cluster: Cluster, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = {
GGModelTypes.ClusterClosestSegmentProc
Used for hit testing. See comments in GGCluster.mesa
ipData: IPData ← NARROW[cluster.data];
ipHitData: IPHitData ← NARROW[cluster.hitData];
[bestDist, ipHitData.bestSeg, bestPoint, success] ← GGBoundBox.NearestSegment[ipData.feedbackBox, testPoint, tolerance];
};
IPFileout: PROC [cluster: Cluster, f: IO.STREAM] = {
GGModelTypes.ClusterFileoutProc
Write a description of yourself onto stream f.
ipData: IPData ← NARROW[cluster.data];
f.PutF["\"%g\"", [rope[ipData.file]]];
GGParseOut.WritePoint[f, ipData.worldPt];
};
IPFilein: PROC [f: IO.STREAM, version: REAL] RETURNS [clus: Cluster] = {
GGModelTypes.ClusterFileinProc
Read a description of yourself from stream f.
fileName: Rope.ROPE;
point: Point;
fileName ← f.GetRopeLiteral[];
GGParseIn.ReadBlank[f];
point ← GGParseIn.ReadPoint[f];
clus ← MakeIPCluster[fileName, point];
};
defaultFont: ImagerFont.Font;
greekFont: ImagerFont.Font;
clusterClasses: LIST OF ClusterClassDef;
Init: PRIVATE PROC [] = {
textDef: ClusterClassDef ← NEW[ClusterClassDefObj ← [type: $Text, class: BuildTextClusterClass[]]];
ipDef: ClusterClassDef ← NEW[ClusterClassDefObj ← [type: $IP, class: BuildIPClusterClass[]]];
boxDef: ClusterClassDef ← NEW[ClusterClassDefObj ← [type: $Box, class: GGBoxCluster.BuildBoxClusterClass[]]];
clusterClasses ← LIST[boxDef, ipDef, textDef];
defaultFont ← ImagerFont.Scale[ImagerFont.Find["xerox/pressfonts/Helvetica-MRR"], 10.0];
defaultFont ← ImagerFont.Find["xerox/tiogafonts/Helvetica10"];
greekFont ← ImagerFont.Scale[ImagerFont.Find["xerox/tiogafonts/Hippo-MRR"], 10.0];
greekFont ← ImagerFont.Find["xerox/tiogafonts/Hippo10"];
};
Init[];
END.
Pier, January 28, 1986 4:17:08 pm PST
changes to: TextBoundBox changed sign in equation to - leftExtent