Quartz-2D繪圖之路徑(Paths)詳解

  在上篇文章中,咱們簡單的理解了繪圖上下文,今天咱們來認識一下Quartz-2D中另外一個重要的概念,路徑(Paths)。數組

1、理解路徑數據結構

  路徑定了一個或多個形狀,或是子路徑。一個子路徑可由直,曲,或者同由二者構成。它能夠是開放的,也能夠是合的。一個子路徑能夠是簡單的形狀,如、矩形、星形;也能夠是復的形狀,如山脈的廓或者是塗3-1示了一些我能夠建的路徑。左上角的直能夠是虛;直也能夠是實線。上的路徑是由多條曲線組成的開放路徑。右上角的同心填充了色,但沒有描。左下角的加利福尼州是合路徑,由多曲和直構成,且路徑行填充和描。兩個星形明瞭填充路徑的兩種方式,我詳細描述。函數

 

2、建及制路徑ui

路徑建及路徑制是兩個獨立的工做。首先我們創建路徑。當我須要渲染路徑,我須要使用Quartz制它。正如3-1中所示,我能夠選擇對路徑行描,填充路徑,或同時進兩種操做。我能夠將其它制到路徑所表示的範內,即對對行裁減。
3-2制了一個路徑,路徑包含兩個子路徑。左的子路徑是一個矩形,右的子路徑是由直和曲線組成的抽象形狀。兩個子路徑都行了填充及描
spa

 

3-3示了多條獨立制的路徑。每一個路徑食隨機生成的曲,一些行填充,另外一些行了描些路徑都包含在一個形裁減區域內。code

 

3、構建塊(Building Block)
子路徑是由直線、弧和曲線構成的。Quartz一樣也提供了簡便的函數用於添加矩形或橢圓等形狀。點也是路徑最基本的構建塊,由於點定義了形狀的起始點與終止點。


點由x, y座標值定義,用於在用戶空間指定 一個位置。咱們能夠調用函數CGContextMoveToPoint來爲新的子路徑指定起始點。Quartz跟蹤當前點,用於記錄路徑構建過程當中最新的位置。例如,若是調用函數CGContextMoveToPoint並設置位置爲(10, 10),即將當前點移動到位置(10, 10)。若是在水平位置繪製50個單位長度的直線,則直線的終點爲(60, 10),該點變成當前點。直線、弧和曲線老是從當前點開始繪製。
一般咱們經過傳遞(x, y)值給Quartz函數來指定一個點。一些函數須要咱們傳遞一個CGPoint數據結構,該結構包含兩個浮點值。


直線由兩個端點定義。起始點一般是當前點,因此建立直線時,咱們只須要指定終止點。咱們使用函數CGContextAddLineToPoint來添加一條直線到子路徑中。
咱們能夠調用CGContextAddLines函數添加一系列相關的直線到子路徑中。咱們傳遞一個點數組給這個函數。第一個點必須是第一條直線的起始點;剩下的點是端點。Quartz從第一個點開始繪製一個新子路徑,而後每兩個相鄰點鏈接成一條線段。


弧是圓弧段。Quartz提供了兩個函數來建立弧。函數CGContextAddArc從圓中來建立一個曲線段。咱們指定一個圓心,半徑和放射角(以弧度爲單位)。放射角爲2 PI時,建立的是一個圓。圖3-4顯示了多個獨立的路徑。每一個路徑飲食一個自動生成的圓;一些是填充的,另外一些是描邊的。對象

函數CGContextAddArcToPoint用於爲矩形建立內切弧的場景。Quartz使用咱們提供的端點建立兩條正切線。一樣咱們須要提供圓的半徑。弧心是兩條半徑的交叉點,每條半徑都與相應的正切線垂直。弧的兩個端點是正切線的正切點,如圖3-5所示。紅色的部分是實際繪製的部分。blog

下面分別是畫直線和圓弧的代碼圖片

