首页 文章

用任意形状的笔画

提问于
浏览
3

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

我希望能够用任意形状的笔绘制,即使上面的例子只显示矩形 .

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

I've considered 一遍又一遍地绘制光圈,以沿着路径的不同点为中心,但这似乎并不符合要求 . 最小化孔径绘制的数量也是非常困难的 .

The best idea I've got 是尝试将'line'绘制为填充形状 . 首先,我认为凸包算法就是答案 - 只需在绘制开始时和绘制结束时获取孔径的顶点,然后通过凸包算法找到'outer'顶点 . 这适用于我上面的第一个例子,但是星形光圈表明这个解决方案是不完整的 . 它只适用于光圈自身凸起的情况 .
enter image description here

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

4 回答

  • 0

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

  • 1

    实际上,您正在使用您提出的解决方案 - 使用各个孔径段创建平行四边形 . 但是,当你尝试使用除了实心画笔之外的任何东西来填充它时,你会遇到问题,因为你实际上正在进行重复填充 .

    最好的办法是创建所有这些多边形,就像你说的那样,然后将它们组合在一起 . 您最终会得到一个表示所需轮廓的单个多边形,然后您可以按照您想要的任何方式填充它 .

    如果您正在使用WPF,只需使用 GeometryGroup 将所有多边形作为子项使用,然后将该组用作 CombinedGeometry 中具有'Union'或'Exclude'模式的第一个几何,为第二个参数传递null .

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

    希望这可以帮助!

  • 1

    我得到的最好的想法涉及一些多余的绘图,但我认为它会起作用 .

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

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

    Other Thoughts:

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

  • 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;
    }
    

相关问题