任意形状笔画绘制

3
我需要使用自定义光圈在位图上绘制线条。在下面的示例中,有两条线,左边的线是水平的,右边的线是对角线-向下和向右:Example image 在示例图像中,红色显示了光圈和路径。黑色显示了结果几何形状。上述两个示例都使用相同的光圈,但沿不同路径擦拭。
我想能够使用任意形状的笔进行绘制,即使上述示例仅显示矩形也可以。
我尝试使用System.Drawing.2D命名空间,但似乎无法满足我的需求。使用笔上的自定义端点似乎很有前途,但端点随着线条方向旋转。此外,正确设置线宽似乎具有挑战性。failed end-caps example 我考虑过多次绘制光圈,在路径的不同点居中绘制,但这似乎不够高效。还很难最小化光圈绘制的数量。
我能想到的最好主意是尝试将“线条”绘制为填充形状。起初,我认为一个凸包算法就是答案-只需在绘制开始和结束时取光圈的顶点并通过凸包算法找到“外部”顶点。这适用于上面的第一个示例,但星形光圈说明这个解决方案是不完整的。它仅在光圈本身是凸形时有效。enter image description here 简单地将所有顶点通过凸包算法会导致填充蓝色突出显示的区域,但我需要仅填充黑色区域。
4个回答

1
这是一种丑陋、不优雅的解决方案,但您可以沿着表示笔画方向和长度的向量多次重绘您的形状。(您必须选择一个不留空隙的间隔来绘制它。)只有当您想要锯齿状输出时,才能产生正确的结果;它会破坏 GDI+ 所做的任何抗锯齿处理,因为中间色调的像素可能会被绘制在上面,使它们比应该更暗。

是的,我在问题中提到了这种可能性。你提出了一些非常好的观点,关于它会弄乱抗锯齿 - 但在这个应用程序中可能不是一个问题。我预计最终图像将是单色的。 - Pete Baughman
抱歉,我看漏了。不管怎样,那是我能提供的最好的了。祝你好运! - adv12

1

实际上,你提出的解决方案是正确的...使用单独的光圈片段创建平行四边形。然而,当你尝试用除了纯色画刷以外的任何东西来填充它时,你会遇到问题,因为你实际上是在做重复的填充。

最好的方法是创建所有这些多边形,就像你说的那样,然后将它们全部合并在一起。你最终将得到一个代表你想要的轮廓的单个多边形,然后你可以以任何你想要的方式填充它。

如果你正在使用WPF,只需使用GeometryGroup作为所有多边形的子级,然后将该组作为CombinedGeometry中的第一个几何图形,使用“Union”或“Exclude”模式,并将第二个参数传递为null。

如果你没有使用WPF,可以尝试使用Clipper等库,它有一个用C#/.NET编写的版本,但更适用于任意绘图,而不仅仅是WPF。

希望这能帮到你!


0

我想到的最好方法涉及一些冗余绘图,但我认为它会起作用。

将光圈多边形视为线段列表。 对于列表中的每个线段,使用光圈的线段在移动开始时,以及使用光圈的相同线段 在移动结束时,绘制平行四边形。 Parallelogram Draw

上图展示了由星形的十条线段中的三条进行此操作。 对于某些笔形状,可能存在许多重叠的线段,但这种方法应该导致所有正确的像素被填充。 请注意,大部分绿色区域已经被蓝色区域和红色区域填满,只有右下角星形的一个小部分是仅绿色的。 冗余地填充这些像素是浪费的,但我认为可以得出正确的图像。

其他想法:

可能有一种方法可以在不重复填充相同区域的情况下完成此操作,这可能涉及到射线投射。请注意,红线和绿线在右下角的交点将落在原始光圈之外的顶点上。您需要从右多边形的最右侧顶点沿着移动方向绘制一条射线,并找到它与绿线相交的位置,以便正确绘制最终形状。


0

我在 WPF 中解决了这个问题。我想要一个能够处理带曲线和洞的复杂形状的解决方案。我最接近的方法是对原始形状使用 GetFlattenedPathGeometry 将曲线转换为线段,然后运行上面提到的方法。虽然进行了大量冗余计算,但将平行四边形组合起来,并在起点和终点处"盖章"原始形状,最终几何体中的几何体数量相对较少。

public Geometry translateGeo(Geometry baseGeo, Point translate)
{
    // sweeps a geometry linearly from current location through vector

    Debug.WriteLine("Translating Geometry");
    Debug.WriteLine("Original Outline Verticies: " + CountVerticies(baseGeo.GetFlattenedPathGeometry()));

    Geometry sweptPathGeo = baseGeo.Clone();
    Geometry capGeo = baseGeo.Clone();
   
    capGeo.Transform = new TranslateTransform(translate.X, translate.Y);
    sweptPathGeo = new CombinedGeometry(GeometryCombineMode.Union, sweptPathGeo, capGeo);
    sweptPathGeo = sweptPathGeo.GetFlattenedPathGeometry();


    geometry = sweptPathGeo.Clone();


    PathGeometry pathGeo = baseGeo.GetFlattenedPathGeometry();

    foreach (PathFigure figure in pathGeo.Figures)
    {
        Point startPoint = figure.StartPoint;
        //Debug.WriteLine(startPoint.X + ", " + startPoint.Y);
        foreach (PathSegment segment in figure.Segments)
        {
            PolyLineSegment polySegment = segment as PolyLineSegment;
            if (polySegment != null)
            {
                foreach (Point point in polySegment.Points)
                {
                    sweptPathGeo = new CombinedGeometry(GeometryCombineMode.Union, sweptPathGeo, getShadow(startPoint, point, translate));
                    startPoint = point;
                }
            }

            LineSegment lineSegment = segment as LineSegment;
            if (lineSegment != null)
            {
                sweptPathGeo = new CombinedGeometry(GeometryCombineMode.Union, sweptPathGeo, getShadow(startPoint, lineSegment.Point, translate));
                startPoint = lineSegment.Point;
            }
        }
    }

    //sweptPathGeo = sweptPathGeo.GetOutlinedPathGeometry();
    Debug.WriteLine("Finale Outline Verticies: " + CountVerticies(sweptPathGeo.GetFlattenedPathGeometry()));
    return sweptPathGeo;

}
public Geometry getShadow(Point startPoint, Point endPoint, Point translate)
{

    PointCollection points = new PointCollection();
    points.Add(startPoint);
    points.Add(endPoint);
    points.Add(new Point(endPoint.X + translate.X, endPoint.Y + translate.Y));
    points.Add(new Point(startPoint.X + translate.X, startPoint.Y + translate.Y));
    points.Add(startPoint);

    Polygon poly = new Polygon();
    poly.Points = points;
    poly.Arrange(geometry.Bounds);
    poly.Measure(geometry.Bounds.Size);

        PathGeometry returnGeo = poly.RenderedGeometry.GetOutlinedPathGeometry();

    return returnGeo;
    //foreach (Point point in points) Debug.WriteLine(point.X + ", " + point.Y);

}
private int CountVerticies(PathGeometry geo)
{
    int verticies = 0;
    foreach (PathFigure figure in geo.Figures)
    {
        Point startPoint = figure.StartPoint;
        verticies += 1;
        foreach (PathSegment segment in figure.Segments)
        {
            PolyLineSegment polySegment = segment as PolyLineSegment;
            if (polySegment != null) verticies += polySegment.Points.Count;
            
            LineSegment lineSegment = segment as LineSegment;
            if (lineSegment != null) verticies += 1;
        }
    }
    return verticies;
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接