_THE DESIGN AND IMPLEMENTATION OF PIE MENUS_ by Don Hopkins [LISTING ONE] % Code to implement the "8 Days a Week" Pie Menu % by Don Hopkins /pie framebuffer /new ClassPieMenu send def [ (Today) (Sunday) (Monday) (Tuesday) (Wednesday) (Thursday) (Friday) (Saturday) ] /setitemlist pie send 90 /setinitialangle pie send false /setclockwise pie send /can framebuffer /new ClassPieMenuCanvas send def pie /setpiemenu can send /minsize {100 100} /installmethod can send /win can framebuffer /new ClassBaseWindow send def /new ClassEventMgr send /activate win send /place win send /map win send [LISTING TWO] /Layout { % - => - PieGSave self setcanvas /LayoutInit self send /LayoutValidateItems self send /LayoutItemRadius self send /LayoutOuterRadius self send grestore } def /LayoutInit { % - => - % Deflate the menu. /Radius 0 def % Figure the slice width. /SliceWidth 360 /itemcount self send 1 max div def % Point the initial slice in the initial angle. /ThisAngle InitialAngle store } def /LayoutValidateItems { % - => - % Loop through the items, validating each one. ItemList { begin % item % Measure the item. /DisplayItem load DisplayItemSize /ItemHeight exch def /ItemWidth exch def % Remember the angle and the direction. /Angle ThisAngle def /DX Angle cos def /DY Angle sin def % Figure the offset from the tip of the inner radius % spoke to the lower left item corner, according to % the direction of the item. % % Items at the very top (bottom) are centered on their % bottom (top) edge. Items to the left (right) are % centered on their right (left) edge. % DX abs .05 lt { % tippy top or bippy bottom % Offset to the North or South edge of the item. /XOffset ItemWidth -.5 mul def /YOffset DY 0 lt {ItemHeight neg} {0} ifelse def } { % left or right % Offset to the East or West edge of the item. /XOffset DX 0 lt {ItemWidth neg} {0} ifelse def /YOffset ItemHeight -.5 mul def } ifelse % Twist around to the next item. /ThisAngle ThisAngle SliceWidth Clockwise? {sub} {add} ifelse NormalAngle store end % item } forall } def /LayoutItemRadius { % - => - % Figure the inner item radius, at least enough to prevent % the items from overlapping. /ItemRadius RadiusMin def /itemcount self send 3 gt { % No sweat if 3 or less. % Check each item against its next neighbor. 0 1 /itemcount self send 1 sub { /I exch def /NextI I 1 add /itemcount self send mod def % See if these two items overlap. % If they do, keep pushing the item radius out % by RadiusStep until they don't. { I /CalcRect self send NextI /CalcRect self send rectsoverlap not {exit} if % They don't overlap! % They overlap. Push them out a notch and try again. /ItemRadius ItemRadius RadiusStep add def } loop } for % Now that we've gone around once checking each pair, % none of them overlap any more! } if % Add in some more space to be nice. /ItemRadius ItemRadius RadiusExtra add def } def /LayoutOuterRadius { % - => - % Now we need to calculate the outer radius, based on the radius % of the farthest item corner. During the loop, Radius actually % holds the square of the radius, since we're comparing it against % squared item corner radii anyway. /Radius ItemRadius dup mul def ItemList { begin % item % Remember the location to center the item edge. /x DX ItemRadius mul def /y DY ItemRadius mul def % Remember the location of the item's SouthWest corner. /ItemX x XOffset add round def /ItemY y YOffset add round def % Figure the distance of the item's farthest corner. % This is easy 'cause we can fold all the items into % the NorthEast quadrant and get the same result. DX abs .05 lt { % tippy top or bippy bottom % (|x|,|y|) is South edge: radius^2 of NorthEast corner x abs ItemWidth .5 mul add dup mul y abs ItemHeight add dup mul add } { % left or right % (|x|,|y|) is West edge: radius^2 of NorthEast corner x abs ItemWidth add dup mul y abs ItemHeight .5 mul add dup mul add } ifelse % Remember the maximum corner radius seen so far. Radius max /Radius exch store end % item } forall % Take the square root and add some extra space. /Radius Radius sqrt Gap add Border add ceiling cvi store % Whew, we're done! Time to party! } def