首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >对矩形进行水平和垂直分组

对矩形进行水平和垂直分组
EN

Code Review用户
提问于 2016-06-16 15:50:50
回答 2查看 583关注 0票数 5

如您所见,除了使用的属性外,每个方法的代码都是相同的。例如,XYWidthHeight。无论如何,我是否可以重构代码以使用一个通用的主方法来更改它所使用的属性?

备注:

all caps常量是BOX_LINEUP_TOLERANCE,它的值为0.15f,并且是由于公司策略所致的所有大写。

这两种方法:

代码语言:javascript
运行
复制
private List<Rectangle[]> GroupRectanglesHorizontally(Rectangle[] rectangles, out int averageGap)
{
    List<int> nlGaps = new List<int>();
    List<Rectangle[]> rectangleGroups = new List<Rectangle[]>();
    for (int bi = 0, bc = rectangles.Length; bi < bc; ++bi)
    {
        if (rectangleGroups.Any(g => g.Contains(rectangles[bi])))
        {
            continue;
        }

        int nTolerance = (int)Math.Ceiling(rectangles[bi].Height * BOX_LINEUP_TOLERANCE);
        Rectangle[] baSimiliarHeight = rectangles.Where(b => Math.Abs(b.Y - rectangles[bi].Y) <= nTolerance
                                                            && b.X > rectangles[bi].X)
                                                    .ToArray();
        if (baSimiliarHeight.Length <= 1)
        {
            rectangleGroups.Add(new Rectangle[] { rectangles[bi] });
            continue;
        }
        baSimiliarHeight = baSimiliarHeight.OrderBy(b => b.X).ToArray();

        int nInitialGap = baSimiliarHeight[0].X - (rectangles[bi].X + rectangles[bi].Width);
        int nInitialGapTolerance = (int)Math.Ceiling(nInitialGap * BOX_LINEUP_TOLERANCE);
        nlGaps.Add(nInitialGap);

        List<Rectangle> rectangleList = new List<Rectangle>();
        rectangleList.Add(rectangles[bi]);
        rectangleList.Add(baSimiliarHeight[0]);

        for (int sbi = 1; sbi < baSimiliarHeight.Length; ++sbi)
        {
            int nGap = baSimiliarHeight[sbi].X - (baSimiliarHeight[sbi - 1].X + baSimiliarHeight[sbi - 1].Width);
            if (nGap <= nInitialGap + nInitialGapTolerance && nGap >= nInitialGap - nInitialGapTolerance)
            {
                rectangleList.Add(baSimiliarHeight[sbi]);
                nlGaps.Add(nGap);
            }
            else if (nGap > nInitialGap)
            {
                break;
            }
            else
            {
                //Initial gap was larger so first rectangle is in its own group
                rectangleList = new List<Rectangle>(new Rectangle[] { rectangles[bi] });
                break;
            }
        }

        rectangleGroups.Add(rectangleList.ToArray());
    }

    averageGap = (int)nlGaps.Average();
    return rectangleGroups;
}

private List<Rectangle[]> GroupRectanglesVertically(Rectangle[] rectangles, out int averageGap)
{
    List<int> nlGaps = new List<int>();
    List<Rectangle[]> rectangleGroups = new List<Rectangle[]>();
    for (int bi = 0, bc = rectangles.Length; bi < bc; ++bi)
    {
        if (rectangleGroups.Any(g => g.Contains(rectangles[bi])))
        {
            continue;
        }

        int nTolerance = (int)Math.Ceiling(rectangles[bi].Width * BOX_LINEUP_TOLERANCE);
        Rectangle[] baSimiliarWidth = rectangles.Where(b => Math.Abs(b.X - rectangles[bi].X) <= nTolerance
                                                            && b.Y > rectangles[bi].Y)
                                                .ToArray();
        if (baSimiliarWidth.Length <= 1)
        {
            rectangleGroups.Add(new Rectangle[] { rectangles[bi] });
            continue;
        }
        baSimiliarWidth = baSimiliarWidth.OrderBy(b => b.Y).ToArray();

        int nInitialGap = baSimiliarWidth[0].Y - (rectangles[bi].Y + rectangles[bi].Height);
        int nInitialGapTolerance = (int)Math.Ceiling(nInitialGap * BOX_LINEUP_TOLERANCE);
        nlGaps.Add(nInitialGap);

        List<Rectangle> rectangleList = new List<Rectangle>();
        rectangleList.Add(rectangles[bi]);
        rectangleList.Add(baSimiliarWidth[0]);

        for (int sbi = 1; sbi < baSimiliarWidth.Length; ++sbi)
        {
            int nGap = baSimiliarWidth[sbi].Y - (baSimiliarWidth[sbi - 1].Y + baSimiliarWidth[sbi - 1].Height);
            if (nGap <= nInitialGap + nInitialGapTolerance && nGap >= nInitialGap - nInitialGapTolerance)
            {
                rectangleList.Add(baSimiliarWidth[sbi]);
                nlGaps.Add(nGap);
            }
            else if (nGap > nInitialGap)
            {
                break;
            }
            else
            {
                //Initial gap was larger so first rectangle is in its own group
                rectangleList = new List<Rectangle>(new Rectangle[] { rectangles[bi] });
                break;
            }
        }

        rectangleGroups.Add(rectangleList.ToArray());
    }

    averageGap = (int)nlGaps.Average();
    return rectangleGroups;
}

