Why isn't my For() Loop animation working? - p5.js

I coded a simple shooting simulator that turns your mouse to a crosshair and animates a bullet on click. I want to create an animation that enlarges the crosshair and then makes it small again when a bullet is fired. I am using a list that has the ten different sizes. This is my best attempt:
var imgConst = 100;
var imgSize = imgConst;
var imgChange = [4, (6 + 2/3), 10, -10, -10, -10, -10, -20, (-33 - 1/3), -50];
for(var i = 0; i < imgChange.length; i++) {
imgChange[i] = imgSize + imgConst/imgChange[i];
imgSize = imgChange[i];
}
I didn't hard code numbers because I eventually want to make the crosshair dependent on window size. I used a for loop to avoid writing repetitive code. imgConst is the what size the crosshair should normally be, imgSize directly affects the crosshair, and imgChange stores the values that the animation should be. Here is my code to execute the animation:
for(var i = 0; i < imgChange.length * 1; i ++) {
imgSize = imgChange[floor(i)]
}
The problem is that this simply does not work. I know that it goes very fast, so I decide to cap the frame rate at ten to see if it worked, and still nothing happened. As for slowing it down, I have tried making For() loops that increase by 0.1 and the size is changed to imgChange[Math.floor(i)] So, I guess I'm asking two questions: How do I make the animation work in the first place, and how do I slow down the animation once it's working?

What #Charlie Wallace said; Is that for loop inside the draw function?
When it is, it probably works. However, you loop over all the changes before drawing to the canvas, only the last change will be displayed.
You could do something like this. Every frame you update the change once. Instead of the whole range.
var imgConst = 100;
var imgSize = imgConst;
var imgChange = [4, (6 + 2/3), 10, -10, -10, -10, -10, -20, (-33 - 1/3), -50];
var changeIdx = 0;
function draw(){
imgChange[ changeIdx ] = imgSize + imgConst/imgChange[ changeIdx ];
imgSize = imgChange[ changeIdx ];
changeIdx += 1;
}

Related

I have a question about using turtle graphic functions and looping methods on p5.js

I have to create these two included images using the turtle function and the loop method on p5js and I am struggling I was given https://editor.p5js.org/dpapanik/sketches/_lbGWWH6N this code on p5js as a start please help, thanksenter image description here
So I've played around with some of the stuff for awhile, and I've created two functions. One that makes a single quadrant of the first problem, and one that creates a single wiggly line for the second problem. This is just a base for you to work of in this process. Here's each of the functions. Also, note that each of them takes in the turtle as a parameter:
function makeLineQuadrant(turtle) {
// this currently makes the top left corner:
let yVal = windowWidth * 0.5;
let xVal = windowWidth * 0.5;
for (let i = 0; i < 13; i++) {
// loop through the 12 lines in one quadrant
turtle.face(0); // reset for the new round
turtle.penUp();
let startLeft = i * ((windowWidth * 0.5) / 12); // decide which component on the button we should start at
let endTop = (12 - i) * ((windowWidth * 0.5) / 12); // how far down the y-axis should we go? You should write this out on paper to see how it works
turtle.goto(startLeft, yVal);
turtle.penDown();
let deg = turtle.angleTo(xVal, endTop); // what direction do I need to turn?
turtle.face(deg);
let distance = turtle.distanceTo(xVal, endTop); // how far away is it?
turtle.forward(distance);
}
}
I tried to add a few comments throughout, but if there is any step that is confusing, please add a comment.
function makeSquiggle(turtle) {
turtle.setColor(color(random(0, 255), random(0, 255), random(0, 255)));
let middleX = windowWidth * 0.5, middleY = windowHeight * 0.5;
turtle.goto(windowWidth * 0.5, windowHeight * 0.5);
// let's start moving in a random direction UNTIL our distance from the center is greater than some number X
let X = 300; // arbitrary distance from center
// some variables that can help us get some random movement for our turtle:
let turtleXvel = random(-3, 3), turtleYvel = random(-3, 3);
while (turtle.distanceTo(middleX, middleY) < X) {
turtle.face(0);
// calculate movement:
let newXmove = turtle.x + turtleXvel, newYmove = turtle.y + turtleYvel;
// direct our turtle:
turtle.face(turtle.angleTo(newXmove, newYmove));
let distance = turtle.distanceTo(newXmove, newYmove); // how far away is it?
// move our turtle
turtle.penDown();
turtle.forward(distance);
// change the velocity a little bit for a smooth curving:
turtleXvel += random(-1, 1);
turtleYvel += random(-1, 1);
}
}
Note that I'm changing the velocities instead of the position directly. This is a classic Calculus / Physics problem where the derivative gives us a smaller range, so adjusting turtleXvel and turtleYvel change the position in much less drastic ways versus:
turtle.x += random(-1, 1);
turtle.y += random(-1, 1);
You should look at the difference as well to visualize this. Beyond this is working with these structural components to finish this up!

