-- Compiler ObjectOps/n;bind griffin;griffin
-- mstone November 10, 1980 5:29 PM
-- Last Edited by: Stone, November 14, 1983 3:08 pm
DIRECTORY
GriffinDefs: FROM "GriffinDefs",
Rope USING [ROPE, Equal],
MenuDefs: FROM "MenuDefs",
ObjectDefs: FROM "ObjectDefs",
RealFns: FROM "RealFns",
StyleDefs: FROM "StyleDefs",
PointDefs: FROM "PointDefs",
OpDefs: FROM "OpDefs",
XFormDefs: FROM "XFormDefs",
GriffinMemoryDefs: FROM "GriffinMemoryDefs",
RefreshDefs: FROM "RefreshDefs",
ScreenDefs USING [ClipPointToScreen],
GriffinMenusInterface,
RelationDefs: FROM "RelationDefs";
ObjectOps: PROGRAM
IMPORTS GriffinDefs, MenuDefs, ObjectDefs, Rope, GriffinMemoryDefs, RealFns, XFormDefs, PointDefs, RefreshDefs, RelationDefs, ScreenDefs
EXPORTS GriffinDefs, OpDefs, GriffinMenusInterface =
BEGIN OPEN ObjectDefs,PointDefs,GriffinMemoryDefs,RefreshDefs;
xftype: {map,scale,scalex,scaley,rotate};
oltype: {top,bottom,up1,down1};
TL,BR: ScrPt;
ROPE: TYPE = Rope.ROPE;
OverallMBB: PROCEDURE[obj: ObjectHandle]=
BEGIN
IF obj.tl[X]<TL[X] THEN TL[X] ← obj.tl[X];
IF obj.tl[Y]<TL[Y] THEN TL[Y] ← obj.tl[Y];
IF obj.br[X] >BR[X] THEN BR[X] ← obj.br[X];
IF obj.br[Y] >BR[Y] THEN BR[Y] ← obj.br[Y];
END;
AnySelected: PROCEDURE RETURNS[BOOLEAN] =
BEGIN
anyselected: BOOLEAN ← FALSE;
IsSelected: PROCEDURE[obj: ObjectHandle]=BEGIN
anyselected ← TRUE;
END;
ForAllSelectedDo[IsSelected];
RETURN[anyselected];
END;
Delete: PUBLIC MenuDefs.MenuProc =
BEGIN
DeleteObjs: PROCEDURE[obj: ObjectHandle]=BEGIN
DeSelectObject[obj];
EraseAndSave[obj];
obj.deleted ← TRUE;
END;
IF AnySelected[] THEN BEGIN
ExpungeObjects[];
ForAllSelectedDo[DeleteObjs];
RestoreScreen[];
END;
END;
Undo: PUBLIC MenuDefs.MenuProc =
BEGIN
Undelete: ObjectProc = BEGIN
IF ~obj.deleted THEN RETURN;
obj.deleted ← FALSE;
IF obj.cluster # 0 THEN [] ← SelectCluster[obj.cluster]
ELSE [] ← SelectObject[obj];
MarkBox[obj.tl,obj.br,obj];
END;
ForAllObjects[Undelete];
RestoreScreen[];
END;
Transfer: PUBLIC MenuDefs.MenuProc =
BEGIN
view: View ← IF objectMenu.view=main THEN alternate ELSE main;
token: ObjectHandle ← NIL;
TransferObjs: PROCEDURE[obj: ObjectHandle]=BEGIN
EraseAndSave[obj];
IF (token ← ReturnSelected[obj]) # NIL
THEN BEGIN
EraseAndSave[token];
token.view ← view;
END;
obj.view ← view;
END;
IF ~AnySelected[] THEN GriffinDefs.UserMessage["Please make a selection"];
ForAllSelectedDo[TransferObjs];
RestoreScreen[];
END;
SelObj: TYPE = RECORD[up,down: REF SelObj, item: ObjectHandle];
ChangeOverlap: PUBLIC MenuDefs.MenuProc =
BEGIN
string: ROPE ← MenuDefs.MenuString[item];
topObj,bottomObj: REF SelObj ← NIL;
ptr,next: REF SelObj ← NIL;
ListSelected: PROCEDURE[obj: ObjectHandle]=BEGIN
selObj: REF SelObj 𡤌Zone.NEW[SelObj];
selObj^ ← [up: NIL,down: topObj,item: obj];
IF bottomObj=NIL THEN bottomObj ← topObj ← selObj
ELSE BEGIN topObj.up ← selObj; topObj ← selObj; END;
END;
Up: PROCEDURE=BEGIN
selObj: REF SelObj ←topObj;
FOR selObj ← topObj, selObj.down UNTIL selObj=NIL DO
FlipUpObject[selObj.item];
ENDLOOP;
END;
Down: PROCEDURE=BEGIN
selObj: REF SelObj ;
FOR selObj ← bottomObj, selObj.up UNTIL selObj=NIL DO
FlipDownObject[selObj.item];
ENDLOOP;
END;
Bottom: PROCEDURE=BEGIN
selObj: REF SelObj ←topObj;
FOR selObj ← topObj, selObj.down UNTIL selObj=NIL DO
SinkObject[selObj.item];
ENDLOOP;
END;
Top: PROCEDURE=BEGIN
selObj: REF SelObj ;
FOR selObj ← bottomObj, selObj.up UNTIL selObj=NIL DO
FloatObject[selObj.item];
ENDLOOP;
END;
--make a list of all items affected. Work in different order for different cases
IF ~AnySelected[] THEN GriffinDefs.UserMessage["Please make a selection"];
ForAllSelectedDo[ListSelected];
IF bottomObj=NIL THEN RETURN; --none found
oltype ← SELECT TRUE FROM
Rope.Equal[string,"Top",FALSE] => top,
Rope.Equal[string,"Bottom",FALSE] => bottom,
Rope.Equal[string,"Up One",FALSE] => up1,
Rope.Equal[string,"Down One",FALSE] => down1,
ENDCASE => ERROR;
SELECT oltype FROM
top => Top[];
bottom => Bottom[];
up1 => Up[];
down1 => Down[];
ENDCASE;
ForAllSelectedDo[ PlotAndMark];
RestoreScreen;
ptr ← bottomObj;
UNTIL ptr=NIL DO
next ←ptr.up;
ptr.up ← NIL;
ptr ← next;
ENDLOOP;
END;
XForm: PUBLIC MenuDefs.MenuProc =
BEGIN
ENABLE XFormDefs.ProblemWithXForms => BEGIN
IF Rope.Equal[string,"Singular transform",FALSE] THEN
SIGNAL GriffinDefs.UserMessage["Transform will flatten picture"]
ELSE SIGNAL GriffinDefs.UserMessage["ProblemWith Transform"];
END;
string: ROPE ← MenuDefs.MenuString[item];
cps: PointDefs.ObjPtSequence ← ReadCPs[];
matrix: XFormDefs.XFMDescriptor ← CZone.NEW[XFormDefs.XFormMatrix];
clusterRelation: RelationDefs.Relation;
MoveObjs: PROCEDURE[obj: ObjectHandle]=BEGIN
MoveObject[obj,ObjToScr[cps[0]],ObjToScr[cps[1]]];
END;
CopyObjs: PROCEDURE[obj: ObjectHandle]=BEGIN
OPEN RelationDefs;
newobj: ObjectHandle;
newcluster: ClusterID;
IF NOT obj.selected OR NOT Visible[obj] THEN RETURN;
newobj ← CopyObject[obj];
IF obj.cluster # 0 THEN
BEGIN
IF Right[clusterRelation,obj.cluster] = notFound
THEN BEGIN
newcluster ← GetNextClusterID[];
AddPair[clusterRelation, obj.cluster,newcluster];
END
ELSE newcluster ← Right[clusterRelation,obj.cluster];
EraseAndSave[ReturnSelected[obj]];
newobj.cluster ← newcluster;
END
ELSE BEGIN
[] ← SelectObject[newobj];
EraseAndSave[ReturnSelected[obj]];
DeSelectObject[obj];
END;
END;
SelectNewClusters: PROCEDURE[leftPart, rightPart: CARDINAL] =
BEGIN
-- selects new clusters formed after XForm with copy
[] ← SelectCluster[rightPart];
DeSelectCluster[leftPart];
END;
XFormObjs: PROCEDURE[obj: ObjectHandle]=BEGIN
ObjectDefs.XFormObject[obj,matrix];
END;
IF cps=NIL OR cps.length <=1 THEN BEGIN
GriffinDefs.UserMessage["All transforms need at least two points"];
RETURN;
END;
IF ~AnySelected[] THEN GriffinDefs.UserMessage["Please make a selection"];
XFormDefs.InitXForms[matrix];
xftype ← SELECT TRUE FROM
Rope.Equal[string,"Map",FALSE] => map,
Rope.Equal[string,"Scale",FALSE] => scale,
Rope.Equal[string,"ScaleX",FALSE] => scalex,
Rope.Equal[string,"ScaleY",FALSE] => scaley,
Rope.Equal[string,"Rotate",FALSE] => rotate,
ENDCASE => ERROR;
SELECT xftype FROM
map => Map[matrix];
scale => Scale[matrix,both];
scalex => Scale[matrix,xonly];
scaley => Scale[matrix,yonly];
rotate => Rotate[matrix];
ENDCASE;
EraseAndSaveAllCPs[];
DeleteAllCPs[];
--will use the selection
IF MenuDefs.IsSelected[copy] THEN
BEGIN
clusterRelation ← RelationDefs.CreateRelation[];
ForAllObjectsThroughObject[CopyObjs, GetTopPictureObj[]];
RelationDefs.ForAllPairs[clusterRelation,SelectNewClusters];
RelationDefs.DestroyRelation[clusterRelation];
END
ELSE ForAllSelectedDo[ EraseAndSave];
IF cps.length=2 AND xftype=map THEN BEGIN
ForAllSelectedDo[MoveObjs];
END
ELSE BEGIN
ForAllSelectedDo[XFormObjs];
END;
ForAllSelectedDo[ PlotAndMark];
RestoreScreen[];
END;
Toggle: PUBLIC MenuDefs.MenuProc =
BEGIN OPEN MenuDefs;
IF IsSelected[item] THEN Deselect[item] ELSE Select[item];
END;
Rotate: PROCEDURE[matrix: XFormDefs.XFMDescriptor] =
BEGIN OPEN XFormDefs;
cps: PointDefs.ObjPtSequence ← ReadCPs[];
npts: INTEGER ← (IF cps=NIL THEN 0 ELSE cps.length);
dorig: ObjPt ← cps[0];
negdorig: ObjPt ← [-cps[0][X],-cps[0][Y]];
dx,dy,theta: REAL;
IF npts>3 THEN GriffinDefs.UserMessage["Only 2 and 3 point rotate is valid"];
dx ← cps[1][X]-cps[0][X];
dy ← cps[1][Y]-cps[0][Y];
theta ← RealFns.ArcTan[dy,dx];
IF npts>=3 THEN BEGIN
theta1: REAL;
dx ← cps[2][X]-cps[0][X];
dy ← cps[2][Y]-cps[0][Y];
theta1 ← RealFns.ArcTan[dy,dx];
theta ← theta1-theta;
END;
Translate[dorig,matrix];
Rotate[theta,z,matrix];
Translate[negdorig,matrix];
END;
--will mirror. Imagine a pinned point and a new point
Scale: PROCEDURE[matrix: XFormDefs.XFMDescriptor,direction: {both,xonly,yonly}] =
BEGIN OPEN XFormDefs;
cps: PointDefs.ObjPtSequence ← ReadCPs[];
npts: INTEGER ← (IF cps=NIL THEN 0 ELSE cps.length);
sx,sy: REAL;
dorig: ObjPt ← cps[0];
tl,br: ObjPt;
negdorig: ObjPt ← [-cps[0][X],-cps[0][Y]];
dx1,dy1,dx2,dy2: REAL;
IF npts>3 THEN GriffinDefs.UserMessage["Only 2 and 3 point scale is valid"];
--scale max dimension into 2 pt span
IF npts=2 THEN BEGIN
TL ← [77777B,77777B]; --init for OverallMBB
BR ← [0,0];
ForAllSelectedDo[OverallMBB];
tl ← ScrToObj[TL];
br ← ScrToObj[BR];
dx1 ← br[X]-tl[X];
dy1 ← tl[Y]-br[Y]; --object space
dx2 ← ABS[cps[1][X]-cps[0][X]];
dy2 ← ABS[cps[1][Y]-cps[0][Y]];
END
-- assume 3 points
ELSE BEGIN
dx2 ← cps[2][X]-cps[0][X];
dy2 ← cps[2][Y]-cps[0][Y];
dx1 ← cps[1][X]-cps[0][X];
dy1 ← cps[1][Y]-cps[0][Y];
END;
Translate[dorig,matrix];
SELECT direction FROM
both => BEGIN
IF dx1=0 OR dy1=0 THEN GriffinDefs.UserMessage["Transform will flatten picture"];
sx ← dx2/dx1;
sy ← dy2/dy1;
Scale[[sx,sy],matrix];
END;
xonly => BEGIN
IF dx1=0 THEN GriffinDefs.UserMessage["Transform will flatten picture"];
sx ← dx2/dx1;
Scale[[sx,1],matrix];
END;
yonly => BEGIN
IF dy1=0 THEN GriffinDefs.UserMessage["Transform will flatten picture"];
sy ← dy2/dy1;
Scale[[1,sy],matrix];
END;
ENDCASE;
Translate[negdorig,matrix];
END;
Map: PROCEDURE[matrix: XFormDefs.XFMDescriptor] =
BEGIN OPEN XFormDefs;
cps: PointDefs.ObjPtSequence ← ReadCPs[];
npts: INTEGER ← (IF cps=NIL THEN 0 ELSE cps.length);
pts: PointDefs.ObjPtSequence;
dorig: ObjPt ← cps[0];
negdorig: ObjPt ← [-cps[0][X],-cps[0][Y]];
IF npts>6 THEN GriffinDefs.UserMessage["Maximum of 6 points for map"];
SELECT npts FROM
--0,1 NOP, 2 is a move, which is done above
=3 => BEGIN
pts ← CZone.NEW[ObjPtSequenceRec[4]];
pts[0] ← cps[0];
pts[1] ← cps[1];
pts[2] ← cps[0];
pts[3] ← cps[2];
npts ← 4;
END;
=4 => pts ← cps;
=5 => BEGIN
pts ← CZone.NEW[ObjPtSequenceRec[6]];
pts[0] ← cps[0];
pts[1] ← cps[1];
pts[2] ← cps[2];
pts[3] ← cps[0];
pts[4] ← cps[3];
pts[5] ← cps[4];
npts ← 6;
END;
ENDCASE=> pts ← cps;
Translate[dorig,matrix];
SELECT npts FROM
=4 => XForm4Pts[pts,matrix];
>=6 => XForm6Pts[pts,matrix];
ENDCASE;
Translate[negdorig,matrix];
END;
Cluster: PUBLIC MenuDefs.MenuProc =
BEGIN
id: ClusterID ← GetNextClusterID[];
called: BOOLEAN ← FALSE;
token: ObjectHandle ← NIL;
clu: ObjectProc=BEGIN
obj.cluster ← id;
called ← TRUE;
IF token # NIL
THEN token ← DeleteObject[token];
token ← ReturnSelected[obj];
EraseAndSave[token];
END;
IF ~AnySelected[] THEN GriffinDefs.UserMessage["Please make a selection"];
ForAllSelectedDo[clu];
IF called THEN BEGIN
GriffinDefs.ShowUserMessage["New cluster made"];
RestoreScreen[];
END;
END;
UnCluster: PUBLIC MenuDefs.MenuProc =
BEGIN
called: BOOLEAN ← FALSE;
ctoken: ObjectHandle;
clu: ObjectProc=BEGIN
IF obj.cluster IN [0..OpenCluster] THEN RETURN;
obj.cluster ← 0;
called ← TRUE;
IF (ctoken ← ReturnSelected[obj]) # NIL THEN
BEGIN
EraseAndSave[ctoken];
RETURN;
END;
obj.selected ← FALSE;
[] ← SelectObject[obj];
END;
IF ~AnySelected[] THEN GriffinDefs.UserMessage["Please make a selection"];
ForAllSelectedDo[clu];
IF called THEN GriffinDefs.ShowUserMessage["Cluster unmade"];
END;
DeselectAll: PUBLIC MenuDefs.MenuProc =
BEGIN
DS: ObjectProc = BEGIN
EraseAndSave[ReturnSelected[obj]];
DeSelectObject[obj];
END;
ObjectDefs.ForAllSelectedDo[DS];
RestoreScreen[];
END;
SelectAll: PUBLIC MenuDefs.MenuProc =
BEGIN OPEN ObjectDefs;
SelectObjs: ObjectProc=BEGIN
token: ObjectHandle ← IF obj.cluster = 0
THEN SelectObject[obj]
ELSE SelectCluster[obj.cluster];
IF token#NIL THEN PlotAndMark[token];
END;
ForAllVisibleObjects[SelectObjs];
RestoreScreen[];
END;
--make a translated copy of indicated object or set, given xy,xy
CopyObjects: PUBLIC PROCEDURE[Down,Up: ScrPt] =
BEGIN OPEN ObjectDefs;
new: ObjectHandle ← GetObjectHandle[Down];
selected: BOOLEAN;
id: ClusterID ← 0;
co: ObjectProc = BEGIN
IF selected THEN BEGIN
EraseAndSave[ReturnSelected[obj]];
IF id = 0 THEN DeSelectObject[obj];
END;
obj ← CopyObject[obj] ;
obj.cluster ← id;
MoveObject[obj,Down,Up];
PlotAndMark[obj];
IF selected AND id = 0 THEN [] ← SelectObject[obj];
-- clusters are selected below.
END;
IF new=NIL THEN RETURN;
selected ← new.selected;
IF new.objectType=menu OR new.objectType=token OR new.cluster=OpenCluster
THEN RETURN;
--click doesn't got thru move/refresh stuff
IF ABS[Down[X]-Up[X]]<1 AND ABS[Down[Y]-Up[Y]]<1 THEN RETURN;
IF new.cluster=0
THEN co[new] -- not a clustered object
ELSE BEGIN
id ← GetNextClusterID[];
ForAllInCluster[new.cluster,co];
IF selected THEN BEGIN
DeSelectCluster[new.cluster];
[] ← SelectCluster[id];
END;
END;
RestoreScreen[];
END;
MoveObjects: PUBLIC PROCEDURE[Down,Up: ScrPt] =
BEGIN
object: ObjectDefs.ObjectHandle ← ObjectDefs.GetObjectHandle[Down] ;
move: ObjectProc = BEGIN
EraseAndSave[obj];
ObjectDefs.MoveObject[obj,Down,Up];
PlotAndMark[obj];
END;
IF object=NIL THEN RETURN;
--click doesn't got thru move/refresh stuff
IF ABS[Down[X]-Up[X]]<1 AND ABS[Down[Y]-Up[Y]]<1 THEN RETURN;
IF object.cluster=OpenCluster THEN RETURN; --unfortuneate
IF object.cluster=0 THEN move[object] ELSE ForAllInCluster[object.cluster,move];
RestoreScreen[];
END;
PlaceControlPoint: PUBLIC PROCEDURE[pt: ScrPt] =
BEGIN
top: ObjectHandle ← GetObjectHandle[pt];
IF top=NIL OR top.objectType#menu
THEN ObjectDefs.AddToken[ScreenDefs.ClipPointToScreen[pt], CP]
END;
DeleteControlPoint: PUBLIC PROCEDURE[Down,Up: ScrPt] =
BEGIN OPEN ObjectDefs;
DelCP: ObjectProc = BEGIN
IF obj=NIL THEN RETURN;
WITH obj SELECT FROM
token=>
IF tokenType = CP THEN BEGIN
EraseAndSave[obj];
[]leteObject[obj];
END;
ENDCASE;
END;
IF NOT ForAllInBoxDo[Down, Up, DelCP] THEN
ForObjectPointedAtDo[Down, Up, DelCP];
RestoreScreen[];
END;
DeselectObjects: PUBLIC PROCEDURE[Down,Up: ScrPt] =
BEGIN
Desel: ObjectProc = BEGIN
IF obj=NIL OR obj.objectType=token OR obj.objectType=menu THEN RETURN;
IF obj.cluster = 0 THEN DS[obj]
ELSE ForAllInCluster[obj.cluster, DS];
END;
DS: ObjectProc = BEGIN
EraseAndSave[ReturnSelected[obj]];
DeSelectObject[obj];
END;
IF NOT ForAllPictureObjectsInBoxDo[Down, Up, Desel]
THEN ForObjectPointedAtDo[Down, Up, Desel];
RestoreScreen[];
END;
SelectObjects: PUBLIC PROCEDURE[Down,Up: ScrPt] =
BEGIN OPEN ObjectDefs,RefreshDefs;
Sel: ObjectProc = BEGIN
IF obj=NIL OR obj.objectType=token OR obj.objectType=menu OR obj.cluster=OpenCluster
THEN RETURN;
S[obj];
END;
S: ObjectProc = BEGIN
obj ← IF obj.cluster = 0 THEN SelectObject[obj]
ELSE SelectCluster[obj.cluster];
--get the token, may return NIL if already selected
-- if clustered, will select whole cluster
IF obj#NIL THEN PlotAndMark[obj];
END;
IF NOT ForAllPictureObjectsInBoxDo[Down, Up, Sel]
THEN ForObjectPointedAtDo[Down, Up, Sel];
RestoreScreen[];
END;
ForObjectPointedAtDo: PROCEDURE[Down,Up: ScrPt,proc: ObjectProc] =
BEGIN
upObj,downObj: ObjectHandle;
upObj ← GetObjectHandle[Up];
IF upObj=NIL THEN RETURN;
downObj ← GetObjectHandle[Down];
IF upObj=downObj OR downObj=NIL THEN proc[upObj] ;
END;
objectMenu, xformMenu, overlapMenu: PUBLIC MenuDefs.MenuHandle←NIL;
copy: MenuDefs.MenuItemHandle←NIL;
StartObjectMenus: PUBLIC PROC = {
objectMenu ← MenuDefs.CreateMenu[horizontal, [100,600], NIL];
[]←MenuDefs.AddMenuItem[objectMenu,"Delete",Delete];
[]←MenuDefs.AddMenuItem[objectMenu,"Undo",Undo];
[]←MenuDefs.AddMenuItem[objectMenu,"Select all",SelectAll];
[]←MenuDefs.AddMenuItem[objectMenu,"Deselect all",DeselectAll];
[]←MenuDefs.AddMenuItem[objectMenu,"Cluster",Cluster];
[]←MenuDefs.AddMenuItem[objectMenu,"Uncluster",UnCluster];
[]←MenuDefs.AddMenuItem[objectMenu,"Transfer",Transfer];
xformMenu ← MenuDefs.CreateMenu[vertical, [400,400], NIL];
[]←MenuDefs.AddMenuItem[xformMenu,"Map",XForm];
[]←MenuDefs.AddMenuItem[xformMenu,"Scale",XForm];
[]←MenuDefs.AddMenuItem[xformMenu,"ScaleX",XForm];
[]←MenuDefs.AddMenuItem[xformMenu,"ScaleY",XForm];
[]←MenuDefs.AddMenuItem[xformMenu,"Rotate",XForm];
copy ← MenuDefs.AddMenuItem[xformMenu,"Use Copy",Toggle];
overlapMenu ← MenuDefs.CreateMenu[vertical, [200,250], NIL];
[]←MenuDefs.AddMenuItem[overlapMenu,"Top",ChangeOverlap];
[]←MenuDefs.AddMenuItem[overlapMenu,"Bottom",ChangeOverlap];
[]←MenuDefs.AddMenuItem[overlapMenu,"Up One",ChangeOverlap];
[]←MenuDefs.AddMenuItem[overlapMenu,"Down One",ChangeOverlap];
};
END.