using Doomoroz.ECS.Components.GameSession.InteractiveDBObjects;
using NECS.Core.Logging;
using NECS.ECS.ECSCore;
using NECS.Extensions;
using NECS.Harness.Model;
using RTree;
using Supercluster.KDTree;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Numerics;
using static OperationBalancerService;
public class PointSearchingService : IService
{
private static PointSearchingService cacheInstance;
public static PointSearchingService instance
{
get
{
if (cacheInstance == null)
cacheInstance = SGT.Get<PointSearchingService>();
return cacheInstance;
}
}
public Dictionary<long, LevelSearchingStructure> LevelSearchingStructures = new Dictionary<long, LevelSearchingStructure>();
public void StopAndClearAllOperations()
{
LevelSearchingStructures.Clear();
}
public void RegisterLevelSearhingStructure(LevelComponent level, Vector2 topLeft,
Vector2 topRight,
Vector2 bottomLeft,
Vector2 bottomRight,
int gridSize = 1024)
{
#if KDTree
var treeData = GenerateSquareCenters(topLeft, topRight, bottomLeft, bottomRight, gridSize);
var treeNodes = treeData.Select(p => new TreePoint(new Vector2(p[0], p[1]))).ToArray();
Func<float[], float[], double> L2Norm = (x, y) =>
{
double dist = 0;
for (int i = 0; i < x.Length; i++)
{
dist += (x[i] - y[i]) * (x[i] - y[i]);
}
return dist;
};
var tree = new KDTree<float, TreePoint>(2, treeData, treeNodes, L2Norm);
#endif
var levelSearch = new LevelSearchingStructure()
{
levelOwner = level,
};
levelSearch.searchTree = levelSearch.CreateSearchTree(32, false);
LevelSearchingStructures.Add(level.instanceId, levelSearch);
}
/// <summary>
/// Устанавливает центральный контейнер для уровня. Все остальные контейнеры будут смещаться относительно него.
/// </summary>
public void SetCenterContainer(long levelInstance, CollideContainer centerContainer)
{
if (LevelSearchingStructures.TryGetValue(levelInstance, out var level))
{
level.centerContainer = centerContainer;
}
}
/// <summary>
/// Убирает центральный контейнер для уровня.
/// </summary>
public void ClearCenterContainer(long levelInstance)
{
if (LevelSearchingStructures.TryGetValue(levelInstance, out var level))
{
level.centerContainer = null;
}
}
/// <summary>
/// Получает смещенную позицию контейнера относительно центрального контейнера
/// </summary>
private Vector2 UnpackPosition(LevelSearchingStructure level, CollideContainer collideContainer, Vector2 container)
{
if (level.centerContainer == null)
return container;
// Если это центральный контейнер - он всегда в точке (0,0)
if (collideContainer == level.centerContainer)
return container;
// Смещаем позицию относительно центрального контейнера
return level.centerContainer.position + container;
}
public enum CollideMasks // adding - need 0 ||1 || 2^x number
{
None = 1,
Player = 2,
Enemy = 4,
Wall = 8,
Interactable = 16,
Projectile = 32
}
public void AppendCollideContainer(long levelInstance, CollideContainer collideContainer)
{
if (collideContainer == null)
return;
LevelSearchingStructures[levelInstance].collideContainers.Add(collideContainer);
}
public void RemoveCollideContainer(long levelInstance, CollideContainer collideContainer)
{
if (collideContainer == null)
return;
var level = LevelSearchingStructures[levelInstance];
level.collideContainers.Remove(collideContainer);
// Если удаляемый контейнер был центральным - сбрасываем центр
if (level.centerContainer == collideContainer)
{
level.centerContainer = null;
}
}
public override void LateUpdate()
{
foreach (var level in LevelSearchingStructures.Values)
{
if (level.collideContainers.Count > 0 && level.collideDetectionOperationBlock != null && level.CollidingCache.Count > 0 && (level.collideReactionOperationBlock == null || level.collideReactionOperationBlock.IsCompleted()) && (level.collideGroupReactionOperationBlock == null || level.collideGroupReactionOperationBlock.IsCompleted()))
{
var realCollisions = new Dictionary<CollideContainer, HashSet<CollideContainer>>();
level.collideReactionOperationBlock = OperationBalancerService.instance.AppendEnumerableOperation(level.CollidingCache.ToList(), (shouldRemove, index, item, accumulatedDelta) =>
{
foreach (var containerTup in item.Value)
{
var container = containerTup.Key;
foreach (var collidedContainerTup in item.Value)
{
var collidedContainer = collidedContainerTup.Key;
if (container == collidedContainer)
{
continue;
}
if (level.collideContainers.Contains(container))
{
var collisionResult = container.CollisionUpdate(containerTup.Value, collidedContainer, false);
if (collisionResult && container.EnteredColliding)
{
if (!realCollisions.ContainsKey(container))
{
realCollisions.Add(container, new HashSet<CollideContainer>());
}
realCollisions[container].Add(collidedContainer);
}
if (container.IsPassable(collidedContainer.CollideMask, false))
{
container.onCollidingWithSameMask(container, containerTup.Value, collidedContainer);
}
}
}
}
return false;
},
(list) =>
{
foreach (var container in level.ActiveCollisionsCache)
{
if (!realCollisions.ContainsKey(container.Key))
{
level.LeaveCollisionsCache.Add(container.Key, container.Value);
}
}
level.collideGroupReactionOperationBlock = OperationBalancerService.instance.AppendEnumerableOperation(realCollisions.ToList(), (shouldRemove, index, item, accumulatedDelta) =>
{
if (level.collideContainers.Contains(item.Key))
{
item.Key.onGroupCollide(item.Key, item.Value);
item.Key.GroupCollideCounter++;
}
return false;
}, (finlist) => { });
level.ActiveCollisionsCache = realCollisions;
});
}
if (level.collideContainers.Count > 0 && (level.collideDetectionOperationBlock == null || level.collideDetectionOperationBlock.IsCompleted()))
{
level.CollidingCache.Clear();
level.collideDetectionOperationBlock = OperationBalancerService.instance.AppendEnumerableOperation(level.collideContainers.ToList(), (shouldRemove, index, item, accumulatedDelta) =>
{
var searchTree = level.searchTree;
// if (item.Size != 32)
// {
// searchTree = level.GetTree(item.Size);
// }
var result = searchTree.Search(item.GetRelativeEnvelope(level.centerContainer), true);
foreach (var block in result)
{
List<TreePoint> additionaPoints = new List<TreePoint> { block };
if (item.Size > 0)
{
additionaPoints.AddRange(level.GetNeighbours(block, item.Size));
// if (!DebugService.instance.debugObjects.ContainsKey(item))
// {
// additionaPoints.ForEach(x => DebugService.instance.RegisterDebugObject(item, UnpackPosition(level, item, x.position).ToGodotVector2()));
// }
// else
// {
// var debugitem = DebugService.instance.GetDebugObjects(item);
// additionaPoints.ForEachWithIndex((x, i) => debugitem[i].position = UnpackPosition(level, item, x.position).ToGodotVector2());
// }
}
var unpacked = UnpackPosition(level, item, block.position);
foreach (var point in additionaPoints)
{
if (!level.CollidingCache.TryGetValue(point, out var list))
{
list = new Dictionary<CollideContainer, Vector2>();
level.CollidingCache.Add(point, list);
}
list.TryAdd(item, unpacked);
}
}
return false;
}, (list) => { });
}
/////NOT IN USE///////////////////
if (level.LeaveCollisionsCache.Count > 0 && (level.exitCollideReactionOperationBlock == null || level.exitCollideReactionOperationBlock.IsCompleted()))
{
var leaveSnap = level.LeaveCollisionsCache.ToList();
level.LeaveCollisionsCache.Clear();
level.exitCollideReactionOperationBlock = OperationBalancerService.instance.AppendEnumerableOperation(leaveSnap, (shouldRemove, index, item, accumulatedDelta) =>
{
item.Value.ForEach(x => item.Key.CollisionUpdate(Vector2.Zero, x, true));
return false;
}, (list) => { });
}
///////////////////////////////////
}
}
//player + globalplayer = interactive space
//interactive coord - global player coord = player local space interactive
public class TreePoint : ISpatialData
{
private Envelope EnvelopeCache;
private Vector2 posCache = Vector2.Zero;
public Vector2 position
{
get
{
if (posCache == Vector2.Zero)
{
posCache = new Vector2(Convert.ToSingle(EnvelopeCache.CenterX), Convert.ToSingle(EnvelopeCache.CenterY));
}
return posCache;
}
}
public Envelope Envelope => EnvelopeCache;
public TreePoint(Vector2 position)
{
EnvelopeCache = new Envelope(position.X, position.Y);
}
public TreePoint(Envelope envelope)
{
EnvelopeCache = envelope;
}
}
public override void InitializeProcess()
{
}
public override void OnDestroyReaction()
{
}
public override void PostInitializeProcess()
{
}
protected override Action<int>[] GetInitializationSteps()
{
return new Action<int>[]{
(step) => { },
(step) => { InitializeProcess(); },
};
}
protected override void SetupCallbacks(List<IService> allServices)
{
}
public class LevelSearchingStructure
{
public IOperationBlock collideDetectionOperationBlock = null;
public IOperationBlock collideReactionOperationBlock = null;
public IOperationBlock collideGroupReactionOperationBlock = null;
public IOperationBlock exitCollideReactionOperationBlock = null;
public LevelComponent levelOwner;
//public KDTree<float, Vector2> searchTree;
public RTree<TreePoint> searchTree;
public TreePoint[][] neighboursMap;
public Dictionary<TreePoint, (int, int)> mapIndexCache;
public TreePoint[][] correctionNeighboursMap;
public Dictionary<TreePoint, (int, int)> correctionMapIndexCache;
public HashSet<CollideContainer> collideContainers = new HashSet<CollideContainer>();
public Dictionary<TreePoint, Dictionary<CollideContainer, Vector2>> CollidingCache = new System.Collections.Generic.Dictionary<TreePoint, Dictionary<CollideContainer, Vector2>>();
public Dictionary<CollideContainer, HashSet<CollideContainer>> ActiveCollisionsCache = new System.Collections.Generic.Dictionary<CollideContainer, HashSet<CollideContainer>>();
public Dictionary<CollideContainer, HashSet<CollideContainer>> LeaveCollisionsCache = new System.Collections.Generic.Dictionary<CollideContainer, HashSet<CollideContainer>>();
public CollideContainer centerContainer;
public RTree<TreePoint> CreateSearchTree(int size, bool generateCorrectionTree = false)
{
var topLeftLocal = new Vector2(512, -512);
var topRightLocal = new Vector2(512, 512);
var bottomLeftLocal = new Vector2(-512, 512);
var bottomRightLocal = new Vector2(-512, -512);
// var topLeftLocal = new Vector2(1024, -1024);
// var topRightLocal = new Vector2(1024, 1024);
// var bottomLeftLocal = new Vector2(-1024, 1024);
// var bottomRightLocal = new Vector2(-1024, -1024);
RTree<TreePoint> tree = new RTree<TreePoint>(2);
var mapmatrix = GenerateSquareMatrix(topLeftLocal, topRightLocal, bottomLeftLocal, bottomRightLocal, size);
var corretionmapmatrix = GenerateSquareMatrix(topLeftLocal, topRightLocal, bottomLeftLocal, bottomRightLocal, size, 0.5f);
neighboursMap = mapmatrix.Item2;
mapIndexCache = mapmatrix.Item3;
correctionNeighboursMap = corretionmapmatrix.Item2;
correctionMapIndexCache = corretionmapmatrix.Item3;
if (generateCorrectionTree)
mapmatrix.Item1.AddRange(corretionmapmatrix.Item1);
tree.BulkLoad(mapmatrix.Item1);
return tree;
}
public List<TreePoint> GetNeighbours(TreePoint point, int neighbours)
{
var result = new List<TreePoint>();
// Определяем, из какой коллекции пришла точка
bool isFromMainCollection = mapIndexCache.ContainsKey(point);
bool isFromCorrectionCollection = correctionMapIndexCache.ContainsKey(point);
// Если точка не найдена ни в одной коллекции, возвращаем пустой результат
if (!isFromMainCollection && !isFromCorrectionCollection)
return result;
// Выбираем подходящие карты и кэши в зависимости от источника точки
TreePoint[][] currentNeighboursMap;
Dictionary<TreePoint, (int, int)> currentMapIndexCache;
if (isFromMainCollection)
{
currentNeighboursMap = neighboursMap;
currentMapIndexCache = mapIndexCache;
}
else
{
currentNeighboursMap = correctionNeighboursMap;
currentMapIndexCache = correctionMapIndexCache;
}
var index = currentMapIndexCache[point];
var centerX = index.Item1;
var centerY = index.Item2;
// Если neighbours = 0, возвращаем только центральную точку
if (neighbours == 0)
{
result.Add(point);
return result;
}
// Получаем размеры матрицы
int maxY = currentNeighboursMap.Length;
int maxX = currentNeighboursMap[0].Length;
// Проходим по всем точкам в радиусе от 1 до neighbours включительно
for (int distance = 1; distance <= neighbours; distance++)
{
// Проходим по квадрату с текущей дистанцией
for (int dy = -distance; dy <= distance; dy++)
{
for (int dx = -distance; dx <= distance; dx++)
{
// Используем метрику Чебышёва (максимум из |dx| и |dy|)
// Берем только точки на границе текущей дистанции
int currentDistance = Math.Max(Math.Abs(dx), Math.Abs(dy));
if (currentDistance != distance)
continue;
int newX = centerX + dx;
int newY = centerY + dy;
// Проверяем границы массива
if (newX >= 0 && newX < maxX && newY >= 0 && newY < maxY)
{
var neighbourPoint = currentNeighboursMap[newX][newY];
if (neighbourPoint != null)
{
result.Add(neighbourPoint);
}
}
}
}
}
return result;
}
public (List<TreePoint>, TreePoint[][], Dictionary<TreePoint, (int, int)>) GenerateSquareMatrix(
Vector2 topLeft,
Vector2 topRight,
Vector2 bottomLeft,
Vector2 bottomRight,
int gridSize = 1024,
float offset = 0.0f)
{
var result = new List<TreePoint>();
var indexMap = new Dictionary<TreePoint, (int, int)>();
// Находим границы области
float minX = Math.Min(Math.Min(topLeft.X, topRight.X), Math.Min(bottomLeft.X, bottomRight.X));
float maxX = Math.Max(Math.Max(topLeft.X, topRight.X), Math.Max(bottomLeft.X, bottomRight.X));
float minY = Math.Min(Math.Min(topLeft.Y, topRight.Y), Math.Min(bottomLeft.Y, bottomRight.Y));
float maxY = Math.Max(Math.Max(topLeft.Y, topRight.Y), Math.Max(bottomLeft.Y, bottomRight.Y));
// Вычисляем размер одного квадрата
float squareSize = Math.Min((maxX - minX) / gridSize, (maxY - minY) / gridSize);
// Применяем offset
minX += squareSize * offset;
maxX += squareSize * offset;
minY += squareSize * offset;
maxY += squareSize * offset;
// Вычисляем количество квадратов по осям
int xCount = (int)Math.Ceiling((maxX - minX) / squareSize);
int yCount = (int)Math.Ceiling((maxY - minY) / squareSize);
// Создаем двумерный массив
var matrix = new TreePoint[yCount][];
for (int i = 0; i < yCount; i++)
{
matrix[i] = new TreePoint[xCount];
}
// Генерируем квадраты
int rowIndex = 0;
for (float y = minY; y < maxY && rowIndex < yCount; y += squareSize, rowIndex++)
{
int colIndex = 0;
for (float x = minX; x < maxX && colIndex < xCount; x += squareSize, colIndex++)
{
// Создаем центральную точку квадрата
Vector2 center = new Vector2(x + squareSize / 2, y + squareSize / 2);
var envelope = new Envelope
{
MinX = x,
MinY = y,
MaxX = x + squareSize,
MaxY = y + squareSize
};
var treePoint = new TreePoint(envelope);
// Добавляем в список
result.Add(treePoint);
// Добавляем в матрицу
matrix[rowIndex][colIndex] = treePoint;
// Добавляем в словарь индексов
indexMap[treePoint] = (rowIndex, colIndex);
}
}
return (result, matrix, indexMap);
}
}
public class CollideContainer
{
public long ownerId;
public bool EnteredColliding = false;
private const int FreePosition = 0;
public uint CollideMask = 0;
public uint CollideToMask = 0;
public int GroupCollideCounter;
/// <summary>
/// CollideContainer 1 = ownercontainer; collisionpoint; CollideContainer 2 = collidedcontainer
/// </summary>
public Action<CollideContainer, Vector2, CollideContainer> onEnterCollide = (a, b, c) => { };
/// <summary>
/// CollideContainer 1 = ownercontainer; collisionpoint; CollideContainer 2 = collidedcontainer
/// </summary>
public Action<CollideContainer, Vector2, CollideContainer> onExitCollide = (a, b, c) => { };
/// <summary>
/// CollideContainer 1 = ownercontainer; collisionpoint; CollideContainer 2 = collidedcontainer
/// </summary>
public Action<CollideContainer, Vector2, CollideContainer> onColliding = (a, b, c) => { };
public Action<CollideContainer, Vector2, CollideContainer> onCollidingWithSameMask = (a, b, c) => { };
public Action<CollideContainer, IEnumerable<CollideContainer>> onGroupCollide = (a, c) => { };
public bool CollisionUpdate(Vector2 nowPoint, CollideContainer collided, bool exit)
{
if (exit)
{
if (EnteredColliding)
{
onExitCollide(this, nowPoint, collided);
EnteredColliding = false;
}
}
else
{
if (this.IsPassable(collided.CollideMask, true))
{
if (!EnteredColliding)
{
onEnterCollide(this, nowPoint, collided);
EnteredColliding = true;
}
onColliding(this, nowPoint, collided);
return true;
}
}
return false;
}
private Vector2 cachePosition = Vector2.Zero;
public bool autoUpdateEnvelope = true;
public int Size = 0;
private Envelope localEnvelope;
public Envelope cacheEnvelope
{
get
{
if (localEnvelope == null)
{
localEnvelope = new Envelope(cachePosition.X, cachePosition.Y); // size
}
return localEnvelope;
}
}
public Vector2 position
{
get
{
return cachePosition;
}
set
{
cachePosition = value;
if (autoUpdateEnvelope)
{
cacheEnvelope.UpdateEnvelopeCenter(cachePosition.X, cachePosition.Y);
}
}
}
public IECSObject OwnerObject;
private Envelope cacheRelativeEnvelope = new Envelope();
public Envelope GetRelativeEnvelope(CollideContainer centerCollide)
{
if (centerCollide == null)
return cacheEnvelope;
if (centerCollide == this)
{
cacheRelativeEnvelope.MinX = 0;
cacheRelativeEnvelope.MinY = 0;
cacheRelativeEnvelope.MaxX = 0;
cacheRelativeEnvelope.MaxY = 0;
return cacheRelativeEnvelope;
}
var centerPosition = centerCollide.position;
cacheRelativeEnvelope.MaxX = cacheEnvelope.MaxX - centerPosition.X;
cacheRelativeEnvelope.MaxY = cacheEnvelope.MaxY - centerPosition.Y;
cacheRelativeEnvelope.MinX = cacheEnvelope.MaxX - centerPosition.X;
cacheRelativeEnvelope.MinY = cacheEnvelope.MinY - centerPosition.Y;
return cacheRelativeEnvelope;
}
/// <summary>
/// Устанавливает битовую маску для указанной позиции
/// </summary>
public void SetMask(uint mask, bool CollideTo)
{
CollideMask = CollideTo ? CollideMask : mask;
CollideToMask = CollideTo ? mask : CollideToMask;
}
/// <summary>
/// Добавляет биты к существующей маске
/// </summary>
public void AddToMask(uint mask, bool CollideTo)
{
if (CollideTo)
{
CollideToMask |= mask;
}
else
{
CollideMask |= mask;
}
}
/// <summary>
/// Убирает биты из существующей маски
/// </summary>
public void RemoveFromMask(uint mask, bool CollideTo)
{
if (CollideTo)
{
CollideToMask &= ~mask;
}
else
{
CollideMask &= ~mask;
}
}
public void ClearPoint(bool CollideTo)
{
if (CollideTo)
{
CollideToMask = 0;
}
else
{
CollideMask = 0;
}
}
/// <summary>
/// Проверяет, проходим ли узел для заданной поисковой маски
/// </summary>
public bool IsPassable(uint? searchMask, bool CollideTo)
{
if (CollideTo)
{
if (searchMask != null)
return CollideToMask == FreePosition || ((CollideToMask & searchMask) != 0);
else
return CollideToMask == FreePosition;
}
else
{
if (searchMask != null)
return CollideMask == FreePosition || ((CollideMask & searchMask) != 0);
else
return CollideMask == FreePosition;
}
}
}
}
////////////////SHIT/////////////////
/// // public static float[][] GenerateSquareCenters(
// Vector2 topLeft,
// Vector2 topRight,
// Vector2 bottomLeft,
// Vector2 bottomRight,
// int gridSize = 1024)
// {
// var centers = new List<float[]>();
// // Шаг для параметров t и s (от 0 до 1)
// float step = 1.0f / gridSize;
// // Для каждой ячейки сетки
// for (int i = 0; i < gridSize; i++)
// {
// for (int j = 0; j < gridSize; j++)
// {
// // Находим параметры t и s для центра текущей ячейки
// float t = (i + 0.5f) * step; // +0.5 чтобы получить центр
// float s = (j + 0.5f) * step;
// // Билинейная интерполяция для нахождения координат центра
// float x = (1 - t) * (1 - s) * topLeft.X + // Top-left contribution
// t * (1 - s) * topRight.X + // Top-right contribution
// (1 - t) * s * bottomLeft.X + // Bottom-left contribution
// t * s * bottomRight.X; // Bottom-right contribution
// float y = (1 - t) * (1 - s) * topLeft.Y + // Top-left contribution
// t * (1 - s) * topRight.Y + // Top-right contribution
// (1 - t) * s * bottomLeft.Y + // Bottom-left contribution
// t * s * bottomRight.Y; // Bottom-right contribution
// centers.Add(new float[] { x, y });
// }
// }
// return centers.ToArray();
// }
// // Вспомогательная функция для проверки, находится ли точка внутри четырехугольника
// public static bool IsPointInsideQuadrilateral(Vector2 point, Vector2 topLeft, Vector2 topRight, Vector2 bottomLeft, Vector2 bottomRight)
// {
// // Создаем массив вершин в правильном порядке (против часовой стрелки)
// Vector2[] vertices = { topLeft, bottomLeft, bottomRight, topRight };
// return IsPointInPolygon(point, vertices);
// }
// public static bool IsPointInPolygon(Vector2 point, Vector2[] vertices)
// {
// int n = vertices.Length;
// bool inside = false;
// for (int i = 0, j = n - 1; i < n; j = i++)
// {
// if (((vertices[i].Y > point.Y) != (vertices[j].Y > point.Y)) &&
// (point.X < (vertices[j].X - vertices[i].X) * (point.Y - vertices[i].Y) / (vertices[j].Y - vertices[i].Y) + vertices[i].X))
// {
// inside = !inside;
// }
// }
// return inside;
// }
// // Альтернативная версия с более точным определением области четырехугольника
// public static List<TreePoint> GenerateSquareMatrixAdvanced(
// Vector2 topLeft,
// Vector2 topRight,
// Vector2 bottomLeft,
// Vector2 bottomRight,
// float squareSize = 1.0f)
// {
// var result = new List<TreePoint>();
// // Находим границы области
// float minX = Math.Min(Math.Min(topLeft.X, topRight.X), Math.Min(bottomLeft.X, bottomRight.X));
// float maxX = Math.Max(Math.Max(topLeft.X, topRight.X), Math.Max(bottomLeft.X, bottomRight.X));
// float minY = Math.Min(Math.Min(topLeft.Y, topRight.Y), Math.Min(bottomLeft.Y, bottomRight.Y));
// float maxY = Math.Max(Math.Max(topLeft.Y, topRight.Y), Math.Max(bottomLeft.Y, bottomRight.Y));
// // Генерируем квадраты с заданным размером
// for (float x = minX; x <= maxX - squareSize; x += squareSize)
// {
// for (float y = minY; y <= maxY - squareSize; y += squareSize)
// {
// // Проверяем углы квадрата
// Vector2 squareTopLeft = new Vector2(x, y);
// Vector2 squareTopRight = new Vector2(x + squareSize, y);
// Vector2 squareBottomLeft = new Vector2(x, y + squareSize);
// Vector2 squareBottomRight = new Vector2(x + squareSize, y + squareSize);
// // Если хотя бы один угол квадрата находится внутри четырехугольника,
// // или центр квадрата внутри - добавляем квадрат
// Vector2 center = new Vector2(x + squareSize / 2, y + squareSize / 2);
// if (IsPointInsideQuadrilateral(center, topLeft, topRight, bottomLeft, bottomRight) ||
// IsPointInsideQuadrilateral(squareTopLeft, topLeft, topRight, bottomLeft, bottomRight) ||
// IsPointInsideQuadrilateral(squareTopRight, topLeft, topRight, bottomLeft, bottomRight) ||
// IsPointInsideQuadrilateral(squareBottomLeft, topLeft, topRight, bottomLeft, bottomRight) ||
// IsPointInsideQuadrilateral(squareBottomRight, topLeft, topRight, bottomLeft, bottomRight))
// {
// var envelope = new Envelope
// {
// MinX = x,
// MinY = y,
// MaxX = x + squareSize,
// MaxY = y + squareSize
// };
// result.Add(new TreePoint(envelope));
// }
// }
// }
// return result;
// }
// public static Vector2[,] GenerateSquareCentersMatrix(
// Vector2 topLeft,
// Vector2 topRight,
// Vector2 bottomLeft,
// Vector2 bottomRight,
// int gridSize = 1024)
// {
// var centers = new Vector2[gridSize, gridSize];
// // Шаг для параметров t и s (от 0 до 1)
// float step = 1.0f / gridSize;
// // Для каждой ячейки сетки
// for (int i = 0; i < gridSize; i++)
// {
// for (int j = 0; j < gridSize; j++)
// {
// // Находим параметры t и s для центра текущей ячейки
// float t = (i + 0.5f) * step; // +0.5 чтобы получить центр
// float s = (j + 0.5f) * step;
// // Билинейная интерполяция для нахождения координат центра
// float x = (1 - t) * (1 - s) * topLeft.X + // Top-left contribution
// t * (1 - s) * topRight.X + // Top-right contribution
// (1 - t) * s * bottomLeft.X + // Bottom-left contribution
// t * s * bottomRight.X; // Bottom-right contribution
// float y = (1 - t) * (1 - s) * topLeft.Y + // Top-right contribution
// t * (1 - s) * topRight.Y + // Top-right contribution
// (1 - t) * s * bottomLeft.Y + // Bottom-left contribution
// t * s * bottomRight.Y; // Bottom-right contribution
// centers[i, j] = new Vector2(x, y);
// }
// }
// return centers;
// }