計算機圖形學常用演算法實現10 多邊形裁剪Sutherland-Hodgman演算法
阿新 • • 發佈:2019-01-01
演算法原理比較簡單,用裁剪區域的四條邊分別去分割多邊形。
假設邊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();
}
}
}