GGTouchImpl.mesa
Copyright c 1985 by Xerox Corporation. All rights reserved.
Last edited by Bier on November 7, 1985 10:24:00 pm PST
Contents: Implements a data structures which remembers which trajectory is touching which other trajectory and where.
DIRECTORY
GGDescribe,
GGError,
GGInterfaceTypes,
GGModelTypes,
GGObjects,
GGSelect,
GGShapes,
GGTouch,
GGTransform,
Imager,
ImagerTransformation,
Rope,
Rosary;
GGTouchImpl:
CEDAR
PROGRAM
IMPORTS GGDescribe, GGError, GGObjects, GGSelect, GGShapes, GGTransform, Rosary
EXPORTS GGTouch =
BEGIN
Cluster: TYPE = GGModelTypes.Cluster;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
Joint: TYPE = GGModelTypes.Joint;
JointGenerator: TYPE = GGObjects.JointGenerator;
Outline: TYPE = GGModelTypes.Outline;
Point: TYPE = GGModelTypes.Point;
Segment: TYPE = GGModelTypes.Segment;
Sequence: TYPE = GGModelTypes.Sequence;
Traj: TYPE = GGModelTypes.Traj;
SegmentGenerator: TYPE = GGObjects.SegmentGenerator;
TrajGenerator: TYPE = GGObjects.TrajGenerator;
TouchGroup: TYPE = REF TouchGroupObj;
TouchGroupObj: TYPE = GGModelTypes.TouchGroupObj;
TouchingPartType: TYPE = GGModelTypes.TouchingPartType;
TouchItem: TYPE = REF TouchItemObj;
TouchItemObj: TYPE = GGModelTypes.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.IsSelected[thisItem.joint, gargoyleData, normal]
THEN {
thisItem.joint.touchItem ← NIL;
group.list ← DeleteTouchItemFromList[thisItem, group.list];
};
};
segment => {
IF
NOT GGSelect.IsSelected[thisItem.seg, gargoyleData, 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 ← GGObjects.JointsInSequence[seq];
FOR i:
INT ← GGObjects.NextJoint[jointGen], GGObjects.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 ← GGObjects.SegmentsInSequence[seq];
FOR seg: Segment ← GGObjects.NextSegment[segGen], GGObjects.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 ← GGObjects.JointsInTraj[traj];
FOR i:
INT ← GGObjects.NextJoint[jointGen], GGObjects.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 ← GGObjects.SegmentsInTraj[traj];
FOR seg: Segment ← GGObjects.NextSegment[segGen], GGObjects.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 ← GGObjects.JointsInSequence[seq];
FOR i:
INT ← GGObjects.NextJoint[jointGen], GGObjects.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 ← GGObjects.SegmentsInSequence[seq];
FOR seg: Segment ← GGObjects.NextSegment[segGen], GGObjects.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 ← GGObjects.JointsInTraj[traj];
FOR i:
INT ← GGObjects.NextJoint[jointGen], GGObjects.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 ← GGObjects.SegmentsInTraj[traj];
FOR seg: Segment ← GGObjects.NextSegment[segGen], GGObjects.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 ← GGObjects.TrajsInOutline[outline];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen]
UNTIL traj =
NIL
DO
TrajDeleted[traj, gargoyleData];
ENDLOOP;
};
ClusterDeleted:
PUBLIC
PROC [cluster: Cluster, 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[text];
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: BOOL ← FALSE;
[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: BOOL ← FALSE;
[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.