❶ dda法生成直線的基本原理是什麼為什麼說Bersenham畫圓的演算法效率較高
DDA演算法主要是根據直線公式y = kx + b來推導出來的,其關鍵之處在於如何設定單位步進,即一個方向的步進為單位步進,另一個方向的步進必然是小於1。演算法的具體思路如下:
1. 輸入直線的起點、終點;
2. 計算x方向的間距:△X和y方向的間距:△Y。
3. 確定單位步進,取MaxSteps = max(△X,△Y); 若△X>=△Y,則X方向的步進為單位步進,X方向步進一個單位,Y方向步進△Y/MaxSteps;否則相反。
4. 設置第一個點的像素值
5. 令循環初始值為1,循環次數為MaxSteps,定義變數x,y,執行以下計算:
a. x增加一個單位步進,y增加一個單位步進
b. 設置位置為(x,y)的像素值
Bresenham演算法是DDA演算法畫線演算法的一種改進演算法。本質上它也是採取了步進的思想。不過它比DDA演算法作了優化,避免了步進時浮點數運算,同時為選取符合直線方程的點提供了一個好思路。首先通過直線的斜率確定了在x方向進行單位步進還是y方向進行單位步進:當斜率k的絕對值|k|<1時,在x方向進行單位步進;當斜率k的絕對值|k|>1時,在y方向進行單位步進。
1. 輸入線段的起點和終點。
2. 判斷線段的斜率是否存在(即起點和終點的x坐標是否相同),若相同,即斜率不存在,
只需計算y方向的單位步進(△Y+1次),x方向的坐標保持不變即可繪制直線。
3. 計算線段的斜率k,分為下面幾種情況處理
a. k等於0,即線段平行於x軸,即程序只需計算x方向的單位步進,y方向的值不變
b. |k|等於1,即線段的x方向的單位步進和y方向的單位步進一樣,皆為1。直接循環△X次計算x和y坐標。
4. 根據輸入的起點和終點的x、y坐標值的大小決定x方向和y方向的單位步進是1還是-1
6. 畫出第一個點。
7. 若|k| <1,設m =0,計算P0,如果Pm>0,下一個要繪制的點為(Xm+單位步進,Ym),
Pm+1 = Pm -2*△Y;
否則要繪制的點為(Xm+單位步進,Ym+單位步進)
Pm+1 = Pm+2*△X-2*△Y;
8. 重復執行第七步△X-1次;
9. 若|k| <1,設m =0,計算Q0,如果Qm>0,下一個要繪制的點為(Xm,Ym+單位步進),
Pm+1 = Pm -2*△X;
否則要繪制的點為(Xm+單位步進,Ym+單位步進)
Pm+1 = Pm+2*△Y-2*△X;
10. 重復執行第9步△Y-1次;
❷ 計算機圖形學直線生成演算法
我連畫圓的一塊給你吧
需要橡皮筋
橢圓
樹什麼的可以和我說
我是用
c#寫的
using
System;
using
System.Collections.Generic;
using
System.ComponentModel;
using
System.Data;
using
System.Drawing;
using
System.Text;
using
System.Windows.Forms;
namespace
line
{
public
partial
class
Form1
:
Form
{
Graphics
g;
public
Form1()
{
InitializeComponent();
g
=
pictureBox1.CreateGraphics();
}
private
void
button1_Click_1(object
sender,
EventArgs
e)
{
//
g.TranslateTransform(-300,-300);
g.Clear(BackColor);
double
x1,
x2,
y1,
y2;
double
x,
y;
x1
=
System.Convert.ToSingle(textBox1.Text);
y1
=
System.Convert.ToSingle(textBox2.Text);
x2
=
System.Convert.ToSingle(textBox3.Text);
y2
=
System.Convert.ToSingle(textBox4.Text);
x
=
x1;
double
k
=
(y1
-
y2)
/
(x1
-
x2);
Color
color1
=
Color.FromArgb(255,
0,
0);
//定義顏色
Brush
bru1
=
new
SolidBrush(color1);//定義筆
Color
color2
=
Color.FromArgb(0,
255,
0);
//定義顏色
Brush
bru2
=
new
SolidBrush(color2);//定義筆
Pen
pen1
=
new
Pen(bru1,
1);
Pen
pen2
=
new
Pen(bru2,
1);
while
(x
<
x2)
{
//
textBox3.Text
=
System.Convert.ToString(k);
y
=
(double)k
*
(x
-
x1)
+
y1;
double
a;
a=y%1;
float
xx
=
Convert.ToSingle(x);
float
yy
=
Convert.ToSingle(y);
if
(a
>
0.5)
{
g.DrawLine(pen1,
xx,
yy,
xx,yy+1);
}
else
{
g.DrawLine(pen2,
xx,
yy,
xx,
yy+1);
}
x++;
}
//g.DrawLine(p,10,10,100,100);
}
private
void
button2_Click(object
sender,
EventArgs
e)
{
g.TranslateTransform(300,
300);
g.Clear(BackColor);
float
r;//r為圓的圓心
r
=System.Convert.ToSingle(textBox6.Text);
Color
color1
=
Color.FromArgb(255,
0,
0);
//定義顏色
Brush
bru1
=
new
SolidBrush(color1);//定義筆
Color
color2
=
Color.FromArgb(0,
255,
0);
//定義顏色
Brush
bru2
=
new
SolidBrush(color2);//定義筆
Pen
pen1
=
new
Pen(bru1,1);
Pen
pen2
=
new
Pen(bru2,
1);
float
x
=
0;
float
y
=
r;
while
(x
<r+3)
{
y
=
(float)System.Math.Sqrt(r
*
r
-
x
*
x);
float
a;
a
=
y
%
1;
y
=
y
-
a;
if
(a
>
0.5)
{
g.DrawLine(pen1,
x,
y,
x,
y+1);
g.DrawLine(pen1,
-x,
y,
-x,
y
+
1);
g.DrawLine(pen1,
x,
-y,
x,
-y
+
1);
g.DrawLine(pen1,
-x,
-y,
-x,
-y
+
1);
}
else
{
g.DrawLine(pen2,
x,
y,
x,
y+1);
g.DrawLine(pen2,
-x,
y,
-x,
y
+
1);
g.DrawLine(pen2,
x,-
y,
x,-
y
+
1);
g.DrawLine(pen2,
-x,
-y,-
x,
-y
+
1);
}
x++;
}
g.TranslateTransform(-300,
-300);
}
private
void
button3_Click(object
sender,
EventArgs
e)
{
}
private
void
comboBox1_SelectedIndexChanged(object
sender,
EventArgs
e)
{
switch
(comboBox1.Text)
{
case
"畫圓":
{
textBox1.Visible
=
false;
textBox2.Visible
=
false;
textBox3.Visible
=
false;
textBox4.Visible
=
false;
textBox5.Visible
=
true;
textBox6.Visible
=
true;
textBox7.Visible
=
false;
textBox8.Visible
=
false;
label1.Visible
=
false;
label2.Visible
=
false;
label3.Visible
=
false;
label4.Visible
=
false;
label5.Visible
=
true;
label6.Visible
=
true;
label7.Visible
=
false;
label8.Visible
=
false;
label9.Visible
=
true;
button1.Visible
=
false;
button2.Visible
=
true;
button3.Visible
=
false;
break;
}
case"畫線":
{
textBox1.Visible
=
true;
textBox2.Visible
=
true;
textBox3.Visible
=
true;
textBox4.Visible
=
true;
textBox5.Visible
=
false;
textBox6.Visible
=
false;
textBox7.Visible
=
false;
textBox8.Visible
=
false;
label1.Visible
=
true;
label2.Visible
=
true;
label3.Visible
=
true;
label4.Visible
=
true;
label5.Visible
=
false;
label6.Visible
=
false;
label7.Visible
=
false;
label8.Visible
=
false;
label9.Visible
=
false;
button1.Visible
=
true;
button2.Visible
=
false;
button3.Visible
=
false;
break;
}
case"畫橢圓":
{
textBox1.Visible
=
false;
textBox2.Visible
=
false;
textBox3.Visible
=
false;
textBox4.Visible
=
false;
textBox5.Visible
=
false;
textBox6.Visible
=
false;
textBox7.Visible
=
true;
textBox8.Visible
=
true;
label1.Visible
=
false;
label2.Visible
=
false;
label3.Visible
=
false;
label4.Visible
=
false;
label5.Visible
=
false;
label6.Visible
=
false;
label7.Visible
=
true;
label8.Visible
=
true;
label9.Visible
=
false;
button1.Visible
=
false;
button2.Visible
=
false;
button3.Visible
=
true;
break;
}
}
}
}
}
❸ 關於Bresenham演算法的求助
今天一下子遇到三個類似的問題,所以我這篇東西就連續復制粘貼了三遍:
(下面的坐標本來是有下標的,但復制過來就變沒了,你可能看的有點暈)
Bresenham演算法是Bresenham提出的一種光柵線生成演算法!
DDA演算法表面上看起來很有效,並且代碼也比較容易實現,但是顯示每個像素都需要進行一次浮點數加法運算,而Bresenham演算法的最大優點是不需要進行浮點數運算!這是一種精確而有效的光柵線生成演算法,該演算法僅使用增量整數計算,計算速度比DDA要快,另外,Bresenham演算法還可用於顯示圓和其他曲線,這里暫時只顯示直線!
與DDA一樣,我們假設線段的兩個端點坐標是整數值(x0,y0)(xEnd,yEnd),且斜率m滿足0<=m>=1!坐標軸的垂直軸表示掃描線位置,水平軸標識像素列,假設以單位x間隔取樣,需要確定下一個每次取樣時兩個可能的像素位置中的哪一個更接近於線路徑!
從給定線段的左端點(x0,y0)開始,逐步處理每個後繼列(x位置),並在其掃描線y值最接近線段的像素處描出一點,假如已經確定要顯示的像素在(xk,yk),那麼下一步就要確定在列xk+1=xk+1上繪制哪個像素,是在位置(xk+1,yk)還是在(xk+1,yk+1)
在取樣位置xk+1,我們使用dlower和pper來標識兩個像素與數學上線路徑的垂直偏移(就是通過這兩個值來比較哪個點離線上的點最近,以下推導過程你可能看得有點暈,但都是為了推出後續的點而已,你可以結合下面例子程序中的Bresenham函數來看),在像素列xk+1處的直線上的y坐標根據直線方程可計算得:
y=m(xk+1)+b
那麼可求得:
dlower=y-yk=m(xk+1)+b-yk
pper=(yk+1)-y=yk+1-m(xk+1)-b
令斜率m=dy/dx,引入決策參數Pk,定義為:
Pk=dx(dlower-pper)
=2dx*xk-2dy*yk+c
C是一個常數,值為2dx+dx(2b-1)
由此可以計算得到
pk+1=Pk+2dy-2dx(yk+1-yk)
其中yk+1-yk取0還是取1取決於參數Pk的符號,Pk為負時取0,Pk非負時取1!
而Pk為負時,下一個要繪制的點就是(xk+1,yk)且pk+1=Pk+2dy
Pk為非負時則下一個要繪制的點就是(xk+1,yk+1)且pk+1=Pk+2dy-2dx
至此,Bresenham演算法介紹完畢,以下為某個示例:
#include<gl/glut.h>
#include<math.h>
#include<stdio.h>
voiddraw_pixel(intix,intiy)
{
glBegin(GL_POINTS);
glVertex2i(ix,iy);
glEnd();
}
voidBresenham(intx1,inty1,intxEnd,intyEnd)
{
intdx=abs(xEnd-x1),dy=abs(yEnd-y1);
intp=2*dy-dx;
inttwoDy=2*dy,twoDyMinusDx=2*dy-2*dx;
intx,y;
if(x1>xEnd)
{
x=xEnd;y=yEnd;
xEnd=x1;
}
else
{
x=x1;
y=y1;
}
draw_pixel(x,y);
while(x<xEnd)
{
x++;
if(p<0)
p+=twoDy;
else
{
y++;
p+=twoDyMinusDx;
draw_pixel(x,y);
}
}
}
voiddisplay()
{
glClear(GL_COLOR_BUFFER_BIT);
Bresenham(0,0,400,400);
glFlush();
}
voidmyinit()
{
glClearColor(0.8,1.0,1.0,1.0);
glColor3f(0.0,0.0,1.0);
glPointSize(1.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,500.0,0.0,500.0);
}
voidmain(intargc,char**argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
glutInitWindowSize(500,500);
glutInitWindowPosition(200.0,200.0);
glutCreateWindow("CG_test_Bresenham_Lineexample");
glutDisplayFunc(display);
myinit();
glutMainLoop();
}
運行效果:
❹ Bresenham直線演演算法的演算方法
Bresenham直線演算法描繪的直線。假設我們需要由 (x0, y0) 這一點,繪畫一直線至右下角的另一點(x1, y1),x,y分別代表其水平及垂直坐標,並且 x1 - x0 > y1 - y0。在此我們使用電腦系統常用的坐標系,即x坐標值沿x軸向右增長,y坐標值沿y軸向下增長。
因此x及y之值分別向右及向下增加,而兩點之水平距離為x1 − x0且垂直距離為y1-y0。由此得之,該線的斜率必定介乎於1至0之間。而此演算法之目的,就是找出在x0與x1之間,第x行相對應的第y列,從而得出一像素點,使得該像素點的位置最接近原本的線。
對於由(x0, y0)及(x1, y1)兩點所組成之直線,公式如下:
因此,對於每一點的x,其y的值是
因為x及y皆為整數,但並非每一點x所對應的y皆為整數,故此沒有必要去計算每一點x所對應之y值。反之由於此線之斜率介乎於1至0之間,故此我們只需要找出當x到達那一個數值時,會使y上升1,若x尚未到此值,則y不變。至於如何找出相關的x值,則需依靠斜率。斜率之計算方法為m = (y1 − y0) / (x1 − x0)。由於此值不變,故可於運算前預先計算,減少運算次數。
要實行此演算法,我們需計算每一像素點與該線之間的誤差。於上述例子中,誤差應為每一點x中,其相對的像素點之y值與該線實際之y值的差距。每當x的值增加1,誤差的值就會增加m。每當誤差的值超出0.5,線就會比較靠近下一個映像點,因此y的值便會加1,且誤差減1。
下列偽代碼是這演算法的簡單表達(其中的plot(x,y)繪畫該點,abs返回的是絕對值)。雖然用了代價較高的浮點運算,但很容易就可以改用整數運算(詳見最佳化一節):
function line(x0, x1, y0, y1)
int deltax := x1 - x0
int deltay := y1 - y0
real error := 0
real deltaerr := deltay / deltax // 假設 deltax != 0 (非垂直線),
// 注意:需保留除法運算結果的小數部分
int y := y0
for x from x0 to x1
plot(x,y)
error := error + deltaerr
if abs(error) ≥ 0.5 then
y := y + 1
error := error - 1.0
❺ 寫出直線段掃描轉換的Bresenham演算法,並生成從點P1(0,0)到點P2 (5,2)的直線段,要求寫出每一步遞推過程
沒分?····不過既然我有現成的那就給你吧,不過復制粘貼而已!
Bresenham演算法是Bresenham提出的一種光柵線生成演算法!DDA演算法表面上看起來很有效,並且代碼也比較容易實現,但是顯示每個像素都需要進行一次浮點數加法運算,而Bresenham演算法的最大優點是不需要進行浮點數運算!這是一種精確而有效的光柵線生成演算法,該演算法僅使用增量整數計算,計算速度比DDA要快,另外,Bresenham演算法還可用於顯示圓和其他曲線,這里暫時只顯示直線!
與DDA一樣,我們假設線段的兩個端點坐標是整數值(x0,y0)(xEnd,yEnd),且斜率m滿足0<=m>=1!坐標軸的垂直軸表示掃描線位置,水平軸標識像素列,假設以單位x間隔取樣,需要確定下一個每次取樣時兩個可能的像素位置中的哪一個更接近於線路徑!
從給定線段的左端點(x0,y0)開始,逐步處理每個後繼列(x位置),並在其掃描線y值最接近線段的像素處描出一點,假如已經確定要顯示的像素在(xk,yk),那麼下一步就要確定在列xk+1=xk+1上繪制哪個像素,是在位置(xk+1,yk)還是在(xk+1,yk+1)
在取樣位置xk+1,我們使用dlower和pper來標識兩個像素與數學上線路徑的垂直偏移,在像素列xk+1處的直線上的y坐標根據直線方程可計算得:
y=m(xk+1)+b
那麼可求得:
dlower=y-yk=m(xk+1)+b-yk
pper=(yk+1)-y=yk+1-m(xk+1)-b
令斜率m=dy/dx,引入決策參數Pk,定義為:
Pk=dx(dlower-pper)
=2dx*xk-2dy*yk+c
C是一個常數,值為2dx+dx(2b-1)
由此可以計算得到
pk+1=Pk+2dy-2dx(yk+1-yk)
其中yk+1-yk取0還是取1取決於參數Pk的符號,Pk為負時取0,Pk非負時取1!
而Pk為負時,下一個要繪制的點就是(xk+1,yk)且pk+1=Pk+2dy
Pk為非負時則下一個要繪制的點就是(xk+1,yk+1)且pk+1=Pk+2dy-2dx
至此,Bresenham演算法介紹完畢,以下為某個示例:
#include<gl/glut.h>
#include<math.h>
#include<stdio.h>
voiddraw_pixel(intix,intiy)
{
glBegin(GL_POINTS);
glVertex2i(ix,iy);
glEnd();
}
voidBresenham(intx1,inty1,intxEnd,intyEnd)
{
intdx=abs(xEnd-x1),dy=abs(yEnd-y1);
intp=2*dy-dx;
inttwoDy=2*dy,twoDyMinusDx=2*dy-2*dx;
intx,y;
if(x1>xEnd)
{
x=xEnd;y=yEnd;
xEnd=x1;
}
else
{
x=x1;
y=y1;
}
draw_pixel(x,y);
while(x<xEnd)
{
x++;
if(p<0)
p+=twoDy;
else
{
y++;
p+=twoDyMinusDx;
draw_pixel(x,y);
}
}
}
voiddisplay()
{
glClear(GL_COLOR_BUFFER_BIT);
Bresenham(0,0,400,400);//這是我的線段兩端點的坐標,你可以換成Bresenham(0,0,5,2),
//不過估計很短,看不清
glFlush();
}
voidmyinit()
{
glClearColor(0.8,1.0,1.0,1.0);
glColor3f(0.0,0.0,1.0);
glPointSize(1.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,500.0,0.0,500.0);
}
voidmain(intargc,char**argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
glutInitWindowSize(500,500);
glutInitWindowPosition(200.0,200.0);
glutCreateWindow("CG_test_Bresenham_Lineexample");
glutDisplayFunc(display);
myinit();
glutMainLoop();
}
程序運行效果如圖:
❻ bresenham演算法的原理
Bresenham演算法是計算機圖形學領域使用最廣泛的直線掃描轉換方法。
其原理是:
過各行、各列像素中心構造一組虛擬網格線,按直線從起點到終點的
順序計算直線各垂直網格線的交點,然後確定該列像素中與此交點最近
的像素。
該演算法的優點在於可以採用增量計算,使得對於每一列,只要檢查一個誤差項
的符號,就可以確定該列所求的像素。
❼ Bresenham畫線演算法
基本上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時,會產生取兩個點都可以的問題。這個問題還沒深入。
❽ bresenham演算法的演算法
void IntegerBresenhamlin(int x0,int y0,int x1,int y1,int color)
{
int x,y,dx,dy,unitx,unity,fabs_dx,fabs_dy,e;
unsigned char i;
dx=x1-x0;
dy=y1-y0;
fabs_dx = (int)fabs(dx);
fabs_dy = (int)fabs(dy);
unitx = dx / fabs_dx ;
unity = dy / fabs_dy ;
x=x0;
y=y0;
if( fabs_dx> fabs_dy )
{
e=-fabs_dx;
for(i=0;i<=fabs_dx;i++)
{
drawpixel(x,y,color);
x+=unitx,e=e+2*fabs_dy;
if(e>=0)
{
y+=unity;e=e-2*fabs_dx;
}
} // for end
}
else
{
e-=fabs_dy;
for(i=0;i<=fabs_dy;i++)
{
drawpixel(x,y,color);
y+=unity,e=e+2*fabs_dx;
if(e>=0)
{
x+=unitx;e=e-2*fabs_dy;
}
} // for end
}// if end
} //:~
❾ bresenham演算法的介紹
bresenham演算法是計算機圖形學中為了「顯示器(屏幕或列印機)系由像素構成」的這個特性而設計出來的演算法,使得在求直線各點的過程中全部以整數來運算,因而大幅度提升計算速度。
❿ 運動控制器2:GRBL的核心結構體block_t和BRESENHAM演算法
typedef struct {
第一部分:bresenham演算法需要的入口條件,包括運動方向,X,Y,Z各需要運動多少步,以及完成這個BLOCK需要運動多少步。
uint8_t direction_bits; //
uint32_t steps_x, steps_y, steps_z; //
int32_t step_event_count; //
Bresenham直線演算法是用來描繪由兩點所決定的直線的演算法,它會算出一條線段在 n 維光柵上最接近的點。這個演算法只會用到較為快速的整數加法、減法和位元移位,常用於繪制電腦畫面中的直線。是計算機圖形學中最先發展出來的演算法。
GRBL中,圓弧是用直線段來接近描述的,所以不需要考慮,直接的畫法通過下面的判斷器,X先走。
把縱軸的一個方格的一半作為基準,如果在基準點以上,則Y軸走一步,如果在下面,則X繼續走一步。而step_event_count為最終走的總步數,為X+Y總步數。
第二部分:
調度器用於計算加速度的內容,也就是說,BRESENHEM用到的是梯形加速度,我們需要計算梯形的各個表徵值。
float nominal_speed; // 勻速運動速度
float entry_speed; // 從一個BLOCK進入到這個BLOCK的速度
float max_entry_speed; // 最大的進入速度
float millimeters; // BLOCK運動的實際mm距離
uint8_t recalculate_flag; // 重新計算梯度的FLAG
uint8_t nominal_length_flag; // 是否進入了勻速的FLAG
第三部分:
實際梯形的各個參數計算
uint32_t initial_rate; // 梯形運動初始值
uint32_t final_rate; // 梯形運動結束
int32_t rate_delta; //計算加速度
uint32_t accelerate_until; // 加速度階段運動的距離
uint32_t decelerate_after; // 減速度階段運行的距離
uint32_t nominal_rate; // 勻速階段運行的距離
} block_t;
實際上,BLOCK的執行需要一定的時間,所以沒有執行完成的BLOCK需要列隊進行等待,所以需要用到調度器。
目前,3D列印機做開源的主要用到了Marlin固件,其實核心演算法就是GRBL,加入了兩軸材料擠出的步進電機軸,另外還用到了一個開源的溫控PID演算法,這個我們暫時用不上,不再考慮。
在找Marlin固件時發現了一個好的晶元,TC2100這顆IC賣出的價格在20元以上,而市面上用的1.5A電流的晶元大部分只賣到了4元左右,這顆IC的核心優勢就是細分數,看了一下資料,也用到了他們的專利演算法:一種新型的PWM演算法,淘寶上賣出的模塊價格在37元左右,其實還有不少的利潤空間,可惜拿貨估計有點麻煩,訂單小了估計都不好拿貨。
另外需要注意的是,初始化的參數是存放在EEPROM中的,GRBL也有一個EEPROM的函數,但是實際上我們定義了我們新的EEPROM函數,用到的是W24512,至於這一塊如何移植,後文再介紹。