Reference Manual for G,
A Low-Level Device-Independent Graphics Language for Macintosh Common Lisp

by Richard S. Sutton



Introduction

This document describes G, a set of routines for low-level graphics programming. G provides a standard language for graphics programming that is easily portable across machines, languages, and graphics devices. The design of G is based on Gus, a graphics package designed by Andrew Cromarty, Richard Sutton and others at the University of Massachusetts in 1981, and on the window system in Macintosh Common Lisp (MCL). G is meant to support the simultaneous use of multiple graphical devices. This document describes the G routines and their use in MCL.

G is not intended to be complete or static. New functionality can easily be added to it. For example, the current specification does not include the ability to draw ovals, but it would be easy to add this, and the current design suggests all the arguments and names for the new routines that would be needed.

This document includes an HTML anchor for every G routine, to facilitate automated access to their documentation. Also, access to the source code of all routines and lisp objects is available by clicking on the bracketed descriptor to the far right of each routine or object.

Views

All graphical operations in G are done in the context of a special graphical environment called a view. The view specifies the region of the display to draw into, the coordinate system for drawing, and a variety of other state information pertaining to drawing commands. Typically, a user will have several views open at the same time, each supporting a different way of drawing onto a device. Windows are a special kind of view, and there is one view corresponding to the entire surface of the graphics device (e.g., to the whole screen).

The region of the display corresponding to a view is called the view's viewport. G provides a complete set of viewport routines for altering and examining viewports.

Coordinate Systems

Views support drawing within their viewports in two coordinate systems. The normalized coordinate system permits drawing in floating-point coordinates within a range of x and y coordinates specified by the user on a view-by-view basis. Many users will probably operate exclusively with normalized coordinates, but those who wish greater control and efficiency can use the device coordinate system. The device coordinate system uses integer coordinates corresponding to the actual pixels of the graphics device. Use of the two coordinate systems can be freely intermixed, even for the same view.

If one draws over the full range of a view's coordinate system then the result will also cover the full span of the view's viewport. Attempts to draw outside the coordinate system will be "clipped" at the viewport boundary and will not appear on the display.

G provides a complete set of coordinate system routines for setting the normalized coordinate system and for examining the current status of both the normalized and the device coordinate systems.

Hierarchies of Views

It is convenient to organize views into hierarchies. The viewports of child views are in terms of the coordinate systems of the the parent views, and are automatically moved when their parent is moved. In the primary example of this, the parent view is a window and the child views are subregions of the window. It is much easier to specify the viewports of the child views with respect to the window than, say, with respect to the screen as a whole. When the window is moved, the child views naturally move with it.

The viewports of child views are not restricted to lie within the viewports of the parent view, but any portion protruding beyond the parent view will not be visible on the display (it will be clipped).

In G, there is one hierarchy of views for each display device. The root view of each hierarchy is the device itself.

G and the MCL Window System

G treats the window system in MCL as a display device called *g-device*. This device is the root view and the windows are the first generation child views. Drawing is not permitted directly on the device, but must be through windows. MCL has its own object classes called view and window, which are the basis for, but distinct from, G object classes g-view and g-window (see figure).

Much of G can be used without learning about its special object classes and routines for setting up views, but using the standard MCL objects and routines instead. In this case drawing can only be done in device coordinates, but it will still be simpler than using the MCL Quickdraw interface. If g-views and g-windows are simply substituted for MCL's views and windows, then drawing will also be much more efficient than using the MCL Quickdraw interface.

Alternatively, G can be used without learning about MCL's special object classes and routines for setting up views, but using entirely G routines instead. To a large extent one can use G routines instead of MCL routines for all programming operations on windows.

Naming Conventions

All G routines begin with the prefix g- or gd-. The gd- prefix is used for routines that specify coordinates in device coordinates and the g- is used otherwise. A suffix convention is used for routines that specify two endpoints, such as g-draw-line. One version of the routine specifies both endpoints in absolute coordinates, while another specifies the second endpoint relatively, by an offset from the first. The relative version of the routine has a suffix of "-r". For example, the version of g-draw-line that specifies the second endpoint relative to the first is named "g-draw-line-r". This convention is used throughout G, not just for drawing routines.

Routines for specifying and constructing colors to be used for drawing all begin with g-color-. All other G routines are meant to read as imperative commands, i.e., with a verb and object. For example, g-set-viewport is the routine directing G to set the viewport of a specified view, and g-draw-circle is the command directing G to draw a circle. This may at first seem a little verbose, but it wears well. All G names are defined in a separate package called "g:". All routines that work by side-effect include the word "set" in their names. Wherever the phrase "coordinate-system" appears in a routine name it can be abbreviated "cs".

