天天看點

滑鼠操作貝塞爾曲線滑鼠操作貝塞爾曲線

滑鼠操作貝塞爾曲線

(C) 2013-2015 Conmajia

Updated on Feb. 22nd, 2015

1 貝塞爾曲線

貝塞爾曲線(The Bézier Curves),是一種在計算機圖形學中相當重要的參數曲線(三維空間中稱為貝塞爾曲面). 貝塞爾曲線由法國工程師皮埃爾·貝塞爾(Pierre Bézier)于1962年發表,他運用貝塞爾曲線來為汽車的主體進行設計.

滑鼠操作貝塞爾曲線滑鼠操作貝塞爾曲線

接下來将從一次貝塞爾曲線開始(以下簡稱一次曲線. 類似的, N 次貝塞爾曲線稱為N次曲線),研究貝賽爾曲線的解析構造和原理。

1.1 一次貝塞爾曲線

給定兩點 P0 、 P1 ,一次曲線在幾何上是一條連接配接這兩點的直線,可以用下面的參數方程表示:

B(t)=P0+(P1−P0)t=(1−t)P0+tP1

其中, t∈[0,1] .

當參數 t 在定義域内變化時,曲線變化過程如下:

滑鼠操作貝塞爾曲線滑鼠操作貝塞爾曲線

一次貝塞爾曲線函數中,參數t會經過由 P0 和 14P0P1 的直線.

1.2 二次貝塞爾曲線

二次曲線的路徑由給定點 P0 、 P1 、 P2 的函數 B(t) 确定:

B(t)=(1−t)2P0+2t(1−t)P1+t2P2

其中, t∈[0,1] .

為了生成二次曲線,可以借助于中間點 Q0 和 Q1 :

  • 由 P0 至 P1 的連續點 Q0 ,描述一條一次曲線
  • 由 P1 至 P2 的連續點 Q1 ,描述一條一次曲線
  • 由 Q0 至 Q1 的連續點 B(t) ,描述一條二次曲線

以 t=0.25 為例,二次曲線如圖:

滑鼠操作貝塞爾曲線滑鼠操作貝塞爾曲線

當參數 t 在定義域内變化時,其變化過程如圖:

滑鼠操作貝塞爾曲線滑鼠操作貝塞爾曲線

1.3 三次貝塞爾曲線

為構造高次曲線,需要借助更多的中間點. 對于三次曲線,可由一次曲線描述的中間點Q0、 Q1 、 Q2 ,以及由二次曲線描述的點 R0 、 R1 來生成.

設 P0 、 P1 、 P2 、 P3 四個點定義了一條三次方貝塞爾曲線. 曲線從 P0 開始,至 P3 結束,其方向從 P1 到 P2 . 一般來說,曲線不會經過 P1 或 P2 . P0 和 P1 之間的間距,決定了曲線在轉而趨進 P3 之前,朝 P2 方向行走的長度.

曲線的參數形式為:

B(t)=P0(1−t)3+3P1t(1−t)2+3P2t2(1−t)+P3t3

其中, t∈[0,1] .

以 t=0.25 為例,三次曲線如圖:

滑鼠操作貝塞爾曲線滑鼠操作貝塞爾曲線

當參數 t 在定義域内變化時,其變化過程如圖:

滑鼠操作貝塞爾曲線滑鼠操作貝塞爾曲線

1.4 高次曲線

如果用BP0P1⋯Pn表示由 P0P1⋯Pn 決定的貝塞爾曲線,那麼高次曲線可以表示為:

B(t)=BP0P1⋯Pn(t)=(1−t)BP0P1⋯Pn(t)+tBP0P1⋯Pn(t)

2 實作

2.1 計算機繪圖

要“畫”出貝塞爾曲線,一般使用逐次逼近方式,需要進行較多的計算,然後繪制出來,類似這樣:

滑鼠操作貝塞爾曲線滑鼠操作貝塞爾曲線

繪制的代碼可以在各類計算機圖形學書籍中找到.

2.2 在GDI+中實作

GDI+已經封裝好了貝塞爾曲線的繪制代碼,如果你想畫出貝塞爾曲線,調用

Graphics.DrawBezier

方法:

public void DrawBezier(Pen pen, Point pt1, Point ctrlPt1, Point pt2, Point ctrlPt2);
           

這是一個三次貝塞爾曲線,其中4個點分别為:起點,起點控制點,終點,終點控制點. 繪制出來的效果如下:

滑鼠操作貝塞爾曲線滑鼠操作貝塞爾曲線

3 與滑鼠互動

怎麼實作Photoshop裡那樣可以調整的貝塞爾曲線呢?兩種方法:

  1. 輸入新參數生成曲線
  2. 用滑鼠互動調整曲線

很顯然第二種看起來比較好,那麼就來試試看.

