DIRECTORY Ascii, Atom, CodeTimer, ColorTool, CubicSplines, EditSpanSupport, Feedback, FeedbackTypes, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGControlPanelTypes, GGCoreOps, GGCoreTypes, GGDescribe, GGDragTypes, GGEvent, GGFont, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMultiGravity, GGOutline, GGParent, GGParseIn, GGRefreshTypes, GGRefresh, GGScene, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGUserInput, GGUserProfile, GGUtility, GGWindow, Imager, ImagerColor, ImagerColorPrivate, ImagerInterpress, ImagerPixel, ImagerPixelArray, ImagerSample, ImagerTransformation, Interpress, IO, IPMaster, NamedColors, NodeProps, Random, Real, RealFns, Rope, SF, TextNode, TiogaAccess, TiogaImager, TiogaOpsDefs, Vectors2d, ViewerClasses, ViewerOps, ViewerTools; GGEventImplE: CEDAR PROGRAM IMPORTS Atom, ColorTool, EditSpanSupport, Feedback, GGAlign, GGBoundBox, GGCaret, GGHistory, GGOutline, GGParent, GGParseIn, GGRefresh, GGScene, GGSegment, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGUserInput, GGUtility, GGWindow, Imager, ImagerColor, ImagerColorPrivate, ImagerPixel, ImagerPixelArray, ImagerSample, ImagerTransformation, IO, NodeProps, Real, Rope, TextNode, TiogaAccess, Vectors2d, ViewerOps, ViewerTools EXPORTS GGEvent, GGHistoryTypes, GGInterfaceTypes = BEGIN ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes DragDataObj: PUBLIC TYPE = GGDragTypes.DragDataObj; -- exported to GGInterfaceTypes HistoryToolObj: PUBLIC TYPE = GGHistory.HistoryToolObj; -- exported to GGHistoryTypes AlignBag: TYPE = GGInterfaceTypes.AlignBag; BitVector: TYPE = GGModelTypes.BitVector; BoundBox: TYPE = GGModelTypes.BoundBox; Caret: TYPE = GGInterfaceTypes.Caret; Color: TYPE = ImagerColor.Color; ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator; FeatureCycler: TYPE = GGInterfaceTypes.FeatureCycler; FeatureData: TYPE = GGModelTypes.FeatureData; MsgRouter: TYPE = FeedbackTypes.MsgRouter; FontData: TYPE = GGFont.FontData; GGData: TYPE = GGInterfaceTypes.GGData; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; HistoryTool: TYPE = REF HistoryToolObj; GravityType: TYPE = GGInterfaceTypes.GravityType; Joint: TYPE = GGModelTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; Orientation: TYPE = GGModelTypes.Orientation; Point: TYPE = GGBasicTypes.Point; RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; RunProc: TYPE = GGTraj.RunProc; Scene: TYPE = GGModelTypes.Scene; SegAndIndex: TYPE = GGSequence.SegAndIndex; Segment: TYPE = GGSegmentTypes.Segment; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; SelectionClass: TYPE = GGModelTypes.SelectionClass; Sequence: TYPE = GGModelTypes.Sequence; SequenceOfReal: TYPE = REF SequenceOfRealObj; SequenceOfRealObj: TYPE = GGCoreTypes.SequenceOfRealObj; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj; 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; Transformation: TYPE = Imager.Transformation; TriggerBag: TYPE = GGInterfaceTypes.TriggerBag; UserInputProc: TYPE = GGEvent.UserInputProc; Vector: TYPE = GGBasicTypes.Vector; Viewer: TYPE = ViewerClasses.Viewer; WalkProc: TYPE = GGModelTypes.WalkProc; reallyBigReal: REAL = 1.0e37; SelectEntireSlice: PUBLIC PROC [slice: Slice, scene: Scene, selectClass: SelectionClass, ggData: GGData]= { extender: SliceDescriptor; GGSelect.SelectEntireSlice[slice, scene, selectClass]; GGState.SetExtendMode[ggData, topLevel]; IF (extender _ GGState.GetSliceToExtend[ggData])#NIL AND extender.slice#slice THEN { sliceD: SliceDescriptor _ GGSliceOps.NewParts[slice, NIL, slice]; GGState.SetSliceToExtend[ggData, sliceD]; }; }; SelectSlice: PUBLIC PROC [sliceD: SliceDescriptor, scene: Scene, selectClass: SelectionClass, ggData: GGData]= { extender: SliceDescriptor; GGSelect.SelectSlice[sliceD, scene, selectClass]; GGState.SetExtendMode[ggData, topLevel]; IF (extender _ GGState.GetSliceToExtend[ggData])#NIL AND extender.slice#sliceD.slice THEN { GGState.SetSliceToExtend[ggData, sliceD]; }; }; UndoOne: UserInputProc = { GGHistory.UndoN[ggData, 1]; }; BuildAHistoryTool: UserInputProc = { ggData.history.tool _ GGHistory.BuildTool[GGState.GetFullName[ggData], ggData]; }; GetSelectedWrapRule: PROC [ggData: GGData] RETURNS [odd: BOOL _ FALSE, success: BOOL _ TRUE] = { DoCheckWrapRule: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { thisOdd: BOOL _ GGOutline.GetWrapRule[sliceD.slice]; IF found THEN done _ thisOdd # odd ELSE { found _ TRUE; odd _ thisOdd; }; }; scene: Scene _ ggData.scene; found: BOOL _ FALSE; success _ NOT GGScene.WalkSelectedSlices[scene, highest, DoCheckWrapRule, normal, $Outline]; }; ShowWrapRule: PUBLIC UserInputProc = { scene: Scene _ ggData.scene; odd: BOOL _ FALSE; success: BOOL _ FALSE; IF GGScene.CountSelectedSlices[scene, highest, normal, $Outline] = 0 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowWrapRule failed: select at least one outline for ShowWrapRule"] ELSE { [odd, success] _ GetSelectedWrapRule[ggData]; IF NOT success THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowWrapRule failed: multiple wrap rules are selected"] ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "ShowWrapRule: wrap rule is %g", [rope[ IF odd THEN "odd wrap" ELSE "non-zero wrap"]] ]; }; }; SetOddWrap: UserInputProc = { SetWrapAux[event, ggData, TRUE]; }; SetNonZeroWrap: UserInputProc = { SetWrapAux[event, ggData, FALSE]; }; SetWrapSlice: PROC [slice: Slice, parts: SliceParts, odd: BOOL] = { SELECT GGSliceOps.GetType[slice] FROM $Cluster => { DoSetWrap: PROC [childD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { SetWrapSlice[childD.slice, childD.parts, odd]; }; [] _ GGParent.WalkIncludedChildren[slice, parts, highest, DoSetWrap, $Outline]; }; $Outline => { GGOutline.SetWrapRule[slice, odd]; }; ENDCASE => {}; }; SetWrapAux: PROC [event: LIST OF REF ANY, ggData: GGData, odd: BOOL _ TRUE] = { scene: Scene _ ggData.scene; DoSetWrap: PROC [nextSD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { SetWrapSlice[nextSD.slice, nextSD.parts, odd]; }; IF GGScene.CountSelectedSlices[scene, highest, normal, $Outline] = 0 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetWrapRule failed: select at least one outline for SetWrapRule"] ELSE { GGHistory.NewCapture["Set wrap rule", ggData]; -- capture scene BEFORE UPDATE [] _ GGScene.WalkSelectedSlices[scene, highest, DoSetWrap, normal, $Outline]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; GGHistory.PushCurrent[ggData]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "SetWrapRule: %g wrap set", [rope[ IF odd THEN "odd" ELSE "non-zero"]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; GetSelectedOrientation: PROC [ggData: GGData] RETURNS [orientation: Orientation _ cw, success: BOOL _ TRUE] = { DoCheckOrientation: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { thisOrient: Orientation; isUnique: BOOL _ FALSE; [thisOrient, isUnique] _ GGSliceOps.GetOrientation[sliceD.slice, sliceD.parts]; IF NOT isUnique THEN { orientation _ thisOrient; RETURN[TRUE]; }; IF found THEN done _ thisOrient # orientation ELSE { found _ TRUE; orientation _ thisOrient; }; }; scene: Scene _ ggData.scene; found: BOOL _ FALSE; success _ NOT GGScene.WalkSelectedSlices[scene, first, DoCheckOrientation, normal]; }; ShowOrientation: PUBLIC UserInputProc = { orientation: Orientation _ reverse; success: BOOL _ FALSE; IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowOrientation failed: select at least one oriented object for ShowOrientation"] ELSE { [orientation, success] _ GetSelectedOrientation[ggData]; IF NOT success THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowOrientation failed: multiple orientations are selected"] ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "ShowOrientation: orientation is %g", [rope[ SELECT orientation FROM cw => "cw", ccw => "ccw", reverse => "none", ENDCASE => ERROR ]] ]; }; }; OrientCW: PUBLIC UserInputProc = { OrientAux[event, ggData, cw]; }; OrientCCW: PUBLIC UserInputProc = { OrientAux[event, ggData, ccw]; }; Reorient: PUBLIC UserInputProc = { OrientAux[event, ggData, reverse]; }; OrientAux: PROC [event: LIST OF REF ANY, ggData: GGData, orientation: Orientation] = { scene: Scene _ ggData.scene; IF GGSelect.NoSelections[scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetOrientation failed: select at least one object for SetOrientation"] ELSE { DoOrient: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { GGSelect.SaveSelectionsInSliceAllClasses[sliceD.slice, scene]; [] _ GGSliceOps.SetOrientation[sliceD.slice, sliceD.parts, orientation, currentEvent]; GGSelect.ReselectSliceAllClasses[sliceD.slice, scene]; }; currentEvent: HistoryEvent _ GGHistory.NewCurrent["Orient", ggData]; [] _ GGScene.WalkSelectedSlices[scene, first, DoOrient, normal]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; GGHistory.PushCurrent[ggData]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "SetOrientation: orientation is %g", [rope[ SELECT orientation FROM cw => "_ cw", ccw => "_ ccw", reverse => "reversed", ENDCASE => ERROR ]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; }; gScale: REAL _ 0.33; gAngle: REAL _ 33; MakeCurveProc: TYPE = PROC [lo, hi: Point, props: LIST OF REF ANY] RETURNS [seg: Segment]; GetPt: PROC[p0, dir: Point] RETURNS [pt: Point] = { pt _ Vectors2d.Add[Vectors2d.Scale[Vectors2d.VectorPlusAngle[dir,gAngle], gScale*Vectors2d.Magnitude[dir] ], p0]; }; SetStraight: PUBLIC UserInputProc = { MakeStraightAux: MakeCurveProc = { seg _ GGSegment.MakeLine[lo, hi, props]; }; SetCurveAux[ggData, MakeStraightAux, $Line]; }; -- end SetStraight SetArc: PUBLIC UserInputProc = { MakeArcAux: MakeCurveProc = { p1: Point; p1 _ Vectors2d.Add[Vectors2d.Scale[Vectors2d.Sub[hi,lo], 0.5], lo]; seg _ GGSegment.MakeArc[lo, p1, hi, props]; }; SetCurveAux[ggData, MakeArcAux, $Arc]; }; -- end SetArc SetConic: PUBLIC UserInputProc = { MakeConicAux: MakeCurveProc = { p1: Point; p1 _ Vectors2d.Add[Vectors2d.Scale[Vectors2d.Sub[hi,lo], 0.5], lo]; seg _ GGSegment.MakeConic[lo, p1, hi, r, props]; }; r: REAL _ NARROW[event.rest.first, REF REAL]^; IF r<0.0 OR r>1.0 THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetConic failed: select a real number in [0.0 .. 1.0] for conic pointiness"]; RETURN; }; SetCurveAux[ggData, MakeConicAux, $Conic]; }; SetConstraintType: PUBLIC UserInputProc = { constraint: Rope.ROPE _ NARROW[event.rest.first]; ggData.drag.editConstraints _ SELECT TRUE FROM Rope.Equal[constraint, "length", FALSE] => length, Rope.Equal[constraint, "tangent", FALSE] => tangent, ENDCASE => none; ShowConstraintType[ggData, event]; }; SetMakeConstrained: PUBLIC UserInputProc = { cpGen: ControlPointGenerator; jointGen: JointGenerator; thisCPNum, thisSegNum: NAT; constrained, done: BOOL _ FALSE; scene: Scene _ ggData.scene; DoConstrain: PROC [nextTrajD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[nextTrajD.slice], NIL]; constrained _ FALSE; jointGen _ GGSequence.JointsInSequence[NARROW[nextTrajD.parts]]; FOR thisJointNum: INT _ GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL thisJointNum = -1 DO constrained _ GGTraj.ConstrainJoint[nextTrajD.slice, ggData.drag.editConstraints, thisJointNum] OR constrained; -- evaluation order important!! ENDLOOP; cpGen _ GGSequence.ControlPointsInSequence[NARROW[nextTrajD.slice.data], NARROW[nextTrajD.parts]]; done _ FALSE; [thisSegNum, thisCPNum, done] _ GGSequence.NextSegNumAndCPNum[cpGen]; -- init loop UNTIL done DO constrained _ GGTraj.ConstrainCP[nextTrajD.slice, ggData.drag.editConstraints, thisSegNum, thisCPNum] OR constrained; -- evaluation order important!! [thisSegNum, thisCPNum, done] _ GGSequence.NextSegNumAndCPNum[cpGen]; ENDLOOP; IF constrained THEN GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[nextTrajD.slice], NIL]; -- constrained bBox }; IF ggData.drag.editConstraints = none THEN RETURN[]; GGRefresh.NullStartBox[ggData]; [] _ GGScene.WalkSelectedSlices[scene, leaf, DoConstrain, normal, $Traj]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "SetConstraints: curve constrained to type: %g", [rope[SELECT ggData.drag.editConstraints FROM none => "none", tangent => "tangent", length => "length", ENDCASE => "error"]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; ShowConstraintType: PUBLIC UserInputProc = { Feedback.PutF[ggData.router, oneLiner, $Show, "ShowConstraint: Bezier edit constraint is %g", [rope[SELECT ggData.drag.editConstraints FROM none => "none", tangent => "tangent", length => "length", ENDCASE => "error"]] ]; }; SetBezier: PUBLIC UserInputProc = { MakeBezierAux: MakeCurveProc = { length: REAL; dir: Point; p1, p2: Point; length _ Vectors2d.Distance[lo, hi]; dir _ Vectors2d.Sub[hi,lo]; p1 _ GetPt[lo, dir]; p2 _ GetPt[hi, [-dir.x, -dir.y] ]; seg _ GGSegment.MakeBezier[lo, p1, p2, hi, props]; }; SetCurveAux[ggData, MakeBezierAux, $Bezier]; }; -- end SetBezier SegToSegMatchCp: PROC [seg: Segment, traj: Traj, makeCurve: MakeCurveProc, type: ATOM] = { tSeg: Segment; success: BOOL _ FALSE; IF seg.class.type = type THEN { -- no need to change type or control points IF type = $Conic THEN { tSeg _ makeCurve[seg.lo, seg.hi, seg.props]; GGSegment.ConicSetControlPoint[tSeg, seg.class.controlPointGet[seg, 0]]; GGSegment.CopyLooks[seg, tSeg]; } ELSE { tSeg _ GGSegment.CopySegment[seg]; }; success _ GGTraj.AddSegment[traj, hi, tSeg, lo]; } ELSE IF type = $Arc AND seg.class.controlPointCount[seg] = 1 THEN { -- if old and new segment types both have single control points, keep the control points the same tSeg _ GGSegment.MakeArc[seg.lo, seg.class.controlPointGet[seg, 0], seg.hi, seg.props]; GGSegment.CopyLooks[seg, tSeg]; success _ GGTraj.AddSegment[traj, hi, tSeg , lo] } ELSE IF type = $Conic AND seg.class.controlPointCount[seg] = 1 THEN { -- if old and new segment types have single control points, keep the control points the same tSeg _ makeCurve[seg.lo, seg.hi, seg.props]; GGSegment.ConicSetControlPoint[tSeg, seg.class.controlPointGet[seg, 0]]; GGSegment.CopyLooks[seg, tSeg]; success _ GGTraj.AddSegment[traj, hi, tSeg, lo] } ELSE { -- make segment of new type with old endpoint info tSeg _ makeCurve[seg.lo, seg.hi, seg.props]; GGSegment.CopyLooks[seg, tSeg]; success _ GGTraj.AddSegment[traj, hi, tSeg, lo]; }; IF NOT success THEN ERROR; }; SegAndCpsToSegs: PROC [seg: Segment, traj: Traj, makeCurve: MakeCurveProc, type: ATOM] = { success: BOOL _ FALSE; IF seg.class.type = type THEN -- if seg is already the right type, then we don't change it success _ GGTraj.AddSegment[traj, hi, GGSegment.CopySegment[seg], lo] ELSE { -- seg isn't the right type, so we convert all of its point/controlpoint spans tSeg: Segment; last, next: Point _ seg.lo; FOR i:INT IN [0..seg.class.controlPointCount[seg]) DO next _ seg.class.controlPointGet[seg, i]; tSeg _ makeCurve[last, next, seg.props]; GGSegment.CopyLooks[seg, tSeg]; -- KAP. March 6, 1987 7:29:37 pm PST success _ GGTraj.AddSegment[traj, hi, tSeg, lo]; IF NOT success THEN ERROR; last _ next; ENDLOOP; tSeg _ makeCurve[last, seg.hi, seg.props]; GGSegment.CopyLooks[seg, tSeg]; -- KAP. March 6, 1987 7:29:37 pm PST success _ GGTraj.AddSegment[traj, hi, tSeg, lo]; IF NOT success THEN ERROR; }; }; ForEachOutlineRun: PROC [scene: Scene, selectClass: SelectionClass, runProc: RunProc, segmentsOnly: BOOL _ TRUE, selectNewRuns: BOOL _ FALSE] RETURNS [bBox: BoundBox] = { DoSaveSelections: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { GGSelect.SaveSelectionsInSliceAllClasses[slice, scene]; }; DoReplaceRuns: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { priority: INT; newOutline: Slice; IF GGScene.IsTopLevel[sliceD.slice] THEN { priority _ GGScene.GetPriority[scene, sliceD.slice]; newOutline _ ReplaceRunsInOutline[sliceD, runProc, segmentsOnly, selectNewRuns]; IF newOutline # NIL THEN { GGScene.DeleteSlice[scene, sliceD.slice]; GGScene.AddSlice[scene, newOutline, priority]; }; } ELSE { parent: Slice _ GGParent.GetParent[sliceD.slice]; priority _ GGParent.GetChildPriority[parent, sliceD.slice]; newOutline _ ReplaceRunsInOutline[sliceD, runProc, segmentsOnly, selectNewRuns]; IF newOutline # NIL THEN { [] _ GGSlice.RemoveChild[parent, sliceD.slice]; GGSlice.AddChildToCluster[parent, newOutline, priority]; }; }; IF newOutline # NIL THEN GGBoundBox.EnlargeByBox[bBox: bBox, by: GGSliceOps.GetBoundBox[newOutline]]; }; DoRestoreSelections: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { GGSelect.ReselectSliceAllClasses[slice, scene]; }; bBox _ GGScene.BoundBoxOfSelected[scene, normal]; GGSelect.DuplicateSelections[scene, normal, active]; -- all normal selected objects become active [] _ GGScene.WalkSlices[scene, first, DoSaveSelections]; [] _ GGScene.WalkSelectedSlices[scene, leaf, DoReplaceRuns, active, $Outline]; [] _ GGScene.WalkSlices[scene, first, DoRestoreSelections]; }; ReplaceRunsInOutline: PROC [outlineD: SliceDescriptor, runProc: RunProc, segmentsOnly: BOOL _ TRUE, selectNewRuns: BOOL _ FALSE] RETURNS [newOutline: Slice _ NIL] = { WHILE outlineD # NIL AND NOT GGSliceOps.IsEmptyParts[outlineD] DO thisChildD: SliceDescriptor _ GGParent.FirstIncludedChild[outlineD.slice, outlineD.parts, first]; SELECT GGSliceOps.GetType[thisChildD.slice] FROM $Traj => { newOutline _ GGTraj.ReplaceFirstRun[thisChildD, runProc, segmentsOnly, selectNewRuns]; IF newOutline = NIL THEN outlineD _ NIL ELSE { parts: SliceParts _ GGSliceOps.RemakeSelections[newOutline, active]; -- yuk outlineD _ IF parts = NIL THEN NIL ELSE GGSlice.DescriptorFromParts[newOutline, parts]; }; }; ENDCASE => { -- $Box or $Circle. Clear its active bits and ignore it. parts: SliceParts; -- yuk GGSliceOps.SaveSelections[thisChildD.slice, NIL, active]; parts _ GGSliceOps.RemakeSelections[thisChildD.slice, active]; outlineD _ IF parts = NIL THEN NIL ELSE GGSlice.DescriptorFromParts[thisChildD.slice, parts]; }; ENDLOOP; }; SetCurveAux: PROC [ggData: GGData, makeCurve: MakeCurveProc, type: ATOM] = { RunOfSegs: RunProc = { runData: TrajData _ NARROW[run.slice.data]; runParts: TrajParts _ NARROW[run.parts]; segGen: SegmentGenerator _ GGSequence.SegmentsInSequence[runData, runParts]; initialSeg: Segment _ GGSequence.NextSegment[segGen]; IF initialSeg = NIL THEN RETURN[NIL]; -- the run is a single vertex traj _ GGTraj.CreateTraj[initialSeg.lo]; FOR seg: Segment _ initialSeg, GGSequence.NextSegment[segGen] UNTIL seg = NIL DO SELECT seg.class.type FROM -- choose from old segment type $Line, $Arc, $Conic, $Bezier => SegToSegMatchCp[seg, traj, makeCurve, type]; -- modify traj $CubicSpline => { IF type=$Line OR (runData.segCount=1 AND runData.role#open) THEN SegAndCpsToSegs[seg, traj, makeCurve, type] -- Splines are changed to multiple lines...(or multiple segs if spline was cyclic) ELSE SegToSegMatchCp[seg, traj, makeCurve, type]; -- ...but single everything else }; ENDCASE => ERROR; ENDLOOP; GGSliceOps.SetStrokeJoint[traj, NIL, runData.strokeJoint, NIL]; }; IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetCurveType failed: select some curves"] ELSE { GGHistory.NewCapture["Set curve type", ggData]; -- capture scene BEFORE UPDATE GGRefresh.NullStartBox[ggData]; GGRefresh.EnlargeStartBox[ggData, ForEachOutlineRun[ggData.scene, normal, RunOfSegs, TRUE, TRUE], NIL]; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; GGHistory.PushCurrent[ggData]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "SetCurve: curves modified to %gs", [rope[Atom.GetPName[type]]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; }; <> < traj _ pattern[seg, traj]; ENDCASE => NULL; ENDLOOP; GGSliceOps.SetStrokeJoint[traj, NIL, runData.strokeJoint, NIL]; }; GGRefresh.NullStartBox[ggData]; GGRefresh.EnlargeStartBox[ggData, ForEachOutlineRun[ggData.scene, normal, RunOfSegs, TRUE, TRUE], NIL]; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; Feedback.Append[ggData.router, "Pattern modified", oneLiner]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; >> SetNaturalSpline: PUBLIC UserInputProc = { SetSpline[splineType: naturalAL, ggData: ggData]; }; SetBSpline: PUBLIC UserInputProc = { SetSpline[splineType: bspline, ggData: ggData]; }; SetSpline: PROC [splineType: CubicSplines.SplineType, ggData: GGData] = { RunToSpline: RunProc = { X: NAT = CubicSplines.X; Y: NAT = CubicSplines.Y; runData: TrajData _ NARROW[run.slice.data]; runParts: TrajParts _ NARROW[run.parts]; tSeg: Segment; controlPoint: Point; cyclic: BOOL _ runData.role#open AND GGSequence.IsComplete[runParts]; cps: CubicSplines.KnotSequence _ NEW[CubicSplines.KnotSequenceRec[IF cyclic THEN runParts.jointCount+runParts.controlPointCount+1 ELSE runParts.jointCount+runParts.controlPointCount]]; segGen: SegmentGenerator _ GGSequence.OrderedSegmentsInSequence[runData, runParts]; initialSeg: Segment _ GGSequence.NextSegment[segGen]; success: BOOL _ FALSE; cpCount: NAT _ 0; IF initialSeg = NIL THEN RETURN[NIL]; IF cyclic AND splineType = naturalAL THEN splineType _ cyclicAL; traj _ GGTraj.CreateTraj[initialSeg.lo]; cps[cpCount] _ [initialSeg.lo.x, initialSeg.lo.y]; cpCount _ cpCount + 1; FOR seg: Segment _ initialSeg, GGSequence.NextSegment[segGen] UNTIL seg = NIL DO FOR i:INT IN [0..seg.class.controlPointCount[seg]) DO controlPoint _ seg.class.controlPointGet[seg, i]; cps[cpCount] _ [controlPoint.x, controlPoint.y]; cpCount _ cpCount + 1; ENDLOOP; cps[cpCount] _ [seg.hi.x, seg.hi.y]; cpCount _ cpCount + 1; ENDLOOP; IF cyclic THEN cps[cps.length-1] _ cps[0]; -- cumulative error sometimes causes small differences. DJK. tSeg _ GGSegment.MakeCubicSpline[cps, splineType, NIL]; GGSegment.CopyLooks[initialSeg, tSeg]; success _ GGTraj.AddSegment[traj, hi, tSeg, lo]; IF NOT success THEN ERROR; GGSliceOps.SetStrokeJoint[traj, NIL, runData.strokeJoint, NIL]; }; IF GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Outline]<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetSplineType failed: select some curves"] ELSE { GGHistory.NewCapture["Set spline type", ggData]; -- capture scene BEFORE UPDATE GGRefresh.NullStartBox[ggData]; GGRefresh.EnlargeStartBox[ggData, ForEachOutlineRun[ggData.scene, normal, RunToSpline, TRUE, TRUE], NIL]; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; GGHistory.PushCurrent[ggData]; Feedback.Append[ggData.router, oneLiner, $Feedback, "MakeSpline: splines created"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; }; SelectMatchingCurve: PUBLIC UserInputProc = { name: Rope.ROPE _ NARROW[event.rest.first]; DoSelectMatching: PROC [traj: Slice] RETURNS [done: BOOL _ FALSE] = { segGen: SegmentGenerator _ GGSequence.SegmentsInTraj[NARROW[traj.data]]; FOR segAndIndex: SegAndIndex _ GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL segAndIndex.seg = NIL DO IF Rope.Equal[segAndIndex.seg.class.describe[segAndIndex.seg, TRUE, TRUE, TRUE, NIL], name, FALSE] THEN { seq: TrajParts _ GGSequence.CreateFromSegment[NARROW[traj.data], segAndIndex.index]; sliceD: SliceDescriptor _ GGSlice.DescriptorFromParts[traj, seq]; GGSelect.SelectSlice[sliceD, ggData.scene, normal]; }; ENDLOOP; }; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE]; GGSelect.DeselectAll[ggData.scene, normal]; [] _ GGScene.WalkSlices[ggData.scene, leaf, DoSelectMatching, $Traj]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "SelectMatchingCurve: curves of type %g selected", [rope[name]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; }; TiogaFillFromSelection: UserInputProc = { complaint: Rope.ROPE = "FillBoxesFromSelection failed: select exactly one leaf object (with boxes) for filling"; selectCount: NAT _ GGScene.CountSelectedSlices[ggData.scene, first, normal]; IF selectCount#1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, complaint] ELSE { aborted: BOOL _ FALSE; myRef: TextNode.Ref; selectedD: SliceDescriptor _ GGScene.FirstSelectedSlice[ggData.scene, first, normal]; IF Rope.Length[ViewerTools.GetSelectionContents[]]#0 THEN { reader: TiogaAccess.Reader = TiogaAccess.FromSelection[]; ref: TextNode.Ref _ IF TiogaAccess.EndOf[reader] THEN NIL ELSE NARROW[TiogaAccess.GetNodeRefs[reader].root]; myRef _ CopyDocument[ref]; -- make a copy that GG owns. System will finalize its own copy. }; GGHistory.NewCapture["FillBoxesFromSelection", ggData]; -- capture scene BEFORE UPDATE SELECT GGSliceOps.GetType[selectedD.slice] FROM $Box => { GGSlice.SetBoxText[selectedD.slice, [myRef, 0], GGSlice.GetBoxText[selectedD.slice].screenStyle, NIL]; }; $Outline => { GGOutline.SetFillText[selectedD.slice, myRef, GGSlice.GetBoxText[selectedD.slice].screenStyle, NIL]; }; $Cluster => { CheckParent: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { child _ sliceD.slice; -- sliceD describes a child Box slice. The parts don't matter. IF parent=NIL THEN parent _ GGParent.GetParent[child]; -- record first parent IF parent#GGParent.GetParent[child] THEN RETURN [TRUE]; -- abort if more than one parent is selected childCount _ childCount+1; }; parent: Slice; child: Slice; -- child Box. Needed below childCount: NAT _ 0; aborted _ GGParent.WalkIncludedChildren[selectedD.slice, selectedD.parts, leaf, CheckParent, $Box]; IF parent=NIL THEN aborted _ TRUE ELSE IF NOT aborted THEN { SELECT GGSliceOps.GetType[parent] FROM $Outline => { -- complete document to parent Outline and its Box children GGOutline.SetFillText[parent, myRef, GGSlice.GetBoxText[parent].screenStyle, NIL]; }; $Cluster => { -- if a single leaf Box of a cluster is selected, then fill it. IF childCount=1 THEN { GGSlice.SetBoxText[child, [myRef, 0], GGSlice.GetBoxText[child].screenStyle, NIL]; } ELSE aborted _ TRUE; }; ENDCASE => ERROR; }; }; ENDCASE => aborted _ TRUE; IF aborted THEN Feedback.Append[ggData.router, oneLiner, $Complaint, complaint] ELSE { GGSelect.SelectEntireSlice[selectedD.slice, ggData.scene, normal]; GGHistory.PushCurrent[ggData]; Feedback.Append[ggData.router, oneLiner, $Feedback, "FillBoxesFromSelection: completed"]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; }; EditTiogaFill: UserInputProc = { complaint: Rope.ROPE = "EditTiogaFill failed: select exactly one filled box object for EditTiogaFill"; selectCount: NAT _ GGScene.CountSelectedSlices[ggData.scene, first, normal]; IF selectCount#1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, complaint] ELSE { MakeViewer: PROC [slice: Slice] = { boxText: TextNode.Location _ GGSlice.GetBoxText[slice].loc; IF boxText.node=NIL THEN aborted _ TRUE ELSE { viewer: Viewer _ ViewerTools.MakeNewTextViewer[info: [name: "TiogaFromGargoyle", column: right], paint: FALSE ]; viewer.class.set[viewer, CopyDocument[boxText.node], FALSE, $TiogaDocument]; ViewerOps.OpenIcon[viewer]; }; }; aborted: BOOL _ FALSE; selectedD: SliceDescriptor _ GGScene.FirstSelectedSlice[ggData.scene, first, normal]; SELECT GGSliceOps.GetType[selectedD.slice] FROM $Box, $Outline => { MakeViewer[selectedD.slice]; }; $Cluster => { CheckParent: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { child _ sliceD.slice; -- sliceD describes a child Box slice. The parts don't matter. IF parent=NIL THEN parent _ GGParent.GetParent[child]; -- record first parent IF parent#GGParent.GetParent[child] THEN RETURN [TRUE]; -- abort if more than one parent is selected childCount _ childCount+1; }; parent: Slice; child: Slice; -- child Box. Needed below childCount: NAT _ 0; aborted _ GGParent.WalkIncludedChildren[selectedD.slice, selectedD.parts, leaf, CheckParent, $Box]; IF parent=NIL THEN aborted _ TRUE ELSE IF NOT aborted THEN { SELECT GGSliceOps.GetType[parent] FROM $Outline => { -- complete document from parent Outline and its Box children MakeViewer[parent]; }; $Cluster => { -- if a single leaf Box of a cluster is selected, then show it. IF childCount=1 THEN { MakeViewer[child]; } ELSE aborted _ TRUE; }; ENDCASE => ERROR; }; }; ENDCASE => aborted _ TRUE; IF aborted THEN Feedback.Append[ggData.router, oneLiner, $Complaint, complaint] ELSE { Feedback.Append[ggData.router, oneLiner, $Feedback, "EditTiogaFill: Tioga document opened"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; }; CopyDocument: PROC [root: TextNode.Ref] RETURNS [TextNode.Ref] ~ { IF root=NIL THEN RETURN[NIL] ELSE { CopyAction: NodeProps.MapPropsAction = { newValue: REF _ NodeProps.CopyInfo[name, value]; NodeProps.PutProp[newRoot, name, newValue]; RETURN[FALSE]; }; span: TextNode.Span _ TextNode.MakeNodeSpan[first: TextNode.FirstChild[root], last: TextNode.LastLocWithin[root].node]; copy: TextNode.Span _ EditSpanSupport.CopySpan[span]; newRoot: TextNode.Ref _ TextNode.Root[copy.start.node]; [] _ NodeProps.MapProps[n: root, action: CopyAction]; RETURN [newRoot]; }; }; PrintStyleKind: UserInputProc = { SetStyleKind[FALSE, ggData]; }; ScreenStyleKind: UserInputProc = { SetStyleKind[TRUE, ggData]; }; SetStyleKind: PROC [wantScreenStyle: BOOL, ggData: GGData] = { scene: Scene _ ggData.scene; IF GGSelect.NoSelections[scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStyleKind failed: select some boxes to set style kind"] ELSE { DoSetStyleKind: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { parent: Slice _ GGParent.GetParent[sliceD.slice]; -- parent of Box [loc, refIsScreenStyle] _ GGSlice.GetBoxText[sliceD.slice]; IF refIsScreenStyle#wantScreenStyle THEN { IF parent#NIL AND GGSliceOps.GetType[parent]=$Outline THEN { loc _ GGSlice.GetBoxText[parent].loc; GGOutline.SetFillText[parent, loc.node, wantScreenStyle, NIL] } ELSE GGSlice.SetBoxText[sliceD.slice, loc, wantScreenStyle, NIL]; }; }; loc: TextNode.Location; refIsScreenStyle: BOOL _ FALSE; GGHistory.NewCapture["Set style kind", ggData]; -- capture scene BEFORE UPDATE [] _ GGScene.WalkSelectedSlices[scene, leaf, DoSetStyleKind, normal, $Box]; GGHistory.PushCurrent[ggData]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "SetStyleKind: style kind set to %g", [rope[IF wantScreenStyle THEN "screen" ELSE "print"] ]]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; ShowStyleKind: UserInputProc = { CheckStyleKind: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { box: Slice _ sliceD.slice; screenStyle: BOOL _ FALSE; loc: TextNode.Location; [loc, screenStyle] _ GGSlice.GetBoxText[box]; IF NOT firstFound THEN {foundScreenStyle _ screenStyle; firstFound _ TRUE}; RETURN[screenStyle#foundScreenStyle]; }; firstFound: BOOL _ FALSE; foundScreenStyle: BOOL _ FALSE; aborted: BOOL _ GGScene.WalkSelectedSlices[ggData.scene, leaf, CheckStyleKind, normal, $Box]; IF NOT firstFound THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowStyleKind failed: no boxes in selected objects"] ELSE IF aborted THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowStyleKind failed: multiple style kinds are selected"] ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "ShowStyleKind: %g style", [rope[IF foundScreenStyle THEN "screen" ELSE "print"] ]]; }; SetNewlineFactor: UserInputProc = { scene: Scene _ ggData.scene; r: REAL _ NARROW[event.rest.first, REF REAL]^; IF GGSelect.NoSelections[scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetNewlineFactor failed: select some text objects for setting newline factor"] ELSE IF r>reallyBigReal THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetNewlineFactor failed: select a reasonable multiplier"] ELSE { DoSetNewlineFactor: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { GGSlice.SetTextLineSpacing[sliceD.slice, r, NIL]; }; GGHistory.NewCapture["Set newline factor", ggData]; -- capture scene BEFORE UPDATE [] _ GGScene.WalkSelectedSlices[scene, leaf, DoSetNewlineFactor, normal, $Text]; GGHistory.PushCurrent[ggData]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "SetNewlineFactor: newline spacing set to %g", [real[r]] ]; }; }; ShowNewlineFactor: UserInputProc = { factor: REAL _ Real.LargestNumber; aborted: BOOL _ FALSE; scene: Scene _ ggData.scene; CheckNewlineFactor: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF factor = Real.LargestNumber THEN factor _ GGSlice.GetTextLineSpacing[sliceD.slice] ELSE RETURN[factor # GGSlice.GetTextLineSpacing[sliceD.slice]]; }; aborted _ GGScene.WalkSelectedSlices[scene, leaf, CheckNewlineFactor, normal, $Text]; IF factor = Real.LargestNumber THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowNewlineFactor failed: no text objects selected"] ELSE IF aborted THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowNewlineFactor failed: multiple factors are selected"] ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "ShowNewlineFactor: %g", [real[factor]]]; }; ConvertTextToSplines: UserInputProc = { ConvertToSplines: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { priority: INT; IF GGScene.IsTopLevel[sliceD.slice] THEN { theseShadows, theseOutlines: LIST OF Slice; [theseShadows, theseOutlines] _ GGSlice.OutlinesFromTextString[sliceD.slice]; priority _ GGScene.GetPriority[scene, sliceD.slice]; GGScene.DeleteSlice[scene, sliceD.slice]; GGScene.AddSlices[scene, theseOutlines, priority]; IF theseShadows # NIL THEN GGScene.AddSlices[scene, theseShadows, priority]; outlines _ GGUtility.AppendSliceList[theseOutlines, outlines]; outlines _ GGUtility.AppendSliceList[theseShadows, outlines]; } ELSE { theseShadows, theseOutlines: LIST OF Slice; cluster: Slice _ GGParent.GetParent[sliceD.slice]; [theseShadows, theseOutlines] _ GGSlice.OutlinesFromTextString[sliceD.slice]; priority _ GGParent.GetChildPriority[cluster, sliceD.slice]; [] _ GGSlice.RemoveChild[cluster, sliceD.slice]; [] _ GGSlice.AddChildrenToCluster[cluster, theseOutlines, priority]; IF theseShadows # NIL THEN [] _ GGSlice.AddChildrenToCluster[cluster, theseShadows, priority]; outlines _ GGUtility.AppendSliceList[theseOutlines, outlines]; outlines _ GGUtility.AppendSliceList[theseShadows, outlines]; }; }; outlines: LIST OF Slice _ NIL; scene: Scene _ ggData.scene; IF GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Text]<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ConvertTextToSplines failed: select at least one text object"] ELSE { GGHistory.NewCapture["Text to splines", ggData]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; [] _ GGScene.WalkSelectedSlices[scene, leaf, ConvertToSplines, normal, $Text]; GGSelect.DeselectAll[scene, normal]; -- de-selects any non-text slices that were selected FOR list: LIST OF Slice _ outlines, list.rest UNTIL list = NIL DO GGSelect.SelectEntireSlice[list.first, scene, normal]; ENDLOOP; GGCaret.NoAttractor[ggData.caret]; GGRefresh.EnlargeStartBox[ggData, GGScene.BoundBoxOfSelected[scene, normal], NIL]; GGHistory.PushCurrent[ggData]; Feedback.Append[ggData.router, oneLiner, $Feedback, "ConvertTextToSplines: completed"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; }; CreateColorWash: PROC [startColor: Imager.ConstantColor, regionWidth: REAL, toAtom: ATOM, vec: ImagerTransformation.VEC] RETURNS [success: BOOL _ TRUE, wash: Imager.SampledColor] ~ { ENABLE Imager.Error => GOTO Abort; FillSampleMap: PROC [size: INT, startSample, endSample: REAL, maxSample: INT] RETURNS [map: ImagerSample.SampleMap] = { step: REAL _ 1.0/size; alpha: REAL _ 0.0; map _ ImagerSample.NewSampleMap[box: box, bitsPerSample: 8]; FOR index: INT IN [0..size) DO nextVec: SF.Vec _ [0, index]; nextValue: REAL _ alpha*endSample + (1.0-alpha)*startSample; nextSample: INTEGER _ Real.Floor[maxSample*nextValue]; ImagerSample.Put[map: map, index: nextVec, value: nextSample]; alpha _ alpha+step; ENDLOOP; }; maxSample: INT _ 255; size: INT _ Real.Floor[regionWidth]; fudgeFactor: REAL; startRGB, endRGB: ImagerColor.RGB; redMap, grnMap, bluMap: ImagerSample.SampleMap; box: SF.Box _ [min: [s: 0, f: 0], max: [s: 1, f: size] ]; pixelMap: ImagerPixel.PixelMap; pixelArray: ImagerPixelArray.PixelArray; um: ImagerTransformation.Transformation; startRGB _ ImagerColorPrivate.RGBFromColor[startColor]; SELECT toAtom FROM $White => endRGB _ [1.0, 1.0, 1.0]; $Black => endRGB _ [0.0, 0.0, 0.0]; $ColorTool => { endColor: Color; endColor _ ColorTool.GetColor[ ! ColorTool.NoColorToolViewer => CONTINUE;]; IF endColor=NIL THEN endColor _ Imager.black; endRGB _ ImagerColorPrivate.RGBFromColor[NARROW[endColor]]; }; ENDCASE => ERROR; redMap _ FillSampleMap[size, startRGB.R, endRGB.R, maxSample]; grnMap _ FillSampleMap[size, startRGB.G, endRGB.G, maxSample]; bluMap _ FillSampleMap[size, startRGB.B, endRGB.B, maxSample]; pixelMap _ ImagerPixel.MakePixelMap[redMap, grnMap, bluMap]; pixelArray _ ImagerPixelArray.FromPixelMap[pixelMap: pixelMap, box: box, scanMode: [down, right], immutable: TRUE]; fudgeFactor _ regionWidth/REAL[size]; um _ ImagerTransformation.PreScale[ImagerTransformation.Translate[vec], fudgeFactor]; wash _ ImagerColor.MakeSampledColor[pa: pixelArray, um: um, colorOperator: ImagerColor.NewColorOperatorRGB[maxIn: maxSample]]; EXITS Abort => success _ FALSE; }; SetWash: PUBLIC UserInputProc = { newSelectList, ptr: LIST OF Slice; currentEvent: HistoryEvent; toAtom: ATOM _ NARROW[event.rest.first]; wash: Imager.SampledColor; scene: Scene _ ggData.scene; SetWashSlice: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { newSelectList, ptr: LIST OF Slice; color: Imager.Color; success: BOOL _ FALSE; [color, success] _ GGSliceOps.GetFillColor[sliceD.slice, sliceD.parts]; IF NOT success OR color=NIL OR NOT ISTYPE[color, Imager.ConstantColor] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetColorWash failed: select only shapes filled with a constant color"] ELSE { box: BoundBox _ GGSliceOps.GetTightBox[sliceD.slice]; regionWidth: REAL _ ABS[box.hiX-box.loX]; success: BOOL _ FALSE; [success, wash] _ CreateColorWash[NARROW[color], regionWidth, toAtom, [box.loX, box.loY]]; IF success THEN { [] _ GGSliceOps.SetFillColor[sliceD.slice, sliceD.parts, wash, $Set, currentEvent]; [newSelectList, ptr] _ GGUtility.AddSlice[sliceD.slice, newSelectList, ptr]; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "SetColorWash failed: Imager ERROR during CreateColorWash"] }; }; IF toAtom = $ColorTool AND NOT GGState.ColorToolIsBound[ggData] THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetColorWash failed: please install ColorTool and retry"]; RETURN; }; IF GGSelect.NoSelections[scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetColorWash failed: select at least one colored object"] ELSE { currentEvent _ GGHistory.NewCurrent["Wash color", ggData]; [newSelectList, ptr] _ GGUtility.StartSliceList[]; [] _ GGScene.WalkSelectedSlices[scene, first, SetWashSlice, normal]; FOR list: LIST OF Slice _ newSelectList, list.rest UNTIL list = NIL DO GGSelect.SelectEntireSlice[list.first, ggData.scene, normal]; ENDLOOP; GGHistory.PushCurrent[ggData]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "SetColorWash: fill color washed to %g", [rope[Atom.GetPName[toAtom]]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; UpdateCaretPosition: PROC [ggData: GGData, point: Point, normal: Vector] = { GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, attractor: TRUE]; GGCaret.SetAttractor[ggData.caret, point, normal, NIL]; ShowCaretValues[ggData, NIL]; GGWindow.NewCaretPos[ggData]; GGCaret.SitOn[ggData.caret, NIL]; GGWindow.RestoreScreenAndInvariants[paintAction: $CaretMovedStatic, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; CaretPositionFromSelection: UserInputProc = { ENABLE GGParseIn.SyntaxError => GOTO SomeError; point: Point; rope: Rope.ROPE = NARROW[event.rest.first]; arg: Point = GGParseIn.ReadPoint[IO.RIS[rope]]; IF arg.x>reallyBigReal OR arg.y>reallyBigReal THEN GOTO SomeError; point _ Vectors2d.Scale[arg, GGState.GetScaleUnit[ggData]]; UpdateCaretPosition[ggData, point, [0,-1]]; -- or leave normal unchanged? EXITS SomeError => Feedback.Append[ggData.router, oneLiner, $Complaint, "Set caret position failed: select a legal caret position [, ] (including brackets)"]; }; CaretAngleFromSelection: UserInputProc = { IF GGCaret.Exists[ggData.caret] THEN { real: REAL ~ NARROW[event.rest.first, REF REAL]^; IF real>reallyBigReal THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Set caret angle failed: select a reasonable real number for caret angle"] ELSE { normal: Vector ~ Vectors2d.Negate[Vectors2d.VectorFromAngle[real]]; UpdateCaretPosition[ggData, GGCaret.GetPoint[ggData.caret], normal]; }; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "Set caret angle failed: caret required for set caret angle"]; }; ShowValuesOfCaret: PROC [ggData: GGData, caret: Caret, name: Rope.ROPE] = { IF GGCaret.Exists[caret] THEN { scale: REAL ~ GGState.GetScaleUnit[ggData]; point: Point ~ GGCaret.GetPoint[caret]; normal: Vector ~ GGCaret.GetNormal[caret]; angle: REAL ~ Vectors2d.AngleFromVector[Vectors2d.Negate[normal]]; Feedback.PutF[ggData.router, oneLiner, $Show, "%g position: [%g, %g], angle: %g", [rope[name]], [real[point.x/scale]], [real[point.y/scale]], [real[angle]]]; } ELSE { Feedback.PutF[ggData.router, oneLiner, $Show, "Show%g: no %g", [rope[name]], [rope[name]] ]; }; }; ShowCaretValues: UserInputProc = { ShowValuesOfCaret[ggData, ggData.caret, "Caret"]; }; ShowAnchorValues: UserInputProc = { ShowValuesOfCaret[ggData, ggData.anchor, "Anchor"]; }; GrabInputFocus: UserInputProc = { GGState.GrabInputFocus[ggData]; }; SceneState: UserInputProc = { scene: Scene _ ggData.scene; DescendASlice: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { objects _ objects+1; IF slice.class=NIL THEN trashedObjects _ trashedObjects+1 ELSE { CheckTrashed: PROC [nextChild: Slice] RETURNS [done: BOOL _ FALSE] = { IF nextChild.class=NIL THEN trashedSubObjects _ trashedSubObjects+1; }; [] _ GGParent.WalkChildren[slice, first, CheckTrashed]; }; }; AddEmUp: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { leafCount _ leafCount + 1; }; objects, trashedObjects, trashedSubObjects, leafCount: INT _ 0; [] _ GGScene.WalkSlices[scene, first, DescendASlice]; [] _ GGScene.WalkSlices[scene, leaf, AddEmUp]; Feedback.PutF[ggData.router, oneLiner, $Show, "Scene: %g objects, %g trashed objects, %g trashed children, %g leaf objects", [integer[objects]], [integer[trashedObjects]], [integer[trashedSubObjects]], [integer[leafCount]] ]; }; SceneBagState: UserInputProc = { IsObsolete: PUBLIC PROC [seq: Sequence] RETURNS [BOOL] = { j: NAT _ 0; trajData: TrajData _ NARROW[seq.slice.data]; trajParts: TrajParts _ NARROW[seq.parts]; segGen: SegmentGenerator; IF trajParts.segments.len # trajData.segCount THEN RETURN[TRUE]; IF trajParts.joints.len # (GGTraj.HiJoint[seq.slice] + 1) THEN RETURN[TRUE]; segGen _ GGSequence.SegmentsInTraj[trajData]; FOR nextSeg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL nextSeg=NIL DO IF nextSeg.class.controlPointCount[nextSeg]#trajParts.controlPoints[j].len THEN RETURN[TRUE]; j _ j+1; ENDLOOP; RETURN[FALSE]; }; DescendAFeature: PROC [feature: FeatureData] RETURNS [done: BOOL _ FALSE] = { IF feature.type=slice THEN { shapeD: SliceDescriptor _ NARROW[feature.shape]; objects _ objects+1; IF shapeD.slice.class=NIL THEN trashedObjects _ trashedObjects+1 ELSE { dList: LIST OF SliceDescriptor; classType: ATOM _ GGSliceOps.GetType[shapeD.slice]; IF classType=$Outline OR classType = $Cluster THEN { dList _ GGParent.ListIncludedChildren[shapeD.slice, shapeD.parts, first]; FOR childList: LIST OF Slice _ GGParent.ListChildren[shapeD.slice, first], childList.rest UNTIL childList = NIL DO nextPart: SliceDescriptor _ dList.first; nextChild: Slice _ childList.first; IF nextChild.class=NIL THEN trashedSubObjects _ trashedSubObjects+1; IF nextChild#nextPart.slice THEN obsoleteDs _ obsoleteDs+1; IF GGSliceOps.GetType[nextChild]=$Traj THEN IF IsObsolete[nextPart] THEN obsoleteSeqs _ obsoleteSeqs+1; dList _ dList.rest; ENDLOOP; }; }; }; }; objects, obsoleteDs, obsoleteSeqs, trashedObjects, trashedSubObjects: INT _ 0; GGAlign.WalkSliceTriggers[ggData.hitTest.sceneBag, DescendAFeature]; Feedback.PutF[ggData.router, oneLiner, $Show, "SceneBag: %g objects, %g obsolete outline descriptors, %g obsolete sequences, %g trashed objects, %g trashed children", [integer[objects]], [integer[obsoleteDs]], [integer[obsoleteSeqs]], [integer[trashedObjects]], [integer[trashedSubObjects]] ]; }; DrawPolylines: PUBLIC UserInputProc = { GGWindow.RestoreScreenAndInvariants[paintAction: $PaintPolylines, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; UserTraceOn: PUBLIC UserInputProc = { GGUserInput.SetUserTraceOn[TRUE]; }; UserTraceOff: PUBLIC UserInputProc = { GGUserInput.SetUserTraceOn[FALSE]; }; RegisterEventProcs: PROC = { OPEN GGUserInput; RegisterAction[$CaretPositionFromSelection, CaretPositionFromSelection, rope]; RegisterAction[$CaretAngleFromSelection, CaretAngleFromSelection, refReal]; RegisterAction[$ShowCaretValues, ShowCaretValues, none]; RegisterAction[$ShowAnchorValues, ShowAnchorValues, none]; RegisterAction[$GrabInputFocus, GrabInputFocus, none]; RegisterAction[$SceneState, SceneState, none]; RegisterAction[$SceneBagState, SceneBagState, none]; RegisterAction[$DrawPolylines, DrawPolylines, none]; RegisterAction[$UserTraceOn, UserTraceOn, none]; RegisterAction[$UserTraceOff, UserTraceOff, none]; RegisterAction[$SetStraight, SetStraight, none]; RegisterAction[$SetArc, SetArc, none]; RegisterAction[$SetConic, SetConic, refReal]; RegisterAction[$SetBezier, SetBezier, none]; RegisterAction[$SetConstraintType, SetConstraintType, rope]; RegisterAction[$SetMakeConstrained, SetMakeConstrained, none]; RegisterAction[$ShowConstraintType, ShowConstraintType, none]; RegisterAction[$SetNaturalSpline, SetNaturalSpline, none]; RegisterAction[$SetBSpline, SetBSpline, none]; RegisterAction[$SelectMatchingCurve, SelectMatchingCurve, rope]; RegisterAction[$OrientCW, OrientCW, none]; RegisterAction[$OrientCCW, OrientCCW, none]; RegisterAction[$Reorient, Reorient, none]; RegisterAction[$ShowOrientation, ShowOrientation, none]; RegisterAction[$SetOddWrap, SetOddWrap, none]; RegisterAction[$SetNonZeroWrap, SetNonZeroWrap, none]; RegisterAction[$ShowWrapRule, ShowWrapRule, none]; RegisterAction[$HistoryTool, BuildAHistoryTool, none]; RegisterAction[$UndoOne, UndoOne, none]; RegisterAction[$ConvertTextToSplines, ConvertTextToSplines, none]; RegisterAction[$TiogaFillFromSelection, TiogaFillFromSelection, none]; RegisterAction[$EditTiogaFill, EditTiogaFill, none]; RegisterAction[$ScreenStyleKind, ScreenStyleKind, none]; RegisterAction[$PrintStyleKind, PrintStyleKind, none]; RegisterAction[$ShowStyleKind, ShowStyleKind, none]; RegisterAction[$SetNewlineFactor, SetNewlineFactor, refReal]; RegisterAction[$ShowNewlineFactor, ShowNewlineFactor, refReal]; RegisterAction[$SetWash, SetWash, rope]; }; RegisterEventProcs[]; END. 0GGEventImplE.mesa Contents: Once an event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module. Copyright Σ 1987, 1988, 1989 by Xerox Corporation. All rights reserved. Pier, May 20, 1992 1:46 pm PDT Bier, March 5, 1992 5:04 pm PST Doug Wyatt, December 5, 1989 2:07:40 pm PST Utility Routines Edit Menu ggData.refresh.startBoundBox^ _ GGScene.BoundBoxOfSelected[scene, normal, TRUE]^; ggData.refresh.startBoundBox^ _ GGScene.BoundBoxOfSelected[ggData.scene, normal]^; Curve Menu SetSnowflake: PUBLIC UserInputProc = { OPEN Vectors2d; MakeSnowflakeAux: PROC [seg: Segment, traj: Traj] RETURNS [patternTraj: Traj] = { length: REAL; p1, p2, p3: Point; sideVec, dir, x: Vector; seg1, seg2, seg3, seg4: Segment; lo: Point _ seg.lo; hi: Point _ seg.hi; success: BOOL _ FALSE; length _ Vectors2d.Distance[lo,hi]; dir _ Sub[hi,lo]; x _ Scale[dir, 1.0/3.0]; p1 _ Add[lo, x]; p3 _ Add[lo, Scale[dir, 2.0/3.0]]; sideVec _ Scale[VectorPlusAngle[dir, 90], length*RealFns.SqRt[3]/6.0]; p2 _ Add[lo, Vectors2d.Add[Scale[dir, 0.5], sideVec]]; patternTraj _ traj; seg1 _ GGSegment.MakeLine[p0: lo, p1: p1, props: seg.props]; seg2 _ GGSegment.MakeLine[p0: p1, p1: p2, props: seg.props]; seg3 _ GGSegment.MakeLine[p0: p2, p1: p3, props: seg.props]; seg4 _ GGSegment.MakeLine[p0: p3, p1: hi, props: seg.props]; success _ GGTraj.AddSegment[patternTraj, hi, seg1, lo]; success _ GGTraj.AddSegment[patternTraj, hi, seg2, lo]; success _ GGTraj.AddSegment[patternTraj, hi, seg3, lo]; success _ GGTraj.AddSegment[patternTraj, hi, seg4, lo]; }; SetPatternAux[ggData, MakeSnowflakeAux]; }; Enforce constraint at selection (joint or CP). GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGSliceOps.GetBoundBox[nextTrajD.slice]]; -- original bBox IF constrained THEN GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGSliceOps.GetBoundBox[nextTrajD.slice]]; -- constrained bBox ggData.refresh.startBoundBox^ _ GGBoundBox.emptyBoundBox^; -- null bound box Replace segs of the old type with segs of the new type, maintaining control point data if possible Replace old segments with segments of the new type, but if the old segment had control points, use these also as joints of new segments. Modifying All Selected Runs With A Call-Back Proc ForEachOutlineRun: For each selected run in the scene, apply the runProc, get the result and replace the old run with the result. If the result is NIL, this is a deletion. The result may have more or fewer segments than the original run, so we must proceed with caution. Our overall plan is this: Modify one run at a time removing from the scene the outline to which it belongs and adding to the scene, at appropriate priorities the new outline(s) that results from the change. This scheme is complicated by the desire to select the newly created outlines. To get around this, we select as "active" all objects originally selected as "normal" and use the active selections to keep track of the work left to be done. [] _ GGScene.WalkSelectedSlices[scene, first, DoSaveSelections, normal]; outlineD is a summary of the active bits of this outline that are set. We proceed as follows: 1) Find the first active run of outlineD. 2) Replace it with the desired new run to create a new trajectory and a new outline. 3) Extract a new outlineD from the active bits of the result. 4) If outlineD is empty, we are done. Otherwise return to step 1. If newOutline is NIL at the end, no replaces were done. Use GGSelect.ForEachOutlineRun to replace all selected runs with runs of a new type. Strategy: Replace each selected run with a new trajectory which has a segment (of the proper type) for each joints/control point span. Note that we we copy verbatim all segs which were already of the specified type. The new segments inherit color and stroke properties from the old ones. [run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Traj] ggData.refresh.startBoundBox^ _ ForEachOutlineRun[ggData.scene, normal, RunOfSegs, TRUE, TRUE]^; Use GGSelect.ForEachOutlineRun to replace all selected runs with runs of a new type. Strategy: Replace each selected run with a new trajectory which has a segment (of the proper type) for each joints/control point span. [run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Traj] ggData.refresh.startBoundBox^ _ GGSelect.ForEachOutlineRun[ggData.scene, normal, RunOfSegs, TRUE, TRUE]^; [run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Traj] ggData.refresh.startBoundBox^ _ ForEachOutlineRun[ggData.scene, normal, RunToSpline, TRUE, TRUE]^; Text Menu complete document to topLevel Box complete document to topLevel Outline and its Box children tricky case. Walk all the leaf boxes in the cluster. If they do not share a single parent, abort. If there is a single parent, it is either an outline or a cluster. If the parent is an outline, then fill it. If the parent is a cluster with a single Box child, then fill the box. Otherwise, abort. if you get here, a single parent was selected. It could be an outline or a cluster complete document to Box ggData.refresh.startBoundBox^ _ GGScene.BoundBoxOfSelected[ggData.scene, normal]^; complete document from top level slice tricky case. Walk all the leaf boxes in the cluster. If they do not share a single parent, abort. If there is a single parent, it is either an outline or a cluster. If the parent is an outline, then show it. If the parent is a cluster with a single selected Box child, then show the box. Otherwise, abort. if you get here, a single parent with some selected Box children was selected. It could be an outline or a cluster complete document from child Box MapPropsAction: TYPE = PROC [name: ATOM, value: REF] RETURNS [quit: BOOL _ FALSE]; IF loc.node=NIL THEN RETURN [FALSE]; -- not filled at all GGSelect.SliceWalkProc ggData.refresh.startBoundBox _ GGScene.BoundBoxOfSelected[scene, normal, TRUE]; Leave only the new outlines selected. bBox _ GGScene.BoundBoxOfSelected[scene, normal]; GGBoundBox.EnlargeByBox[ggData.refresh.startBoundBox, bBox]; Color Menus startSample, endSample IN [0.0 .. 1.0] Caret Menu Debug2 Menu TEST if this sequence cannot possibly encode its trajectory, probably because the trajectory has been modified. Not a guarantee that it accurately encodes anything. Caret Menu Debug2 Menu Curve Menu RegisterAction[$SetSnowflake, SetSnowflake, none]; Edit Menu Text Menu Color/Fill Menus Κ5"– "cedar" style•NewlineDelimiter ˜Icodešœ™šΟnœx™€KšœH™HKšœ™K™K™,—K™šΟk ˜ Jšœͺ˜ͺ—K˜š œžœž˜JšžœίžœQ˜ΉJšžœ-ž˜9K˜Kšœ ž œ$Οc˜[Kšœ žœžœŸ˜SKšœžœžœŸ˜UK˜Kšœ žœ˜+Kšœ žœ˜)Kšœ žœ˜'Kšœžœ˜%Kšœžœ˜ Kšœžœ&˜AKšœžœ"˜5Kšœ žœ˜-Kšœ žœ˜*Kšœ žœ˜!Kšœžœ˜'Kšœžœ˜1Kšœ žœžœ˜'Kšœ žœ ˜1Kšœžœ˜!Kšœžœ˜3Kšœ žœ˜-Kšœžœ˜!Kšœžœžœ!˜˜>KšœV˜VK–<[outline: GGModelTypes.Slice, scene: GGModelTypes.Scene]šœ6˜6J˜—Kšœ'  œ˜DKšœ,‘œ‘œ‘˜@K–T[scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass _ normal]šœR™RKšœ   œ!žœžœ˜RKšœ   œ ˜šœ]˜]šžœ ž˜K˜ K˜Kšœ˜Kšžœž˜K˜—Kšœ˜—Kšœ}žœžœ˜›K˜—K˜—K™Kšœ ™ Kšœžœ˜Kšœžœ˜Kšœžœžœžœžœžœžœžœ˜ZK˜šœžœžœ˜3Kšœq˜qKšœ˜K˜—š œžœ˜%šœ˜"Kšœ(˜(Kšœ˜—Kšœ,˜,KšœŸ˜K˜—šœžœ˜ š œ˜K˜ KšœC˜CKšœ+˜+Kšœ˜—Kšœ&˜&KšœŸ ˜K˜—š œžœ™&Kšžœ ™šœžœžœ™QKšœžœ™ Kšœ™Kšœ™Kšœ ™ Kšœ™Kšœ™Kšœ ž œ™Kšœ#™#Kšœ™Kšœ™Kšœ™Kšœ"™"KšœF™FKšœ6™6Kšœ™K–N[p0: GGBasicTypes.Point, p1: GGBasicTypes.Point, props: LIST OF REF ANY]šœ<™˜>Kš œ žœ žœžœžœžœ6˜]K˜——Kšžœ˜—K˜K˜—š œžœ2žœ˜LKšœT™TKšœš™’–B -- [run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Traj]š œ ˜Kšœžœ™>Kšœžœ˜+Kšœžœ ˜(KšœL˜LK˜5Kš žœžœžœžœžœŸ˜CK˜(šžœ;žœžœž˜PšžœžœŸ˜:KšœMŸ˜[šœ˜Kš žœ žœžœžœ-ŸR˜ΏKšžœ/Ÿ ˜SK˜—Kšžœžœ˜—Kšžœ˜—Kšœ žœžœ˜?K˜—K˜Kšžœ-žœ`˜“šžœ˜Kšœ  œŸ˜NKšœ  œ"žœžœ™`Jšœ   œ ˜Jš œ  œ  œ"žœžœžœ˜gK˜"Kšœžœ˜!Kšœ   œ ˜Kšœt˜tKšœ}žœžœ˜›K˜—K˜K˜—Kšœžœžœžœ˜Tšœžœ+˜@KšœT™TKšœ™‡–B -- [run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Traj]š œ˜Kšœžœ™>Kšœžœ˜+Kšœžœ ˜(KšœL˜LKšœ5˜5Kš žœžœžœžœžœŸ˜CKšœ(˜(šžœ;žœžœž˜PšžœžœŸ˜:Kšœ#˜#Kšžœžœ˜—Kšžœ˜—Kšœ žœžœ˜?Kšœ˜—Kšœ\žœžœ™iJšœ   œ ˜Jš œ  œ  œ"žœžœžœ˜gK˜"Kšœžœ˜!Kšœ=˜=Kšœ}žœžœ˜›Kšœ˜Kš˜—šœžœ˜*K˜1K˜K˜—š œžœ˜$K˜/K˜K˜—š œžœ:˜Iš œ ˜Kšœžœ™>Kšœžœ˜Kšœžœ˜Kšœžœ˜+Kšœžœ ˜(K˜K˜Kšœžœžœ"˜FKš œ!žœžœžœ2žœ2˜ΈKšœS˜SKšœ5˜5Kšœ ž œ˜Kšœ žœ˜Kš žœžœžœžœžœ˜%Kšžœžœžœ˜@Kšœ(˜(Kšœ2˜2K˜šžœ;žœžœž˜Pšžœžœžœ'ž˜5Kšœ1˜1K˜0K˜Kšžœ˜—Jšœ$˜$J˜Kšžœ˜—KšžœžœŸ=˜hKšœ2žœ˜7Jšœ&˜&Jšœ0˜0Jšžœžœ žœžœ˜Jšœ žœžœ˜?Kšœ˜—KšžœDžœa˜¬šžœ˜Kšœ  œŸ˜OKšœUžœžœ™bJšœ   œ ˜Jš œ  œ  œ$žœžœžœ˜iK˜"Kšœžœ˜!Kšœ   œ ˜KšœS˜SKšœ}žœžœ˜›K˜—K˜K™—šœžœ˜-Kšœ žœžœ˜+š œžœžœžœžœ˜EKšœ5žœ ˜Hšžœkžœžœž˜Œ–[atom: ATOM]šžœ<žœžœžœžœ žœžœ˜iKšœ.žœ ˜TKšœA˜AKšœ   œ˜3Kšœ˜—Kšžœ˜—J˜—Kšœ   œ!žœžœ˜PKšœ+˜+KšœE˜EK–Ϋ[feedback: ViewerClasses.Viewer, msgType: GGError.MsgType, format: ROPE _ NIL, v1: IO.Value _ [null[]], v2: IO.Value _ [null[]], v3: IO.Value _ [null[]], v4: IO.Value _ [null[]], v5: IO.Value _ [null[]]]šœt˜tKšœjžœžœ˜‰K˜—K˜Kšœ ™ šœ˜)Kšœžœ\˜pKšœ žœ<˜LKšžœžœ@˜Ušžœ˜Kšœ žœžœ˜Kšœ˜KšœU˜Ušžœ3žœ˜;Kšœ9˜9Kš œžœžœžœžœžœ'˜lKšœŸ?˜ZK˜—Kšœ  œ$Ÿ˜Všžœ%ž˜/šœ ˜ KšŸ!™!Kšœažœ˜fK˜—šœ ˜ KšŸ:™:Kšœ_žœ˜dK˜—šœ ˜ K™­š  œžœžœžœžœ˜LKšœŸ?˜UKšžœžœžœ%Ÿ˜MKš žœ"žœžœžœŸ,˜dKšœ˜K˜—K˜KšœŸ˜(Kšœ žœ˜Kšœc˜cšžœžœžœ žœžœžœžœ žœ˜K˜Kšžœ&žœq˜šžœ˜š œžœžœžœžœ˜OKšœ2Ÿ˜BKšœ;˜;šžœ"žœ˜*šžœžœžœ%žœ˜˜>Jšœ=˜=J˜—šžœ˜Kšœžœžœ˜+Kšœ2˜2KšœM˜MKšœ<˜˜>Jšœ=˜=J˜—K˜—Kšœ žœžœ žœ˜K˜KšžœBžœu˜½šžœ˜Kšœ  œ‘œ ˜0KšœIžœ™OKšœ   œ!žœžœ˜RKšœ+‘œ‘œ ‘˜NKš %™%Kšœ%Ÿ4˜Yš žœžœžœžœžœž˜AJšœ6˜6Jšžœ˜—Jšœ"˜"Kšœ1™1Kšœ<™˜>Kšœ˜Kšžœ˜—K˜—K˜Kšœ žœ˜Kšœžœ˜$Kšœ žœ˜Kšœžœ˜"Kšœ/˜/Kšœžœ2˜9Kšœ˜Kšœ(˜(Kšœ(˜(K˜Kšœ7˜7šžœž˜Kšœžœ˜#Kšœ#˜#˜Kšœ˜Kšœ@žœ˜KK–0[feedback: Feedback.FeedbackData, msg: ROPE]šžœ žœžœ˜-Kšœ)žœ ˜;K˜—Kšžœžœ˜—Kšœ>˜>Kšœ>˜>Kšœ>˜>Kšœ<˜˜>Kšœ>˜>Kšœ:˜:Kšœ.˜.Kšœ@˜@K˜K˜Kš  ™ Kšœ*˜*Kšœ,˜,Kšœ*˜*Kšœ8˜8Kšœ.˜.Kšœ6˜6Kšœ2˜2Kšœ6˜6Kšœ(˜(KšœB˜BK˜Kš  ™ KšœF˜FKšœ4˜4Kšœ8˜8Kšœ6˜6Kšœ4˜4Kšœ=˜=Kšœ?˜?K˜Kš ™Kšœ(˜(K˜—K˜K˜J˜Kšžœ˜—…—Α$v