How do I use the X and Y values from an object from touches in P5js?

This is my first week playing with P5js and Processing so be gentle.
I've got the following P5js code where I'm looking at touch values. By pressing multi-points on a touch screen brings up more circles anchored to a central position.
What I'd like to know is how do I call the x value for object 1 that's printed in the console? or y value for object 0? how would i state them in the code?
What I'd like to do is use these values to change the dimensions of a shape in the middle of the screen, like something in object 0 driving the height of the shape or object 1 on the the x or y values driving strokeWeight or colour . However I'm in my first week and completely lost to how to use these values in the code.
function setup() {
createCanvas(windowWidth, windowHeight);
background(200);
}
function draw() {
background(255);
fill(255, 0, 0);
strokeWeight(0)
for (var i = 0; i < touches.length; i++) {
fill(255);
strokeWeight(3)
ellipse(touches[i].x, touches[i].y, 50, 50);
line(touches[i].x, touches[i].y, windowWidth / 2, windowHeight / 2);
fill(255);
ellipse(windowWidth / 2, windowHeight / 2, 10, 10);
print(touches);
}
}
// do this prevent default touch interaction
function mousePressed() {
return false;
}
document.addEventListener('gesturestart', function(e) {
e.preventDefault();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.2/p5.min.js"></script>
or Sketch file is here
any help would be muchly muchly appreciated, i'm a newb trying to find my way
There is only one mistake in it which I found till now, the color of the background and the fill colors are the same! you may change them to actually see the shapes, lines etc.

How do I animate this image to match with a BPM in P5.js?

So I am working with p5.js for class and I am very lost with it, as I dont understand very well. How do I animate this image to match with the sound? I tried frequency analysis but i dont know how to apply to the image. I wanted to animate it as i it was beating, like a heart, but according to the bpm sound i put in the sketch.
here is the sketch + image + sound
https://editor.p5js.org/FilipaRita/sketches/cUG6qNhIR
Actually finding the BMP for an entire piece of music would be a bit complicated (see this sound.stackexchange.com question), but if you just want to detect beats in real time I think you can probably hack something together that will work. Here is a visualization that I think will help you understand the data returned by fft.analyze():
const avgWindow = 20;
const threshold = 0.4;
let song;
let fft;
let beat;
let lastPeak;
function preload() {
song = loadSound("https://www.paulwheeler.us/files/metronome.wav");
}
function setup() {
createCanvas(400, 400);
fft = new p5.FFT();
song.loop();
beat = millis();
}
function draw() {
// Pulse white on the beat, then fade out with an inverse cube curve
background(map(1 / pow((millis() - beat) / 1000 + 1, 3), 1, 0, 255, 100));
drawSpectrumGraph(0, 0, width, height);
}
let i = 0;
// Graphing code adapted from https://jankozeluh.g6.cz/index.html by Jan Koželuh
function drawSpectrumGraph(left, top, w, h) {
let spectrum = fft.analyze();
stroke('limegreen');
fill('darkgreen');
strokeWeight(1);
beginShape();
vertex(left, top + h);
let peak = 0;
// compute a running average of values to avoid very
// localized energy from triggering a beat.
let runningAvg = 0;
for (let i = 0; i < spectrum.length; i++) {
vertex(
//left + map(i, 0, spectrum.length, 0, w),
// Distribute the spectrum values on a logarithmic scale
// We do this because as you go higher in the spectrum
// the same perceptible difference in tone requires a
// much larger chang in frequency.
left + map(log(i), 0, log(spectrum.length), 0, w),
// Spectrum values range from 0 to 255
top + map(spectrum[i], 0, 255, h, 0)
);
runningAvg += spectrum[i] / avgWindow;
if (i >= avgWindow) {
runningAvg -= spectrum[i] / avgWindow;
}
if (runningAvg > peak) {
peak = runningAvg;
}
}
// any time there is a sudden increase in peak energy, call that a beat
if (peak > lastPeak * (1 + threshold)) {
// print(`tick ${++i}`);
beat = millis();
}
lastPeak = peak;
vertex(left + w, top + h);
endShape(CLOSE);
// this is the range of frequencies covered by the FFT
let nyquist = 22050;
// get the centroid (value in hz)
let centroid = fft.getCentroid();
// the mean_freq_index calculation is for the display.
// centroid frequency / hz per bucket
let mean_freq_index = centroid / (nyquist / spectrum.length);
stroke('red');
// convert index to x value using a logarithmic x axis
let cx = map(log(mean_freq_index), 0, log(spectrum.length), 0, width);
line(cx, 0, cx, h);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/addons/p5.sound.min.js"></script>
Hopefully this code with the comments helps you understand the data returned by fft.analyze() and you can use this as a starting point to achieve the effect you are looking for.
Disclaimer: I have experience with p5.js but I'm not an audio expert, so there could certainly be better ways to do this. Also while this approach works for this simple audio file there's a good chance it would fail horribly for actual music or real world environments.
If I were you then I would cheat and add some meta data that explicitly includes the timestamps of the beats. This would be a much simpler problem if you could shift the problem of beat detection to pre-processing. Maybe even do it by hand. Rather than trying to do it at runtime. The signal processing to do beat detection in an audio signal is non-trivial.

Animating with the `ImageData` data array causes very unexpected and glitch-like results

I recently stretched a gradient across the canvas using the ImageData data array; ie the ctx.getImageData() and ctx.putImageData() methods, and thought to myself, "this could be a really efficient way to animate a canvas full of moving objects". So I wrapped it into my main function along with the requestAnimationFrame(callback) statement, but that's when things got weird. The best I can do at describing is to say it's like the left most column of pixels on the canvas is duplicated in the right most column of pixels, and based on what coordinates you specify for the get and put ctx methods, this can have bizarre consequences.
I started out with the get and put methods targeting the canvas at 0, 0 like so:
imageData = ctx.getImageData( 0, 0, cvs.width, cvs.height );
// here I set the pixel colors according to their location
// on the canvas using nested for loops to target the
// the correct imageData array indexes.
ctx.putImageData( imageData, 0, 0 );
But I immediately noticed the right side of the canvas was wrong. Like the gradient has started over, and the last pixel just didn't get touched for some reason:
So scaled back my draw region changed the put ImageData coordinates to get some space between the drawn image and the edge of the canvas, and I changed the get coordinated to eliminate that line on the right edge of the canvas:
imageData = ctx.getImageData( 1, 1, cvs.width, cvs.height );
for ( var x = 0; x < cvs.width - 92; x++ ) {
for ( var y = 0; y < cvs.height - 92; y++ ) {
// array[ x + y * width ] = value / x; // or similar
}
}
ctx.putImageData( imageData, 2, 2 );
Pretty! But wrong... So I reproduced it in codepen. Can someone help me understand and overcome this behavior?
Note: The codepen has the scaled back draw area. If you change the get coordinates to 0 you'll see it basically behaves the same way as the first example but with white-space in between the expected square and the unexpected line. That said, I left the get at 1 and the put at zero for the most interesting behavior yet.
I've changed your code a little. In your double loop I am declaring a variable var i = (x + y*cvs.width)*4; This is only reducing the verbosity of your code so that I can see it better. The i variable represents the index of your pixel in the imageData.data array. Since you are doing
imageData.data[i - 4 ] ...
imageData.data[i - 3 ] ...
imageData.data[i - 2 ] ...
imageData.data[i - 1 ] ...
you are going one pixel backwards and the first pixel from every row appears as the last pixel of the previous row. So I've changed it from var i = (x + y*cvs.width)*4; to var i = 4 + (x + y*cvs.width)*4;.
When you are animating it, since the imageData is inside the test() function, you are recalculating the values of the imageData.data array in base of the last frame. So in the second frame you have that 1px line from the first frame copied again and moved 1px upward and 1px to the left.
I hope this is what you were asking.
var ctx, cvs, imageData;
cvs = document.getElementById('canv');
ctx = cvs.getContext('2d');
function test() {
// imageData = ctx.getImageData( 0, 0, cvs.width, cvs.height );
// produces a line on the right side of the screen
imageData = ctx.getImageData( 1, 1, cvs.width, cvs.height );
// bizzar reverse cascading
for (var x=0;x<cvs.width-92;x++) {
for (var y=0;y<cvs.height-92;y++) {
var i = 4+(x + y*cvs.width)*4;
imageData.data[i - 4 ] = Math.floor((255-y)-Math.floor(x/55)*55);
imageData.data[i - 3 ] = Math.floor(255/(cvs.height-92)*y);
imageData.data[i - 2 ] = Math.floor(255/(cvs.width-92)*x);
imageData.data[i - 1 ] = 255;
}
}
ctx.putImageData( imageData, 0, 0 );
requestAnimationFrame( test );
}
test();
canvas {
box-shadow: 0 0 2.5px 0 black;
}
<canvas id="canv" height="256" width="256"></canvas>

THREE.js Mutating vertices of Plane according to Data from mp3

So i've been stuck for a while because i've been having trouble dynamically changing the shape of the vertices in a place geometry according to the frequency data of an mp3, I've been having 2 main problems:
1)The array generated by the mp3 has too many values and it is impossible to render out the vertices that fast and accordingly, i am getting the frequency data with this code.
var frequencyData = new Uint8Array(analyser.frequencyBinCount);
2) Re-Rendering the plane everytime frequencyData changes causes extreme performance issues to the point it does not render out anymore
I've been using simplex noise to cause the vertices to morph, and it does work until obviously i pass in frequency data and everything breaks, this is the code i'm trying to use to morph the vertices of the plane according to the music.
function adjustVertices() {
for (var i = 0; i < 100; i++) {
for (var j = 0; j < 100; j++) {
var ex = 0.5;
pgeom.vertices[i + j * 100].z =
(noise.simplex2(i / 100, j / 100) +
noise.simplex2((i + 500) / 50, j / 50) * Math.pow(ex, frequencyData[2]) +
noise.simplex2((i + 400) / 25, j / 25) * Math.pow(ex, frequencyData[2]) +
noise.simplex2((i + 600) / 12.5, j / 12.5) * Math.pow(ex, frequencyData[2]) +
+(noise.simplex2((i + 800) / 6.25, j / 6.25) * Math.pow(ex, frequencyData[2]))) /
2;
pgeom.verticesNeedUpdate = true;
pgeom.computeVertexNormals();
}
}
}
This is my plane object:
var pgeom = new THREE.PlaneGeometry(5, 5, 99, 99);
var plane = THREE.SceneUtils.createMultiMaterialObject(pgeom, [
new THREE.MeshPhongMaterial({
color: 0x33ff33,
specular: 0x773300,
side: THREE.DoubleSide,
shading: THREE.FlatShading,
shininess: 3,
}),
]);
scene.add(plane);
I am very grateful for the help, I am just doing my best in mastering three.js :)
I would check if the computeVertexNormals is what is taking the most time in that render loop, and then look into optimizing it, if you still require it.
You can optimize the normal calculation by building the mesh topology once at startup, since it doesn't change at runtime, making the recalc run in constant time.
Then reduce the vertex count until things become manageable. :)
The first answer is correct. Most likely computing vertex normals is causing the hit, and it's most likely happening because the Geometry method which you seem to be using creates a lot of new THREE.Vector3. If you profile this i imagine you'd see a lot of GC activity and not so much of computation time.
One more thing to consider since you only map one variable, is to move this computation in the shader. You could write your values to a texture and only update that. You would not have to refresh the vertex and normal buffers which are much larger than the texture you'd need to store just the input variable. You would also be able to do this computation in parallel.

Resources