关于GDI+中GraphicsPath进行合并(Union)截切(Exclude)等编程的探讨(1)

我们知道,在GDI+中,两个图形路径(GraphicsPath)的区域(Region)合并,我们可以采用Region.Union方法进行。但使用它之后,我们再想取得合并后的Region的GraphicsPath却变得不再可能。比如下图1红色部分:


图1  合并GraphicsPath后想要达到的效果

它由两个椭圆共同组成:

            Rectangle rect1 = new Rectangle(100, 100, 178, 178);
            Rectangle rect2 = new Rectangle(230, 100, 178, 178);
            GraphicsPath gp = new GraphicsPath(FillMode.Winding);
            gp.AddEllipse(rect1);
            gp.AddEllipse(rect2);

            Bitmap bmp = new Bitmap(520, 520);
            Graphics g = Graphics.FromImage(bmp);
            g.CompositingQuality = CompositingQuality.HighQuality;
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
            g.SmoothingMode = SmoothingMode.HighQuality;


            g.DrawPath(new Pen(Color.Red, 3f), gp);
            picEncode.Image = bmp;

但上面的代码并无法得到图1的结果,而是下图2所示的样式:

图2  使用graphicsPath.Add()方法生成的两条图形路径的轮廊绘制

有没有办法将这两个GraphicsPath真正合并呢?

首先来看看GraphicsPath背后的东东:gp.PathData,它里面包含有路径的点 (points)  和点的类型(types)数组。

对于每一点,byte值0 到 2 都表示点的类型,而 3 到 7 则保存一组可指定点的属性(Attribute) 的标志。其中值的含义如下:
0表示此点为图形的起始点;1表示此点为直线两个端点之其中一点;3表示此点为三次贝塞尔曲线的端点或控制点;0x7为遮罩所有位元,但表示点类型的三个低阶位不在此列;0x20指定此点为标记;0x80表示此点为封闭式子路径的最后一个点。

这是其原始的定义:
typedef enum  {
  PathPointTypeStart          = 0,
  PathPointTypeLine           = 1,
  PathPointTypeBezier         = 3,
  PathPointTypePathTypeMask   = 0x7,
  PathPointTypePathDashMode   = 0x10,
  PathPointTypePathMarker     = 0x20,
  PathPointTypeCloseSubpath   = 0x80,
  PathPointTypeBezier3        = 3 
} PathPointType;
我们来看看图2的GraphicsPath(通过C#写的小工具得到其背后发生的秘密,哈哈~~):

GraphicsPath path = new GraphicsPath(
new PointF[] {
new PointF(278F,99F),new PointF(278F,148.1533F),new PointF(238.1533F,188F),new PointF(189F,188F),new PointF(139.8466F,188F),
new PointF(100F,148.1533F),new PointF(100F,99F),new PointF(100F,49.84666F),new PointF(139.8466F,10F),new PointF(189F,10F),
new PointF(238.1533F,10F),new PointF(278F,49.84666F),new PointF(278F,99F),new PointF(408F,99F),new PointF(408F,148.1533F),
new PointF(368.1533F,188F),new PointF(319F,188F),new PointF(269.8466F,188F),new PointF(230F,148.1533F),new PointF(230F,99F),
new PointF(230F,49.84666F),new PointF(269.8466F,10F),new PointF(319F,10F),new PointF(368.1533F,10F),new PointF(408F,49.84666F),
new PointF(408F,99F)},
            new System.Byte[] { 0,3,3,3,3,3,3,3,3,3,3,3,131,0,3,3,3,3,3,3,3,3,3,3,3,131 });

注意上面最后一行那个Byte数组,里面有0,3,131等值,其中131是128(即0x80)+3的组合,说明该点为封闭子路径的最后一点及是贝塞尔曲线的端点。

下图3为关键点显示出来的样示(其中蓝色为关键点):


图3  图形路径的关键点

通过上面可以看出:左右两个圆的交叉点位置并不是关键点!

麻烦就出来了,我们必须找到这些交叉点,然后,将交叉点也作为关键点放入最后的GraphicsPath中,这样才能解决图形路径的合并问题。

那么,如何找到交叉点呢?对于矩形(如下图4)、圆形等较规则的图形路径而言还比较好办,我们可以想办法通过数学公式来进行。


图4  两个矩形构建的GraphicsPath(其中蓝色点为关键点)

图4的源码仅是将图2所示的代码中加粗的两行代码中的AddEllipse改为AddRectangle而已。很明显,求它们的交叉点是非常容易的。但是,对于不规则的贝塞尔曲线等则变得非常棘手了。

大家如果有兴趣,可以先看看这篇文章,比较基础,但非常有用,不过是英文的,如果你的英文不咋的,请耐着性子看下去,看明白了,一定会有收获的!

http://processingjs.nihongoresources.com/bezierinfo/

顺便还可以看看这个:

http://processingjs.nihongoresources.com/bezierinfo/sketchsource.php?sketch=cubicSubdivision

http://fei.edu.br/~psergio/CG_arquivos/IntroSplines.pdf

http://www.uio.no/studier/emner/matnat/ifi/INF3320/h03/undervisningsmateriale/lecture8.pdf

http://losingfight.com/blog/2011/07/08/how-to-implement-boolean-operations-on-bezier-paths-part-2/

http://www.cs.berkeley.edu/~hling/research/paper/surface.htm

http://blog.sina.com.cn/s/blog_640531380100q669.html


(未完待续,准备另起一篇专门介绍这个问题)

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值