Multiple object intersection and removal

I am new to the discourse and not quite sure how to use it. But here I am. I am working on a ecosystem project, and I have an animal class with 2 different genders(0 for female, 1 for male). When 2 different genders intersect each other than I want to remove those 2 objects and add a couple(different object, static) object on that position. I kinda did it but it only works for a couple of seconds. Then the code just breaks. In the console it says,
“Uncaught TypeError: Cannot read property ‘intersects’ of undefined”

Here’s the sketch.js file:

var animats = [];

var couples = [];

function setup
createCanvas(400, 400);
				
for(var i = 0; i < 50; i++) {
animats[i] = new Animat(random(0, width), random(0, height));
}
}

function draw() {
background(255,100,100);
for(var i = animats.length-1; i >= 0; i--) {
								animats[i].birth();
								animats[i].grow();

for(var j = i; j >= 0; j--) {
													  if(j != i && animats[i].intersects(animats[j])) {
														    animats.splice(i, 1);
																  animats.splice(j, 1);
								
														}
}
}	

}

Here’s the animat class file:

function Animat(x, y) {
				this.x = x;
				this.y = y;
				
				this.gender;
				var g = random(0,1);
				var c;
								
								if(g > 0.5) {
												c = 0;
								    this.gender = 0;
								} else {
												c = 255;
								    this.gender = 1;
								}
				
				this.speed = 1;

				this.age = 0;
				this.length = 0.5;
				
				this.birth = function() {
								
								//gender
								
								//create
noStroke();
fill(c);
ellipse(this.x, this.y, this.length * 2, this.length * 2);
								
								
								//move
								
								switch(floor(random(0,4))) {
												case 0:
																this.x += this.speed;
break;
												case 1:
																this.y += this.speed;
break;
												case 2:
																this.x -= this.speedbreak;
												case 3:
																this.y -= this.speed;
break;
												default:
																this.x++;
																this.y--;
}
								
			     	//bounce
								
if(this.x > width || this.x < 4){
												this.speed *= -1;
}
if(this.y > height || this.y < 4){
												this.speed *= -1;
}
												
}
this.grow = function() {
this.age += 0.01;
this.length += 0.05;
								
								//age checks
								
if(this.age > 10) {
												this.speed + 5;
} else if(this.age > 21) {
												this.length = 25;
												this.speed = this.speed
												//console.log("max age:" + this.age)
} else if(this.age > 70) {
												//die
} else {
												
}
								
								//length checks 
								
if(this.length > 25) {
												this.length = 25;
											 //console.log("max length");
}
}
				
				//relationship
				
this.intersects = function(other) {
var d = dist(this.x, this.y, other.x, other.y);
var r = this.length + other.length;
								
if(d < r) {
												if(((this.gender == 0) && (other.gender == 1)) || ((this.gender == 1) && (other.gender == 0))) {
											     	return true;
												} else {
																this.speed *= -1;
												}
												
} else {
												return false;
}
}
				
				//mate
				
this.couple = function() {
								if(((this.gender == 0) && (other.gender == 1)) || ((this.gender == 1) && (other.gender == 0))) {
											     	return true;
												} else {
																this.speed *= -1;
												}
}
						
}

Here is the codepen link:

1 Like

Backwards loops are a great way to iterate & remove an array’s current item at the same time.

However you attempt to splice() the indices j & i from the same array at 1 swoop.

That makes all items inside that array to left-shift their indices 2 times while the outer loop is still dealing w/ its index i.

In short, we can’t use method splice() to delete more than 1 index from the same array on 1 single iteration.

As a workaround, rather than mutating the array inside that double loop, we merely collect on a separate array the indices marked to be removed later.

  • Let’s call that extra array toRemoveIndices[].
  • Before starting the double loop, we clear that array like this: toRemoveIndices.length = 0;
  • Now, inside the inner loop, we replace the splice() calling w/ toRemoveIndices.push(i, j);
  • Once the double loop is finished we can finally use splice() in order to delete each animat[]'s index matching the indices collected by toRemoveIndices[].
  • We can even create a function for that task. I’ve just named it as removeIndices().

The excerpt below are the changes I’ve made to your posted code:

const animats = [], toRemoveIndices = [];

function draw() {
  background(255, 100, 100);

  var j, i = animats.length;
  toRemoveIndices.length = 0;

  while (i--) {
    const a = animats[j = i];

    a.birth();
    a.grow();

    while (j--)  if (a.intersects(animats[j])) {
      toRemoveIndices.push(i, j);
      break;
    }
  }

  removeIndices(animats, toRemoveIndices);
}

function removeIndices(array, indices) {
  for (const index of indices)  array.splice(index, 1);
  return array;
}
2 Likes

Thank you so much sir. It indeed worked🙂