namespace Sand.Core; public sealed partial class SandSimulation { public void Clear() { Array.Clear(TypeId); Array.Clear(Temperature); Array.Clear(BurnTime); Array.Clear(Burning); Array.Clear(SparkTime); Array.Clear(Lifetime); Array.Clear(_pressureDuration); Array.Clear(_cellAge); Array.Clear(_integrity); Array.Clear(_windFieldX); Array.Clear(_windFieldY); Array.Clear(_forceFieldX); Array.Clear(_forceFieldY); Array.Clear(_airPressure); Array.Clear(_processedFrame); Array.Clear(_explosionFrame); Array.Clear(_rgbBuffer); Array.Clear(_rgbaBuffer); ParticleCount = 0; Frame = 0; _activeStepToken = 0; _boundsDirty = false; _staleOccupiedBoundsFrames = 0; _hasOccupiedBounds = false; _fieldBoundsDirty = false; _hasActiveFieldBounds = false; _hasDirtyVisualBounds = false; _fullVisualDirty = true; RefreshSettingsState(); } public void RefreshSettingsState() { if (_settings.OuterWall && _idWall != 0) { for (var x = 0; x < Width; x++) { ForceSetCell(x, 0, _idWall); ForceSetCell(x, Height - 1, _idWall); } for (var y = 0; y < Height; y++) { ForceSetCell(0, y, _idWall); ForceSetCell(Width - 1, y, _idWall); } } else if (_idWall != 0) { ClearBoundaryWalls(); } RecomputeOccupiedBounds(); MarkFullVisualDirty(); } public ushort GetTypeIdAtCell(int x, int y) => TypeId[x, y]; public float GetTemperatureAtCell(int x, int y) => Temperature[x, y]; public (float X, float Y) GetWindAtCell(int x, int y) => (_windFieldX[x, y], _windFieldY[x, y]); public (float X, float Y) GetForceAtCell(int x, int y) => (_forceFieldX[x, y], _forceFieldY[x, y]); public float GetAirPressureAtCell(int x, int y) => _airPressure[x, y]; public void ApplyWindBrushAtPixel(int centerX, int centerY, int brushRadius, float forceX, float forceY) { ApplyDirectionalBrush(centerX, centerY, brushRadius, forceX, forceY, _windTool, _windFieldX, _windFieldY); } public void ApplyAirBrushAtPixel(int centerX, int centerY, int brushRadius, float forceX, float forceY) { ApplyDirectionalBrush(centerX, centerY, brushRadius, forceX, forceY, _airTool, _windFieldX, _windFieldY, addPressure: true); } public void ApplyGravityBrushAtPixel(int centerX, int centerY, int brushRadius, float strength) { ApplyRadialForceBrushAtPixel(centerX, centerY, brushRadius, MathF.Abs(strength), inward: true, _gravityTool); } public void ApplyRepulsorBrushAtPixel(int centerX, int centerY, int brushRadius, float strength) { ApplyRadialForceBrushAtPixel(centerX, centerY, brushRadius, MathF.Abs(strength), inward: false, _repulsorTool); } public void CreateParticleAtPixel(int x, int y, string particleId) { var gridX = x / ParticleSize; var gridY = y / ParticleSize; if (!TryNormalizeCoordinate(ref gridX, ref gridY)) { return; } if (!_library.TryGetTypeId(particleId.ToLowerInvariant(), out var typeId)) { return; } if (_settings.OuterWall && typeId != _idWall && IsBoundary(gridX, gridY)) { return; } if (TypeId[gridX, gridY] != 0) { if (typeId == _idSpark) { var targetId = TypeId[gridX, gridY]; if (_conductivity[targetId] > 0.5f && SparkTime[gridX, gridY] == 0) { SparkTime[gridX, gridY] = 4; } } return; } SetCell(gridX, gridY, typeId, markProcessed: false, clearDynamics: true); } public void CreateParticleCircle(int centerX, int centerY, int brushRadius, string particleId) { for (var dx = -brushRadius; dx <= brushRadius; dx++) { for (var dy = -brushRadius; dy <= brushRadius; dy++) { if ((dx * dx) + (dy * dy) > brushRadius * brushRadius) { continue; } CreateParticleAtPixel(centerX + (dx * ParticleSize), centerY + (dy * ParticleSize), particleId); } } } public void CreateParticlePourAtPixel(int centerX, int centerY, int brushRadius, string particleId, int maxParticles, int seed) { var gridCenterX = centerX / ParticleSize; var gridCenterY = centerY / ParticleSize; if (!_library.TryGetTypeId(particleId.ToLowerInvariant(), out var typeId)) { return; } var radiusSq = brushRadius * brushRadius; var diameter = (brushRadius * 2) + 1; var maxCells = diameter * diameter; var attempts = Math.Max(maxParticles * 6, maxCells); var placed = 0; for (var i = 0; i < attempts && placed < maxParticles; i++) { var hashX = Hash(gridCenterX + (i * 17), gridCenterY - (i * 31), seed + i); var hashY = Hash(gridCenterX - (i * 29), gridCenterY + (i * 13), seed - i); var dx = (int)(hashX % (uint)diameter) - brushRadius; var dy = (int)(hashY % (uint)diameter) - brushRadius; if ((dx * dx) + (dy * dy) > radiusSq) { continue; } var x = gridCenterX + dx; var y = gridCenterY + dy; if (!TryNormalizeCoordinate(ref x, ref y)) { continue; } if (_settings.OuterWall && typeId != _idWall && IsBoundary(x, y)) { continue; } if (TypeId[x, y] != 0) { continue; } SetCell(x, y, typeId, markProcessed: false, clearDynamics: true); placed++; } } public void ClearParticleCircle(int centerX, int centerY, int brushRadius) { for (var dx = -brushRadius; dx <= brushRadius; dx++) { for (var dy = -brushRadius; dy <= brushRadius; dy++) { if ((dx * dx) + (dy * dy) > brushRadius * brushRadius) { continue; } var gridX = (centerX / ParticleSize) + dx; var gridY = (centerY / ParticleSize) + dy; if (!TryNormalizeCoordinate(ref gridX, ref gridY)) { continue; } if (_settings.OuterWall && IsBoundary(gridX, gridY)) { continue; } if (TypeId[gridX, gridY] != 0) { ResetCell(gridX, gridY); } } } } public void ClearParticlePourAtPixel(int centerX, int centerY, int brushRadius, int maxParticles, int seed) { var gridCenterX = centerX / ParticleSize; var gridCenterY = centerY / ParticleSize; var radiusSq = brushRadius * brushRadius; var diameter = (brushRadius * 2) + 1; var maxCells = diameter * diameter; var attempts = Math.Max(maxParticles * 6, maxCells); var cleared = 0; for (var i = 0; i < attempts && cleared < maxParticles; i++) { var hashX = Hash(gridCenterX + (i * 17), gridCenterY - (i * 31), seed + i); var hashY = Hash(gridCenterX - (i * 29), gridCenterY + (i * 13), seed - i); var dx = (int)(hashX % (uint)diameter) - brushRadius; var dy = (int)(hashY % (uint)diameter) - brushRadius; if ((dx * dx) + (dy * dy) > radiusSq) { continue; } var x = gridCenterX + dx; var y = gridCenterY + dy; if (!TryNormalizeCoordinate(ref x, ref y)) { continue; } if (_settings.OuterWall && IsBoundary(x, y)) { continue; } if (TypeId[x, y] == 0) { continue; } ResetCell(x, y); cleared++; } } }