DIRECTORY BiScrollersTransformsTypes, CodeTimer, CubicSplines, Feedback, FeedbackTypes, Geom2D, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGCoreOps, GGCoreTypes, GGDescribe, GGEmbedTypes, GGEvent, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGMeasure, GGModelTypes, GGMouseEvent, GGParent, GGParseIn, GGRefresh, GGRefreshTypes, GGScene, GGScrollMonitor, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGUIUtility, GGUserInput, GGViewerOps, GGWindow, Imager, ImagerBox, ImagerTransformation, IO, Real, Rope, Vector2, Vectors2d; GGEventImplB: CEDAR PROGRAM IMPORTS CodeTimer, Feedback, Geom2D, GGAlign, GGBoundBox, GGCaret, GGCoreOps, GGDescribe, GGHistory, GGMeasure, GGMouseEvent, GGParent, GGParseIn, GGRefresh, GGScene, GGScrollMonitor, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGUIUtility, GGUserInput, GGViewerOps, GGWindow, ImagerBox, ImagerTransformation, IO, Rope, Vectors2d EXPORTS GGEvent, GGHistoryTypes, GGInterfaceTypes = BEGIN AlignBag: TYPE = GGInterfaceTypes.AlignBag; Axis: TYPE = BiScrollersTransformsTypes.Axis; BoundBox: TYPE = GGModelTypes.BoundBox; Camera: TYPE = GGModelTypes.Camera; Caret: TYPE = GGInterfaceTypes.Caret; Change: PUBLIC TYPE = GGHistory.Change; -- exported to GGHistoryTypes DisplayStyle: TYPE = GGModelTypes.DisplayStyle; EmbedDataObj: PUBLIC TYPE = GGEmbedTypes.EmbedDataObj; -- exported to GGInterfaceTypes FeatureData: TYPE = GGInterfaceTypes.FeatureData; MsgRouter: TYPE = FeedbackTypes.MsgRouter; FilterLists: TYPE = GGAlign.FilterLists; GGData: TYPE = GGInterfaceTypes.GGData; GravityType: TYPE = GGInterfaceTypes.GravityType; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; Point: TYPE = GGBasicTypes.Point; RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; Scene: TYPE = GGModelTypes.Scene; SegAndIndex: TYPE = GGSequence.SegAndIndex; Segment: TYPE = GGSegmentTypes.Segment; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; Sequence: TYPE = GGModelTypes.Sequence; SequenceOfReal: TYPE = REF SequenceOfRealObj; SequenceOfRealObj: TYPE = GGCoreTypes.SequenceOfRealObj; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceParts: TYPE = GGModelTypes.SliceParts; StrokeEnd: TYPE = Imager.StrokeEnd; StrokeJoint: TYPE = Imager.StrokeJoint; Traj: TYPE = GGModelTypes.Traj; TrajData: TYPE = GGModelTypes.TrajData; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajParts: TYPE = GGModelTypes.TrajParts; TriggerBag: TYPE = GGInterfaceTypes.TriggerBag; Transformation: TYPE = ImagerTransformation.Transformation; UserInputProc: TYPE = GGEvent.UserInputProc; Vector: TYPE = GGBasicTypes.Vector; Vec: TYPE = Vector2.VEC; WalkProc: TYPE = GGModelTypes.WalkProc; pointsPerIn: REAL = 72.0; pointsPerCm: REAL = 72.0/2.54; reallyBigReal: REAL = 1.0e37; multipleValues: Rope.ROPE = "multiple values - Aborted!!"; complainAboutIconic: Rope.ROPE = "Viewing transformations not allowed on iconic viewer"; Problem: SIGNAL [msg: Rope.ROPE] = Feedback.Problem; GetArg: PROC [diffOK: BOOL ¬ FALSE, sel: Rope.ROPE, router: MsgRouter] RETURNS [valid: BOOL, arg, arg2: REAL ¬ 0.0] ~ { s: IO.STREAM; valid ¬ SELECT sel.Length[] FROM > 1 => TRUE, > 0 => sel.Fetch[0] IN ['0 .. '9], ENDCASE => FALSE; IF NOT valid THEN RETURN; s ¬ IO.RIS[sel]; { ENABLE IO.Error, IO.EndOfStream => { valid ¬ FALSE; Feedback.PutF[router, oneLiner, $Complaint, "Select a number, not %g", IO.refAny[sel]]; CONTINUE}; arg ¬ arg2 ¬ s.GetReal[]; IF diffOK AND (NOT s.EndOf[]) AND s.PeekChar[]=': THEN { IF NOT s.GetChar[]=': THEN ERROR; arg2 ¬ s.GetReal[]; valid ¬ valid}; IF NOT s.EndOf[] THEN IO.Error[ec: SyntaxError, stream: s]; }; s.Close[]; }; WindowBigEnough: PROC [ggData: GGData] RETURNS [BOOL] = { iconW, iconH: INT ¬ 0; w: INT ¬ GGState.GetWidth[ggData]; h: INT ¬ GGState.GetHeight[ggData]; [iconW, iconH] ¬ GGViewerOps.GetIconSize[]; RETURN [w>iconW AND h>iconH]; }; ScalePop: PUBLIC UserInputProc = { bigEnough: BOOL ¬ WindowBigEnough[ggData]; IF NOT bigEnough THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, complainAboutIconic]; } ELSE { key: REF ¬ event.rest.first; sel: Rope.ROPE ¬ NARROW[event.rest.rest.first]; valid: BOOL; op: GGState.ScaleOp; arg, arg2: REAL; [valid, arg, arg2] ¬ GetArg[TRUE, sel, ggData.router]; IF NOT valid THEN arg ¬ arg2 ¬ 2.0; IF key=$Shrink THEN {arg ¬ 1.0/arg; arg2 ¬ 1.0/arg2}; op _ SELECT key FROM $Magnify, $Shrink => IF arg=arg2 THEN [byArg[arg]] ELSE [diff[arg, arg2]], $Reset => [reset[]], ENDCASE => ERROR; GGState.BiScrollersScale[ggData, op]; GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, FALSE, TRUE]; }; }; RotatePop: PUBLIC UserInputProc = { bigEnough: BOOL ¬ WindowBigEnough[ggData]; IF NOT bigEnough THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, complainAboutIconic]; } ELSE { key: REF ¬ event.rest.first; sel: Rope.ROPE ¬ NARROW[event.rest.rest.first]; valid: BOOL; op: GGState.RotateOp; arg: REAL; [valid, arg] ¬ GetArg[FALSE, sel, ggData.router]; IF NOT valid THEN arg ¬ 90; op _ SELECT key FROM $Left => [byArg[arg]], $Right => [byArg[-arg]], $Half => [byArg[180]], $Reset => [reset[]], ENDCASE => ERROR; GGState.BiScrollersRotate[ggData, op]; GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, FALSE, TRUE]; }; }; FitPop: PUBLIC UserInputProc = { bigEnough: BOOL ¬ WindowBigEnough[ggData]; IF NOT bigEnough THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, complainAboutIconic]; } ELSE { key: REF ¬ event.rest.first; uniformly: BOOL ~ SELECT key FROM $FitUniformly => TRUE, $FitXY => FALSE, ENDCASE => ERROR; BiScrollersFit[ggData, uniformly]; GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, FALSE, TRUE]; }; }; Norm: PROC [t: Transformation, axis: Axis] RETURNS [norm: Vec] = { tn: Geom2D.Trans ¬ Geom2D.ToTrans[t]; SELECT axis FROM X => norm ¬ [tn.dxdx, tn.dxdy]; Y => norm ¬ [tn.dydx, tn.dydy]; ENDCASE => ERROR; }; BiScrollersFit: PROC [ggData: GGData, uniformly: BOOL] = { old: Transformation = GGState.GetBiScrollersTransform[ggData]; viewport: Imager.Rectangle ¬ GGState.GetViewport[ggData]; cw: REAL _ viewport.w; ch: REAL _ viewport.h; normX: Vec ¬ Norm[old, X]; normY: Vec ¬ Norm[old, Y]; minX, maxX: Vec; minY, maxY: Vec; limits: Imager.Box; from, to: Geom2D.Rect; [minX, maxX] _ GGExtremaProc[ggData, normX]; [minY, maxY] _ GGExtremaProc[ggData, normY]; [limits.xmin, limits.xmax] ¬ ViewLimitsOfImage[old, minX, maxX, X]; [limits.ymin, limits.ymax] ¬ ViewLimitsOfImage[old, minY, maxY, Y]; from ¬ ImagerBox.RectangleFromBox[limits]; to ¬ [0, 0, cw, ch]; GGState.BiScrollersBoxScale[ggData, from, to, uniformly]; }; ViewLimitsOfImage: PROC [t: Transformation, min, max: Vec, axis: Axis] RETURNS [vmin, vmax: REAL] = { min ¬ t.Transform[min]; max ¬ t.Transform[max]; SELECT axis FROM X => {vmin ¬ min.x; vmax ¬ max.x}; Y => {vmin ¬ min.y; vmax ¬ max.y}; ENDCASE => ERROR; }; GGExtremaProc: PROC [ggData: GGData, direction: Vec] RETURNS [min, max: Vec] = { area: Geom2D.Rect; bigBox: BoundBox ¬ GGBoundBox.BoundBoxOfBoxes[GGScene.BoundBoxesInScene[ggData.scene].list]; area ¬ IF bigBox#NIL THEN [x: bigBox.loX, y: bigBox.loY, w: bigBox.hiX-bigBox.loX, h: bigBox.hiY-bigBox.loY] ELSE [0.0, 0.0, 1.0, 1.0]; [min, max] ¬ Geom2D.ExtremaOfRect[r: area, n: direction]; }; paperHeight: REAL = 11.0*72.0; paperWidth: REAL = 8.5*72.0; ResetPop: PUBLIC UserInputProc = { bigEnough: BOOL ¬ WindowBigEnough[ggData]; IF NOT bigEnough THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, complainAboutIconic]; } ELSE { key: REF ¬ event.rest.first; SELECT key FROM $Vanilla => { viewport: Imager.Rectangle ¬ GGState.GetViewport[ggData]; cw: REAL _ viewport.w; ch: REAL _ viewport.h; vanilla: Transformation; vanilla _ ImagerTransformation.Translate[[(cw-paperWidth)/2.0, (ch-paperHeight)/2.0]]; GGState.SetBiScrollersTransform[ggData, vanilla, FALSE, remember]; GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, FALSE, TRUE]; -- paint properly }; $VanillaAndCenter => { viewport: Imager.Rectangle ¬ GGState.GetViewport[ggData]; cw: REAL _ viewport.w; ch: REAL _ viewport.h; vanilla: Transformation; vanilla _ ImagerTransformation.Translate[[(cw-paperWidth)/2.0, (ch-paperHeight)/2.0]]; GGState.SetBiScrollersTransform[ggData, vanilla, FALSE, remember]; }; $Center => NULL; ENDCASE => ERROR; SELECT key FROM $Center, $VanillaAndCenter => { GGState.BiScrollersAlign[ ggData: ggData, client: [fraction[0.5, 0.5]], viewer: [fraction[0.5, 0.5]], doX: TRUE, doY: TRUE, ageOp: remember]; GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, FALSE, TRUE]; }; $Vanilla => NULL; ENDCASE => ERROR; }; }; EdgePop: PUBLIC UserInputProc = { bigEnough: BOOL ¬ WindowBigEnough[ggData]; IF NOT bigEnough THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, complainAboutIconic]; } ELSE { key: REF ¬ event.rest.first; loc: fraction BiScrollersTransformsTypes.Location ¬ [fraction[0, 0]]; doX, doY: BOOL ¬ TRUE; SELECT key FROM $Left => {loc.fx ¬ 0; doY ¬ FALSE}; $Right => {loc.fx ¬ 1; doY ¬ FALSE}; $Bottom => {loc.fy ¬ 0; doX ¬ FALSE}; $Top => {loc.fy ¬ 1; doX ¬ FALSE}; $BotLeft => loc ¬ [fraction[0, 0]]; $MidLeft => loc ¬ [fraction[0, 0.5]]; $TopLeft => loc ¬ [fraction[0, 1]]; $BotMiddle => loc ¬ [fraction[0.5, 0]]; $BotRight => loc ¬ [fraction[1, 0]]; $MidRight => loc ¬ [fraction[1, 0.5]]; $TopRight => loc ¬ [fraction[1, 1]]; $TopMiddle => loc ¬ [fraction[0.5, 1]]; ENDCASE => ERROR; GGState.BiScrollersAlign[ ggData: ggData, client: loc, viewer: loc, doX: doX, doY: doY, ageOp: remember]; GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, FALSE, TRUE]; }; }; CenterSel: PUBLIC UserInputProc = { bigEnough: BOOL ¬ WindowBigEnough[ggData]; IF NOT bigEnough THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, complainAboutIconic]; } ELSE { bBox: BoundBox ¬ GGScene.BoundBoxOfSelected[ggData.scene, normal]; IF bBox.null THEN { IF GGCaret.Exists[ggData.caret] THEN { caretPos: Point ¬ GGCaret.GetPoint[ggData.caret]; GGState.BiScrollersAlign[ ggData: ggData, client: [variant: coord[x: caretPos.x, y: caretPos.y]], viewer: [variant: fraction[fx: 0.5, fy: 0.5]], doX: TRUE, doY: TRUE, ageOp: remember]; GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, FALSE, TRUE]; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "CenterSel failed: either a selection or a caret is required to center"]; } ELSE { GGState.BiScrollersAlign[ ggData: ggData, client: [variant: coord[x: (bBox.loX+bBox.hiX)/2.0, y: (bBox.loY+bBox.hiY)/2.0]], viewer: [variant: fraction[fx: 0.5, fy: 0.5]], doX: TRUE, doY: TRUE, ageOp: remember]; GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, FALSE, TRUE]; }; }; }; CenterCaret: PUBLIC UserInputProc = { bigEnough: BOOL ¬ WindowBigEnough[ggData]; IF NOT bigEnough THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, complainAboutIconic]; } ELSE { IF GGCaret.Exists[ggData.caret] THEN { caretPos: Point ¬ GGCaret.GetPoint[ggData.caret]; GGState.BiScrollersAlign[ ggData: ggData, client: [variant: coord[x: caretPos.x, y: caretPos.y]], viewer: [variant: fraction[fx: 0.5, fy: 0.5]], doX: TRUE, doY: TRUE, ageOp: remember]; GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, FALSE, TRUE]; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "CenterCaret failed: a caret is required to center"]; }; }; FitSel: PUBLIC UserInputProc = { bigEnough: BOOL ¬ WindowBigEnough[ggData]; IF NOT bigEnough THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, complainAboutIconic]; } ELSE { tbBox, vBox, tvBox: Geom2D.Rect; cToV: Transformation; bBox: BoundBox ¬ GGScene.BoundBoxOfSelected[scene: ggData.scene, selectClass: normal]; IF bBox.null THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "FitSel failed: a selection is required to fit"] ELSE { cToV ¬ GGState.GetBiScrollersTransform[ggData]; tbBox ¬ ImagerTransformation.TransformRectangle[cToV, GGBoundBox.RectangleFromBoundBox[bBox] ]; vBox ¬ GGState.GetViewport[ggData]; tvBox ¬ ImagerTransformation.TransformRectangle[cToV, vBox]; GGState.BiScrollersBoxScale[ggData: ggData, from: tbBox, to: tvBox, uniformly: TRUE]; GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, FALSE, TRUE]; }; }; }; PrevTransform: PUBLIC UserInputProc = { bigEnough: BOOL ¬ WindowBigEnough[ggData]; IF NOT bigEnough THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, complainAboutIconic]; } ELSE { GGState.BiScrollersPreviousTransform[ggData]; GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, FALSE, TRUE]; }; }; SetBiScrollersTransform: PUBLIC UserInputProc = { rope: Rope.ROPE ¬ NARROW[event.rest.first]; clientToViewer: Transformation ¬ GGDescribe.FactoredTransformationFromRope[rope ! GGParseIn.SyntaxError => GOTO SyntaxError]; GGState.SetBiScrollersTransform[ggData, clientToViewer]; EXITS SyntaxError => Feedback.Append[ggData.router, oneLiner, $Complaint, "SetBiScrollersTransform failed: select a legal factored transformation for SetBiScrollersTransform"]; }; ShowBiScrollersTransform: PUBLIC UserInputProc = { clientToViewer: Imager.Transformation ¬ GGState.GetBiScrollersTransform[ggData]; Feedback.PutF[ggData.router, oneLiner, $Show, "BiScrollers transform: %g", [rope[GGDescribe.FactoredTransformationToRope[clientToViewer] ]] ]; }; ScrollBar: UserInputProc = { newEvent: LIST OF REF ¬ CONS[$First, event]; -- so no paint occurs with clearClient = TRUE GGState.DoBSUserAction[ggData, newEvent]; GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, FALSE, TRUE]; }; OneZoom: UserInputProc = { IF IsIdentity[ggData.embed.scrollDue] THEN RETURN; GGScrollMonitor.UpdateBiScrollersTransformAndClearDue[ggData]; GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, FALSE, TRUE]; }; identity: Transformation = ImagerTransformation.Scale[1.0]; IsIdentity: PROC [m: Transformation] RETURNS [BOOL] = { RETURN[ImagerTransformation.Equal[m, identity]]; }; OneScroll: UserInputProc = { clientToViewer, viewerToClient: Transformation; caretPoint: Point; caretViewer: Point; caretClient: Point; IF IsIdentity[ggData.embed.scrollDue] THEN RETURN; clientToViewer ¬ GGState.GetBiScrollersTransform[ggData]; caretPoint ¬ GGCaret.GetPoint[ggData.caret]; caretViewer ¬ ImagerTransformation.Transform[clientToViewer, caretPoint]; GGScrollMonitor.UpdateBiScrollersTransformAndClearDue[ggData]; GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, FALSE, TRUE]; viewerToClient ¬ GGState.GetBiScrollersTransforms[ggData].viewerToClient; caretClient ¬ ImagerTransformation.Transform[viewerToClient, caretViewer]; GGMouseEvent.HandleMouse[ggData, LIST[$During, NEW[Point ¬ caretClient]]]; }; ViewersPaintEntireScene: PUBLIC UserInputProc = { GGWindow.RestoreScreenAndInvariants[$ViewersPaintEntireScene, ggData, none, FALSE, FALSE, TRUE]; }; ViewersPaintAllPlanes: PUBLIC UserInputProc = { GGWindow.RestoreScreenAndInvariants[$ViewersPaintAllPlanes, ggData, none, FALSE, FALSE, TRUE]; }; MakeHot: PUBLIC UserInputProc = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "MakeHot failed: select some objects for MakeHot"] ELSE { MakeSliceHot: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { GGSelect.SelectSlice[sliceD, ggData.scene, hot]; }; UpdateTriggerAndAlignBags: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { sliceFeature: FeatureData ¬ GGAlign.AddSliceFeature[sliceD, triggerBag]; alignObjects: LIST OF FeatureData; alignObjects ¬ GGAlign.IncrementalNewTrigger[sliceFeature, filterLists, NOT showAlignments, alignBag]; GGRefresh.NoteNewForeground[ggData, alignObjects]; -- fix foreground plane }; showAlignments: BOOL ¬ GGState.GetShowAlignments[ggData]; triggerBag: TriggerBag ¬ ggData.hitTest.triggerBag; alignBag: AlignBag ¬ ggData.hitTest.alignBag; filterLists: FilterLists ¬ GGState.GetFilterLists[ggData]; CodeTimer.StartInt[$MakeHot, $Gargoyle]; [] ¬ GGScene.WalkSelectedSlices[ggData.scene, first, MakeSliceHot, normal]; [] ¬ GGScene.WalkSelectedSlices[ggData.scene, first, UpdateTriggerAndAlignBags, hot]; Feedback.Append[ggData.router, oneLiner, $Feedback, "MakeHot: selected objects made hot"]; GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeHot, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; CodeTimer.StopInt[$MakeHot, $Gargoyle]; }; }; MakeAllHot: PUBLIC UserInputProc = { MakeSliceHot: PROC [slice: Slice] RETURNS [done: BOOL ¬ FALSE] = { GGSelect.SelectEntireSlice[slice, ggData.scene, hot]; }; triggerBag: TriggerBag ¬ ggData.hitTest.triggerBag; alignBag: AlignBag ¬ ggData.hitTest.alignBag; CodeTimer.StartInt[$MakeAllHot, $Gargoyle]; [] ¬ GGScene.WalkSlices[ggData.scene, first, MakeSliceHot]; Feedback.Append[ggData.router, oneLiner, $Feedback, "MakeAllHot: all objects made hot"]; GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeHot, ggData: ggData, remake: triggerBagNotSceneBag, edited: FALSE, okToSkipCapture: FALSE]; CodeTimer.StopInt[$MakeAllHot, $Gargoyle]; }; MakeCold: PUBLIC UserInputProc = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "MakeCold failed: select some objects for MakeCold"] ELSE { MakeSliceCold: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { GGSelect.DeselectSlice[sliceD.slice, sliceD.parts, ggData.scene, hot]; }; CodeTimer.StartInt[$MakeCold, $Gargoyle]; [] ¬ GGScene.WalkSelectedSlices[ggData.scene, first, MakeSliceCold, normal]; Feedback.Append[ggData.router, oneLiner, $Feedback, "MakeCold: selected objects made cold"]; GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeCold, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: FALSE]; CodeTimer.StopInt[$MakeCold, $Gargoyle]; }; }; MakeAllCold: PUBLIC UserInputProc = { MakeSliceCold: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { GGSelect.DeselectSlice[sliceD.slice, sliceD.parts, ggData.scene, hot]; }; CodeTimer.StartInt[$MakeAllCold, $Gargoyle]; [] ¬ GGScene.WalkSelectedSlices[ggData.scene, first, MakeSliceCold, hot]; Feedback.Append[ggData.router, oneLiner, $Feedback, "MakeCold: all objects made cold"]; GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeCold, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: FALSE]; CodeTimer.StopInt[$MakeAllCold, $Gargoyle]; }; DropAnchor: PUBLIC UserInputProc = { IF GGCaret.Exists[ggData.caret] THEN { IF GGCaret.Exists[ggData.anchor] THEN GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, anchor: TRUE] ELSE GGRefresh.NullStartBox[ggData]; GGCaret.Copy[to: ggData.anchor, from: ggData.caret]; [] ¬ GGAlign.CreateAnchorTrigger[ggData.anchor, ggData.hitTest.triggerBag]; Feedback.Append[ggData.router, oneLiner, $Feedback, "DropAnchor: anchor dropped"]; GGWindow.RestoreScreenAndInvariants[paintAction: $AnchorAdded, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "DropAnchor failed: caret needed to drop anchor"]; }; LiftAnchor: PUBLIC UserInputProc = { IF GGCaret.Exists[ggData.anchor] THEN { GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, anchor: TRUE, alignments: TRUE]; GGCaret.Kill[ggData.anchor]; Feedback.Append[ggData.router, oneLiner, $Feedback, "LiftAnchor: anchor lifted"]; GGWindow.RestoreScreenAndInvariants[paintAction: $AnchorRemoved, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: TRUE]; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "LiftAnchor failed: no anchor"]; }; StandardAlignments: PUBLIC UserInputProc = { StandardSlopes[ggData, event]; StandardAngles[ggData, event]; StandardRadii[ggData, event]; StandardDistances[ggData, event]; IF event.first = $StandardAlignments THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "StandardAlignments: standard alignments set"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: FALSE, okToSkipCapture: TRUE]; }; }; ClearAlignments: PUBLIC UserInputProc = { ClearSlopes[ggData, event]; ClearAngles[ggData, event]; ClearRadii[ggData, event]; ClearDistances[ggData, event]; IF event.first = $ClearAlignments THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "ClearAlignments: alignments cleared"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: FALSE, okToSkipCapture: TRUE]; }; }; AllAlignmentsOff: PUBLIC UserInputProc = { SlopePrompt[ggData, event]; AnglePrompt[ggData, event]; RadiusPrompt[ggData, event]; DistancePrompt[ggData, event]; GGState.SetMidpoints[ggData, FALSE]; IF event.first = $AllAlignmentsOff THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "AllAlignmentsOff: all alignments off"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: FALSE, okToSkipCapture: TRUE]; }; }; InitializeAlignments: PUBLIC UserInputProc = { AllAlignmentsOff[ggData, event]; IF event.first = $InitializeAlignments THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "InitializeAlignments: alignments initialized"]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: TRUE]; }; }; DisplayStyleChange: PUBLIC UserInputProc = { forward: BOOL ¬ event.rest.first = $FlipForward; camera: Camera ¬ ggData.camera; SetTextDisplayStyle: PROC [slice: Slice] RETURNS [done: BOOL ¬ FALSE] = { GGSlice.SetTextDisplayStyle[slice, camera.displayStyle, NIL]; }; GGState.CycleDisplayStyle[ggData, forward]; [] ¬ GGScene.WalkSlices[scene: ggData.scene, level: leaf, walkProc: SetTextDisplayStyle, classType: $Text]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Display style changed"]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; ToggleShowAlignments: PUBLIC UserInputProc = { GGState.SetShowAlignments[ggData, NOT GGState.GetShowAlignments[ggData]]; IF event.first = $ToggleShowAlignments THEN GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: TRUE]; }; SetShowAlignments: PUBLIC UserInputProc = { boolRope: Rope.ROPE ¬ NARROW[event.rest.first]; showAlignments: BOOL ¬ GGCoreOps.RopeToBool[boolRope]; GGState.SetShowAlignments[ggData, showAlignments]; }; ToggleMidpoints: PUBLIC UserInputProc = { GGState.SetMidpoints[ggData, NOT GGState.GetMidpoints[ggData]]; IF event.first = $ToggleMidpoints THEN GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: TRUE]; }; SetMidpoints: PUBLIC UserInputProc = { boolRope: Rope.ROPE ¬ NARROW[event.rest.first]; setMidpoints: BOOL ¬ GGCoreOps.RopeToBool[boolRope]; GGState.SetMidpoints[ggData, setMidpoints]; }; ToggleHeuristics: PUBLIC UserInputProc = { GGState.SetHeuristics[ggData, NOT GGState.GetHeuristics[ggData]]; IF event.first = $ToggleHeuristics THEN GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: TRUE]; }; SetHeuristics: PUBLIC UserInputProc = { boolRope: Rope.ROPE ¬ NARROW[event.rest.first]; setHeuristics: BOOL ¬ GGCoreOps.RopeToBool[boolRope]; GGState.SetHeuristics[ggData, setHeuristics]; }; SetDisplayStyle: PUBLIC UserInputProc = { styleRope: Rope.ROPE ¬ NARROW[event.rest.first]; style: DisplayStyle ¬ GGUIUtility.DisplayStyleFromRope[styleRope]; GGState.SetDisplayStyle[ggData, style]; }; ScaleUnitAux: PROC [ggData: GGData, distance: REAL, noisy: BOOL] = { IF distance <=0.0 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetUnits failed: zero or illegal unit value"] ELSE { currentEvent: HistoryEvent ¬ GGHistory.NewCurrent["Set scale unit", ggData]; -- start new history event; GGState.SetScaleUnit[ggData, distance, currentEvent]; -- in screen dots GGHistory.PushCurrent[ggData]; IF noisy THEN PrintScaleUnit[ggData, NIL]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: alignBag, edited: FALSE, okToSkipCapture: TRUE]; }; }; ScaleUnitFromSegment: PUBLIC UserInputProc = { IF GGScene.CountSelectedSlices[ggData.scene, leaf, normal]#1 THEN GOTO NoNoNoNoNO ELSE { distance: REAL ¬ 0.0; -- illegal scale unit firstSliceD: SliceDescriptor ¬ GGScene.FirstSelectedSlice[ggData.scene, leaf, normal]; segGen: SegmentGenerator ¬ GGSliceOps.SegmentsInDescriptor[firstSliceD]; firstSeg: Segment ¬ GGSliceOps.NextSegment[firstSliceD.slice, segGen].seg; nextSeg: Segment ¬ GGSliceOps.NextSegment[firstSliceD.slice, segGen].seg; IF firstSeg=NIL OR nextSeg#NIL THEN GOTO NoNoNoNoNO; distance ¬ Vectors2d.Distance[firstSeg.lo, firstSeg.hi]; ScaleUnitAux[ggData, distance, TRUE]; }; EXITS NoNoNoNoNO => { Feedback.Append[ggData.router, oneLiner, $Complaint, "UnitsFromSegment failed: select a single segment to set Units"]; }; }; ScaleUnitFromValue: PUBLIC UserInputProc = { radius: REAL; success: BOOL ¬ FALSE; [radius, success] ¬ GGState.GetRadiusValue[ggData]; IF success THEN ScaleUnitAux[ggData, radius*ggData.hitTest.scaleUnit, TRUE]; }; ScaleUnitFromSelection: PUBLIC UserInputProc = { distance: REAL ¬ WITH event.rest.first SELECT FROM real: REF REAL => real­, int: REF INT => REAL[int­], ENDCASE => -1.0; IF distance>reallyBigReal OR distance=LAST[INT] OR distance <= 0.0 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "UnitsFromSelection failed: select a positive real number (in inches) for unit"] ELSE ScaleUnitAux[ggData, distance*72.0, TRUE]; -- screen dots }; SetScaleUnit: PUBLIC UserInputProc = { points: REAL ¬ NARROW[event.rest.first, REF REAL]­; noisy: ATOM ¬ NARROW[event.rest.rest.first]; ScaleUnitAux[ggData, points, noisy = $Noisy]; }; InchScaleUnit: PUBLIC UserInputProc = { SetScaleUnit[ggData, LIST[$SetScaleUnit, NEW[REAL ¬ pointsPerIn], $Quiet]]; }; PrintScaleUnit: PUBLIC UserInputProc = { scale: REAL ¬ GGState.GetScaleUnit[ggData: ggData]; Feedback.PutFL[ggData.router, oneLiner, $Show, "Current scale is %g points = %g inches = %g centimeters", LIST[[real[scale]], [real[scale/pointsPerIn]], [real[scale/pointsPerCm]]] ]; }; standardSlopeDegrees: LIST OF REAL ¬ LIST[150.0, 135.0, 120.0, 90.0, 60.0, 45.0, 30.0, 0.0]; StandardSlopes: PUBLIC UserInputProc = { GGState.NewSlopeList[ggData, standardSlopeDegrees]; IF event.first = $StandardSlopes THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "Standard slopes installed"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: FALSE, okToSkipCapture: TRUE]; }; }; SetSlopes: PUBLIC UserInputProc = { slopeRope: Rope.ROPE; values: LIST OF REAL; on: LIST OF BOOL; slopeRope ¬ IF ISTYPE[event.rest.first, REF TEXT] THEN Rope.FromRefText[NARROW[event.rest.first]] ELSE NARROW[event.rest.first]; [----, values, on] ¬ GGDescribe.ScalarButtonValuesFromRope[slopeRope]; FOR nextValues: LIST OF REAL ¬ values, nextValues.rest UNTIL nextValues = NIL DO AddSlopeInternal[ggData, nextValues.first, on.first]; on ¬ on.rest; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; ClearSlopes: PROC [ggData: GGData, event: LIST OF REF] = { GGState.NewSlopeList[ggData, NIL]; IF event.first = $ClearSlopes THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "Slopes cleared"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: FALSE, okToSkipCapture: TRUE]; }; }; SlopePrompt: PUBLIC UserInputProc = { GGState.SetAllAlignmentStates[ggData, slope, FALSE]; IF event.first = $SlopePrompt THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "Slopes cleared"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; }; AddSlope: PUBLIC UserInputProc = { slope: REAL; success: BOOL; [slope, success] ¬ GGState.GetSlopeValue[ggData]; IF NOT success THEN RETURN; [] ¬ GGState.AddSlope[ggData, slope]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Slope added"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; AddSlopeInternal: PUBLIC PROC [ggData: GGData, degrees: REAL, on: BOOL ¬ FALSE] = { IF degrees<0.0 THEN degrees ¬ degrees+180.0; IF degrees=360.0 THEN degrees ¬ 0.0; ggData.measure.slopeViewValue ¬ degrees; [] ¬ GGState.AddSlope[ggData, degrees, on]; }; DeleteSlope: PUBLIC UserInputProc = { GGState.DeleteSelectedAlignments[ggData, slope]; ggData.measure.slopeViewValue ¬ Real.LargestNumber; -- invalidate cached value Feedback.Append[ggData.router, oneLiner, $Feedback, "Slopes deleted"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; GetSlope: PUBLIC UserInputProc = { IF GGScene.CountSelectedSlices[ggData.scene, leaf, normal]<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Select one or more segments for GetSlope"] ELSE { DoGetSlope: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { segGen: SegmentGenerator ¬ GGSliceOps.SegmentsInDescriptor[sliceD]; FOR nextSeg: Segment ¬ GGSliceOps.NextSegment[sliceD.slice, segGen].seg, GGSliceOps.NextSegment[sliceD.slice, segGen].seg UNTIL nextSeg=NIL DO [] ¬ GGState.AddSlope[ggData, GGMeasure.SlopeOfSegment[nextSeg]]; ENDLOOP; }; [] ¬ GGScene.WalkSelectedSlices[ggData.scene, leaf, DoGetSlope, normal]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Slopes added"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; }; ToggleSlope: PUBLIC UserInputProc = { menuValue: REAL; changedToOn: BOOL ¬ FALSE; paintAction: ATOM; remake: GGWindow.ForegroundParts ¬ alignBag; CodeTimer.StartInt[$ToggleSlope, $Gargoyle]; [menuValue, changedToOn, paintAction] ¬ ToggleAux[ggData, event, slope]; IF changedToOn THEN { alignObjects: LIST OF FeatureData; hideAlignments: BOOL ¬ NOT GGState.GetShowAlignments[ggData]; alignObjects ¬ GGAlign.IncrementalNewFilter[menuValue, slope, ggData.hitTest.triggerBag, hideAlignments, ggData.hitTest.alignBag]; GGRefresh.NoteNewForeground[ggData, alignObjects]; remake ¬ none; } ELSE remake ¬ alignBag; GGWindow.RestoreScreenAndInvariants[paintAction: paintAction, ggData: ggData, remake: remake, edited: TRUE, okToSkipCapture: TRUE]; CodeTimer.StopInt[$ToggleSlope, $Gargoyle]; }; ToggleAux: PROC [ggData: GGData, event: LIST OF REF, type: GGState.AlignmentType] RETURNS [menuValue: REAL ¬ 777.0, changedToOn: BOOL ¬ FALSE, paintAction: ATOM] = { argValue: REAL ¬ WITH event.rest.first SELECT FROM real: REF REAL => real­, int: REF INT => REAL[int­], ENDCASE => Real.LargestNumber; IF argValue>reallyBigReal OR argValue=LAST[INT] THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "ToggleAux: Unreasonable real argument"]; RETURN; }; [menuValue, changedToOn] ¬ GGState.ToggleAlignment[ggData, argValue, type]; IF changedToOn THEN paintAction ¬ $NewAlignmentsSelected ELSE paintAction ¬ $NewAlignmentsDeselected; }; standardAngles: LIST OF REAL ¬ LIST[90.0, 60.0, 45.0, 30.0, 0.0, -30.0, -45.0, -60.0, -90.0]; StandardAngles: PUBLIC UserInputProc = { GGState.NewAngleList[ggData, standardAngles]; IF event.first = $StandardAngles THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "Standard angles installed"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; }; SetAngles: PUBLIC UserInputProc = { angleRope: Rope.ROPE; values: LIST OF REAL; on: LIST OF BOOL; angleRope ¬ IF ISTYPE[event.rest.first, REF TEXT] THEN Rope.FromRefText[NARROW[event.rest.first]] ELSE NARROW[event.rest.first]; [----, values, on] ¬ GGDescribe.ScalarButtonValuesFromRope[angleRope]; FOR nextValues: LIST OF REAL ¬ values, nextValues.rest UNTIL nextValues = NIL DO AddAngleInternal[ggData, nextValues.first, on.first]; on ¬ on.rest; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; ClearAngles: PROC [ggData: GGData, event: LIST OF REF] = { GGState.NewAngleList[ggData, NIL]; IF event.first = $ClearAngles THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "Angles cleared"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; }; AnglePrompt: PUBLIC UserInputProc = { GGState.SetAllAlignmentStates[ggData, angle, FALSE]; IF event.first = $AnglePrompt THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "Angles cleared"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; }; AddAngle: PUBLIC UserInputProc = { angle: REAL; success: BOOL; [angle, success] ¬ GGState.GetAngleValue[ggData]; IF NOT success THEN RETURN; [] ¬ GGState.AddAngle[ggData, angle]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Angle added"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; AddAngleInternal: PUBLIC PROC [ggData: GGData, degrees: REAL, on: BOOL ¬ FALSE] = { IF degrees=360.0 THEN degrees ¬ 0.0; ggData.measure.angleViewValue ¬ degrees; [] ¬ GGState.AddAngle[ggData, degrees, on]; }; DeleteAngle: PUBLIC UserInputProc = { GGState.DeleteSelectedAlignments[ggData, angle]; ggData.measure.angleViewValue ¬ Real.LargestNumber; -- invalidate cached value Feedback.Append[ggData.router, oneLiner, $Feedback, "Angles deleted"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; GetAngle: PUBLIC UserInputProc = { IF GGScene.CountSelectedSlices[ggData.scene, leaf, normal]<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Select two or more segments for GetAngle"] ELSE { DoGetAngle: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { segGen: SegmentGenerator ¬ GGSliceOps.SegmentsInDescriptor[sliceD]; firstSeg, secondSeg: Segment ¬ NIL; degrees: REAL ¬ 0.0; FOR seg: Segment ¬ GGSliceOps.NextSegment[sliceD.slice, segGen].seg, GGSliceOps.NextSegment[sliceD.slice, segGen].seg UNTIL seg = NIL DO IF firstSeg=NIL THEN {firstSeg ¬ seg; LOOP;}; IF secondSeg=NIL THEN secondSeg ¬ seg; degrees ¬ GGMeasure.CounterClockwiseBetweenSegments[firstSeg, secondSeg]; [] ¬ GGState.AddAngle[ggData, degrees]; firstSeg ¬ secondSeg; secondSeg¬ NIL; ENDLOOP; }; [] ¬ GGScene.WalkSelectedSlices[ggData.scene, leaf, DoGetAngle, normal]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Angles added"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; }; ToggleAngle: PUBLIC UserInputProc = { menuValue: REAL; changedToOn: BOOL ¬ FALSE; paintAction: ATOM; remake: GGWindow.ForegroundParts ¬ alignBag; [menuValue, changedToOn, paintAction] ¬ ToggleAux[ggData, event, angle]; IF changedToOn THEN { remake ¬ alignBag; } ELSE remake ¬ alignBag; GGWindow.RestoreScreenAndInvariants[paintAction: paintAction, ggData: ggData, remake: remake, edited: TRUE, okToSkipCapture: TRUE]; }; standardRadii: LIST OF REAL ¬ LIST[1.0/18.0, 1.0/9.0, 0.125, 0.25, 1.0/3.0, 0.5, 2.0/3.0, 0.75, 1.0, 2.0, 4.0]; standardRadiiNames: LIST OF Rope.ROPE ¬ LIST["1/18", "1/9", "1/8", "1/4", "1/3", "1/2", "2/3", "3/4", "1", "2", "4"]; StandardRadii: PUBLIC UserInputProc = { GGState.NewRadiusList[ggData, standardRadiiNames, standardRadii]; IF event.first = $StandardRadii THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "Standard radii installed"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; }; SetRadii: PUBLIC UserInputProc = { radiiRope: Rope.ROPE; values: LIST OF REAL; on: LIST OF BOOL; names: LIST OF Rope.ROPE; radiiRope ¬ IF ISTYPE[event.rest.first, REF TEXT] THEN Rope.FromRefText[NARROW[event.rest.first]] ELSE NARROW[event.rest.first]; [names, values, on] ¬ GGDescribe.ScalarButtonValuesFromRope[radiiRope]; FOR nextValues: LIST OF REAL ¬ values, nextValues.rest UNTIL nextValues = NIL DO AddRadiusInternal[ggData, names.first, nextValues.first, on.first]; on ¬ on.rest; names ¬ names.rest; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; ClearRadii: PROC [ggData: GGData, event: LIST OF REF] = { GGState.NewRadiusList[ggData, NIL, NIL]; IF event.first = $ClearRadii THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "Radii cleared"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; }; RadiusPrompt: PUBLIC UserInputProc = { GGState.SetAllAlignmentStates[ggData, radius, FALSE]; IF event.first = $RadiusPrompt THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "Radii cleared"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; }; AddRadius: PUBLIC UserInputProc = { radius: REAL; success: BOOL; [radius, success] ¬ GGState.GetRadiusValue[ggData]; IF NOT success THEN RETURN; [] ¬ GGState.AddRadius[ggData, NIL, radius]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Radius added"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; AddRadiusInternal: PUBLIC PROC [ggData: GGData, name: Rope.ROPE, radius: REAL, on: BOOL ¬ FALSE] = { ggData.measure.radiusViewValue ¬ radius; [] ¬ GGState.AddRadius[ggData, name, radius, on]; }; DeleteRadius: PUBLIC UserInputProc = { GGState.DeleteSelectedAlignments[ggData, radius]; ggData.measure.radiusViewValue ¬ Real.LargestNumber; -- invalidate cached value Feedback.Append[ggData.router, oneLiner, $Feedback, "Radii deleted"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; GetRadius: PUBLIC UserInputProc = { IF GGScene.CountSelectedSlices[ggData.scene, leaf, normal]<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Select one or more segments for GetRadius"] ELSE { DoGetRadius: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { segGen: SegmentGenerator ¬ GGSliceOps.SegmentsInDescriptor[sliceD]; FOR nextSeg: Segment ¬ GGSliceOps.NextSegment[sliceD.slice, segGen].seg, GGSliceOps.NextSegment[sliceD.slice, segGen].seg UNTIL nextSeg=NIL DO radius: REAL ¬ GGMeasure.DistanceBetweenPoints[nextSeg.lo, nextSeg.hi]/ggData.hitTest.scaleUnit; [] ¬ GGState.AddRadius[ggData, NIL, radius]; ENDLOOP; }; [] ¬ GGScene.WalkSelectedSlices[ggData.scene, leaf, DoGetRadius, normal]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Radii added"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; }; ToggleRadius: PUBLIC UserInputProc = { menuValue: REAL; changedToOn: BOOL ¬ FALSE; paintAction: ATOM; remake: GGWindow.ForegroundParts ¬ alignBag; [menuValue, changedToOn, paintAction] ¬ ToggleAux[ggData, event, radius]; IF changedToOn THEN { remake ¬ alignBag; } ELSE remake ¬ alignBag; GGWindow.RestoreScreenAndInvariants[paintAction: paintAction, ggData: ggData, remake: remake, edited: TRUE, okToSkipCapture: TRUE]; }; standardDistances: LIST OF REAL = LIST [0.0, 1.0/18.0, 1.0/9.0, 0.5, 1.0]; standardDistanceNames: LIST OF Rope.ROPE = LIST ["0", "1/18", "1/9", "1/2", "1"]; StandardDistances: PUBLIC UserInputProc = { GGState.NewLineDistanceList[ggData, standardDistanceNames, standardDistances]; IF event.first = $StandardDistances THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "Standard distances installed"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; }; SetDistances: PUBLIC UserInputProc = { distanceRope: Rope.ROPE; values: LIST OF REAL; on: LIST OF BOOL; names: LIST OF Rope.ROPE; distanceRope ¬ IF ISTYPE[event.rest.first, REF TEXT] THEN Rope.FromRefText[NARROW[event.rest.first]] ELSE NARROW[event.rest.first]; [names, values, on] ¬ GGDescribe.ScalarButtonValuesFromRope[distanceRope]; FOR nextValues: LIST OF REAL ¬ values, nextValues.rest UNTIL nextValues = NIL DO AddDistanceInternal[ggData, names.first, nextValues.first, on.first]; on ¬ on.rest; names ¬ names.rest; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; ClearDistances: PROC [ggData: GGData, event: LIST OF REF] = { GGState.NewLineDistanceList[ggData, NIL, NIL]; IF event.first = $ClearDistances THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "Distances cleared"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; }; DistancePrompt: PUBLIC UserInputProc = { GGState.SetAllAlignmentStates[ggData, lineDistance, FALSE]; IF event.first = $DistancePrompt THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "Distances cleared"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; }; AddDistance: PUBLIC UserInputProc = { distance: REAL; success: BOOL; [distance, success] ¬ GGState.GetLineDistanceValue[ggData]; IF NOT success THEN RETURN; [] ¬ GGState.AddLineDistance[ggData, NIL, distance]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Distance added"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; AddDistanceInternal: PUBLIC PROC [ggData: GGData, name: Rope.ROPE, distance: REAL, on: BOOL ¬ FALSE] = { ggData.measure.lineDistViewValue ¬ distance; [] ¬ GGState.AddLineDistance[ggData, name, distance, on]; }; DeleteDistance: PUBLIC UserInputProc = { GGState.DeleteSelectedAlignments[ggData, lineDistance]; ggData.measure.lineDistViewValue ¬ Real.LargestNumber; -- invalidate cached value Feedback.Append[ggData.router, oneLiner, $Feedback, "Distances deleted"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; GetDistance: PUBLIC UserInputProc = { IF GGScene.CountSelectedSlices[ggData.scene, leaf, normal]<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Select one or more segments for GetDistance"] ELSE { DoGetDist: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { segGen: SegmentGenerator ¬ GGSliceOps.SegmentsInDescriptor[sliceD]; FOR nextSeg: Segment ¬ GGSliceOps.NextSegment[sliceD.slice, segGen].seg, GGSliceOps.NextSegment[sliceD.slice, segGen].seg UNTIL nextSeg=NIL DO distance: REAL ¬ GGMeasure.LengthOfSegment[nextSeg]/ggData.hitTest.scaleUnit; [] ¬ GGState.AddLineDistance[ggData, NIL, distance]; ENDLOOP }; [] ¬ GGScene.WalkSelectedSlices[ggData.scene, leaf, DoGetDist, normal]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Distances added"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: alignBag, edited: TRUE, okToSkipCapture: TRUE]; }; }; ToggleDistance: PUBLIC UserInputProc = { menuValue: REAL; changedToOn: BOOL ¬ FALSE; paintAction: ATOM; remake: GGWindow.ForegroundParts ¬ alignBag; [menuValue, changedToOn, paintAction] ¬ ToggleAux[ggData, event, lineDistance]; IF changedToOn THEN { remake ¬ alignBag; } ELSE remake ¬ alignBag; GGWindow.RestoreScreenAndInvariants[paintAction: paintAction, ggData: ggData, remake: remake, edited: TRUE, okToSkipCapture: TRUE]; }; MeasureSlopeHit: PUBLIC UserInputProc = { [] ¬ GGState.SelectSlope[ggData]; }; MeasureSlopeFromSelection: PUBLIC UserInputProc = { slope: REAL ¬ NARROW[event.rest.first, REF REAL]­; IF slope>reallyBigReal THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SlopeFromSelection failed: select a reasonable value for slope"]; RETURN; }; GGState.SetSlopeValue[ggData, slope]; AddSlope[ggData, LIST[$AddSlope]]; }; MeasureAngleHit: PUBLIC UserInputProc = { [] ¬ GGState.SelectAngle[ggData]; }; MeasureAngleFromSelection: PUBLIC UserInputProc = { angle: REAL ¬ NARROW[event.rest.first, REF REAL]­; IF angle>reallyBigReal THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "AngleFromSelection failed: select a reasonable value for angle"]; RETURN; }; GGState.SetAngleValue[ggData, angle]; AddAngle[ggData, LIST[$AddAngle]]; }; MeasureRadiusHit: PUBLIC UserInputProc = { [] ¬ GGState.SelectRadius[ggData]; }; MeasureRadiusFromSelection: PUBLIC UserInputProc = { radius: REAL ¬ NARROW[event.rest.first, REF REAL]­; IF radius>reallyBigReal THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "RadiusFromSelection failed: select a reasonable value for radius"]; RETURN; }; GGState.SetRadiusValue[ggData, radius]; AddRadius[ggData, LIST[$AddRadius]]; }; MeasureLineDistHit: PUBLIC UserInputProc = { [] ¬ GGState.SelectLineDistance[ggData]; }; MeasureLineDistFromSelection: PUBLIC UserInputProc = { lineDist: REAL ¬ NARROW[event.rest.first, REF REAL]­; IF lineDist>reallyBigReal THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "DistanceFromSelection failed: select a reasonable value for line distance"]; RETURN; }; GGState.SetLineDistanceValue[ggData, lineDist]; AddDistance[ggData, LIST[$AddDistance]]; }; DeleteCaretSegment: PUBLIC UserInputProc = { jointNum, newEndJoint: NAT; traj, shortTraj, smallerOutline: Slice; smallerOutlines: LIST OF Slice; trajEnd: TrajEnd; chairD, trajD: SliceDescriptor; delSeq: TrajParts; bBoxOfTraj: BoundBox; point: Point; normal: Vector; caret: Caret ¬ ggData.caret; scene: Scene ¬ ggData.scene; IF GGCaret.SittingOnEnd[caret] THEN { -- delete the joint and segment near the caret success, isHot: BOOL ¬ FALSE; delD: SliceDescriptor; oldOutline: Slice; chairD ¬ GGCaret.GetChair[caret]; [success, trajD, jointNum] ¬ GGSliceOps.UnpackJoint[chairD]; IF NOT success THEN ERROR Problem[msg: "Chair not simple descriptor"]; traj ¬ trajD.slice; oldOutline ¬ GGParent.GetParent[traj]; bBoxOfTraj ¬ GGSliceOps.GetBoundBox[traj]; isHot ¬ GGSelect.IsSelectedInPart[traj, ggData.scene, hot]; SELECT jointNum FROM 0 => {newEndJoint ¬ 1; trajEnd ¬ lo}; GGTraj.HiJoint[traj] => {newEndJoint ¬ jointNum -1; trajEnd ¬ hi}; ENDCASE => ERROR Problem[msg: "failed assertion"]; point ¬ GGTraj.FetchJointPos[traj, newEndJoint]; normal ¬ GGTraj.FetchJointNormal[traj, newEndJoint]; delSeq ¬ GGSequence.LastSegAndJoint[NARROW[traj.data, TrajData], trajEnd]; GGHistory.NewCapture["Backspace segment", ggData]; -- capture scene BEFORE UPDATE GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, caret: TRUE, attractor: TRUE, hotCPs: isHot, alignments: isHot]; GGSelect.SaveSelectionsInSliceAllClasses[chairD.slice, scene]; delD ¬ GGSlice.DescriptorFromParts[traj, delSeq]; smallerOutlines ¬ GGTraj.DeleteSequence[delD].openTrajOutlines; smallerOutline ¬ IF smallerOutlines = NIL THEN NIL ELSE smallerOutlines.first; IF smallerOutline = NIL THEN { -- we removed the last segment GGCaret.SetAttractor[ggData.caret, point, normal, NIL]; GGCaret.SitOn[caret, NIL]; IF GGScene.IsTopLevel[oldOutline] THEN { GGScene.DeleteSlice[scene, oldOutline]; } ELSE { cluster: Slice ¬ GGParent.GetParent[oldOutline]; smallerCluster: Slice ¬ GGSlice.RemoveChild[cluster, oldOutline]; IF smallerCluster = NIL THEN GGSlice.PruneNullClusters[scene, cluster]; }; } ELSE { jointParts: TrajParts; jointD, outD: SliceDescriptor; priority: INT; IF GGScene.IsTopLevel[oldOutline] THEN { priority ¬ GGScene.GetPriority[scene, oldOutline]; GGScene.DeleteSlice[scene, oldOutline]; GGScene.AddSlice[scene, smallerOutline, priority]; GGSelect.ReselectSliceAllClasses[smallerOutline, scene]; } ELSE { cluster: Slice ¬ GGParent.GetParent[oldOutline]; priority ¬ GGParent.GetChildPriority[cluster, oldOutline]; [] ¬ GGSlice.RemoveChild[cluster, oldOutline]; GGSlice.AddChildToCluster[cluster, smallerOutline, priority]; GGSelect.ReselectSliceAllClasses[cluster, scene]; }; shortTraj ¬ GGParent.FirstChild[smallerOutline, first, $Traj]; jointParts ¬ GGSequence.CreateFromJoint[NARROW[shortTraj.data], IF trajEnd = lo THEN 0 ELSE newEndJoint]; jointD ¬ GGSlice.DescriptorFromParts[shortTraj, jointParts]; -- traj descriptor outD ¬ GGParent.TopLevelDescriptorFromChildDescriptor[jointD]; GGSelect.SelectSlice[outD, ggData.scene, normal]; GGCaret.SitOn[ggData.caret, outD]; GGCaret.SetAttractor[ggData.caret, point, normal, outD]; }; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.Append[ggData.router, oneLiner, $Feedback, "Backspace segment: caret segment deleted"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "Delete segment failed: \"joint select\" the end of an open trajectory to delete"]; }; Nop: UserInputProc ~ { }; RegisterEventProcs: PROC = { OPEN GGUserInput; RegisterAction[$OneScroll, OneScroll, rope2, FALSE, FALSE]; RegisterAction[$OneZoom, OneZoom, rope, FALSE, FALSE]; RegisterAction[$ScalePop, ScalePop, rope2]; RegisterAction[$RotatePop, RotatePop, rope2]; RegisterAction[$FitPop, FitPop, none]; RegisterAction[$ResetPop, ResetPop, none]; RegisterAction[$EdgePop, EdgePop, none]; RegisterAction[$PrevTransform, PrevTransform, none]; RegisterAction[$CenterSel, CenterSel, none]; RegisterAction[$CenterCaret, CenterCaret, none]; RegisterAction[$FitSel, FitSel, none]; RegisterAction[$SetBiScrollersTransform, SetBiScrollersTransform, rope]; RegisterAction[$ShowBiScrollersTransform, ShowBiScrollersTransform, none]; RegisterAction[$AlignFracs, ScrollBar, none]; RegisterAction[$Shift, ScrollBar, none]; RegisterAction[$ViewersPaintEntireScene, ViewersPaintEntireScene, none]; RegisterAction[$ViewersPaintAllPlanes, ViewersPaintAllPlanes, none]; RegisterAction[$MakeHot, MakeHot, none]; RegisterAction[$MakeAllHot, MakeAllHot, none]; RegisterAction[$MakeCold, MakeCold, none]; RegisterAction[$MakeAllCold, MakeAllCold, none]; RegisterAction[$DropAnchor, DropAnchor, none]; RegisterAction[$LiftAnchor, LiftAnchor, none]; RegisterAction[$StandardAlignments, StandardAlignments, none]; RegisterAction[$ClearAlignments, ClearAlignments, none]; RegisterAction[$AllAlignmentsOff, AllAlignmentsOff, none]; RegisterAction[$InitializeAlignments, InitializeAlignments, none]; RegisterAction[$ScreenChoiceChange, DisplayStyleChange, none]; RegisterAction[$ToggleShowColors, Nop, none, FALSE]; RegisterAction[$SetShowColors, Nop, none, FALSE]; RegisterAction[$ToggleMidpoints, ToggleMidpoints, none]; RegisterAction[$SetMidpoints, SetMidpoints, none, FALSE]; RegisterAction[$ToggleHeuristics, ToggleHeuristics, none]; RegisterAction[$SetHeuristics, SetHeuristics, none, FALSE]; RegisterAction[$ToggleShowAlignments, ToggleShowAlignments, none]; RegisterAction[$SetShowAlignments, SetShowAlignments, none]; RegisterAction[$SetDisplayStyle, SetDisplayStyle, rope]; RegisterAction[$RadiusUnitFromSegment, ScaleUnitFromSegment, none]; RegisterAction[$RadiusUnitFromValue, ScaleUnitFromValue, none]; RegisterAction[$RadiusUnitFromSelection, ScaleUnitFromSelection, refReal]; RegisterAction[$InchScaleUnit, InchScaleUnit, none]; RegisterAction[$SetScaleUnit, SetScaleUnit, none]; RegisterAction[$PrintScaleUnit, PrintScaleUnit, none]; RegisterAction[$SlopePrompt, SlopePrompt, none]; RegisterAction[$SetSlopes, SetSlopes, none]; RegisterAction[$AddSlope, AddSlope, none]; RegisterAction[$GetSlope, GetSlope, none]; RegisterAction[$ToggleSlope, ToggleSlope, none]; RegisterAction[$DeleteSlope, DeleteSlope, none]; RegisterAction[$AnglePrompt, AnglePrompt, none]; RegisterAction[$SetAngles, SetAngles, none]; RegisterAction[$AddAngle, AddAngle, none]; RegisterAction[$GetAngle, GetAngle, none]; RegisterAction[$ToggleAngle, ToggleAngle, none]; RegisterAction[$DeleteAngle, DeleteAngle, none]; RegisterAction[$RadiusPrompt, RadiusPrompt, none]; RegisterAction[$SetRadii, SetRadii, none]; RegisterAction[$AddRadius, AddRadius, none]; RegisterAction[$GetRadius, GetRadius, none]; RegisterAction[$ToggleRadius, ToggleRadius, none]; RegisterAction[$DeleteRadius, DeleteRadius, none]; RegisterAction[$DistancePrompt, DistancePrompt, none]; RegisterAction[$SetDistances, SetDistances, none]; RegisterAction[$AddDistance, AddDistance, none]; RegisterAction[$GetDistance, GetDistance, none]; RegisterAction[$ToggleDistance, ToggleDistance, none]; RegisterAction[$DeleteDistance, DeleteDistance, none]; RegisterAction[$MeasureSlopeHit, MeasureSlopeHit, none]; RegisterAction[$MeasureSlopeFromSelection, MeasureSlopeFromSelection, refReal]; RegisterAction[$MeasureAngleHit, MeasureAngleHit, none]; RegisterAction[$MeasureAngleFromSelection, MeasureAngleFromSelection, refReal]; RegisterAction[$MeasureRadiusHit, MeasureRadiusHit, none]; RegisterAction[$MeasureRadiusFromSelection, MeasureRadiusFromSelection, refReal]; RegisterAction[$MeasureLineDistHit, MeasureLineDistHit, none]; RegisterAction[$MeasureLineDistFromSelection, MeasureLineDistFromSelection, refReal]; RegisterAction[$DeleteCaretSegment, DeleteCaretSegment, none]; }; RegisterEventProcs[]; END. : GGEventImplB.mesa Copyright Σ 1988, 1991, 1992 by Xerox Corporation. All rights reserved. Contents: Once an event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module. Pier, October 2, 1992 5:28 pm PDT Bier, June 23, 1993 9:47 pm PDT Doug Wyatt, April 16, 1992 4:33 pm PDT BiScrollers Operations GetBiScroller: PROC [ggData: GGData] RETURNS [bs: BiScrollers.BiScroller] = { RETURN [IF WindowBigEnough[ggData] THEN GGState.GetBiScroller[ggData] ELSE NIL]; -- NIL means viewer is iconic or pathologically small }; Mostly copied from GGWindowImpl.GGExtremaProc bs.style.ChangeTransform[bs: bs, new: vanilla, ageOp: remember, paint: FALSE]; bs.style.ChangeTransform[bs: bs, new: bs.class.common.vanilla[bs], ageOp: remember, paint: FALSE]; IF key = $Center THEN remember ELSE ignore -- can't express this in BiScrollers.Align BiScrollers.Align[ bs: bs, client: [variant: coord[x: caretPos.x, y: caretPos.y]], viewer: [variant: fraction[fx: 0.5, fy: 0.5]], paint: FALSE]; BiScrollers.Align[ bs: bs, client: [variant: coord[x: (bBox.loX+bBox.hiX)/2.0, y: (bBox.loY+bBox.hiY)/2.0]], viewer: [variant: fraction[fx: 0.5, fy: 0.5]], paint: FALSE]; BiScrollers.Align[ bs: bs, client: [variant: coord[x: caretPos.x, y: caretPos.y]], viewer: [variant: fraction[fx: 0.5, fy: 0.5]], paint: FALSE]; cToV ¬ BiScrollers.GetStyle[].GetTransforms[bs].clientToViewer; vBox ¬ BiScrollers.ViewportBox[bs]; BiScrollers.BoxScale[bs: bs, from: tbBox, to: tvBox, paint: FALSE]; Change scale and offsets so that client data previously in `from' covers as much as possible of `to' (from and to in viewer coords). bs.style.ChangeTransform[ bs: bs, new: bs.style.GetTransforms[bs, previous].clientToViewer, ageOp: remember, paint: FALSE]; BiScrollers.DoBSUserAction[bs, newEvent]; ggData.embed.scrollDue is updated in GGUserImpl.ProcessAndQueueEvent. GGState.BiScrollersTransform[ggData, ggData.embed.scrollDue]; ggData.embed.scrollDue ¬ ImagerTransformation.Scale[1.0]; ggData.embed.scrollDue is updated in GGUserImpl.ProcessAndQueueEvent. this code implements the following behavior: scrolling while DuringCaretPos leaves the caret at the same place in the viewer while the scroll happens. GGState.BiScrollersTransform[ggData, ggData.embed.scrollDue]; ggData.embed.scrollDue ¬ ImagerTransformation.Scale[1.0]; PROC [ggData: GGData, event: LIST OF REF]; PROC [ggData: GGData, event: LIST OF REF]; Alignment Operations Make all normal selected slices hot. Fix the trigger bags, object bags, and Foreground plane (for efficiency). Make all slices hot. Make all normal selected slices cold. Find all hot slices and make them cold. Does AllAlignmentsOff, turns alignment processing on, sets the gravity extent to a default value, turns gravity on, sets gravity type to PreferPoints, resets the radius unit and turns heuristics on. This is done before creating or playing a session log to get repeatable results. Screen style Units Menu Slope Line adds slope from angle viewer in measure line If slopeViewValue was set internally, viewer and value will be consistent. If SlopeValue viewer was set by typein, viewer and value will be inconsistent. only positive slopes, please Gets selected segment slopes, adds to slope menu and turns it on Angle Line adds angle from angle viewer in measure line See comments in AddSlope Gets selected segment angles, adds to angle menu and turns it on Radius Line adds radius from Radius viewer in measure line See comments in AddSlope Distance Line adds distance from LineDistance viewer in measure line See comments in AddSlope Coordinate/Measure Line Miscellaneous since the attractor is the outline containing the trajectory, this is all the startBox we should need. Build a descriptor for the new chair. ggData.refresh.startBoundBox­ ¬ bBoxOfTraj­; see comment in GGRefresh.SetStartBox call, above Trackball Actions BiScrollers Menu Alignment Operations Setting Parts of the Gargoyle State Unit Line Slope Line Angle Line Radius Line Distance Line Coordinate Line TBox: PROC RETURNS [b, t: BoundBox] = { if: InputFocus.Focus _ InputFocus.GetInputFocus[]; ggData: GGData ¬ NARROW[BiScrollers.ClientDataOfViewer[if.owner]]; sliceD: SliceDescriptor ¬ GGScene.FirstSelectedSlice[ggData.scene, first, normal, $Text]; b _ GGSliceOps.GetBoundBox[sliceD.slice, NIL]; t _ GGSliceOps.GetTightBox[sliceD.slice, NIL]; IF TRUE THEN RETURN; }; Κ3X•NewlineDelimiter –(cedarcode) style™code™Kšœ Οeœ=™HKšΟnœx™€K™!K™K™&K™—šΟk ˜ Jšœ‚Ÿœ!˜₯K˜—šž œŸœŸ˜JšŸœΏŸœ˜ΩKšŸœ-Ÿ˜9—˜Kšœ Ÿœ˜+KšœŸœ#˜-Kšœ Ÿœ˜'KšœŸœ˜#KšœŸœ˜%KšœŸœŸœΟc˜EKšœŸœ˜/KšœŸœŸœ œ ˜VKšœ Ÿœ ˜1Kšœ Ÿœ˜*Kšœ Ÿœ˜(KšœŸœ˜'Kšœ Ÿœ ˜1KšœŸœ˜1KšœŸœ˜!KšœŸœŸœ!˜K˜:KšœŸœ˜KšœŸœ˜K˜K˜K˜K˜K˜K˜K˜K˜,K˜,K˜CK˜CK˜*K˜K˜9K˜K˜—šžœŸœ0ŸœŸœ˜eK˜K˜šŸœŸ˜KšŸœ!˜"KšŸœ!˜"KšŸœŸœ˜—K˜K˜—šΠbn œŸœ"Ÿœ˜PK™-Kšœ˜K˜\Kš œŸœŸœŸœTŸœ˜‡K˜9K˜K˜—Kšœ Ÿœ ˜Kšœ Ÿœ ˜K˜šžœŸœ˜"Kšœ Ÿœ˜*šŸœŸœ Ÿœ˜K˜JK˜—šŸœ˜KšœŸœ˜šŸœŸ˜šœ ˜ K˜9KšœŸœ˜KšœŸœ˜K˜K˜VKšœGŸœ™NKšœ1Ÿœ ˜BKšœLŸœŸœŸœ ˜rK˜—šœ˜K˜9KšœŸœ˜KšœŸœ˜K˜K˜VKšœ[Ÿœ™bKšœ1Ÿœ ˜BK˜—Kšœ Ÿœ˜KšŸœŸœ˜—šŸœŸ˜šœ˜˜K˜Kšœ˜Kšœ˜KšœŸœ˜ KšœŸœ˜ Kšœ˜—KšœLŸœŸœŸœ˜`K˜—KšŸœŸœ Ÿœ2™UKšœ Ÿœ˜KšŸœŸœ˜—K˜—K˜K˜—šžœŸœ˜!Kšœ Ÿœ˜*šŸœŸœ Ÿœ˜K˜JK˜—šŸœ˜KšœŸœ˜K˜EKšœ ŸœŸœ˜šŸœŸ˜KšœŸœ˜#KšœŸœ˜$KšœŸœ˜%KšœŸœ˜"K˜#K˜%K˜#K˜'K˜$K˜&K˜$K˜'KšŸœŸœ˜—˜K˜K˜ K˜ K˜ K˜ Kšœ˜—KšœLŸœŸœŸœ˜`K˜—K˜K˜—šž œŸœ˜#Kšœ Ÿœ˜*šŸœŸœ Ÿœ˜K˜JK˜—šŸœ˜K˜B–œ[bs: BiScrollers.BiScroller, client: BiScrollers.Location, viewer: BiScrollers.Location, doX: BOOL _ TRUE, doY: BOOL _ TRUE, paint: BOOL _ TRUE]šŸœ Ÿœ˜šŸœŸœ˜&K˜1˜K˜K˜7K˜.K˜ K˜ Kšœ˜—šœ™Kšœ™Kšœ7™7Kšœ.™.KšœŸœ™—KšœLŸœŸœŸœ˜`K˜—KšŸœ˜ƒK˜—šŸœ˜šœ™Kšœ™KšœQ™QKšœ.™.KšœŸœ™—˜K˜K˜QK˜.KšœŸœ˜ KšœŸœ˜ Kšœ˜—KšœLŸœŸœŸœ˜`K˜—K˜—K˜K˜—šž œŸœ˜%Kšœ Ÿœ˜*šŸœŸœ Ÿœ˜K˜JK˜—šŸœ˜šŸœŸœ˜&K˜1šœ™Kšœ™Kšœ7™7Kšœ.™.KšœŸœ™—˜K˜K˜7K˜.KšœŸœ˜ KšœŸœ˜ Kšœ˜—KšœLŸœŸœŸœ˜`K˜—KšŸœk˜oK˜—K˜K˜—šžœŸœ˜ Kšœ Ÿœ˜*šŸœŸœ Ÿœ˜K˜JK˜—šŸœ˜Kšœ ˜ K˜K˜VKšŸœ Ÿœf˜wšŸœ˜K™?K˜/K–O[m: ImagerTransformation.Transformation, r: ImagerTransformation.Rectangle]˜_K™#K˜#K˜<šœ<Ÿœ™CKšœ„™„—KšœOŸœ˜UKšœLŸœŸœŸœ˜`K˜—K˜—K˜K˜—šž œŸœ˜'Kšœ Ÿœ˜*šŸœŸœ Ÿœ˜K˜JK˜—šŸœ˜šœ™Kšœ™Kšœ9™9Kšœ™KšœŸœ™—K˜-KšœLŸœŸœŸœ˜`K˜—K˜K˜—šžœŸœ˜1Kšœ ŸœŸœ˜+KšœkŸœ˜}Kšœ8˜8šŸ˜Kšœ«˜«—K˜K˜—šžœŸœ˜2K˜PKšœŽ˜ŽK˜K˜—šž œ˜Kš œ ŸœŸœŸœŸœ -˜ZKšœ)™)K˜)KšœLŸœŸœŸœ˜`K˜K˜—šžœ˜KšœE™EKšΠbkΟb$’£’£˜2Kšœ=™=K™9K˜>KšœLŸœŸœŸœ˜`K˜K˜—KšœE™EKšœ;˜;šž œŸœŸœŸœ˜7KšŸœ*˜0K˜K˜—šž œ˜K™–Kšœ/˜/Kšœ˜KšœΟuœ˜Kšœ€œ˜K˜KšŸœ$ŸœŸœ˜2K˜K˜9K˜,Kšœ€œ>˜IK˜Kšœ=™=K™9K˜>KšœLŸœŸœŸœ˜`K˜K˜IKšœ€œ7€œ˜JKšœ!Ÿœ Ÿœ€œ˜JK˜—K˜šžœŸœ˜1KšŸœŸœŸœŸœ™*KšœLŸœŸœŸœ˜`K˜K˜—šžœŸœ˜/KšŸœŸœŸœŸœ™*KšœJŸœŸœŸœ˜^K˜K˜—K™K™šžœŸœ˜!KšŸœ-Ÿœh˜›šŸœ˜š ž œŸœŸœŸœŸœ˜MKšœ+£œ˜0K˜—š žœŸœŸœŸœŸœ˜ZK˜HKšœŸœŸœ ˜"KšœHŸœ˜fKšœ3 ˜JK˜K˜—KšœŸœ%˜9K˜3K˜-K˜:K™K˜(Kšœ$™$KšœC£œ˜KKšœI™IKšœP£œ˜UK˜ZKšœjŸœŸœ˜‰K˜'K˜—K˜K˜—šž œŸœ˜$š ž œŸœŸœŸœŸœ˜BKšœ0£œ˜5K˜—K˜K˜3K˜-K˜Kšœ+˜+Kšœ™Kšœ&£œ˜;KšœX˜XKšœ{ŸœŸœ˜šKšœ*˜*K˜K˜—šžœŸœ˜"KšŸœ-Ÿœj˜šŸœ˜š ž œŸœŸœŸœŸœ˜NKšœA£œ˜FK˜K˜—Kšœ)˜)Kšœ%™%KšœD£œ˜LKšœ\˜\KšœqŸœŸœ˜Kšœ(˜(K˜—K˜K˜—šž œŸœ˜%š ž œŸœŸœŸœŸœ˜NKšœF˜FK˜—K˜Kšœ,˜,Kš£'™'KšœD£œ˜IKšœW˜WKšœqŸœŸœ˜Kšœ+˜+K˜K˜—šž œŸœ˜$šŸœŸœ˜&šŸœŸœ7Ÿœ Ÿœ˜pKšŸœ ˜$—Kšœ4˜4K˜KKšœR˜RKš œ2£ œ£œ ŸœŸœ˜†K˜—KšŸœh˜lK˜K˜—šž œŸœ˜$šŸœŸœ˜'Kšœ6Ÿœ ŸœŸœ˜]Kšœ˜KšœQ˜QKšœY£ œ ŸœŸœ˜ŠK˜—KšŸœV˜ZK˜K™—šžœŸœ˜,Kšœ˜Kšœ˜Kšœ˜Kšœ!˜!šŸœ#Ÿœ˜+Kšœc˜cKšœuŸœŸœ˜“K˜—K˜K˜—šžœŸœ˜)Kšœ˜Kšœ˜Kšœ˜Kšœ˜šŸœ Ÿœ˜(Kšœ\˜\KšœuŸœŸœ˜“K˜—K˜K˜—šžœŸœ˜*Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ£ œ Ÿœ˜$šŸœ!Ÿœ˜)Kšœ\˜\KšœuŸœŸœ˜“K˜—K˜K˜—šžœŸœ˜.Kšœ˜™˜Kšœ ˜ šŸœ%Ÿœ˜-Kšœd˜dKšœpŸœŸœ˜ŽK˜—Kšœ˜—K˜K™ šžœŸœ˜,Kšœ Ÿœ#˜0K˜š žœŸœŸœŸœŸœ˜IKšœ8Ÿœ˜=K˜—Kšœ£œ˜+K˜kKšœM˜MKšœjŸœŸœ˜ˆKšœ˜K˜—šžœŸœ˜.Kšœ"Ÿœ$˜IKšŸœ%ŸœqŸœŸœ˜ΊK˜K˜—šžœŸœ˜+KšœŸœŸœ˜/KšœŸœ"˜6Kšœ2˜2K˜K˜—šžœŸœ˜)KšœŸœ˜?KšŸœ ŸœeŸœŸœ˜©K˜K˜—šž œŸœ˜&KšœŸœŸœ˜/KšœŸœ"˜4Kšœ+˜+K˜K˜—šžœŸœ˜*KšœŸœ ˜AKšŸœ!ŸœqŸœŸœ˜ΆK˜K˜—šž œŸœ˜'KšœŸœŸœ˜/KšœŸœ"˜5Kšœ-˜-K˜K˜—šžœŸœ˜)KšœŸœŸœ˜0K˜BKšœ'˜'K˜—K˜K™ šž œŸœŸœ Ÿœ˜DKšŸœŸœd˜{šŸœ˜Kšœ&£ œΟtœ  ˜hKšœ6 ˜GKšœ £ œ ˜K–§[paintAction: ATOM, gargoyleData: GGInterfaceTypes.GargoyleData, remake: GGWindow.ForegroundParts _ triggerBag, backgndOK: BOOL _ FALSE, edited: BOOL _ TRUE]šŸœŸœŸœ˜*KšœsŸœŸœ˜‘K˜—K˜K™—šžœŸœ˜.KšŸœ;ŸœŸœ ˜QšŸœ˜Kšœ Ÿœ ˜+K˜VK–([sliceD: GGModelTypes.SliceDescriptor]˜HK˜JK˜IKš Ÿœ ŸœŸœ ŸœŸœŸœ ˜4K˜8KšœŸœ˜%K˜—šŸ˜˜Kšœv˜vK˜——K˜K™—šžœŸœ˜,KšœŸœ˜ Kšœ ŸœŸœ˜K˜3KšŸœ Ÿœ7Ÿœ˜LK˜K˜—šžœŸœ˜0šœ ŸœŸœŸœŸ˜2KšœŸœŸœ ˜KšœŸœŸœŸœ˜KšŸœ ˜—Kš ŸœŸœ ŸœŸœŸœŸœ†˜ΝKšŸœ%Ÿœ ˜>K˜K˜—šž œŸœ˜&Kš œŸœŸœŸœŸœ˜3KšœŸœŸœ˜,Kšœ-˜-K˜K˜—šž œŸœ˜'KšœŸœŸœŸœ˜KK˜K˜—šžœŸœ˜(K–#[ggData: GGInterfaceTypes.GGData]šœŸœ(˜3KšœjŸœI˜·K˜—K˜K™ Kš œŸœŸœŸœŸœ3˜\šžœŸœ˜(Kšœ3˜3šŸœŸœ˜'K˜QKšœuŸœŸœ˜“K˜—K˜K˜—šž œŸœ˜#KšœŸœ˜KšœŸœŸœŸœ˜KšœŸœŸœŸœ˜Kšœ ŸœŸœŸœŸœŸœŸœŸœŸœ˜€Kšœ œA˜Fš Ÿœ ŸœŸœŸœŸœŸœŸ˜PKšœ5˜5K˜ KšŸœ˜—KšœsŸœŸœ˜K˜K˜—š ž œŸœŸœŸœŸœ˜:KšœŸœ˜"šŸœŸœ˜$K˜FKšœuŸœŸœ˜“K˜—K˜K˜—šž œŸœ˜%Kšœ-Ÿœ˜4šŸœŸœ˜$K˜FKšœuŸœŸœ˜’Kšœ˜—Kšœ˜K˜—šžœŸœ˜"™-Kš£J™JKš£N™N—KšœŸœ˜ Kšœ Ÿœ˜K˜1KšŸœŸœ ŸœŸœ˜K˜%K˜CKšœsŸœŸœ˜K˜K˜—š žœŸœŸœŸœŸœŸœ˜SKš£™KšŸœ Ÿœ˜,KšŸœŸœ˜$K˜(K˜+K˜K˜—šž œŸœ˜%Kšœ0˜0Kšœ4 ˜NK˜FKšœuŸœŸœ˜’Kšœ˜K˜—šžœŸœ˜"K™@KšŸœ;Ÿœa˜’šŸœ˜š ž œŸœŸœŸœŸœ˜KK–([sliceD: GGModelTypes.SliceDescriptor]˜CšŸœwŸœ ŸœŸ˜ŽK˜AKšŸœ˜—K˜—K˜HKšœD˜DKšœsŸœŸœ˜K˜—K˜K˜—šž œŸœ˜%Kšœ Ÿœ˜Kšœ ŸœŸœ˜Kšœ Ÿœ˜K˜,Kšœ,˜,K˜HšŸœ Ÿœ˜KšœŸœŸœ ˜"KšœŸœŸœ#˜=K˜‚Kšœ2˜2K˜K˜—KšŸœ˜KšœfŸœŸœ˜ƒKšœ+˜+K˜K˜—šž œŸœŸœŸœŸœŸœ ŸœŸœŸœŸœ˜₯šœ ŸœŸœŸœŸ˜2KšœŸœŸœ ˜KšœŸœŸœŸœ˜KšŸœ˜—š ŸœŸœ ŸœŸœŸœ˜6Kšœ^˜^KšŸœ˜K˜—K˜KKšŸœ Ÿœ%˜8KšŸœ(˜,K˜—K˜K™ Kš œŸœŸœŸœŸœ:˜]šžœŸœ˜(Kšœ-˜-šŸœŸœ˜'K˜QKšœuŸœŸœ˜’K˜—K˜K˜—šž œŸœ˜#KšœŸœ˜KšœŸœŸœŸœ˜KšœŸœŸœŸœ˜Kšœ ŸœŸœŸœŸœŸœŸœŸœŸœ˜€Kšœ œA˜Fš Ÿœ ŸœŸœŸœŸœŸœŸ˜PKš£œ%˜5K˜ KšŸœ˜—KšœsŸœŸœ˜K˜K˜—š ž œŸœŸœŸœŸœ˜:KšœŸœ˜"šŸœŸœ˜$K˜FKšœuŸœŸœ˜’K˜—K˜K˜—šž œŸœ˜%Kšœ-Ÿœ˜4šŸœŸœ˜$K˜FKšœuŸœŸœ˜’Kšœ˜—Kšœ˜K˜—šžœŸœ˜"K™-K™KšœŸœ˜ Kšœ Ÿœ˜K˜1KšŸœŸœ ŸœŸœ˜K˜%KšœC˜CKšœsŸœŸœ˜K˜K˜—š žœŸœŸœŸœŸœŸœ˜SKšŸœŸœ˜$K˜(K˜+K˜K˜—šž œŸœ˜%Kšœ0˜0Kšœ4 ˜NK˜FKšœuŸœŸœ˜’Kšœ˜K˜—šžœŸœ˜"K™@šŸœ;Ÿ˜AKšœ`˜`—šŸœ˜š ž œŸœŸœŸœŸœ˜KK–([sliceD: GGModelTypes.SliceDescriptor]˜CKšœŸœ˜#Kšœ Ÿœ˜šŸœsŸœŸœŸ˜ˆKšŸœ ŸœŸœŸœ˜-KšŸœ ŸœŸœ˜&K˜IK˜'K˜Kšœ Ÿœ˜KšŸœ˜—K˜—K˜HKšœD˜DKšœsŸœŸœ˜K˜—K˜K˜—šž œŸœ˜%Kšœ Ÿœ˜Kšœ ŸœŸœ˜Kšœ Ÿœ˜K˜,K˜HšŸœ Ÿœ˜K˜K˜—KšŸœ˜KšœfŸœŸœ˜ƒK˜—K˜K™ Kš œŸœŸœŸœŸœM˜oKš œŸœŸœŸœŸœI˜ušž œŸœ˜'KšœA˜AšŸœŸœ˜&K˜PKšœuŸœŸœ˜’K˜—K˜K˜—šžœŸœ˜"KšœŸœ˜KšœŸœŸœŸœ˜KšœŸœŸœŸœ˜KšœŸœŸœŸœ˜Kšœ ŸœŸœŸœŸœŸœŸœŸœŸœ˜€K˜Gš Ÿœ ŸœŸœŸœŸœŸœŸ˜PKš£œ2˜CK˜ K˜KšŸœ˜—KšœsŸœŸœ˜K˜K˜K˜—š ž œŸœŸœŸœŸœ˜9KšœŸœŸœ˜(šŸœŸœ˜#KšœE˜EKšœuŸœŸœ˜’K˜—K˜K˜—šž œŸœ˜&Kšœ.Ÿœ˜5šŸœŸœ˜%KšœE˜EKšœuŸœŸœ˜’K˜—K˜K˜—šž œŸœ˜#K™/K™KšœŸœ˜ Kšœ Ÿœ˜K˜3KšŸœŸœ ŸœŸœ˜KšœŸœ ˜,K˜DKšœsŸœŸœ˜K˜K˜—šžœŸœŸœŸœ ŸœŸœŸœ˜dK˜(K˜1K˜K˜—šž œŸœ˜&Kšœ1˜1Kšœ5 ˜OK˜EKšœuŸœŸœ˜’Kšœ˜K˜—šž œŸœ˜#šŸœ;Ÿ˜AKšœa˜a—šŸœ˜š ž œŸœŸœŸœŸœ˜LK–([sliceD: GGModelTypes.SliceDescriptor]˜CšŸœwŸœ ŸœŸ˜ŽKšœŸœT˜`KšœŸœ ˜,KšŸœ˜—K˜—K˜IKšœC˜CKšœsŸœŸœ˜Kšœ˜—Kšœ˜K˜—šž œŸœ˜&Kšœ Ÿœ˜Kšœ ŸœŸœ˜Kšœ Ÿœ˜K˜,K˜IšŸœ Ÿœ˜K˜K˜—KšŸœ˜KšœfŸœŸœ˜ƒK˜K˜—K˜K™ Kš œŸœŸœŸœŸœ$˜JKš œŸœŸœŸœŸœ"˜QšžœŸœ˜+KšœN˜NšŸœ"Ÿœ˜*K˜TKšœuŸœŸœ˜’K˜—K˜K˜—šž œŸœ˜&KšœŸœ˜KšœŸœŸœŸœ˜KšœŸœŸœŸœ˜KšœŸœŸœŸœ˜KšœŸœŸœŸœŸœŸœŸœŸœŸœ˜ƒK˜Jš Ÿœ ŸœŸœŸœŸœŸœŸ˜PKš£œ2˜EK˜ K˜KšŸœ˜—KšœsŸœŸœ˜K˜K˜—š žœŸœŸœŸœŸœ˜=Kšœ$ŸœŸœ˜.šŸœŸœ˜'KšœI˜IKšœuŸœŸœ˜’K˜—K˜K˜—šžœŸœ˜(Kšœ4Ÿœ˜;šŸœŸœ˜'KšœI˜IKšœuŸœŸœ˜’K˜—K˜K˜—šž œŸœ˜%K™6K™Kšœ Ÿœ˜Kšœ Ÿœ˜K˜;KšŸœŸœ ŸœŸœ˜Kšœ%Ÿœ ˜4KšœF˜FKšœsŸœŸœ˜K˜K˜—šžœŸœŸœŸœ ŸœŸœŸœ˜hK˜,K˜9K˜K˜—šžœŸœ˜(Kšœ7˜7Kšœ7 ˜QKšœI˜IKšœuŸœŸœ˜’Kšœ˜K˜—šž œŸœ˜%šŸœ;Ÿ˜AKšœc˜c—šŸœ˜š ž œŸœŸœŸœŸœ˜JK–([sliceD: GGModelTypes.SliceDescriptor]˜CšŸœwŸœ ŸœŸ˜ŽKšœ Ÿœ?˜MKšœ%Ÿœ ˜4KšŸ˜—K˜—K˜GKšœG˜GKšœsŸœŸœ˜Kšœ˜—Kšœ˜K˜—šžœŸœ˜(Kšœ Ÿœ˜Kšœ ŸœŸœ˜Kšœ Ÿœ˜K˜,K˜OšŸœ Ÿœ˜K˜K˜—KšŸœ˜KšœfŸœŸœ˜ƒK˜K˜—K˜K™šžœŸœ˜)K˜!K˜K˜—šžœŸœ˜3Kš œŸœŸœŸœŸœ˜2šŸœŸœ˜Kšœw˜wKšŸœ˜K˜—Kšœ%˜%KšœŸœ ˜"K˜K˜—šžœŸœ˜)K˜!K˜K˜—šžœŸœ˜3Kš œŸœŸœŸœŸœ˜2šŸœŸœ˜Kšœw˜wKšŸœ˜K˜—Kšœ%˜%KšœŸœ ˜"K˜K˜—šžœŸœ˜*K˜"K˜K˜—šžœŸœ˜4Kš œŸœŸœŸœŸœ˜3šŸœŸœ˜Kšœy˜yKšŸœ˜K˜—Kšœ'˜'KšœŸœ˜$K˜K˜—šžœŸœ˜,K˜(K˜K˜—šžœŸœ˜6Kš œ ŸœŸœŸœŸœ˜5šŸœŸœ˜ Kšœ‚˜‚KšŸœ˜K˜—Kšœ/˜/KšœŸœ˜(K˜—K™K™ šžœŸœ˜,KšœŸœ˜Kšœ'˜'KšœŸœŸœ˜K˜Kšœ˜Kšœ˜K˜K˜ K˜K˜K˜K˜šŸœŸœ .˜TKšœŸœŸœ˜K˜Kšœ˜K˜!K˜K˜1K˜?Kš œŸœŸœŸœŸœŸœ˜NšŸœŸœŸœ ˜=Kšœ2Ÿœ˜7KšœŸœ˜šŸœ Ÿœ˜(Kšœ'˜'K˜—šŸœ˜K˜0K˜AKšŸœŸœŸœ+˜GK˜—K˜—šŸœ˜Kšœ˜Kšœ˜Kšœ Ÿœ˜šŸœ Ÿœ˜(K˜2Kšœ'˜'Kšœ2˜2Kšœ8˜8K˜—šŸœ˜K˜0K˜:K˜.Kšœ=˜=Kšœ1˜1K˜—Kš£%™%K˜>Kš œ(ŸœŸœŸœŸœ˜iKšœ= ˜OK˜>Kšœ ž œ˜1Kšœ£œ˜"Kšœ£ œ$˜8K˜—™,K™0—Kšœ £ œ  0˜PKšœ`˜`Kšœ}ŸœŸœ˜›K˜—KšŸœ‰˜Kšœ˜—K˜šžœ˜K˜—K˜šžœŸœ˜KšŸœ ˜K™K™Kšœ-ŸœŸœ˜;Kšœ(ŸœŸœ˜6—™K™Kšœ+˜+Kšœ-˜-Kšœ&˜&Kšœ*˜*Kšœ(˜(Kšœ4˜4Kšœ,˜,Kšœ0˜0Kšœ&˜&KšœH˜HKšœJ˜JKšœ-˜-Kšœ(˜(K˜KšœH˜HKšœD˜DK˜K™Kšœ(˜(Kšœ.˜.Kšœ*˜*Kšœ0˜0Kšœ.˜.Kšœ.˜.Kšœ>˜>Kšœ8˜8Kšœ:˜:KšœB˜BK˜K™#Kšœ>˜>Kšœ-Ÿœ˜4Kšœ*Ÿœ˜1Kšœ8˜8Kšœ2Ÿœ˜9Kšœ:˜:Kšœ4Ÿœ˜;KšœB˜BKšœ<˜˜>KšœU˜UK˜Kšœ>˜>K˜K˜—K˜šžœŸœŸœ™'K™2KšœŸœ+™BK™YKšœ)Ÿœ™.Kšœ)Ÿœ™.KšŸœŸœŸœŸœ™K™—K˜K˜K˜KšŸœ˜—…—Τ`ς