public static decimal forceBetweenParticles(particle acting, particle actedUpon)
        {
            var     posDiff = actedUpon.position - acting.position;
            decimal dist    = posDiff.magnitude * scale;

            if (dist == 0)
            {
                dist = 0.000000001M;
            }
            return((k * acting.charge * q * actedUpon.charge * q) / (dist * dist));
        }
        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);
        }
        private void spawnParticles(int particles, particle proto, vector v)
        {
            var height = bottomRightBoundary.y - topLeftBoundary.y;
            var width  = bottomRightBoundary.x - topLeftBoundary.x;

            if (height == 0 || width == 0)
            {
                throw new Exception("Can't define simulation zone with zero area!");
            }
            decimal ratio            = height / width;
            decimal inverseRatio     = 1 / ratio;
            decimal numberToMultiply = vector.Sqrt(particles);

            int wide = (int)(inverseRatio * numberToMultiply);
            int high = (int)(ratio * numberToMultiply);

            if (containedParticles == null)
            {
                containedParticles = new List <particle>();
            }

            for (int x = 0; x < wide; x++)
            {
                for (int y = 0; y < high; y++)
                {
                    particle p    = proto.copyWithNewID;
                    decimal  posx = topLeftBoundary.x + (width / wide) * x;
                    decimal  posy = topLeftBoundary.y + (height / high) * y;
                    p.position = new simPoint(posx, posy);
                    if (v != null)
                    {
                        p.momentum = v;
                    }

                    containedParticles.Add(p);
                }
            }
        }
        //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)];
                }
            }
        }