Next: Building JavaFX applications, Up: Miscellaneous topics [Contents][Index]
The (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 pictures
library
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 kawa
with the -w
flag, or by running the kawa
command
inside an existing DomTerm-based terminal emulator.
The screenshot below is of the latter,
using the qtdomterm
terminal emulator.
After (import (kawa swing))
you can use show-picture
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.
[
x y ]
¶Construct a point value with the specified x and y values. Both x and y are 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.
[
width height ]
¶Construct a dimension value with the specified width and height values, 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 java.awt.Shape
interface.
The picture
library only provides relatively simple shapes,
but you can use any methods that create a java.awt.Shape
object.
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 draw
procedure.
In the simple case two points are specified, and the result is a line that goes from point p1 to p2. If n points are specied, the result is a polyline: a path consisting of n-1 line 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 line
with 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 p2 is not specified, constructs a rectangle whose upper-left corner
is &P[0 0]
and whose lower-right corner is p1.
If p2 is specified, constructs a rectangle whose upper-left corner
is p1 and whose lower-right corner is p2.
If p2 is a dimension it is considered a relative offset from p1,
just like for polygon
.
Creates a circle with the specified radius.
If the center is not specified, it defaults to &P[0 0]
.
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.
Converts value to a color - or more general a paint.
Specificlly, the return type is java.awt.Paint
.
The value can be any one of:
java.awt.Paint
, commonly a java.awt.Color
.
#xRRGGBB
.
Then 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.
gnu/kawa/models/StandardColor.java
source file.
Case is ignored, and you can optionally use hyphens to separate words.
For example 'hot-pink
, 'hotpink
, and 'hotPink
are all the same sRGB color #xFF69B4
.
Create a new picture that is the “same” as picture
but use paint as the default paint.
For paint you 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 draw
(stroke outline).
#|kawa:1|# (! 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-paint
to create a cloned picture, which is
the same as the original hbox
, except for the default paint,
in this case the color hot-pink
.
#|kawa:4|# (! circ2 (hbox (fill circ1) (with-paint 'hot-pink (fill circ1)))) #|kawa:5|# circ2#|kawa:6|# (with-paint 'lime-green circ2)
![]()
Here circ2
is an hbox
of two filled circles,
one that has unspecified paint, and one that is hot-pink
.
Printing circ2
directly uses black for the
circle with unspecified color,
but if we wrap circ2
in another with-paint
that provides a default that is used for the first circle.
Returns a picture that draws the outline of the shape. This is called stroking. An option may be one of:
Paint
or Color
object, which is used to draw the shape.
'red
or 'medium-slate-blue
.
This is mapped to a Color
.
'miter-join
, 'round-join
, and 'bevel-join
.
The default if none is specified is 'miter-join
.
'square-cap
, 'round-cap
, or 'butt-cap
.
The default is 'butt-cap
.
(This follows SVG and HTML Canvas.
The default in plain Java AWT is a square cap.)
java.awt.Stroke
object.
This combines join-specifier, end-cap-specifier, thickness, and more
in a single object. The BasicStroke
class can specify dashes,
though that is not yet supported for SVG output; only AWT or image output.
Let us illustrate with a sample line lin
and 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|# (define-syntax show-draw #|....:12|# (syntax-rules () #|....:13|# ((_ 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-transform xx xy yx yy x0 y0)
to the point &P[x y]
is the transformed point
&P[(+ (* x xx) (* y yx) x0) (+ (* x xy) (* y yy) y0)]
If using point arguments,
(affine-transform &P[xx xy] &P[yx yy] &P[x0 y0])
is equivalent to:
(affine-transform xx xy yx yy x0 y0)
.
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 angle yields a clockwise rotation.
The angle can be either a quantity (with a unit of
either rad
radians, deg
(degrees), or 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 angle) picture)
Scales the picture by the given factor. The factor can be a real number. The factor can 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 factor) picture)
The single-argument variant creates a transform that adds the offset to each point. The offset can 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 offset) picture)
Make a combined picture from multiple sub-pictures drawn either “next to” or “on top of” each other.
The case of zbox
is 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).
The hbox
and vbox
instead place the sub-pictures
next to each other, in a row or column.
If spacing is specified, if must be a real number.
That much extra spacing is added between each sub-picture.
More precisely: hbox
shifts 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
plus the amount of spacing.
Similarly, vbox
shifts 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 spacing.
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 picture with a rectangular border (frame) around picture’s bounding box. The size specifies 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 paint is specified it is used for the border; otherwise the default paint is used. The border is painted before (below) the picture painted. The bounding box of the result is that of the border, while the origin point is that of the original picture.
#|kawa:2|# (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 size is specified the same way.
If background is specified,
it becomes the background paint for the entire padded
rectangle (both picture and 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.
Translate the picture such that the point specified by xpos and ypos is the new origin point, adjusting the bounding box to match. If the picture is a shape, so is the result.
The xpos can 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);
'center
(or 'centre
) (move the origin to halfway between the left and right edges);
or 'origin
(don’t change the location along the x-axis).
The ypos can 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);
'center
(or 'centre
) (move the origin to halfway between the
top and bottom edges);
or 'origin
(don’t change the location along the y-axis).
A single 'center
argument
is the same as specifying 'center
for both axis; this is the default.
A single 'origin
argument
is the same as specifying 'origin
for both axis;
this is the same as just picture.
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
LC
means '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 'center P)
is P translated so the origin is at control point LC
.
#|kawa:1|# (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 D
as a vaguely diamond-shaped quadrilateral.
A small red circle is added to show the origin point.
Below we display 5 versions of D
in a line (an hbox
),
starting with the original D
and 4 calls to re-center
.
#|kawa:3|# (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=0 shows the effects of re-center
.
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 png
, gif
,
or jpg
/jpeg
.
A “native image” is an instance of java.awt.image.BufferedImage
,
while a “picture image” is an instance of gnu.kawa.models.DrawImage
.
(Both classes implement the java.awt.image.RenderedImage
interface.)
A BufferedImage
is automatically converted to a DrawImage
when needed.
Creates a picture image, using either an existing native image bimage, or an image file specified by path.
Writing (image src: path)
is roughly the same
as (image (read-image path))
except that the former
has the path associated 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 ->image
.
Read an image file from the specified path,
and returns a native image object (a BufferedImage
).
#|kawa:10|# (define img1 (image-read "http://pics.bothner.com/2013/Cats/06t.jpg")) #|kawa:11|# img1#|kawa:12|# (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.
The picture is 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 ".jpg"
or ".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.
Convert picture to an image (a RenderedImage
).
If the picture is an image, return as-is.
Otherwise, create an empty image (a BufferedImage
whose size is the
picture’s bounding box), and “paint” the picture into it.
#|kawa:1|# (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.
Writes the picture to the file specified by path,
in SVG (Structered Vector Graphics) format.
If headers is 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 .svg
, .xml
, or .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 write
the <svg>
element
you get an XML literal of form "#<svg>...</svg>"
.
If you display
the <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 show-pictures
,
displays picture in a new top-level window (using the Swing toolkit).
Sequenent calls to show-picture
will reuse the window.
#|kawa:1|# (import (kawa swing) (kawa pictures)) #|kawa:2|# (show-picture some-picture) #|kawa:3|# (set-frame-size! &D[200 200]) ; Adjust window size #|kawa:4|# (show-picture some-other-picture)
Return a JPanel
that displays picture.
You can change the displayed picture by:
(set! panel:picture some-other-picture)
If frame is specified, set its size.
Otherwise, remember the size for future show-picture
calls;
if there is already a show-picture
window, adjust its size.
You can convert a picture to an image using the ->image
procedure,
or write it to a file using the image-write
procedure.
Next: Building JavaFX applications, Up: Miscellaneous topics [Contents][Index]