P5.js Drawing new shapes is causing the older shapes to disappear - javascript

i’m working on a complex visualisation project, using p5.js i want to create multiple clusters of shapes to visualise all sorts of data, so i’m looping through the data, checking the objects specified by user and drawing them to canvas, this happens in a nested for loop…the problem i’m facing is when an inner loop is done drawing a cluster of shapes and the overall loop continues and re-enters the inner loop again to draw the next cluster, the older cluster of drawn shapes disappears…i don’t understand why, i thought this had to do with that the shapes are related to the inner loop context/scope and tried to recreate the problem based on that but it appears that this is not the case.
this code for example works perfectly, it draws 5 circles on 20 lines, run by a nested loop
function setup() {
createCanvas(window.innerWidth, window.innerHeight).parent('myContainer');
}
function draw() {
background(220);
x = 100;
y = 100;
for (let i = 1; i <= 20; i++) {
for(let j=1;j<=5;j++){
fill(color(0, 255, 0));
stroke(0);
ellipse(x, y, 80);
x += 90;
}
x = 100;
y += 90;
}
}
while in my code, after the inner loop moves to the next cluster the previous shapes just disappear and only the last cluster is shown…i did a lot of console logging and debugging and made sure that every cluster is actually getting drawn but it just disappears when a new one is created…can anyone please help?
is there anyway to emphasise the library to draw a NEW separate shape…like this?
new ellipse(x,y,rad)
this is my project’s code, it shows where the bug happens
function setup() {
createCanvas(viewWidth, viewHeight).parent('myContainer');
}
function draw() {
//code...
var objects_len = objects.length;
for (var cluster_index = 1; cluster_index < cluster_len; cluster_index++) {
array_center = partition(cluster_index - 1, cluster_len - 1);
for (var object_index = 0; object_index < objects_len; object_index++) {
if (!objects[object_index].deleted) {
draw_simple_object(objects[object_index], data, cluster_index, array_center, cluster_len);
}
}
addClusterName(cluster_index, data, array_center);
}
function draw_simple_object(object, data, cluster_index, array_center, cluster_len) {
.
.
.
//this is where the drawing happens
var center_shape = [array_center[0] + object.centerOffsetX * array_center[2], array_center[1] + object.centerOffsetY * array_center[3]];
if (object.shape === 'ellipse' && enable) {
var size_min = Math.min(array_center[2], array_center[3]) * 2;
fill(colorFill);
stroke(object.colorBorder);
ellipse(center_shape[0], center_shape[1], size_min * width * proportion, size_min * length * proportion);
}
.
.
.
}
}
please help me

Can it be because you are calling the background() function inside the loop? (that part is actually not shown in your snippet)

Solved.
i was using resizeCanvas() in one of the function inside draw() which cleared all content off the canvas before resizing. removing resizeCanvas solves the problem.

Related

How do I generate a random X value for each "projectile" in my falling objects game using Javascript?

