圖形學基礎(一)光柵圖形學_下:剪裁

c++,MFC,VS2017c++

準備(矩形,線段,多邊形)算法

void CclipView::OnDraw(CDC* pDC)
{
    CclipDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;

    // TODO: 在此處爲本機數據添加繪製代碼
    CPen newpen(PS_SOLID, 1, RGB(0, 0, 0));
    CPen *old = pDC->SelectObject(&newpen);
    pDC->Rectangle(CRect(Xmin, Ymax, Xmax, Ymin));//矩形
    ptset[0] = CPoint(320, 150);
    ptset[1] = CPoint(370, 110);
    ptset[2] = CPoint(200, 190);
    ptset[3] = CPoint(550, 150);
    ptset[4] = CPoint(200, 250);
    ptset[5] = CPoint(350, 230);
    ptset[6] = CPoint(400, 50);
    ptset[7] = CPoint(450, 130);
    pDC->TextOutW(0, 0, TEXT("雙擊,出現要剪裁的線段"));
    pDC->TextOutW(0, 20, TEXT("雙擊右鍵,出現要剪裁的多邊形"));
    pDC->SelectObject(old);
}

void CclipView::OnLButtonDblClk(UINT nFlags, CPoint point)//線段
{
    // TODO: 在此添加消息處理程序代碼和/或調用默認值
    flag = 1;
    CDC* pDC = GetDC(); 
    CPen newpen(PS_SOLID, 1, RGB(0, 255, 0));
    CPen *old = pDC->SelectObject(&newpen);
    for (int i = 0; i < N; i++) {
        pDC->MoveTo(ptset[i]);
        pDC->LineTo(ptset[i + 1]);
        i++;
    }
    
    CView::OnLButtonDblClk(nFlags, point);
}

void CclipView::OnRButtonDblClk(UINT nFlags, CPoint point)//多邊形
{
    // TODO: 在此添加消息處理程序代碼和/或調用默認值
    flag = 1;
    CDC* pDC = GetDC();
    CPen newpen(PS_DOT, 1, RGB(255, 0, 0));
    CPen *old = pDC->SelectObject(&newpen);
    pt[0] = ptset[1]; pt[1] = ptset[7]; pt[2] = ptset[5]; pt[3] = ptset[2]; pt[4] = ptset[1];
    pDC->MoveTo(ptset[1]);
    pDC->LineTo(ptset[7]);
    pDC->LineTo(ptset[5]);
    pDC->LineTo(ptset[2]);
    pDC->LineTo(ptset[1]);
    CView::OnRButtonDblClk(nFlags, point);
}

 

直線段剪裁(Cohen-Sutherland算法 / Liang-Barsky算法)編碼

(默認矩形窗口)spa

一、Cohen-Sutherland算法code

 以窗口的矩形爲中心,造成一個九宮格,窗口左:0001,窗口右:0010,窗口下:0100,窗口上:1000,其他均由或運算獲得。中點分割法是對Cohen-Sutherland算法的改進。(四次求交,所有捨棄。)blog

 步驟:爲線段的端點編碼 -> 由端點位置決定簡取、簡棄、求交 -> [ 更新兩端點 -> ] 畫線教程

