So, i'm sorry if this is a weird or stupid question but I genuinely couldn't find an answer. So, the thing is I'm trying to do a visual representation of the multiplication tables, so i divide a circle in a certain number of "slices", for example I divide it in 10. Then I join each point with its product, for example for the 2 times table i make a line between 1 and 2, another one between 2 and 4, 3 and 6 and so on...
The thing is, if i surpass a certain amount of "slices" i can clearly see Processing drawing each one of the lines one by one. I wanted to progressively change the number of slices so you can see how does it evolve, but then the lines would just have to "appear" or change instantaneously since the "animation" makes no sense if you see it drawing every line. Is there a way I can improve the speed of the program, or just make it show all lines at once?
For reference, this is how i kinda want it to look like:
YouTube video
This is the code i'm using (with the ControlP5 library and soooo poorly optimized):
import controlP5.*;
ControlP5 cp5;
Knob myKnobA;
Knob myKnobB;
int ncosas = 30;
float sumangle = (2*PI)/ncosas;
float angle = HALF_PI + PI + sumangle;
int radius = 100;
int counter = 1;
int sumar = 15;
int tablade = 2;
int prueba = 30;
void setup(){
size(400,400);
background(255);
textAlign(CENTER,CENTER);
fill(0);
stroke(0);
textSize(8);
cp5 = new ControlP5(this);
myKnobA = cp5.addKnob("Servo")
.setRange(1,120)
.setValue(1)
.setPosition(20,20)
.setRadius(30)
.setDragDirection(Knob.HORIZONTAL)
.setCaptionLabel("N")
.setColorCaptionLabel(0)
;
myKnobB = cp5.addKnob("TablaD")
.setRange(1,50)
.setValue(1)
.setPosition(20,120)
.setRadius(30)
.setDragDirection(Knob.HORIZONTAL)
.setCaptionLabel("Tabla de")
.setColorCaptionLabel(0)
;
//translate(height/2,width/2);
//line(0,0,radius*sin(radians(prueba)),radius*cos(radians(prueba)));
}
void draw(){
if(counter <= ncosas){
dibujar();
}
}
void Servo(int theValue){
background(255);
counter = 1;
ncosas = theValue;
sumangle = (2*PI)/ncosas;
angle = HALF_PI + PI + sumangle;
}
void TablaD(int theValue){
background(255);
counter = 1;
tablade = theValue;
angle = HALF_PI + PI + sumangle;
}
void dibujar(){
pushMatrix();
translate(width*2.5/4,height/2);
circle(radius*sin(angle),radius*cos(angle),2);
//if(counter*tablade<=ncosas){
line(radius*sin(angle),radius*cos(angle),radius*sin((counter*tablade*sumangle)+(angle-counter*sumangle)),radius*cos((counter*tablade*sumangle)+(angle-counter*sumangle)));
//}
println(counter*tablade + " -> " + counter*tablade*degrees(sumangle));
text(counter,(radius+sumar)*sin(angle),(radius+sumar)*cos(angle));
angle += sumangle;
counter++;
popMatrix();
}
void keyPressed(){
if (key == 'D' || key == 'd'){
Servo(int(myKnobA.getValue())+1);
myKnobA.setValue(int(myKnobA.getValue())+1);
}
if (key == 'A' || key == 'a'){
Servo(int(myKnobA.getValue())-1);
myKnobA.setValue(int(myKnobA.getValue())-1);
}
if (key == 'W' || key == 'w'){
TablaD(int(myKnobB.getValue())+1);
myKnobB.setValue(int(myKnobB.getValue())+1);
}
if (key == 'S' || key == 's'){
TablaD(int(myKnobB.getValue())-1);
myKnobB.setValue(int(myKnobB.getValue())-1);
}
}
Thank you in advance
To expand on what John Coleman said, you need to execute the dibujar() command multiple times in draw(). In Processing, the canvas is rendered at the end of the draw() loop, so if you draw multiple lines in draw(), they will all appear at the same time.
This will involve some kind of loop. If you want to draw the entire multiplication circle at once, you could replace if with while in the draw() loop:
void draw(){
while (counter <= ncosas){
dibujar();
}
}
I believe this will draw the entire multiplication circle in a single frame. You can then adjust the knobs to change the parameters of the multiplication circle, and the multiplication circle will change as you adjust the knobs.
Related
i have a task to make a pattern of circles and squares as described on photo, and i need to animate it so that all objects smoothly increase to four times the size and then shrink back to their original size and this is repeated. i tried but i cant understand problem
{
size(500,500);
background(#A5A3A3);
noFill();
rectMode(CENTER);
ellipseMode(CENTER);
}
void pattern(int a, int b)
{
boolean isShrinking = false;
for(int x = 0; x <= width; x += a){
for(int y = 0; y <= height; y += a){
stroke(#1B08FF);
ellipse(x,y,a,a);
stroke(#FF0000);
rect(x,y,a,a);
stroke(#0BFF00);
ellipse(x+25,y+25,a/2,a/2);
if (isShrinking){a -= b;}
else {a += b;}
if (a == 50 || a == 200){
isShrinking = !isShrinking ; }
}
}
}
void draw()
{
pattern(50,1);
}
this is what pattern need to look like
Great that you've posted your attempt.
From what you presented I can't understand the problem either. If this is an assignment, perhaps try to get more clarifications ?
If you comment you the isShrinking part of the code indeed you have an drawing similar to image you posted.
animate it so that all objects smoothly increase to four times the size and then shrink back to their original size and this is repeated
Does that simply mean scaling the whole pattern ?
If so, you can make use of the sine function (sin()) and the map() function to achieve that:
sin(), as the reference mentions, returns a value between -1 and 1 when you pass it an angle between 0 and 2 * PI (because in Processing trig. functions use radians not degrees for angles)
You can use frameCount divided by a fractional value to mimic an even increasing angle. (Even if you go around the circle multiple times (angle > 2 * PI), sin() will still return a value between -1 and 1)
map() takes a single value from one number range and maps it to another. (In your case from sin()'s result (-1,1) to the scale range (1,4)
Here's a tweaked version of your code with the above notes:
void setup()
{
size(500, 500, FX2D);
background(#A5A3A3);
noFill();
rectMode(CENTER);
ellipseMode(CENTER);
}
void pattern(int a)
{
for (int x = 0; x <= width; x += a) {
for (int y = 0; y <= height; y += a) {
stroke(#1B08FF);
ellipse(x, y, a, a);
stroke(#FF0000);
rect(x, y, a, a);
stroke(#0BFF00);
ellipse(x+25, y+25, a/2, a/2);
}
}
}
void draw()
{
// clear frame (previous drawings)
background(255);
// use the frame number as if it's an angle
float angleInRadians = frameCount * .01;
// map the sin of the frame based angle to the scale range
float sinAsScale = map(sin(angleInRadians), -1, 1, 1, 4);
// apply the scale
scale(sinAsScale);
// render the pattern (at current scale)
pattern(50);
}
(I've chosen the FX2D renderer because it's smoother in this case.
Additionally I advise in the future formatting the code. It makes it so much easier to read and it barely takes any effort (press Ctrl+T). On the long run you'll read code more than you'll write it, especially on large programs and heaving code that's easy to read will save you plenty of time and potentially headaches.)
I'm new to Processing and I need to make a program that, captured the main monitor, shows on the second screen the average color and makes a spiral using another color (perceptual dominant color) get by a function.
The problem is that the program is so slow (lag, 1FPS). I think it's because it has too many things to do everytime i do a screenshot, but I have no idea how to make it faster.
Also there could be many other problems, but the main one is that.
Thank you very much!
Here's the code:
import java.awt.Robot;
import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.color.ColorSpace;
PImage screenshot;
float a = 0;
int blockSize = 20;
int avg_c;
int per_c;
void setup() {
fullScreen(2); // 1920x1080
noStroke();
frame.removeNotify();
}
void draw() {
screenshot();
avg_c = extractColorFromImage(screenshot);
per_c = extractAverageColorFromImage(screenshot);
background(avg_c); // Average color
spiral();
}
void screenshot() {
try{
Robot robot_Screenshot = new Robot();
screenshot = new PImage(robot_Screenshot.createScreenCapture
(new Rectangle(0, 0, displayWidth, displayHeight)));
}
catch (AWTException e){ }
frame.setLocation(displayWidth/2, 0);
}
void spiral() {
fill (per_c);
for (int i = blockSize; i < width; i += blockSize*2)
{
ellipse(i, height/2+sin(a+i)*100, blockSize+cos(a+i)*5, blockSize+cos(a+i)*5);
a += 0.001;
}
}
color extractColorFromImage(PImage screenshot) { // Get average color
screenshot.loadPixels();
int r = 0, g = 0, b = 0;
for (int i = 0; i < screenshot.pixels.length; i++) {
color c = screenshot.pixels[i];
r += c>>16&0xFF;
g += c>>8&0xFF;
b += c&0xFF;
}
r /= screenshot.pixels.length; g /= screenshot.pixels.length; b /= screenshot.pixels.length;
return color(r, g, b);
}
color extractAverageColorFromImage(PImage screenshot) { // Get lab average color (perceptual)
float[] average = new float[3];
CIELab lab = new CIELab();
int numPixels = screenshot.pixels.length;
for (int i = 0; i < numPixels; i++) {
color rgb = screenshot.pixels[i];
float[] labValues = lab.fromRGB(new float[]{red(rgb),green(rgb),blue(rgb)});
average[0] += labValues[0];
average[1] += labValues[1];
average[2] += labValues[2];
}
average[0] /= numPixels;
average[1] /= numPixels;
average[2] /= numPixels;
float[] rgb = lab.toRGB(average);
return color(rgb[0] * 255,rgb[1] * 255, rgb[2] * 255);
}
public class CIELab extends ColorSpace {
#Override
public float[] fromCIEXYZ(float[] colorvalue) {
double l = f(colorvalue[1]);
double L = 116.0 * l - 16.0;
double a = 500.0 * (f(colorvalue[0]) - l);
double b = 200.0 * (l - f(colorvalue[2]));
return new float[] {(float) L, (float) a, (float) b};
}
#Override
public float[] fromRGB(float[] rgbvalue) {
float[] xyz = CIEXYZ.fromRGB(rgbvalue);
return fromCIEXYZ(xyz);
}
#Override
public float getMaxValue(int component) {
return 128f;
}
#Override
public float getMinValue(int component) {
return (component == 0)? 0f: -128f;
}
#Override
public String getName(int idx) {
return String.valueOf("Lab".charAt(idx));
}
#Override
public float[] toCIEXYZ(float[] colorvalue) {
double i = (colorvalue[0] + 16.0) * (1.0 / 116.0);
double X = fInv(i + colorvalue[1] * (1.0 / 500.0));
double Y = fInv(i);
double Z = fInv(i - colorvalue[2] * (1.0 / 200.0));
return new float[] {(float) X, (float) Y, (float) Z};
}
#Override
public float[] toRGB(float[] colorvalue) {
float[] xyz = toCIEXYZ(colorvalue);
return CIEXYZ.toRGB(xyz);
}
CIELab() {
super(ColorSpace.TYPE_Lab, 3);
}
private double f(double x) {
if (x > 216.0 / 24389.0) {
return Math.cbrt(x);
} else {
return (841.0 / 108.0) * x + N;
}
}
private double fInv(double x) {
if (x > 6.0 / 29.0) {
return x*x*x;
} else {
return (108.0 / 841.0) * (x - N);
}
}
private final ColorSpace CIEXYZ =
ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
private final double N = 4.0 / 29.0;
}
There's lots that can be done, even beyond what's already been mentioned.
Iteration & Threading
After taking the screenshot, immediately iterate over every 1/N pixels (perhaps every 4 or 8) of the buffered image. Then, during this iteration, calculate the LAB value for each pixel (as you have each pixel channel directly available), and meanwhile increment the running total of each RGB channel.
This saves us from iterating over the same pixels twice and avoids unncessary conversions (BufferedImage → PImage; and composing then decomposing pixel channels from PImage pixels).
Likewise, we avoid Processing's expensive resize() call (as suggested in another answer), which is not something we want to call every frame (even though it does speed the program up, it's not an efficient method).
Now, on top of iteration change, we can wrap the iteration in a Callable to easily run the workload across multiple system threads concurrently (after all, pixel iteration is embarrassingly parallel); the example below does this with 2 threads, each screenshotting and processing half of the display's pixels.
Optimise RGB→XYZ→LAB conversion
We're not so concerned about the backwards conversion since that's only done for one value per frame
It looks like you've implemented XYZ→LAB yourself and are using the RGB→XYZ converter from java.awt.color.
As has been identified, the forward conversion XYZ→LAB uses a cbrt() which is as a bottleneck. I also imagine that the RGB→XYZ implementation makes 3 calls to Math.Pow(x, 2.4) — 3 non-integer exponents per pixel adds considerably to the computation. The solution is faster math...
Jafama
Jafama is a drop-in java.math replacement -- simply import the library and replace any Math.__() calls with FastMath.__() for a free speedup (you could go even further by trading Jafama's E-15 precision with less accurate and even faster dedicated LUT-based classes).
So at the very least, swap out Math.cbrt() for FastMath.cbrt(). Then consider implementing RGB→XYZ yourself (example), again using Jafama in place of java.math.
You may even find that for such a project, converting to XYZ only is a sufficient color space to work with to overcome the well known weaknesses with RGB (and therefore save yourself from the XYZ→LAB conversion).
Cache LAB Calculation
Unless most pixels are changing every frame, then consider caching the LAB value for every pixel, recalculating it only when the pixel has changed between the current the previous frames. The tradeoff here is the overhead from checking every pixel against its previous value, versus how much calculation positive checks will save. Given that the LAB calculation is much more expensive it's very worthwhile here. The example below uses this technique.
Screen Capture
No matter how well optimised the rest of the program is, a considerable bottleneck is the AWT Robot's createScreenCapture(). It will struggles to go past 30FPS on large enough displays. I can't offer any exact advice but it's worth looking at other screen capture methods in Java.
Reworked code with iteration changes & threading
This code implements what has discussed above minus any changes to the LAB calculation.
float a = 0;
int blockSize = 20;
int avg_c;
int per_c;
java.util.concurrent.ExecutorService threadPool = java.util.concurrent.Executors.newFixedThreadPool(4);
List<java.util.concurrent.Callable<Boolean>> taskList;
float[] averageLAB;
int totalR = 0, totalG = 0, totalB = 0;
CIELab lab = new CIELab();
final int pixelStride = 8; // look at every 8th pixel
void setup() {
size(800, 800, FX2D);
noStroke();
frame.removeNotify();
taskList = new ArrayList<java.util.concurrent.Callable<Boolean>>();
Compute thread1 = new Compute(0, 0, width, height/2);
Compute thread2 = new Compute(0, height/2, width, height/2);
taskList.add(thread1);
taskList.add(thread2);
}
void draw() {
totalR = 0; // re init
totalG = 0; // re init
totalB = 0; // re init
averageLAB = new float[3]; // re init
final int numPixels = (width*height)/pixelStride;
try {
threadPool.invokeAll(taskList); // run threads now and block until completion of all
}
catch (Exception e) {
e.printStackTrace();
}
// calculate average LAB
averageLAB[0]/=numPixels;
averageLAB[1]/=numPixels;
averageLAB[2]/=numPixels;
final float[] rgb = lab.toRGB(averageLAB);
per_c = color(rgb[0] * 255, rgb[1] * 255, rgb[2] * 255);
// calculate average RGB
totalR/=numPixels;
totalG/=numPixels;
totalB/=numPixels;
avg_c = color(totalR, totalG, totalB);
background(avg_c); // Average color
spiral();
fill(255, 0, 0);
text(frameRate, 10, 20);
}
class Compute implements java.util.concurrent.Callable<Boolean> {
private final Rectangle screenRegion;
private Robot robot_Screenshot;
private final int[] previousRGB;
private float[][] previousLAB;
Compute(int x, int y, int w, int h) {
screenRegion = new Rectangle(x, y, w, h);
previousRGB = new int[w*h];
previousLAB = new float[w*h][3];
try {
robot_Screenshot = new Robot();
}
catch (AWTException e1) {
e1.printStackTrace();
}
}
#Override
public Boolean call() {
BufferedImage rawScreenshot = robot_Screenshot.createScreenCapture(screenRegion);
int[] ssPixels = new int[rawScreenshot.getWidth()*rawScreenshot.getHeight()]; // screenshot pixels
rawScreenshot.getRGB(0, 0, rawScreenshot.getWidth(), rawScreenshot.getHeight(), ssPixels, 0, rawScreenshot.getWidth()); // copy buffer to int[] array
for (int pixel = 0; pixel < ssPixels.length; pixel+=pixelStride) {
// get invididual colour channels
final int pixelColor = ssPixels[pixel];
final int R = pixelColor >> 16 & 0xFF;
final int G = pixelColor >> 8 & 0xFF;
final int B = pixelColor & 0xFF;
if (pixelColor != previousRGB[pixel]) { // if pixel has changed recalculate LAB value
float[] labValues = lab.fromRGB(new float[]{R/255f, G/255f, B/255f}); // note that I've fixed this; beforehand you were missing the /255, so it was always white.
previousLAB[pixel] = labValues;
}
averageLAB[0] += previousLAB[pixel][0];
averageLAB[1] += previousLAB[pixel][1];
averageLAB[2] += previousLAB[pixel][2];
totalR+=R;
totalG+=G;
totalB+=B;
previousRGB[pixel] = pixelColor; // cache last result
}
return true;
}
}
800x800px; pixelStride = 4; fairly static screen background
Yeesh, about 1 FPS on my machine:
To optimize code can be really hard, so instead of reading everything looking for stuff to improve, I started by testing where you were losing so much processing power. The answer was at this line:
per_c = extractAverageColorFromImage(screenshot);
The extractAverageColorFromImage method is well written, but it underestimate the amount of work it has to do. There is a quadratic relationship between the size of a screen and the number of pixels in this screen, so the bigger the screen the worst the situation. And this method is processing every pixel of the screenshot all the time, several time per screenshot.
This is a lot of work for an average color. Now, if there was a way to cut some corners... maybe a smaller screen, or a smaller screenshot... oh! there is! Let's resize the screenshot. After all, we don't need to go into such details as individual pixels for an average. In the screenshot method, add this line:
void screenshot() {
try {
Robot robot_Screenshot = new Robot();
screenshot = new PImage(robot_Screenshot.createScreenCapture(new Rectangle(0, 0, displayWidth, displayHeight)));
// ADD THE NEXT LINE
screenshot.resize(width/4, height/4);
}
catch (AWTException e) {
}
frame.setLocation(displayWidth/2, 0);
}
I divided the workload by 4, but I encourage you to tweak this number until you have the fastest satisfying result you can. This is just a proof of concept:
As you can see, resizing the screenshot and making it 4x smaller gives me 10x more speed. That's not a miracle, but it's much better, and I can't see a difference in the end result - but about that part, you'll have to use your own judgement, as you are the one who knows what your project is about. Hope it'll help!
Have fun!
Unfortunately I can't provide a detailed answer like laancelot (+1), but hopefully I can provide a few tips:
Resizing the image is definitely a good direction. Bare in mind you can also skip a number of pixels instead of incrementing every single pixel. (if you handle the pixel indices correctly, you can get a similar effect to resize without calling resize, though that won't save you a lot CPU time)
Don't create a new Robot instance multiple times a second. Create it once in setup and re-use it. (This is more of a good habit to get into)
Use a CPU profiler, such as the one in VisualVM to see what exactly is slow and aim to optimise the slowest stuff first.
point 1 example:
for (int i = 0; i < numPixels; i+= 100)
point 2 example:
Robot robot_Screenshot;
...
void setup() {
fullScreen(2); // 1920x1080
noStroke();
frame.removeNotify();
try{
robot_Screenshot = new Robot();
}catch(AWTException e){
println("error setting up screenshot Robot instance");
e.printStackTrace();
}
}
...
void screenshot() {
screenshot = new PImage(robot_Screenshot.createScreenCapture
(new Rectangle(0, 0, displayWidth, displayHeight)));
frame.setLocation(displayWidth/2, 0);
}
point 3 example:
Notice the slowest bit are actually AWT's fromRGB and Math.cbrt()
I'd suggest finding another alternative RGB -> XYZ -> L*a*b* conversion method that is simpler (mainly functions, less classes, with AWT or other dependencies) and hopefully faster.
So I made the Sierpinski carpet fractal in processing using a Square data type which draw a square and has a function generate() that generates 9 equal squares out of itself and returns an ArrayList of (9-1)=8 squares removing the middle one (it is not added to the returned ArrayList) in order to generate the Sierpinski carpet.
Here is the class Square -
class Square {
PVector pos;
float r;
Square(float x, float y, float r) {
pos = new PVector(x, y);
this.r = r;
}
void display() {
noStroke();
fill(120,80,220);
rect(pos.x, pos.y, r, r);
}
ArrayList<Square> generate() {
ArrayList<Square> rects = new ArrayList<Square>();
float newR = r/3;
for (int i=0; i<3; i++) {
for (int j=0; j<3; j++) {
if (!(i==1 && j==1)) {
Square sq = new Square(pos.x+i*newR, pos.y+j*newR, newR);
rects.add(sq);
}
}
}
return rects;
}
}
This is the main sketch which moves forward the generation on mouse click -
ArrayList<Square> current;
void setup() {
size(600, 600);
current = new ArrayList<Square>();
current.add(new Square(0, 0, width));
}
void draw() {
background(255);
for (Square sq : current) {
sq.display();
}
}
void mousePressed() {
ArrayList<Square> next = new ArrayList<Square>();
for(Square sq: current) {
ArrayList<Square> rects = sq.generate();
next.addAll(rects);
}
current = next;
}
The problem :
The output that I am getting has very thin white lines which are not supposed to be there :
First generation -
Second generation -
Third generation -
My guess is that these lines are just the white background that shows up due to the calculations in generate() being off by a pixel or two. However I am not sure about how to get rid of these. Any help would be appreciated!
Here's a smaller example that demonstrates your problem:
size(1000, 100);
noStroke();
background(0);
float squareWidth = 9.9;
for(float squareX = 0; squareX < width; squareX += squareWidth){
rect(squareX, 0, squareWidth, height);
}
Notice that the black background is showing through the squares. Please try to post this kind of minimal example instead of your whole sketch in the future.
Anyway, there are three ways to fix this:
Option 1: Call the noSmooth() function.
By default, Processing uses anti-aliasing to make your drawings look smoother. Usually this is a good thing, but it can also add some fuzziness to the edges of shapes. If you disable anti-aliasing, your shapes will be more clear and you won't see the artifacts.
Option 2: Use a stroke with the same color as the fill.
As you've already discovered, this draws an outline around the shape.
Option 3: Use int values instead of float values.
You're storing your coordinates and sizes in float values, which can contain decimal places. The problem is, the screen (the actual pixels on your monitor) don't have decimal places (there is no such thing as half a pixel), so they're represented by int values. So when you convert a float value to an int, the decimal part is dropped, which can cause small gaps in your shapes.
If you just switch to using int values, the problem goes away:
size(1000, 100);
noStroke();
background(0);
int squareWidth = 10;
for(int squareX = 0; squareX < width; squareX += squareWidth){
rect(squareX, 0, squareWidth, height);
}
Dealing with collisions in Processing is straightforward. However, how do I identify collisions with trails?
Example: imagine the light cycles in Tron, if you wish, with the alteration that trails of derezzed light cycles do not disappear. In Tron, if a cycle intersects any point another cycle, itself included, has ever been, it 'dies'. How do I efficiently find this event in Processing?
One hacky workaround is to draw the line into a PImage and check if the colour at a location is the same as the background or not (e.g. a pre-existing line, hence a collision).
Here's a rough (and slightly inefficient (due to get()/set() calls) proof of concept:
PImage buffer;
//how mutch we scale down = how pixely this will look
int multiplier = 8;
//scaled down width/height
int w,h;
//cursor position
int px,py;
//cursor velocity;
int vx,vy;
void setup(){
size(400,400);
noSmooth();
w = width / multiplier;
h = height / multiplier;
buffer = createImage(w,h,RGB);
clear();
}
void clear(){
java.util.Arrays.fill(buffer.pixels,color(0));
buffer.updatePixels();
}
void draw(){
//update cursor
px += vx;
py += vy;
//check edges
if(px < 0){
px = w-1;
}
if(px > w){
px = 0;
}
if(py < 0){
py = h-1;
}
if(py > h){
py = 0;
}
//check collision
if(keyPressed){
if(keyCode == UP || keyCode == DOWN || keyCode == LEFT || keyCode == RIGHT){
checkSelfIntersection();
}
}
//paint cursor
buffer.set(px,py,color(0,192,0));
//render on screen
image(buffer,0,0,width,height);
}
void checkSelfIntersection(){
//if the pixel ahead is not the same colour as the background
if(buffer.get(px+vx,py+vy) > color(0)){
clear();
println("Cycle go BOOM!");
}
}
void keyPressed(){
if(keyCode == UP){
vy = -1;
}
if(keyCode == DOWN){
vy = +1;
}
if(keyCode == LEFT){
vx = -1;
}
if(keyCode == RIGHT){
vx = +1;
}
}
void keyReleased(){
vx = vy = 0;
}
A similar concept can be done be keeping track of points in a list and checking if a new point is already part of this list (collision) or not:
ArrayList<PVector> path = new ArrayList<PVector>();
//cursor position
int px,py;
//cursor velocity;
int vx,vy;
void setup(){
size(400,400);
noFill();
strokeWeight(10);
}
void draw(){
//update cursor
px += vx;
py += vy;
//check edges
if(px < 0){
px = 0;
}
if(px > width){
px = width;
}
if(py < 0){
py = 0;
}
if(py > height){
py = height;
}
//check collision
if(keyPressed){
if(keyCode == UP || keyCode == DOWN || keyCode == LEFT || keyCode == RIGHT){
checkSelfIntersection();
}
}
background(255);
beginShape();
for(int i = 0 ; i < path.size(); i++){
PVector p = path.get(i);
vertex(p.x,p.y);
}
endShape();
}
void checkSelfIntersection(){
PVector cursor = new PVector(px,py);
if(path.contains(cursor)){
path.clear();
println("Cycle go BOOM!");
}else{
path.add(cursor);
}
}
void keyPressed(){
if(keyCode == UP){
vy = -5;
}
if(keyCode == DOWN){
vy = +5;
}
if(keyCode == LEFT){
vx = -5;
}
if(keyCode == RIGHT){
vx = +5;
}
}
void keyReleased(){
vx = vy = 0;
}
The concept isn't that different from how games like Snake/Volfied/etc. check for self intersections.
Note I'm cheating a bit by updating a "cursor" on keys with a small velocity: this avoid gaps in the lines. If you try to replace with the mouse, you'll notice the collision check may fail if the mouse moves fast as it checks one point against a list of recorded points. An alternative might be to split the list of points into pairs of lines and check if the new point intersects any of them.
You might want to also check this similar question
Stack Overflow isn't really designed for general "how do I do this" type questions. It's for specific "I tried X, expected Y, but got Z instead" type questions. That being said, I'll try to help in a general sense.
You can probably just keep track of the lines formed by the cycles, in the form of an ArrayList of all of the points where a player turned. Then at each step, you can check whether the player is intersecting with any of those lines.
More specifically, you'd probably want to form another line between the previous player coordinate and the next player coordinate. Then check whether that line intersects with any of the other lines using formulas that I'm sure you can find through a google search or two.
You probably don't have to do anything smarter than that unless you're talking about very very large playing fields (as in, millions of lines). So it's a little early to ask about efficiency.
There are of course many other ways to approach the problem. You might also use a 2D array that keeps track of the trails, or you could use pixel-based collision, or probably any number of other solutions. The point is you need to try something and post a MCVE along with a specific question if you get stuck, and we'll go from there. Good luck.
I'm attempting to create a very simple "whack-a-mole" type game, designed for students new to p5.js, and Processing in general.
Currently, I've created an array, and a random search within that array that randomly picks a square and makes it brown (a "mole", for now).
How do I make it so that after selecting a square, it stays there for a couple seconds, and then jumps to the next one, using basic p5?
I've managed to implement noLoop(), which can stop the search, but after a certain time I want it to resume.
Here's the code I have so far:
function setup() {
createCanvas(610,610)
}
function draw() {
var grid = []
for (var x = 0; x < 6; x += 1){
grid[x]=0;
for (var y = 0; y < 6; y += 1){
rand=round(random(360))
grid[x][y]=0
if (rand==0){
grid[x]=1
grid[y]=1
noLoop()
}
if (grid[x]==0 || grid[y]==0){
fill(76,153,0)
rect((x*100+10),(y*100+10),90,90)
}
if (grid[x]>0 && grid[y]>0){
fill(102,51,0)
rect((x*100+10),(y*100+10),90,90)
}
}
}
}
Instead of using noLoop(), you could keep looping at 60 frames per second, but then use the millis() function to keep track of the elapsed time.
Here's an example that shows a circle for 1 second whenever the user clicks:
var clickTime;
function mousePressed(){
clickTime = millis();
}
function draw() {
background(0);
if(millis() < clickTime + 1000){
ellipse(width/2, height/2, width/4, height/4);
}
}
Edit: Another approach is to use the % operator along with the frameCount variable in order to do something every X frames. This examples draws a circle in a random position every 60 frames:
function draw() {
if (frameCount % 60 == 0) {
background(0);
ellipse(random(width), random(height), width / 4, height / 4);
}
}