Bresenham画线算法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Bresenham画线算法相关的知识,希望对你有一定的参考价值。

求一个能运行的Bresenham画线算法,如果能用鼠标画就更好了,最好是用C++写的。采纳后会再送上100分!
要是有源文件就好了,我对这个不是很清楚呀!

#include <windows.h>

void Bresenham(HDC hdc,int x1,int y1,int x2,int y2);

/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/* Make the class name into a global variable */
char szClassName[ ] = "WinClassName";

int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nCmdShow)

HWND hwnd; /* This is the handle for our window */
MSG msg; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */

/* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX);

/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_CROSS);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
/* Use Windows's default colour as the background of the window */
wincl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);

/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
return 0;

/* The class is registered, let's create the program*/
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
"Bresenham", /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
544, /* The programs width */
375, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);

/* Make the window visible on the screen */
ShowWindow (hwnd, nCmdShow);

/* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage (&msg, NULL, 0, 0))

/* Translate virtual-key messages into character messages */
TranslateMessage(&msg);
/* Send message to WindowProcedure */
DispatchMessage(&msg);


/* The program return-value is 0 - The value that PostQuitMessage() gave */
return msg.wParam;


/* This function is called by the Windows function DispatchMessage() */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

HDC hdc;
PAINTSTRUCT ps;
static int x1,x2,y1,y2;
switch (message) /* handle the messages */

case WM_LBUTTONDOWN:
x1=LOWORD(lParam);
y1=HIWORD(lParam);
break;
case WM_LBUTTONUP:
x2=LOWORD(lParam);
y2=HIWORD(lParam);
hdc=GetDC(hwnd);
Bresenham(hdc,x1,y1,x2,y2);
ReleaseDC(hwnd,hdc);
break;
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);

EndPaint(hwnd,&ps);
break;
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);


return 0;


void Bresenham(HDC hdc,int x1,int y1,int x2,int y2)

int t,x,y,dx,dy,error;
bool flag = abs(y2-y1)>abs(x2-x1);
if( flag )

t=x1;x1=y1;y1=t;
t=x2;x2=y2;y2=t;

if( x1>x2 )

t=x1;x1=x2;x2=t;
t=y1;y1=y2;y2=t;

dx=x2-x1;
dy=abs(y2-y1);
error=dx/2;
for(x=x1,y=y1;x<=x2;++x)

if(flag) SetPixel(hdc,y,x,0);
else SetPixel(hdc,x,y,0);
error-=dy;
if(error<0)

y1<y2?++y:--y;
error+=dx;


参考资料:win32 application

参考技术A 基本上Bresenham画线算法的思路如下:
//
假设该线段位于第一象限内且斜率大于0小于1,设起点为(x1,y1),终点为(x2,y2).
//
根据对称性,可推导至全象限内的线段.
1.画起点(x1,y1).
2.准备画下个点。x坐标增1,判断如果达到终点,则完成。否则,由图中可知,下个要画的点要么为当前点的右邻接点,要么是当前点的右上邻接点.
2.1.如果线段ax+by+c=0与x=x1+1的交点的y坐标大于M点的y坐标的话,下个点为U(x1+1,y1+1)
2.2.否则,下个点为B(x1+1,y1+1)
3.画点(U或者B).
4.跳回第2步.
5.结束.
这里需要细化的是怎么判断下个要画的点为当前点的右邻接点还是当前点的右上邻接点.
设线段方程:ax+by+c=0(x1<x<x2,y1<y<y2)
令dx=x2-x1,dy=y2-y1
则:斜率-a/b
=
dy/dx.
从第一个点开始,我们有F(x,1,y1)
=
a*x1+b*y1+c=0
下面求线段ax+by+c=0与x=x1+1的交点:
由a*(x1+1)+b*y+c
=
0,
求出交点坐标y=(-c-a(x1+1))/b
所以交点与M的y坐标差值Sub1
=
(-c-a(x1+1))/b
-
(y1+0.5)
=
-a/b-0.5,即Sub1的处始值为-a/b-0.5。
则可得条件当
Sub1
=
-a/b-0.5>0时候,即下个点为U.
反之,下个点为B.
代入a/b,则Sub1
=
dy/dx-0.5.
因为是个循环中都要判断Sub,所以得求出循环下的Sub表达式,我们可以求出Sub的差值的表达式.下面求x=x1+2时的Sub,即Sub2
1.如果下下个点是下个点的右上邻接点,则
Sub2
=
(-c-a(x1+2))/b
-
(y1+1.5)
=
-2a/b
-
1.5
故Sub差值Dsub
=
Sub2
-
Sub1
=
-2a/b
-
1.5
-
(-a/b-0.5)
=
-a/b
-
1.代入a/b得Dsub
=
dy/dx
-1;
2.如果下下个点是下个点的右邻接点,
Sub2
=
(-c-a(x1+2))/b
-
(y1+0.5)
=
-2a/b
-
0.5
故Sub差值Dsub
=
Sub2
-
Sub1
=
-2a/b
-
0.5
-
(-a/b-0.5)
=
-a/b.
代入a/b得Dsub
=
dy/dx;
于是,我们有了Sub的处始值Sub1
=
-a/b-0.5
=
dy/dx-0.5,又有了Sub的差值的表达式Dsub
=
dy/dx
-1
(当Sub1
>
0)或
dy/dx(当Sub1
<
0).细化工作完成。
于是pcode可以细化如下:
//
Pcode
for
Bresenham
Line
//
By
SoRoMan
x=x1;
y=y1;
dx
=
x2-x1;
dy
=
y2-y1;
Sub
=
dy/dx-0.5;
//
赋初值,下个要画的点与中点的差值
DrawPixel(x,
y);
//
画起点
while(x<x2)

