p5.js draw a rectangle at cursor position on mouse click - p5.js

I have a very simple 9x9 grid. I know how to handle the rectangles on this grid. What I want is when I click on one of the grid rectangles, this rectangle should now be marked with a fill or border around it.
I can draw a rectangle, with a blue fill at exact the correct rectangle on the grid.
But with the next click on the grid, the new rectangle is drawn but the old rectangle is still there and will stay there. And so on.
My question is now, how can I paint always exact on rectangle at the click position?
Is maybe a class the right way?
Creating every time a new rectangle and destroy the old one?
var nullx = 140;
var nully = 50;
var breite = 65;
var hoehe = 65;
var pressed = false;
function setup() {
createCanvas(800, 1200);
}
function draw() {
//background(220);
noFill();
//strokeWeight(2);
sudokulines();
numberstorect();
if(pressed == true){
sqMark();
}
if (mousePressed == true){
console.log("click...")
sqMark();
}
}
function mousePressed(){
pressed = true;
}
function sqMark(){
var tempX;
var tempY;
//console.log(tempX,tempY);
tempX = (floor((mouseX - nullx) / breite) * breite) + nullx;
tempY = (floor((mouseY - nully) / hoehe) * hoehe) + nully;
console.log(tempX,tempY);
if (tempX > 139 && tempY > 49 ){
if (tempX < 661 && tempY < 571){
strokeWeight(0.7);
fill(0,0,255);
rect(tempX+2,tempY+2,breite-4,hoehe-4);
pressed = false;
}
}
}
function sudokulines(){
//The vertical lines
for (var i = 1; i <= 8; i++){
if (i == 3 || i == 6){
strokeWeight(3);
}else{
strokeWeight(1);
}
line(nullx + breite * i, nully, nullx + breite * i, nully+ 9*hoehe);
}
//The horizontal lines
for (var i = 1; i <= 8; i++){
if (i == 3 || i == 6){
strokeWeight(3);
}else{
strokeWeight(1);
}
//The big rectangle around
line(nullx , nully + hoehe * i, nullx + 9*breite, nully + hoehe * i);
}
strokeWeight(3);
rect(nullx,nully,9*breite,9*hoehe);
}
function numberstorect(){
textAlign(CENTER,CENTER);
fill(0);
textSize(breite/1.3);
text(2,nullx + breite/2, nully + hoehe/2);
}

It's important to understand that p5.js draws in immediate mode, so each element, once drawn, is not removable unless intentionally drawn over or cleared. This simplest solution to your specific problem is to simply save to location to be highlighted when the user presses the mouse, and then always clear the canvas and redraw everything (including the selected square) in the draw function. Because there's no animation, you can optimize this by disabling looping in your setup function, and then explicitly calling redraw when something happens that effects the display (i.e. the mouse is pressed).
var nullx = 140;
var nully = 50;
var breite = 65;
var hoehe = 65;
let selectedX = -1;
let selectedY = -1;
function setup() {
createCanvas(800, 1200);
// By default don't re draw every frame
noLoop();
}
function draw() {
background(255);
noFill();
//strokeWeight(2);
sudokulines();
numberstorect();
sqMark();
}
function mousePressed() {
// Set the selection
selectedX = (floor((mouseX - nullx) / breite) * breite) + nullx;
selectedY = (floor((mouseY - nully) / hoehe) * hoehe) + nully;
// Only redraw when something changes.
redraw();
}
function sqMark() {
if (selectedX > 139 && selectedY > 49) {
if (selectedX < 661 && selectedY < 571) {
strokeWeight(0.7);
fill(0, 0, 255);
rect(selectedX + 2, selectedY + 2, breite - 4, hoehe - 4);
pressed = false;
}
}
}
function sudokulines() {
//The vertical lines
for (var i = 1; i <= 8; i++) {
if (i == 3 || i == 6) {
strokeWeight(3);
} else {
strokeWeight(1);
}
line(nullx + breite * i, nully, nullx + breite * i, nully + 9 * hoehe);
}
//The horizontal lines
for (var i = 1; i <= 8; i++) {
if (i == 3 || i == 6) {
strokeWeight(3);
} else {
strokeWeight(1);
}
//The big rectangle around
line(nullx, nully + hoehe * i, nullx + 9 * breite, nully + hoehe * i);
}
strokeWeight(3);
rect(nullx, nully, 9 * breite, 9 * hoehe);
}
function numberstorect() {
textAlign(CENTER, CENTER);
fill(0);
textSize(breite / 1.3);
text(2, nullx + breite / 2, nully + hoehe / 2);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

Related

moving a sketch from draw to setup

Im playing around with the prime spiral code from Dan Shiffman:
https://www.youtube.com/watch?v=a35KWEjRvc0&t=716s&ab_channel=TheCodingTrain
I need to use this code as a background to another sketch, but due to the moving nature of the spiral its impossible that the spiral will not draw ontop of the other sketch, even with p5.layers.
I am therefore searching for a way to refactor this code into something that draw all the figures at once so use it as a background.
My idea is to create some kind of for loop in setup, but i am afraid its to big of nut to crack for me.
let x, y;
let step = 1;
let stepSize = 20;
let numSteps = 1;
let state = 0;
let turnCounter = 1;
let totalSteps;
let offset = 0;
function isPrime(value) {
if (value == 1) return false;
for (let i = 2; i <= sqrt(value); i++) {
if (value % i == 0) {
return false;
}
}
return true;
}
function setup() {
createCanvas(500, 500);
const cols = width / stepSize;
const rows = height / stepSize;
totalSteps = cols * rows;
x = width / 2;
y = height / 2;
background(0);
}
function draw() {
primeSpiral(20, 1)
primeSpiral(30, 200)
incrementStep();
noStroke();
}
function incrementStep()
{
switch (state) {
case 0:
x += stepSize;
break;
case 1:
y -= stepSize;
break;
case 2:
x -= stepSize;
break;
case 3:
y += stepSize;
break;
}
if (step % numSteps == 0) {
state = (state + 1) % 4;
turnCounter++;
if (turnCounter % 2 == 0) {
numSteps++;
}
}
step++;
if (step > totalSteps) {
noLoop();
}
}
function primeSpiral(offset, color){
if (!isPrime(step+offset)) {
//might put something here
} else {
let r = stepSize * 0.5;
fill(color, 99, 164);
push();
translate(x, y);
rotate(-PI / 4);
triangle(-r, +r, 0, -r, +r, +r);
pop();
}
}
You can move the code from draw() to the loop in setup(), here is example:
...
function setup() {
createCanvas(500, 500);
const cols = width / stepSize;
const rows = height / stepSize;
totalSteps = cols * rows;
x = width / 2;
y = height / 2;
background(0);
for (let i = 0; i<100; i++) { // 100 is the number of processed frames, you can change it as you need
primeSpiral(20, 1)
primeSpiral(30, 200)
incrementStep();
noStroke();
}
}
function draw() { // now draw is empty and you can place new code here
}
...

p5js sketch slows down over time

I'm new to p5js and I'm trying to plot incoming x y data by drawing circles which initially expand and then shrink becoming 'fixed' , but the speed at which the circles expand slows down over time - and I can't work out why. I have one array which stores the animated expanding / shrinking circle and another which stores the final state of the fixed circle.
let circles = [];
let circlesStatic = [];
function setup() {
createCanvas(640, 480);
frameRate(60);
smooth();
// e1 = new Ellipse(320,240);
// e2 = new Ellipse(20,20);
background(0);
}
function mouseClicked() {
let e = new Ellipse(mouseX, mouseY);
circles.push(e);
}
function draw() {
for (let i = 0; i < circles.length; i++) {
circles[i].render();
}
for (let i = 0; i < circlesStatic.length; i += 2) {
// noFill();
fill('rgba(50,50,50, 0.1)');
strokeWeight(1);
stroke('rgba(100,100,100, 0.25)');
circle(circlesStatic[i], circlesStatic[i + 1], 20);
}
}
class Ellipse {
// constructor
constructor(x, y) {
this.x = x; // copy x argument value to the instance (this) x property
this.y = y; // copy x argument value to the instance (this) x property
// current size - continuously updated
this.size = 10;
// minimum size
this.minSize = 10;
// maximum size
this.maxSize = random(30, 35);
// change speed for size (how much will the size increase/decrease each frame)
this.sizeSpeed = 3;
// internal frameCount replacement
this.tick = 0;
// this.fill=(255,0,0);
}
render() {
// if the size is either too small, or too big, flip the size speed sign (if it was positive (growing) - make it negative (shrink) - and vice versa)
if (this.size < this.minSize || this.size > this.maxSize) {
this.sizeSpeed *= -1;
}
// increment the size with the size speed (be it positive or negative)
this.size += this.sizeSpeed;
console.log(this.sizeSpeed);
if (this.size < this.minSize) {
this.sizeSpeed = 0;
circlesStatic.push(this.x);
circlesStatic.push(this.y);
}
background(0);
// noStroke();
fill(200, 50, 0);
ellipse(this.x, this.y, this.size, this.size);
// this.tick++;
// this.size = map(sin(this.tick * this.sizeSpeed),-1.0,1.0,this.minSize,this.maxSize);
// fill(random(255), random(255), random(255));
// ellipse(this.x,this.y, this.size,this.size);
}
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>
The problem is that every time you render a circle it checks if it should become "static," and if it should it adds new values to circlesStatic. However, there is nothing to stop the animating circle to continue being rendered on subsequent frames. This results in massive numbers of entries being added to circlesStatic which means longer and longer render times. I've implemented one possible fix below. I also made it so that if you hold the shift key down you will see the previous behavior (watch the length of circlesStatic skyrocket after a few clicks). As soon as you release the shift key the fix will run and the number of entries in circlesStatic will plateau.
let circles = [];
let circlesStatic = [];
function setup() {
createCanvas(640, 480);
frameRate(60);
smooth();
// e1 = new Ellipse(320,240);
// e2 = new Ellipse(20,20);
textSize(16);
}
function mouseClicked() {
let e = new Ellipse(mouseX, mouseY);
circles.push(e);
}
function draw() {
background(0);
push();
noStroke();
fill('red')
text(`circles: ${circles.length}; circlesStatic: ${circlesStatic.length}`, 20, 20);
pop();
for (let i = 0; i < circles.length; i++) {
circles[i].render();
if (!keyIsDown(SHIFT)) {
if (circles[i].isStatic()) {
circles.splice(i, 1);
i--;
}
}
}
for (let i = 0; i < circlesStatic.length; i += 2) {
// noFill();
fill('rgba(50,50,50, 1)');
strokeWeight(1);
stroke('rgba(100,100,100, 1)');
circle(circlesStatic[i], circlesStatic[i + 1], 20);
}
}
class Ellipse {
// constructor
constructor(x, y) {
this.x = x; // copy x argument value to the instance (this) x property
this.y = y; // copy x argument value to the instance (this) x property
// current size - continuously updated
this.size = 10;
// minimum size
this.minSize = 10;
// maximum size
this.maxSize = random(30, 35);
// change speed for size (how much will the size increase/decrease each frame)
this.sizeSpeed = 3;
// internal frameCount replacement
this.tick = 0;
// this.fill=(255,0,0);
}
isStatic() {
return this.sizeSpeed === 0;
}
render() {
// if the size is either too small, or too big, flip the size speed sign (if it was positive (growing) - make it negative (shrink) - and vice versa)
if (this.size < this.minSize || this.size > this.maxSize) {
this.sizeSpeed *= -1;
}
// increment the size with the size speed (be it positive or negative)
this.size += this.sizeSpeed;
console.log(this.sizeSpeed);
if (this.size < this.minSize) {
this.sizeSpeed = 0;
circlesStatic.push(this.x);
circlesStatic.push(this.y);
}
// background(0);
// noStroke();
fill(200, 50, 0);
ellipse(this.x, this.y, this.size, this.size);
// this.tick++;
// this.size = map(sin(this.tick * this.sizeSpeed),-1.0,1.0,this.minSize,this.maxSize);
// fill(random(255), random(255), random(255));
// ellipse(this.x,this.y, this.size,this.size);
}
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>

P5.js Creating a fading curve on a grid

I've made a square grid on top of the canvas and also a cruve (that is meant to have a fading trail). I made them seperately and tried combining them so the curve would appear on top of the grid. However, it doesn't show the curve.
I've commented out the grid so it's easier to see the curve.
How do I get this to work?
var cols = 10;
var rows = 10;
var t = 0;
var particleArray = [];
function setup() {
createCanvas(600, 600);
background(0);
fill(100);
rect(0, 0, 550, 550, 25);
}
// blue grid
function draw() {
/*for (var c = 0; c < cols; c++) {
for (var r = 0; r < rows; r++) {
var XO = 25 + c * 50;
var YO = 25 + r * 50;
stroke(0);
fill(100,149,237);
rect(XO, YO, 50, 50);
noLoop();
// :(
}
}
*/
//curve
y = width / 2 + 270 * sin(3 * t + PI / 2) - 25;
x = height / 2 + 270 * sin(1 * t) - 25;
particleArray.push(new Particle(x, y, t));
for (i=0; i<particleArray.length; i++) {
particleArray[i].show(t);
}
if (particleArray.length > 700) {
particleArray.shift();
}
t += .01;
}
function Particle(x, y, t) {
this.x = x;
this.y = y;
this.t = t;
this.show = function(currentT) {
var _ratio = t / currentT;
_alpha = map(_ratio, 0, 1, 0, 255); //points will fade out as time elaps
fill(255, 255, 255, _alpha);
ellipse(x, y, 5, 5);
}
}
I don't know if this was intentional but you called noLoop() function where you're drawing the grid. If you comment that out it works.

p5.js Add a dissapearing ellipse trail to Lissajous curve line

I have a simple code that traces the Liss cruve with a small ellipse. I was wondering how to add a fading trail to this shape so it represents the cruve more clearly. I only know a bit about adding trails that follows the mouse but I'm not sure how to do this one.
Any help is appreciated, here is the code:
var t = 0;
function setup() {
createCanvas(500, 500);
fill(255);
}
function draw() {
background(0);
for (i = 0; i < 1; i++) {
y = 160*sin(3*t+PI/2);
x = 160*sin(1*t);
fill(255);
ellipse(width/2+x, height/2+y, 5, 5);
t += .01;
}
}
Try changing background(0) to background(0, 0, 0, 4) :)
Here is a working example:
https://editor.p5js.org/chen-ni/sketches/I-FbLFDXi
Edit:
Here is another solution that doesn't use the background trick:
https://editor.p5js.org/chen-ni/sketches/HiT4Ycd5U
Basically, it keeps track of each point's position and redraws them in every frame with updated alpha to create the "fading out" effect.
var t = 0;
var particleArray = [];
function setup() {
createCanvas(500, 500);
}
function draw() {
background(0);
y = width / 2 + 160 * sin(3 * t + PI / 2);
x = height / 2 + 160 * sin(1 * t);
particleArray.push(new Particle(x, y, t));
for (i=0; i<particleArray.length; i++) {
particleArray[i].show(t);
}
//keep the array short, otherwise it runs very slow
if (particleArray.length > 800) {
particleArray.shift();
}
t += .01;
}
function Particle(x, y, t) {
this.x = x;
this.y = y;
this.t = t;
this.show = function(currentT) {
var _ratio = t / currentT;
_alpha = map(_ratio, 0, 1, 0, 255); //points will fade out as time elaps
fill(255, 255, 255, _alpha);
ellipse(x, y, 5, 5);
}
}

"Interactive overlay" in p5.js

I would like to do the following in p5.js. Say I have a canvas roughly like this:
Let's say I have given those red squares some interactivity using the function mouseClicked() and their respective coordinates in the canvas (as in, if I click on a square, change its color).
Now I'd like to use that blue "i"-button to display some sort of info box, and it should look approximately like this:
I want that "info dialog" to go away if the user clicks on that "OK-button" (whcih is not really a button, but also just a square in a p5 canvas).
Question: Is there an elegant way of deactivating the "square interactivity" and activating that "OK button interactivity" and to have the interactivity the other way around whenever the info box is not being displayed?
The only way I can think of to achieve this goes like this:
function mouseClicked(){
if(infoBoxIsBeingDisplayed){
do something
}else{
do something else
}
}
However, this seems a little convoluted.
I'd appreciate any suggestions on how to do this better.
Your solution seems fine, and it also seems much less "convoluted" than any of the other options. Keep it simple.
You might be able to clean up your code by splitting up your logic into smaller utility functions. Something like this:
function mouseClicked(){
if(infoBoxIsBeingDisplayed){
mouseClickedInfoBox();
}else{
mouseClickedSquaresCanvas();
}
}
Then your logic would be in those utility functions, specific to each screen. You could further split it up to generalize your bounds-checking, but the idea is the same.
I've run into a similar problem to this before, the best way I came up with to solve this (which is a fairly patchy way to do it) is to move the squares outside of the canvas. sorry my code is probably really messy but look at the function game and you will see what i did
If you run the game make it full screen.
example:(to play the game use A and D or use the arrow keys but i suggest A and D because the way the code snippet is set up the game doesn't fit on the page and the page moves around when you press the arrow keys)
var bs = [];
var speed;
var ship1;
var num = 40;
var d;
var gscore = 0;
var highscore = 0;
var cap = 0;
function setup() {
createCanvas(windowWidth,windowHeight- 4);
for(var i = 0; i < num; i++) {
bs[i] = new Box(random(0, width), random(-600,-30));
}
ship1 = new ship();
button1 = new button();
}
function draw() {
background(0);
if(cap == 0){
gscore = gscore + 0.1;
}
if(highscore < gscore){
highscore = gscore;
}
speed = map(gscore,4,100,4,5);
ship1.show();
ship1.update();
for(var i = 0; i < num; i++) {
bs[i].update();
bs[i].show();
if(bs[i].y >= height){
bs[i].x = random(0, width);
bs[i].y = random(-600,-30);
}
for(var j = 0; j < num; j++) {
if(bs[i].touch(bs[j])){
if(bs[i] != bs[j]){
bs[j].x = random(0, width);
bs[j].y = random(-600,-30);
}
}
}
if(bs[i].touch(ship1)){
game();
}
}
push();
fill(255,0,0);
textSize(36);
text("score: "+ floor(gscore),0,36);
text("highscore: "+floor(highscore),0,72);
pop();
}
function Box(x, y) {
this.x = x;
this.y = y;
this.show = function() {
fill(255);
noStroke();
rect(this.x, this.y, 30, 30);
}
this.update = function() {
this.y = this.y + speed;
}
this.touch = function(other){
d = dist(this.x, this.y, other.x, other.y);
if(d < 15/*half of the squares*/+15/*the total area of the ship*/){
return true;
}else {
return false;
}
}
}
function game(){//look here, game is the end game screen
for(var i = 0; i < num; i++) {
bs[i].x = -200;//making all squares x value -200
bs[i].y = -200;//making all squares y value -200
}
ship1.x = -200;//making ship x value -200
ship1.y = -200;//making ship y value -200
cap = 1;//cap is a variable made to stop the score from increasing when the end game screen is shown its "capping" the score
push();
fill(255,0,0);
textAlign(CENTER);
textSize(64);
text("You lose", width/2, height/2);
fill(255);
text("Try again?", width/2,height/2+64);
button1.touch();//touch checks if the mouse is over the button(the button sucks ass btw the hitbox for it is a square not a rectangle)
button1.show();//showing the button
button1.update();//updates the text and button color when highlighted
fill(texthover);
textSize(48);
text("Yes",width/2, height/2+145);
pop();
}
function button(){
this.x = width/2;
this.y = height/2+128;
this.d;
this.update = function() {
this.x = width/2;
this.y = height/2+128;
}
this.show = function(){
push();
rectMode(CENTER);
fill(hover);
rect(this.x, this.y, 128, 64, 50);
pop();
}
this.touch = function(){
this.d = dist(this.x, this.y, mouseX, mouseY);
if(this.d <32){
hover = 51;
texthover = 255;
if(mouseIsPressed){
for(var i = 0; i < num; i++) {
bs[i].x = random(0, width);
bs[i].y = random(-600,-30);
}
ship1.x = width/2;
ship1.y = 450;
gscore = 0;
cap = 0;
}
}else {
hover = 200;
texthover = 0;
}
}
}
function ship() {
this.x = width/2;
this.y = 450;
this.update = function() {
if(keyIsDown(LEFT_ARROW) || keyIsDown(65)) {
if(this.x>14){
this.x = this.x - map(gscore,2,100,2,3);
}
}
if(keyIsDown(RIGHT_ARROW) || keyIsDown(68)) {
if(this.x<width- 15){
this.x = this.x + map(gscore,2,100,2,3);
}
}
}
this.show = function() {
push();
rectMode(CENTER);
fill(200,200,0);
rect(this.x+15, this.y+5, 5, 30);
fill(150,100,200);
rect(this.x+15, this.y + 15,30, 15)
pop();
}
}
function windowResized() {
createCanvas(windowWidth,windowHeight- 4);
button1.update();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.14/addons/p5.dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.14/p5.js"></script>

Resources