CodeDraw is a beginner-friendly drawing library which can be used to create pictures, animations and even interactive applications. It is designed for people who are just starting to learn programming, enabling them to create graphical applications.
The source code, documentation and additional examples can be found in the CodeDraw Repository.
If you are interested in the detailed designed decision that shaped this library, consider reading my bachelor thesis on CodeDraw: A Graphics Library for Novice Programmers.
The following two programs are implemented using CodeDraw and provide examples of how this library can be used.
Particle Gravity
The first program is a particle game where each particle moves towards the mouse cursor. The particles' speed increases as they get closer to the cursor.
As this example demonstates, CodeDraw programs can be implemented using structured programming alone. Users of the library do not have to know how to implement classes or functions.
import codedraw.*;
public class ParticleGravity {
public static void main(String[] args) {
CodeDraw cd = new CodeDraw();
EventScanner es = cd.getEventScanner();
final int particleCount = 100000;
final double exponent = -0.25 * Math.sqrt(2) - 0.5;
final double speed = 100;
// game state
int mouseX = cd.getWidth() / 2;
int mouseY = cd.getHeight() / 2;
double[] particlesX = new double[particleCount];
double[] particlesY = new double[particleCount];
for (int i = 0; i < particleCount; i++) {
particlesX[i] = Math.random() * cd.getWidth();
particlesY[i] = Math.random() * cd.getHeight();
}
// game loop
while (!cd.isClosed()) {
// event handling
while (es.hasEventNow()) {
if (es.hasMouseMoveEvent()) {
MouseMoveEvent a = es.nextMouseMoveEvent();
mouseX = a.getX();
mouseY = a.getY();
}
else {
es.nextEvent();
}
}
// game logic
for (int i = 0; i < particleCount; i++) {
double distanceX = mouseX - particlesX[i];
double distanceY = mouseY - particlesY[i];
double movementFactor = speed * Math.pow(distanceX * distanceX + distanceY * distanceY, exponent);
particlesX[i] += distanceX * movementFactor;
particlesY[i] += distanceY * movementFactor;
}
// rendering
cd.clear(Palette.BLACK);
for (int i = 0; i < particleCount; i++) {
cd.setPixel((int)particlesX[i], (int)particlesY[i], Palette.RED);
}
cd.show(16);
}
}
}
Game of Life
The second example is an implementation of Conway's Game of Life. The rules that determine the state of each cell (white or black) can be found in the Conway's Game of Life Wikipedia article.
This implementation also allows the user place black cells by clicking on a white cell and dragging the mouse, as well as place white cells by clicking on a black cell and dragging the mouse.
import codedraw.*;
public class GameOfLife {
private static final int FIELD_SIZE = 10;
public static void main(String[] args) {
final int size = 1 << 6; // must be power of 2
final int mask = size - 1;
CodeDraw cd = new CodeDraw(size * FIELD_SIZE, size * FIELD_SIZE);
EventScanner es = cd.getEventScanner();
boolean[][] field = createRandomBooleans(size);
boolean isMouseDown = false;
boolean setValue = false;
for (int i = 0; !cd.isClosed(); i++) {
while (es.hasEventNow()) {
if (es.hasMouseDownEvent()) {
MouseDownEvent a = es.nextMouseDownEvent();
isMouseDown = true;
// the value that is used to draw cells depends on the state
// of the cell where the initial click happens
// if that cell was white then every subsequent mouse move will draw black cells.
int x = a.getX() / FIELD_SIZE;
int y = a.getY() / FIELD_SIZE;
setValue = field[x][y] = !field[x][y];
}
else if (es.hasMouseUpEvent() || es.hasMouseLeaveEvent()) {
es.nextEvent();
isMouseDown = false;
}
else if (es.hasMouseMoveEvent()) {
MouseMoveEvent a = es.nextMouseMoveEvent();
if (isMouseDown) {
field[a.getX() / FIELD_SIZE][a.getY() / FIELD_SIZE] = setValue;
}
}
else {
es.nextEvent();
}
}
// update to next generation only every eighth render
if (i % 8 == 0) {
field = simulateNextGeneration(field, mask);
}
render(cd, field);
}
}
private static boolean[][] createRandomBooleans(int size) {
boolean[][] field = new boolean[size][size];
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
field[x][y] = Math.random() > 0.5;
}
}
return field;
}
public static boolean[][] simulateNextGeneration(boolean[][] field, int mask) {
final int radius = 1;
boolean[][] nextField = new boolean[field.length][field.length];
for (int x = 0; x < field.length; x++) {
for (int y = 0; y < field[x].length; y++) {
int sum = 0;
for (int xn = -radius; xn <= radius; xn++) {
for (int yn = -radius; yn <= radius; yn++) {
// ignore the center and only sum if the neighbor is alive
if ((xn != 0 || yn != 0) && field[(x + xn) & mask][(y + yn) & mask]) {
sum++;
}
}
}
if (field[x][y]) {
nextField[x][y] = sum == 2 || sum == 3;
}
else {
nextField[x][y] = sum == 3;
}
}
}
return nextField;
}
public static void render(CodeDraw cd, boolean[][] field) {
for (int x = 0; x < field.length; x++) {
for (int y = 0; y < field[x].length; y++) {
cd.setColor(field[x][y] ? Palette.BLACK : Palette.WHITE);
cd.fillSquare(FIELD_SIZE * x, FIELD_SIZE * y, FIELD_SIZE);
}
}
cd.show(16);
}
}