setInterval javascript memory leak - javascript

I can't figure out why the memory is increasing and it stays there each time I run this code:
easingFunction = function (t, b, c, d) {
if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b;
return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
}
processFrame = function () {
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
tile.percent += 4;
if (tile.percent > 0) {
var TH = Math.max(0, Math.min(TILE_HEIGHT, targetObj.height - tile.imageY));
var TW = Math.max(0, Math.min(TILE_WIDTH, targetObj.width - tile.imageX));
var SW, SH, SX, SY, amount;
draw.save();
draw.translate(tile.imageX, tile.imageY);
if (direction == "tb" || direction == "bt") {
amount = easingFunction(tile.percent, 0, TW, 100);
SW = Math.min(TW, amount);
SH = TH;
SX = 0;
SY = 0;
} else {
amount = easingFunction(tile.percent, 0, TH, 100);
SW = TW;
SH = Math.min(TH, amount);
SX = 0;
SY = 0;
}
draw.drawImage(copycanvas, tile.imageX, tile.imageY, SW, SH, SX, SY, SW, SH);
draw.restore();
}
}
var ok = true;
for (i = 0; i < tiles.length; i++) {
if (tiles[i].percent < 100) {
ok = false;
break;
}
}
if (ok) {
clearInterval(interval);
showComplete();
}
};
this.show = function (target, hideTarget) {
createTiles();
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
tile.percent = 0 - i * 10;
}
}
var intervalDelay = (config.duration * 1000) / (tiles.length * 3 + 25);
interval = setInterval(function () {
processFrame();
}, intervalDelay);
};
function Tile() {
this.imageX = 0;
this.imageY = 0;
this.percent = 0;
};
};
I left out some unimportant code. The ideea is that I call externally the show() function. The setInterval is initialized and runs processFrame() about 100 times.
I've tried to leave some code outside from processFrame, and I got to :
processFrame = function () {
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
tile.percent += 4;
}
var ok = true;
for (i = 0; i < tiles.length; i++) {
if (tiles[i].percent < 100) {
ok = false;
break;
}
}
if (ok) {
clearInterval(interval);
showComplete();
}
};
But the memory still increases.

