3

UPDATES; -changing j=0 to j=i allowed up to 700 particles with a smooth frame rate

I am trying to simulate 2D water with hundreds of particles that have Vector2s declaring their position and a Vector2 for their velocity.

When it comes to collision detection, my program dislikes having more than 450 particles despite only using Pythagoras' theorem.

Here's the collision detection in the main class;

        for (int i = 0; i < particleList.Count; i++)
            {
                for (int j = 0; j < particleList.Count; j++)
                {
                    if (distanceBetween(particleList[i].position, particleList[j].position) < reactDistance)
                    {
                        if (particleList[i].position.X > particleList[j].position.X) //x axis
                        {
                            particleList[i].velocity.X += repelSpeed;
                            particleList[j].velocity.X -= repelSpeed;

                            particleList[i].position.X -= attractSpeed;
                            particleList[j].position.X += attractSpeed;
                        }
                        else
                        {
                            particleList[i].velocity.X -= repelSpeed;
                            particleList[j].velocity.X += repelSpeed;

                            particleList[i].position.X += attractSpeed;
                            particleList[j].position.X -= attractSpeed;
                        }

                        if (particleList[i].position.Y > particleList[j].position.Y) //y axis
                        {
                            particleList[i].velocity.Y += repelSpeed;
                            particleList[j].velocity.Y -= repelSpeed;

                            particleList[i].position.Y -= attractSpeed;
                            particleList[j].position.Y += attractSpeed;
                        }
                        else
                        {
                            particleList[i].velocity.Y -= repelSpeed;
                            particleList[j].velocity.Y += repelSpeed;

                            particleList[i].position.Y += attractSpeed;
                            particleList[j].position.Y -= attractSpeed;
                        }
                    }
                }
            }

Here's the distanceBetween(v1, v2) method;

        public float distanceBetween(Vector2 a, Vector2 b)
    {
        float xDist, yDist, distTo;
        if (a.X > b.X) //x axis
        {
            xDist = a.X - b.X;
        }
        else
        {
            xDist = b.X - a.X;
        }

        if (a.Y > b.Y) //y axis
        {
            yDist = a.Y - b.Y;
        }
        else
        {
            yDist = b.Y - a.Y;
        }
        distTo = (float)(Math.Sqrt((xDist * xDist) + (yDist * yDist)));
        return distTo;
    }

Vector2.Distance(v1, v2) produces no visible performance changes.

If you're wondering what on Earth attractSpeed does; it's my poor attempt at trying to form collections of water. I'm not sure how to do it.

Ultimately I want something like this: http://grantkot.com/MPM/Liquid.html

ShadowByte
  • 83
  • 10
  • 1
    Since the first loop particles have already been checked can't the second loop start as j=i rather than j=0? – Mike B Nov 02 '13 at 15:40
  • Wow, you're right. The program now allows up to 700 particles with a smooth frame rate! – ShadowByte Nov 02 '13 at 15:42
  • 1
    You could also try to do that in parallel. http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallel.aspx – Lasse Nov 02 '13 at 15:42
  • It seems unclear as to how I can pass i and j into the method when they aren't declared in Parallel.For(0, particleList.Count, collision()); – ShadowByte Nov 02 '13 at 15:53
  • See the examples on the bottom of the page I linked. The lambda expression could very well work for you. – Lasse Nov 02 '13 at 16:10
  • 'Parallel.For(0, N, i => { // Do Work. });' What's actually going on here? The page doesn't explain bit-by-bit what it actually does. – ShadowByte Nov 02 '13 at 16:21
  • `Parallel.For` takes `start, end, loop`. the equivalent `for` loop would be: `for(int i = start; i < N; i++) { loop(i); }`. The advantage is, that `Parallel.For` can execute `loop` in multiple threads instead of just one, speed up the process. – Femaref Nov 02 '13 at 16:24
  • But how do I actually write that with i and j? – ShadowByte Nov 02 '13 at 19:33
  • `Parallel.For(start, end, i => { //i is used here just like in normal for loop. });` – Lasse Nov 02 '13 at 20:36
  • http://blogs.msdn.com/b/csharpfaq/archive/2010/06/01/parallel-programming-in-net-framework-4-getting-started.aspx – Lasse Nov 02 '13 at 20:44

1 Answers1

2

While there are some performance aspects that can be improved, ultimately, it's the storage of the particles that will dwarf any efforts.

Your algorithm is O(n^2), as for each particle, you iterate over the whole particle list again. For n = 700, that's 700*700 = 490000 executions of the loop. Also, many particles are checked i too often. If you start the inner loop at j=i, you'll gain a noticeable speed up.

However, this will only be a bandaid in my opinion. You should look into a more efficient storage of your particles, namely the Quadtree.

Also, instead of calculating a sqrt for each distance, square the distance you compare against:

distTo = (xDist * xDist) + (yDist * yDist);
...

if(distanceBetween(particleList[i].position, particleList[j].position) < reactDistance * reactDistance)

You can even precalculate that before the loops so you don't have that overhead every time.

Femaref
  • 60,705
  • 7
  • 138
  • 176
  • Thank you. Changing to j=i, like Mike B also suggested, now allows up to 700 particles with a smooth frame rate. – ShadowByte Nov 02 '13 at 15:44
  • I'll look into Quadtrees if this is only a bandaid. Thanks so much. – ShadowByte Nov 02 '13 at 16:04
  • No satisfactory Quadtree tutorials on YouTube. I get the idea, and it's very efficient, but nobody is explaining how to program it well enough for me to begin approaching it. After many attempts at optimization, my program supports roughly 850 particles at 50 FPS which is comfortable. – ShadowByte Nov 03 '13 at 00:47
  • The linked wikipedia article has a pretty good introduction regarding the most important methods (insert and range) are detailed as pseudocode. You'll have to fill the rest in. – Femaref Nov 03 '13 at 09:36