如何画直线,看起来似乎很简单,拿个直线方程,遍历X求出Y,再把对应点画出来不就行了么,嘿嘿,没那么简单~ 平时我们所说的直线是在实数域的,也就是说对应的Y可能是小数,而在屏幕上,所画直线是基于正整数域的,那么根据直线方程如何画直线呢? 先考虑斜率 0 < k < 1的情况 1.Bresenham算法 该算法由Bresenham在1965年发明,它到底做了什么事呢?其实想法很简单,就是每X移动一个像素,则考虑Y应该是如何移动。 由于由(x0,y0),(x1,y1)两点构成的直线方程为: y-y0 = (y1-y0)/(x1-x0) * (x-x0) 对于每一点的x值其y为: (y1-y0)/(x1-x0) * (x-x0) + y0 可见并非每一点x对应的y都为整数,所以没有必要去计算每一点x对应的y值,只需求出哪一点的x值导致y值+1,如果x尚未到此值,则这中间的x对应的y都不变。 如何找到这个值则要依靠直线方程斜率k,x每增加1个单位,y值都增加k个单位,而每一个x的像素点都对应一个误差,为直线中根据x求得的y与x的像素点y的差,如果误差绝对值>0.5则表示其靠近下一个像素点的y,此时把y+1,误差值-1。 2.伪代码 drawline(x0,y0,x1,y1) { int dx = x1 - x0; int dy = y1 - y0; float k = (float)dy / dx; float error = 0; for(int x = x0,y = y0:x <= x1; ++x) { draw(x,y); error += k; if(fabs(error) >= 0.5) { y += 1; error -= 1; } } } 3.在linux终端窗口中画直线 主要思想: 构造一个二维字符数组,以空格为其基本元素,以此在屏幕形成画布 若要画点则将对应点赋值'*',画完后刷新画布,可见图形 4.具体实现 CCanvas类接口: CCanvas(int width = 40,int height = 40); //构造函数,形成一定大小的画布 Init(); //完成画布初始化工作 void SetValue(int x,int y,char c); //在(x,y)处"画"一个点 void Refresh(); //刷新显示画布
//CCanvas.h #include <vector> class CCanvas { public: CCanvas(int width = 40,int height = 40); ~CCanvas(); void SetValue(int x,int y,char c); typedef std::vector<std::vector<char> >::iterator ITER; private: int m_width,m_height; std::vector<std::vector<char> > m_info; void Init(); };
//CCanvas.cpp #include <iostream> #include "CCanvas.h" CCanvas::CCanvas(int width,int height) { m_width = width; m_height = height; Init(); } CCanvas::~CCanvas() { } void CCanvas::Init() { m_info.resize(m_height); for(ITER it = m_info.begin();it != m_info.end(); ++it) { it->resize(m_width,' '); } void CCanvas::SetValue(int x,int y,char c) { if(x >= m_width || y >= m_height) { printf("bad args!"); return; } m_info[y][x] = c; } void CCanvas::Refresh() { for(int i = 0;i != m_height; ++i) { for(int j = 0;j != m_width; ++j) { printf("%c",m_info[i][j]); } printf("/n"); } }
//主文件 #include <iostream> #include <cmath> #include "CCanvas.h"
CCanvas g_canvas; void DrawLine(int x0,int y0,int x1,int y1) { int dx = x1 - x0; int dy = y1 - y0; float k = (float)dy / dx; float error = 0; for(int x = x0,y = y0:x <= x1; ++x) { draw(x,y); error += k; if(fabs(error) >= 0.5) { y += 1; error -= 1; } } }
int main() { std::cout << "please enter the startpoint (x0,y0) and endpoint (x1,y1)..." << std::endl; int x0,y0,x1,y1; std::cin >> x0 >> y0 >> x1 >> y1; DrawLine(x0,y0,x1,y1); g_canvas.Refresh();
return 0; }
为了便于调试写一个Makefile文件 Makefile objects = CCanvas.o DrawLine.o DrawLine : $(objects) g++ -g $(objects) -o DrawLine DrawLine.o : DrawLine.cpp CCanvas.h g++ -g -c CCanvas.h DrawLine.cpp CCanvas.o : CCanvas.h g++ -g -c CCanvas.h CCanvas.cpp .PHONY : clean clean : -rm DrawLine $(objects) //编译 运行 make ./DrawLine |