二维图形变换&裁剪
实验三 二维图形变换&裁剪
一、综述
掌握二维图形显示处理的原理、流程和实现方法,包括二维图形空间建模、基本变换/变换序列、裁剪、视见变换和绘制处理以及简单的交互控制手段。本实验是矩形窗口裁剪,算法包括:Cohen-Sutherland裁剪算法,Sutherland多边形裁剪
二、程序框架
MFC程序:
Ccg2020LMMDrawLineView.h是视图层的头文件,负责声明各种成员变量和成员函数。
Ccg2020LMMDrawLineView.cpp是视图层的源文件,负责实现裁剪和基本图形变换等功能。
CgTransControl.h是为窗口面板的按键和文本定义成员变量和成员函数。
CgTransControl.cpp是实现面板的功能,例如+x,-x,+y,-y,对比等图形裁剪输出展示。
三、模块设计算法描述
Cohen-Sutherland裁剪算法
1.1 基本思想
对于每条线段P1P2分为三种情况处理:
(1)若P1P2完全在窗口内,则显示该线段P1P2。
(2)若P1P2明显在窗口外,则丢弃该线段。
(3)若线段不满足(1)或(2)的条件,则在交点处把线段分为两段。其中一段完全在窗口外,可弃之。然后对另一段重复上述处理。
1.2编码方式
把窗口的边界延长成直线,窗口平台就分成9个分区,每个区设定一个4位的编码与之对应。
- 若x<wxl,则D0=1,否则D0=0;
- 若x>wxr,则D1=1,否则D1=0;
- 若y<wyb,则D2=1,否则D2=0;
- 若y>wyt,则D3=1,否则D3=0。
1.3裁剪
- 若P1P2完全在窗口内code1=0,且code2=0,则“取”;
- 若P1P2明显在窗口外code1&code2≠0,则“弃” ;
- 在交点处把线段分为两段。其中一段完全在窗口外,可弃之。然后对另一段重复上述处理。
1.4 求交处理
按左、下、右、上的顺序求出直线段与窗口边界的交点,分段处理
Sutherland-Hodgman 多边形裁剪实现
实现思路很清晰,有大一部分思路是和线段裁剪相似。
- 顺序存储多边形的顶点;
- 依次用每一条裁剪边对输入的顶点序列进行如下处理:
- 若当前顶点可见(在裁剪框内部),则将该顶点加入到输出顶点序列中(output());对当前顶点和下一顶点构成的线段进行裁剪判断;
- 如果这条线段边和裁剪边界有交点,则将该交点加入到输出序列中;
- 最后进行封闭检查,使得输出序列中的顶点信息的头和尾是相同的。
四、处理流程
在根据cg2020QB2DTrans程序提供的源码,自建项目完善补充时,基本按照如下顺序理解算法及相关思路并完成实验要求:
- 基本变换矩阵的实现
- 绘制直线
- 直线2D变换
- 直线裁剪
- 绘制多边形
- 多边形2D变换
- 多边形裁剪
五、运行结果
六、实验总结
首先,现在对于MFC的相关调用及基础实现应该比较熟练了,再者通过本次二维图形变换实验,学生进一步理解掌握二维图形显示处理的原理、流程和实现方法,将上课学习的裁剪方法落地,当然对于包括二维图形空间建模、基本变换/变换序列、视见变换等简单的交互控制手段有了清楚的认知。
实验中遇到的问题及解决如下:
- 在初步补充完成直线裁剪、展示代码后运行没有实验结果(具体指运行时Picture Control没有内容显示),在几次思考代码没有问题情况下,请教同学,被告知是在运行界面有进一步操作后才有所展示,理解到控件展示的小窗口被规划到onupdate函数中,了解。
- 重合多边形轮廓的颜色总和窗口颜色保持一致,非常不便于观察,在仔细考虑程序思路及打断点后发现是绘制多边形函数靠近结束部分,含有窗口轮廓重新绘制代码,去掉后解决该问题。
- picture control大小规划的不是很合适,使2D图形展示不是很完整,重新调整控件大小,问题解决。
- 窗口展示的多边形方向不太对头,怀疑是点规划时x,y是不是有什么加减没有定义好,是y的问题应该改为dcRect.bottom - (pDoc->clipPolygon[i].y - pDoc->m_wndLy)*Sy;问题解决
收获良多,谢谢。
核心代码
void CalculateMatrix(float transMatrix[3][2])函数的系列变换:
void Ccg2020YBH2DtransView::RotateMatrix(float S, float C, float m[3][2])
{
float temp;
for (int i = 0; i < 3; i++) {
temp = m[i][0] * C - m[i][1] * S;
m[i][1] = m[i][0] * S + m[i][1] * C;
m[i][0] = temp;
}
}
void Ccg2020YBH2DtransView::TranslateMatrix(float Tx, float Ty, float m[3][2])
{
m[2][0] += Tx;
m[2][1] += Ty;
}
void Ccg2020YBH2DtransView::ScaleMatrix(float Sx, float Sy, float m[3][2])
{
m[0][0] *= Sx;
m[1][1] *= Sy;
}
直线、多边形2D变换及绘制:
void Ccg2020YBH2DtransView::TransLine(CPoint *p1, CPoint *p2, CPoint *tp1, CPoint *tp2,float transMatrix[3][2])
{
tp1->x = (int)(p1->x * transMatrix[0][0] + p1->y * transMatrix[1][0] + transMatrix[2][0]);
tp1->y = (int)(p1->x * transMatrix[0][1] + p1->y * transMatrix[1][1] + transMatrix[2][1]);
tp2->x = (int)(p2->x * transMatrix[0][0] + p2->y * transMatrix[1][0] + transMatrix[2][0]);
tp2->y = (int)(p2->x * transMatrix[0][1] + p2->y * transMatrix[1][1] + transMatrix[2][1]);
}
void Ccg2020YBH2DtransView::DisplayLine(CDC* pDC, CPoint p1, CPoint p2, COLORREF rgbColor)
{
Ccg2020YBH2DtransDoc* pDoc = GetDocument();
CPen newPen;
CPen *oldPen;
CPoint VP1, VP2;
newPen.CreatePen(PS_SOLID, 2, rgbColor);
oldPen = (CPen *)pDC->SelectObject(&newPen);
VP1.x = m_wndWidth / 2 + p1.x;
VP1.y = m_wndHeight / 2 - p1.y;
VP2.x = m_wndWidth / 2 + p2.x;
VP2.y = m_wndHeight / 2 - p2.y;
pDC->MoveTo(VP1);
pDC->LineTo(VP2);
pDC->SelectObject(oldPen);
newPen.DeleteObject();
}
void Ccg2020YBH2DtransView::TransPolygon(int pointNumber, CPoint spPolygon[N],
CPoint transPolygon[N], float transMatrix[3][2])
{
Ccg2020YBH2DtransDoc* pDoc = GetDocument();
for (int i = 0; i < pointNumber; i++) {
transPolygon[i].x = spPolygon[i].x * transMatrix[0][0] +
spPolygon[i].y * transMatrix[1][0] +transMatrix[2][0];
transPolygon[i].y = spPolygon[i].x * transMatrix[0][1] +
spPolygon[i].y * transMatrix[1][1] +transMatrix[2][1];
}
}
void Ccg2020YBH2DtransView::DisplayPolygon(CDC* pDC, int pointNumber,
CPoint transPolygon[N], COLORREF rgbColor)
{
Ccg2020YBH2DtransDoc* pDoc = GetDocument();
CPen newPen;
CPen *oldPen;
CPoint VPolygon[N];
newPen.CreatePen(PS_SOLID, 2, rgbColor);
oldPen = (CPen *)pDC->SelectObject(&newPen);
for (int i = 0; i < pointNumber; i++) {
VPolygon[i].x = m_wndWidth / 2 + transPolygon[i].x;
VPolygon[i].y = m_wndHeight / 2 - transPolygon[i].y;
}
pDC->MoveTo(VPolygon[0]);
for (int i = 1; i < pointNumber; i++) pDC->LineTo(VPolygon[i]);
pDC->SelectObject(oldPen);
newPen.DeleteObject();
}
Cohen-Sutherland裁剪:
// Cohn-Sutherland Subdivision Line Clip
int Ccg2020YBH2DtransView::ClipLine(int *x1, int *y1, int *x2, int *y2)
{
int visible, m_window[4];
Ccg2020YBH2DtransDoc* pDoc = GetDocument();
m_window[0] = pDoc->m_wndLx; m_window[1] = pDoc->m_wndRx;
m_window[2] = pDoc->m_wndRy; m_window[3] = pDoc->m_wndLy;
for (int i = 0; i < 4; i++) { // Along the WIN Border
visible = LineVisible(x1, y1, x2, y2);
if (visible == 1) return 1; // Total Visible
if (visible == 0) return 0; // Total Unvisible
if (LineCross(*x1, *y1, *x2, *y2, i)) {
if (i < 2 && *x2 - *x1) { // Left , Right
float m = (float)(*y2 - *y1) / (*x2 - *x1);
float iy = m * (m_window[i] - *x1) + *y1;
// 根据端点大小,来进行端点的更新,舍弃window框之外的部分
if (i == 0) {
if (*x1 < *x2) {
*x1 = m_window[i];
*y1 = iy;
}
else {
*x2 = m_window[i];
*y2 = iy;
}
}
else {
if (*x1 > *x2) {
*x1 = m_window[i];
*y1 = iy;
}
else {
*x2 = m_window[i];
*y2 = iy;
}
}
}
else if (*y2 - *y1) { // Top Bottom
float m = (float)(*x2 - *x1) / (*y2 - *y1);
float ix = m * (m_window[i] - *y1) + *x1;
// Please fill in the right code below ...
if (i == 2) {
if (*y1 > *y2) {
*x1 = ix;
*y1 = m_window[i];
}
else {
*x2 = ix;
*y2 = m_window[i];
}
}
else {
if (*y1 < *y2) {
*x1 = ix;
*y1 = m_window[i];
}
else {
*x2 = ix;
*y2 = m_window[i];
}
}
}
}
}
return 1;
}
int Ccg2020YBH2DtransView::LineVisible(int *x1, int *y1, int *x2, int *y2)
{
int pcode1, pcode2;
pcode1 = pCode(x1, y1);
pcode2 = pCode(x2, y2);
if (!pcode1 && !pcode2) return 1; // Visible
if ((pcode1&pcode2) != 0) return 0; // Unvisible
if (pcode1 == 0) {
float temp;
temp = *x1; *x1 = *x2; *x2 = temp;
temp = *y1; *y1 = *y2; *y2 = temp;
}
return 2;
}
int Ccg2020YBH2DtransView::pCode(int *x, int *y)
{
int code = 0;
Ccg2020YBH2DtransDoc* pDoc = GetDocument();
if (*x <= pDoc->m_wndLx) code |= 1;
if (*x >= pDoc->m_wndRx) code |= 2;
if (*y >= pDoc->m_wndRy) code |= 4;
if (*y <= pDoc->m_wndLy) code |= 8;
return code;
}
int Ccg2020YBH2DtransView::LineCross(int x1, int y1, int x2, int y2, int i)
{
int visible1, visible2;
visible1 = pVisible(x1, y1, i);
visible2 = pVisible(x2, y2, i);
if (visible1 != visible2) return 1;
else return 0;
}
int Ccg2020YBH2DtransView::pVisible(int x, int y, int i)
{
int visible = 0;
Ccg2020YBH2DtransDoc* pDoc = GetDocument();
switch (i) {
case 0: // Left
if (x >= pDoc->m_wndLx) visible = 1; break;
case 1: // Right
if (x <= pDoc->m_wndRx) visible = 1; break;
case 2: // Top
if (y <= pDoc->m_wndRy) visible = 1; break;
case 3: // Bottom
if (y >= pDoc->m_wndLy) visible = 1; break;
}
return visible;
}
Sutherland-Hodgman 多边形裁剪:
// Sutherland-Hodgman Polygon Clip
int Ccg2020YBH2DtransView::ClipPolygon(int n, CPoint *tPoints, int *cn, CPoint *cPoints)
{
int Nin, Nout, ix, iy, Sx, Sy;
Ccg2020YBH2DtransDoc* pDoc = GetDocument();
Nin = n;
for (int i = 0; i < 4; i++) { // Along the window border
*cn = 0;
for (int j = 0; j < Nin; j++) { // Scan polygon every point and line.
if (j > 0) {
// 如果存在边穿过边界,则更新顶点
if (LineCross(Sx, Sy, tPoints[j].x, tPoints[j].y, i)) {
interSect(Sx, Sy, tPoints[j].x, tPoints[j].y, i, &ix, &iy);
outPut(ix, iy, cn, cPoints);
}
}
//当前顶点可见
Sx = tPoints[j].x;
Sy = tPoints[j].y;
if (pVisible(Sx, Sy, i))
outPut(Sx, Sy, cn, cPoints);
}
Nin = *cn;
if (*cn == 0) return 0;
for (int j = 0; j < Nin; j++) {
tPoints[j].x = cPoints[j].x;
tPoints[j].y = cPoints[j].y;
}
if (cPoints[0].x != cPoints[Nin - 1].x ||
cPoints[0].y != cPoints[Nin - 1].y) {
tPoints[Nin].x = cPoints[Nin].x = cPoints[0].x;
tPoints[Nin].y = cPoints[Nin].y = cPoints[0].y;
Nin++;
*cn = Nin;
}
}
return 1;
}
void Ccg2020YBH2DtransView::interSect(int Sx, int Sy, int Px, int Py,
int i, int *ix, int *iy)
{
Ccg2020YBH2DtransDoc* pDoc = GetDocument();
switch (i) {
case 0: // Left
*ix = pDoc->m_wndLx;
*iy = (Sy - Py) * (pDoc->m_wndLx - Px) / (Sx - Px) + Py;
break;
case 1: // Right
*ix = pDoc->m_wndRx;
*iy = (Sy - Py) * (pDoc->m_wndRx - Px) / (Sx - Px) + Py;
break;
case 2: // Top
*iy = pDoc->m_wndRy;
*ix = (Sx - Px) * (pDoc->m_wndRy - Py) / (Sy - Py) + Px;
break;
case 3: // Bottom
*iy = pDoc->m_wndLy;
*ix = (Sx - Px) * (pDoc->m_wndLy - Py) / (Sy - Py) + Px;
break;
}
}
void Ccg2020YBH2DtransView::outPut(int x, int y, int *cn, CPoint *cPoints)
{
cPoints[*cn].x = x;
cPoints[*cn].y = y;
(*cn)++;
}
加油呀