mirror of
https://github.com/Stone-Red-Code/LargeGridPathfinding.git
synced 2026-09-04 09:06:12 +02:00
Add Shift+Click area fill for brush tools and allow batch tile operations
This commit is contained in:
@@ -21,7 +21,7 @@ internal class GridFiller
|
|||||||
public int Width { get; }
|
public int Width { get; }
|
||||||
public int Height { get; }
|
public int Height { get; }
|
||||||
|
|
||||||
public GridFiller(int width, int height, List<Rectangle> obstacles)
|
public GridFiller(int width, int height)
|
||||||
{
|
{
|
||||||
Width = width;
|
Width = width;
|
||||||
Height = height;
|
Height = height;
|
||||||
@@ -36,8 +36,6 @@ internal class GridFiller
|
|||||||
WeightGrid[y, x] = 1;
|
WeightGrid[y, x] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaceObstacles(obstacles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FillGrid(int? x1 = null, int? y1 = null, int? x2 = null, int? y2 = null, bool fillAll = false, IProgress<float>? totalProgress = null, IProgress<float>? calculatingCandidatesProgress = null, IProgress<float>? placingCandidatesProgress = null)
|
public void FillGrid(int? x1 = null, int? y1 = null, int? x2 = null, int? y2 = null, bool fillAll = false, IProgress<float>? totalProgress = null, IProgress<float>? calculatingCandidatesProgress = null, IProgress<float>? placingCandidatesProgress = null)
|
||||||
@@ -167,7 +165,7 @@ internal class GridFiller
|
|||||||
PlaceObstacles([rectangle]);
|
PlaceObstacles([rectangle]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlaceObstacles(IEnumerable<Rectangle> rectangles)
|
public void PlaceObstacles(IEnumerable<Rectangle> rectangles, bool recalculate = true)
|
||||||
{
|
{
|
||||||
lock (mutationLock)
|
lock (mutationLock)
|
||||||
{
|
{
|
||||||
@@ -207,7 +205,7 @@ internal class GridFiller
|
|||||||
|
|
||||||
if (changed)
|
if (changed)
|
||||||
{
|
{
|
||||||
RecalculateAroundArea(minX, minY, maxX, maxY, () =>
|
RecalculateAroundArea(minX, minY, maxX, maxY, recalculate, () =>
|
||||||
{
|
{
|
||||||
foreach (Rectangle rectangle in clampedRectangles)
|
foreach (Rectangle rectangle in clampedRectangles)
|
||||||
{
|
{
|
||||||
@@ -237,7 +235,7 @@ internal class GridFiller
|
|||||||
RemoveObstacles([rectangle]);
|
RemoveObstacles([rectangle]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveObstacles(IEnumerable<Rectangle> rectangles)
|
public void RemoveObstacles(IEnumerable<Rectangle> rectangles, bool recalculate = true)
|
||||||
{
|
{
|
||||||
lock (mutationLock)
|
lock (mutationLock)
|
||||||
{
|
{
|
||||||
@@ -277,7 +275,7 @@ internal class GridFiller
|
|||||||
|
|
||||||
if (changed)
|
if (changed)
|
||||||
{
|
{
|
||||||
RecalculateAroundArea(minX, minY, maxX, maxY, () =>
|
RecalculateAroundArea(minX, minY, maxX, maxY, recalculate, () =>
|
||||||
{
|
{
|
||||||
foreach (Rectangle rectangle in clampedRectangles)
|
foreach (Rectangle rectangle in clampedRectangles)
|
||||||
{
|
{
|
||||||
@@ -310,7 +308,7 @@ internal class GridFiller
|
|||||||
ResetTileWeights([new Point(x, y)]);
|
ResetTileWeights([new Point(x, y)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTileWeights(IEnumerable<Point> points, int weight)
|
public void SetTileWeights(IEnumerable<Point> points, int weight, bool recalculate = true)
|
||||||
{
|
{
|
||||||
lock (mutationLock)
|
lock (mutationLock)
|
||||||
{
|
{
|
||||||
@@ -337,7 +335,7 @@ internal class GridFiller
|
|||||||
|
|
||||||
if (changed)
|
if (changed)
|
||||||
{
|
{
|
||||||
RecalculateAroundArea(minX, minY, maxX, maxY, () =>
|
RecalculateAroundArea(minX, minY, maxX, maxY, recalculate, () =>
|
||||||
{
|
{
|
||||||
foreach (Point point in points)
|
foreach (Point point in points)
|
||||||
{
|
{
|
||||||
@@ -456,14 +454,14 @@ internal class GridFiller
|
|||||||
return rectangle;
|
return rectangle;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RecalculateAroundArea(int minX, int minY, int maxX, int maxY, Action applyChanges)
|
private void RecalculateAroundArea(int minX, int minY, int maxX, int maxY, bool enabled, Action applyChanges)
|
||||||
{
|
{
|
||||||
int left = int.Clamp(minX - RecalculationRadius, 0, Width);
|
int left = int.Clamp(minX - RecalculationRadius, 0, Width);
|
||||||
int top = int.Clamp(minY - RecalculationRadius, 0, Height);
|
int top = int.Clamp(minY - RecalculationRadius, 0, Height);
|
||||||
int right = int.Clamp(maxX + RecalculationRadius, 0, Width);
|
int right = int.Clamp(maxX + RecalculationRadius, 0, Width);
|
||||||
int bottom = int.Clamp(maxY + RecalculationRadius, 0, Height);
|
int bottom = int.Clamp(maxY + RecalculationRadius, 0, Height);
|
||||||
|
|
||||||
if (left >= right || top >= bottom)
|
if (left >= right || top >= bottom || !enabled)
|
||||||
{
|
{
|
||||||
applyChanges();
|
applyChanges();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ public class LargeGridPathfindingGame : Game
|
|||||||
private BrushMode brushMode = BrushMode.Weight;
|
private BrushMode brushMode = BrushMode.Weight;
|
||||||
private int paintWeight = 5;
|
private int paintWeight = 5;
|
||||||
private Vector2? previousMousePosition;
|
private Vector2? previousMousePosition;
|
||||||
|
private bool wasShiftLeftDown;
|
||||||
|
private bool wasShiftRightDown;
|
||||||
|
private Point? fillSelectionStart;
|
||||||
|
private Point? fillSelectionCurrent;
|
||||||
private bool batchProcessingScheduled;
|
private bool batchProcessingScheduled;
|
||||||
|
|
||||||
public LargeGridPathfindingGame()
|
public LargeGridPathfindingGame()
|
||||||
@@ -147,6 +151,13 @@ public class LargeGridPathfindingGame : Game
|
|||||||
|
|
||||||
MouseStateExtended mouseState = MouseExtended.GetState();
|
MouseStateExtended mouseState = MouseExtended.GetState();
|
||||||
KeyboardStateExtended keyboardState = KeyboardExtended.GetState();
|
KeyboardStateExtended keyboardState = KeyboardExtended.GetState();
|
||||||
|
bool shiftPressed = keyboardState.IsKeyDown(Keys.LeftShift) || keyboardState.IsKeyDown(Keys.RightShift);
|
||||||
|
bool shiftLeftDown = shiftPressed && mouseState.IsButtonDown(MouseButton.Left);
|
||||||
|
bool shiftLeftClicked = shiftLeftDown && !wasShiftLeftDown;
|
||||||
|
bool shiftRightDown = shiftPressed && mouseState.IsButtonDown(MouseButton.Right);
|
||||||
|
bool shiftRightClicked = shiftRightDown && !wasShiftRightDown;
|
||||||
|
wasShiftLeftDown = shiftLeftDown;
|
||||||
|
wasShiftRightDown = shiftRightDown;
|
||||||
|
|
||||||
float movementSpeed = (float)Math.Pow(200, 2 - camera.Zoom);
|
float movementSpeed = (float)Math.Pow(200, 2 - camera.Zoom);
|
||||||
|
|
||||||
@@ -254,6 +265,73 @@ public class LargeGridPathfindingGame : Game
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shiftPressed)
|
||||||
|
{
|
||||||
|
if (fillSelectionStart.HasValue && TryGetMouseGridPoint(mouseState, out Point hoverGridPoint))
|
||||||
|
{
|
||||||
|
fillSelectionCurrent = hoverGridPoint;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fillSelectionCurrent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((shiftLeftClicked || shiftRightClicked) && TryGetMouseGridPoint(mouseState, out Point clickedGridPoint))
|
||||||
|
{
|
||||||
|
if (!fillSelectionStart.HasValue)
|
||||||
|
{
|
||||||
|
fillSelectionStart = clickedGridPoint;
|
||||||
|
fillSelectionCurrent = clickedGridPoint;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<Point> selectionPoints = GetRectanglePoints(fillSelectionStart.Value, clickedGridPoint);
|
||||||
|
Color indicatorColor = brushMode == BrushMode.Weight ? Color.Orange : Color.Red;
|
||||||
|
|
||||||
|
foreach (Point point in selectionPoints)
|
||||||
|
{
|
||||||
|
temporaryIndicators[point.ToVector2()] = indicatorColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingOperation operation;
|
||||||
|
if (brushMode == BrushMode.Weight && shiftLeftClicked)
|
||||||
|
{
|
||||||
|
operation = new PendingOperation(PendingOperationKind.SetWeight, paintWeight);
|
||||||
|
}
|
||||||
|
else if (brushMode == BrushMode.Weight && shiftRightClicked)
|
||||||
|
{
|
||||||
|
operation = new PendingOperation(PendingOperationKind.ResetWeight, 0);
|
||||||
|
}
|
||||||
|
else if (brushMode == BrushMode.Obstacle && shiftLeftClicked)
|
||||||
|
{
|
||||||
|
operation = new PendingOperation(PendingOperationKind.PlaceObstacle, 0);
|
||||||
|
}
|
||||||
|
else if (brushMode == BrushMode.Obstacle && shiftRightClicked)
|
||||||
|
{
|
||||||
|
operation = new PendingOperation(PendingOperationKind.RemoveObstacle, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Invalid brush mode or mouse button state.");
|
||||||
|
}
|
||||||
|
|
||||||
|
EnqueuePendingOperations(selectionPoints, operation);
|
||||||
|
fillSelectionStart = null;
|
||||||
|
fillSelectionCurrent = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousMousePosition = null;
|
||||||
|
base.Update(gameTime);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fillSelectionStart = null;
|
||||||
|
fillSelectionCurrent = null;
|
||||||
|
fillSelectionCurrent = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Paint and clear tiles via mouse input
|
// Paint and clear tiles via mouse input
|
||||||
|
|
||||||
if (mouseState.IsButtonDown(MouseButton.Left))
|
if (mouseState.IsButtonDown(MouseButton.Left))
|
||||||
@@ -420,6 +498,20 @@ public class LargeGridPathfindingGame : Game
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fillSelectionStart.HasValue)
|
||||||
|
{
|
||||||
|
Point selectionEnd = fillSelectionCurrent ?? fillSelectionStart.Value;
|
||||||
|
int left = Math.Min(fillSelectionStart.Value.X, selectionEnd.X);
|
||||||
|
int top = Math.Min(fillSelectionStart.Value.Y, selectionEnd.Y);
|
||||||
|
int right = Math.Max(fillSelectionStart.Value.X, selectionEnd.X);
|
||||||
|
int bottom = Math.Max(fillSelectionStart.Value.Y, selectionEnd.Y);
|
||||||
|
|
||||||
|
Rectangle selectionRectangle = new Rectangle(left * 10, top * 10, (right - left + 1) * 10, (bottom - top + 1) * 10);
|
||||||
|
spriteBatch.FillRectangle(selectionRectangle, Color.LightBlue * 0.2f, layerDepth: 0.24f);
|
||||||
|
spriteBatch.DrawRectangle(selectionRectangle, Color.Blue, 2f, layerDepth: 0.19f);
|
||||||
|
}
|
||||||
|
|
||||||
spriteBatch.End();
|
spriteBatch.End();
|
||||||
|
|
||||||
// Draw paths in a separate batch if needed
|
// Draw paths in a separate batch if needed
|
||||||
@@ -478,6 +570,7 @@ public class LargeGridPathfindingGame : Game
|
|||||||
uiSpriteBatch.DrawString(uiFont, $"Agents: {agents.Count}", new Vector2(10, 70), Color.Black);
|
uiSpriteBatch.DrawString(uiFont, $"Agents: {agents.Count}", new Vector2(10, 70), Color.Black);
|
||||||
uiSpriteBatch.DrawString(uiFont, $"Brush mode: {brushMode} [O]", new Vector2(10, 90), Color.Black);
|
uiSpriteBatch.DrawString(uiFont, $"Brush mode: {brushMode} [O]", new Vector2(10, 90), Color.Black);
|
||||||
uiSpriteBatch.DrawString(uiFont, $"Weight: {paintWeight} [Keys 1-9, Weight mode]", new Vector2(10, 110), Color.Black);
|
uiSpriteBatch.DrawString(uiFont, $"Weight: {paintWeight} [Keys 1-9, Weight mode]", new Vector2(10, 110), Color.Black);
|
||||||
|
uiSpriteBatch.DrawString(uiFont, "Area fill: hold Shift + left click twice", new Vector2(10, 130), Color.Black);
|
||||||
|
|
||||||
IReadOnlyList<ProgressTracker.ProgressData> progresses = progressTracker.GetProgresses();
|
IReadOnlyList<ProgressTracker.ProgressData> progresses = progressTracker.GetProgresses();
|
||||||
|
|
||||||
@@ -487,7 +580,7 @@ public class LargeGridPathfindingGame : Game
|
|||||||
|
|
||||||
string progress = progressData.Indeterminate ? "..." : progressData.Progress.ToString("P0");
|
string progress = progressData.Indeterminate ? "..." : progressData.Progress.ToString("P0");
|
||||||
|
|
||||||
uiSpriteBatch.DrawString(uiFont, $"{progressData.Name}: {progress}", new Vector2(10, 130 + (i * 20)), Color.Black);
|
uiSpriteBatch.DrawString(uiFont, $"{progressData.Name}: {progress}", new Vector2(10, 150 + (i * 20)), Color.Black);
|
||||||
}
|
}
|
||||||
|
|
||||||
uiSpriteBatch.End();
|
uiSpriteBatch.End();
|
||||||
@@ -592,7 +685,8 @@ public class LargeGridPathfindingGame : Game
|
|||||||
Debug.WriteLine("Initializing grid...");
|
Debug.WriteLine("Initializing grid...");
|
||||||
|
|
||||||
ProgressTracker.ProgressData progressData = progressTracker.AddProgress("Initializing grid", true, out _);
|
ProgressTracker.ProgressData progressData = progressTracker.AddProgress("Initializing grid", true, out _);
|
||||||
gridFiller = new GridFiller(width, height, obstacles);
|
gridFiller = new GridFiller(width, height);
|
||||||
|
gridFiller.PlaceObstacles(obstacles, false);
|
||||||
progressTracker.RemoveProgress(progressData);
|
progressTracker.RemoveProgress(progressData);
|
||||||
|
|
||||||
Debug.WriteLine("Filling grid...");
|
Debug.WriteLine("Filling grid...");
|
||||||
@@ -769,6 +863,41 @@ public class LargeGridPathfindingGame : Game
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool TryGetMouseGridPoint(MouseStateExtended mouseState, out Point gridPoint)
|
||||||
|
{
|
||||||
|
Vector2 mousePosition = camera.ScreenToWorld(mouseState.Position.ToVector2());
|
||||||
|
Point point = new Point((int)mousePosition.X / 10, (int)mousePosition.Y / 10);
|
||||||
|
|
||||||
|
if (point.X < 0 || point.X >= gridFiller.Width || point.Y < 0 || point.Y >= gridFiller.Height)
|
||||||
|
{
|
||||||
|
gridPoint = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
gridPoint = point;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Point> GetRectanglePoints(Point start, Point end)
|
||||||
|
{
|
||||||
|
int minX = Math.Min(start.X, end.X);
|
||||||
|
int minY = Math.Min(start.Y, end.Y);
|
||||||
|
int maxX = Math.Max(start.X, end.X);
|
||||||
|
int maxY = Math.Max(start.Y, end.Y);
|
||||||
|
|
||||||
|
List<Point> points = new((maxX - minX + 1) * (maxY - minY + 1));
|
||||||
|
|
||||||
|
for (int y = minY; y <= maxY; y++)
|
||||||
|
{
|
||||||
|
for (int x = minX; x <= maxX; x++)
|
||||||
|
{
|
||||||
|
points.Add(new Point(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
private static List<Point> GetBrushPoints(Vector2 currentGridPosition, Vector2? previousGridPosition)
|
private static List<Point> GetBrushPoints(Vector2 currentGridPosition, Vector2? previousGridPosition)
|
||||||
{
|
{
|
||||||
HashSet<Point> points = [];
|
HashSet<Point> points = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user