GGEventImplE.mesa
Copyright Ó 1987, 1988, 1989, 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, June 30, 1992 2:03 pm PDT
Bier, August 23, 1993 5:04 pm PDT
Doug Wyatt, April 20, 1992 10:55 am PDT
DIRECTORY
Ascii, Atom, CodeTimer, ColorTool, CubicSplines, EditSpanSupport, Feedback, FeedbackTypes, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGCoreOps, GGCoreTypes, GGDescribe, GGDragTypes, GGEvent, GGFont, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMultiGravity, GGOutline, GGParent, GGParseIn, GGRefresh, GGRefreshTypes, GGScene, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGTrajTypes, GGUserInput, GGUserProfile, GGUtility, GGViewerOps, GGWindow, Imager, ImagerColor, ImagerInterpress, ImagerPixel, ImagerPixelArray, ImagerSample, ImagerTransformation, IO, IPMaster, NamedColors, NodeProps, Random, Real, RealFns, Rope, SF, TextNode, TiogaAccess, TiogaAccessViewers, 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, GGViewerOps, GGWindow, Imager, ImagerColor, ImagerPixel, ImagerPixelArray, ImagerSample, ImagerTransformation, IO, NodeProps, Real, RealFns, Rope, TextNode, TiogaAccess, TiogaAccessViewers, Vectors2d, ViewerOps, ViewerTools
EXPORTS GGEvent, GGHistoryTypes, GGInterfaceTypes = BEGIN
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;
MakeCurveProc: TYPE = GGTrajTypes.MakeCurveProc;
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;
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;
Utility Routines
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];
};
};
Edit Menu
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, 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];
ggData.refresh.startBoundBox^ ← GGScene.BoundBoxOfSelected[scene, normal, TRUE]^;
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, 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];
ggData.refresh.startBoundBox^ ← GGScene.BoundBoxOfSelected[ggData.scene, 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];
};
};
Curve Menu
gScale: REAL ¬ 0.33;
gAngle: REAL ¬ 33;
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
SetSnowflake:
PUBLIC UserInputProc = {
OPEN Vectors2d;
MakeSnowflakeAux:
PROC [seg: Segment, traj: Slice]
RETURNS [patternTraj: Slice] = {
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];
};
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 = {
Enforce constraint at selection (joint or CP).
cpGen: ControlPointGenerator;
jointGen: JointGenerator;
thisCPNum, thisSegNum: NAT;
constrained, done: BOOL ¬ FALSE;
scene: Scene ¬ ggData.scene;
DoConstrain:
PROC [nextTrajD: SliceDescriptor]
RETURNS [done:
BOOL ¬
FALSE] = {
GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGSliceOps.GetBoundBox[nextTrajD.slice]]; -- original bBox
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 GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGSliceOps.GetBoundBox[nextTrajD.slice]]; -- constrained bBox
IF constrained THEN GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[nextTrajD.slice], NIL]; -- constrained bBox
};
IF ggData.drag.editConstraints = none THEN RETURN[];
ggData.refresh.startBoundBox^ ← GGBoundBox.emptyBoundBox^; -- null bound box
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
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.
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 ¬ GGOutline.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 ¬ GGOutline.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.WalkSelectedSlices[scene, first, DoSaveSelections, normal];
[] ¬ GGScene.WalkSlices[scene, first, DoSaveSelections];
[] ¬ GGScene.WalkSelectedSlices[scene, leaf, DoReplaceRuns, active, $Outline];
[] ¬ GGScene.WalkSlices[scene, first, DoRestoreSelections];
};
SetCurveAux:
PROC [ggData: GGData, makeCurve: MakeCurveProc, type:
ATOM] = {
Use 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.
RunOfSegs: RunProc = {
[run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Slice]
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 => GGTraj.SegToSegMatchCp[seg, traj, makeCurve, type]; -- modify traj
$CubicSpline => {
IF type=$Line OR (runData.segCount=1 AND runData.role#open) THEN GGTraj.SegAndCpsToSegs[seg, traj, makeCurve, type] -- Splines are changed to multiple lines...(or multiple segs if spline was cyclic)
ELSE GGTraj.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.
SetStartBox[ggData: ggData, dragInProgress:
FALSE, selectedCPs:
TRUE];
need to bound selectedCPs here because curve bound boxes may not include CPs.
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];
};
};
PatternProc: TYPE = PROC [seg: Segment, traj: Slice] RETURNS [patternTraj: Slice];
SetPatternAux:
PROC [ggData: GGData, pattern: PatternProc] = {
Use ForEachOutlineRun to replace all selected runs with new runs determined by a graphical replacement pattern
RunOfSegs: RunProc = {
[run: SliceDescriptor] RETURNS [traj: Slice]
Replace each selected straight line segment with the result of applying the graphical replacement to that segment. Weld together the resulting new pieces, so that each selected run maps to a single new trajectory.
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 => traj ¬ pattern[seg, traj];
ENDCASE => [] ¬ GGTraj.AddSegment[traj, hi, GGSegment.CopySegment[seg], lo];
ENDLOOP;
GGSliceOps.SetStrokeJoint[traj, NIL, runData.strokeJoint, NIL];
};
bBox: BoundBox;
GGRefresh.
SetStartBox[ggData: ggData, dragInProgress:
FALSE, selectedCPs:
TRUE];
need to bound selectedCPs here because curve bound boxes may not include CPs.
bBox ¬ ForEachOutlineRun[ggData.scene, normal, RunOfSegs, TRUE, TRUE];
GGRefresh.EnlargeStartBox[ggData, bBox, NIL];
GGCaret.NoAttractor[ggData.caret];
GGCaret.SitOn[ggData.caret, NIL];
Feedback.Append[ggData.router, oneLiner, $Feedback, "Pattern modified"];
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 = {
[run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Slice]
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.
SetStartBox[ggData: ggData, dragInProgress:
FALSE, selectedCPs:
TRUE];
need to bound selectedCPs here because curve bound boxes may not include CPs.
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, selectionChanged: TRUE, okToSkipCapture: FALSE];
};
Text Menu
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[GGViewerOps.GetSelectionContents[]]#0
THEN {
reader: TiogaAccess.Reader = TiogaAccessViewers.FromSelection[];
ref: TextNode.Ref ¬ IF TiogaAccess.EndOf[reader] THEN NIL ELSE TextNode.Root[TiogaAccess.GetLocation[reader].node];
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 => {
complete document to topLevel Box
GGSlice.SetBoxText[selectedD.slice, [myRef, 0], GGSlice.GetBoxText[selectedD.slice].screenStyle, NIL];
};
$Outline => {
complete document to topLevel Outline and its Box children
GGOutline.SetFillText[selectedD.slice, myRef, GGSlice.GetBoxText[selectedD.slice].screenStyle, NIL];
};
$Cluster => {
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.
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 {
if you get here, a single parent was selected. It could be an outline or a cluster
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 {
complete document to Box
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"];
ggData.refresh.startBoundBox^ ← GGScene.BoundBoxOfSelected[ggData.scene, normal]^;
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 => {
complete document from top level slice
MakeViewer[selectedD.slice];
};
$Cluster => {
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.
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 {
if you get here, a single parent with some selected Box children was selected. It could be an outline or a cluster
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 {
complete document from child Box
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 = {
MapPropsAction: TYPE = PROC [name: ATOM, value: REF] RETURNS [quit: BOOL ← FALSE];
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 loc.node=NIL THEN RETURN [FALSE]; -- not filled at all
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] = {
GGSelect.SliceWalkProc
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];
ggData.refresh.startBoundBox ← GGScene.BoundBoxOfSelected[scene, normal, TRUE];
GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE];
[] ¬ GGScene.WalkSelectedSlices[scene, leaf, ConvertToSplines, normal, $Text];
Leave only the new outlines selected.
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];
bBox ← GGScene.BoundBoxOfSelected[scene, normal];
GGBoundBox.EnlargeByBox[ggData.refresh.startBoundBox, bBox];
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];
};
};
Color Menus
RGBFromColor:
PROC [color: ImagerColor.ConstantColor]
RETURNS [ImagerColor.
RGB] ~ {
RETURN[ImagerColor.RGBFromColor[ImagerColor.NarrowToOpConstantColor[color]]];
};
CreateColorWash:
PROC [startColor: Imager.ConstantColor, regionWidth:
REAL, toAtom:
ATOM, vec: Imager.
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] = {
startSample, endSample IN [0.0 .. 1.0]
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 ¬ RGBFromColor[startColor];
SELECT toAtom
FROM
$White => endRGB ¬ [1.0, 1.0, 1.0];
$Black => endRGB ¬ [0.0, 0.0, 0.0];
$ColorTool => {
endColor: Imager.ConstantColor ¬ NIL;
endColor ¬ ColorTool.GetColor[ ! ColorTool.NoColorToolViewer => CONTINUE;];
IF endColor=NIL THEN endColor ¬ Imager.black;
endRGB ¬ RGBFromColor[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[sWhite: maxSample]];
EXITS
Abort => success ¬ FALSE;
};
SetWash: 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] = {
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];
};
};
Caret Menu
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 [<x>, <y>] (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.PutFL[ggData.router, oneLiner, $Show, "%g position: [%g, %g], angle: %g", LIST[[rope[name]], [real[point.x/scale]], [real[point.y/scale]], [real[angle]]]];
}
ELSE {
Feedback.PutFL[ggData.router, oneLiner, $Show,
"Show%g: no %g", LIST[[rope[name]], [rope[name]] ]];
};
};
ShowCaretValues: UserInputProc = {
ShowValuesOfCaret[ggData, ggData.caret, "Caret"];
};
ShowAnchorValues: UserInputProc = {
ShowValuesOfCaret[ggData, ggData.anchor, "Anchor"];
};
GrabInputFocus: UserInputProc = {
GGState.GrabInputFocus[ggData];
};
Debug2 Menu
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.PutFL[ggData.router, oneLiner, $Show, "Scene: %g objects, %g trashed objects, %g trashed children, %g leaf objects", LIST[[integer[objects]], [integer[trashedObjects]], [integer[trashedSubObjects]], [integer[leafCount]] ]];
};
SceneBagState: UserInputProc = {
IsObsolete:
PUBLIC
PROC [seq:
Sequence]
RETURNS [
BOOL] = {
TEST if this sequence cannot possibly encode its trajectory, probably because the trajectory has been modified. Not a guarantee that it accurately encodes anything.
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.PutFL[ggData.router, oneLiner, $Show, "SceneBag: %g objects, %g obsolete outline descriptors, %g obsolete sequences, %g trashed objects, %g trashed children", LIST[[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;
Caret Menu
RegisterAction[$CaretPositionFromSelection, CaretPositionFromSelection, rope];
RegisterAction[$CaretAngleFromSelection, CaretAngleFromSelection, refReal];
RegisterAction[$ShowCaretValues, ShowCaretValues, none];
RegisterAction[$ShowAnchorValues, ShowAnchorValues, none];
RegisterAction[$GrabInputFocus, GrabInputFocus, none];
Debug2 Menu
RegisterAction[$SceneState, SceneState, none];
RegisterAction[$SceneBagState, SceneBagState, none];
RegisterAction[$DrawPolylines, DrawPolylines, none];
RegisterAction[$UserTraceOn, UserTraceOn, none];
RegisterAction[$UserTraceOff, UserTraceOff, none];
Curve Menu
RegisterAction[$SetStraight, SetStraight, none];
RegisterAction[$SetArc, SetArc, none];
RegisterAction[$SetSnowflake, SetSnowflake, 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];
Edit Menu
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];
Text Menu
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];
Color/Fill Menus
RegisterAction[$SetWash, SetWash, rope];
};
RegisterEventProcs[];
END.