1. 程式人生 > >計算機圖形學常用演算法實現10 多邊形裁剪Sutherland-Hodgman演算法

計算機圖形學常用演算法實現10 多邊形裁剪Sutherland-Hodgman演算法

演算法原理比較簡單,用裁剪區域的四條邊分別去分割多邊形。
假設邊p1,p2,用區域某條邊進行裁剪。方向為p1->p2
1.p1在邊外,p2在邊外
無操作
2.p1外,p2內
儲存交點p和p2
3.p1內,p2內
儲存p2
4.p1內,p2外
儲存交點
分割完之後更新多邊形的點集
程式碼裁剪區域為(200,200)到(400,400)


       private void SutherlandHodgmanClip()
        {
            //分四條邊裁剪
            clipEdge(1);
            clipEdge(2);
            clipEdge
(3); clipEdge(4); g.Clear(Color.White); drawEdge(); for (int i = 0; i < indexOfPolygon; i++) g.DrawLine(p, polygon[i], polygon[(i + 1) % indexOfPolygon]); }

求交點的函式

     private Point findIntersection(int edge,Point p1,Point p2)
{ Point pt = new Point(); if (edge == 1) { pt.X = 200; pt.Y = (int)(p1.Y - (float)(p1.Y - p2.Y) * (p1.X - 200) / (p1.X - p2.X)); } else if (edge == 3) { pt.X = 400; pt.Y = (
int)(p1.Y - (float)(p1.Y - p2.Y) * (p1.X - 400) / (p1.X - p2.X)); } else if (edge == 2) { pt.Y = 200; pt.X = (int)(p1.X - (float)(p1.X - p2.X) * (p1.Y - 200) / (p1.Y - p2.Y)); } else if (edge == 4) { pt.Y = 400; pt.X = (int)(p1.X - (float)(p1.X - p2.X) * (p1.Y - 400) / (p1.Y - p2.Y)); } return pt; }

判斷點是不是在邊外

    private bool inSide(int edge,Point pt)
     {
         //左垂直邊
         if (edge == 1 && pt.X < 200|| edge == 3 && pt.X > 400|| edge == 2 && pt.Y < 200||edge == 4 &&pt.Y>400)
             return false;
         return true;
     }

裁剪區域的某條邊進行裁剪

    private void clipEdge(int edge)
     {
         indexOfPolygonTemp = 0;
         bool isP1In, isP2In;
         for (int i = 0; i < indexOfPolygon; i++)
         {
             isP1In = inSide(edge,polygon[i]);
             isP2In = inSide(edge,polygon[(i+1)%indexOfPolygon]);
             if (isP1In && isP2In)
                 polygonTemp[indexOfPolygonTemp++] = polygon[(i + 1) % indexOfPolygon];
             else if (isP1In)
                 polygonTemp[indexOfPolygonTemp++] = findIntersection(edge, polygon[i], polygon[(i + 1) % indexOfPolygon]);
             else if (isP2In)
             {
                 polygonTemp[indexOfPolygonTemp++] = findIntersection(edge, polygon[i], polygon[(i + 1) % indexOfPolygon]);
                 polygonTemp[indexOfPolygonTemp++] = polygon[(i + 1) % indexOfPolygon];
             }
         }
         indexOfPolygon = indexOfPolygonTemp;
         for (int i = 0; i < indexOfPolygon; i++)
             polygon[i] = polygonTemp[i];
     }

在這裡插入圖片描述

在這裡插入圖片描述

