The Design and Implementation of Pie Menus

They’re Fast, Easy, and Self-Revealing.

Originally published in Dr. Dobb’s Journal, Dec. 1991.

Introduction

Pie Menu Properties

Pie Menu Advantages

Pie Menu Flavors

Pie Menu Implementations

Usability Testing

Figure 1: Eight Days a Week Pie Menu

Pie Menu Disadvantages

Other Design Considerations

Sample Pie Menu

Figure 2: Window Management Pie Menu
Figure 3: Color Wheel Pie Menu

Conclusion

References

Momenta’s Command Compass A Pie Menu by any Other Name

% 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
    /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

User interface flower child. Pie menus, PizzaTool, SimCity, The Sims, Visual Programming, VR, AR, Unity3D / JavaScript bridge.