(kawa pictures) library lets you create geometric shapes
and images, and combine them in interesting ways.
The tutorial gives an introduction.
The easiest way to use and learn the
is with a suitable REPL. You can use the old Swing-based console
or any DomTerm-based terminal emulator.
You can create a suitable window either by starting
-w flag, or by running the
inside an existing DomTerm-based terminal emulator.
The screenshot below is of the latter,
qtdomterm terminal emulator.
(import (kawa swing)) you can use
to display a picture in a Swing window.
A picture is an object that can be displayed on a screen, web-page, or printed page, and combined with other pictures.
A picture has a method printing itself in a graphical context. It also has various properties.
An important property of a picture is its bounding box. This is a rectangle (with edges parallel to the axes) that surrounds the contents of the picture. Usually the entire visible part of the picture is inside the bounding box, but in some cases part of the picture may stick outside the bounding box. For example when a circle is drawn (stroked) with a “pen”, the bounding box is that of the infinitely-thin mathematical circle, so “ink” from the pen that is outside the circle may be outside the bounding box.
A picture has an origin point corresponding to the (0 0) cordinates.
The origin is commonly but not always inside the bounding box.
Certain operations (for example
hbox) combine pictures by putting
them “next to” each other, where “next to” is defined in
terms of the bounding box and origin point.
The library works with a two-dimensional grid, where each position has an x cordinate and y coordinate. Normally, x values increase as you move right on the screen/page, while y values increase as you move down. This convention matches that used by Java 2D, SVG, and many other graphics libraries. However, note that this is different from the traditional mathematical convention of y values increasing as you go up.
By default, one unit is one “pixel”. (More precisely,
it is the
px unit in the CSS specification.)
A point is a pair consisting of an x and a y coordinate.
Construct a point value with the specified
yare expressions that evaluate to real numbers:&P[(+ old-right 10) baseline]
A dimension value is a pair of a width and a height. It is used for the size of pictures in the two dimensions.
In a context that expects a point, a dimension is treated as an offset relative to some other point.
Construct a dimension value with the specified
heightvalues, which are both expressions that evaluate to real numbers.
A shape is a collection of lines and curves. Examples include lines, circles, and polygons. A shape can be stroked, which you can think of as being drawn by a very fancy calligraphic pen that follows the lines and curves of the shape.
A closed shape is a shape that is continuous and ends up where it started. This includes circles and polygons. A closed shape can be filled, which means the entire “interior” is painted with some color or texture.
A shape is represented by the Java
picture library only provides relatively simple shapes,
but you can use any methods that create a
Shape is effectively a sub-type of picture,
though they’re represented using using disjoint classes:
If you use a shape where a picture is required,
the shape is automatically converted to a picture,
as if using the
In the simple case two points are specified, and the result is a line that goes from point
npoints are specied, the result is a polyline: a path consisting of
n-1line segments, connecting adjacent arguments. (If only a single point is specified, the result is degenerate single-point shape.)
All of the points except the first can optionally be specified using a dimension, which is treated an an offset from the previous point:(line &P[30 40] &D[10 5] &D[10 -10])
is the same as:(line &P[30 40] &P[40 45] &P[50 35])
Constructs a closed shape from line segments. This is the same as calling
linewith the same arguments, with the addition of a final line segment from the last point back to the first point.
A rectangle is closed polygon of 4 line segments that are alternatively parallel to the x-axis and the y-axis. I.e. if you rotate a rectangle it is no longer a rectangle by this definition, though of course it still has a rectangular shape. If
p2is not specified, constructs a rectangle whose upper-left corner is
&P[0 0]and whose lower-right corner is
p2is specified, constructs a rectangle whose upper-left corner is
p1and whose lower-right corner is
p2is a dimension it is considered a relative offset from
p1, just like for
Creates a circle with the specified
radius. If the
centeris not specified, it defaults to
A paint is a color pattern used to fill part of the canvas. A paint can be a color, a texture (a replicated pattern), or a gradient (a color that gradually fades to some other color).
A color is defined by red, green, and blue values. It may also have an alpha component, which specifies transparency.
valueto a color - or more general a paint. Specificlly, the return type is
valuecan be any one of:
java.awt.Paint, commonly a
A 24-bit integer value is converted to a color. Assume the integer is equal to a hexadecimal literal of the form
RR(bits 16-23) is the intensity of the red component;
GG(bits 8-15) is the intensity of the green component; and
RR(bits 0-7) is the intensity of the red component.
One of the standard HTML/CSS/SVG color names, as a string or symbol. See the table in
gnu/kawa/models/StandardColor.javasource file. Case is ignored, and you can optionally use hyphens to separate words. For example
'hotPinkare all the same sRGB color
Create a new picture that is the “same” as
paintas the default paint. For
paintyou can use any valid argument to
->paint. The default paint (which is the color black if none is specified) is used for both
fill(paint interior) and
(! circ1 (circle 20 &P[20 20]))#|kawa:2|#
(hbox (fill circ1) (draw circ1))#|kawa:3|#
(with-paint 'hot-pink (hbox (fill circ1) (draw circ1)))
Above we use
with-paintto create a cloned picture, which is the same as the original
hbox, except for the default paint, in this case the color
(! circ2 (hbox (fill circ1) (with-paint 'hot-pink (fill circ1))))#|kawa:5|#
(with-paint 'lime-green circ2)
hboxof two filled circles, one that has unspecified paint, and one that is
circ2directly uses black for the circle with unspecified color, but if we wrap
with-paintthat provides a default that is used for the first circle.
Fill the “inside” of
shape. If no
paintis specified, uses the current default paint. Otherwise,
(fillis the same
Returns a picture that draws the outline of the
shape. This is called stroking. An
optionmay be one of:
Colorobject, which is used to draw the shape.
A standard color name, such as
'medium-slate-blue. This is mapped to a
A join-specifier, which is a symbol specifying how each curve of the shape is connected to the next one. The options are
'bevel-join. The default if none is specified is
A end-cap-specifier, which is a symbol specifying how each end of the shape is terminated. The options are
'butt-cap. The default is
'butt-cap. (This follows SVG and HTML Canvas. The default in plain Java AWT is a square cap.)
A real number specifies the thickness of the stroke.
java.awt.Strokeobject. This combines join-specifier, end-cap-specifier, thickness, and more in a single object. The
BasicStrokeclass can specify dashes, though that is not yet supported for SVG output; only AWT or image output.
Let us illustrate with a sample line
linand a helper macro
show-draw, which adds a border around a shape, then draws the given shape with some options, and finally re-draws the shape in plain form.#|kawa:10|#
(define lin (line &P[0 0] &P[300 40] &P[200 100] &P[50 70]))#|kawa:11|#
((_ options ... shape)#|....:14|#
(border 12 'bisque (zbox (draw options ... shape) shape)))))#|....:15|#
(show-draw 8 'lime lin)#|....:16|#
(show-draw 8 'lime 'round-cap 'round-join lin)#|....:17|#
(show-draw 8 'lime 'square-cap 'bevel-join lin)
Notice how the different cap and join styles change the drawing. Also note how the stroke (color lime) extends beyond its bounding box, into the surrounding border (color bisque).
A 2D affine transform is a linear mapping from coordinates to coordinates. It generalizes translation, scaling, flipping, shearing, and their composition. An affine transform maps straight parallel lines into other straight parallel lines, so it is only a subset of possible mappings - but a very useful subset.
Creates a new affine transform. The result of applying
(affine-transformto the point
&P[is the transformed point
x0) (+ (*
If using point arguments,
(affine-transform &P[is equivalent to:
Creates a transformed picture.
If the argument is a
shape, then the result is also a shape.
Apply a transform to a single point, yielding a new point.
Combine two transforms, yielding the composed transform.
The one-argument variant creates a new affine transform that rotates a picture about the origin by the specified
angle. A positive
angleyields a clockwise rotation. The
anglecan be either a quantity (with a unit of either
grad(gradians)), or it can be a unit-less real number (which is treated as degrees).
The two-argument variant applies the resulting transform to the specified picture. It is equivalent to:(with-transform (rotate
pictureby the given
factorcan be a real number. The
factorcan also be a point or a dimension, in which case the two cordinates are scaled by a different amount.
The two-argument variant applies the resulting transform to the specified picture. It is equivalent to:(with-transform (scale
The single-argument variant creates a transform that adds the
offsetto each point. The
offsetcan be either a point or a dimension (which are treated quivalently).
The two-argument variant applies the resulting transform to the specified picture. It is equivalent to:(with-transform (translate
Make a combined picture from multiple sub-pictures drawn either “next to” or “on top of” each other.
The case of
zboxis simplest: The sub-pictures are drawn in argument order at the same position (origin). The “
z” refers to the idea that the pictures are stacked on top of each other along the “Z-axis” (the one perpendicular to the screen).
vboxinstead place the sub-pictures next to each other, in a row or column. If
spacingis specified, if must be a real number. That much extra spacing is added between each sub-picture.
hboxshifts each sub-picture except the first so its left-origin control-point (see discussion at
re-center) has the same position as the right-origin control point of the previous picture
plusthe amount of
vboxshifts each sub-picture except the first so its top-origin control point has the same position as the bottom-origin point of the previous picture, plus
The bounding box of the result is the smallest rectangle that includes the bounding boxes of the (shifted) sub-pictures. The origin of the result is that of the first picture.
Return a picture that combines
picturewith a rectangular border (frame) around
picture’s bounding box. The
sizespecifies the thickness of the border: it can be real number, in which it is the thickness on all four sides; it can be a Dimension, in which case the width is the left and right thickness, while the height is the top and bottom thickness; or it can be a Rectangle, in which case it is the new bounding box. If
paintis specified it is used for the border; otherwise the default paint is used. The border is painted before (below) the
picturepainted. The bounding box of the result is that of the border, while the origin point is that of the original
(with-paint 'blue (border &D[8 5] (fill 'pink (circle 30))))
This is similar to
border, but it just adds extra space around
picture, without painting it. The
sizeis specified the same way. If
backgroundis specified, it becomes the background paint for the entire padded rectangle (both
pictureand the extra padding).#|kawa:3|#
(define circ1 (fill 'teal (circle 25)))#|kawa:4|#
(zbox (line &P[-30 20] &P[150 20])#|kawa:5|#
(hbox circ1 (padding 6 'yellow circ1) (padding 6 circ1)))
This shows a circle drawn three ways: as-is; with padding and a background color; with padding and a transparent background. A line is drawn before (below) the circles to contrast the yellow vs transparent backgrounds.
picturesuch that the point specified by
yposis the new origin point, adjusting the bounding box to match. If the
pictureis a shape, so is the result.
xposcan have four possible values, all of which are symbols:
'left(move the origin to the left edge of the bounding box);
'right(move the origin to the right edge of the bounding box);
'centre) (move the origin to halfway between the left and right edges); or
'origin(don’t change the location along the x-axis). The
yposcan likewise have four possible values:
'top(move the origin to the top edge of the bounding box);
'bottom(move the origin to the bottom edge of the bounding box);
'centre) (move the origin to halfway between the top and bottom edges); or
'origin(don’t change the location along the y-axis).
'centerargument is the same as specifying
'centerfor both axis; this is the default. A single
'originargument is the same as specifying
'originfor both axis; this is the same as just
The 16 control points are shown below, relative to a picture’s bounding box and the X- and Y-axes. The abbreviations have the obvious values, for example
'left 'center.LT OT CT RT ┌────┬──────────┐ │ │ │ │ │ │ LC│ OC C │RC LO├────O──CO──────┤RO │ │ │ └────┴──────────┘ LB OB CB RB
The result of (for example)
(re-center 'left 'centeris
Ptranslated so the origin is at control point
(define D (fill 'light-steel-blue (polygon &P[-20 0] &P[0 -20] &P[60 0] &P[0 40])))#|kawa:2|#
(zbox D (draw 'red (circle 5)))
Above we defined
Das a vaguely diamond-shaped quadrilateral. A small red circle is added to show the origin point. Below we display 5 versions of
Din a line (an
hbox), starting with the original
Dand 4 calls to
(zbox (hbox D (re-center 'top D) (re-center 'bottom D)#|....:4|#
(re-center 'center D) (re-center 'origin D))#|....:5|#
(line &P[0 0] &P[300 0]))
The line at
y=0shows the effects of
An image is a picture represented as a rectangular grid of color values.
An image file is some encoding (usually compressed) of an image,
and mostly commonly has the extensions
A “native image” is an instance of
while a “picture image” is an instance of
(Both classes implement the
BufferedImage is automatically converted to a
Creates a picture image, using either an existing native image
bimage, or an image file specified by
(image src:is roughly the same as
(image (read-imageexcept that the former has the
pathassociated with the resulting picture image. This can make a difference when the image is used or displayed.
If the argument is a
picture, it is converted to an image as if by
Read an image file from the specified
path, and returns a native image object (a
(define img1 (image-read "http://pics.bothner.com/2013/Cats/06t.jpg"))#|kawa:11|#
(scale 0.6 (rotate 30 img1))
Note that while
img1 above is a (native) image,
the scaled rotated image is not an image object.
It is a picture - a more complex value that contains an image.
pictureis converted to an image (as if by using
->image) and then it is written to the specified
path. The format used depends on (the lower-cased string value of) the path: A JPG file if the name ends with
".jpeg"; a GIF file if the name ends with
".gif"; a PNG file if the name ends with
".png". (Otherwise, the defalt is PNG, but that might change.)
Return the width or height of the given
image, in pixels.
pictureto an image (a
RenderedImage). If the
pictureis an image, return as-is. Otherwise, create an empty image (a
BufferedImagewhose size is the
picture’s bounding box), and “paint” the
(define c (fill (circle 10)))#|kawa:2|#
(scale 3 (hbox c (->image c)))
Here we take a circle
c, and convert it to an image. Note how when the image is scaled, the pixel artifacts are very noticable. Also note how the origin of the image is the top-level corner, while the origin of the original circle is its center.
Limited support - SVG and DomTerm output has not been implemented.
pictureto the file specified by
path, in SVG (Structered Vector Graphics) format. If
headersis true (which is the default) first write out the XML and DOCTYPE declarations that should be in a well-formed standaline SVG file. Otherwise, just write of the
<svg>element. (Modern browers should be able to display a file consisting of just the
<svg>element, as long as it has extension
.html; the latter may add some extra padding.)
Returns a SVG representation of
picture, in the form of an
<svg>element, similar to those discussed in Creating XML nodes. If you convert the
<svg>element to a string, you get formatted XML; if you
<svg>element you get an XML literal of form
"#<svg>...</svg>". If you
<svg>element in a DomTerm terminal you get the picture (as a picture). This works because when you display an element in DomTerm it gets inserted into the display.
These procedures require
(import (kawa swing)) in addition to
(import (kawa pictures)).
The convenience function
show-picture is useful
for displaying a picture in a new (Swing) window.
If this is the first call to
picturein a new top-level window (using the Swing toolkit). Sequenent calls to
show-picturewill reuse the window.#|kawa:1|#
(import (kawa swing) (kawa pictures))#|kawa:2|#
(set-frame-size! &D[200 200]); Adjust window size #|kawa:4|#
picture. You can change the displayed picture by:(set!
frameis specified, set its size. Otherwise, remember the size for future
show-picturecalls; if there is already a
show-picturewindow, adjust its size.