JustPaste.it

кастомная коллизия

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