Advertisement
JontePonte

Iterative quadtree

Nov 17th, 2024
38
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 7.81 KB | None | 0 0
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using Particle = Simulation.Particle;
  4.  
  5. struct Node
  6. {
  7.     public Quad boundary;
  8.     public float mass;
  9.     public Vector2 centerOfMass;
  10.     public int childIndex; //NW index
  11. }
  12. public struct Quad
  13. {
  14.     //Bottom left of rectangle
  15.     public Vector2 position;
  16.     public float size;
  17.    
  18.     public bool Intersects(Vector2 pos)
  19.     {
  20.         return pos.x > position.x && pos.x < position.x + size &&
  21.                pos.y > position.y && pos.y < position.y + size;
  22.     }
  23.     public int FindNode(Vector2 pos)
  24.     {
  25.         bool wX = pos.x - position.x < size / 2;
  26.         bool sY = pos.y - position.y < size / 2;
  27.  
  28.         if (wX && !sY) return 0;
  29.         if (!wX && !sY) return 1;
  30.         if (wX && sY) return 2;
  31.         if(!wX && sY) return 3;
  32.  
  33.         return 0;
  34.     }
  35. }
  36.  
  37. public class QuadTree
  38. {
  39.     List<Node> quadTree;
  40.  
  41.     public QuadTree()
  42.     {
  43.         quadTree = new();
  44.         Quad boundary = new() { position = new Vector2(-5, -5), size = 10 };
  45.         quadTree.Add(new Node
  46.         {
  47.             boundary = boundary,
  48.         });
  49.     }
  50.  
  51.     public void DrawTree()
  52.     {
  53.         foreach (var node in quadTree)
  54.         {
  55.             DrawSquare(node.boundary.position, node.boundary.size);
  56.         }
  57.     }
  58.     static void DrawSquare(Vector2 position, float size)
  59.     {
  60.         Debug.DrawLine(position, new Vector2(position.x, position.y + size), Color.red);
  61.         Debug.DrawLine(new Vector2(position.x, position.y + size), new Vector2(position.x + size, position.y + size), Color.red);
  62.         Debug.DrawLine(new Vector2(position.x + size, position.y + size), new Vector2(position.x + size, position.y), Color.red);
  63.         Debug.DrawLine(new Vector2(position.x + size, position.y), new Vector2(position.x, position.y), Color.red);
  64.     }
  65.  
  66.     public Vector2 CalculateAcceleration(Particle particle, float theta, float epsilon, float timeStep)
  67.     {
  68.         Vector2 acceleration = new();
  69.  
  70.         float thetaSqr = theta * theta;
  71.         float epsilonSqr = epsilon * epsilon;
  72.  
  73.         Stack<int> stack = new();
  74.         stack.Push(0);
  75.         while (stack.Count > 0)
  76.         {
  77.             Node node = quadTree[stack.Pop()];
  78.             Vector2 diff = node.centerOfMass - particle.position;
  79.             float sqrDst = Mathf.Max(diff.sqrMagnitude, 0.05f);
  80.  
  81.             if (node.childIndex == 0 || node.boundary.size * node.boundary.size < sqrDst * thetaSqr)
  82.             {
  83.                 float denom = (sqrDst + epsilonSqr) * Mathf.Sqrt(sqrDst);
  84.                 acceleration += diff * node.mass / denom * timeStep;
  85.                 continue;
  86.             }
  87.  
  88.             for (int i = 3; i >= 0; i--)
  89.             {
  90.                 stack.Push(node.childIndex + i);
  91.             }
  92.         }
  93.        
  94.         return acceleration;
  95.     }
  96.  
  97.     public void Propagate()
  98.     {
  99.         for (int i = quadTree.Count - 1; i > 0; i--)
  100.         {
  101.             if(quadTree[i].childIndex == 0) continue;
  102.            
  103.             int child = quadTree[i].childIndex;
  104.             Node node = quadTree[i];
  105.  
  106.             node.centerOfMass = quadTree[child].centerOfMass * quadTree[child].mass
  107.                                 + quadTree[child + 1].centerOfMass * quadTree[child + 1].mass
  108.                                 + quadTree[child + 2].centerOfMass * quadTree[child + 2].mass
  109.                                 + quadTree[child + 3].centerOfMass * quadTree[child + 3].mass;
  110.             node.mass = quadTree[child].mass
  111.                         + quadTree[child + 1].mass
  112.                         + quadTree[child + 2].mass
  113.                         + quadTree[child + 3].mass;
  114.             node.centerOfMass /= node.mass;
  115.             quadTree[i] = node;
  116.  
  117.         }
  118.     }
  119.  
  120.     public void Insert(Particle particle)
  121.     {
  122.         int maxIterations = 100;
  123.         int iteration = 0;
  124.         while (true && iteration < maxIterations)
  125.         {
  126.             iteration++;
  127.             int bestNode = 0;
  128.             for (int i = 0; i < quadTree.Count; i++)
  129.             {
  130.                 Node node = quadTree[i];
  131.                
  132.                 //Continue if node isn't leaf node
  133.                 if(node.childIndex != 0) continue;
  134.                 //Continue if particle does not belong in this node
  135.                 if (!node.boundary.Intersects(particle.position)) continue;
  136.                
  137.                 bestNode = i;
  138.                 //If node is empty, set the mass and position
  139.                 if (node.mass == 0)
  140.                 {
  141.                     node.mass = particle.mass;
  142.                     node.centerOfMass = particle.position;
  143.                     quadTree[i] = node;
  144.                     return;
  145.                 }
  146.                 //If the node's position is the same as the particle's, add the particle's mass
  147.                 if (node.centerOfMass == particle.position)
  148.                 {
  149.                     node.mass += particle.mass;
  150.                     quadTree[i] = node;
  151.                     return;
  152.                 }
  153.             }
  154.  
  155.             Vector2 nodePos = quadTree[bestNode].centerOfMass;
  156.             float nodeMass = quadTree[bestNode].mass;
  157.            
  158.             int iterations = 0;
  159.             while (true && iterations < 100)
  160.             {
  161.                 iterations++;
  162.                 //Check what node particle and bestNode ends up in
  163.                 //If they are the same, continue and set bestNode to FindNode(particle)
  164.                 //Otherwise, set the mass and pos of FindNode(particle)
  165.                 Subdivide(bestNode);
  166.                 int childIndex = quadTree[bestNode].childIndex;
  167.                 int q1 = quadTree[bestNode].boundary.FindNode(nodePos);
  168.                 int q2 = quadTree[bestNode].boundary.FindNode(particle.position);
  169.  
  170.                 if (q1 == q2)
  171.                 {
  172.                     bestNode = q1 + childIndex;
  173.                 }
  174.                 else
  175.                 {
  176.                     int n1 = q1 + childIndex;
  177.                     int n2 = q2 + childIndex;
  178.                    
  179.                     quadTree[n1] = new Node
  180.                     {
  181.                         centerOfMass = nodePos,
  182.                         mass = nodeMass,
  183.                         boundary = quadTree[n1].boundary,
  184.                     };
  185.                     quadTree[n2] = new Node
  186.                     {
  187.                         centerOfMass = particle.position,
  188.                         mass = particle.mass,
  189.                         boundary = quadTree[n2].boundary,
  190.                     };
  191.                     return;
  192.                 }
  193.             }
  194.         }
  195.     }
  196.    
  197.     void Subdivide(int parentIndex)
  198.     {
  199.         Node parent = quadTree[parentIndex];
  200.         parent.childIndex = quadTree.Count;
  201.         quadTree[parentIndex] = parent;
  202.         Vector2 parentPos = parent.boundary.position;
  203.         Vector2 nwPosition = new(parentPos.x, parentPos.y + parent.boundary.size / 2);
  204.         Vector2 nePosition = new(parentPos.x + parent.boundary.size / 2, parentPos.y + parent.boundary.size / 2);
  205.         Vector2 swPosition = new(parentPos.x, parentPos.y);
  206.         Vector2 sePosition = new(parentPos.x + parent.boundary.size / 2, parentPos.y);
  207.  
  208.         Node nw = new()
  209.         {
  210.             boundary = new Quad { position = nwPosition, size = parent.boundary.size / 2 }
  211.         };
  212.         Node ne = new()
  213.         {
  214.             boundary = new Quad { position = nePosition, size = parent.boundary.size / 2 }
  215.         };
  216.         Node sw = new()
  217.         {
  218.             boundary = new Quad { position = swPosition, size = parent.boundary.size / 2 }
  219.         };
  220.         Node se = new()
  221.         {
  222.             boundary = new Quad { position = sePosition, size = parent.boundary.size / 2 }
  223.         };
  224.        
  225.         quadTree.Add(nw);
  226.         quadTree.Add(ne);
  227.         quadTree.Add(sw);
  228.         quadTree.Add(se);
  229.     }
  230. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement