Skip to content

Instantly share code, notes, and snippets.

@riptl
Last active April 3, 2018 21:05
Show Gist options
  • Save riptl/22caf6aed22cc2816930706c70f553e1 to your computer and use it in GitHub Desktop.
Save riptl/22caf6aed22cc2816930706c70f553e1 to your computer and use it in GitHub Desktop.
Grid Engine for Processing
Grid grid;
void setup() {
size(400, 400, FX2D);
grid = new Grid();
}
float radius = 0f;
float phase = 0f;
void draw() {
background(0);
grid.draw();
if (frameCount % 2 == 0) {
radius += 0.5f;
phase += PI / radius;
int x = (int)(radius * cos(phase));
int y = (int)(radius * sin(phase));
grid.set(x, y, new DefaultPart());
}
}
void mouseWheel(MouseEvent event) {
float e = event.getCount();
grid.zoom(e);
}
import java.util.Stack;
// v1
boolean gboxDebug;
class Grid {
// x,y: middle (1 = part), z: distance
final PVector pos;
final int screenFit;
float partScale;
private HashMap<ChunkPos, Chunk> chunks;
float zoomSpeed = 0.025f;
float minZoom = 0.25f;
Grid() {
pos = new PVector(-50, -50, 100);
chunks = new HashMap<ChunkPos, Chunk>();
screenFit = min(width, height);
}
void draw() {
if (gboxDebug)
background(0xFF, 0, 0);
// z-Distance of 1: length of chunk = screenFit
// scale times 1
partScale = (1 / pos.z) * screenFit;
// Move around
if (mousePressed) {
PVector move = PVectors.alloc();
move.set(pmouseX - mouseX, pmouseY - mouseY);
// Convert screen space to part space
move.div(partScale);
pos.x += move.x;
pos.y += move.y;
PVectors.free(move);
}
// Remove one chunk per frame
ChunkPos toClear = null;
// TODO: No offscreen chunk drawing!!
for (Chunk c : chunks.values()) {
c.draw();
if (c.emptyLastFrame) toClear = c.pos;
}
if (toClear != null)
chunks.remove(toClear);
if (gboxDebug) {
noStroke(); fill(0, 0, 0, 128);
rect(0, 0, 200, 65);
fill(0xFF);
text(String.format("Center: Part [%.2f, %.2f]", pos.x, pos.y), 20, 20);
text(String.format("On-screen part size: %.2f", partScale), 20, 40);
}
}
// Zoom after cursor
void zoom(float steps) {
// TODO Use multiple calculations if steps is too big
float dist = sqrt(pos.z) - zoomSpeed * steps;
// new Z pos
float nPosZ = dist * dist;
nPosZ = max(minZoom, nPosZ);
float dZ = pos.z - nPosZ;
// New upper left corner
pos.x += mouseX * dZ / width;
pos.y += mouseY * dZ / height;
pos.z = nPosZ;
partScale = ((1 / pos.z) * screenFit);
}
ChunkPos chunkOfPart(int x, int y) {
ChunkPos p = ChunkPoses.alloc();
p.x = floor((float) x / Chunk.CHUNK_SIZE);
p.y = floor((float) y / Chunk.CHUNK_SIZE);
return p;
}
void set(int x, int y, Part p) {
ChunkPos cp = chunkOfPart(x, y);
Chunk c = chunks.get(cp);
// Create chunk if necessary
if (c == null) chunks.put(cp, c = new Chunk(this, cp));
c.set(x, y, p);
}
Part get(int x, int y) {
ChunkPos cp = chunkOfPart(x, y);
Chunk c = chunks.get(cp);
if (c == null) return null;
return c.get(x, y);
}
}
class Chunk {
// 2^n for best performance
static final int CHUNK_SIZE = 64;
final ChunkPos pos;
final Grid grid;
private Part[] parts;
boolean emptyLastFrame;
Chunk(Grid grid, ChunkPos pos) {
this.grid = grid;
this.pos = pos;
this.parts = new Part[CHUNK_SIZE * CHUNK_SIZE];
}
void draw() {
PVector scrPos = getScreenPos();
// Clear area
if (gboxDebug) {
fill(0);
noStroke();
rect(scrPos.x, scrPos.y, scrPos.z, scrPos.z);
}
// Keep track of draw positions float xp = scrPos.x; float yp = scrPos.y;
// ^ probably less efficient than multiplication if not enough elements
boolean notEmpty = false;
// Iterate all parts, keeping track of index i
for (int y = 0, i = 0; y < CHUNK_SIZE; y++) {
for (int x = 0; x < CHUNK_SIZE; x++, i++) {
Part p = parts[i];
if (p == null) continue;
float xp = scrPos.x + x * grid.partScale;
float yp = scrPos.y + y * grid.partScale;
p.draw(xp, yp, grid.partScale);
notEmpty = true;
}
}
emptyLastFrame = !notEmpty;
if (gboxDebug) {
fill(0, 0xFF, 0, 23);
stroke(0, 0, 0, 23);
rect(scrPos.x, scrPos.y, scrPos.z, scrPos.z);
}
PVectors.free(scrPos);
}
// x, y: top left; z: length of side
PVector getScreenPos() {
PVector v = PVectors.alloc();
// v = distance to middle
v.set(pos.x * CHUNK_SIZE, pos.y * CHUNK_SIZE, 0);
v.sub(grid.pos.x, grid.pos.y, 0);
// scale part distance to real distance
v.mult(grid.partScale);
// set Z to length of side
v.z = CHUNK_SIZE * grid.partScale;
return v;
}
int absModulo(int base, int modulo) {
if (base >= 0)
return base % modulo;
else
return modulo - ((-base-1) % modulo) - 1;
}
void set(int x, int y, Part p) {
int rx = absModulo(x, CHUNK_SIZE);
int ry = absModulo(y, CHUNK_SIZE);
try {
parts[ry * CHUNK_SIZE + rx] = p;
} catch(Exception e) {
println(x);
println(y);
println(rx);
println(ry);
throw e;
}
}
Part get(int x, int y) {
x = absModulo(x, CHUNK_SIZE);
y = absModulo(y, CHUNK_SIZE);
return parts[y * CHUNK_SIZE + x];
}
void clear() {
java.util.Arrays.fill(parts, null);
}
}
static class ChunkPos {
int x, y;
ChunkPos() { x = 0; y = 0; }
ChunkPos(int x, int y) { this.x = x; this.y = y; }
@Override
public int hashCode() {
return java.util.Objects.hash(x, y);
}
@Override
public boolean equals(Object other) {
if (other == this) { return true; }
if (!(other instanceof ChunkPos)) { return false; }
ChunkPos rhs = ((ChunkPos) other);
return x == rhs.x && y == rhs.y;
}
}
// Cache PVectors for performance :P
static class SimpleFactory<T> {
// Wish I had lambdas
static interface Allocator<T> {
public T allocate();
}
public SimpleFactory(Allocator<T> allocator) { this.allocator = allocator; }
private Allocator<T> allocator;
private Stack<T> cache = new Stack<T>();
T alloc() {
if (cache.empty())
return allocator.allocate();
else
return cache.pop();
}
void free(T t) {
cache.push(t);
}
}
// Make it look like a static class
static SimpleFactory<PVector> PVectors = new SimpleFactory(new SimpleFactory.Allocator() {
@Override public PVector allocate() { return new PVector(); }
});
static SimpleFactory<ChunkPos> ChunkPoses = new SimpleFactory(new SimpleFactory.Allocator() {
@Override public ChunkPos allocate() { return new ChunkPos(); }
});
abstract class Part {
abstract void draw(float x, float y, float s);
// Returns true if part is offscreen
boolean cull(float x, float y, float s) {
return x > width || y > height || x < -s || y < -s;
}
}
class DefaultPart extends Part {
void draw(float x, float y, float s) {
if (cull(x, y, s)) return;
strokeWeight(2); stroke(0xCC); fill(0xFF);
rect(x, y, s, s);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment