My p5.js code is lagging i dont know why? - javascript

I was trying to draw while moving the circle when we press the mouse. But eventually, when I move it after some time the rotating circle and the Big circle are getting slowed.
I don't know what is the reason, I was thinking it because the list is accumulated with the coordinates and making the code lag eventually please help.
If you press the mouse and wait for some time you can observe the circles getting slowed eventually. correct me if I am wrong.
class Particle{
constructor(){
this.center = createVector(0,0);
this.radius = 20;
this.theta = 0;
this.line = createVector(0,0);
this.history = [];
this.velocity = createVector();
}
render(){
translate(60,60);
circle(this.center.x,this.center.y,this.radius);
circle(this.line.x+this.center.x, this.line.y+this.center.y,10);
beginShape();
for(let i=0;i < this.history.length; i++){
let pos = this.history[i];
noFill();
vertex(pos.x, pos.y);
endShape();
}
}
update(){
this.line.x = this.radius*cos(this.theta);
this.line.y = this.radius*sin(this.theta);
if (mouseIsPressed){
this.center.x = (1-0.025)*this.center.x + 0.025*(this.line.x + this.center.x);
this.center.y = (1-0.025)*this.center.y + 0.025*(this.line.y + this.center.y);
let v = createVector(this.center.x, this.center.y);
this.history.push(v);
} else{
this.theta += 0.01;
}
}
}
let particle;
function setup() {
createCanvas(windowWidth, windowHeight);
particle = new Particle();
}
function draw() {
background(220);
particle.render();
particle.update();
}
<html>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.js"></script>
</body>
</html>

There is a misalignment of beginShape() and endShape() in your code. beginShape() is before the loop, but endShape() is in the loop. Move endShape() after the loop:
beginShape();
for(let i=0;i < this.history.length; i++){
let pos = this.history[i];
noFill();
vertex(pos.x, pos.y);
}
endShape();
Very likely the code is lagging, because the number of points in the history increase. Try to reduce the number of points. Pixels can only be display on integral positions. Avoid adding duplicate items to the list:
update(){
this.line.x = this.radius*cos(this.theta);
this.line.y = this.radius*sin(this.theta);
if (mouseIsPressed){
this.center.x = (1-0.025)*this.center.x + 0.025*(this.line.x + this.center.x);
this.center.y = (1-0.025)*this.center.y + 0.025*(this.line.y + this.center.y);
let v = createVector(this.center.x, this.center.y);
let h = this.history;
if (h.length == 0 ||
Math.trunc(h[h.length-1].x) != Math.trunc(v.x) ||
Math.trunc(h[h.length-1].y) != Math.trunc(v.y)) {
this.history.push(v);
}
} else{
this.theta += 0.01;
}
}
If the application is still lagging, you can try drawing the dots on a p5.Renderer object the size of the canvas (createGraphics()). Draw this object on the canvas like a background image.

Related

How to have the random function run once in the draw function?

This is the code
let x = 10;
let y = 0;
let bottomy = 100;
let Speed = 1
function setup() {
createCanvas(windowWidth,600);
}
function draw() {
background(0)
strokeWeight(3)
stroke(255)
for (i = 0; i < width; i += 20) {
water()
line(i,y,i,bottomy)
}
bottomy = bottomy + Speed;
if (bottomy > height) {
bottomy = 100
}
frameRate(1)
}
function water(){
bottomy = random(0,600)
//noLoop()
}
I want to randomise each y2 line coordinate in the for loop. But then have the y2 line coordinate to increment by 1. To create a rain effect.
I can't put the random variable in setup and then call it in the for loop because it won't affect each line in the for loop and I can't put the for loop in setup because I need the line to be drawn.
I've also tried creating a function that loops once and then calling it in draw but it ends up stopping all the code in the draw function.
I've seen examples where they generate like an infinite amount of random lines. But I would like to keep the x position of each line the same if possible. If it's not possible to do this with a for loop and I have to draw each line individually that's fine I was just wondering if this is possible to efficiently do this with a for loop.
I think what you are looking for is individual variables for each line.
Probably a classic:
from-several-variables-to-arrays
from-several-arrays-to-classes
situation. (those were made in Java's processing, but the concept can be easily adapted)
Anyway, i think this is what you tried to make with your code, but it does not work as intended, since you only have one bottomY var for all lines.
let x = 10;
let y = 0;
let bottomY = 100;
let spd = 1; //by convention capitalized names are for classes
function setup() {
createCanvas(windowWidth, 600);
}
function draw() {
background(0);
strokeWeight(3);
stroke(255);
for (i = 0; i < width; i += 20) {
if (bottomY > height) {
bottomY = random(600);
}
line(i, y, i, bottomY);
}
bottomY += spd;
}
What you want is several lines that each has individual x and bottomY
So you could use two arrays for that:
let x = [];
let y = 0;
let bottomY = [];
//why not have individual speeds as well...
let spd = [];
function setup() {
createCanvas(windowWidth, 600);
for (i = 0; i < width; i += 20) {
x.push(i);
bottomY.push(random(height));
spd.push(random(0.6, 2));
}
strokeWeight(3);
stroke(255);
}
function draw() {
background(0);
for (let i = 0; i < x.length; i++) {
line(x[i], y, x[i], bottomY[i]);
if (bottomY[i] < height) {
bottomY[i] += spd[i];
} else {
bottomY[i] = random(height);
}
}
}
And finally a simple class implementation:
let rain = [];
function setup() {
createCanvas(windowWidth, 600);
for (i = 0; i < width; i += 20) {
rain.push(new Drop(i, 0));
}
}
function draw() {
background(0);
for (const d of rain) {
d.run();
}
}
class Drop {
constructor(x, y) {
this.x = x;
this.y = y;
this.btY = random(height);
this.spd = random(0.6, 2);
}
run() {
strokeWeight(3);
stroke(255);
line(this.x, this.y, this.x, this.btY);
if (this.btY < height) {
this.btY += this.spd;
} else {
this.btY = random(height);
}
}
} //class
Did it make sense?

The particles aren't showing up on screen

I don't know why my particles won't show up on the screen, when I play the sketch everything is frozen in place and particles don't come out. Particles are supposed to come out from the circles, while the circle is an audio visualizer of the song. I'm following a tutorial and making a few tweaks on my own so I can actually learn the code, but I'm stuck on showing the particles on the screen.
const { Part } = require("c:/users/12403/.vscode/extensions/samplavigne.p5-vscode-1.2.8/p5types");
var song
var fft
var img
var particles = []
function preload(){
song = loadSound('Clair de Lune With Rain.mp3')
img = loadImage('rain.jpg')
}
function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(DEGREES)
imageMode(CENTER)
img.filter(BLUR, 2)
fft = new p5.FFT()
}
function draw() {
background(0)
stroke(255)
strokeWeight(3)
noFill()
translate(width/2, height/2)
image(img, 0, 0, width, height)
fft.analyze()
amp = fft.getEnergy(20,200)
var wave = fft.waveform()
for (var t = -1; t <= 1; t += 2){
beginShape()
for(var i = 0; i <= 180; i+= 0.3){
var index = floor (map(i, 0, width, 0 , wave.length-1))
var r = map(wave[index], -1, 1, 150, 350)
var x = r*sin(i) * t
var y = r*cos(i)
vertex(x,y)
}
endShape()
}
var p = new Particle()
particles.push(p)
for(var i=0; i<particles.length; i++){
particles[i].show()
}
}
function mouseClicked(){
if (song.isPlaying()){
song.pause()
}
else{
song.play()
loop()
}
}
class Particle{
constructor(){
this.pos = p5.Vector.random2D().mult(250)
}
show(){
noStroke()
fill(random)
ellipse(this.pos.x, this.pos.y, 4)
}
}
Your code was crashing because this is invalid:
fill(random)
The fill function expects a color or number, and random is a function, so you need to invoke the random function to get a numeric value. Something like fill(random(0, 255)).
With that fixed your sketch runs and draws the particles, but there is clearly some logic missing:
you create one new particle per frame
each new particle gets a random position around a circle with a radius of 200
each particle's position has nothing to do with the audio and never changes over time
you never remove particles
You question is much to vague to figure out what you actually want to have happen, and you clearly need to take a few more steps to make it happen (such as setting each particle's position according to the audio FFT values, or updating each particle's position each frame). Also You definitely need a way to limit the number of particles, otherwise your sketch will eventually grind to a halt.
var song
var fft
var img
var particles = []
function preload() {
// Audio licensed Creative Commons Attribution Non-Commercial
// by apoxode https://apoxode.bandcamp.com/releases
// http://dig.ccmixter.org/files/Apoxode/63948
song = loadSound('https://www.paulwheeler.us/files/Apoxode_-_halcyon.mp3');
img = loadImage('https://www.paulwheeler.us/files/windows-95-desktop-background.jpg');
}
function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(DEGREES)
imageMode(CENTER)
img.filter(BLUR, 2)
fft = new p5.FFT()
}
function draw() {
background(0)
stroke(255)
strokeWeight(3)
noFill()
translate(width / 2, height / 2)
image(img, 0, 0, width, height)
fft.analyze()
amp = fft.getEnergy(20, 200)
var wave = fft.waveform()
for (var t = -1; t <= 1; t += 2) {
beginShape()
for (var i = 0; i <= 180; i += 0.3) {
var index = floor(map(i, 0, width, 0, wave.length - 1))
var r = map(wave[index], -1, 1, 150, 350)
var x = r * sin(i) * t
var y = r * cos(i)
vertex(x, y)
}
endShape()
}
var p = new Particle()
particles.push(p)
for (var i = 0; i < particles.length; i++) {
particles[i].show()
}
}
function mouseClicked() {
if (song.isPlaying()) {
song.pause()
} else {
song.play()
loop()
}
}
class Particle {
constructor() {
this.pos = p5.Vector.random2D().mult(250)
}
show() {
noStroke()
fill(random())
ellipse(this.pos.x, this.pos.y, 4)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>

how can i rewrite this code to make an object fall from top of canvas and when hits bottom it resets at top

I am trying to implement a group of objects that will fall from the top of the canvas in random spots, and then when they hit the bottom of the canvas they respawn at the top of the canvas. Ideally, I want to change this code in the future to use sprites that will fall from the top of the canvas and explode when they hit the bottom of the canvas but then respawn again at the top.
I have the bellow code that works when the mouse is pressed but I would like to rewrite this code so the event happens automatically without the mouse needed to be pressed to make the objects fall.
see code below.
var x, y;
var particle = [];
function setup() {
createCanvas(720, 400);
// Starts in the middle
x = width / 2;
y = height;
}
function draw() {
background(0);
if (mouseIsPressed){
stroke(50);
fill(100);
ellipse(x, y, 24, 24);
}
x = x + 0;
// Moving up at a constant speed
y = y + 2;
// Reset to the bottom
if (y >= height) {
y = 0;
}
}
Use the mousePressed() to add new particles to the list. The particles are added at the current mouse position (mouseX, mouseY). Move and draw the particles in a loop:
var particles = [];
function setup() {
createCanvas(720, 400);
}
function mousePressed() {
particles.push([mouseX, mouseY]);
}
function draw() {
background(0);
stroke(50);
for (let i=0; i < particles.length; i++) {
particles[i][1] += 2;
if (particles[i][1] > height) {
particles[i][1] = 0;
}
fill(100);
ellipse(particles[i][0], particles[i][1], 24, 24);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Alternatively, you can spawn the particles with a time interval. Get the number of milliseconds (thousandths of a second) since starting the sketch with millis(). The y-coordinate of a new particle is 0 and the x-coordinate is random():
var particles = [];
function setup() {
createCanvas(720, 400);
}
function mousePressed() {
particles.push([mouseX, mouseY]);
}
function draw() {
let max_no_of_particles = 40;
let expected_no_of_praticles = min(max_no_of_particles, millis() / 100);
if (particles.length < expected_no_of_praticles) {
particles.push([random(12, width-12), 0]);
}
background(0);
stroke(50);
for (let i=0; i < particles.length; i++) {
particles[i][1] += 2;
if (particles[i][1] > height) {
particles[i][1] = 0;
}
fill(100);
ellipse(particles[i][0], particles[i][1], 24, 24);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>

Canvas shape click removes more than one shape

I'm attempting to detect a canvas click and if the click coordinates match a shape. It works fine, the problem is that when the shapes overlap on the canvas (one shape might be smaller than the other) then both shapes are removed. Is there a way to avoid this and only remove one at a time?
addShape() {
const randomNum = (min, max) => Math.round(Math.random() * (max - min) + min),
randomRad = randomNum(10, 100),
randomX = randomNum(randomRad, this.canvas.width - randomRad);
let shape = new Shape(randomX, randomNum, randomRad);
shape.drawShape();
this.shapes.push(shape);
}
canvasClick() {
if(!this.paused) {
const canvasRect = event.target.getBoundingClientRect(),
clickedX = event.clientX - canvasRect.left,
clickedY = event.clientY - canvasRect.top;
for (let i = 0; i < this.shapes.length; i++) {
if(Math.pow(clickedX - this.shapes[i].x, 2) + Math.pow(clickedY - this.shapes[i].y, 2)
< Math.pow(this.shapes[i].rad,2)) {
this.shapes.splice(i, 1);
}
}
}
}
Thanks in advance for the help!
If I have understood what you what, the solution is pretty simple. Just break the loop after you remove one shape. Something like this:
for (let i = 0; i < this.shapes.length; i++) {
if (
Math.pow(clickedX - this.shapes[i].x, 2)
+ Math.pow(clickedY - this.shapes[i].y, 2)
< Math.pow(this.shapes[i].rad, 2)
) {
this.shapes.splice(i, 1);
break; // <--
}
}
You might also what to store the z position of shapes so that you can first sort them in the terms of z and then run this loop so that you don't remove shape below the clicked shape.
This is how I would do it. Please read the comments in the code. I am using the ctx.isPointInPath method but you may use the formula if you prefer.
The main idea is to exit the loop after finding and deleting the first circle.
I hope it helps.
let ctx = canvas.getContext("2d");
canvas.width = 300;
canvas.height = 300;
ctx.fillStyle = "rgba(0,0,0,.5)";
// the array of the circles
let circles = []
class Circle{
constructor(){
this.x = ~~(Math.random() * canvas.width);
this.y = ~~(Math.random() * canvas.height);
this.r = ~~(Math.random() * (40 - 10 + 1) + 10);
this.draw();
}
draw(){
ctx.beginPath();
ctx.arc(this.x,this.y, this.r, 0,2*Math.PI)
}
}
// create 20 circles
for(i=0;i<20;i++){
let c = new Circle();
ctx.fill()
circles.push(c)
}
canvas.addEventListener("click",(e)=>{
// detect the position of the mouse
let m = oMousePos(canvas, e)
for(let i=0;i<circles.length;i++){
// draw a circle but do not fill
circles[i].draw();
// check if the point is in path
if(ctx.isPointInPath(m.x,m.y)){
//remove the circle from the array
circles.splice(i, 1);
// clear the context
ctx.clearRect(0,0,canvas.width,canvas.height)
// redraw all the circles from the array
for(let j=0;j<circles.length;j++){
circles[j].draw();
ctx.fill()
}
//exit the loop
return;
}
}
})
// a function to detect the mouse position
function oMousePos(canvas, evt) {
var ClientRect = canvas.getBoundingClientRect();
return { //objeto
x: Math.round(evt.clientX - ClientRect.left),
y: Math.round(evt.clientY - ClientRect.top)
}
}
canvas{border:1px solid}
<canvas id="canvas"></canvas>

p5.js – Smoothly morphing random shape

first of all, i am a beginner on js and p5.js. My aim on this program is a smoothly morphing random shape. I was satisfied with the calculateShape()-function and the drawShape()-function, but when it comes to morphing (updateShape()) it gets really ugly. I thought it might be a good idea to save my current array into a temporary array, then loop over the array and add a random number to each x and y of each index and then replace the old x and y at this index. The main problem is, that it is always adding new shapes on the screen instead of changing the values of the vertices of the existing shape. Can anybody of you please give me a hint or point out my mistake(s)? THANK YOU IN ADVANCE!
var c1;
var c2;
var c3;
var centerX;
var centerY;
var fb;
var radius;
var angle;
var shape = [];
var temp;
/*function to calculate the inital shape*/
function calculateShape() {
//calculate coordinates and save into array
for (var i = 0; i < fb; i++) {
var x = cos(angle * i) * radius + random(-77,77);
var y = sin(angle * i) * radius + random(-77,77);
var v = createVector(x, y);
shape.push(v);
}
}
/*function for morphing the shape*/
function updateShape() {
var temp = shape;
for (var i = 0; i < shape.length - 1; i++) {
var x = temp[i].x + random(-1, 1);
var y = temp[i].y + random(-1, 1);
var p = temp[i];
var v = createVector(x, y);
shape.splice(p,1);
shape.push(v);
}
}
/*function for drawing the shape on the screen*/
function createShape(){
beginShape();
curveVertex(shape[shape.length-1].x, shape[shape.length-1].y);
for (var i = 0; i < shape.length; i++){
curveVertex(shape[i].x, shape[i].y);
}
curveVertex(shape[0].x, shape[0].y);
endShape(CLOSE);
}
function setup() {
createCanvas(windowWidth, windowHeight);
smooth();
background(250);
//frameRate(2);
// defining possible colors
c1 = color(0, 196, 181, 235);
c2 = color(50, 227, 232, 235);
c3 = color(248, 49, 62, 255);
var colors = [c1, c2, c3];
//center of the window
centerX = windowWidth/2;
centerY = windowHeight/2;
//defining all variables
fb = 8;
angle = radians(360 / fb);
radius = random(120, 140);
//calling thefunction that initalises the shape
calculateShape();
}
function draw() {
translate(centerX, centerY);
blendMode(BLEND);
fill(c3);
noStroke();
createShape();
updateShape();
}
The main problem is, that it is always adding new shapes on the screen instead of changing the values of the vertices of the existing shape.
Sure, you just need to clear the screen before drawing again. So, reset the background with the background(250) from setup, in draw.

Categories