void CclipView::OnClipline()
{
    // TODO: 在此添加命令處理程序代碼
    CDC* pDC = GetDC();
    CPen newpen(PS_SOLID, 1, RGB(0, 0, 0));
    CPen *old = pDC->SelectObject(&newpen);
    if (flag != 1) {
        MessageBox(TEXT("請先雙擊"), TEXT("警告!"));
    }
    else {
        float x, y, x1, x2, y1, y2;
        int i, code1, code2;
        //求兩端點的區號
        for (i = 0; i < N; i++, i++) {
            int c = 0;
            if (ptset[i].x < Xmin)    c = c | LEFT;
            else if (ptset[i].x > Xmax)    c = c | RIGHT;
            if (ptset[i].y > Ymin)    c = c | BOTTOM;
            else if (ptset[i].y < Ymax)    c = c | TOP;
            code1 = c; c = 0;
            if (ptset[i + 1].x < Xmin)    c = c | LEFT;
            else if (ptset[i + 1].x > Xmax)    c = c | RIGHT;
            if (ptset[i + 1].y > Ymin)    c = c | BOTTOM;
            else if (ptset[i + 1].y < Ymax)    c = c | TOP;
            code2 = c;
            //線段與區域相交
            if (code1 != 0 && code2 != 0 && (code1&code2) == 0) { //端點全在框外
                if ((LEFT&code1) != 0) {//與左邊界交
                    x = Xmin;
                    y = ptset[i].y + (ptset[i + 1].y - ptset[i].y)*(Xmin - ptset[i].x) / (ptset[i + 1].x - ptset[i].x);
                } else if ((RIGHT&code1) != 0) {//與右邊界交
                    x = Xmax;
                    y = ptset[i].y + (ptset[i + 1].y - ptset[i].y)*(Xmax - ptset[i].x) / (ptset[i + 1].x - ptset[i].x);
                } else if ((BOTTOM&code1) != 0) {//與下邊界交
                    y = Ymin;
                    x = ptset[i].x + (ptset[i + 1].x - ptset[i].x)*(Ymin - ptset[i].y) / (ptset[i + 1].y - ptset[i].y);
                } else if ((TOP&code1) != 0) {//與上邊界交
                    y = Ymax;
                    x = ptset[i].x + (ptset[i + 1].x - ptset[i].x)*(Ymax - ptset[i].y) / (ptset[i + 1].y - ptset[i].y);
                }
                ptset[i].x = x; ptset[i].y = y;
                if ((LEFT&code2) != 0) {//與左邊界交
                    x = Xmin;
                    y = ptset[i].y + (ptset[i + 1].y - ptset[i].y)*(Xmin - ptset[i].x) / (ptset[i + 1].x - ptset[i].x);
                }
                else if ((RIGHT&code2) != 0) {//與右邊界交
                    x = Xmax;
                    y = ptset[i].y + (ptset[i + 1].y - ptset[i].y)*(Xmax - ptset[i].x) / (ptset[i + 1].x - ptset[i].x);
                }
                else if ((BOTTOM&code2) != 0) {//與下邊界交
                    y = Ymin;
                    x = ptset[i].x + (ptset[i + 1].x - ptset[i].x)*(Ymin - ptset[i].y) / (ptset[i + 1].y - ptset[i].y);
                }
                else if ((TOP&code2) != 0) {//與上邊界交
                    y = Ymax;
                    x = ptset[i].x + (ptset[i + 1].x - ptset[i].x)*(Ymax - ptset[i].y) / (ptset[i + 1].y - ptset[i].y);
                }
                ptset[i + 1].x = x; ptset[i + 1].y = y;
                pDC->MoveTo(ptset[i].x, ptset[i].y);
                pDC->LineTo(ptset[i + 1].x, ptset[i + 1].y);
            }
            if (code1 == 0 && code2 == 0) {    //端點全在框內
                pDC->MoveTo(ptset[i].x, ptset[i].y);
                pDC->LineTo(ptset[i + 1].x, ptset[i + 1].y);
            }
            if (code1 == 0 && code2 != 0) {
                if ((LEFT&code2) != 0) {//與左邊界交
                    x = Xmin;
                    y = ptset[i].y + (ptset[i + 1].y - ptset[i].y)*(Xmin - ptset[i].x) / (ptset[i + 1].x - ptset[i].x);
                }
                else if ((RIGHT&code2) != 0) {//與右邊界交
                    x = Xmax;
                    y = ptset[i].y + (ptset[i + 1].y - ptset[i].y)*(Xmax - ptset[i].x) / (ptset[i + 1].x - ptset[i].x);
                }
                else if ((BOTTOM&code2) != 0) {//與下邊界交
                    y = Ymin;
                    x = ptset[i].x + (ptset[i + 1].x - ptset[i].x)*(Ymin - ptset[i].y) / (ptset[i + 1].y - ptset[i].y);
                }
                else if ((TOP&code2) != 0) {//與上邊界交
                    y = Ymax;
                    x = ptset[i].x + (ptset[i + 1].x - ptset[i].x)*(Ymax - ptset[i].y) / (ptset[i + 1].y - ptset[i].y);
                }
                ptset[i + 1].x = x; ptset[i + 1].y = y;
                pDC->MoveTo(ptset[i].x, ptset[i].y);
                pDC->LineTo(ptset[i + 1].x, ptset[i + 1].y);
            }
            if (code1 != 0 && code2 == 0) {
                if ((LEFT&code1) != 0) {//與左邊界交
                    x = Xmin;
                    y = ptset[i].y + (ptset[i + 1].y - ptset[i].y)*(Xmin - ptset[i].x) / (ptset[i + 1].x - ptset[i].x);
                }
                else if ((RIGHT&code1) != 0) {//與右邊界交
                    x = Xmax;
                    y = ptset[i].y + (ptset[i + 1].y - ptset[i].y)*(Xmax - ptset[i].x) / (ptset[i + 1].x - ptset[i].x);
                }
                else if ((BOTTOM&code1) != 0) {//與下邊界交
                    y = Ymin;
                    x = ptset[i].x + (ptset[i + 1].x - ptset[i].x)*(Ymin - ptset[i].y) / (ptset[i + 1].y - ptset[i].y);
                }
                else if ((TOP&code1) != 0) {//與上邊界交
                    y = Ymax;
                    x = ptset[i].x + (ptset[i + 1].x - ptset[i].x)*(Ymax - ptset[i].y) / (ptset[i + 1].y - ptset[i].y);
                }
                ptset[i].x = x; ptset[i].y = y;
                pDC->MoveTo(ptset[i + 1].x, ptset[i + 1].y);
                pDC->LineTo(ptset[i].x, ptset[i].y);
            }
        }        
    }
}

