📅  最后修改于: 2023-12-03 14:59:04.645000             🧑  作者: Mango
在计算机图形学中,剪切是指在二维或三维几何体中提取一个子集的操作。在二维图形中,剪切可以用来裁剪图形的一部分。通常情况下,剪切是通过定义一个裁剪区域来实现的。在裁剪区域之外的部分将被删除或隐藏,而裁剪区域内的部分将被保留。在计算机图形学中,剪切是一种常见的操作,是制作动画和游戏中常用的技术之一。
根据剪切算法的原理,我们可以将常见的剪切算法分为以下几类:
线段剪切可以将线段裁剪到裁剪区域内。有几种线段剪切算法,包括框架线剪切、中点线剪切和Cohen–Sutherland剪切算法(也称为直线剪切算法)。Cohen–Sutherland剪切算法是最广泛使用的线段剪切算法之一。它使用二进制数来表示线段所在的区域,并使用位运算来判断线段是否与裁剪区域相交。
多边形剪切可以将多边形裁剪到裁剪区域内。有几种多边形剪切算法,包括Sutherland–Hodgman多边形剪切算法、Weiler–Atherton多边形剪切算法和Li–Canny多边形剪切算法。其中,Sutherland–Hodgman多边形剪切算法是最广泛使用的多边形剪切算法之一,它通过将多边形的每个顶点和裁剪区域相交来计算多边形的新形状。
图像剪切可以将图像裁剪到裁剪区域内。有几种图像剪切算法,包括绘图API剪切、基于遮罩的剪切和图像数据剪切。绘图API剪切是最简单的剪切算法之一,它可以使用现代图形API的剪切功能来裁剪图像。基于遮罩的剪切是一种更高级的剪切技术,它可以使用遮罩来保留图像的一部分,并在剩余区域中应用其他效果。
Cohen–Sutherland剪切算法使用二进制数来表示线段所在的区域,并使用位运算来判断线段是否与裁剪区域相交。该算法的实现涉及到一个线段网格分割,分为四个区域:左侧、右侧、上方和下方。每个线段都被分成四个部分,以便在判断重叠区域时更容易处理。如果最终的结果是“1”,则表示该线段与裁剪区域无交集;如果最终结果为“0”,则表示该线段与裁剪区域相交。以下是Cohen–Sutherland线段剪切算法的示例实现:
enum {LEFT=1, RIGHT=2, BOTTOM=4, TOP=8}; // 4边赋值为char的2进制值
int ComputeOutCode(double x, double y, double xMin, double yMin, double xMax, double yMax)
{ //根据点(x,y)相对于窗口的位置关系,返回相应的区域码
int code = 0;
if (x<xMin)
code |= LEFT;
else if (x>xMax)
code |= RIGHT;
if (y<yMin)
code |= BOTTOM;
else if (y>yMax)
code |= TOP;
return code;
}
void Cohen_Sutherland_Line_Clip_And_Draw(double x1, double y1, double x2, double y2, double xMin, double yMin, double xMax, double yMax, COLORREF color)
{
int outCode1 = ComputeOutCode(x1, y1, xMin, yMin, xMax, yMax);
int outCode2 = ComputeOutCode(x2, y2, xMin, yMin, xMax, yMax);
bool accept = false;
while (true)
{
if (!((outCode1 | outCode2) != 0)) //线段在窗口内部
{
accept = true;
break;
}
else if ((outCode1 & outCode2) != 0) //线段在窗口外部
{
break;
}
else //线段跨越窗口边界
{
double x, y;
int outCode = outCode1 ? outCode1 : outCode2;
if (outCode & TOP) //线段从顶部离开窗口
{
x = x1 + (x2 - x1) * (yMax - y1) / (y2 - y1);
y = yMax;
}
else if (outCode & BOTTOM) //线段从底部离开窗口
{
x = x1 + (x2 - x1) * (yMin - y1) / (y2 - y1);
y = yMin;
}
else if (outCode & RIGHT) //线段从右侧离开窗口
{
y = y1 + (y2 - y1) * (xMax - x1) / (x2 - x1);
x = xMax;
}
else if (outCode & LEFT) //线段从左侧离开窗口
{
y = y1 + (y2 - y1) * (xMin - x1) / (x2 - x1);
x = xMin;
}
if (outCode == outCode1) //更新第一个点坐标
{
x1 = x;
y1 = y;
outCode1 = ComputeOutCode(x1, y1, xMin, yMin, xMax, yMax);
}
else //更新第二个点坐标
{
x2 = x;
y2 = y;
outCode2 = ComputeOutCode(x2, y2, xMin, yMin, xMax, yMax);
}
}
}
if (accept)
{
DrawLine(x1, y1, x2, y2, color);
}
}
Sutherland–Hodgman多边形剪切算法是最广泛使用的多边形剪切算法之一,它通过将多边形的每个顶点和裁剪区域相交来计算多边形的新形状。该算法的实现涉及到计算多边形的交点,然后根据交点构建新的多边形。以下是Sutherland–Hodgman多边形剪切算法的示例实现:
typedef struct _POINT
{
double x;
double y;
} POINT, *PPOINT;
inline double distance(const POINT &p1, const POINT &p2)
{ //求两点距离
return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
bool inside(const POINT &p, const POINT &p1, const POINT &p2)
{ //判断点是否在边界内
double d1 = distance(p, p1);
double d2 = distance(p, p2);
double D = distance(p1, p2);
return fabs(d1 + d2 - D) <= 0.0001;
}
void sutherland_hodgman_polygon_clip(PPOINT pIn, int nIn, PPOINT pOut, int &nOut, double xMin, double yMin, double xMax, double yMax)
{
PPOINT pS = pIn + nIn -1;
for (int j=0; j<4; ++j, --pS) // 扫描全部边界(分别作为裁剪边)、上右下左
{
PPOINT pC = pIn + nIn -1;
int count = 0;
PPOINT pTemp = pOut;
for (int i=0; i<nIn; ++i, ++pC)
{
if (inside(*pC, *pS, *(pS-1))) // 内部点
{
if (!inside(*(pC-1), *pS, *(pS-1))) // 外部点
{
double x = pC->x, y = pC->y;
if (pS->x != (pS-1)->x) // 如果非水平边
{
y = ( pC->y - ( (pS-1)->y ) ) * ( (pS->x)-(pS-1)->x) / ((pS->y)-(pS-1)->y) + (pS-1)->x;
if (y > yMax) // 走过裁剪窗口
y = yMax;
else if (y < yMin)
y = yMin;
}
else // 如果是水平边
{
x = pS->x;
if (pC->y > (pS-1)->y) // 非自下向上的线段
{
if (x > xMax)
continue;
else if (x < xMin)
continue;
}
else if (pC->y < (pS-1)->y) // 自下向上线段
{
if (x > xMax)
x = xMax;
else if (x < xMin)
x = xMin;
}
}
pTemp->x = x;
pTemp++ ->y = y;
count++;
}
pTemp->x = pC->x;
pTemp++ ->y = pC->y;
count++;
}
else if (inside(*(pC-1), *pS, *(pS-1))) // 外部点
{
double x = pC->x, y = pC->y;
if (pS->x != (pS-1)->x) // 如果非水平边
{
y = ( pC->y - ( (pS-1)->y ) ) * ( (pS->x)-(pS-1)->x) / ((pS->y)-(pS-1)->y) + (pS-1)->x;
if (y > yMax)
y = yMax;
else if (y < yMin)
y = yMin;
}
else // 如果是水平边
{
x = pS->x;
if (pC->y > (pS-1)->y) // 非自下向上的线段
{
if (x > xMax)
continue;
else if (x < xMin)
continue;
}
else if (pC->y < (pS-1)->y) // 自下向上线段
{
if (x > xMax)
x = xMax;
else if (x < xMin)
x = xMin;
}
}
pTemp->x = x;
pTemp++ ->y = y;
count++;
}
}
nIn = count;
memcpy(pIn, pOut, count * sizeof(POINT));
}
nOut = nIn;
}
剪切是计算机图形学中的重要技术之一,可以用于编辑和划分二维图形、三维立体图形、图像等。在实际应用中,剪切算法的选择应根据具体需求来进行。在实现剪切算法时,需要考虑算法的效率以及面对不同情景时的表现。以上是两个常见的剪切算法,我们可以根据实际需要来选择并实现适合自己的算法。