This page documents the 2D drawing library shipped under lib/graphics.
These are library classes, not core built-in classes. Load them with intern. They are written in Nex on top of Java2D and Swing (with "java"), with no runtime, typechecker or compiler support of their own.
Platform Scope
graphics/Canvas: JVM interpreter, generated JVMgraphics/Color: JVM interpreter, generated JVMgraphics/Font: JVM interpreter, generated JVMgraphics/Key: JVM interpreter, generated JVMgraphics/Canvas_Input: JVM interpreter, generated JVMgraphics/Picture: JVM interpreter, generated JVM
Opening a window needs a display. Everything else (drawing, reading pixels, saving images) also works headless, for example in tests or on a server.
Load
intern graphics/Canvas -- also interns Color, Font, Key, Canvas_Input and Picture
Example
intern graphics/Canvas
let c := create Canvas.make(400, 400)
c.set_pen_color(Color.BLUE)
c.filled_circle(0.5, 0.5, 0.25)
c.set_pen_color(Color.BLACK)
c.text(0.5, 0.9, "hello")
c.save("circle.png")
c.show() -- opens a window
c.wait_until_closed() -- keep it on screen until the user closes it
examples/graphics_example.nex draws a scene and runs a short animation. examples/graphics_input_example.nex is a small sketchpad driven by the mouse and keyboard. examples/graphics_sprite_example.nex flies a rocket sprite with the arrow keys and drags it with the mouse.
Canvas
A Canvas draws in user coordinates onto an offscreen image, and shows that image in a window on request.
- Coordinates. (0, 0) is the bottom left and (1, 1) the top right until
set_x_scale/set_y_scalechange them. y grows upward. - Centre-based shapes.
circle(x, y, radius),square(x, y, half_side)andrectangle(x, y, half_width, half_height)are all centred on (x, y). Lengths are scaled per axis, so with unequal x and y scales a circle comes out as an ellipse. - Pen. Lines and outlines are drawn with the pen color,
2 * pen_radiuspixels wide. The pen radius is in pixels, not user units. Colors with an alpha below 255 blend with what is underneath. - Window.
showcopies the offscreen image to the window, opening the window the first time. A canvas that never callsshownever opens one. Without a display,showonly updates the onscreen image andwait_until_closedreturns at once, so the same program runs headless. - Buffering. With double buffering off (the default), every drawing command also refreshes an already open window. With it on, the window changes only on
show. Turn it on for flicker-free animation. - Program exit. The window closes when the program ends. A script that should stay on screen ends with
wait_until_closed(). - File names. A relative name given to
saveorPicture.loadis resolved against the directorynexwas started from (NEX_USER_DIR), not the Nex install directory the launcher runs in.
Construction
| Constructor | Arguments | Description |
|---|---|---|
make |
w, h: Integer |
A w by h pixel canvas, white, scale 0..1 on both axes, black pen of radius 1, Font.SANS, no double buffering. Requires w > 0, h > 0. |
Queries
| Feature | Type | Description |
|---|---|---|
width, height |
Integer |
Size in pixels. |
x_min, x_max, y_min, y_max |
Real |
The visible user-coordinate rectangle. |
pen_radius |
Real |
Pen radius in pixels. |
pen_color |
Color |
Current drawing color. |
font |
Font |
Current text font. |
is_double_buffered |
Boolean |
Whether drawing waits for show. |
has_display() |
Boolean |
Whether a window can be opened at all. |
is_window_open() |
Boolean |
Whether this canvas has a window on screen. |
color_at(x, y: Real) |
Color |
The color of the pixel at user position (x, y). Requires the point to be inside the scale. |
is_image_file_name(filename: String) |
Boolean |
Whether save understands the file's extension. |
input |
Canvas_Input |
The underlying mouse and keyboard state. See Input. |
Drawing commands
| Method | Arguments | Description |
|---|---|---|
line |
x0, y0, x1, y1: Real |
A line from (x0, y0) to (x1, y1). |
point |
x, y: Real |
A dot of the pen's radius. |
circle, filled_circle |
x, y, radius: Real |
A circle centred on (x, y). |
square, filled_square |
x, y, half_side: Real |
An axis-aligned square centred on (x, y). |
rectangle, filled_rectangle |
x, y, half_width, half_height: Real |
An axis-aligned rectangle centred on (x, y). |
polygon, filled_polygon |
x, y: Array[Real] |
The closed polygon through (x[i], y[i]). Requires equal lengths and at least 3 points. |
text |
x, y: Real, s: String |
s centred on (x, y). |
text_left |
x, y: Real, s: String |
s starting at (x, y), vertically centred. |
text_right |
x, y: Real, s: String |
s ending at (x, y), vertically centred. |
picture |
x, y: Real, p: Picture |
p centred on (x, y) at its natural size in pixels. |
picture |
x, y: Real, p: Picture, half_width, half_height: Real |
p centred on (x, y), scaled to 2 * half_width by 2 * half_height user units. |
picture |
x, y: Real, p: Picture, half_width, half_height, degrees: Real |
As above, turned degrees counterclockwise about its centre. |
Radii and half-lengths must be non-negative.
Control commands
| Method | Arguments | Description |
|---|---|---|
set_x_scale |
x0, x1: Real |
Make x0..x1 the visible x range. Requires x0 < x1. |
set_y_scale |
y0, y1: Real |
Make y0..y1 the visible y range. Requires y0 < y1. |
set_pen_radius |
radius: Real |
Requires radius >= 0. |
set_pen_color |
color: Color |
|
set_font |
f: Font |
|
set_canvas_size |
w, h: Integer |
Resize and clear to white. Scale, pen and font are kept. |
enable_double_buffering, disable_double_buffering |
none | See Buffering above. |
show |
none | Copy the offscreen image to the window, opening it if needed. |
clear |
none, or color: Color |
Fill the whole canvas with white, or with color. |
pause |
ms: Integer |
Sleep for ms milliseconds. For pacing an animation. |
wait_until_closed |
none | Block until the user closes the window. Returns at once if none is open. |
save |
filename: String |
Write the image. The format comes from the extension: .png, .jpg/.jpeg, .gif or .bmp. |
Input
While the window is open, the mouse and keyboard are polled, as in StdDraw. A program reads them once per pass through its loop:
from until c.is_key_pressed(Key.ESCAPE) or not c.is_window_open() do
if c.is_mouse_pressed() then
c.filled_circle(c.mouse_x(), c.mouse_y(), 0.01)
end
c.pause(16)
end
| Feature | Returns | Description |
|---|---|---|
has_mouse_position() |
Boolean |
Whether the mouse has been over the window yet. |
mouse_x(), mouse_y() |
Real |
Where the mouse was last seen over the window, in user coordinates. Require has_mouse_position(). |
is_mouse_pressed() |
Boolean |
Whether a mouse button is held down. |
has_next_key_typed() |
Boolean |
Whether a typed character is waiting. |
next_key_typed() |
String |
The oldest typed character not read yet, as a one-character string, removed from the queue. Requires has_next_key_typed(). |
is_key_pressed(code: Integer) |
Boolean |
Whether the key with this code is held down. Use the Key constants. |
Typed characters are what the keyboard produced ("A" with Shift held, "a" without). Held keys are physical keys (Key.A either way, Key.LEFT, Key.SHIFT). Keys and buttons held when the window loses focus count as released, since their release would never be heard.
Keyboard input goes to the window that has focus. A new window normally gets it. If it doesn't, click it.
Pictures
Load a Picture once and draw it as often as needed. A PNG's transparency is kept, so a sprite with a transparent background draws cleanly over the scene. Pictures are always drawn fully opaque, whatever the pen's alpha.
let ship := create Picture.load("ship.png")
c.enable_double_buffering()
from until c.is_key_pressed(Key.ESCAPE) or not c.is_window_open() do
if c.is_key_pressed(Key.LEFT) then heading := heading + 4.0 end
if c.is_mouse_pressed() then
x := c.mouse_x()
y := c.mouse_y()
end
c.clear()
c.picture(x, y, ship, 0.05, 0.05, heading)
c.show()
c.pause(16)
end
To tell whether the mouse is over a picture, compare mouse_x/mouse_y with where you drew it, for example (c.mouse_x() - x).hypot(c.mouse_y() - y) < r.
Canvas_Input
The state behind a canvas's input queries, in pixels (0, 0 at the top left). The window feeds it from Swing's event thread, so it is safe to read while events arrive. It can also be fed directly, to test input handling without a window or to replay a scripted session:
c.input.record_mouse_button(40, 60, true) -- pixel (40, 60), button down
c.input.record_key_typed("q")
c.input.record_key(Key.LEFT, true) -- LEFT held
| Feature | Description |
|---|---|
record_mouse_moved(px, py: Integer) |
The mouse is at pixel (px, py). |
record_mouse_button(px, py: Integer, is_down: Boolean) |
A button went down or up at (px, py). |
record_key_typed(s: String) |
A character was typed. Requires a one-character s. |
record_key(code: Integer, is_down: Boolean) |
A key went down or up. |
release_all() |
Forget held keys and buttons. |
has_mouse_position(), mouse_px(), mouse_py(), is_mouse_pressed(), has_next_key_typed(), next_key_typed(), is_key_pressed(code) |
As on Canvas, with positions in pixels. |
It also implements java.awt.event.MouseListener, MouseMotionListener, KeyListener and WindowFocusListener, which is how the window feeds it.
Key
Key codes for is_key_pressed. They are java.awt.event.KeyEvent's virtual key codes.
- Letters
AtoZand digitsDIGIT_0toDIGIT_9. - Arrows
LEFT,RIGHT,UP,DOWN. SPACE,ENTER,ESCAPE,TAB,BACKSPACE,DELETE.SHIFT,CONTROL,ALT.HOME,END_KEY,PAGE_UP,PAGE_DOWN. (END_KEYbecauseendis a keyword.)F1toF12.
Picture
An image in memory: read from a PNG, JPEG, GIF or BMP file, or made blank, then read and changed pixel by pixel, transformed, drawn with Canvas.picture and saved.
Pixels are addressed by (col, row), with (0, 0) at the top left and rows growing downward, as in the image file. This is unlike a Canvas's user coordinates. Every pixel can hold any color and alpha exactly, whatever format the picture was loaded from.
| Constructor | Arguments | Description |
|---|---|---|
load |
filename: String |
Read the image. A relative name is resolved against the directory nex was started from. Raises Picture.load: no such file: ... or Picture.load: not a readable image: .... |
make |
w, h: Integer |
A blank w by h picture, every pixel fully transparent. |
from_java |
raw: Any |
A copy of a java.awt.image.BufferedImage. |
| Feature | Returns | Description |
|---|---|---|
width, height |
Integer |
Size in pixels. |
source |
String |
The file it was read from, as resolved; "" for a picture made in memory. |
get(col, row: Integer) |
Color |
The pixel's color, alpha included. |
set(col, row: Integer, color: Color) |
| Set the pixel, alpha included. | |
get_rgb(col, row: Integer) |
Integer |
The pixel as 0xRRGGBB, ignoring alpha. |
set_rgb(col, row, rgb: Integer) |
| Set the pixel to the opaque color rgb (0xRRGGBB), so 0xFF0000 is red. |
|
get_argb(col, row: Integer) |
Integer |
The pixel as 0xAARRGGBB (always non-negative); alpha 0x00 is transparent, 0xFF opaque. |
set_argb(col, row, argb: Integer) |
| Set the pixel from 0xAARRGGBB. |
|
fill(color: Color) |
| Set every pixel to color, alpha included. |
|
cropped(col, row, w, h: Integer) |
Picture |
The w by h region whose top-left pixel is (col, row); it must lie inside the picture. |
scaled(w, h: Integer) |
Picture |
Resized to w by h pixels, smoothly (bilinear) interpolated. |
flipped_horizontally() |
Picture |
Mirror image, left and right swapped. |
flipped_vertically() |
Picture |
Upside down, top and bottom swapped. |
rotated_left() |
Picture |
A quarter turn counterclockwise; width and height swap. |
rotated_right() |
Picture |
A quarter turn clockwise; width and height swap. |
grayscale() |
Picture |
Shades of gray of the same brightness (ITU-R BT.601 luma: 0.299 R + 0.587 G + 0.114 B), alpha kept. |
save(filename: String) |
| Write the picture; the format comes from the extension (.png, .jpg/.jpeg, .gif, .bmp). Only PNG keeps transparency; for JPEG, GIF and BMP the picture is laid over white first. |
|
is_image_file_name(filename: String) |
Boolean |
Whether save understands the extension. |
clone |
Any |
An independent copy: changing its pixels leaves the original alone. |
to_java() |
Any |
The underlying java.awt.image.BufferedImage (TYPE_INT_ARGB), for use inside with "java". It is shared, not copied. |
Pixel coordinates are checked: col must be in 0..width-1 and row in 0..height-1, and rgb/argb must be in range, or the call fails its precondition. The transformations return a new picture and leave this one unchanged. Only set, set_rgb, set_argb and fill change a picture.
intern graphics/Picture
-- Red rises left to right, blue top to bottom.
let p := create Picture.make(256, 256)
from let row := 0 until row = 256 do
from let col := 0 until col = 256 do
p.set_rgb(col, row, col * 65536 + row)
col := col + 1
end
row := row + 1
end
p.save("gradient.png")
-- Swap the red and blue channels of a photo.
let photo := create Picture.load("photo.jpg")
from let row := 0 until row = photo.height do
from let col := 0 until col = photo.width do
let c := photo.get(col, row)
photo.set(col, row, create Color.make_rgba(c.blue, c.green, c.red, c.alpha))
col := col + 1
end
row := row + 1
end
photo.grayscale().rotated_right().save("photo_gray.png")
A per-pixel loop is fine for small and medium images: on the compiled backend one get and one set per pixel over a 512x512 picture takes a second or two (grayscale is such a loop, so it costs about the same). fill, cropped, scaled and the flips and turns are done by Java2D in a single call, so they are much faster.
Color
An immutable RGBA value. Each channel is 0..255, and the constructor checks it. = compares colors by value.
| Constructor | Arguments | Description |
|---|---|---|
make |
r, g, b: Integer |
An opaque color. |
make_rgba |
r, g, b, a: Integer |
Alpha 0 is transparent, 255 opaque. |
| Feature | Returns | Description |
|---|---|---|
red, green, blue, alpha |
Integer |
The channels. |
with_alpha(a: Integer) |
Color |
This color with a different alpha. |
is_opaque() |
Boolean |
alpha = 255. |
Constants: BLACK, WHITE, RED, GREEN, BLUE, YELLOW, CYAN, MAGENTA, ORANGE, PINK, GRAY, LIGHT_GRAY, DARK_GRAY.
Font
An immutable font description. "Serif", "SansSerif" and "Monospaced" are always available. Other family names depend on the machine and fall back to a default when missing.
| Constructor | Arguments | Description |
|---|---|---|
make |
family_name: String, point_size: Integer |
A plain font. |
make_styled |
family_name: String, point_size: Integer, bold_flag, italic_flag: Boolean |
| Feature | Returns | Description |
|---|---|---|
family |
String |
Family name. |
size |
Integer |
Point size. |
is_bold, is_italic |
Boolean |
Style. |
with_size(n: Integer) |
Font |
This font at size n. |
bold(), italic() |
Font |
A bold or italic version. |
Constants: SANS, SERIF, MONO (all 16 point).