-- Compiler ObjectOps/n;bind griffin;griffin -- mstone November 10, 1980 5:29 PM DIRECTORY GriffinDefs: FROM "GriffinDefs", GriffinStartDefs: FROM "GriffinStartDefs", StringDefs: FROM "StringDefs", 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: FROM "ScreenDefs" USING [ClipPointToScreen], RelationDefs: FROM "RelationDefs"; ObjectOps: PROGRAM IMPORTS GriffinDefs, MenuDefs, ObjectDefs, StringDefs, GriffinMemoryDefs, RealFns, XFormDefs, PointDefs, RefreshDefs, RelationDefs, ScreenDefs EXPORTS GriffinDefs, GriffinStartDefs,OpDefs = BEGIN OPEN ObjectDefs,PointDefs,GriffinMemoryDefs,RefreshDefs; xftype: {map,scale,scalex,scaley,rotate}; oltype: {top,bottom,up1,down1}; TL,BR: ScrPt; 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: 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: 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: 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: POINTER TO SelObj, item: ObjectHandle]; ChangeOverlap: MenuDefs.MenuProc = BEGIN OPEN StringDefs; string: STRING ← MenuDefs.MenuString[item]; topObj,bottomObj: POINTER TO SelObj ← NIL; ptr,next: POINTER TO SelObj ← NIL; ListSelected: PROCEDURE[obj: ObjectHandle]=BEGIN selObj: POINTER TO SelObj ←Allocate[SIZE[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: POINTER TO SelObj ←topObj; FOR selObj ← topObj, selObj.down UNTIL selObj=NIL DO FlipUpObject[selObj.item]; ENDLOOP; END; Down: PROCEDURE=BEGIN selObj: POINTER TO SelObj ; FOR selObj ← bottomObj, selObj.up UNTIL selObj=NIL DO FlipDownObject[selObj.item]; ENDLOOP; END; Bottom: PROCEDURE=BEGIN selObj: POINTER TO SelObj ←topObj; FOR selObj ← topObj, selObj.down UNTIL selObj=NIL DO SinkObject[selObj.item]; ENDLOOP; END; Top: PROCEDURE=BEGIN selObj: POINTER TO 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 EquivalentStrings[string,"Top"] => top, EquivalentStrings[string,"Bottom"] => bottom, EquivalentStrings[string,"Up One"] => up1, EquivalentStrings[string,"Down One"] => 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; Free[ptr]; ptr ← next; ENDLOOP; END; XForm: MenuDefs.MenuProc = BEGIN OPEN StringDefs; ENABLE XFormDefs.ProblemWithXForms => BEGIN IF EquivalentString[string,"Singular transform"] THEN SIGNAL GriffinDefs.UserMessage["Transform will flatten picture"] ELSE SIGNAL GriffinDefs.UserMessage["ProblemWith Transform"]; END; string: STRING ← MenuDefs.MenuString[item]; cps: DESCRIPTOR FOR ARRAY OF ObjPt ← ReadCPs[]; mspace: XFormDefs.XFormMatrix; matrix: XFormDefs.XFMDescriptor ← DESCRIPTOR[mspace]; 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: UNSPECIFIED] = 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 LENGTH[cps] <=1 THEN BEGIN Free[BASE[cps]]; 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 EquivalentStrings[string,"Map"] => map, EquivalentStrings[string,"Scale"] => scale, EquivalentStrings[string,"ScaleX"] => scalex, EquivalentStrings[string,"ScaleY"] => scaley, EquivalentStrings[string,"Rotate"] => 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 LENGTH[cps]=2 AND xftype=map THEN BEGIN ForAllSelectedDo[MoveObjs]; END ELSE BEGIN ForAllSelectedDo[XFormObjs]; END; Free[BASE[cps]]; ForAllSelectedDo[ PlotAndMark]; RestoreScreen[]; END; Toggle: MenuDefs.MenuProc = BEGIN OPEN MenuDefs; IF IsSelected[item] THEN Deselect[item] ELSE Select[item]; END; Rotate: PROCEDURE[matrix: XFormDefs.XFMDescriptor] = BEGIN OPEN XFormDefs; cps: DESCRIPTOR FOR ARRAY OF ObjPt ← ReadCPs[]; npts: INTEGER ← LENGTH[cps]; 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: DESCRIPTOR FOR ARRAY OF ObjPt ← ReadCPs[]; npts: INTEGER ← LENGTH[cps]; 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: DESCRIPTOR FOR ARRAY OF ObjPt ← ReadCPs[]; npts: INTEGER ← LENGTH[cps]; pts: DESCRIPTOR FOR ARRAY OF ObjPt; free: BOOLEAN ← FALSE; 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 ← DESCRIPTOR[Allocate[4*SIZE[ ObjPt]],4]; free ← TRUE; 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 ← DESCRIPTOR[Allocate[6*SIZE[ ObjPt]],6]; free ← TRUE; 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; =6 => pts ← cps; ENDCASE=> pts ← DESCRIPTOR[BASE[cps],6]; Translate[dorig,matrix]; SELECT npts FROM =4 => XForm4Pts[pts,matrix]; >=6 => XForm6Pts[pts,matrix]; ENDCASE; Translate[negdorig,matrix]; Free[BASE[cps]]; IF free THEN Free[BASE[pts]]; END; Cluster: 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: 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: MenuDefs.MenuProc = BEGIN DS: ObjectProc = BEGIN EraseAndSave[ReturnSelected[obj]]; DeSelectObject[obj]; END; ObjectDefs.ForAllSelectedDo[DS]; RestoreScreen[]; END; SelectAll: 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]; []←DeleteObject[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; 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.