算法原理比较简单,用裁剪区域的四条边分别去分割多边形。
假设边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();
}
}
}