x++;
if(Sub
>
0)
//
下个要画的点为当前点的右上邻接点

Sub
+=
dy/dx
-
1;
//下下个要画的点与中点的差值
y++;
//
右上邻接点y需增1

else//
下个要画的点为当前点的右邻接点

Sub
+=
dy/dx;

//
画下个点
DrawPixel(x,y);

PS:一般优化:
为避免小数转整数以及除法运算,由于Sub只是用来进行正负判断,所以可以令Sub
=
2*dx*Sub
=
2dy-dx,则
相应的DSub
=
2dy
-
2dx或2dy.
思考1:如果Sub
=
0时,会产生取两个点都可以的问题。这个问题还没深入。
参考技术B 基本上Bresenham画线算法的思路如下:
//
假设该线段位于第一象限内且斜率大于0小于1,设起点为(x1,y1),终点为(x2,y2).
//
根据对称性,可推导至全象限内的线段.
1.画起点(x1,y1).
2.准备画下个点。x坐标增1,判断如果达到终点,则完成。否则,由图中可知,下个要画的点要么为当前点的右邻接点,要么是当前点的右上邻接点.
2.1.如果线段ax+by+c=0与x=x1+1的交点的y坐标大于M点的y坐标的话,下个点为U(x1+1,y1+1)
2.2.否则,下个点为B(x1+1,y1+1)
3.画点(U或者B).
4.跳回第2步.
5.结束.
这里需要细化的是怎么判断下个要画的点为当前点的右邻接点还是当前点的右上邻接点.
设线段方程:ax+by+c=0(x1<x<x2,y1<y<y2)
令dx=x2-x1,dy=y2-y1
则:斜率-a/b
=
dy/dx.
从第一个点开始,我们有F(x,1,y1)
=
a*x1+b*y1+c=0
下面求线段ax+by+c=0与x=x1+1的交点:
由a*(x1+1)+b*y+c
=
0,
求出交点坐标y=(-c-a(x1+1))/b
所以交点与M的y坐标差值Sub1
=
(-c-a(x1+1))/b
-
(y1+0.5)
=
-a/b-0.5,即Sub1的处始值为-a/b-0.5。
则可得条件当
Sub1
=
-a/b-0.5>0时候,即下个点为U.
反之,下个点为B.
代入a/b,则Sub1
=
dy/dx-0.5.
因为是个循环中都要判断Sub,所以得求出循环下的Sub表达式,我们可以求出Sub的差值的表达式.下面求x=x1+2时的Sub,即Sub2
1.如果下下个点是下个点的右上邻接点,则
Sub2
=
(-c-a(x1+2))/b
-
(y1+1.5)
=
-2a/b
-
1.5
故Sub差值Dsub
=
Sub2
-
Sub1
=
-2a/b
-
1.5
-
(-a/b-0.5)
=
-a/b
-
1.代入a/b得Dsub
=
dy/dx
-1;
2.如果下下个点是下个点的右邻接点,
Sub2
=
(-c-a(x1+2))/b
-
(y1+0.5)
=
-2a/b
-
0.5
故Sub差值Dsub
=
Sub2
-
Sub1
=
-2a/b
-
0.5
-
(-a/b-0.5)
=
-a/b.
代入a/b得Dsub
=
dy/dx;
于是,我们有了Sub的处始值Sub1
=
-a/b-0.5
=
dy/dx-0.5,又有了Sub的差值的表达式Dsub
=
dy/dx
-1
(当Sub1
>
0)或
dy/dx(当Sub1
<
0).细化工作完成。
于是pcode可以细化如下:
//
Pcode
for
Bresenham
Line
//
By
SoRoMan
x=x1;
y=y1;
dx
=
x2-x1;
dy
=
y2-y1;
Sub
=
dy/dx-0.5;
//
赋初值,下个要画的点与中点的差值
DrawPixel(x,
y);
//
画起点
while(x<x2)

