文章目录
- 前言
- 一、前期准备
- 1.成员变量
- 2.鼠标响应事件
- 二、正式画图
- 1. 调用Display()函数
- 2. 画网格
- 3. 画线
- 4. DDA算法
- 总结
前言
这是上一篇文章的详解版本,容我和大家详细地分享一下画线思路
一、前期准备
1.成员变量
// 背景网格的间隔const float GRIDGAP = 0.1f;// 记录起始点CPoint startPoint;CPoint endPoint;// 判断绘图状态bool isDrawing;// 保存画好的线int lineCount;CPoint linePoints[1000][2];
2.鼠标响应事件
鼠标按下时,准备绘制当前线段。将isDrawing置为true,并记录起点位置。嗯……顺便把终点位置也放到那
void CMy22uCGv1View::OnLButtonDown(UINT nFlags, CPoint point)
{isDrawing = true;startPoint = point;endPoint = point;Invalidate(false);COpenGLView::OnLButtonDown(nFlags, point);
}
鼠标移动时,只要在画图,就让当前线段的尾部跟着移动
void CMy22uCGv1View::OnMouseMove(UINT nFlags, CPoint point)
{if (isDrawing) {endPoint = point;Invalidate(false);}COpenGLView::OnMouseMove(nFlags, point);
}
鼠标抬起时,结束绘制,并保存当前线段
void CMy22uCGv1View::OnLButtonUp(UINT nFlags, CPoint point)
{isDrawing = false;endPoint = point;linePoints[lineCount][0] = startPoint;linePoints[lineCount][1] = endPoint;lineCount++;Invalidate(false);COpenGLView::OnLButtonUp(nFlags, point);
}
二、正式画图
1. 调用Display()函数
我们所有的画图操作都会丢在这里面进行
void CMy22uCGv1View::Display()
{// 清空屏幕glClearColor(0.2f, 0.2f, 0.2f,1.0f);glClear(GL_COLOR_BUFFER_BIT);// 画网格DrawGrids();// 画线DrawLines();
}
2. 画网格
总之先把背景的网格画出来
void CMy22uCGv1View::DrawGrids()
{// 获取视口的宽高CRect rc;GetClientRect(rc);float width = rc.right - rc.left;float height = rc.bottom - rc.top;// 计算画线数量float wNum = (int)(width / height * 10) / 10.f;// 调好颜色开画// OpenGL坐标系见上一篇文章glColor3f(0.5f, 0.5f, 0.5f);glBegin(GL_LINES);for (float i = -wNum; i < wNum + 0.0001f; i += GRIDGAP)// 因为是float类型所以需要加上一个小值作误差// GRIDGAP是之前定义的浮点型常量{// 竖线glVertex2f(i, -1);glVertex2f(i, 1);}for (float i = -1; i < 1.0001f; i += GRIDGAP){// 横线glVertex2f(-wNum, i);glVertex2f(wNum, i);}glEnd();
}
画出来的效果就像这样:
3. 画线
用OpenGL的glvertex方法画线。注意要把当前正在画的线(isDrawing为true)和已经画好的线都画出来
void CMy22uCGv1View::DrawLines()
{// 拖拽线glColor3f(0.95f, 0.95f, 0.95f);if (isDrawing){glBegin(GL_LINES);// 坐标转换glVertex2f(ChangePos(startPoint.x, 1), ChangePos(startPoint.y, 2));glVertex2f(ChangePos(endPoint.x, 1), ChangePos(endPoint.y, 2));// 不转换就看不见// glVertex2f(startPoint.x, startPoint.y);// glVertex2f(endPoint.x, endPoint.y);glEnd();}// 已画线for (int i = 0; i < lineCount; i++){float x0 = ChangePos(linePoints[i][0].x, 1);float y0 = ChangePos(linePoints[i][0].y, 2);float x1 = ChangePos(linePoints[i][1].x, 1);float y1 = ChangePos(linePoints[i][1].y, 2);// 把线画上glBegin(GL_LINES);glVertex2f(x0, y0);glVertex2f(x1, y1);glEnd();// DDA算法画圆DrawLine_DDA(x0, y0, x1, y1);}
}
这里大的来了,它就是——坐标变换!
因为之前记录的startPoint和endPoint点都是屏幕坐标,随随便便就几十上百了,直接画会超出视口显示范围,导致啥也看不见,所以得先把它们转成视口坐标。这里是写了一个ChangePos()函数
float CMy22uCGv1View::ChangePos(float num, int mode)
// 用mode判断这是x坐标还是y坐标
{float n = num;CRect rc;GetClientRect(rc);float width = rc.right - rc.left;float height = rc.top - rc.bottom;if (mode == 1) // n = n / width * 4.5376 - 2.2688;// n = -(n / width * width / height * 2 - width / height);n = (width - 2 * n) / height;else n = n / height * 2 + 1;return n;
}
现在就能看到我们辛辛苦苦画的线了
4. DDA算法
最后是用DDA算法沿线画圆。关于算法本身的介绍可以看上篇文章里贴的链接
void CMy22uCGv1View::DrawLine_DDA(float x0, float y0, float x1, float y1)
{float dx = x1 - x0;float dy = y1 - y0;float k = dy / dx;bool flag = false;if (k > 1 || k < -1) {flag = true;k = dx / dy;Swap(x0, y0, x1, y1);}// 起点调整,把圆心放在网格交点上float adjust = x0 >= 0 ? 1 : -1;float x = int(x0 / GRIDGAP + adjust * GRIDGAP * 5) * GRIDGAP;adjust = y0 >= 0 ? 1 : -1;float y = int(y0 / GRIDGAP + adjust * GRIDGAP * 5) * GRIDGAP;if (!flag)DrawCircle(x, y);elseDrawCircle(y, x);if (x0 <= x1) // 往右 {while (x < x1) {x += GRIDGAP;if (x > x1) {// 终点调整if (x1 < (x - 0.5f * GRIDGAP)) break;}y0 += k * GRIDGAP;if (k >= 0) {if (y0 >= y + GRIDGAP * 0.5f) {y += GRIDGAP;}}else {if (y0 <= y - GRIDGAP * 0.5f) {y -= GRIDGAP;}}if (!flag)DrawCircle(x, y);elseDrawCircle(y, x);}}else // 往左 {while (x > x1) {x -= GRIDGAP;if (x < x1) {// 终点调整if (x1 > (x + 0.5f * GRIDGAP)) break;}y0 -= k * GRIDGAP;if (k <= 0) {if (y0 >= y + GRIDGAP * 0.5f) {y += GRIDGAP;}}else {if (y0 <= y - GRIDGAP * 0.5f) {y -= GRIDGAP;}}if (!flag)DrawCircle(x, y);elseDrawCircle(y, x);}}
}
画圆的函数DrawCircle()长这样:
void CMy22uCGv1View::DrawCircle(float x, float y)
{glBegin(GL_TRIANGLE_FAN);for (int i = 0; i <= 360; i += 30) {float p = (float)(i * 3.14 / 180);glVertex2f(x + (float)sin(p) * GRIDGAP * 0.5f, y + (float)cos(p) * GRIDGAP * 0.5f);}glEnd();
}
交换坐标的函数Swap()长这样:
void CMy22uCGv1View::Swap(float &x0, float &x1, float &y0, float &y1)
{float tmp = x0;x0 = x1;x1 = tmp;tmp = y0;y0 = y1;y1 = tmp;
}
这下终于可以画出本文开头一样的效果了
总结
这就是我在MFC框架中用OpenGL画线的全过程了,希望对你有帮助~