I am coding a game that is currently in its very early stages for a project to try to learn more about coding. In my game, objects generate randomly (green squares), and the player (red square), avoids them. I am having trouble trying to get the green squares to generate from a random position on the x-axis. I already have a formula to generate a random number for X, but after it selects a number randomly, all the "projectiles" generate there, rather than all generating from a different area. How would I get all the "projectiles" to generate from different positions on the x-axis randomly?
var randomX = Math.floor(Math.random() * 480) + 15;
function updateGameArea() {
var x, y;
for (i = 0; i < projectiles.length; i += 1) {
if (player.crashWith(projectiles[i])) {
gameArea.stop();
return;
}
}
gameArea.clear();
gameArea.frameNo += 1;
if (gameArea.frameNo == 1 || everyinterval(150)) {
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
for (i = 0; i < projectiles.length; i += 1) {
projectiles[i].y += -1; // the shape is using its coordinates to build downward from its y position
projectiles[i].update();
}
player.newPos();
player.update();
}
function everyinterval(n) {
if ((gameArea.frameNo / n) % 1 == 0) {return true;}
return false;
Expected: Green squares generate in random positions on the x- axis every 3 seconds and move upwards
Actual: Green squares all generate from the same random position on the X-axis.
You should reset X every time you're adding a new projectile:
if (gameArea.frameNo == 1 || everyinterval(150)) {
randomX = Math.floor(Math.random() * 480) + 15;
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
Otherwise, the randomX value stays constant as the value originally evaluated on line 1 when the interpreter reached it.
Here's your problem:
var randomX = Math.floor(Math.random() * 480) + 15;
// Generates a random number and stores it to randomX
// Called using 'randomX'
You need to turn it into a function if you want it to run each time:
var randomX = function() { Math.floor(Math.random() * 480) + 15 };
// Returns a new number each time
// Called using 'randomX()'
Both shivashriganesh mahato and natelouisdev have, essentially responded to how to fix the issue but since you are learning coding here is a tip. When you code, the code will run in a particular order. If you want something to be reassigned repeatedly, in this case a randomized number being used, and you want it to occur only after an event, you need to make sure that it gets trigger within each event.
natelouisdev has a good approach because, by using it as a function, you can call your randomizer more cleanly in your code and make it reassign the value of x each time.
Since you are building a game, it is also a good idea to compartmentalize your code. It'll make it easier to keep your ideas in order for each event trigger.
Example:
function gameLoss(){} - Define event return upon game loss. You can
then create editable rules to reason for loss without having to edit
the loss
function gameActive(){} - Defines what is normal gameplay. everything that occurs during normal gameplay should be managed here.
function gameArea(){} - Defines game canvas that function more for UI than for gameplay (scores, lifes, size of screen, etc)
Had you created individual functions you'd realize you only need a randomized 'x' value upon regular play thus you'd assign it within the gameActive() function and not as a global variable. Then you'd call the gameActive() function as many times as needed within a time interval to ensure a unique value is created each time.
-Side note: Don't litter unnecessary global variables. It'll make a mess off of your code when debugging. -

Javascript canvas images are coming out blocky when stacking on top of each other

So I'm randomly generating two types of clouds, and the clouds are intended to stack on top of each other a bit and move slowly (like clouds irl would).
So essentially I'm drawing a cloud image, clearing the area, moving the image over (by recreating it in a different position), and creating another cloud and repeating the process, but when the clouds are stacking on top of each other I'm getting a square area in between the stacked clouds. Not sure how to fix it.
Check this out: (see how the clouds have a block-like look as they stack over eachother)
http://testcloudsmc.bitballoon.com
Here's the full code:
https://repl.it/FTo8/3
And here's the most relevant parts of the code:
------------------------------------------------
var canvas; var ctx; var frameRate; var assets = [];
window.onload = function(){
canvas = document.getElementById('mycanvas');
ctx = canvas.getContext('2d');
frameRate = 1000/30; //30 fps
//Decides on a x position for clouds:
for(var f = 0;f<Math.floor(Math.random()*40)+10;f++){
cloudsarrayxpos[f] = Math.floor(Math.random()*800)+10;
}
/* Move/Render images */
for (var a = 0; a < 21; a++){
assets[a] = document.getElementById(a);
console.log(assets[a]);
};
/* CREATE OBJECTS FUNCTIONS: */
var clouds = function(x,y,h,w){
ctx.clearRect(x,y,w,h);
if((x>300)||(x<100)){
ctx.drawImage(assets[7],x,y,w,h);
}else{
ctx.drawImage(assets[20],x,y,w,h);
}
} //end of clouds function
function animate(){
for(var g = 0; g<cloudsarrayxpos.length;g++){
clouds(cloudsarrayxpos[g],(5*g),300,200);
cloudsarrayxpos[g]+=(Math.random()*.1)+.05; //changing the x position of each cloud in the array making it appear to be moving
}
window.requestAnimationFrame(animate);
}
}//end of window.onload
The problem is that you are clearing the canvas in each cloud spot before drawing it.
That means that the transparency (alpha) values of the cloud images don't come into play as each resulting pixel is composed only of the last image that was drawn on it.
To fix this, only clear the canvas once per animation cycle.
Specifically:
Remove ctx.clearRect from clouds function
Add this at the top of the animate function:
ctx.clearRect(0, 0, 1300, 600);

Drawing any function's graph using Javascript

I'm taking a javascript class for my bachelor in CS, and the IDE we're using (it's an educative IDE developed by one of the graduates here, called codeboot) has a "turtle drawing" feature. Basically, it works using several basic commands;
Forward; fd(x), Back; bk(x), rotate right; rt(x), rotate left; lt(x), pen up; pu() and pen down; pd(). Where x represent pixels.
For example, to have it draw a circle, one would write something like:
for (var n = 0; n < 360; n++) {
fd(1);
rt(1);
}
I'm trying to have it draw graphs for basic functions,but the only way I found how to do it is to have it draw a single point for each 0.005x (so it looks like a continuous line when zoomed out), but it makes the execution extremely slow, and you can kind of see it's not legit. How would I go about having just a continuous line instead of individually plotting each point?
Screenshot of what it looks like using my first technique:
Screenshot of what it looks like using my first technique:
and the JS code for said technique:
var x = -9;
var traceDown = function(d) {
var y = Math.cos(x) * 30;
setpw(3);
pu();
fd(y);
pd();
fd(1);
pu();
bk(1 + (y));
rt(90);
fd(d);
lt(90);
pd();
x += 0.0005;
};
pu();
lt(90);
fd(90);
lt(90);
fd(45);
pd();
rt(180);
while (x < 9) {
traceDown(0.005);
}

Rendering too many points on Javascript-player

As part of a project, I have to render a video on a JS-player from a text file which has the points - all the changed coordinates along-with the color in each frame. Below is the code I'm using to draw these point on the screen.
But the issue is that the number of changed pixels in each frame are as high as ~20,000 and I need to display these in less than 30ms (inter-frame time difference). So, when I run this code the browser hangs for almost each frame. Could someone suggest an improvement for this?
Any help is really appreciated.
c.drawImage(img,0,0,800,800);
setInterval(
function(){
while(tArr[index]==time) {
var my_imageData = c.getImageData(0,0,width, height);
color(my_imageData,Math.round(yArr[index]),Math.round(xArr[index]),Math.round(iArr[index]),255);
c.putImageData(my_imageData,0,0);
index=index+1;
}
time = tArr[index];
}
,30);
xArr, yArr, iArr, tArr are arrays of x-coordinate, y-coordinate, intensity value and time of appearance respectively for the corresponding point to be rendered
function color(imageData,x,y,i,a){ //wrapper function to color the point
var index = (x + y * imageData.width) * 4;
imageData.data[index+0] = i;
imageData.data[index+1] = i;
imageData.data[index+2] = i;
imageData.data[index+3] = a;
}

canvas class javascript

I'm having trouble with my javascript code. I'm trying to create a moving set of circles where each circle has their own attributes. So far I've managed to input all the needed values into an array, but I can't figure out how to use them properly for drawing on canvas.
Here's the javascript:
var radius = 10;
var step = x = y = 0;
var r = g = b = 255;
var circleHolder = [];
var loop = setInterval(function(){update();}, 30);
function Circle(x, y, radius, r, g, b)
{
this.x = x;
this.y = y;
this.radius = radius;
this.r = r;
this.g = g;
this.b = b;
circleHolder.push(this);
}
Circle.prototype.draw = function()
{
Circle.prototype.ctx = document.getElementById("MyCanvas").getContext("2d");
Circle.prototype.ctx.clearRect(0,0,720,720); // clear canvas
Circle.prototype.ctx.beginPath();
Circle.prototype.ctx.strokeStyle = "rgb("+ this.r +", "+ this.g +", "+ this.b +")";
Circle.prototype.ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
Circle.prototype.ctx.stroke();
}
Circle.prototype.update = function ()
{
step += .02;
step %= 2 * Math.PI;
this.x = parseInt((Math.sin(step)) * 150) + 360;
this.y = parseInt((Math.cos(step)) * 150) + 360;
this.radius += 16;
if (this.radius > 200)
{
for (i in circleHolder)
{
if (circleHolder[i]==this)
{
circleHolder.splice(i, 1);
}
}
}
}
function update()
{
var ci = new Circle(x, y, radius, r, g, b);
for (i in circleHolder)
{
ci = circleHolder[i];
ci.update();
ci.draw();
}
}
I'm pretty sure my problem lies within update() {} but I can't figure out how to do it properly.
EDIT: Okay, I've got it working with some changes! Check this Fiddle! I'm getting "ci not defined" error in the console though, and it has a strange bug: Changing the "if (this.radius > 128)" to higher integer it will make the circles spin faster, I don't know why. If you want you can try to change it to 256 and see what happens.
for (var i=0; i < allCircles; i++)
{
ci = circleHolder[i]; <----- This is causing the error
ci.update();
ci.draw();
}
it's not 100% clear to me what you're trying to do, but I tried to fix the main problem
One problem is your for loop.. you shouldn't use for in for arrays, do this instead:
for (var i=0 ; i<circleHolder.length ; i++)
{
ci = circleHolder[i];
ci.update();
ci.draw();
}
see this fiddle
Also I moved your get context and other things that should happen only once into the constructor, instead of having it in the update function.
You're also clearing the canvas before each draw, so the it will only show the last drawn circle per frame. (if you remove the clearRect it looks like one of those old spirographs).
You were also drawing the circles with (255,255,255)(white) so it wasn't showing until the color was changed.
Edit:
Really there are a few problems with this code:
The context shouldn't be inside a circle class if you plan on having many of them.
You should have some object which contains the canvas/context and an array of all circles.
Then have that object manage the updating/drawing.
For starters, unless there's something else going on, outside of this code:
You are using for ... in ... on an array, for-in is for objects, when used on arrays, most browsers will include methods like .splice and .forEach, and not just the numeric 0...n index.
function splice () {}.draw(); doesn't end well.
Also, what is the colour of your page's background? You're setting the rgb colour of each circle to 100% white. You're also clearing the canvas... ...which might well mean that the whole thing is transparent. So if you've got a transparent canvas, white circles and a white background, chances are great you're not going to be seeing anything at all, if this is even working without spitting out an error.
It might make a lot more sense to move your logic around in a way that lets you follow what's going on.
If you make a circle constructor, don't have it do anything but make a new circle.
Inside of your update, create a circle.
THEN put it inside of your circle collection (not in the circle constructor).
In a large application, you will typically call update on ALL objects, and then call draw on ALL objects, rather than updating and drawing one at a time.
Imagine a game that didn't bother to check if you had been hit by a bullet before drawing you and letting you move, for instance.
So inside of your loop, you should have an update and a draw.
Inside of the update, create your circles add them to the list and update the positions of them.
Inside of the draw, draw the circles.
In the future, this will give you the benefit of having things like collision-detection, without having to redraw everything, multiple times per frame.
Also, don't do DOM-access inside of a function that's going to be called many, many times (Circle.draw).
That will demolish your framerate in the future.
Instead, pass the function a dependency (the canvas).
// inside of the main game's scope
var screen = document.getElementById(...).getContext("2d");
// inside of your new draw function, in the animation/game loop
var i = 0, allCircles = circleHolder.length, currentCircle;
for (; i < allCircles; i += 1) {
currentCircle = circleHolder[i];
currentCircle.draw(screen);
}

Categories

Resources