//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); }