iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 8
1

引言

Day7連結: https://ithelp.ithome.com.tw/articles/10218420
昨天我們講解了DDA演算法的原理與解釋,今天我們來實作它。

因為只是單純實作這個演算法,我們還沒實作螢幕的部分。
因此我們會先設計一個簡單的二維陣列當作螢幕來顯示計算出來的直線!


實作細節

我們將以條列式列出昨天演算法的細節,並一邊使用C語言實作在下方:

  • 首先先宣告好所需的變數:
#include<stdio.h>
#include<stdlib.h>
#include<math.h>

int main()
{
    char screen[21][41] = {};  // 顯示直線的二維陣列,
    // 大小21*41(cmd文字的高會是寬的兩倍,且因需取中點考量,會取奇數當長寬)

    int p1x = 0, p2x = 0, p1y = 0, p2y = 0;  // P1與P2的x, y分量
    int dx = 0, dy = 0, steps = 0;  // dx: P1與P2的x分量差, dy: P1與P2的y分量差, steps: 總步數
    float xinc, yinc, currX, currY;  // xinc, yinc為x, y方向每一步的距離,
                                     // currX, currY用來記錄目前畫到的點座標
    int rx = 0, ry = 0;  // rx, ry為做過四捨五入後的currX, currY
    
  1. 決定兩點p1, p2以及兩點x, y分量的差(以下簡稱dx, dy)
    printf("請輸入P1的x, y及P2的x, y:\n");
    scanf("%d %d %d %d",&p1x, &p1y, &p2x, &p2y);

    dx = p2x - p1x;  // x分量差
    dy = p2y - p1y;  // y分量差
  1. 判斷由x, y連起來的直線是「平緩的 Gentle」或是「急劇的 Sharp」。平緩的定義是dx的大小大於dy的大小;急劇的定義是dy的大小大於dx的大小。
  2. 若確定是平緩的直線,則把總像素點數設為dx(因為dx比較大),反之則總像素點數設為dy。
    if(abs(dx) > abs(dy)) steps = abs(dx);  // dx, dy可能為正數或負數,因此加上絕對值比較
    else steps = abs(dy);
  1. 我們可以把畫線當作爬山,總像素點數我們當作總步數(stepNumber),一共要走幾步才能畫完這條線。以前面的3,4,5三角形為例,4是總步數,因此我們要繪製4個像素點才能畫完這條線。
  2. 再來我們計算每一步要走多長,有了總山路長度(dx, dy的比擬),也有了總步數,我們可以算出x, y每一步要走多長(分別設為xStepLenyStepLen),式子如下:
    https://chart.googleapis.com/chart?cht=tx&amp;chl=xStepLen%20%3D%20%5Cfrac%7Bdx%7D%7BstepNumber%7D
    https://chart.googleapis.com/chart?cht=tx&amp;chl=yStepLen%20%3D%20%5Cfrac%7Bdy%7D%7BstepNumber%7D
    xinc = dx/(float)steps;  // xinc, yinc為浮點數,因此須將int強轉為浮點數再相除
    yinc = dy/(float)steps;
  1. 得到了每步的距離後,由第一個點開始畫(通常是p1或p2其中之一),而後分別將x, y分量加上xStepLen與yStepLen,得到第二個點,但此點通常是帶有小數點的浮點數,需要先經過四捨五入處理,成為整數點,再畫上去,不斷重複直到每個點都畫上去(一共有 總步數+1 個像素點哦!)。
    currX = p1x;  // 以P1作為繪製的第一個點
    currY = p1y;
    rx = round(currX);  // 四捨五入currX, currY
    ry = round(currY);
    if((ry + (21/2) >= 0 && ry + (21/2) < 21) && (rx + (41/2) >= 0 && rx + (41/2) < 41))
        // 檢測是否超出邊界
    {
        screen[ry + (21/2)][rx + (41/2)] = '#';  // '#'當作一個點,為了將(0, 0)校正到中心,需進行平移
    }

    int i = 0;
    for(i=0;i<steps;i++)  // 加上上面的第一個點,一共(steps + 1)個點
    {
        currX += xinc;  // 每步分別加上xinc及yinc,成為下一步
        currY += yinc;
        rx = round(currX);  // 四捨五入
        ry = round(currY);
        if((ry + (21/2) >= 0 && ry + (21/2) < 21) && (rx + (41/2) >= 0 && rx + (41/2) < 41))
        {
            screen[ry + (21/2)][rx + (41/2)] = '#';
        }
    }
  1. 印出計算後的線。
    // 印出screen上畫好的點
    int j = 0;
    for(i = 0; i < 21; i++)
    {
        for(j = 0; j < 41; j++)
        {
            printf("%c", screen[i][j]);
        }
        printf("\n");
    }

    // 須注意y方向的正向是往「下」的!
    
    return 0;
}

程式執行結果

  1. (-2, -2) & (3, 5)
    https://ithelp.ithome.com.tw/upload/images/20190922/20111429YcsqUpRTmD.png
    https://ithelp.ithome.com.tw/upload/images/20190922/20111429Ebhb3FnK3p.jpg

  2. (6, -9) & (11, -3)
    https://ithelp.ithome.com.tw/upload/images/20190922/20111429H7sjgRT5fg.png
    https://ithelp.ithome.com.tw/upload/images/20190922/20111429WUCjIkJKFp.jpg

  3. (-100, 80) & (80, -100)
    超出範圍的話,會將超出的部分裁去忽略
    https://ithelp.ithome.com.tw/upload/images/20190922/20111429oolRze1blb.png


這樣就是一個簡單利用DDA設計的繪圖器。


上一篇
[11屆鐵人賽Day7] 用文字當像素來繪製直線 — DDA演算法
下一篇
[11屆鐵人賽Day9] 旋轉矩陣
系列文
若沒有遊戲引擎、合作夥伴...做得出遊戲嗎? 不試試看不知道吧? [使用C語言]30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言