算法并非完美,欢迎大佬指正!
代码如下:
public class CellExtract
{
/// <summary>
/// 根据表格线计算单元格
/// </summary>
/// <param name="horiLines">水平表格线</param>
/// <param name="vertLines">垂直表格线</param>
/// <returns></returns>
public static XExtents[] CalExtents2dByLines(XLine[] horiLines, XLine[] vertLines)
{
if (horiLines.Length == 0 || vertLines.Length == 0)
{
return new XExtents[0];
}
XPoint[,] insectPoints = CalCellPoint(horiLines, vertLines);
double tol = horiLines[0].Length() * 0.002;//控制精度,以便提升性能
XExtents[] xEntentsArry = CalExtents2dByPoints(insectPoints);//垂直和水平直线构成的单元格
List<XExtents> fitlerExt = new List<XExtents>();
foreach (XExtents xExtents in xEntentsArry)
{
try
{
//判断单元格四条边的中点是否在表格线的间隔范围内
XPoint left = new XPoint(xExtents.MinPoint.X, (xExtents.MinPoint.Y + xExtents.MaxPoint.Y) * 0.5);
XPoint right = new XPoint(xExtents.MaxPoint.X, (xExtents.MinPoint.Y + xExtents.MaxPoint.Y) * 0.5);
XPoint top = new XPoint((xExtents.MinPoint.X + xExtents.MaxPoint.X) * 0.5, xExtents.MaxPoint.Y);
XPoint bottom = new XPoint((xExtents.MinPoint.X + xExtents.MaxPoint.X) * 0.5, xExtents.MinPoint.Y);
XLine[] leftline = vertLines.Where(t => Math.Abs(t.StartPt.X - left.X) < tol || Math.Abs(t.EndPt.X - left.X) < tol).ToArray();
bool leftflag = leftline.Length > 0 && leftline.Any(t => t.In(left));
XLine[] rightline = vertLines.Where(t => Math.Abs(t.StartPt.X - right.X) < tol || Math.Abs(t.EndPt.X - right.X) < tol).ToArray();
bool rightflag = rightline.Length > 0 && rightline.Any(t => t.In(right));
XLine[] topline = horiLines.Where(t => Math.Abs(t.StartPt.Y - top.Y) < tol || Math.Abs(t.EndPt.Y - top.Y) < tol).ToArray();
bool topflag = topline.Length > 0 && topline.Any(t => t.In(top));
XLine[] bottomline = horiLines.Where(t => Math.Abs(t.StartPt.Y - bottom.Y) < tol || Math.Abs(t.EndPt.Y - bottom.Y) < tol).ToArray();
bool bottomflag = bottomline.Length > 0 && bottomline.Any(t => t.In(bottom));
if (leftflag && rightflag && topflag && bottomflag)
{
fitlerExt.Add(xExtents);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
return fitlerExt.ToArray();
}
/// <summary>
/// 计算横线段和竖线段的所有交点
/// </summary>
/// <param name="horlines">网格中所有横线</param>
/// <param name="verlines">网格中所有竖线</param>
/// <returns></returns>/
public static XPoint[,] CalCellPoint(XLine[] horlines, XLine[] verlines)
{
XPoint[,] points = new XPoint[horlines.Length + 2, verlines.Length + 2];
int iiii = -1;
int jjjj = -1;
try
{
for (int i = 0; i < horlines.Length; i++)
{
for (int j = 0; j < verlines.Length; j++)
{
XPoint sectPnt = XLine.IntersectWith(horlines[i], verlines[j], 0.01);
points[i + 1, j + 1] = sectPnt;
iiii = i;
jjjj = j;
}
}
//用线的起点终点补齐交点集周围一圈点
for (int i = 0; i < horlines.Length; i++)
{
points[i + 1, 0] = horlines[i].StartPt.X < horlines[i].EndPt.X ? horlines[i].StartPt : horlines[i].EndPt;
points[i + 1, verlines.Length + 1] = horlines[i].StartPt.X > horlines[i].EndPt.X ? horlines[i].StartPt : horlines[i].EndPt;
}
for (int j = 0; j < verlines.Length; j++)
{
points[0, j + 1] = verlines[j].StartPt;
points[horlines.Length + 1, j + 1] = verlines[j].EndPt;
}
points[0, 0] = new XPoint(double.NaN, double.NaN);
points[horlines.Length + 1, 0] = new XPoint(double.NaN, double.NaN);
points[horlines.Length + 1, verlines.Length + 1] = new XPoint(double.NaN, double.NaN);
points[0, verlines.Length + 1] = new XPoint(double.NaN, double.NaN);
}
catch (Exception e)
{
string text = iiii + "-" + jjjj;//调试代码,用于定位求交出错的线
Console.WriteLine($"【{text}】{e.Message}");
}
return points;
}
/// <summary>
/// 根据二维点列,计算单元格
/// </summary>
/// <param name="points">二维点列</param>
/// <returns></returns>
private static XExtents[] CalExtents2dByPoints(XPoint[,] points)
{
List<XExtents> xExtentsList = new List<XExtents>();
int rowCount = points.GetLength(0);
int colCount = points.GetLength(1);
for (int i = 0; i < rowCount; i++)
{
for (int j = 0; j < colCount; j++)
{
if (XPoint.IsNaN(points[i, j])) continue;
XPoint[] p = new XPoint[4];//P1左上,P2右上,P3右下,P4左下
p[0] = points[i, j];//找到第一个点P1
//第一步,向右找
int k;
for (k = j + 1; k < colCount; k++)
{
if (!XPoint.IsNaN(points[i, k]))
{
p[1] = points[i, k];//找到第二个点P2
//第二步,P1,P2同时向下找
for (int s = i + 1; s < rowCount; s++)
{
if ((!XPoint.IsNaN(points[s, j])) && (!XPoint.IsNaN(points[s, k])))
{
p[2] = points[s, k];//找到第三个点P3
p[3] = points[s, j];//找到第四个点P4
if (XPoint.JugeRect(p[0], p[1], p[2], p[3]) || XPoint.JugeRect(p[0], p[2], p[1], p[3]))//判断4个点是否构成正常的矩形
{
double minX = Min(new List<double>() { p[0].X, p[1].X, p[2].X, p[3].X });
double maxX = Max(new List<double>() { p[0].X, p[1].X, p[2].X, p[3].X });
double minY = Min(new List<double>() { p[0].Y, p[1].Y, p[2].Y, p[3].Y });
double maxY = Max(new List<double>() { p[0].Y, p[1].Y, p[2].Y, p[3].Y });
XPoint minPt = new XPoint(minX, minY);
XPoint maxPt = new XPoint(maxX, maxY);
XExtents xExtents = new XExtents(minPt, maxPt);
xExtentsList.Add(xExtents);
}
}
}
}
}
}
}
for (int i = 0; i < rowCount; i++)
{
for (int j = 0; j < colCount; j++)
{
if (XPoint.IsNaN(points[i, j])) continue;
XPoint[] p = new XPoint[4];//P1左上,P2右上,P3右下,P4左下
p[0] = points[i, j];//找到第一个点P1
//第一步,向下找
int s = j + 1;
for (int k = i + 1; k < rowCount; k++)
{
if (!XPoint.IsNaN(points[k, j]))
{
p[1] = points[k, j];//找到第二个点P2
//第二步,P1,P2同时向右找
for (s = j + 1; s < colCount; s++)
{
if ((!XPoint.IsNaN(points[i, s])) && (!XPoint.IsNaN(points[k, s])))
{
p[2] = points[k, s];//找到第三个点P3
p[3] = points[i, s];//找到第四个点P4
if (XPoint.JugeRect(p[0], p[3], p[2], p[1]) || XPoint.JugeRect(p[0], p[2], p[1], p[3]))//判断4个点是否构成正常的矩形
{
double minX = Min(new List<double>() { p[0].X, p[1].X, p[2].X, p[3].X });
double maxX = Max(new List<double>() { p[0].X, p[1].X, p[2].X, p[3].X });
double minY = Min(new List<double>() { p[0].Y, p[1].Y, p[2].Y, p[3].Y });
double maxY = Max(new List<double>() { p[0].Y, p[1].Y, p[2].Y, p[3].Y });
XPoint minPt = new XPoint(minX, minY);
XPoint maxPt = new XPoint(maxX, maxY);
XExtents xExtents = new XExtents(minPt, maxPt);
xExtentsList.Add(xExtents);
}
}
}
}
}
}
}
return xExtentsList.ToArray();
}
/// <summary>
/// 获取集合中的最小值
/// </summary>
/// <param name="list"></param>
/// <returns></returns>
public static double Min(IList<double> list)
{
if (list.Count == 0)
{
return 0;
}
double temp = double.MaxValue;
for (int i = 0; i < list.Count; i++)
{
if (temp > list[i]) temp = list[i];
}
return temp;
}
/// <summary>
/// 获取集合中的最大值
/// </summary>
/// <param name="list"></param>
/// <returns></returns>
public static double Max(IList<double> list)
{
if (list.Count == 0)
{
return 0;
}
double temp = double.MinValue;
for (int i = 0; i < list.Count; i++)
{
if (temp < list[i]) temp = list[i];
}
return temp;
}
}
代码中,对于点、线、面的相关操作,详见:C#平面几何简单操作(点、线、面)