直線:ip

 

 1   //    1. 獲取一個與視圖相關聯的上下文
 2     CGContextRef context = UIGraphicsGetCurrentContext();
 3    
 4     //    2. 構建路徑
 5     //    2.1 設置上下文路徑起點
 6     CGContextMoveToPoint(context, 85, 85);
 7     //    2.2 增長路徑內容……
 8     CGContextAddLineToPoint(context, 150, 150);
 9     CGContextAddLineToPoint(context, 250, 50);
10     //    3. 保存上下文狀態
11     //    4. 設置上下文狀態
12     //    4.1 設置邊線顏色
13     CGContextSetRGBStrokeColor(context, 1, 0, 0, 1);
14     //    4.2 設置線寬
15     CGContextSetLineWidth(context, 10);
16     //    4.3 設置線段鏈接樣式
17     CGContextSetLineJoin(context, kCGLineJoinRound);
18     //    4.4 設置線段收尾樣式
19     CGContextSetLineCap(context, kCGLineCapRound);
20     //    4.5 設置虛線樣式
21     //    4. 繪製路徑
22     CGContextDrawPath(context, kCGPathStroke);

 

圓弧:

    //    1. 獲取一個與視圖相關聯的上下文
    CGContextRef context = UIGraphicsGetCurrentContext();
   
    [[UIColor redColor]set];
    // 2. 添加弧線
    // 2.1 上下文
    // 2.2 中心點座標
    // 2.3 半徑
    // 2.4 開始角度,結束角度,角度的單位是「弧度」
    CGContextAddArc(context, 160, 230, 100, M_PI, -M_PI_2, 0);
   
    // 繪製路徑
    CGContextStrokePath(context);


二次與三次Bezier曲線是代數曲線,能夠指定任意的曲線形狀。曲線上的點經過一個應用於起始、終點及一個或多個控制點的多項式計算得出。這種方式定義的形狀是向量圖的基礎。這個公式比將位數組更容易存儲,而且曲線能夠在任何分辨下從新建立。
圖3-6顯示了一些路徑的曲線。每條路徑包含一條隨機生成的曲線;一些是填充的,另外一些是描邊的。

 

咱們使用函數CGContextAddCurveToPoint將Bezier曲線鏈接到當前點,並傳遞控制點和端點做爲參數,如圖3-7所示。兩個控制點的位置決定了曲線的形狀。若是兩個控制點都在兩個端點上面,則曲線向上凸起。若是兩個控制點都在兩個端點下面,則曲線向下凹。若是第二個控制點比第一個控制點離得當前點近,則曲線自交叉,建立了一個迴路。

咱們也能夠調用函數CGContextAddQuadCurveToPoint來建立Bezier,並傳遞端點及一個控制點,如圖3-8所示。控制點決定了曲線彎曲的方向。因爲只使用一個控制點,因此沒法建立出如三次Bezier曲線同樣多的曲線。例如咱們沒法建立出交叉的曲線

1     /*畫貝塞爾曲線*/  
2     //二次曲線  
3     CGContextMoveToPoint(context, 120, 300);//設置Path的起點  
4     CGContextAddQuadCurveToPoint(context,190, 310, 120, 390);//設置貝塞爾曲線的控制點座標和終點座標  
5     CGContextStrokePath(context);  
6     //三次曲線函數  
7     CGContextMoveToPoint(context, 200, 300);//設置Path的起點  
8     CGContextAddCurveToPoint(context,250, 280, 250, 400, 280, 300);//設置貝塞爾曲線的控制點座標和控制點座標終點座標  
9     CGContextStrokePath(context);  

 

合路徑
咱們能夠調用函數CGContextClosePath來閉合曲線。該函數用一條直接來鏈接當前點與起始點,以使路徑閉合。起始與終點重合的直線、弧和曲線並不自動閉合路徑,咱們必須調用CGContextClosePath來閉合路徑。
Quartz的一些函數將路徑的子路徑當作是閉合的。這些函數顯示地添加一條直線來閉合 子路徑,如同調用了CGContextClosePath函數。
在閉合一條子路徑後,若是程序再添加直線、弧或曲線到路徑,Quartz將在閉合的子路徑的起點開始建立一個子路徑。

