I am experiencing an issue with a Quad Tree implementation I am working on in C#. In the file Tree.cs, the following line will cause a Stack Overflow Exception, starting consistently around 50 objects in the tree (probably enough to cause the first branch of the bottom right quad):
else
{
//bottom right
TreeList[3].PushB(b);
return;
}
For some reason it seems that, when I allow this code to be called, it creates an infinite loop, hence the Stack Overflow Exception. I am not seeing why this would cause an infinite recursion while the others don't.
Here's the code. Ball.cs and Tree.cs both reside in a Classes folder.
Ball.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QuadTree.Classes
{
class Ball
{
protected int x, y, r;
protected decimal vx, vy;
public static int min_w = 0,
max_w = 200,
min_h = 0,
max_h = 200;
//treating origin as top-left of screen
public Ball(int set_x = 1, int set_y = 1, decimal set_vx = 1, decimal set_vy = 1, int set_r = 1)
{
x = set_x;
y = set_y;
vx = set_vx;
vy = set_vy;
r = set_r;
}
public int get_x()
{
return x;
}
public int get_y()
{
return y;
}
public void Print()
{
Console.WriteLine("x: {0} y: {1} vx: {2} vy: {3} r: {4}", x, y, vx, vy, r);
}
//get the y-intercept of the current ball
protected decimal getB()
{
return (decimal)y - ((vy / vx) * (decimal)x);
}
//get the y-intercept given an x, y, and slope
public decimal getB(int x, int y, decimal m)
{
return (decimal)y - (m * (decimal)x);
}
//get the slop of the line that goes through both balls
protected decimal getM(Ball b)
{
return getM(y, b.y, x, b.x);
}
//get the slop of the line going through two points
public decimal getM(int y1, int y2, int x1, int x2)
{
if (x1 - x2 == 0)
{
return 0;
}
else
{
return ((decimal)(y1 - y2)) / ((decimal)(x1 - x2));
}
}
public void Move()
{
x += (int)vx;
y += (int)vy;
if (x > max_w)
{
vx *= -1;
x = x - (x - max_w);
}
else if (x < min_w)
{
vx *= -1;
x *= -1; //won't work if min_w != 0
}
if(y > max_h)
{
vy *= -1;
y = y - (y - max_h);
}
else if (y < min_h)
{
vy *= -1;
y *= -1; //won't work if min_h !=0
}
}
//detect if the current ball collides with the given ball
public void Collide(Ball b)
{
decimal d;
d = (decimal)Math.Sqrt(Math.Pow((x - b.x), 2) + Math.Pow((y - b.y), 2));
if (d<= r || d <= b.r)
{
ResolveCollision(b);
}
return;
}
//determine the resulting vectors after the collision
private void ResolveCollision(Ball b)
{
//get the line between the center points
decimal M;
M = getM(b);
//determine angle between the line and ball a
double theta_1;
if (b.vx != 0)
{
double top = (double)((M - (b.vy / b.vx)));
double bottom = (double)(1 + (M * (b.vy / b.vx)));
if (bottom != 0)
{
theta_1 = Math.Atan(top / bottom);
}
else
{
theta_1 = 0;
}
}
else
{
if (1 + M != 0)
{
theta_1 = Math.Atan((double)(M / (1 + M)));
}
else
{
theta_1 = 0;
}
}
theta_1 = theta_1 * (Math.PI / 180);
//calculate new vx and vy for ball a
//http://www.gamefromscratch.com/post/2012/11/24/GameDev-math-recipes-Rotating-one-point-around-another-point.aspx
double new_vx, new_vy;
new_vx = Math.Cos(theta_1) * (double)(vx) - Math.Sin(theta_1) * (double)(vy) + x;
new_vy = Math.Sin(theta_1) * (double)(vx) + Math.Cos(theta_1) * (double)(vy) + y;
vx = (decimal)new_vx - x;
vy = (decimal)new_vy - y;
//determine angle between the line and ball b
if (b.vx != 0)
{
double top = (double)((M - (b.vy / b.vx)));
double bottom = (double)(1 + (M * (b.vy / b.vx)));
if (bottom != 0)
{
theta_1 = Math.Atan(top / bottom);
}
else
{
theta_1 = 0;
}
}
else
{
if (1 + M != 0)
{
theta_1 = Math.Atan((double)(M / (1 + M)));
}
else
{
theta_1 = 0;
}
}
theta_1 = theta_1 * (Math.PI / 180);
//calculate new vx and vy for ball a
new_vx = Math.Cos(theta_1) * (double)(b.vx) - Math.Sin(theta_1) * (double)(b.vy) + b.x;
new_vy = Math.Sin(theta_1) * (double)(b.vx) + Math.Cos(theta_1) * (double)(b.vy) + b.y;
b.vx = (decimal)new_vx - x;
b.vy = (decimal)new_vy - y;
}
}
}
Tree.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QuadTree.Classes
{
class Tree //: IDisposable
{
protected int min_w,
max_w,
min_h,
max_h,
thresh_hold, level;
bool leaf = true;
protected List<Ball> BallList = new List<Ball>();
protected List<Tree> TreeList = new List<Tree>();
public Tree(int set_min_w, int set_max_w, int set_min_h, int set_max_h, int set_thresh_hold, int set_level)
{
min_w = set_min_w;
max_w = set_max_w;
min_h = set_min_h;
max_h = set_max_h;
thresh_hold = set_thresh_hold;
level = set_level;
}
//push a ball onto the tree
public void PushB(Ball b)
{
if(leaf)
{
BallList.Add(b);
if (BallList.Count > thresh_hold)
{
Branch();
}
}
else
{
LeafPush(b); //push the ball to a leaf node
}
return;
}
//push a ball onto a leaf of the current node
protected void LeafPush(Ball b)
{
if (b.get_x() <= max_w / 2)
{
//left
if (b.get_y() <= max_h / 2)
{
//top left
TreeList[0].PushB(b);
return;
}
else
{
//bottom left
TreeList[2].PushB(b);
return;
}
}
else
{
//right
if (b.get_y() <= max_h / 2)
{
//top right
TreeList[1].PushB(b);
return;
}
else
{
//bottom right
TreeList[3].PushB(b);
return;
}
}
}
private void Branch()
{
Console.WriteLine("Branching level {0}", level);
leaf = false;
TreeList.Add(new Tree(min_w, max_w / 2, min_h, max_h / 2, thresh_hold, level + 1)); //top left
TreeList.Add(new Tree((max_w / 2) + 1, max_w, min_h, max_h / 2, thresh_hold, level + 1)); //top right
TreeList.Add(new Tree(min_w, max_w / 2, (max_h / 2) + 1, max_h, thresh_hold, level + 1)); //bottom left
TreeList.Add(new Tree((max_w / 2) + 1, max_w, (max_h / 2) + 1, max_h, thresh_hold, level + 1)); //bottom right
foreach(Ball b in BallList)
{
LeafPush(b);
}
BallList.Clear();
return;
}
}
}
Program.cs
using QuadTree.Classes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace QuadTree
{
class Program
{
static void Main(string[] args)
{
Random rnd = new Random();
List<Ball> BallList = new List<Ball>();
for (int i = 0; i < 100; i++)
{
BallList.Add(new Ball(rnd.Next(Ball.min_w, Ball.max_w),
rnd.Next(Ball.min_h, Ball.max_h),
rnd.Next(1, 5),
rnd.Next(1, 5),
rnd.Next(1, 5)));
}
Tree t = new Tree(Ball.min_w, Ball.max_w, Ball.min_h, Ball.max_h, 10, 0);
foreach (Ball b in BallList)
{
b.Move();
t.PushB(b);
}
Console.ReadLine();
}
}
}