x++;
if(Sub
>
0)
//
下个要画的点为当前点的右上邻接点

Sub
+=
dy/dx
-
1;
//下下个要画的点与中点的差值
y++;
//
右上邻接点y需增1

else//
下个要画的点为当前点的右邻接点

Sub
+=
dy/dx;

//
画下个点
DrawPixel(x,y);

PS:一般优化:
为避免小数转整数以及除法运算,由于Sub只是用来进行正负判断,所以可以令Sub
=
2*dx*Sub
=
2dy-dx,则
相应的DSub
=
2dy
-
2dx或2dy.
思考1:如果Sub
=
0时,会产生取两个点都可以的问题。这个问题还没深入。
参考技术C void Bresenhamline(int x1,int y1,int x2,int y2,int c,CDC* pDC)//画直线

int x,y,dx,dy,p,const1,const2,inc,tmp;
dx=x2-x1;
dy=y2-y1;
if(dx*dy>=0)//准备x或y的单位递变值
inc=1;
else
inc=-1;
if(abs(dx)>=abs(dy))

if(dx<0)

tmp=x1;//将2a、3a象限方向的直线变换到1a、4a方向
x1=x2;
x2=tmp;
tmp=y1;
y1=y2;
y2=tmp;
dx=-dx;
dy=-dy;

if(dy<0)
dy=-dy;
p=2*dy-dx;
const1=2*dy;
const2=2*(dy-dx);
x=x1;
y=y1;
pDC->SetPixel(x,y,c);
while(x<x2)

x++;
if(p<0)
p+=const1;
else

y+=inc;
p+=const2;

pDC->SetPixel(x,y,c);


else

if(dy<0)

tmp=x1;//将3b、4b象限方向的直线变换到2b、1b方向
x1=x2;
x2=tmp;
tmp=y1;
y1=y2;
y2=tmp;
dx=-dx;
dy=-dy;

if(dx<0)
dx=-dx;
p=2*dx-dy;
const1=2*dx;
const2=2*(dx-dy);
x=x1;
y=y1;
pDC->SetPixel(x,y,c);
while(y<y2)

y++;
if(p<0)
p+=const1;
else

x+=inc;
p+=const2;

pDC->SetPixel(x,y,c);



在View.h中找到下面内容并添加num和spt的定义
class CMyView : public CView

protected: // create from serialization only
CMy072414View();
DECLARE_DYNCREATE(CMy072414View)
int num;//添加
CPoint spt[2];//添加
// Attributes
然后建立类向导,建立函数OnLButtonDown:
num=0;
void CMyView::OnLButtonDown(UINT nFlags, CPoint point)

CDC* pDC=GetDC();
CPen newpen(PS_SOLID,1,RGB(255,0,255));
CPen *old=pDC->SelectObject(&newpen);
int c=RGB(255,0,255);
spt[num]=point;
if(num)
Bresenhamline(spt[num-1].x,spt[num-1].y,spt[num].x,spt[num].y,c,pDC);
else
num++;
CView::OnLButtonDown(nFlags, point);

运行时只要单击两个点就会显示直线了~

计算机图形学中的中点画线,中点画圆,Bresenham画线与画圆算法

#include<iostream>
#include<graphics.h>  // 这样引用 EasyX 图形库
#include<conio.h>
#include<time.h>
#include<math.h>
#include<stdlib.h>
using namespace std;
 
//Bresenham画线
void Bresenham_line(int x0,int y0,int x1,int y1)
{
 int x,y,dx,dy;
 float k,e;
 dx=x1-x0;
 dy=y1-y0;
 k=dy/dx;//这里是完成k值的初始化
 e=-0.5;
 x=x0;
 y=y0;
 //这里的i每次都是加一个格字,直到终点,y这是选择性的加一或者不加
 for(int i=0;i<=dx;i++)
 {
  //画点的函数
 putpixel(x,y,255);
 x=x+1;
 e=e+k;
 if(e>=0)
 {
 y=y+1;
 e=e-1;//如果e大于零了就要马上减一
 }
 }
}

