GGTouchImpl.mesa
Copyright c 1985 by Xerox Corporation. All rights reserved.
Last edited by Bier on December 28, 1986 3:08:57 pm PST
Contents: Implements a data structures which remembers which trajectory is touching which other trajectory and where.
Pier, October 20, 1986 5:16:08 pm PDT
DIRECTORY
GGBasicTypes, GGDescribe, GGError, GGInterfaceTypes, GGModelTypes, GGObjects, GGOutline, GGSegmentTypes, GGSelect, GGShapes, GGSequence, GGTouch, GGTransform, Imager, ImagerTransformation, Rope, Rosary;
GGTouchImpl: CEDAR PROGRAM
IMPORTS GGDescribe, GGError, GGObjects, GGOutline, GGSelect, GGSequence, GGShapes, GGTransform, Rosary
EXPORTS GGTouch =
BEGIN
Slice: TYPE = GGModelTypes.Slice;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
Joint: TYPE = GGModelTypes.Joint;
JointGenerator: TYPE = GGSequence.JointGenerator;
Outline: TYPE = GGModelTypes.Outline;
Point: TYPE = GGBasicTypes.Point;
Segment: TYPE = GGSegmentTypes.Segment;
Sequence: TYPE = GGModelTypes.Sequence;
Traj: TYPE = GGModelTypes.Traj;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
TrajGenerator: TYPE = GGObjects.TrajGenerator;
TouchGroup: TYPE = REF TouchGroupObj;
TouchGroupObj: TYPE = GGSegmentTypes.TouchGroupObj;
TouchingPartType: TYPE = GGSegmentTypes.TouchingPartType;
TouchItem: TYPE = REF TouchItemObj;
TouchItemObj: TYPE = GGSegmentTypes.TouchItemObj;
TouchItemGenerator: TYPE = REF TouchItemGeneratorObj;
TouchItemGeneratorObj: TYPE = GGTouch.TouchItemGeneratorObj;
NotYetImplemented: PUBLIC SIGNAL = CODE;
CreateTouchGroup: PUBLIC PROC [gargoyleData: GargoyleData, point: Point] RETURNS [empty: TouchGroup] = {
empty ← NEW[TouchGroupObj ← [
list: NIL,
point: point
]];
gargoyleData.touching ← CONS[empty, gargoyleData.touching];
};
AddJoint: PUBLIC PROC [traj: Traj, joint: Joint, touchGroup: TouchGroup] RETURNS [item: TouchItem] = {
item ← NEW[TouchItemObj ← [
group: touchGroup,
traj: traj,
touchingPartType: joint,
joint: joint,
seg: NIL,
segPoint: [0.0,0.0]
]];
touchGroup.list ← CONS[item, touchGroup.list];
};
AddSegment: PUBLIC PROC [traj: Traj, seg: Segment, segPoint: Point, touchGroup: TouchGroup] RETURNS [item: TouchItem] = {
item ← NEW[TouchItemObj ← [
group: touchGroup,
traj: traj,
touchingPartType: segment,
joint: NIL,
seg: seg,
segPoint: segPoint
]];
touchGroup.list ← CONS[item, touchGroup.list];
};
MergeGroups: PUBLIC PROC [group1, group2: TouchGroup, gargoyleData: GargoyleData] = {
touchItemGen: TouchItemGenerator;
IF group1 = group2 THEN RETURN;
touchItemGen ← AllTouchItems[group1];
FOR item1: TouchItem ← NextTouchItem[touchItemGen], NextTouchItem[touchItemGen] UNTIL item1 = NIL DO
item1.group ← group2;
group2.list ← CONS[item1, group2.list];
ENDLOOP;
group1.list ← NIL;
gargoyleData.touching ← DeleteTouchGroupFromList[group1, gargoyleData.touching];
};
Updating when something has moved -- the basic routines.
InitializeTouching: PUBLIC PROC [gargoyleData: GargoyleData] = {
FOR groupList: LIST OF TouchGroup ← gargoyleData.touching, groupList.rest UNTIL groupList = NIL DO
groupList.first.updated ← FALSE;
ENDLOOP;
};
UpdateTouchGroup: PUBLIC PROC [item: TouchItem, point: Point, gargoyleData: GargoyleData] = {
group: TouchGroup ← item.group;
itemGen: TouchItemGenerator;
IF group.updated THEN {
The group has been updated, already. Ignore this update.
FOR itemList: LIST OF TouchItem ← group.list, itemList.rest UNTIL itemList = NIL DO
IF itemList.first = item THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
RETURN;
};
group.updated ← TRUE;
group.point ← point;
itemGen ← AllTouchItems[group];
FOR thisItem: TouchItem ← NextTouchItem[itemGen], NextTouchItem[itemGen] UNTIL thisItem = NIL DO
SELECT thisItem.touchingPartType FROM
joint => {
IF NOT GGSelect.IsSelectedInFull[thisItem.joint, gargoyleData.scene, normal] THEN {
thisItem.joint.touchItem ← NIL;
group.list ← DeleteTouchItemFromList[thisItem, group.list];
};
};
segment => {
IF NOT GGSelect.IsSelectedInFull[thisItem.seg, gargoyleData.scene, normal] THEN {
thisItem.seg.touchItemList ← DeleteTouchItemFromList[thisItem, thisItem.seg.touchItemList];
group.list ← DeleteTouchItemFromList[thisItem, group.list];
};
};
ENDCASE => ERROR;
ENDLOOP;
IF group.list = NIL THEN ERROR;
IF group.list.rest = NIL THEN { -- only one item left. Delete group.
thisItem: TouchItem ← group.list.first;
SELECT thisItem.touchingPartType FROM
joint => {
thisItem.joint.touchItem ← NIL;
group.list ← DeleteTouchItemFromList[thisItem, group.list];
};
segment => {
thisItem.seg.touchItemList ← DeleteTouchItemFromList[thisItem, thisItem.seg.touchItemList];
group.list ← DeleteTouchItemFromList[thisItem, group.list];
};
ENDCASE => ERROR;
gargoyleData.touching ← DeleteTouchGroupFromList[group, gargoyleData.touching];
};
};
Updating when something has moved -- convenience routines (which repeatedly call the basic routines for many joints and segments).
SequenceMoved: PUBLIC PROC [seq: Sequence, gargoyleData: GargoyleData, transform: ImagerTransformation.Transformation ← NIL] = {
jointGen: JointGenerator;
joint: Joint;
segGen: SegmentGenerator;
jointGen ← GGSequence.JointsInSequence[seq];
FOR i: INT ← GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL i = -1 DO
joint ← NARROW[Rosary.Fetch[seq.traj.joints, i]];
IF joint.touchItem # NIL THEN {
UpdateTouchGroup[joint.touchItem, joint.point, gargoyleData];
};
ENDLOOP;
segGen ← GGSequence.SegmentsInSequence[seq];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO
FOR itemList: LIST OF TouchItem ← seg.touchItemList, itemList.rest UNTIL itemList = NIL DO
itemList.first.segPoint ← GGTransform.Transform[transform, itemList.first.segPoint];
UpdateTouchGroup[itemList.first, itemList.first.segPoint, gargoyleData];
ENDLOOP;
ENDLOOP;
};
TrajMoved: PUBLIC PROC [traj: Traj, gargoyleData: GargoyleData, transform: ImagerTransformation.Transformation ← NIL] = {
jointGen: JointGenerator;
joint: Joint;
segGen: SegmentGenerator;
jointGen ← GGSequence.JointsInTraj[traj];
FOR i: INT ← GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL i = -1 DO
joint ← NARROW[Rosary.Fetch[traj.joints, i]];
IF joint.touchItem # NIL THEN {
UpdateTouchGroup[joint.touchItem, joint.point, gargoyleData];
};
ENDLOOP;
segGen ← GGSequence.SegmentsInTraj[traj];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO
FOR itemList: LIST OF TouchItem ← seg.touchItemList, itemList.rest UNTIL itemList = NIL DO
itemList.first.segPoint ← GGTransform.Transform[transform, itemList.first.segPoint];
UpdateTouchGroup[itemList.first, itemList.first.segPoint, gargoyleData];
ENDLOOP;
ENDLOOP;
};
Updating when something has been deleted -- the basic routine.
DeleteTouchItem: PUBLIC PROC [touchItem: TouchItem, gargoyleData: GargoyleData] = {
Remove the touch item from the group's list. If the group has only one remaining object, remove the group from the global list.
lastItem: TouchItem;
group: TouchGroup ← touchItem.group;
touchItem.joint ← NIL;
touchItem.seg ← NIL;
group.list ← DeleteTouchItemFromList[touchItem, group.list];
IF group.list.rest = NIL THEN {
lastItem ← group.list.first;
IF lastItem.touchingPartType = joint THEN {
lastItem.joint.touchItem ← NIL;
lastItem.joint ← NIL;
}
ELSE {
lastItem.seg.touchItemList ← DeleteTouchItemFromList[lastItem, lastItem.seg.touchItemList];
lastItem.seg ← NIL;
};
group.list ← NIL;
gargoyleData.touching ← DeleteTouchGroupFromList[group, gargoyleData.touching];
};
};
Updating when something has been deleted -- convenience routines (which repeatedly call the basic routines for many joints and segments).
SequenceDeleted: PUBLIC PROC [seq: Sequence, gargoyleData: GargoyleData] = {
jointGen: JointGenerator;
joint: Joint;
segGen: SegmentGenerator;
jointGen ← GGSequence.JointsInSequence[seq];
FOR i: INT ← GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL i = -1 DO
joint ← NARROW[Rosary.Fetch[seq.traj.joints, i]];
IF joint.touchItem # NIL THEN {
DeleteTouchItem[joint.touchItem, gargoyleData];
joint.touchItem ← NIL;
};
ENDLOOP;
segGen ← GGSequence.SegmentsInSequence[seq];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO
FOR itemList: LIST OF TouchItem ← seg.touchItemList, itemList.rest UNTIL itemList = NIL DO
DeleteTouchItem[itemList.first, gargoyleData];
ENDLOOP;
seg.touchItemList ← NIL;
ENDLOOP;
};
TrajDeleted: PUBLIC PROC [traj: Traj, gargoyleData: GargoyleData] = {
jointGen: JointGenerator;
joint: Joint;
segGen: SegmentGenerator;
jointGen ← GGSequence.JointsInTraj[traj];
FOR i: INT ← GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL i = -1 DO
joint ← NARROW[Rosary.Fetch[traj.joints, i]];
IF joint.touchItem # NIL THEN {
DeleteTouchItem[joint.touchItem, gargoyleData];
joint.touchItem ← NIL;
};
ENDLOOP;
segGen ← GGSequence.SegmentsInTraj[traj];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO
FOR itemList: LIST OF TouchItem ← seg.touchItemList, itemList.rest UNTIL itemList = NIL DO
DeleteTouchItem[itemList.first, gargoyleData];
ENDLOOP;
seg.touchItemList ← NIL;
ENDLOOP;
};
OutlineDeleted: PUBLIC PROC [outline: Outline, gargoyleData: GargoyleData] = {
trajGen: TrajGenerator;
trajGen ← GGOutline.TrajsInOutline[outline];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen] UNTIL traj = NIL DO
TrajDeleted[traj, gargoyleData];
ENDLOOP;
};
SliceDeleted: PUBLIC PROC [slice: Slice, gargoyleData: GargoyleData] = {
ERROR NotYetImplemented;
};
Browsing.
TouchGroupOfItem: PUBLIC PROC [item: TouchItem] RETURNS [touchGroup: TouchGroup] = {
touchGroup ← item.group;
};
AllTouchItems: PUBLIC PROC [touchGroup: TouchGroup] RETURNS [touchItemGen: TouchItemGenerator] = {
touchItemGen ← NEW[TouchItemGeneratorObj ← [
list: touchGroup.list
]];
};
NextTouchItem: PUBLIC PROC [touchItemGen: TouchItemGenerator] RETURNS [item: TouchItem] = {
IF touchItemGen.list = NIL THEN RETURN[NIL];
item ← touchItemGen.list.first;
touchItemGen.list ← touchItemGen.list.rest;
};
Drawing.
DrawAllTouchPoints: PUBLIC PROC [dc: Imager.Context, gargoyleData: GargoyleData] = {
FOR groupList: LIST OF TouchGroup ← gargoyleData.touching, groupList.rest UNTIL groupList = NIL DO
GGShapes.DrawPlus[dc, groupList.first.point];
ENDLOOP;
};
DescribeAllTouchPoints: PUBLIC PROC [gargoyleData: GargoyleData] = {
text: Rope.ROPE;
FOR groupList: LIST OF TouchGroup ← gargoyleData.touching, groupList.rest UNTIL groupList = NIL DO
text ← GGDescribe.DescribeTouchGroup[groupList.first];
GGError.AppendTypescript[gargoyleData.feedback, text, oneLiner];
ENDLOOP;
};
List Processing Utilities (will polymorphism never come?)
FindTouchItemAndNeighbors: PROC [entity: TouchItem, entityList: LIST OF TouchItem] RETURNS [beforeEnt, ent, afterEnt: LIST OF TouchItem] = {
lastE: LIST OF TouchItem ← NIL;
eList: LIST OF TouchItem ← entityList;
IF eList = NIL THEN ERROR EntityNotFound;
UNTIL eList = NIL DO
IF eList.first = entity THEN {
beforeEnt ← lastE; ent ← eList; afterEnt ← eList.rest; RETURN};
lastE ← eList;
eList ← eList.rest;
ENDLOOP;
SIGNAL EntityNotFound;
};
DeleteTouchItemFromList: PUBLIC PROC [entity: TouchItem, entityList: LIST OF TouchItem] RETURNS [smallerList: LIST OF TouchItem] = {
beforeEnt, ent, afterEnt: LIST OF TouchItem;
notFound: BOOLFALSE;
[beforeEnt, ent, afterEnt] ← FindTouchItemAndNeighbors[entity, entityList];
IF notFound THEN RETURN[entityList];
IF beforeEnt = NIL THEN smallerList ← afterEnt
ELSE {
beforeEnt.rest ← afterEnt;
smallerList ← entityList;
};
}; -- end of DeleteTouchItemFromList
FindTouchGroupAndNeighbors: PROC [entity: TouchGroup, entityList: LIST OF TouchGroup] RETURNS [beforeEnt, ent, afterEnt: LIST OF TouchGroup] = {
lastE: LIST OF TouchGroup ← NIL;
eList: LIST OF TouchGroup ← entityList;
IF eList = NIL THEN ERROR EntityNotFound;
UNTIL eList = NIL DO
IF eList.first = entity THEN {
beforeEnt ← lastE; ent ← eList; afterEnt ← eList.rest; RETURN};
lastE ← eList;
eList ← eList.rest;
ENDLOOP;
SIGNAL EntityNotFound;
};
EntityNotFound: PUBLIC SIGNAL = CODE;
DeleteTouchGroupFromList: PUBLIC PROC [entity: TouchGroup, entityList: LIST OF TouchGroup] RETURNS [smallerList: LIST OF TouchGroup] = {
beforeEnt, ent, afterEnt: LIST OF TouchGroup;
notFound: BOOLFALSE;
[beforeEnt, ent, afterEnt] ← FindTouchGroupAndNeighbors[entity, entityList];
IF notFound THEN RETURN[entityList];
IF beforeEnt = NIL THEN smallerList ← afterEnt
ELSE {
beforeEnt.rest ← afterEnt;
smallerList ← entityList;
};
}; -- end of DeleteTouchGroupFromList
END.