Try validating your code with JSLint. http://www.jslint.com/
Right now your adding easingFunction & processFrame to the Global object (which isn't a good thing). Not that this is the cause of the problem, but I've found that mismanagement of my objects is the usual cause of memory leaks.
You'll want to do something like:
var MyObject = {};
MyObject.easingFunction = function(){};
MyObject.processFrame = function(){};
In short make sure you declare all objects with var before using them.

I found the problem. I was continuously redrawing the canvas. To resolve this problem I had to erase the canvas each time before modifying it.

Related

Why wont my function draw multiple bricks?

So I have this simple function that should show multiple bricks, but it only shows one. Anyone knows why? I doesn't show any errors.
function drawbricks() {
for (var r = 0; r < 12; r++) {
for (var c = 0; c < 6; c++) {
var posix = r + 20;
var posiy = c + 20;
ctx.fillRect(posix, posiy, 50, 50);
}
}
}
I've added some constants so you can configure your bricks.
const c = document.getElementsByTagName('canvas')[0];
const ctx = c.getContext('2d');
ctx.fillStyle = 'orange';
drawbricks(ctx);
function drawbricks(ctx) {
const brickCountRows = 10;
const brickCountColumns = 15;
const brickWidth = 20;
const brickHeight = 10;
const brickSpacingX = 2;
const brickSpacingY = 2;
for (var r = 0; r < brickCountRows; r++) {
for (var c = -1; c < brickCountColumns; c++) { // start at -1 to fill gap caused by offset (see below)
var posix = c * (brickWidth + brickSpacingX); // switched r by c (column is x component)
var posiy = r * (brickHeight + brickSpacingY); // switched c by r (row is y component)
// offset every other row by half brickWidth + brickSpacing
if (r%2 == 1) {
posix += Math.floor((brickWidth + brickSpacingX)/2)
}
ctx.fillRect(posix, posiy, brickWidth, brickHeight);
}
}
}
<canvas/>
I believe you ment to multiply the coordinates instead of adding.
var posix = r * 20;
var posiy = c * 20;
But still, the "bricks" will overlay each other.

Many bouncing balls in javascript

I've been trying to get this to work for a while and I've hit a block and can't get these balls to bounce off of each other and the walls. I'm trying to make essentially a virus simulator, with different balls having different properties determining infection chances whenever they contact each other. Here's the code I'm working with:
Molecule.js:
class Molecule {
constructor(_i, _max_rad){
this.moleculeIndex = _i;
this.rad = random(min_rad, _max_rad);
this.position = createVector(random(this.rad,width-this.rad),random(this.rad, height-this.rad));
this.velocity = createVector(random(-2,5),random(-2,5));
this.bounce = false;
}
render() {
noStroke();
if (this.bounce) {
let dx = this.position.x - molecules[moleculeIndex].position.x;
let dy = this.position.y - molecules[moleculeIndex].position.y;
let dist = Math.sqrt(dx * dx + dy * dy);
let normalX = dx / dist;
let normalY = dy / dist;
let midpointX = (this.position.x.x + molecules[moleculeIndexmoleculeIndex].position.x) / 2;
let midpointY = (this.position.x.y + molecules[moleculeIndex].position.y) / 2;
let dVector = (this.velocity.x - molecules[moleculeIndex].velocity.x) * normalX;
dVector += (this.velocity.y - molecules[moleculeIndex].velocity.y) * normalY;
let dvx = dVector * normalX;
let dvy = dVector * normalY;
this.velocity.x -= dvx;
this.velocity.y -= dvy;
molecules[moleculeIndex].velocity.x += dvx;
molecules[moleculeIndex].velocity.y += dvy;
}
push();
translate(this.position.x,this.position.y)
ellipse(0,0,this.rad*2,this.rad*2);
pop();
}
step() {
this.position.add(this.velocity);
}
checkEdges(){
if(this.position.x < this.rad || this.position.x > width-this.rad){
this.velocity.x = this.velocity.x * -1;
}
if(this.position.y < this.rad || this.position.y > height-this.rad){
this.velocity.y = this.velocity.y * -1;
}
}
}
Sketch.js:
let molecules = [];
let numOfMolecules = 100;
let min_rad = 10;
let max_rad = 50;
let row = 5;
let col = 5;
let gridHeight;
let gridWidth;
let moleculeKey;
let tempArray;
let intersectCount;
let numchecks;
let displayMolecules = true;
let draw_grid = true;
let display_info = true;
function setup() {
createCanvas(window.innerWidth, window.innerHeight); //create canvas size of screen
background(150, 178, 164);
createMolecules();
}
function createMolecules() {
molecules = [];
for (let i = 0; i < numOfMolecules; i++) {
molecules.push(new Molecule(i, max_rad));
}
}
function draw() {
background(150, 178, 164);
gridWidth = window.innerWidth / col;
gridHeight = window.innerHeight / row;
splitIntoGrids();
checkIntersections();
drawGrid();
renderGrid();
resetBalls();
}
function splitIntoGrids() {
moleculeKey = [];
for (let i = 0; i < row; i++) {
moleculeKey.push([]);
for (let j = 0; j < col; j++) {
moleculeKey[i].push([]);
molecules.forEach(molecule => {
if ((molecule.position.x + molecule.rad > j * gridWidth) &&
(molecule.position.x - molecule.rad < j * gridWidth + gridWidth) &&
(molecule.position.y + molecule.rad > i * gridHeight) &&
(molecule.position.y - molecule.rad < i * gridHeight + gridHeight)) {
moleculeKey[i][j].push(molecule.moleculeIndex);
}
});
}
}
}
/* Splits into grids and counts the molecules in each grid.
* Also checks molecules when overlapping between two cells
* Stores molecules in an array, to track the location of each molecule
*/
function checkIntersections() {
intersectCount = 0;
numchecks = 0;
for (let i = 0; i < moleculeKey.length; i++) {
for (let j = 0; j < moleculeKey[i].length; j++) {
// if a cell contains more than one molecule, store the molecules into temporary array
if (moleculeKey[i][j].length > 1) {
tempArray = moleculeKey[i][j];
// loops through each molecule in the temporary array
for (let k = 0; k < tempArray.length; k++) {
for (let l = k + 1; l < tempArray.length; l++) {
// calculate distance of the molecules between each other
let distanceMolecules = p5.Vector.sub(molecules[tempArray[k]].position, molecules[tempArray[l]].position);
let vectorLength = distanceMolecules.mag();
numchecks++;
//checks if molecules are intersecting
if (vectorLength < molecules[tempArray[k]].rad + molecules[tempArray[l]].rad) {
molecules[tempArray[k]].bounce = true;
molecules[tempArray[l]].bounce = true;
intersectCount++;
}
}
}
}
}
}
}
function drawGrid() {
if (draw_grid) {
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
stroke(255);
noFill();
rect(j * gridWidth, i * gridHeight, gridWidth, gridHeight);
fill(255);
textSize(12);
text(moleculeKey[i][j].length, j * gridWidth + 10, i * gridHeight + gridHeight - 10);
}
}
}
}
function resetBalls() {
for (let i = 0; i < moleculeKey.length; i++) {
for (let j = 0; j < moleculeKey[i].length; j++) {
if (moleculeKey[i][j].length > 1) {
tempArray = moleculeKey[i][j];
//console.log(tempArray);
for (let k = 0; k < tempArray.length; k++) {
for (let l = k + 1; l < tempArray.length; l++) {
let distanceMolecules = p5.Vector.sub(molecules[tempArray[k]].position, molecules[tempArray[l]].position);
let vectorLength = distanceMolecules.mag(); //get the length of vector
//checks if molecules are not intersecting
if (!vectorLength < molecules[tempArray[k]].rad + molecules[tempArray[l]].rad) {
//change back color
molecules[tempArray[k]].bounce = false;
molecules[tempArray[l]].bounce = false;
}
}
}
}
}
}
}
function renderGrid() {
molecules.forEach(molecule => {
if (displayMolecules) {
molecule.render();
}
molecule.checkEdges();
molecule.step();
});
}
With this code I get an error 'Uncaught ReferenceError: moleculeIndex is not defined' on the first line of the if(bounce), I've tried replacing moleculeIndex with some other things but really it was just hoping. Does anyone know why I'm having this problem?
Thanks.
Try replacing the: moleculeIndex with this.moleculeIndex

