1. 程式人生 > >關於GDI+中GraphicsPath進行合併 Union 截切 Exclude 等程式設計的探討 1

關於GDI+中GraphicsPath進行合併 Union 截切 Exclude 等程式設計的探討 1

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

我們知道,在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


(未完待續,準備另起一篇專門介紹這個問題)

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述