I'm coding a tree in different seasons (though the original code for the trunk and randomized branches is not my own). For winter, I want to keep the tree randomized, but also have animated snowflakes.
However, each time the function that makes the tree is called, the random numbers are (obviously) different. It there a way to save the output of the function (or just of the random() functions within it) and just draw that output?
The draw() function calls the function that makes the tree, so if it is called multiple times, the tree will rapidly change, since it is randomized each time. The tree function has to repeat itself to draw the tree, so there are a lot of random numbers. Ultimately, I just want to draw a randomized tree, and then repeatedly draw that same tree, so that the draw function can repeatedly run and not cover up the tree. Here are the 2 lines of code with the random numbers I want to save:
rotate(random(-0.05,0.05));
if (random(1.0) < 0.6) {
You want to use the randomSeed() function here. If you call it at the top of your draw() function each time, with the same number, then you will get the same 'random' tree. If you change the number, you will get a different 'random' tree. If you want a new tree (perhaps when the mouse is clicked), then you can change the number. For example:
let rs;
function setup() {
rs = random(0, 1000);
// your code
}
function draw() {
randomSeed(rs);
// your code
}
function mouseClicked {
rs = random(0, 1000);
}
You could do this by storing the values in variables. Here's a simplified example:
var x;
var y;
function setup(){
createCanvas(500, 500);
x = random(width);
y = random(height);
}
function draw(){
background(32);
ellipse(x, y, 25, 25);
}
This is just a simple example, but you could get fancier by using something like a Tree class that stores its own values. But the idea is the same: generate the random value once, store it in a variable, and then use that variable when you draw it.
You could also draw the tree to a buffer once, and then draw that buffer to the screen. The createGraphics() function would come in handy for this approach.
Related
In p5.js I'm trying to visualise a Linear Search Algorithm. I have an array of numbers and want to visualise them by drawing them as lines pointing upwards.
When I put the forEach loop in the setup function, nothing appears. If I put it in draw function, I see the exact result that I'm looking for pop up for half a second and then dissapear...
(The setup function is called when the program is run, and the draw loop is continuously called every frame) (The line function draws a line from the first x,y to the second x,y line(x,y x,y)
Here's the code:
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let buffer = 0
function setup() {
createCanvas(400, 400);
numbers.forEach((number) => {
stroke(0);
strokeWeight(4);
line(buffer, height, buffer, number * 50)
buffer += 15
});
}
function draw() {
background(255);
}
Think about the order that things happen in.
You said it yourself:
The setup function is called when the program is run, and the draw loop is continuously called every frame
So with the code you have, the setup function runs once at the very beginning of your program, and then the draw function is called 60 times per second. The only thing you're doing inside the draw function is drawing a background, which removes what you drew in the setup function. Try removing the call to background(255) to see what I mean.
Then when you move your code into the draw function, I'll offer this hint: What is the value of the buffer variable each frame? You might use some console.log() statements to answer that.
I'm a beginner on here, so apologies in advance for naivety. I've made a simple image on Brackets using Javascript, trying to generate circles with random x and y values, and random colours. There are no issues showing when I open the browser console in Developer Tools, and when I save and refresh, it works. But I was expecting the refresh to happen on a loop through the draw function. Any clues as to where I've gone wrong?
Thanks so much
var r_x
var r_y
var r_width
var r_height
var x
var y
var z
function setup()
{
r_x = random()*500;
r_y = random()*500;
r_width = random()*200;
r_height = r_width;
x = random(1,255);
y= random(1,255);
z= random(1,255);
createCanvas(512,512);
background(255);
}
function draw()
{
ellipse(r_x, r_y, r_width, r_height);
fill(x, y, z);
}
Brackets.io is just your text editor (or IDE if you want to be technical) - so we can remove that from the equation. The next thing that baffles me is that something has to explicitly call your draw() method as well as the setup() method -
I'm thinking that you're working in some sort of library created to simplify working with the Canvas API because in the setup() method you're calling createCanvas(xcord,ycord) and that doesn't exist on it's own. If you want to rabbit hole on that task check out this medium article, it walks you thru all the requirements for creating a canvas element and then drawing on that canvas
Your also confirming that you're drawing at least 1 circle on browser refresh so i think all you need to focus on is 1)initiating your code on load and 2)a loop, and we'll just accept there is magic running in the background that will handle everything else.
At the bottom of the file you're working in add this:
// when the page loads call drawCircles(),
// i changed the name to be more descriptive and i'm passing in the number of circles i want to draw,
// the Boolean pertains to event bubbling
window.addEventListener("load", drawCircles(73), false);
In your drawCircles() method you're going to need to add the loop:
// im using a basic for loop that requires 3 things:
// initialization, condition, evaluation
// also adding a parameter that will let you determine how many circles you want to draw
function drawCircles(numCircles) {
for (let i = 0; i < numCircles; i++) {
ellipse(r_x, r_y, r_width, r_height);
fill(x, y, z);
}
}
here's a link to a codepen that i was tinkering with a while back that does a lot of the same things you are
I hope that helps - good luck on your new learning venture, it's well worth the climb!
Thank you so much for your help! What you say makes sense - I basically deleted the equivalent amount of code from a little training exercise downloaded through coursera, thinking that I could then essentially use it as an empty sandpit to play in. But there's clearly far more going on under the hood!
Thanks again!
The for loop does not run like I would expect it to. I would expect the for loop to run only once, but when I run it, it shows an animation.
The programming environment I am using:
https://www.khanacademy.org/computing/computer-programming/programming/arrays/pp/project-make-it-rain
var xPositions = [200];
var yPositions = [0];
draw = function() {
background(204, 247, 255);
for (var i = 0; i < xPositions.length; i++) {
noStroke();
fill(0, 200, 255);
ellipse(xPositions[i], yPositions[i], 10, 10);
yPositions[i] += 5;
}
};
When we say i++ then the condition i < xPositions.length is no longer true.
So why does the the loop run more than once?
I was told that because the draw function is called forever, the loop will also get called forever.
But, the second time the loop tries to run, the condition of the for loop is not met and therefore should not run.
Thanks.
Blockquote
.
Blockquote
I would expect the for loop to run only once
This is the correct expectation...with one addtion: the for loop will only run once per function call. If draw() is called more than once, then it will execute the loop every time you call the function.
draw() creates a single frame of the animation. In this case, you move the rain drop down 5 pixels and then render the frame with it at the new position. But to get the animation, you need to call draw() several times a second. This is similar to flipping the corners of your notebook with a slightly different version of a stick man drawn on each page to create an illusion of motion. The repeated calls to draw() are taken care of in your programming environment.
The for loop you write inside of draw() is intended to iterate over each raindrop. In this case, you only have one. I suggest adding 3 or 4 raindrops at different positions. Then you will see how the for loop iterates over each raindrop, moving them each down 5 pixels. Then your programming environment on Kahn Academy will call draw() several time per second for each frame in the animation.
The loop should run only once, but in processing js, the draw function is called forever.
I'm building a desktop application using Electron and Two.js. The application is basically a 2D level editor. There are Tile objects which represent 32x32 tiles in game. There's an array of Tile objects and I have tried to draw them using Two.JS but as it turns out twois not able to find renderable stuff which are not in the same scope as two.update()
I have tried creating a group using two.makeGroup() and then while looping Tile objects, i've tried adding them to the group using two.add(newRect). No luck.
Doing it this way doesn't work at all. (out of scope?)
for (let i = 0; i < tiles.length-1; i++) {
let rect = two.makeRectangle(tiles[i].getX(), tiles[i].getY(), size, size);
rect.fill = 'rgb('+tiles[i].color[0]+', '+tiles[i].color[1]+', '+tiles[i].color[2]+')';
rect.noStroke();
}
two.update();
but this way works quite fine for some reason
let rect = two.makeRectangle(tiles[i].getX(), tiles[i].getY(), size, size);
rect.fill = 'rgb('+tiles[i].color[0]+', '+tiles[i].color[1]+', '+tiles[i].color[2]+')';
rect.noStroke();
two.update();
My question is: How do I render multiple objects from different scopes?
(where it doesn't draw) this function is where im trying to draw from : https://github.com/idontreallywolf/gyarb_mapper/blob/750eddc6cbb179d359c47b4fc6711c9e79a0472d/renderer.js#L73
EDIT : (where it is able to draw)
I tried adding a random function, it worked with this one. For some reason the previous scope is not (detected?)
function troll(){
let rect = two.makeRectangle(100, 100, 100, 100);
rect.fill = "rgb(255,255,255)";
rect.stroke = "#f00";
}
troll();
two.update();
UPDATE:
Two.js wasn't able to draw them because the variables that I've used as parameters were inaccessible in that scope, so after doing some debugging I've solved the issue.
I am coding an application which displays balls obeying certain laws of physics.
So I have a Ball Object, and a path prototype. This path prototype calculates the coordinates of the ball at any given moment and draws it, that goes kinda like this :
Ball.prototype.path = function(v) {
modifying the ball coordinates...
ctx.arc(....);
(other canvas stuff)}
If I want to display an animation of the ball, I do this:
var ball1 = new Ball(...);
var ball2...
function loop () {
ctx.beginPath(); // The balls won't show up if I begin and close the Path in path(), I don't know why...
ball1.path();
ball2...
ctx.closePath();
};
setInterval(loop, 0.0015);
But I want to add a button which adds and displays balls. So I'm looking for a function which executes these commands to any ball added.
It's a little bit tricky, because it has to:
Create and name a new variable.
Execute path() according to that name.
All of that, in the same loop function so I can make a setInterval later.
EDIT: FIXED
#Frederik #Doorknob I've used a BallArray:
var BallArray = new Array(); i=0; function AddBallonClick() { i++; BalleArray.push(i) }; function loop() { for (var i=0;i<BalleArray.length;i++) { ctx.beginPath(); var ball = new Ball(0, 0, 40); ball.path(); ctx.closePath(); }; }; setInterval(loop, dt);
But I want to name the new variables ball_i, ie: ball_1, ball_2..., and I don't know how to. The script doesn't seem to be working even when I add the ball just once, so that's a problem too...
EDIT 2: FIXED
Also, I want to set an initial speed to every new ball, typically I do this:
ball.v = new V(...);
But now that I have an array, I added this to the loop, but it doesn't work...:
balles[i].v = new V(...)
EDIT 3:
I have another problem, whenever I click the button, a ball is not added and drawn, but instead the animation "replays". It seems that javascript can't draw balls at the same time with my kind of code:
function loop()
{
for(var i = 0; i < balls.length; i++) {
ctx.beginPath();
balls[i].path();
ctx.closePath();
}
};
setInterval(loop, dt);
EDIT: ALL FIXED
I've solved the last problem you just have to put the ctx.clearRect(0, 0, width, height) in the loop function but before the for(var i=0...).
Thanks to all of you :) !
As mentioned in the comments, the answer is arrays. You don't seem to quite understand them, so here's a quick overview. An array is sort of a list of objects. In your case, you probably want a list of Balls. You can initialize it like this:
var balls = []; // [] is about the same as new Array(), but more concise
To add a new ball to it, you can use push, passing it the ball:
balls.push(new Ball(/* ... */));
(You could, of course, pass it an already-existing ball, too:)
var ball = /* obtain ball from elsewhere */;
balls.push(ball);
You do seem to understand how to loop through the arrays, but not how to get the values as you loop through it. To loop through the array, you use a for loop:
for(var i = 0; i < balls.length; i++) {
// ...
}
Obviously, i will be an integer from 0 to balls.length. We can't do much with the number on its own, though; what we really want is the ball at that index in the array. You can do this by indexing the array. That would look like this:
var ball = balls[i];
Now ball contains the ball at position i in the balls array, and you can do whatever you want with it from there. In your case, you probably want to call path on it:
// If you've stored it into a variable as above:
ball.path();
// Or more concisely without having to store it into a variable:
balls[i].path();
With arrays, there is no need for variables named, e.g., ball_1, ball_2, etc. Instead, you just have an array, balls, and index it, e.g., balls[0], balls[1], etc.