JS (p5.js) canvas multiplying bug and being drawn in weird resolution

I'm experiencing a bug in my code that doubles the display and ends up drawing it twice, besides itself in a weird resolution. After changing a global variable (total_sand) to a different integer, and changing it back, the display has actually tripled.
The intended result is for it to display just once in full resolution (in this case, 301x301 pixels). This bug didn't happen immediately and seemed to happen randomly upon changing the code one day.
total_sand = 100000;
sandpiles = [];
var next_sandpiles;
function setup() {
createCanvas(301, 301);
for (var x = 0; x < width; x++) {
sandpiles[x] = [];
for (var y = 0; y < height; y++) {
sandpiles[x][y] = 0;
}
}
next_sandpiles = sandpiles;
//STARTING CONDITIONS
if (width % 2 == 0) {
sandpiles[width/2][height/2] = total_sand;
} else {
sandpiles[(width-1)/2][(height-1)/2] = total_sand;
}
}
function topple() {
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
if (sandpiles[x][y] > 3) {
toppling = true;
next_sandpiles[x][y] = next_sandpiles[x][y] - 4;
if (x > 0) {
next_sandpiles[x-1][y]++;
}
if (x < width - 1) {
next_sandpiles[x+1][y]++;
}
if (y > 0) {
next_sandpiles[x][y-1]++;
}
if (y < height - 1) {
next_sandpiles[x][y+1]++;
}
}
}
}
sandpiles = next_sandpiles;
}
function update() {
loadPixels();
var r;
var g;
var b;
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
if (sandpiles[x][y] == int(0)) {
r = 255;
g = 255;
b = 0;
} else if(sandpiles[x][y] == 1) {
r = 0;
g = 185;
b = 63;
} else if(sandpiles[x][y] == 2) {
r = 0;
g = 104;
b = 255;
} else if(sandpiles[x][y] == 3) {
r = 122;
g = 0;
b = 229;
} else {
r = 255;
g = 0;
b = 0;
}
var index = (x + y * width)*4;
pixels[index] = r;
pixels[index+1] = g;
pixels[index+2] = b;
pixels[index+3] = 255; // alpha
}
}
updatePixels();
}
function draw() {
background(0);
topple();
update();
}

Separating Axis Theorem Implementation always returning true. What am I doing wrong?

