/** *

Layers_by_spxl

*

by subpixel

* *

Last modified: 2009-01-09 10:47

* *

Experiments in graphics using PGraphics layers to construct an image.

* *

I like the idea of having artificial "construction lines" optionally * appear in an image, similar to showing a "wireframe" on a 3D model. This * example uses two layers of construction lines: one behind the objects, and * one in front of the objects. The level of detail can also be adjusted; in * this case, the crosshairs to the edge of the window are at a higher * level (not to be confused with higher layer!) than the small "+" to mark * a circle's centre and a circle's outline.

* *

The base Shape class includes details for presentation (fill colour, * edge colour, edge weight, etc) as well as position and animation (in this * case simple velocity with acceleration), including drawing a "trace" of * the shape's location. The motion calculations aren't "reality-accurate", * but they are time based, not frame based. To demonstrate this, you can * increase and decrease the sketch's frame rate and see that the objects * seem to move in similar trajectories whether the updates are regular * (eg at 32 fps) or infrequent (eg at 1 fps). Time is "stopped" (or more * like skipped) when the sketch is paused.

* *

Another neat trick implemented is seamless wrapping of objects at the * edges of the window. This is achieved by drawing an object multiple times, * if required, offset by the window width and/or height. A similar trick is * used to wrap the trace line generated for the shape.

* *

Keys:

* */ // CONSTANTS static final int MAX_CIRCLES = 30; static final int NUM_LAYERS = 5; static final int LAYER_TRACE = 0; static final int LAYER_CNSTR_UNDER = 1; static final int LAYER_BACKGROUND = 2; static final int LAYER_DRAW = 3; static final int LAYER_CNSTR_OVER = 4; // GLOBAL VARIABLES boolean paused = false; int frameRate = 32; boolean showConstructionLines = true; int showConstructionLinesLevel = 2; color constructionLinesColor = color(127, 127, 127, 127); int constructionLinesWeight = 1; boolean showTrace = true; color traceColor = color(255, 255, 0, 63); int traceLinesWeight = 2; color shapeBgColor = color(255, 0, 0, 80); color cWhite = color(255, 255, 255); color cWhite50 = color(255, 255, 255, 127); color cRed = color(255, 0, 0); Vector2D gravity = new Vector2D(0, 0); Circle[] c; PGraphics[] layer; boolean[] showLayer; int clockMillis; int prevMillis; void setup() { size(600, 400, P2D); frameRate(this.frameRate); smooth(); background(0); layer = new PGraphics[NUM_LAYERS]; showLayer = new boolean[NUM_LAYERS]; for (int i = 0; i < NUM_LAYERS; i++) { layer[i] = createGraphics(width, height, P2D); showLayer[i] = true; } c = new Circle[MAX_CIRCLES]; for (int i = 0; i < MAX_CIRCLES; i++) { c[i] = new Circle(random(width), random(height), 5 + random(50)); c[i].v = new Vector2D((random(50) + 20) * (random(1) < 0.5 ? -1 : 1), - random(50)); c[i].a = gravity; c[i].fillColor = color(rnd(256), rnd(256), rnd(256)); c[i].bgColor = shapeBgColor; c[i].showEdge = false; c[i].showFill = true; c[i].edgeWeight = 2; println("c[" + i + "]: " + c[i].str()); } } void draw() { prevMillis = clockMillis; clockMillis = millis(); int frameMillis = clockMillis - prevMillis; background(0); // Clear the layers (except the trace layer) for (int i = 0; i < NUM_LAYERS; i++) { if (i != LAYER_TRACE) layer[i].background(0, 0); // Note: layers are transparent! layer[i].beginDraw(); } for (int i = 0; i < MAX_CIRCLES; i++) { c[i].animTick(frameMillis); c[i].draw(); } for (int i = 0; i < NUM_LAYERS; i++) { layer[i].endDraw(); if (showLayer[i]) image(layer[i], 0, 0); } } void keyPressed() { // SHOW/HIDE LAYERS if (key >= '1' && (key - '0' <= NUM_LAYERS)) { int n = key - '0' - 1; showLayer[n] = !showLayer[n]; } else switch (key) { // RESET case 'r': setup(); break; // FRAMERATE case ' ': togglePaused(); break; case 'f': if (frameRate > 1) frameRate(frameRate /= 2); // Slow down break; case 'g': frameRate(frameRate *= 2); // Go faster break; // MOTION TRACE case 't': showTrace = !showTrace; break; case 'x': layer[LAYER_TRACE].background(0, 0); // Erase trace layer break; // CONSTRUCTION LINES case 'c': showConstructionLines = !showConstructionLines; break; case 'e': showConstructionLinesLevel++; break; case 'd': if (showConstructionLinesLevel > 0) showConstructionLinesLevel--; break; // GRAVITY case 'z': gravity.y = 0; break; case 'q': gravity.y -= 1; // Gravity "upwards" break; case 'a': gravity.y += 1; // gravity "downwards" break; } } public void pause() { paused = true; noLoop(); } public void togglePaused() { paused = ! paused; if (paused) { noLoop(); } else { loop(); clockMillis = millis(); } } // ------------------------------------------------------------ // Return a random colour // ------------------------------------------------------------ color rndColor() { return color(rnd(256), rnd(256), rnd(256)); } // ------------------------------------------------------------ // Return a random integer from 0 to n (not including n) // that is also not equal to x // ------------------------------------------------------------ int rndNotX(int n, int x) { int val = int(random(n - 1)); return (val < x) ? val : val + 1; } // ------------------------------------------------------------ // Return a random integer from 0 to n (not including n) // ------------------------------------------------------------ int rnd(int n) { return int(random(n)); } // ------------------------------------------------------------ // "Wrap" a value within the limits of minValue and maxValue (inclusive). // Similar to performing modulo arithmetic. // ------------------------------------------------------------ int wrapConstrain(int value, int minValue, int maxValue) { if (value < minValue || value > maxValue) { int rangeSize = maxValue - minValue + 1; for (;value < minValue; value += rangeSize); for (;value > maxValue; value -= rangeSize); } return value; }