二、Liang-Barsky算法ip

 二維參數表示一條直線段,把線段當作有向的,每次都要算出直線與四條邊的交點,入邊、出邊由 P 的正負判斷。class

 步驟:求交 -> 每次選取兩點max(入邊交點1,入邊交點2,向量始點),min(出邊交點1,出邊交點2,向量終點)-> 畫線原理

 

多邊形剪裁(Sutherland-Hodgman算法)

一、Sutherland-Hodgman算法

 

 

 步驟:用左右下上四條邊切割原圖形,切割時經過兩點位置,判斷該有向線段簡取/求交,初取時取兩點,後每次取可見側向量終點(多是交點)。

void CclipView::OnClippolygon()
{
    // TODO: 在此添加命令處理程序代碼
    CDC* pDC = GetDC();
    CPen newpen(PS_SOLID, 1, RGB(0, 0, 255));
    CPen *old = pDC->SelectObject(&newpen);
    if (flag != 1) {
        MessageBox(TEXT("請先雙擊右鍵"), TEXT("警告!"));
    } else {
        int i, k; 
        int code1, code2;
        k = 0;
        for (i = 0; i < M-1; i++) {// L
            if (pt[i].x < Xmin)    code1 = LEFT;
            else if (pt[i].x > Xmin)    code1 = 0;
            if (pt[i + 1].x < Xmin)    code2 = LEFT;
            else if (pt[i + 1].x > Xmin)    code2 = 0;
            if (code1 != 0 && code2 == 0) {
                p[k].x = Xmin;
                p[k].y = pt[i].y + (pt[i + 1].y - pt[i].y)*(Xmin - pt[i].x) / (pt[i + 1].x - pt[i].x);
                p[k + 1].x = pt[i + 1].x; p[k + 1].y = pt[i + 1].y;
                k = k + 2;
            }
            if (code1 == 0 && code2 == 0) {
                if (k == 0) {
                    p[k].x = pt[i].x; p[k].y = pt[i].y;
                    p[k + 1].x = pt[i + 1].x; p[k + 1].y = pt[i + 1].y;
                    k = k + 2;
                } else {
                    p[k].x = pt[i + 1].x; p[k].y = pt[i + 1].y;
                    k = k + 1;
                }
            }
            if (code1 == 0 && code2 != 0) {
                p[k].x = Xmin;
                p[k].y = pt[i].y + (pt[i + 1].y - pt[i].y)*(Xmin - pt[i].x) / (pt[i + 1].x - pt[i].x);
                k = k + 1;
            }
        }
        M = k; k = 0;
        for (i = 0; i < M-1; i++) {// R
            if (p[i].x < Xmax)    code1 = 0;
            else if (p[i].x > Xmax)    code1 = RIGHT;
            if (p[i + 1].x < Xmax)    code2 = 0;
            else if (p[i + 1].x > Xmax)    code2 = RIGHT;
            if (code1 != 0 && code2 == 0) {
                pt[k].x = Xmax;
                pt[k].y = p[i].y + (p[i + 1].y - p[i].y)*(Xmax - p[i].x) / (p[i + 1].x - p[i].x);
                pt[k + 1].x = p[i + 1].x; pt[k + 1].y = p[i + 1].y;
                k = k + 2;
            }
            if (code1 == 0 && code2 == 0) {
                if (k == 0) {
                    pt[k].x = p[i].x; pt[k].y = p[i].y;
                    pt[k + 1].x = p[i + 1].x; pt[k + 1].y = p[i + 1].y;
                    k = k + 2;
                }
                else {
                    pt[k].x = p[i + 1].x; pt[k].y = p[i + 1].y;
                    k = k + 1;
                }
            }
            if (code1 == 0 && code2 != 0) {
                pt[k].x = Xmax;
                pt[k].y = p[i].y + (p[i + 1].y - p[i].y)*(Xmax - p[i].x) / (p[i + 1].x - p[i].x);
                k++;
            }
        }
        M = k; k = 0;    
        for (i = 0; i < M-1; i++) {// B
            if (pt[i].y > Ymin)    code1 = BOTTOM;
            else if (pt[i].y < Ymin)    code1 = 0;
            if (pt[i + 1].y > Ymin)    code2 = BOTTOM;
            else if (pt[i + 1].y < Ymin)    code2 = 0;
            if (code1 != 0 && code2 == 0) {
                p[k].y = Ymin;
                p[k].x = pt[i].x + (pt[i + 1].x - pt[i].x)*(Ymin - pt[i].y) / (pt[i + 1].y - pt[i].y);
                p[k + 1].x = pt[i + 1].x; p[k + 1].y = pt[i + 1].y;
                k = k + 2;
            }
            if (code1 == 0 && code2 == 0) {
                if (k == 0) {
                    p[k].x = pt[i].x; p[k].y = pt[i].y;
                    p[k + 1].x = pt[i + 1].x; p[k + 1].y = pt[i + 1].y;
                    k = k + 2;
                }
                else {
                    p[k].x = pt[i + 1].x; p[k].y = pt[i + 1].y;
                    k = k + 1;
                }
            }
            if (code1 == 0 && code2 != 0) {
                p[k].y = Ymin;
                p[k].x = pt[i].x + (pt[i + 1].x - pt[i].x)*(Ymin - pt[i].y) / (pt[i + 1].y - pt[i].y);
                k++;
            }
        }
        M = k; k = 0;
        for (i = 0; i < M-1; i++) {// T
            if (p[i].y > Ymax)    code1 = 0;
            else if (p[i].y < Ymax)    code1 = 0;
            if (p[i + 1].y > Ymax)    code2 = 0;
            else if (p[i + 1].y < Ymax)    code2 = TOP;
            if (code1 != 0 && code2 == 0) {
                pt[k].y = Ymax;
                pt[k].x = p[i].x + (p[i + 1].x - p[i].x)*(Ymax - p[i].y) / (p[i + 1].y - p[i].y);
                pt[k + 1].x = p[i + 1].x; pt[k + 1].y = p[i + 1].y;
                k = k + 2;
            }
            if (code1 == 0 && code2 == 0) {
                if (k == 0) {
                    pt[k].x = p[i].x; pt[k].y = p[i].y;
                    pt[k + 1].x = p[i + 1].x; pt[k + 1].y = p[i + 1].y;
                    k = k + 2;
                }
                else {
                    pt[k].x = p[i + 1].x; pt[k].y = p[i + 1].y;
                    k = k + 1;
                }
            }
            if (code1 == 0 && code2 != 0) {
                pt[k].y = Ymax;
                pt[k].x = p[i].x + (p[i + 1].x - p[i].x)*(Ymax - p[i].y) / (p[i + 1].y - p[i].y);
                k++;
            }
        }
        M = k;
        pDC->MoveTo(pt[0]);
        for (int j = 1; j < M; j++) {
            pDC->LineTo(pt[j]);
        }
    }
}

二、ex:Weiler-Atherton算法

先這樣吧,之後再加。

 

參考資料:

一、《計算機圖形學原理及算法教程》和青芳 編著

二、計算機圖形學 - 中國農業大學 趙明 

 

本文采用CC BY 4.0知識共享許可協議。

相關文章
相關標籤/搜索