橢圓
橢圓是一種特殊的圓。橢圓是經過定義兩個焦點,在平面內全部與這兩個焦點的距離之和相等的點所構成的圖形。圖3-9顯示了一些獨立的路徑。每一個路徑都包含一個隨機生成的橢圓;一些進行了填充,另外一邊進行了描邊。

咱們能夠調用CGContextAddEllipseInRect函數來添加一個橢圓到當前路徑。咱們提供一個矩形來定義一個橢圓。Quartz利用一系列的Bezier曲線來模擬橢圓。橢圓的中心就是矩形的中心。若是矩形的寬與高相等,則橢圓變成了圓,且圓的半徑爲矩形寬度的一半。若是矩形的寬與高不相等,則定義了橢圓的長軸與短軸。
添加到路徑中的橢圓開始於一個move-to操做,結束於一個close-subpath操做,全部的移動方向都是順時針。

矩形
咱們能夠調用CGContextAddRect來添加一個矩形到當前路徑中,並提供一個CGRect結構體(包含矩形的原點及大小)做爲參數。
添加到路徑的矩形開始於一個move-to操做,結束於一個close-subpath操做,全部的移動方向都是順時針。
咱們也可能調用CGContextAddRects函數來添加一系列的矩形到當前路徑,並傳遞一個CGRect結構體的數組。圖3-10顯示了一些獨立的路徑。每一個路徑包含一個隨機生成的矩形;一些進行了填充,另外一邊進行了描邊。

 4、建立路徑

當咱們須要在一個圖形上下文中構建一個路徑時,咱們須要調用CGContextBeginPath來標記Quartz。而後,咱們調用函數CGContextMovePoint來設置每個圖形或子路徑的起始點。在構建起始點後,咱們能夠添加直線、弧、曲線。記住以下規則:

  • 在開始繪製路徑前,調用函數CGContextBeginPath;
  • 直線、弧、曲線開始於當前點。空路徑沒有當前點;咱們必須調用CGContextMoveToPoint來設置第一個子路徑的起始點,或者調用一個便利函數來隱式地完成該任務。
  • 若是要閉合當前子路徑,調用函數CGContextClosePath。隨後路徑將開始一個新的子路徑,即便咱們不顯示設置一個新的起始點。
  • 當繪製弧時,Quartz將在當前點與弧的起始點間繪製一條直線。
  • 添加橢圓和矩形的Quartz程序將在路徑中添加新的閉合子路徑。
  • 咱們必須調用繪製函數來填充或者描邊一條路徑,由於建立路徑時並不會繪製路徑。

在繪製路徑後,將清空圖形上下文。咱們也許想保留路徑,特別是在繪製複雜場景時,咱們須要反覆使用。基於此,Quartz提供了兩個數據類型來建立可複用路徑—CGPathRef和CGMutablePathRef。咱們能夠調用函數CGPathCreateMutable來建立可變的CGPath對象,並可向該對象添加直線、弧、曲線和矩形。Quartz提供了一個相似於操做圖形上下文的CGPath的函數集合。這些路徑函數操做CGPath對象,而不是圖形上下文。這些函數包括:

  • CGPathCreateMutable,取代CGContextBeginPath
  • CGPathMoveToPoint,取代CGContextMoveToPoint
  • CGPathAddLineToPoint,取代CGContexAddLineToPoint
  • CGPathAddCurveToPoint,取代CGContexAddCurveToPoint
  • CGPathAddEllipseInRect,取代CGContexAddEllipseInRect
  • CGPathAddArc,取代CGContexAddArc
  • CGPathAddRect,取代CGContexAddRect
  • CGPathCloseSubpath,取代CGContexClosePath

若是想要添加一個路徑到圖形上下文,能夠調用CGContextAddPath。路徑將保留在圖形上下文中,直到Quartz繪製它。咱們能夠調用CGContextAddPath再次添加路徑。

 5、繪制路徑