I'm trying to implement collision detection for concave polygons in Javascript/p5.js using the Separating Axis Theorem. I've been following the following tutorial on how to use it: http://www.dyn4j.org/2010/01/sat/
However, my check is always returning true, no matter the positioning of the two polygons. Here's my code:
function SAT(shape1, shape2)
{
let axes1 = getAxes(shape1);
let axes2 = getAxes(shape2);
let colliding = true;
for (let i = 0; i < axes1.length; i++)
{
let axis = axes1[i];
let p1 = shape1.project(axis);
let p2 = shape2.project(axis);
if (!p1.overlap(p2)) colliding = false;
}
for (let i = 0; i < axes2.length; i++)
{
let axis = axes2[i];
let p1 = shape1.project(axis);
let p2 = shape2.project(axis);
if (!p1.overlap(p2)) colliding = false;
}
return colliding;
}
function getAxes(shape)
{
let axes = [];
for (let i = 0; i < shape.vertices.length; i++)
{
let p1 = shape.vertices[i];
let p2 = shape.vertices[i + 1 == shape.vertices.length ? 0 : i + 1];
let edge = p1.sub(p2);
let normal = createVector(-edge.y, edge.x);
axes[i] = normal;
}
return axes;
}
class Projection
{
constructor(min, max)
{
this.min = min;
this.max = max;
}
overlap(other)
{
if (this.max < other.min || other.max < this.min) return false;
else return true;
}
}
class PolygonCollider extends Component
{
constructor(gameObject)
{
super(gameObject);
this.untransformedVertices = [];
this.vertices = [];
...
}
setVerts(verts)
{
if (verts && verts.length > 2)
{
this.untransformedVertices = verts;
this.vertices = this.transform.getTransformedPoints(verts);
return this;
}
else return false;
}
project(axis)
{
let min = axis.dot(this.vertices[0]);
let max = min;
for (let i = 1; i < this.vertices.length; i++)
{
let p = axis.dot(this.vertices[i]);
if (p < min) min = p;
else if (p > max) max = p;
}
return new Projection(min, max);
}
update()
{
this.vertices = this.transform.getTransformedPoints(this.untransformedVertices);
}
...
}
Vertices are transformed with the following function, using a defined scale, rotation and position:
getTransformedPoints(points)
{
let transformed = [];
for (let i = 0; i < points.length; i++)
{
let rX = ((this.scale.x * points[i].x) * Math.cos(this.rotation)) - ((this.scale.y * points[i].y) * Math.sin(this.rotation));
let rY = ((this.scale.x * points[i].x) * Math.sin(this.rotation)) + ((this.scale.y * points[i].y) * Math.cos(this.rotation));
transformed[i] = createVector(rX + this.position.x, rY + this.position.y);
}
return transformed;
}
The SAT method is always returning true. I believe I'm checking for the overlap incorrectly, but I can't figure out what exactly I'm doing wrong.
So, it turns out the issue with my implementation lied with p5.js, a library I am using in this case.
In the getAxes method, I was subtracting p1 from p2 using p5's built in p5.Vector.sub function. This didn't have the desired effect. I'm not sure, but I believe the issue was that it wasn't creating a new vector that was the difference of the equation. I fixed this simply by creating the new vector myself as such: createVector(p2.x - p1.x, p2.y - p1.y);

Creating svg paths with javascript(shape morphing)