完整可執行程式碼如下:(vs2015 winform)

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace cgdemo5
    {
        public partial class Form1 : Form
        {
            private Point[] polygon;
            private Point[] polygonTemp;
            private bool isClick=false;
            private int indexOfPolygon = 0;
            private int indexOfPolygonTemp = 0;
            private bool isFinished = false;
            Graphics g;
            Pen p = new Pen(Color.Red);
            public Form1()
            {
                polygon = new Point[10010];
                polygonTemp = new Point[10010];
                InitializeComponent();
                g = this.CreateGraphics();
            }
            private void drawEdge()
            {
                g.DrawLine(p,200,200,200,400);
                g.DrawLine(p,200,400,400,400);
                g.DrawLine(p, 400, 400, 400, 200);
                g.DrawLine(p,400,200,200,200);
            }
            private bool inSide(int edge,Point pt)
            {
                //左垂直邊
                if (edge == 1 && pt.X < 200|| edge == 3 && pt.X > 400|| edge == 2 && pt.Y < 200||edge == 4 &&pt.Y>400)
                    return false;
                return true;
            }
            private void SutherlandHodgmanClip()
            {
                //分四條邊裁剪
                clipEdge(1);
                clipEdge(2);
                clipEdge(3);
                clipEdge(4);
                g.Clear(Color.White);
                drawEdge();
                for (int i = 0; i < indexOfPolygon; i++)
                    g.DrawLine(p, polygon[i], polygon[(i + 1) % indexOfPolygon]);
            }
            private Point findIntersection(int edge,Point p1,Point p2)
            {
                Point pt = new Point();
                if (edge == 1)
                {
                    pt.X = 200;
                    pt.Y = (int)(p1.Y - (float)(p1.Y - p2.Y) * (p1.X - 200) / (p1.X - p2.X));
                }
                else if (edge == 3)
                {
                    pt.X = 400;
                    pt.Y = (int)(p1.Y - (float)(p1.Y - p2.Y) * (p1.X - 400) / (p1.X - p2.X));
                }
                else if (edge == 2)
                {
                    pt.Y = 200;
                    pt.X = (int)(p1.X - (float)(p1.X - p2.X) * (p1.Y - 200) / (p1.Y - p2.Y));
                }
                else if (edge == 4)
                {
                    pt.Y = 400;
                    pt.X = (int)(p1.X - (float)(p1.X - p2.X) * (p1.Y - 400) / (p1.Y - p2.Y));
                }
                return pt;
            }
            private void clipEdge(int edge)
            {
                indexOfPolygonTemp = 0;
                bool isP1In, isP2In;
                for (int i = 0; i < indexOfPolygon; i++)
                {
                    isP1In = inSide(edge,polygon[i]);
                    isP2In = inSide(edge,polygon[(i+1)%indexOfPolygon]);
                    if (isP1In && isP2In)
                        polygonTemp[indexOfPolygonTemp++] = polygon[(i + 1) % indexOfPolygon];
                    else if (isP1In)
                        polygonTemp[indexOfPolygonTemp++] = findIntersection(edge, polygon[i], polygon[(i + 1) % indexOfPolygon]);
                    else if (isP2In)
                    {
                        polygonTemp[indexOfPolygonTemp++] = findIntersection(edge, polygon[i], polygon[(i + 1) % indexOfPolygon]);
                        polygonTemp[indexOfPolygonTemp++] = polygon[(i + 1) % indexOfPolygon];
                    }
                }
                indexOfPolygon = indexOfPolygonTemp;
                for (int i = 0; i < indexOfPolygon; i++)
                    polygon[i] = polygonTemp[i];
            }
            private double distance(Point p1,Point p2)
            {
                return Math.Sqrt((p1.X-p2.X)*(p1.X-p2.X)+(p1.Y-p2.Y)*(p1.Y-p2.Y));
            }
    
            private void Form1_MouseClick(object sender, MouseEventArgs e)
            {
                drawEdge();
                   RectangleF rec = new RectangleF(e.Location.X - 1, e.Location.Y - 1, 3, 3);
                g.DrawEllipse(p, rec);
                if (isClick == false)
                {
                    Point pp = new Point();
                    pp.X = e.Location.X;
                    pp.Y = e.Location.Y;
                    polygon[indexOfPolygon++] = pp;
                    isClick = true;
                }
                else
                {
                    Point pp = new Point();
                    pp.X = e.Location.X;
                    pp.Y = e.Location.Y;
                    if (distance(pp, polygon[0]) < 8)
                    {
                        for (int i = 0; i < indexOfPolygon; i++)
                            g.DrawLine(p,polygon[i],polygon[(i+1)%indexOfPolygon]);
                        isFinished = true;
                    }
                    polygon[indexOfPolygon++] = pp;
                }
            }
    
            private void Form1_KeyUp(object sender, KeyEventArgs e)
            {
                if (isFinished)
                    SutherlandHodgmanClip();
            }
        }
    }