咱們能夠繪製填充或描邊的路徑。描邊(Stroke)是繪製路徑的邊框。填充是繪製路徑包含的區域。Quartz提供了函數來填充或描邊路徑。描邊線的屬性(寬度、顏色等),填充色及Quartz用於計算填充區域的方法都是圖形狀態的一部分。

影響描的屬性
咱們可使用表3-1中的屬性來決定如何對路徑進行描邊操做。這邊屬性是圖形上下文的一部分,這意味着咱們設置的值將會影響到後續的描邊操做,直到咱們個性這些值。

linewidth是線的總寬度,單位是用戶空間單元。
linejoin屬性指定如何繪製線段間的聯接點。Quartz支持表3-2中描述的聯接樣式。

Table 3-2 直線聯接樣式

linecap指定如何繪製直線的端點。Quartz支持表3-3所示的線帽類型。默認的是butt cap。

 

閉合路徑將起始點看做是一個聯接點;起始點一樣也使用選定的直線鏈接方法進行渲染。若是經過添加一條鏈接到起始點的直線來閉合路徑,則路徑的兩個端點都使用選定的線帽類型來繪製。
Linedash pattern(虛線模式)容許咱們沿着描邊繪製虛線。咱們經過在CGContextSetLineDash結構體中指定虛線數組和虛線相位來控制虛線的大小及位置。

CGContextSetLineDash結構以下:

 

1 void CGContextSetLineDash (
2 CGContextRef ctx,
3 float phase,
4 const float lengths[],
5 size_t count,
6 );

 其中lengths屬性指定了虛線段的長度,該值是在繪製片段與未繪製片段之間交替。phase屬性指定虛線模式的起始點。圖3-11顯示了虛線模式:

路徑描的函數
Quartz提供了表3-4中的函數來描邊當前路徑。其中一些是描邊矩形及橢圓的便捷函數。
表3-4 描邊路徑函數

 

函數CGContextStrokeLineSegments等同於以下代碼

1 CGContextBeginPath(context);                                                                                                                                                                                                                                                                 for(k = 0; k < count; k += 2) {                                                                                                                                                                                                                                                                                  CGContextMoveToPoint(context,s[k].x, s[k].y); CGContextAddLineToPoint(context,s[k+1].x, s[k+1].y);}CGContextStrokePath(context);

當咱們調用CGContextStrokeLineSegments時,咱們經過點數組來指定線段,並組織成點對的形式。每一對是由線段的起始點與終止點組成。例如,數組的第一個點指定了第一條直線的起始點,第二個點是第一條直線的終點,第三個點是第二條直線的起始點,依此類推。


6、填充路徑
當咱們填充當前路徑時,Quartz將路徑包含的每一個子路徑都看做是閉合的。而後,使用這些閉合路徑並計算填充的像素。 Quartz有兩種方式來計算填充區域。橢圓和矩形這樣的路徑其區域都很明顯。可是若是路徑是由幾個重疊的部分組成或者路徑包含多個子路徑(如圖3-12所示),咱們則有兩種規則來定義填充區域。
默認的規則是非零纏繞數規則(nonzero windingnumber rule)。爲了肯定一個點是否須要繪製,咱們從該點開始繪製一條直線穿過繪圖的邊界。從0開始計數,每次路徑片段從左到右穿過直線是,計數加1;而從右到左穿過直線時,計數減1。若是結果爲0,則不繪製該點,不然繪製。路徑片段繪製的方向會影響到結果。圖3-13顯示了使用非纏繞數規則對內圓和外圓進行填充的結果。當兩個圓繪製方向相同時,兩個圓都被填充。若是方向相反,則內圓不填充。
咱們也可使用偶數-奇數規則。爲了肯定一個點是否被繪製,咱們從該點開始繪製一條直線穿過繪圖的邊界。計算穿過該直線的路徑片段的數目。若是是奇數,則繪製該點,若是是偶數,則不繪製該點。路徑片段繪製的方向不影響結果。如圖3-12所示,不管兩個圓的繪製方向是什麼,填充結果都是同樣的。

