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知識共享許可協議。