// Sketch // Move your mouse to draw. Click to erase. import pulpcore.image.CoreGraphics; import pulpcore.image.BlendMode; import static pulpcore.image.Colors.*; import pulpcore.Input; import pulpcore.math.CoreMath; import pulpcore.scene.Scene; import pulpcore.Stage; public class Sketch extends Scene { int[] colors = { rgb(0, 2, 19), rgb(4, 14, 0), rgb(15, 0, 21), rgb(21, 4, 7), rgb(19, 18, 1) }; Particle[] particles; int particleIndex; int nextFade; int lastX, lastY; boolean erase; boolean wasMouseInside; boolean firstTime = true; @Override public void load() { particles = new Particle[5000]; int numParticlesPerAttractor = 500; for (int i = 0; i < particles.length; i++) { boolean visible = firstTime && i < numParticlesPerAttractor*2; int color = colors[(i / numParticlesPerAttractor) % colors.length]; particles[i] = new Particle(visible, color); } for (int i = 0; i < particles.length; i++) { int j = (i + particles.length - 1) % particles.length; j = j - (j % numParticlesPerAttractor); particles[i].attractor = particles[j]; } erase = true; if (firstTime) { firstTime = false; particleIndex = numParticlesPerAttractor*2; } Input.setCursor(Input.CURSOR_OFF); } @Override public void updateScene(int elapsedTime) { if (Input.isMousePressed()) { load(); } else { // Draw int x = Input.getMouseX(); int y = Input.getMouseY(); if (wasMouseInside && (x != lastX || y != lastY)) { makeParticles(lastX, lastY, x, y); } lastX = x; lastY = y; wasMouseInside = Input.isMouseInside(); // Update particles elapsedTime = Math.min(elapsedTime, 20); for (Particle particle : particles) { particle.update(elapsedTime); } nextFade -= elapsedTime; } } @Override public void drawScene(CoreGraphics g) { if (erase) { erase = false; g.setColor(BLACK); g.fill(); } // Draw particles to surface g.setBlendMode(BlendMode.Add()); for (Particle particle : particles) { particle.draw(g); } // Slowly fade if (nextFade <= 0) { nextFade = 50; g.setBlendMode(BlendMode.SrcOver()); g.setColor(BLACK); g.setAlpha(0x06); g.fill(); } } void makeParticles(int x1, int y1, int x2, int y2) { int numParticles = 2*(int)Math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)); for (int i = 0; i < numParticles; i++) { double x = x1 + i * (x2 - x1) / (double)numParticles; double y = y1 + i * (y2 - y1) / (double)numParticles; particles[particleIndex].init(x, y); particleIndex = (particleIndex + 1) % particles.length; } } // Start the particle the middle, but don't draw until init() static class Particle { double x = Stage.getWidth() / 2 + CoreMath.rand(-50, 50); double y = Stage.getHeight() / 2 + CoreMath.rand(-50, 50); boolean alive = false; double velocityX, velocityY; double lastX, lastY; Particle attractor; int color; Particle(boolean visible, int color) { this.color = color; if (visible) { init(x, y); } } void init(double x, double y) { this.x = x; this.y = y; this.lastX = x; this.lastY = y; this.velocityX = CoreMath.rand(-20, 20); this.velocityY = CoreMath.rand(-20, 20); alive = true; } void update(int elapsedTime) { double dt = elapsedTime / 1000.0; double dx = attractor.x-x; double dy = attractor.y-y; double dSq = dx*dx + dy*dy; if (dSq > 0) { double s = 400 / dSq; if (dSq < 25 * 25) { s = -s / 4; } velocityX += s*dx; velocityY += s*dy; } velocityX *= 0.97; velocityY *= 0.97; x += velocityX * dt; y += velocityY * dt; } void draw(CoreGraphics g) { if (alive) { g.setColor(color); g.drawLine(lastX, lastY, x, y); lastX = x; lastY = y; } } } }