A Complete Example of Using G

In this example a small circle is drawn in the middle of a newly created window:
(load "g.lisp")
(use-package :g)
(setq my-window (make-instance 'g-window))
(setq black (g-color-black my-window))
(g-draw-circle my-window .5 .5 .1 black)

G Objects and Object Classes

Here are the major class-subclass relationships in G:


[class name]
g-view

The basic class of all G views. Inherits from MCL's view class. Mix this into new classes to define your own specialized G views.

[make-instance]
make-instance 'g-view &key :parent

(make-instance 'g-view :parent parent) returns a new G view with parent parent, which must be an existing window or view. If parent is not provided, then it defaults to the frontmost g-window. If there are no g-windows, then it defaults to the frontmost window of any class, typically the listener being typed into. Other initargs accepted by MCL's view class can also be provided.


[class name]
g-window

The class of all G windows. Inherits from g-view and MCL's window class. Mix this into new classes to define your own specialized G windows.

Windows are a special class of view in that they appear with a border around them, including a title-bar at the top. The viewport of a window refers to the region inside the border. If you want the border to be visible you must leave extra room for it.

[make-instance]
make-instance 'g-window &key :parent :g-viewport :gd-viewport :g-viewport-r :gd-viewport-r

Returns a new G window on the device given by parent. If no parent is specified it defaults to *g-device*. If one of the viewport arguments is provided it is a list of four numbers, (x1 y1 x2 y2) or (x y delta-x delta-y), specifying the initial viewport of the new window as if they had been input to one of the viewport setting routines. At most one viewport argument should be provided. Other initargs accepted by MCL's window class can also be provided. For example:

(setq my-window (make-instance 'g-window 
:g-viewport '(.5 .5 .75 .75)
:window-title "My Window"))


[class name]
g-device

The class of all G graphics devices. Inherits from g-view.

[variable]
*g-device*

This variable is bound to the root view and the graphics device corresponding to the main screen, an instance of g-device. (Drawing is not permitted directly on *g-device*.) All windows that have no explicit parent are taken to have *g-device* as their parent. Thus, it is the parent view of all regular MCL windows. It can be passed into coordinate system routines that use normalized coordinates to establish a new coordinate system for positioning windows on the screen. For example, here is how one would establish a coordinate system for positioning windows in inches from the upper left corner of the screen:

(g-set-cs-scale *g-device* 0 0 :inches :inches :upper-left)

In MCL, the viewport of *g-device* is always the entire screen, but the first 20 pixels of this are usually obscured by the menubar. Another 18 pixels are needed for window titles. Thus, if you want to make sure a window placed to start at 0,0 in normalized coordinates has its border and title bar within the visible range, you might do:

(g-set-cs-scale *g-device* (/ -1 72) (/ -38 72) :inches :inches :upper-left)

Multiple screens? Other devices? Yes, yes, but not yet!


[function]
g-get-parent view

(g-get-parent view) returns the parent of view. This value is suitable for passing into make-instance to make a sibling view.

[function]
g-get-children view

(g-get-children view) returns a list of the child views of view.


[function]
g-close-view view

When you are done using a G view you should close it, causing various bookkeeping operations to be done. In MCL, the view's parent disposes of the subview corresponding to the view. If view is it window, it will disappear from the screen.


Coordinate System Routines

G provides a complete set of routines for examining the normalized and device coordinate systems of a view, for setting the normalized coordinate system, and for converting back and forth between device and normalized coordinate systems.

Coordinate systems are specified by two points, together giving the extreme x and y coordinates that will appear within the viewport, plus an indication of the corner of the viewport that will be occupied by the first point. For example, (g-set-coordinate-system view 0 0 1 1 :lower-left) sets the coordinate system for view to run from 0,0 in the lower-left corner of the viewport to 1,1 in the upper right. The allowed values for the corner, in all routines, are :lower-left, :upper-left, :lower-right, and :upper-right.

The second point that defines a coordinate system can be specified either absolutely or relatively (as an offset with respect to the first point). In the relative case the routine ends with "-r". Routines starting with "gd-" pertain to (or return coordinates in) the device coordinate system whereas routines starting with "g-" pertain to (or return coordinates in) the normalized coordinate system. Thus, the routine g-set-coordinate-system-r is used to set the normalized coordinate system of a view, specifying its second corner point relative to the first. In all coordinate system routines, the phrase "coordinate-system" can be abbreviated to just "cs".


Examining the Coordinate Systems

[function]
g-get-coordinate-system view
[function]
gd-get-coordinate-system view

Returns five values, x1, y1, x2, y2, corner, describing the current normalized or device coordinate system for view. The coordinate system runs from coordinates x1,y1 to x2,y2, with coordinates x1,y1 occupying the corner of the viewport indicated by corner, which is one of :lower-left, :upper-right, :lower-right, or :upper-left. It will always be the case that x1 < x2 and y1 < y2. g-get-coordinate-system requires that view be a g-view.

[function]
g-get-coordinate-system-r view
[function]
gd-get-coordinate-system-r view

Returns five values, x, y, x-size, y-size, corner, describing the current normalized or device coordinate system for view. The coordinate system runs from coordinates x,y to x+x-size,y+y-size, with coordinates x,y occupying the corner of the viewport indicated by corner, which is one of :lower-left, :upper-right, :lower-right, or :upper-left. x-size and y-size will always be positive. g-get-coordinate-system-r requires that view be a g-view.

[function]
g-get-cs-scale view

Returns five values, x, y, x-scale, y-scale corner, describing the current normalized coordinate system for view. The coordinate system gives the corner of the viewport indicated by corner the coordinates x,y and then increases across the rest of the view according to the scale arguments x-scale and y-scale. corner is one of :lower-left, :upper-right, :lower-right, or :upper-left. The scale parameters are positive floating point numbers giving the scaling in pixels per unit of the normalized coordinate system.


Setting the Normalized Coordinate System

[function]
g-set-coordinate-system view x1 y1 x2 y2 corner

(g-set-coordinate-system view x1 y1 x2 y2 corner) sets the normalized coordinate system for view to run from coordinates x1,y1 to coordinates x2,y2, with the point x1,y1 occupying the corner of the viewport indicated by corner, which should be one of :lower-left, :upper-right, :lower-right, or :upper-left. In subsequent calls to g- drawing routines using view, the normalization will be performed such that objects drawn to fill the these new coordinates will fill the viewport of the view. (gd- coordinate specification is not affected.)

The initial normalized coordinate system for each view is from 0,0 in the lower-left corner to 1,1 in the upper right. This could be restored at any time by (g-set-coordinate-system view 0 0 1 1 :lower-left).

[function]
g-set-coordinate-system-r view x y delta-x delta-y corner

(g-set-coordinate-system-r view x y delta-x delta-y corner) sets the normalized coordinate system for view to run from coordinates x,y to coordinates x+delta-x,y+delta-y, with the point x,y occupying the corner of the viewport indicated by corner, which should be one of :lower-left, :upper-right, :lower-right, or :upper-left. Otherwise, the efect of this routine is the same as discussed above for g-set-coordinate-system.

[function]
g-set-cs-scale view x y x-scale y-scale corner

(g-set-cs-scale view x y x-scale y-scale corner) sets the normalized coordinate system of view to have coordinates x,y in the corner of the viewport indicated by corner, and then increase across the the rest of the view according to the scale arguments x-scale and y-scale. corner should be one of should be one of :lower-left, :upper-right, :lower-right, or :upper-left. The scale parameters should be one of :inches, :centimeters, :pixels, :points, or a positive number indicating pixels per unit of the normalized coordinate system.

See *g-device* for an example of how to set use g-set-cs-scale to set the normalized coordinate system such that windows can henceforth be positioned in inches from the upper left corner of the screen.


Converting Between Device and Normalized Coordinates

[function]
g-coord-x view dx
[function]
g-coord-y view dy

Returns the normalized coordinate corresponding to the provided device coordinate, with respect to view.

[function]
gd-coord-x view x
[function]
gd-coord-y view y

Returns the device coordinate corresponding to the provided normalized coordinate, with respect to view.


Converting Between Device and Normalized Offsets

[function]
g-offset-x view dx-offset
[function]
g-offset-y view dy-offset

Converts an offset (distance) in device coordinates to the same offset in normalized coordinates, with respect to view. Note that although the arguments are scaled in pixels, they may be reals to prevent round off error.

[function]
gd-offset-x view x-offset
[function]
gd-offset-y view y-offset

Converts an offset (distance) in normalized coordinates to the same offset in device coordinates, with respect to view. Note that although the returned values are scaled in pixels, they will be reals to prevent round off error.


Converting Between the Coordinate Systems of Different Views

[function]
g-convert-x from-view to-view x
[function]
g-convert-y from-view to-view y
[function]
gd-convert-x from-view to-view x
[function]
gd-convert-y from-view to-view y

Given an x or y coordinate in the coordinate system of from-view, these routines return the corresponding coordinate in the coordinate system of to-view.


Viewport Routines

G provides routines for setting and examining the viewport of each view in both normalized and device coordinates. A view's viewport is always given in terms of a coordinate system of the parent of the view.

Changing the viewport affects subsequent drawing operations. In subsequent drawing, clipping will be to the new viewport (or rather to the portion of it which is viewable and within the parent view's viewport). In addition, in subsequent calls to g- graphics routines, normalization of the coordinates will also be to the new viewport. Thus, a program that normally fills the entire parent view can be made to use just a portion of it merely by resetting the viewport (provided it uses entirely normalized coordinates). For example, if the parent has the default coordinate system (running from 0 to 1 in both dimensions), then one can use just the upper right quandrant of it by calling (g-set-viewport view .5 .5 1 1).

Changing the viewport normally does not change the coordinate systems in any way. However, it is possible to trigger arbitrary used code whenever the viewport is changed. See the section on Hooks into Viewport Setting.

I'd prefer that changing the viewport did not change the display in any way. However, in the current implementation, using MCL views, this does not seem to be possible. Whenever the viewport is changed, any regions uncovered or newly covered will be erased, and then have g-draw-view called on them).


Getting the Viewport

[function]
g-get-viewport view
[function]
gd-get-viewport view

Returns four values, x1, y1, x2, y2, indicating the current viewport in device coordinates or normalized coordinates, in the coordinate system of the parent of view. It will always be the case that x1 < x2 and y1 < y2. g-get-viewport requires that the parent of view be a g-view.

[function]
g-get-viewport-r view
[function]
gd-get-viewport-r view

Returns four values, x y x-size y-size, indicating the current viewport in device coordinates or normalized coordinates, in the coordinate system of the parent of view. x and y are the position of the corner of the viewport that has smallest coordinates, and x-size and y-size are the size of the viewport. g-get-viewport-r requires that the parent of view be a g-view.


Setting the Viewport

[function]
g-set-viewport view x1 y1 x2 y2
[function]
gd-set-viewport view x1 y1 x2 y2

Sets the viewport for view to the rectangular portion of the display including the pixels x1,y1 and x2,y2, in the normalized or device coordinates of the parent of view. g-set-viewport requires that the parent of view be a g-view.

The default viewport after initialization of a view is the full display surface of the parent.

[function]
g-set-viewport-r view x y delta-x delta-y
[function]
gd-set-viewport-r view x y delta-x delta-y

Sets the viewport for view to the rectangular portion of the display including the pixels x,y and x+delta-x,y+delta-y, in the normalized or device coordinates of the parent of view. If any of the last four arguments are nil (or not provided) then they default to their values for the current viewport. g-set-viewport-r requires that the parent of view be a g-view.


Hooks into Viewport Setting

In many applications, viewports can be changed interactively by users, e.g., when they resize or move a window using the mouse. When this is done, user code may need to be run to adjust coordinate systems or to redraw objects. To assist with this sort of programming, G provides hooks into the viewport setting system. The following routines are called whenever the viewport is changed, either interactively by mousing or by user code.

[primary method]
g-accept-new-viewport-size view

This method is called whenever the viewport of view is changed in size. The default method does nothing. Users may want to write specialized methods for specialized view or window classes in order to make automatic adjustments whenever the viewport size is changed. For example, instead of having the normalized coordinate system expanding to fill a larger viewport, one may prefer the coordinate system to grow proportionately, to reveal more of the data space. This and other affects can be easily achieved by resetting the coordinate system each time the viewport is changed, e.g., by calls to g-set-cs-scale. For example, the effect just described can be accomplished as follows:

(defclass fixed-scale-window (g-window) ())
(defmethod g-accept-new-viewport-size :before ((window fixed-scale-window))
(g-set-cs-scale window 0 0 :inches :inches :upper-left))

Now window classes with fixed-scale-window mixed in will behave as desired.

Consider another example of the use of g-accept-new-viewport-size. Suppose you want the sizes and positions of child views within a window to be relative to the viewport of the window. The natural way to do this is to set up a normalized coordinate system for the window and place the child views within the window using that coordinate system. However, ordinarily all this would get messed up if the window's viewport were changed, because the viewports of the child views would not be readjusted The following code defines a mixin that causes the child views to all be automatically changed as well:

(defclass maintain-g-viewports-of-children (g-view) () )

(defmethod g-accept-new-viewport-size ((view maintain-g-viewports-of-children))
(let ((children (g-get-children view))
(g-viewports (loop for child in children
collect (multiple-value-list
(g-get-viewport child)))))
(call-next-method view)
(loop for child in children
for g-viewport in g-viewports
do (apply #'g-set-viewport child g-viewport))))

Views of this class, or with this class mixed in, will, whenever their viewports are changed, automatically change the viewports of their child views to maintain their positions and sizes in the normalized coordinates of this view. Thus, whenever a view is changed in size, all the child views will be changed proportionately.


Color Specification Routines

G provides several routines for specifying colors, all beginning with g-color-. Each returns a color-code to be used in drawing routines. Color-codes may be specific to the window, and thus a view must be specified whenever constructing a color-code. Normally, color-codes are constructed infrequently and then used over and over, but it is also possible to construct a new color-code each time anything is drawn.

If the device does not support the requested color -- for example, if you ask for a blue color when using a gray-scale screen -- then you will get some approximation to the requested color.

In MCL, one can also set the characteristics of the Quickdraw "pen" as part of the color-code. The pen specifies various characteristics of Quickdraw drawing, which underlies G. These characteristics can be set by the G routine g-color-pen, but these effects are entirely device specific.

Color-codes should not be altered by the user in anyway. The implementation may rely on this.


[functions]
g-color-black view
g-color-white view
g-color-pink view
g-color-red view
g-color-orange view
g-color-yellow view
g-color-green view
g-color-dark-green view
g-color-light-blue view
g-color-blue view
g-color-purple view
g-color-brown view
g-color-tan view
g-color-light-gray view
g-color-gray view
g-color-dark-gray view
g-color-flip view
g-color-invisible view
g-color-on view
g-color-off view

All these functions return a color code for view according to the last part of their name. The name "flip" refers to a color that reversibly inverts the color currently on the screen. The color "invisible" draws but does not affect the color of any pixels on the screen. (These and other possibilities can also be constructed via the use of the MCL specific routine g-color-pen.) The names off and on have special meaning in conjunction with the g-clear drawing command. g-clear sets the entire display to the off color, which may be different for different devices. In general, on is the normal drawing color for a device, and off is its opposite, or erasing color.

For MCL windows, on is the same as :black and off is the same as :white.


[function]
g-color-user-pick view &rest initargs

Allows the user to pick the desired color using the standard interactive Macintosh color picker. Initargs can be used to set the initial color and other aspects of the color picker.


[function]
g-color-rgb view red green blue

Returns a color-code for view corresponding to the specified red, green, and blue color components, which range from 0 for minimum intensity to 1 for maximum intensity.

[function]
g-color-rgb-255 view red green blue

Returns a color-code for view corresponding to the specified red, green, and blue color components, which range from 0 for minimum intensity to 255 for maximum intensity.


[function]
g-color-bw view intensity

Returns a color-code for view for the color specified according to its intensity along a continuum from the off color at intensity=0 to the on color at intensity=1.0. See above for definitions of on and off colors.


[function]
g-color-pen view color-code pattern mode x-size y-size

Returns a new color-code for view that is like color-color except that it has quickdraw pen characteristics as specified by the remaining arguments. If an argument is not provided, then that characteristic of the pen is unchanged.

The pattern is an 8 by 8 bit pattern that acts like the ink of the pen. MCL provides five patterns as constants:

For example, drawing with the :gray-pattern will have a checker-board dithering effect in which only every alternate pixel is affected. The default pattern in :black-pattern, which changes all the pixels.

The mode determines the interaction between the pixels being drawn (from pattern) and the pixels already on the display. For example, the drawn pixels could replace them, OR with them, XOR with them, etc. There are eight modes:

:patCopy        Replace (paint over) existing pixels (the default)
:patOr Or with the existing pixels (black if either black)
:patXor Wherever source is black, invert the existing pixels
:pacBic Wherever source is black, erase the existing pixels
:notPatCopy like :patCopy, but first invert source pixels
:notPatOr like :patOr, but first invert source pixels
:notPatXor like :patXor, but first invert source pixels
:notPatBic like :patBic, but first invert source pixels
The last two arguments to g-color-pen make the pen x-size pixels wide and y-size pixels tall. This causes lines and other graphical objects drawn with the new color to appear with the new thickness. When the pen size is greater than one, the extra pixels are drawn below and to the right of the usual pixels.

Drawing Routines

The drawing routines are those G routines that perform a display (or output-buffering) function.

Most drawing routines come in two forms, one prefaced by g-, and the other by gd-. These two forms differ only in the method used to specify spatial coordinates on the display surface. The g- routines use normalized real-valued coordinates. Many users will probably use exclusively g- routines, but for those who wish to have greater control at the pixel level, the gd- routines operate in device-dependent integer coordinates corresponding to pixels of the display. Most calls to g- routines get quickly translated into calls to gd- routines within G. The use of these two kinds of routines can be freely intermixed.

A suffix convention is used for routines that specify two endpoints, such as g-draw-line. One version of the routine specifies both endpoints in absolute coordinates, while another specifies the second endpoint relatively, by an offset from the first. The relative version of the routine has a suffix of "-r". For example, the version of g-draw-line that specifies the second endpoint relative to the first is named "g-draw-line-r".

Here are some examples of drawing in device coordinates:

The arguments of all the drawing routines are patterned as follows. The first argument is the view within which drawing takes place. The following arguments typically specify the x and y coordinates of the drawing operation. For g- routines these are in normalized coordinates. For gd- routines, these are in pixel coordinates, and in MCL they must be integers. The final argument is an optional color-code. (Color codes are constructed by the color specification routines.) If a color is not provided (or nil), then the color used is the same as the last color used with the window associated with the view. To establish a current color for drawing without calling an actual drawing routine, use g-set-color.

Not all of Quickdraw's abilities are currently available via G routines. Additional routines should be added, patterned after these, as the need for their additional abilities arise.


[function]
g-clear view &optional color

Clears the viewport by filling it with color, or with the off color if color is not provided. Under normal circumstances, a display is initialized to the off color, but G itself performs no initialization of display.


[function]
g-make-visible view

Makes the effects of all previously called graphics routines visible in the display for the device associated with view. For MCL, this causes the window corresponding to view to be made frontmost.


[function]
g-draw-point view x y &optional color
[function]
gd-draw-point view x y &optional color

Colors the pixel x,y on view.


[function]
g-draw-line view x1 y1 x2 y2 &optional color
[function]
gd-draw-line view x1 y1 x2 y2 &optional color

Draws the line segment between and including the pixels x1,y1 and x2,y2.

[function]
g-draw-line-r view x y delta-x delta-y &optional color
[function]
gd-draw-line-r view x y delta-x delta-y &optional color

Draws the line segment between and including the pixels x,y and x+delta-x,y+delta-y.


[function]
g-outline-rect view x1 y1 x2 y2 &optional color
[function]
gd-outline-rect view x1 y1 x2 y2 &optional color

Draws a wire-frame or hollow rectangle with one corner at x1,y1 and the other at x2,y2.

[function]
g-outline-rect-r view x y delta-x delta-y &optional color
[function]
gd-outline-rect-r view x y delta-x delta-y &optional color

Draws a wire-frame or hollow rectangle with one corner at x,y and the other at x+delta-x,y+delta-y.

Only vertically or horizontally oriented rectangles can be drawn -- arbitrary angles are not possible. A box of dimension zero appears as a single pixel.


[function]
g-fill-rect view x1 y1 x2 y2 &optional color
[function]
gd-fill-rect view x1 y1 x2 y2 &optional color

Fills in the rectangular area including the pixels x1,y1 and x2,y2 with color.

[function]
g-fill-rect-r view x y delta-x delta-y &optional color
[function]
gd-fill-rect-r view x y delta-x delta-y &optional color

Fills in the rectangular area including the pixels x,y and x+delta-x,y+delta-y with color.

Only vertically or horizontally oriented rectangles can be drawn -- arbitrary angles are not possible. A rectangle of dimension zero appears as a single pixel.


[function]
g-fill-polygon view color x1 y1 x2 y2 x3 y3 ...
[function]
gd-fill-polygon view color x1 y1 x2 y2 x3 y3 ...

Fills in the polygonal area specified by the points x1,y1 x2,y2 x3,y3 ... with color. The area filled is the area inside the points, such that if they are all the same, then nothing is drawn. If color is nil, then it defaults to the last drawn color on the view. There must be at least one point.


[function]
g-draw-circle view x y radius &optional color
[function]
gd-draw-circle view x y radius &optional color

Draws a circle to the display with its center at x,y and with radius radius. A circle of radius zero will not appear on the display at all. A radius passed to g-draw-circle is interpretted as a distance along the "y" or vertical dimension and the circle size is scaled accordingly .

As currently implemented, this routine will draw circles that are as wide as they are tall as measured in numbers of pixels. Thus, if the aspect ratio of the display is not 1:1, circles will come out as elipses. The shape of the circle is never affected by the coordinate systems associated with view. Note that this can cause different parts of a display to change their positions relative to each other (when using g- routines) as a view is mapped to different viewports.


[function]
g-draw-disk view x y radius &optional color
[function]
gd-draw-disk view x y radius &optional color

Draws a disk (i.e., a filled-in circle) to the display with its center at x,y and with radius radius. Otherwise, these routine behave just like the circle-drawing routines above.


[function]
g-draw-arc view x y radius start-angle angle &optionalcolor
[function]
gd-draw-arc view x y radius start-angle angle &optionalcolor

Draws a arc to the display with its center of rotation at x,y and with radius radius. The arc begins at starting-angle degrees and proceeds for angle degrees. Degrees follow the standard mathematical conventions, increasing counterclockwise from zero pointing directly to the right. For example, (g-draw-arc view x y radius 180 90 color) draws a quarter circle in the lower left quadrant. Otherwise, these routines behave similarly to the circle-drawing routines described above.


[function]
g-draw-text view string font x y &optional color
[function]
gd-draw-text view string font x y &optional color

Writes the string of text to view in color color. The lower left corner of the string appears at x,y. Font is a list of font description information, e.g., ("Monaco" 12 :italic :bold) in MCL.

In MCL, permitted font names include all the fonts installed in your system. The font size is in points from 1 to 127. The font styles should be one or more of the following: :plain, :italic, :bold, :outline, :condense, :shadow, :extend, :underline. If several of these are provided, they are processed in order. A :plain font style implies the absence of other font styles. Any aspects of the font not explicitly specified defaults to its value in the last font used in the view. Thus to obtain an italic font that is ensured not to be bolded (or extended, or whatever) one must use the sequence of styles :plain :italic.

[function]
g-draw-text-centered view string font x y &optional color
[function]
gd-draw-text-centered view string font x y &optional color

Writes the string of text to view in color color. The center of the string appears at x,y. Font is as described above.


[function]
g-text-height view string font
[function]
gd-text-height view string font

Returns the vertical size of text when drawn to view in font, in either pixels or normalized coordinates. Font is as described above.

[function]
g-text-width view string font
[function]
gd-text-width view string font

Returns the horizontal size of text when drawn to view in font, in either pixels or normalized coordinates. Font is as described above.


[function]
g-set-color view color

(g-set-color view color) establishes color as the current drawing color of the window associated with view. Subsequent drawing to any view within that window that does not explicitly specify a color will appear in this color. This will remain in effect until something is drawn into the window that does specify a color, or until the next call to g-set-color.


Cursor and Event Routines

These are routines for reading the cursor location, controlling its appearance, and responding to mouse events.


[function]
g-get-cursor-position view
[function]
gd-get-cursor-position view

Returns as two values the current x and y coordinates of the cursor (mouse) in the coordinate system of view. If the cursor is not within the viewport of view, then nil is returned.


[primary method]
g-click-event-handler view x y
[primary method]
gd-click-event-handler view dx dy

If you would like your program to be able to respond to mouse clicks within a G view, then you should specialize the g-view class and write a new method for g-click-event-handler or gd-click-event-handler specialized for the new class. That method will be called with the coordinates of the mouse click whenever there is a mouse click within the viewport of a view of that new class. For example, here is how to cause the current mouse coordinates to be printed to the listener each time the mouse button is clicked within the view:

(defclass my-view (g-view) ()) 
(defmethod g-click-event-handler ((view my-view) x y)
(print (list x y)))


[primary method]
g-cursor view x y
[primary method]
gd-cursor view x y

Use these methods if you would like to have the cursor appear in some special form (possibly dependent of its coordinates, x and y) when it moves over the viewport of a G view. For example, to make the cross-hairs cursor appear whenever the cursor goes over your view, you would do the following:

(defclass my-view (g-view) ()) 
(defmethod g-cursor ((view my-view) x y)
g::*cross-hair-cursor*)


[primary method]
g-draw-view view

This routine is called each time your view is moved or uncovered from behind other windows. If you make a specialized form for a subclass of g-view, then you can arrange for the view to to be automatically redrawn when uncovered. Otherwise, covered and uncovered views are simply blanked out with the off color. For example, suppose you want a black circle on a white background to always appear in your view. This is what you should do:

(defclass my-view (g-view) ()) 

(defmethod g-draw-view ((view my-view))
(g-draw-circle view .5 .5 .2 (g-color-black view)))

(defmethod g-accept-new-viewport-size :after ((view my-view))
(g-clear view)
(g-draw-view view)) ; this causes complete redrawing on viewport changes

A little care is needed if you use g-draw-view for a window and you want to initialize its colors at the time the window is created. If you initialize the color-codes as slots in a standard :after method to initialize-instance, then an error will occur when the window is first made. This is because the event system tries to draw the window as soon as it is made, which is before the :after method has been run to define the color-codes. The solution is to hold off on event processing until your new window has been completely initialized. This is done by writing a new primary method for initialize-instance that invokes without-event-processing, as follows:

(defclass my-window (g-window my-view) 
(black)) ; black will be a color-code

(defmethod initialize-instance ((window my-window) &key)
(without-event-processing ; Temporarily suspend event processing,
(call-next-method) ; then run main inits without events
(setf (slot-value w 'black) ; and make any color-codes.
(g-color-black window)))) ; The view will be draw when events resume.


Additional Routines Built on G

The following routines follow the argument conventions established for G, and are designed to extend and be compatible with the other G routines. Since they are written "on top of" G, i.e., since they simply call G routines, they are not considered elementary G routines. (Obviously, this division is a bit arbitrary.)


[function]
g-draw-arrow view x1 y1 x2 y2 &optional color
[function]
gd-draw-arrow view x1 y1 x2 y2 &optional color

Draws an arrow from x1,y1 to x2,y2 on view.

[function]
g-draw-arrow-r view x y delta-x delta-y &optional color
[function]
gd-draw-arrow-r view x y delta-x delta-y &optional color

Draws an arrow from x,y to x+delta-x,y+delta-y on view.


[function]
g-draw-arrowhead view x1 y1 x2 y2 body-size head-size &optional color
[function]
gd-draw-arrowhead view x1 y1 x2 y2 body-size head-size &optional color

Draws an arrow from x1,y1 to x2,y2 with independent control over the size of the main segment of the arrow and the head of the arrow. Body-size is the fraction of the total length between the two endpoints that is filled by the main segment (starting from the head end of the arrow) and head-size is the relative length of the head segments to the distance between the endpoints. For example, just the arrowhead alone could be drawn by setting body-size=0. As another example, g-draw-arrow is implemented as

(defun g-draw-arrow (view x1 y1 x2 y2 &optional color) 
(g-draw-arrowhead view x1 y1 x2 y2 1 .25 color))

[function]
g-draw-arrowhead-r view x y delta-x delta-y body-size head-size &optional color
[function]
gd-draw-arrowhead-r view x y delta-x delta-y body-size head-size &optional color

Draws an arrow from x,y to x+delta-x,y+delta with independent control over the size of the main segment of the arrow and the head of the arrow. Otherwise as above.


Obtaining and Installing G

G as described herein can be obtained by downloading the file g.lisp.html and renaming it "g.lisp". To install G, first load the MCL quickdraw interface, e.g., by (load "ccl:library;quickdraw") (if necessary), and then load the G source file, e.g., by (load "g.lisp"). If you then "use" the newly created package, e.g., by (use-package :g), you will have access to all the G routines.


Implementation Notes

To avoid continual refocusing, each routine checks when it runs whether or not the view is focused properly. It is refocused only if necessary. The check is by checking if MCL's *current-view* is the same as the view. Refocusing is done by calling focus-view. (This trick has been disabled for the moment because of an incompletely understood interaction with MCL's dialog-view functions.)

A similar trick is done to prevent unneeded calls to set-fore-color. The last foreground color set on each view is stored with the view. It is set again only if a new color is asked for. This assumes color-codes are never changed, i.e., that they are used only with G routines. It also assumes no other processes are changing the foreground color of the views without changing it back when they are done!

These tricks result in an efficient graphics system. G is about 5 times faster than calling quickdraw routines through the normal interface provided in MCL (this is based on drawing random line segments within a 200 by 200 pixel window). Compared to the fastest possible interface from MCL, undercutting the quickdraw interface, G loses by only about 10%. All these comparisons are for gd- routines of course. g- routines are slower (by about a factor of 3) because they require floating point calculation. Whereas gd- routines make no garbage during normal drawing, g- routines make garbage because normal floating point operations generate garbage in MCL. If desired, this can be avoided by using MCL's short-float data type. If the coordinate system is set using short floats then calls to g- routines using coordinates that are also short-floats will generate no garbage (speed is unaffected). A good future project might be to convert G internally to use Eran Gat's floating point compiler, which can speed up floating point calculations significantly, but may require some care to be used successfully.


Detailed Table of Contents