Quartz提供了表3-5中的函數來填充當前路徑。其中一些是填充矩形及橢圓的便捷函數。
表3-5 填充路徑的函數

 好,說了這麼多下面上代碼

 1 - (void)drawLine1
 2 {
 3     //    1. 獲取一個與視圖相關聯的上下文
 4     CGContextRef context = UIGraphicsGetCurrentContext();
 5    
 6     //    2. 建立一個可變路徑
 7     //    2.1 建立路徑
 8     CGMutablePathRef path = CGPathCreateMutable();
 9     //    2.2 設置路徑起點
10     CGPathMoveToPoint(path, NULL, 50, 50);
11     //    2.3 增長路徑內容……
12     CGPathAddLineToPoint(path, NULL, 150, 150);
13     CGPathAddLineToPoint(path, NULL, 50, 150);
14     //    CGPathAddLineToPoint(path, NULL, 50, 50);
15     // 閉合路徑,關閉路徑,閉合路徑是收尾相連的
16     CGPathCloseSubpath(path);
17    
18     //    3. 將路徑添加到上下文;
19     CGContextAddPath(context, path);
20    
21     //    4. 設置上下文狀態
22     //    4.1 設置邊線顏色
23     // 顏色數值 = RGB數值 / 255
24     CGContextSetRGBStrokeColor(context, 255.0 / 255.0, 0.0, 0.0 , 1.0);
25     //    4.2 設置填充顏色
26     CGContextSetRGBFillColor(context, 0.0, 0.0, 128.0 / 255.0, 1.0);
27     //    4.3 設置線寬
28     CGContextSetLineWidth(context, 5);
29     //    4.4 設置線段鏈接樣式
30     CGContextSetLineJoin(context, kCGLineJoinBevel);
31     //    4.5 設置線段首尾樣式
32     CGContextSetLineCap(context, kCGLineCapRound);
33     //    4.6 設置虛線樣式
34     // lengths 設置公有幾條虛線,每條虛線的長度
35     // count 指的是lengths數組的長度
36     CGFloat lengthes[2] = {10.0, 10.0};
37     CGContextSetLineDash(context, 0, lengthes, 2);
38     //    5. 繪製路徑
39     /**
40      kCGPathStroke  繪製邊線
41      kCGPathFill    填充
42      */
43     CGContextDrawPath(context, kCGPathFillStroke);
44     //    6. 釋放路徑,不一樣對象對應着不一樣的release方法
45     CGPathRelease(path);
46 }

 7、裁剪路徑

當前裁剪區域是從路徑中建立,做爲一個遮罩,從而容許遮住咱們不想繪製的部分。例如,咱們有一個很大的圖片,但只須要顯示其中一小部分,則能夠設置裁減區域來顯示咱們想顯示的部分。

當咱們繪製的時候,Quartz只渲染裁剪區域裏面的東西。裁剪區域內的閉合路徑是可見的;而在區域外的部分是不可見的。

當圖形上下文初始建立時,裁減區域包含上下文全部的可繪製區域(例如,PDF上下文的media box)。咱們能夠經過設置當前路徑來改變裁剪區域,而後使用裁減函數來取代繪製函數。裁剪函數與當前已有的裁剪區域求交集以得到路徑的填充區域。所以,咱們能夠求交取得裁減區域,縮小圖片的可視區域,可是不能擴展裁減區域。

裁減區域是圖形狀態的一部分。爲了恢復先前的裁減區域,咱們能夠在裁減前保存圖形狀態,並在裁減繪製後恢復圖形狀態。

 

1 Listing 3-1 Setting up a circular clip area
2 CGContextBeginPath(context);
3 CGContextAddArc(context, w/2, h/2, ((w>h) ? h : w)/2, 0, 2*PI, 0);
4 CGContextClosePath(context);
5 CGContextClip(context);

 

表3-6 裁減圖形上下文的函數

相關文章
相關標籤/搜索