//o(n^2) -- terrible efficiency overall but it might work
        public void runSimulation()
        {
            if (!this.active)
            {
                return;
            }
            lock (this)
            {
                if (particlesToAdd != null)
                {
                    containedParticles.AddRange(particlesToAdd);
                }
                lock (containedParticles)
                {
                    List <particle> toRemoveNow = new List <particle>();
                    if (particlesToRemove != null)
                    {
                        foreach (Guid pid in particlesToRemove)
                        {
                            foreach (particle p in containedParticles)
                            {
                                if (p.identifier == pid)
                                {
                                    toRemoveNow.Add(p);
                                }
                            }
                        }
                    }
                    foreach (particle p in toRemoveNow)
                    {
                        containedParticles.Remove(p);
                    }
                }


                particlesToRemove = new List <Guid>();
                particlesToAdd    = new List <particle>();
            }

            simPoint av = simPoint.fromPointCollection(new[] { topLeftBoundary, bottomRightBoundary });

            List <particle> ps = new List <particle>();

            foreach (particle p in containedParticles)
            {
                if (p != null)
                {
                    ps.Add(p);
                }
            }
            //p == null ? (() => { }). : ps.Add(p);
            containedParticles = ps;

            for (int i = 0; i < containedParticles.Count; i++)
            {
                vector momentumToAdd = new vector();
                for (int z = 0; z < 9; z++)
                {
                    if (this[z] != null && this[z].active)
                    {
                        for (int j = 0; j < this[z].containedParticles.Count; j++)
                        {
                            if ((i != j || z != 4) && (j < this[z].containedParticles.Count && i < containedParticles.Count))
                            {
                                try
                                {
                                    var vecDiff = containedParticles[i].position - this[z].containedParticles[j].position;
                                    vecDiff        = vecDiff.normalised * simulation.forceBetweenParticles(this[z].containedParticles[j], containedParticles[i]);
                                    momentumToAdd += vecDiff;
                                }
                                catch (ArgumentOutOfRangeException)
                                {
                                    //don't know how this can go wrong, but, it can
                                }
                                catch (NullReferenceException)
                                {
                                    //Also don't know how this happens, but sometimes it does
                                }
                            }
                        }
                    }
                }

                for (int gp = 0; gp < containedGeometry.Count; gp++)
                {
                    var vecDiff = containedParticles[i].position - containedGeometry[gp];
                    vecDiff = vecDiff.normalised * simulation.forceBetweenParticles(new particle()
                    {
                        position = containedGeometry[gp], momentum = new vector(), repellanceCoefficient = simulation.solidCoefficient
                    }, containedParticles[i]);
                    momentumToAdd += vecDiff;
                }

                simPoint normalised = (containedParticles[i].position - av).asPoint;

                //needs to push back harder if the density is lower in the current chunk than the other chunk. Should probably use distance for this? or charge? idk
                //trying with charge first because linear relations are more easy to understand, but distance is going to be the one that needs to change, because density is proportional to distance, not charge.
                //not going to work, because there's no way of knowing where a particle lies in relation to the simulationchunk.
                decimal density = chunkDensity;

                //top
                simulationCell top = this[0, -1];
                if (top != null)
                {
                    decimal topDensity = top.chunkDensity;
                    var     downVec    = new vector(0, 1M);
                    if (!top.active)
                    {
                        momentumToAdd += (downVec * containedParticles[i].momentum.magnitude) / (normalised).asVector.magnitude;
                    }
                }


                //right
                simulationCell right = this[1, 0];
                if (right != null)
                {
                    decimal rightDensity = right.chunkDensity;
                    var     leftVec      = new vector(-1M, 0);
                    if (!right.active)
                    {
                        momentumToAdd += (leftVec * containedParticles[i].momentum.magnitude) / (normalised).asVector.magnitude;
                    }
                }


                //bottom
                simulationCell bottom = this[0, 1];
                if (bottom != null)
                {
                    decimal bottomDensity = bottom.chunkDensity;
                    var     upVec         = new vector(0, -1M);
                    if (!bottom.active)
                    {
                        momentumToAdd += (upVec * containedParticles[i].momentum.magnitude) / (normalised).asVector.magnitude;
                    }
                }


                //left
                simulationCell left = this[-1, 0];
                if (left != null)
                {
                    decimal leftDensity = left.chunkDensity;
                    var     rightVec    = new vector(1M, 0);
                    if (!left.active)
                    {
                        //momentumToAdd += (rightVec * containedParticles[i].momentum.magnitude) / (normalised).asVector.magnitude;
                    }
                }

                containedParticles[i].momentumChange = momentumToAdd;

                containedParticles[i].momentum += momentumToAdd;
            }

            for (int i = 0; i < containedParticles.Count; i++)
            {
                containedParticles[i].position += containedParticles[i].momentum * simulation.simulationDelta;
                if (this.isPointContained(containedParticles[i].position) == false)
                {
                    bool relocated = false;

                    for (int z = 0; z < 9; z++)
                    {
                        if (this[z] != null)
                        {
                            if (this[z].isPointContained(containedParticles[i].position) && z != 4)
                            {
                                this[z].addParticlesToAddList(new[] { containedParticles[i] });
                                if (particlesToRemoveFromList == null)
                                {
                                    particlesToRemoveFromList = new List <particle>();
                                }
                                particlesToRemoveFromList.Add(containedParticles[i]);
                                relocated = true;
                            }
                        }
                    }


                    if (!relocated)
                    {
                        if (particlesToRemoveFromList == null)
                        {
                            particlesToRemoveFromList = new List <particle>();
                        }
                        particlesToRemoveFromList.Add(containedParticles[i]);
                    }
                }
            }
        }
        //air will always flow right to left
        public simulation(List <simPoint> geometryPoints, int width = 700, int height = 300, int cellsHigh = 6, int cellsWide = 16, decimal baseDensity = 0.0125M, vector startingVelocity = null, particle p = null)
        {
            spawnCells = new List <simulationCell>();

            int realWidth     = width * 2;
            int realCellsWide = cellsWide + 2;//* 2;

            if (startingVelocity == null)
            {
                startingVelocity = new vector(-0.1M, 0);
            }
            if (p == null)
            {
                p = new particle()
                {
                    momentum = startingVelocity, repellanceCoefficient = airCoefficient, position = vector.zero.asPoint
                }
            }
            ;

            if (cells == null)
            {
                cells = new List <simulationCell>();
            }


            decimal cellWidth  = (decimal)(width / cellsWide);
            decimal cellHeight = (decimal)(height / cellsHigh);

            int particlesPerCell = (int)(cellWidth * cellHeight * baseDensity);

            spawncount = particlesPerCell;

            Dictionary <Tuple <int, int>, simulationCell> cellDict = new Dictionary <Tuple <int, int>, simulationCell>();

            for (int x = -1; x <= realCellsWide; x++)
            {
                for (int y = -1; y <= cellsHigh; y++)
                {
                    simPoint topLeftB     = new simPoint(x * cellWidth, y * cellHeight);
                    simPoint bottomRightB = new simPoint((x + 1) * cellWidth, (y + 1) * cellHeight);

                    //make real simulation cells
                    simulationCell c;
                    if (y >= 0 && y < cellsHigh)
                    {
                        if (x > cellsWide && x < realCellsWide)
                        {
                            c = new simulationCell(topLeftB, bottomRightB, new List <simPoint>(), particlesPerCell, p);
                            spawnCells.Add(c);
                        }
                        else if (x >= 0)
                        {
                            List <simPoint> ptsRelevant = geometryPoints.FindAll((s) => {
                                if (s.x <= bottomRightB.x && s.x >= topLeftB.x && s.y >= topLeftB.y && s.y < bottomRightB.y)
                                {
                                    return(true);
                                }
                                else
                                {
                                    return(false);
                                }
                            });
                            c = new simulationCell(topLeftB, bottomRightB, ptsRelevant, 0, p);
                        }
                        else
                        {
                            c = new simulationCell(particlesPerCell / 2, topLeftB, bottomRightB);
                        }
                    }
                    else
                    {
                        c = new simulationCell(particlesPerCell / 2, topLeftB, bottomRightB);
                    }
                    cellDict[new Tuple <int, int>(x, y)] = c;
                    cells.Add(c);
                }
            }

            for (int x = 0; x < realCellsWide; x++)
            {
                for (int y = 0; y < cellsHigh; y++)
                {
                    cellDict[new Tuple <int, int>(x, y)].bottom      = cellDict[new Tuple <int, int>(x, y + 1)];
                    cellDict[new Tuple <int, int>(x, y)].top         = cellDict[new Tuple <int, int>(x, y - 1)];
                    cellDict[new Tuple <int, int>(x, y)].right       = cellDict[new Tuple <int, int>(x + 1, y)];
                    cellDict[new Tuple <int, int>(x, y)].left        = cellDict[new Tuple <int, int>(x - 1, y)];
                    cellDict[new Tuple <int, int>(x, y)].bottomRight = cellDict[new Tuple <int, int>(x + 1, y + 1)];
                    cellDict[new Tuple <int, int>(x, y)].bottomLeft  = cellDict[new Tuple <int, int>(x - 1, y + 1)];
                    cellDict[new Tuple <int, int>(x, y)].topRight    = cellDict[new Tuple <int, int>(x + 1, y - 1)];
                    cellDict[new Tuple <int, int>(x, y)].topLeft     = cellDict[new Tuple <int, int>(x - 1, y - 1)];
                }
            }
        }
        public simulationCell(simPoint tlBoundary, simPoint brBoundary, List <simPoint> geometry, int numberOfParticles, particle prototype, vector startingVector = null)
        {
            topLeftBoundary     = tlBoundary;
            bottomRightBoundary = brBoundary;
            containedGeometry   = geometry;

            initialPrototype       = prototype;
            initialVectorPrototype = startingVector;

            spawnParticles(numberOfParticles, prototype, startingVector);
        }