All chapters
Cookbook · Chapter 2

Drawing text, curves, and shapes in 2D

Shapes, color, images, type, curves, custom polygons, SVG shapes, and offscreen buffers.

Basic shapes

The primitives match Processing one-for-one:

point(x, y);
line(x1, y1, x2, y2);
rect(x, y, w, h);
square(x, y, extent);
circle(x, y, diameter);
ellipse(x, y, w, h);
triangle(x1, y1, x2, y2, x3, y3);
quad(x1, y1, x2, y2, x3, y3, x4, y4);

arc() takes start and stop angles (radians) plus a mode — PIE, OPEN, or CHORD:

arc(50, 55, 50, 50, 0, HALF_PI, PIE);
arc(200, 200, 300, 300, 0, PI + QUARTER_PI, CHORD);

Change how coordinates are read with rectMode() / ellipseMode() (CENTER, RADIUS, CORNER, CORNERS):

ellipseMode(RADIUS);
ellipse(200, 200, 120, 120);   // 120 is now the radius

Stroke and fill

fill(255);          // white interior
stroke(0);          // black outline
strokeWeight(8);    // line thickness
noFill();           // outline only
noStroke();         // fill only

Style line corners and ends with strokeJoin() (ROUND, BEVEL, MITER) and strokeCap() (ROUND, SQUARE, PROJECT):

strokeWeight(15);
strokeJoin(ROUND);
strokeCap(ROUND);

Working with colors

Channels run 0–255, like Processing’s default. A second value on a grey, or a fourth on an RGB color, is the alpha:

fill(255);            // white
fill(255, 204);       // white at ~80% opacity
fill(255, 63, 89);    // an RGB color
fill(0, 0, 0, 12);    // near-transparent black

Store a color in a color_t, and pull channels back out with red(), green(), blue():

const color_t soft_red = color(255, 63, 89);
fill(soft_red);

float r = red(soft_red) / 255.0f;

Need HSB? Umfeld ships the GLM math library; convert with it:

#include "glm/gtx/color_space.hpp"

glm::vec3 rgb = glm::rgbColor(glm::vec3(hue * 360.0f, saturation, brightness));
fill(rgb.r * 255.0f, rgb.g * 255.0f, rgb.b * 255.0f);

Working with images

Load once in setup(), draw with image(). Files live in a data/ folder beside the sketch; loadImage() also accepts a URL.

PImage* img;

void setup() {
    img = loadImage("umfeld.png");   // or a "https://…" URL
    imageMode(CENTER);
}

void draw() {
    image(img, mouseX, mouseY);       // or image(img, x, y, w, h)
}

tint() colors/fades an image and noTint() clears it. img->width / img->height give the size, and img->resize(w, h) rescales (pass 0 for one side to keep the aspect ratio):

img->resize(0, height);

tint(255, 0, 0, 128);   // red wash at half alpha
image(img, mouseX, mouseY);
noTint();

Drawing text

Load a font (its size is baked in), set it, then text():

PFont* font;

void setup() {
    font = loadFont("RobotoMono-Regular.ttf", 44);
    textFont(font);
}

void draw() {
    fill(0);
    textSize(32);
    textAlign(CENTER, CENTER);     // horizontal, vertical
    text("hello umfeld", width / 2, height / 2);
}

Alignment constants: LEFT / CENTER / RIGHT and TOP / CENTER / BOTTOM / BASELINE. textLeading() sets line spacing for multi-line strings.

For a quick on-screen readout (FPS, debug values), skip font loading and use debug_text(), which draws with a built-in font:

debug_text("FPS: " + nf(frameRate, 3, 1), 10, 20);

Drawing curves

bezier() draws a cubic curve from four control points; curveVertex() builds a smooth Catmull-Rom spline through points inside a shape:

bezier(120, 80, 120, 300, 340, 80, 340, 300);

beginShape();
curveVertex(100, 200);   // control point
curveVertex(100, 200);   // first visible point (repeated)
curveVertex(200, 100);
curveVertex(300, 200);
curveVertex(300, 200);   // last visible point (repeated)
endShape();

To compute a point on a Bézier yourself (Umfeld has no bezierPoint() helper), evaluate the formula — useful for placing objects along a curve:

float bezier_at(float a, float b, float c, float d, float t) {
    float u = 1 - t;
    return u*u*u*a + 3*u*u*t*b + 3*u*t*t*c + t*t*t*d;
}
// float x = bezier_at(120, 120, 340, 340, 0.5f);

Drawing custom shapes

Build any polygon with beginShape() / vertex() / endShape(). Pass CLOSE to join the last point to the first. The optional mode (POINTS, LINES, TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN, QUAD_STRIP, LINE_STRIP) controls how vertices connect:

beginShape();
vertex(120, 80);
vertex(340, 80);
vertex(340, 300);
vertex(120, 300);
endShape(CLOSE);

Cut a hole with beginContour() / endContour() (wind it opposite to the outer shape):

beginShape();
vertex(20, 20); vertex(180, 20); vertex(180, 180); vertex(20, 180);
beginContour();
vertex(60, 60); vertex(60, 120); vertex(120, 120); vertex(120, 60);
endContour();
endShape(CLOSE);

SVG and PShape

Vector shapes load into a PShape and draw with shape():

PShape* s;

void setup() {
    s = loadShape("bot.svg");
}

void draw() {
    shape(s, 100, 100);
}

Offscreen drawing

createGraphics() makes a PGraphics buffer you draw into off-screen, then stamp onto the window with image(). Wrap its drawing in beginDraw() / endDraw():

PGraphics* pg;

void setup() {
    pg = createGraphics(100, 100);
}

void draw() {
    pg->beginDraw();
    pg->background(102);
    pg->stroke(255);
    pg->line(0, 0, mouseX, mouseY);
    pg->endDraw();

    image(pg, 150, 50);
}