如何在窗口中畫直線

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章