sandpypi/Sand.Core/SandSimulation.Painting.cs

267 lines
8.3 KiB
C#

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++;
}
}
}