填充两条线之间的区域(不是直线)

时间:2014-01-20 09:13:07

标签: algorithm

enter image description here 我问的是算法允许用浅绿色填充黑色和深绿色区域之间的区域。黑色区域是固定的。绿色是黑色区域之间的痕迹。 黑色和深绿色的线条只能是4个方向中的1个 - 北,西,南和东。

我对算法有一些想法(不是算法),但认为它容易出错。

所以,我有开始和结束的坐标(完成正好是绿色触及黑色)。我有(xstart,ystart)和(xfinish,yfinish)坐标以及连接该坐标的黑色和深绿色线条。我的目标是在中间区域填充浅绿色。

如果我找到了我的解决方案并且它很短,我也会在这里发布。 对此算法的任何想法都非常感谢。 BTW,由1x1的小矩形组成。因此,可以使用高度(或宽度)或1的线条对区域进行着色。

一旦找到算法,我会尝试在此处发布(如果这不是某人的算法)或提供链接。

谢谢。

P.S。我的第一个想法是关注那些(总是?)偶数行穿过任何水平或垂直线的行。

4 个答案:

答案 0 :(得分:3)

你可以从上到下循环“图像”。

对于每一行,您将从左向右循环,从“外部”开始。每次你穿过你正在看的当前线的垂直线时,你会翻转“外/内”位。

然后你将里面的所有方块着色。

这是一个LINQPad程序,演示了:

const int scale = 20;

void Main()
{
    var polyline = new[]
    {
        new Point(4, 0),
        new Point(4, 5),
        new Point(10, 5),
        new Point(10, 10),
        new Point(6, 10),
        new Point(6, 3),
        new Point(15, 3),
        new Point(15, 8),
        new Point(14, 8),
        new Point(14, 7),
        new Point(16, 7),
        new Point(16, 0),
    };

    int maxY = polyline.Max(point => point.Y);
    int maxX = polyline.Max(point => point.X);

    var bitmap = new Bitmap((maxX + 1) * scale, (maxY + 1) * scale);
    var previousPoint = polyline[0];

    using (var g = Graphics.FromImage(bitmap))
    {
        // TODO: y=0 should be y = minY - 1
        for (int y = 0; y < maxY + 1; y++)
        {
            bool isInside = false;
            var xCoordinatesOfCrossingLines = new HashSet<int>(
                from index in Enumerable.Range(0, polyline.Length)
                let p1 = polyline[index]
                let p2 = polyline[(index + 1) % polyline.Length]
                where p1.X == p2.X
                where (p1.Y <= y && p2.Y > y)       // must cross the y-slice in downwards
                      || (p2.Y <= y && p1.Y > y)    // or upwards direction
                let x = p1.X
                group x by x into xGroup            // if we somehow have 2 (or an even number of) lines overlapping, don't count them
                where xGroup.Count() % 2 != 0       // so we will only except distinct x values that occur 1, 3, 5, etc. times
                select xGroup.Key);

            // TODO: x=0 should be x = minX - 1
            for (int x = 0; x < maxX + 1; x++)
            {
                // Every time we hit a vertical line, we flip the "is inside" bit
                if (xCoordinatesOfCrossingLines.Contains(x))
                    isInside = !isInside;

                // Colorize all the squares inside
                if (isInside)
                    g.FillRectangle(Brushes.Green, new Rectangle(
                        ScalePoint(new Point(x, y), scale),
                        new Size(scale, scale)));
            }
        }
        for (int index = 1; index <= polyline.Length; index++)
        {
            g.DrawLine(Pens.Black, ScalePoint(previousPoint, scale), ScalePoint(polyline[index % polyline.Length], scale));
            previousPoint = polyline[index % polyline.Length];
        }
    }
    bitmap.Dump();
}

public Point ScalePoint(Point p, int scale)
{
    return new Point(p.X * scale, p.Y * scale);
}

输出:

LINQPad output

答案 1 :(得分:1)

这是标准的填充填充或栅格填充算法。我猜它是在30多年前解决的。您可以在任何标准教科书或网络上找到它。以下是指向起点的链接:https://en.wikipedia.org/wiki/Flood_fill

如果没有填充像素或光栅线,基本上你会走线。每次穿越后,您都会切换到另一侧。让它工作很容易。快速行动很难。

如果您正在使用从Windows GDI到OpenGL的任何类型的图形库到GPU着色器代码,它已经内置。

这里的一些代码是思想的来源:http://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm

答案 2 :(得分:1)

我解决了一些密切相关的问题,以便对位图图形进行“完美”跟踪。 “完美”是指迹线完美地跟随位图边界(而不是通常优越的推断成角度线和曲线的方法)。一旦有了矢量边界,转换回像素就只是多边形渲染 - 实际上是一个简单的例子,因为所有边都是水平或垂直的,而且是像素之间的边界。

我计划使用相关的想法从其他矢量图形生成矢量图形(例如,生成精确填充非零宽度多边形边缘内部的填充)但尚未完成,所以没有更新笔记。通过将线条用作矢量形状而不是已经渲染的像素,您也可以从这种方法中受益。

我的4shared帐户here上有一张啰嗦的文档,上面有一些漂亮的图片。我会尽力把它留在那里。

快速摘要 - 核心是定义一组像素长的像素间边界,以便在遵循边缘时,将其从仍需要遵循的集合中移除。然后选择一个起点,顺时针跟随边界,直到你回到起点,然后重复,直到你的边界用完为止。

对我来说,一个技术性问题是我需要处理多色图像,结果表明每个像素边界都遵循两次 - 每个方向一次(因为每侧有一个填充区域)。我怀疑你需要那个 - 你的黑色和绿色线条只是为了这个目的的边界,所以你可以假装它们是相同的颜色。

另一个技术性涉及交叉,穿孔和缠绕规则的线条,看起来你关心它。

答案 3 :(得分:0)

这是一种方法: -

  
      
  1. 从任何未填充的点进行填充
  2.   
  3. 如果在洪水填充期间遇到图像边界,则用任意颜色重新填充填充中的所有点。
  4.   
  5. 如果洪水填充仅通过黑色或绿色线终止,则使用浅绿色
  6.   
  7. 将任意颜色更改为白色
  8.