一个简单的线类被定义为包含起点和终点坐标的两个PointF成员:
public class Line {
PointF s, e;
}
我有两个列表,其中包含出现在绘图画布上并形成一个或多个表格的所有水平和垂直线。
List<Line> AllHorizontalLines;
List<Line> AllVerticalLines;
我需要对这些行进行分组,以便属于一个表的行被捕获在一个组中,因此分组函数将具有如下签名:
List<List<Line>> GroupLines(List<Line> hor, List<Line> ver)
{
}
为简单起见,我们假设页面上只有“简单”表,即没有嵌套表。但是可以有合并的单元格,因此我们必须忽略小于父表的完整高度的小水平和垂直线。为了进一步简化,假设两个输入列表都已排序(水平线w.r.t. Y轴和垂直线w.r.t.X轴)。
有没有已知的算法来解决这个问题?或者任何人都可以帮我设计一个?
答案 0 :(得分:1)
以下似乎有效:
这是我得到的代码:
public static IEnumerable<IEnumerable<Line>> GroupLines(IEnumerable<Line> lines)
{
var grouped = new Dictionary<Rectangle, IEnumerable<Line>>();
foreach (var line in lines)
{
var boundedLines = new List<Line>(new[] { line });
IEnumerable<Rectangle> crossedRectangles;
var boundingRectangle = CalculateRectangle(boundedLines);
while (
(crossedRectangles = grouped.Keys
.Where(r => Crosses(boundingRectangle, r))
.ToList()
).Any())
{
foreach (var crossedRectangle in crossedRectangles)
{
boundedLines.AddRange(grouped[crossedRectangle]);
grouped.Remove(crossedRectangle);
}
boundingRectangle = CalculateRectangle(boundedLines);
}
grouped.Add(boundingRectangle, boundedLines);
}
return grouped.Values;
}
public static bool Crosses(Rectangle r1, Rectangle r2)
{
return !(r2.Left > r1.Right ||
r2.Right < r1.Left ||
r2.Top > r1.Bottom ||
r2.Bottom < r1.Top);
}
public static Rectangle CalculateRectangle(IEnumerable<Line> lines)
{
Rectangle rtn = new Rectangle
{
Left = int.MaxValue,
Right = int.MinValue,
Top = int.MaxValue,
Bottom = int.MinValue
};
foreach (var line in lines)
{
if (line.P1.X < rtn.Left) rtn.Left = line.P1.X;
if (line.P2.X < rtn.Left) rtn.Left = line.P2.X;
if (line.P1.X > rtn.Right) rtn.Right = line.P1.X;
if (line.P2.X > rtn.Right) rtn.Right = line.P2.X;
if (line.P1.Y < rtn.Top) rtn.Top = line.P1.Y;
if (line.P2.Y < rtn.Top) rtn.Top = line.P2.Y;
if (line.P1.Y > rtn.Bottom) rtn.Bottom = line.P1.Y;
if (line.P2.Y > rtn.Bottom) rtn.Bottom = line.P2.Y;
}
return rtn;
}
答案 1 :(得分:1)
以下是我建议的方法:
<强>代码:强>
public static IEnumerable<IEnumerable<Line>> Group(IEnumerable<Line> horizontalLines, IEnumerable<Line> verticalLines)
{
// Clean the input lists here. I'll leave the implementation up to you.
var ungroupedLines = new HashSet<Line>(horizontalLines.Concat(verticalLines));
var groupedLines = new List<List<Line>>();
while (ungroupedLines.Count > 0)
{
var group = new List<Line>();
var unprocessedLines = new HashSet<Line>();
unprocessedLines.Add(ungroupedLines.TakeFirst());
while (unprocessedLines.Count > 0)
{
var line = unprocessedLines.TakeFirst();
group.Add(line);
unprocessedLines.AddRange(ungroupedLines.TakeIntersectingLines(line));
}
groupedLines.Add(group);
}
return groupedLines;
}
public static class GroupingExtensions
{
public static T TakeFirst<T>(this HashSet<T> set)
{
var item = set.First();
set.Remove(item);
return item;
}
public static IEnumerable<Line> TakeIntersectingLines(this HashSet<Line> lines, Line line)
{
var intersectedLines = lines.Where(l => l.Intersects(line)).ToList();
lines.RemoveRange(intersectedLines);
return intersectedLines;
}
public static void RemoveRange<T>(this HashSet<T> set, IEnumerable<T> itemsToRemove)
{
foreach(var item in itemsToRemove)
{
set.Remove(item);
}
}
public static void AddRange<T>(this HashSet<T> set, IEnumerable<T> itemsToAdd)
{
foreach(var item in itemsToAdd)
{
set.Add(item);
}
}
}
将此方法添加到Line
public bool Intersects(Line other)
{
// Whether this line intersects the other line or not.
// I'll leave the implementation up to you.
}
备注:强>
如果此代码运行得太慢,您可能需要水平扫描,随时选择连接的线路。可能还值得看this。
<强>专门:强>
public static IEnumerable<IEnumerable<Line>> Group(IEnumerable<Line> horizontalLines, IEnumerable<Line> verticalLines)
{
// Clean the input lists here. I'll leave the implementation up to you.
var ungroupedHorizontalLines = new HashSet<Line>(horizontalLines);
var ungroupedVerticalLines = new HashSet<Line>(verticalLines);
var groupedLines = new List<List<Line>>();
while (ungroupedHorizontalLines.Count + ungroupedVerticalLines.Count > 0)
{
var group = new List<Line>();
var unprocessedHorizontalLines = new HashSet<Line>();
var unprocessedVerticalLines = new HashSet<Line>();
if (ungroupedHorizontalLines.Count > 0)
{
unprocessedHorizontalLines.Add(ungroupedHorizontalLines.TakeFirst());
}
else
{
unprocessedVerticalLines.Add(ungroupedVerticalLines.TakeFirst());
}
while (unprocessedHorizontalLines.Count + unprocessedVerticalLines.Count > 0)
{
while (unprocessedHorizontalLines.Count > 0)
{
var line = unprocessedHorizontalLines.TakeFirst();
group.Add(line);
unprocessedVerticalLines.AddRange(ungroupedVerticalLines.TakeIntersectingLines(line));
}
while (unprocessedVerticalLines.Count > 0)
{
var line = unprocessedVerticalLines.TakeFirst();
group.Add(line);
unprocessedHorizontalLines.AddRange(ungroupedHorizontalLines.TakeIntersectingLines(line));
}
}
groupedLines.Add(group);
}
return groupedLines;
}
这假设没有线重叠,因为它不检查水平线是否接触其他水平线(垂直相同)。
您可以删除if-else。只是存在垂直线没有连接到水平线的情况。