基于C#的GDI花式探索三:路径的分析和线段化

一、简单聊两句

路径在GDI里面是一个比较特殊的东西,它可以加入很多图形,组成一个路径,用处也是很大的,做复杂图形的组合就比较方便了。就因为方便,我们常常忽略了一个问题,它里面的数据到底是什么样的啊?都是线段吗?我经过尝试,发现并不完全是。

二、路径数据的刨析

1.路径数据的分析

为了涵盖足够复杂的图形,我们在路径中加入文字来做分析。

最简单的一个文字路径的代码:

            GraphicsPath Strpath = new GraphicsPath();
            Strpath.AddString("U", font.FontFamily, (int)FontStyle.Regular, (float)font.Size, new PointF(300.0f, 50.0f), format);

路径中加入了一个字母。

我们再看看这个路径里的属性PathPoints,里面是一堆点。起初我也没关注过这堆点是啥,因为我一调用DrawPath他就能自己绘制出来,因此我猜测,他就是一堆路径上的点吧。后来,我要把这个文字绘制到别的地方,比如3D里面,可是那里可没有DrawPath调用啊,我只能通过它这堆点去绘制了,因此我把它当作一堆直线段上的点去绘制,发现偏差有点大。

如图:

我用黄色绘制的路径,用蓝色将路径中的点按线段绘制,发现有些地方并不重合。

于是我去参考下官方文档关于路径点的介绍:

GraphicsPath.PathTypes 属性 (System.Drawing.Drawing2D) | Microsoft Learn

原来这些点并不是都一样的!它们有各种的类别标记,也就是路径的PathTypes属性。因而对路径点的解析要从这里下手。

通过调试发现,路径的起始点Type都是0,每段闭合线的终止点Type基本都大于80,然后1表示直线上的点,3表示贝塞尔曲线的控制点。目前还没遇到过其他的类型,至少我在文字里面没看到过别的。因此,路径的点并不完全都是直线段,所以我直接绘制成直线段产生偏差得到了解释。

2.路径数据的线段化

有了上述的实践结果,我们可以对路径点进行整理转化,生成我们需要的全部为线段上的点。

根据点类型分析:

类型为0,起点,无法看出是直线点还是贝塞尔曲线的,因此起点只能作为分段的一个信号。

类型为1和3就不用说,可以进行相应的处理。

类型大于80,可定性为终止点,需要与起点形成闭合。

对于直线上的点,我们不需要处理,直接接收,对于贝塞尔曲线上的点,我们可以参考公式计算:

由于是4个点控制,所以这里是3阶贝塞尔曲线:

由此,可以开始写代码了:

            List<List<PointF>> points = new List<List<PointF>>();
            PointF before = new PointF();
            for (int i = 0; i < Strpath.PathPoints.Length;)
            {
                if (Strpath.PathTypes[i] == 0)
                    points.Add(new List<PointF>());

                if (Strpath.PathTypes[i] == 3)
                {
                    int segments = 10;
                    for (int j = 1; j < segments + 1; j++)
                    {
                        double t = (double)j / (double)segments;
                        PointF point = new PointF();
                        point.X = (1.0f - t) * (1.0f - t) * (1.0f - t) * before.X + 3 * t * (1.0f - t) * (1.0f - t) * Strpath.PathPoints[i].X + 3 * t * t * (1.0f - t) * Strpath.PathPoints[i + 1].X + t * t * t * Strpath.PathPoints[i + 2].X;
                        point.Y = (1.0f - t) * (1.0f - t) * (1.0f - t) * before.Y + 3 * t * (1.0f - t) * (1.0f - t) * Strpath.PathPoints[i].Y + 3 * t * t * (1.0f - t) * Strpath.PathPoints[i + 1].Y + t * t * t * Strpath.PathPoints[i + 2].Y;
                        points[points.Count - 1].Add(point);
                    }
                    if (Strpath.PathTypes[i + 2] > 80)
                    {
                        PointF last = new PointF(points[points.Count - 1][0]);
                        points[points.Count - 1].Add(last);
                    }
                    i += 3;
                }
                else
                {
                    points[points.Count - 1].Add(Strpath.PathPoints[i]);
                    if (Strpath.PathTypes[i] > 80)
                    {
                        PointF last = new PointF(points[points.Count - 1][0]);
                        points[points.Count - 1].Add(last);
                    }
                    i++;
                }
                before = points[points.Count - 1][points[points.Count - 1].Count - 1];
            }

经过解析就可以得到线段化后的点集,有了这个点集,我们可以在GDI里面绘制,也可以在OpenGL里绘制,还可以对这些点做各种变换。

以下是我绘制在OpenGL里的一个效果图:

三、结尾

对路径的探索就到这里了,很多东西,深入探索下才能看到更多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值