例如想要獲得是這樣的效果:

滑鼠操作貝塞爾曲線滑鼠操作貝塞爾曲線

這是一條三次貝塞爾曲線,圖中各點含義為:

滑鼠操作貝塞爾曲線滑鼠操作貝塞爾曲線

幾個需要解決的問題:

  1. 表示貝塞爾曲線的錨點和控制點
  2. 繪制曲線和控制點/控制柄
  3. 滑鼠互動

接下來分别解決。.

3.1 錨點

對于三次貝塞爾曲線而言,有兩個錨點:

起始點

結束點

. 每個錨點有兩個坐标:本身坐标和控制點坐标. 于是,可以用這樣的類來描述:

// Anchor
public class AnchorPoint {
    public static AnchorPoint Empty { get; }
    public Point Point { get; set; }
    public Point ControlPoint { get; set; }
}
           

3.2 三次貝塞爾曲線

三次曲線前面已經說過原理了,它的結構應該就是這樣的:

public class BezierSegment {
    public AnchorPoint Begin;
    public AnchorPoint End;
}
           

3.3 一個簡單的渲染器

為了簡單,就用一個最基本的渲染器來畫,代碼如下:

public class BezierRenderer {
    public void DrawSegment(Graphics g, BezierSegment seg, Color color) {
    using (Pen p = new Pen(color)) {
        g.DrawBezier(
            p,
            seg.Begin.Point,
            seg.Begin.ControlPoint,
            seg.End.Point,
            seg.End.ControlPoint);
    }
    public void DrawHandle(Graphics g, Point pt, bool solid, Color color) {
        if (solid)
            using (SolidBrush b = new SolidBrush(color))
                g.FillRectangle(b, pt.X - , pt.Y - , , );
        else
            using (Pen p = new Pen(color))
                g.DrawRectangle(b, pt.X - , pt.Y - , , );
    }
    public void DrawBar(Graphics g, Point pt1, Point pt2, Color color) {
        using (Pen p = new Pen(color))
            g.DrawLine(p, pt1, pt2);
    }
}
           

繪制曲線和控制點及其搖桿:

// 畫曲線
renderer.DrawSegment(g, seg, Color.DimGray);

// 畫錨點
renderer.DrawHandle(g, seg.Begin.Point, false, Color.DimGray);
renderer.DrawHandle(g, seg.End.Point, false, Color.DimGray);

// 畫控制柄
renderer.DrawBar(g, seg.Begin.Point, seg.Begin.ControlPoint, Color.Black);
renderer.DrawHandle(g, seg.Begin.ControlPoint, false, Color.Black);
renderer.DrawBar(g, seg.End.Point, seg.End.ControlPoint, Color.Black);
renderer.DrawHandle(g, seg.End.ControlPoint, false, Color.Black);

// 目前控制柄為實心
if (target != null)
    renderer.DrawHandle(g, target.ControlPoint, true, Color.Black);
           

畫出來的效果如下:

滑鼠操作貝塞爾曲線滑鼠操作貝塞爾曲線

3.4 滑鼠互動

和滑鼠實際上是這樣互動的:

  1. 按下滑鼠,如果落點在某個控制點上,就表示選中了該點,否則落空
  2. 移動滑鼠,如果選中了某個控制點,則調整控制點坐标至新坐标,否則忽略
  3. 放開滑鼠,取消任何選擇

看起來隻需要處理

MouseDown

MouseMove

MouseUp

這三個事件就夠了.

先弄一個全局标記:

// 熱點檢測區
Rectangle rectBegin, rectEnd;

// 激活辨別
AnchorPoint target = null;
           

然後處理滑鼠事件.

3.4.1 MouseDown事件

if (rectBegin.Contains(e.Location))
    target = seg.Begin;
else if (rectEnd.Contains(e.Location))
    target = seg.End;
refresh();
           

3.4.2 MouseMove事件

if (target == null) return;
if (!this.ClientRectangle.Contains(e.Location)) return;
target.ControlPoint = e.Location;
refresh();
           

3.4.3 MouseUp事件

target = null;
refresh();
           

3.4.4

refresh()

方法

簡便起見,

refresh()

方法隻作簡單的重新整理:

Invalidate();
           

更高效的重新整理應該隻處理髒區.

3.5 效果

最後的效果如圖:

滑鼠操作貝塞爾曲線滑鼠操作貝塞爾曲線

修改渲染器,可以得到更多的圖像效果:

滑鼠操作貝塞爾曲線滑鼠操作貝塞爾曲線

4 擴充和優化

要實作Photoshop那種曲線效果,需要多個錨點連接配接起來,用和本文類似的方法來畫。上面的方法在效率上也還有可以提高的地方。

(完)

(C) 2013-2015 Conmajia

繼續閱讀