So I have this class which is used for shape morphing:
class ShapeOverlays {
constructor(elm) {
this.elm = elm;
this.path = elm.querySelectorAll('path');
this.numPoints = 18;
this.duration = 600;
this.delayPointsArray = [];
this.delayPointsMax = 300;
this.delayPerPath = 100;
this.timeStart = Date.now();
this.isOpened = false;
this.isAnimating = false;
}
toggle() {
this.isAnimating = true;
const range = 4 * Math.random() + 6;
for (var i = 0; i < this.numPoints; i++) {
const radian = i / (this.numPoints - 1) * Math.PI;
this.delayPointsArray[i] = (Math.sin(-radian) + Math.sin(-radian * range) + 2) / 4 * this.delayPointsMax;
}
if (this.isOpened === false) {
this.open();
} else {
this.close();
}
}
open() {
this.isOpened = true;
this.elm.classList.add('is-opened');
this.timeStart = Date.now();
this.renderLoop();
}
close() {
this.isOpened = false;
this.elm.classList.remove('is-opened');
this.timeStart = Date.now();
this.renderLoop();
}
updatePath(time) {
const points = [];
for (var i = 0; i < this.numPoints + 1; i++) {
points[i] = ease.cubicInOut(Math.min(Math.max(time - this.delayPointsArray[i], 0) / this.duration, 1)) * 100
}
let str = '';
str += (this.isOpened) ? `M 0 0 V ${points[0]} ` : `M 0 ${points[0]} `;
for (var i = 0; i < this.numPoints - 1; i++) {
const p = (i + 1) / (this.numPoints - 1) * 100;
const cp = p - (1 / (this.numPoints - 1) * 100) / 2;
str += `C ${cp} ${points[i]} ${cp} ${points[i + 1]} ${p} ${points[i + 1]} `;
}
str += (this.isOpened) ? `V 0 H 0` : `V 100 H 0`;
return str;
}
render() {
if (this.isOpened) {
for (var i = 0; i < this.path.length; i++) {
this.path[i].setAttribute('d', this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * i)));
}
} else {
for (var i = 0; i < this.path.length; i++) {
this.path[i].setAttribute('d', this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * (this.path.length - i - 1))));
}
}
}
renderLoop() {
this.render();
if (Date.now() - this.timeStart < this.duration + this.delayPerPath * (this.path.length - 1) + this.delayPointsMax) {
requestAnimationFrame(() => {
this.renderLoop();
});
}
else {
this.isAnimating = false;
}
}
}
(function() {
const elmHamburger = document.querySelector('.hamburger');
const gNavItems = document.querySelectorAll('.global-menu__item');
const elmOverlay = document.querySelector('.shape-overlays');
const overlay = new ShapeOverlays(elmOverlay);
elmHamburger.addEventListener('click', () => {
if (overlay.isAnimating) {
return false;
}
overlay.toggle();
if (overlay.isOpened === true) {
elmHamburger.classList.add('is-opened-navi');
for (var i = 0; i < gNavItems.length; i++) {
gNavItems[i].classList.add('is-opened');
}
} else {
elmHamburger.classList.remove('is-opened-navi');
for (var i = 0; i < gNavItems.length; i++) {
gNavItems[i].classList.remove('is-opened');
}
}
});
}());
Can some one please explain this code? I don't really get how the paths are created using time,how the points are placed and how could I modify it.What is range used for? Why are trigonometral functions used for the delayPointsArray?
Basically it's this part that I don't get:
updatePath(time) {
const points = [];
for (var i = 0; i < this.numPoints + 1; i++) {
points[i] = ease.cubicInOut(Math.min(Math.max(time - this.delayPointsArray[i], 0) / this.duration, 1)) * 100
}
let str = '';
str += (this.isOpened) ? `M 0 0 V ${points[0]} ` : `M 0 ${points[0]} `;
for (var i = 0; i < this.numPoints - 1; i++) {
const p = (i + 1) / (this.numPoints - 1) * 100;
const cp = p - (1 / (this.numPoints - 1) * 100) / 2;
str += `C ${cp} ${points[i]} ${cp} ${points[i + 1]} ${p} ${points[i + 1]} `;
}
str += (this.isOpened) ? `V 0 H 0` : `V 100 H 0`;
return str;
}
render() {
if (this.isOpened) {
for (var i = 0; i < this.path.length; i++) {
this.path[i].setAttribute('d', this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * i)));
}
} else {
for (var i = 0; i < this.path.length; i++) {
this.path[i].setAttribute('d', this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * (this.path.length - i - 1))));
}
}
}
Why is time being used? What is the purpose of this:
points[i] = ease.cubicInOut(Math.min(Math.max(time - this.delayPointsArray[i], 0) / this.duration, 1)) * 100
If you look at how updatePath() is being called, it's like this:
this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * i))
So the time value passed in is the difference between the current time, and the start time of the path we are working with.
So what then is the line of code you are interested in, doing?
points[i] = ease.cubicInOut(Math.min(Math.max(time - this.delayPointsArray[i], 0) / this.duration, 1)) * 100
I'm going to ignore delayPointsArray. It is modifying the start time slightly based on angle. Without seeing the full demo, I'm not sure of the reason for that.
The purpose of this line of code is to calculate how far through the current path's animation we are. The result is in the form of a coordinate value from 0 to 100.
It's doing a lot in that one line of code. So let's break down the individual steps.
Firstly, we are clamping the elapsed time to minimum of 0.
Math.max(time, 0)
In other words, anything before the animation start time becomes zero.
Then we divide by the animation's duration.
Math.max(time, 0) / duration
This will result in a value from 0, representing the start of the animation, to 1, representing the end of the animation. However, the value might also be greater than 1 if the elapsed time is after the end of the animation. Hence the next step.
Now clamp this value to a maximum of 1.
Math.min( Math.max(time, 0) / duration, 1)
We now have a value >= 0 and <= 1 whichdescribes where in the course of the animation, the path is supposed to be. 0 if we should be at the animations start position. 1 if we should be at the animations end position. And somewhere in between if the animation is in progress.
However this value is strictly linear, corresponding with the progression of time. And usually linear movement is not what you want. It is unnatural. Objects accelarate when the start moving and decelerate when the come to a stop. That will be what the easeInOut() function will be doing. If you are not familiar with easing curves, take a look at the diagram below.
Source: Google: The Basics of Easing
So we pass in a linear time value from 0..1 (horizontal axis). It will return a modified value that takes into account acceleration and deceleration.
The final step is to multiply by 100, to convert to a final coordinate value (0..100).
Hope this helps.

Categories

Resources