如何畫直線,看起來似乎很簡單,拿個直線方程,遍歷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 |