【问题标题】:Draw with arbitrary shaped pen用任意形状的笔画
【发布时间】:2014-12-17 17:43:48
【问题描述】:

我需要使用自定义光圈在位图上画线。在下面的示例中,有两行。左边的线是水平的。右线是对角线 - 向下和向右: 在示例图像中,光圈和路径以红色显示。生成的几何图形显示为黑色。上述两个示例都使用相同的光圈,但沿不同的路径擦拭。

我希望能够使用任意形状的笔进行绘图,尽管上面的示例只显示了矩形。

我尝试使用 System.Drawing.2D 命名空间,但它似乎没有做我需要做的事情。在笔上使用自定义端盖似乎很有希望,但端盖会随着线条的方向旋转。此外,获得正确的线宽似乎具有挑战性。

我考虑过一遍又一遍地绘制光圈,以路径的不同点为中心,但这似乎并不高效。尽量减少孔径绘制的次数也是相当困难的。

我的最佳想法是尝试将“线”绘制为填充形状。起初,我认为凸包算法将是答案 - 只需在绘制开始时和绘制结束时获取孔径的顶点,然后通过凸包算法找到“外部”顶点。这适用于我上面的第一个示例,但星形光圈表明此解决方案不完整。它仅在光圈本身为凸面时才有效。

简单地将所有顶点通过凸包算法将导致以蓝色突出显示的区域被填充,但我只需要填充黑色区域。

【问题讨论】:

    标签: c# .net graphics drawing


    【解决方案1】:

    这是一个丑陋、不雅的解决方案,但您可以采用表示笔划方向和长度的向量,并沿着该向量多次重绘您的形状。 (您必须选择一个不留间隙的间隔来绘制它。)如果您想要别名输出,这只会产生正确的结果;它会破坏 GDI+ 完成的任何抗锯齿,因为带有中间色调的像素可能会被覆盖,使它们比应有的颜色更暗。

    【讨论】:

    • 是的,我在问题中提到了这种可能性。您提出了一些关于它破坏抗锯齿的非常好的观点 - 但这可能不是这个应用程序中的一个问题。我希望最终图像是单色的。
    • 糟糕,抱歉。我猜我略读了。无论如何,这是我所拥有的最好的。祝你好运!
    【解决方案2】:

    实际上,您提出的解决方案走在正确的道路上……使用各个光圈段创建平行四边形。但是,当您尝试使用除实心笔刷之外的任何东西来填充它时,您会遇到问题,因为您实际上是在进行重复填充。

    正如您所说,最好的办法是创建所有这些多边形,然后将它们合并在一起。你最终会得到一个代表你想要的轮廓的多边形,然后你可以简单地用任何你想要的方式填充它。

    如果您使用 WPF,只需使用 GeometryGroup 将所有多边形作为子项,然后将该组用作 CombinedGeometry 中的第一个几何图形,并使用“联合”或“排除”模式,将 null 传递给第二个参数。

    如果您不使用 WPF,您可以尝试使用 Clipper 之类的库,该库具有用 C#/.NET 编写的版本,但更多用于任意绘图,而不仅仅是 WPF。

    希望这会有所帮助!

    【讨论】:

      【解决方案3】:

      我的最佳想法涉及一些多余的绘图,但我认为它会起作用。

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

      上图显示了构成星星的十条线段中的三条。对于某些笔形,线段之间可能存在大量重叠,但此方法应该会导致填充所有正确的像素。请注意,大部分绿色区域已被蓝色区域和红色区域,右下角的星星只有一小部分是绿色的。重复填充这些像素很浪费,但我认为会产生正确的图像。

      其他想法:

      可能有一种方法可以做到这一点,而无需在同一区域进行冗余填充,并且可能涉及光线投射。请注意右下角的红线和绿线的交点将如何落在不属于原始光圈一部分的顶点上。您需要沿着移动方向从右侧多边形的最右侧顶点绘制一条射线,并找到与绿线相交的位置,以便正确绘制最终形状。

      【讨论】:

        【解决方案4】:

        我在 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;
        }

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-10-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-02-06
          • 1970-01-01
          相关资源
          最近更新 更多