Files
better-genshin-impact/BetterGenshinImpact/GameTask/Model/GameUI/GridCell.cs
2025-11-24 11:03:39 +08:00

163 lines
6.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Linq;
namespace BetterGenshinImpact.GameTask.Model.GameUI
{
/// <summary>
/// 具有行号列号的单元格
/// ColNum和RowNum也是0-based的
/// 不仅方便编程ClusterToCells方法也需要一个引用类型
/// </summary>
/// <param name="rect"></param>
public class GridCell(Rect rect)
{
public Rect Rect = rect;
public int ColNum;
public int RowNum;
/// <summary>
/// 表示该单元格并非CV方法识别得到而是通过算法推测出的
/// </summary>
public bool IsPhantom;
/// <summary>
/// 把检出的矩形聚簇成类似Excel的单元格集合
/// </summary>
/// <param name="rects"></param>
/// <param name="threshold"></param>
/// <returns></returns>
public static IEnumerable<GridCell> ClusterToCells(IEnumerable<Rect> rects, int threshold)
{
return ClusterToCells(rects.Select(r => Tuple.Create(0, r)), threshold).Select(t => t.Item2).ToArray();
}
/// <summary>
/// 把检出的矩形聚簇成类似Excel的单元格集合
/// </summary>
/// <param name="rects"></param>
/// <param name="threshold"></param>
/// <returns></returns>
public static IEnumerable<Tuple<T, GridCell>> ClusterToCells<T>(IEnumerable<Tuple<T, Rect>> rects, int threshold)
{
if (!rects.Any())
{
return [];
}
var result = rects.Select(r => new Tuple<T, GridCell>(r.Item1, new GridCell(r.Item2)));
result = result.ToArray(); // 必需,不然引用会丢掉。。
var orderByX = result.OrderBy(t => t.Item2.Rect.Left).ToArray();
int col = 0;
int? lastX = null;
int avgWidth = (int)rects.Average(r => r.Item2.Width);
for (int i = 0; i < orderByX.Length; i++)
{
if (lastX != null && orderByX[i].Item2.Rect.X - lastX > threshold)
{
col += (int)Math.Round((float)(orderByX[i].Item2.Rect.X - lastX.Value) / (avgWidth + threshold));
}
orderByX[i].Item2.ColNum = col;
lastX = orderByX[i].Item2.Rect.X;
}
var orderByY = result.OrderBy(t => t.Item2.Rect.Top).ToArray();
int row = 0;
int? lastY = null;
int avgHeight = (int)rects.Average(r => r.Item2.Height);
for (int i = 0; i < orderByY.Length; i++)
{
if (lastY != null && orderByY[i].Item2.Rect.Y - lastY > threshold)
{
row += (int)Math.Round((float)(orderByY[i].Item2.Rect.Y - lastY.Value) / (avgHeight + threshold)); // 估算隔了多少行
}
orderByY[i].Item2.RowNum = row;
lastY = orderByY[i].Item2.Rect.Y;
}
return result;
}
/// <summary>
/// 遍历方阵补上缺的Cell
/// </summary>
/// <param name="cells"></param>
public static void FillMissingGridCells(ref List<GridCell> cells)
{
if (cells.Count <= 0)
{
return;
}
double avgWidth = cells.Average(c => c.Rect.Width);
double avgHeight = cells.Average(c => c.Rect.Height);
double avgColSpacing;
double avgRowSpace;
{
int count = 0;
int sum = 0;
foreach (var row in cells.GroupBy(t => t.RowNum))
{
for (int i = 0; i < row.Max(r => r.ColNum); i++)
{
var x1 = row.SingleOrDefault(r => r.ColNum == i);
var x2 = row.SingleOrDefault(r => r.ColNum == i + 1);
if (x1 == null || x2 == null)
{
continue;
}
sum += x2.Rect.X - x1.Rect.X - x1.Rect.Width;
count++;
}
}
avgColSpacing = count == 0 ? 0 : ((double)sum) / count;
}
{
int count = 0;
int sum = 0;
foreach (var col in cells.GroupBy(t => t.ColNum))
{
for (int i = 0; i < col.Max(r => r.RowNum); i++)
{
var y1 = col.SingleOrDefault(r => r.RowNum == i);
var y2 = col.SingleOrDefault(r => r.RowNum == i + 1);
if (y1 == null || y2 == null)
{
continue;
}
sum += y2.Rect.Y - y1.Rect.Y - y1.Rect.Height;
count++;
}
}
avgRowSpace = count == 0 ? 0 : ((double)sum) / count;
}
double avgLeft = cells.Average(c => c.Rect.X - (avgWidth + avgColSpacing) * c.ColNum);
double avgTop = cells.Average(c => c.Rect.Y - (avgHeight + avgRowSpace) * c.RowNum);
for (int i = 0; i < cells.Max(r => r.ColNum) + 1; i++)
{
for (int j = 0; j < cells.Max(r => r.RowNum) + 1; j++)
{
if (cells.Any(c => c.ColNum == i && c.RowNum == j))
{
continue;
}
int x = (int)Math.Round(avgLeft + (avgWidth + avgColSpacing) * i, MidpointRounding.AwayFromZero);
int y = (int)Math.Round(avgTop + (avgHeight + avgRowSpace) * j, MidpointRounding.AwayFromZero);
int width = (int)Math.Round(avgWidth, MidpointRounding.AwayFromZero);
int height = (int)Math.Round(avgHeight, MidpointRounding.AwayFromZero);
GridCell cell = new GridCell(new Rect(x, y, width, height))
{
ColNum = i,
RowNum = j,
IsPhantom = true
};
cells.Add(cell);
}
}
}
}
}