//中点画线
void MidpointLine(int x0,int y0,int x1,int y1)
{
 int a,b,delta1,delta2,d,x,y;
 a=y0-y1;
 b=x1-x0;
 d=2*a+b;
 //这里的2是为了避免小数的计算
 delta1=2*a;
 delta2=2*(a+b);
 x=x0;
 y=y0;
 putpixel(x,y,255);
 while(x<x1){
  if(d<0){//这样的话就会选取上面一个点
  x++;y++;d+=delta2;
  }else{//否者就选取下面的一个点
  x++;d+=delta1;
  }
  putpixel(x,y,255);
 }
}

//中点画线

void MidpointLine2(int x1,int y1,int x2,int y2,COLORREF color)  //中点画线法
{
 int x = x1, y = y1; //初始化x,y
 int a = y1 - y2, b = x2 - x1;      //a,b分别为x和y的增量
 //考虑四种情况,分别计算增量
 int deltx = (b >= 0 ? 1 : (b = -b, -1)); 
 int delty = (a <= 0 ? 1 : (a = -a, -1));

 for(int i=0;i<=400;i++)
 {putpixel(i, 200, color);putpixel(200, i, color);}
 putpixel(x+200, y+200, color);

 int d, delt1, delt2;
 if (-a <= b)  // 斜率绝对值 <= 1
 {
  d = 2 * a + b;
  delt1 = 2 * a;
  delt2 = 2 * (a + b);
  while(x != x2)
  {
   if (d < 0)
    y += delty, d += delt2;
   else
    d += delt1;
   x += deltx;
   putpixel(x+200, y+200, color);
  }
 }
 else    // 斜率绝对值 > 1
 {
  d = 2 * b + a;
  delt1 = 2 * b;
  delt2 = 2 * (a + b);
  while(y != y2)
  {
   if(d < 0)
    d += delt1;
   else
    x += deltx, d += delt2;
   y += delty;
   putpixel(x+200, y+200, color);
  }
 }
}

//中点画圆
void MidpointCircle(int x0,int y0,int r,int color)

{
 int x=0,y=r;
 float d=5.0/4-r;
 while(x<=y){
 putpixel(x0+x,y0+y,color);
 putpixel(x0+x,y0-y,color);
 putpixel(x0-x,y0+y,color);
 putpixel(x0-x,y0-y,color);
 putpixel(x0+y,y0+x,color);
 putpixel(x0+y,y0-x,color);
 putpixel(x0-y,y0+x,color);
 putpixel(x0-y,y0-x,color);
 if(d<0)
 d+=x*2.0+3;
 else{
 d+=2.0*(x-y)+5;y--;
 }
 x++;
 }
}

//Bresenhem画圆

void BresenhemCircle(int centerx, int centery, int radius, int color)
{
 int x =0;
 int y = radius;
 int delta = 2*(1-radius);
 int direction;
 while (y >= 0) {
   putpixel(centerx+x, centery+y, color);
   putpixel(centerx-x, centery+y, color);
   putpixel(centerx-x, centery-y, color);
   putpixel(centerx+x, centery-y, color);
  if (delta < 0) {
   if ((2*(delta+y)-1) < 0) {
    direction = 1;
   }
   else {
    direction = 2;
   }
  }
  else if(delta > 0) {
   if ((2*(delta-x)-1) <= 0) {
    direction = 2;
   }
   else {
    direction = 3;
   }
  }
  else {
   direction=2;
  }
  switch(direction) {
  case 1:
   x++;
   delta += (2*x+1);
   break;
  case 2:
   x++;
   y--;
   delta += 2*(x-y+1);
   break;
  case 3:
   y--;
   delta += (-2*y+1);
   break;
  }
 }
}

 


void main()
{
 int x0,y0,x1,y1;

 initgraph(640, 480); 
 Bresenham_line(10,10,56,56);
 MidpointLine2(0,100,369,0,255);
  MidpointCircle(100,100,50,255);
 BresenhemCircle(100,100,100,255);

 

 getch();    // 按任意键继续
 closegraph();   // 关闭图形界面
}

 

多的也不说了,源码直接可以使用,而且还是带有坐标轴的

 

技术分享

 

以上是关于Bresenham画线算法的主要内容,如果未能解决你的问题,请参考以下文章

Bresenham画线算法详解及其OpenGL编程实现

图形学--(中点画线法+Bresenham画线算法)

计算机图形学中的中点画线,中点画圆,Bresenham画线与画圆算法

计算机图形学DDA画线法+中点画线法+Bresenham画线法

C++语言通过Bresenham算法画直线怎么写,求代码高人,谢谢了!

用C实现Bresenham算法生成直线和圆的程序(要求具体步骤有必要解述)