我也将感谢对代码的一般反馈。

EN

回答 2

Code Review用户

回答已采纳

发布于 2016-06-16 16:30:53

您可以使用布尔值作为参数,并根据该参数选择X或Y和宽度或高度。您可以这样声明该方法:

代码语言:javascript
运行
复制
private List<Rectangle[]> GroupRectangles(Rectangle[] rectangles, bool horizontally, out int averageGap)

可以使用水平方法中的代码,并修改属性调用以使用布尔值。例如,您可以将rectangle.X替换为horizontally ? rectangle.X : rectangle.Y,将rectangle.Width替换为horizontally ? rectangle.Width : rectangle.Height

然后,可以在以前的方法中使用它:

代码语言:javascript
运行
复制
private List<Rectangle[]> GroupRectanglesHorizontally(Rectangle[] rectangles, out int averageGap)
{
    return GroupRectangles(rectangles, true, out averageGap);
}


private List<Rectangle[]> GroupRectanglesVertically(Rectangle[] rectangles, out int averageGap)
{
    return GroupRectangles(rectangles, false, out averageGap);
}

或者,您可以使用函数作为参数来选择属性,比如在OrderBy()中使用lambda表达式。

编辑:对于第一个方法本身:

在以下代码行中:

代码语言:javascript
运行
复制
Rectangle[] baSimiliarHeight = rectangles.Where(b => Math.Abs(b.Y - rectangles[bi].Y) <= nTolerance
                                                        && b.X > rectangles[bi].X)
                                                .ToArray();

它不会得到位于完全相同X位置的矩形。但如果保证所有矩形都不重叠,那就无关紧要了。然后是下面的代码:

代码语言:javascript
运行
复制
if (baSimiliarHeight.Length <= 1)
{
    rectangleGroups.Add(new Rectangle[] { rectangles[bi] });
    continue;
}

因为以前没有匹配具有相同X位置的矩形,所以baSimiliarHeight将不包含rectangles[bi],因此如果它的长度为1,那么该1元素将是一个不同的矩形,但只有原始矩形才会作为一个组被添加。

在内部for循环中,最后一个else的主体只添加初始矩形,而不对以前添加的nGaps做任何操作,因此循环可以添加一组nGaps,但只添加初始矩形。

如果你马上得到一个矩形的列表,按照它们的X位置排序,然后从原来的列表中循环出来,你可以在for循环的正文中选择一些快捷键。

编辑2:

您应该添加更多的描述性名称。我不知道bibc,nl in nlGaps等代表什么。对于lambda表达式和循环变量来说,短名称是可以的,但是即使这样,您也应该坚持使用xyij或与对象相关的字母。我认为对矩形使用r比使用b更有意义。

票数 4
EN

Code Review用户

发布于 2016-06-16 16:52:22

为了保持代码整洁,您可以定义委托:

代码语言:javascript
运行
复制
delegate int AxisLocationDelegate(Rectangle r, bool primaryAxis);
delegate int AxisSizeDelegate(Rectangle r, bool primaryAxis);

然后定义一个统一的GroupRectanglesBy函数,它将每个委托的实现作为参数:

代码语言:javascript
运行
复制
private List<Rectangle[]> GroupRectanglesBy(Rectangle[] rectangles,
    AxisLocationDelegate getLocation, AxisSizeDelegate getSize, out int averageGap)
{
    // ...

在主函数中,您可以引用委托函数getLocationgetSize,并指定您对主轴还是次轴感兴趣,例如:

代码语言:javascript
运行
复制
    int nTolerance = (int)Math.Ceiling(getSize(rectangles[bi], false) * BOX_LINEUP_TOLERANCE);
    Rectangle[] baSimiliarMagnitude = rectangles.Where(
        b => Math.Abs(getLocation(b, false) - getSize(rectangles[bi], false)) <= nTolerance
                && getLocation(b, true) > getLocation(rectangles[bi], true))
        .ToArray();

然后使用选择相关属性的委托的水平/垂直实现进行调用:

代码语言:javascript
运行
复制
private List<Rectangle[]> GroupRectanglesHorizontally(Rectangle[] rectangles, out int averageGap)
{
    return GroupRectanglesBy(
        rectangles,
        (rect, primaryAxis) => primaryAxis ? rect.Location.X : rect.Location.Y,
        (rect, primaryAxis) => primaryAxis ? rect.Width : rect.Height,
        out averageGap);
}

private List<Rectangle[]> GroupRectanglesVertically(Rectangle[] rectangles, out int averageGap)
{
    return GroupRectanglesBy(
        rectangles,
        (rect, primaryAxis) => primaryAxis ? rect.Location.Y : rect.Location.X,
        (rect, primaryAxis) => primaryAxis ? rect.Height : rect.Width,
        out averageGap);
}

编辑:如果您感觉特别糟糕,并且希望使用布尔XOR运算符^,您甚至可以将其简化为一个函数:

代码语言:javascript
运行
复制
private List<Rectangle[]> GroupRectangles(Rectangle[] rectangles, bool vertically, out int averageGap)
{
    return GroupRectanglesBy(
        rectangles,
        (rect, primaryAxis) => primaryAxis ^ vertically ? rect.Location.X : rect.Location.Y,
        (rect, primaryAxis) => primaryAxis ^ vertically ? rect.Width : rect.Height,
        out averageGap);
}
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/132203

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档