2021年4月14日 星期三

p5.js 學習筆記 (四) : 基本繪圖 (下)

本篇繼續測試 p5.js 的基本繪圖, 主要是測試橢圓, 圓弧, 頂點連線圖之繪製. 

本系列之前的文章參考 : 


以下測試使用之參考文件 : 

https://p5js.org/reference/ (教學文件)
https://editor.p5js.org (線上編輯器)


13. 繪製橢圓 : 

繪製橢圓需呼叫 ellipse(x, y, w [, h]), 其中 (x, y) 是橢圓的圓心, w 是寬徑 (即 x 軸方向), h 是高徑 (即 y 軸方向), 此乃預設之參數模式 (CENTER). 參考 :


橢圓邊線顏色與粗細可分別用 stroke(color) 與 strokeWeight(px) 設定; 填滿顏色可用 fill(color) 設定; 而 noStroke() 則取消設定變成無邊線, noFill() 則取消設定回復預設之白色填滿, 例如 :



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(200, 100);      
      background(200, 200, 200);
      }
    function draw() {
      fill('orange');
      ellipse(50, 50, 80, 60);       //圓心 (50, 50), 寬高 (80, 60)
      fill('orchid');
      ellipse(150, 50, 60, 80);     //圓心 (150, 50), 寬高 (60, 80)
      }
  </script>
</body>
</html>

此例繪製了兩個橢圓, 兩個寬高徑顛倒, 左邊的寬>高, 故為橫躺之橢圓; 右邊的高>寬為直立之橢圓, 結果如下 :




如果只傳入三個參數, 不傳入第四個參數 (高徑), 則會被視為寬高相等, 這樣就會變成正圓, 跟呼叫 circle() 一樣了, 也可傳入寬高徑相等來繪製正圓, 例如 : 



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(200, 100);      
      background(200, 200, 200);
      }
    function draw() {
      fill('orange');
      ellipse(50, 50, 80);          # 未傳第四參數, 高徑被認為與寬徑相同 (=80px)
      fill('orchid');
      ellipse(150, 50, 80, 80);  # 寬=高變成正圓形
      }
  </script>
</body>
</html>

此例左圖的 ellipse() 沒有傳入第四參數, 被視為高=寬, 所以繪製了直徑為 80px 的正圓. 右圖則直接指定寬高徑相同為 80px, 也是正圓, 事實上前一篇用 circle() 畫圓, 函式內部就是這麼做出來的, 結果如下 : 




如同矩形函式 rect() 那樣, 傳入的參數可以有不同意義, 稱為參數模式, ellipse() 函式的參數有四種模式, 上面範例使用的是預設模式 CENTER :
  • CENTER : 
    預設模式, 前兩個參數 (x, y) 是橢圓圓心, 後兩個 (w, h) 分別是寬徑與高徑. 
  • RADIUS :
    前兩個參數 (x, y) 是橢圓圓心, 後兩個 (w, h) 分別是寬徑與高徑的一半.
  • CORNER :
    前兩個參數 (x, y) 是橢圓外方框左上角座標, 後兩個 (w, h) 分別是寬徑與高徑.
  • CORNERS :
    前兩個參數 (x1, y1) 是橢圓外方框的一個頂點座標, 後兩個 (x2, y2) 則是相對頂點座標.
參數模式可利用 ellipseMode() 函式更改, 例如 ellipseMode(RADIUS) 會將 ellipse() 改為 RADIUS 參數模式, 參考 : 


例如上面範例 13-1 用 CENTER 模型畫的兩個橢圓若使用 RADIUS 來畫要改這樣 : 



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(200, 100);      
      background(200, 200, 200);
      }
    function draw() {
      ellipseMode(RADIUS);   // 設定參數模式為 RADIUS (第三四參數都是半徑)
      fill('khaki');
      ellipse(50, 50, 40, 30);      //第三四參數是寬高徑的一半
      fill('cyan');
      ellipse(150, 50, 30, 40);    //第三四參數是寬高徑的一半
      }
  </script>
</body>
</html>

此處用 ellipseMode() 將參數模式改成 RADIUS 後, 第三四參數就要傳入半徑, 即 (80, 60) 的一半 (40, 30) 了, 結果如下 :




可見圖形與上面範例 13-1 完全一樣, 僅填滿色彩不同而已. 

CORNER 模式的第一二參數則是橢圓外方框左上角座標, 第三四參數仍然是寬徑與高徑, 例如 :



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(200, 100);      
      background(200, 200, 200);
      }
    function draw() {      
      ellipseMode(CORNER);    //設定橢圓參數模式為 CORNER
      stroke(255, 0, 0);                //設定邊線為紅色
      rect(10, 20, 80, 60);            //繪製包住橢圓的矩形
      ellipse(10, 20, 80, 60);        //繪製橢圓
      stroke(0, 0, 255);                //設定邊線為藍色
      rect(120, 10, 60, 80);          //繪製包住橢圓的矩形
      ellipse(120, 10, 60, 80);      //繪製橢圓
      }
  </script>
</body>
</html>

此例在繪製橢圓之前先繪製剛好框住它的矩形, 注意, 因為設定為 CORNER 參數模式, 因此 ellipse() 的第一二參數為此外框左上角頂點座標, 所以 rect() 與 ellipse() 的四個參數會完全一樣, 結果如下 : 




同樣的圖形若改用 CORNERS 參數模式的話, 就要修改傳入參數, 第一二參數與第三四參數會被解讀為橢圓外框矩形任兩個對角頂點之座標, 例如  : 



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(200, 100);      
      background(200, 200, 200);
      }
    function draw() {      
      ellipseMode(CORNERS);     //設定橢圓參數模式為 CORNER
      stroke('indigo');                      //設定邊線顏色
      rect(10, 20, 80, 60);               //繪製包住橢圓的矩形
      ellipse(10, 20, 90, 80);          //繪製橢圓 (後兩個參數為對角頂點座標)
      stroke('maroon');                    //設定邊線顏色
      rect(120, 10, 60, 80);             //繪製包住橢圓的矩形
      ellipse(120, 10, 180, 90);       //繪製橢圓 (後兩個參數為對角頂點座標)
      }
  </script>
</body>
</html>

此例用 ellipseMode() 設定為 CORNERS 參數, 因此若前兩個參數為橢圓外框矩形之左上角頂點座標, 則後兩個參數須為其對角之右下角頂點座標, 結果如下 : 




14. 繪製弧形 : 

呼叫 arc(x, y, w, h, start, stop) 可繪製弧形, 此弧形是基於橢圓的, 亦即前面四個參數的模式可以用 ellipseMode() 來設定, 預設是 CENTER 模式, 即 (x, y) 為橢圓的中心坐標, (w, h) 是寬徑與高徑. 第五六參數為弧形之起訖弧度, 值為 0 到 TWO_PI (2*3.1415926), 參考 :


注意, arc() 的 start 與 stop 參數預設單位為弧度不是角度, 常用與弧度有關的的內建常數如下 :
  • QUARTER_PI : 45 度
  • HALF_PI : 90 度
  • PI : 180 度
  • PI + QUARTER_PI : 225 度
  • PI + HALF_PI : 270 度
  • TWO_PI : 360 度
角度與弧度可以利用 radians(deg) 與 degrees(rad) 這兩個內建函式互轉, 參考 : 


事實上整個 p5.js 預設採用弧度模式, 且適用所有與方位度數有關的函式, 參數或傳回值若為方位度數預設都是弧度, 但可以用 angleMode(mode) 來設定, mode=RADIANS (預設) 或 DEGREES, 更改設定後會'套用於全部函式, 參考 :


繪製弧形時採順時針方向由 start 至 stop 度, 超過 360 度 (TWO_PI) 歸零, 例如 : 



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(400, 100);      
      background(200, 200, 200);
      }
    function draw() { 
      arc(50, 50, 80, 80, 0, HALF_PI);    //繪製弧形, 起始角 0 度, 終止角 90 度
      arc(150, 50, 80, 80, 0, PI + HALF_PI);   //繪製弧形, 起始角 0 度, 終止角 270 度
      arc(250, 50, 80, 80, PI, TWO_PI + HALF_PI);  //繪製弧形, 起始角 180 度, 終止角 90 度
      arc(350, 50, 80, 80, QUARTER_PI, PI + QUARTER_PI);   //起始角 45 度, 終止角 225 度
      }
  </script>
</body>
</html>

此例因為要繪製四個弧形, 故將畫布放寬為 400*100, 其中第一個弧形從 0 度順時針畫到 90 度; 第二個從 0 度順時針畫到 270 度; 第三個從 180 度畫到 90 度 (TWO_PI+HALF_PI 超過 360 度剩下 HALF_PI 即90 度); 第四個從 45 度畫到 225 度, 結果如下 : 




上面的 draw() 若改成如下以 radians() 將角度轉換成弧度的方式效果一樣 :



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(400, 100);      
      background(200, 200, 200);
      }
    function draw() { 
      arc(50, 50, 80, 80, 0, radians(90));    
      arc(150, 50, 80, 80, 0, radians(270));   
      arc(250, 50, 80, 80, radians(180), radians(90));  
      arc(350, 50, 80, 80, radians(45), radians(225));
      }
  </script>
</body>
</html>

結果與上面相同. 

或者乾脆用 angleMode(DEGREES) 做全域設定, 固定使用角度模式, 例如 : 



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(400, 100);      
      background(200, 200, 200);
      }
    function draw() { 
      angleMode(DEGREES);        //將方位度數改為角度模式
      arc(50, 50, 80, 80, 0, 90);        //繪製弧形, 起始角 0 度, 終止角 90 度
      arc(150, 50, 80, 80, 0, 270);    //繪製弧形, 起始角 0 度, 終止角 270 度
      arc(250, 50, 80, 80, 180, 90);  //繪製弧形, 起始角 180 度, 終止角 90 度
      arc(350, 50, 80, 80, 45, 225);  //起始角 45 度, 終止角 225 度
      }
  </script>
</body>
</html>

結果與上面範例相同 : 




弧形事實上是在 ellipse() 的基礎上繪製的, 亦即它基本上是橢圓的一部份, 例如 :


測試 14-4 : 橢圓上的弧形 [看原始碼]  

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(200, 100);      
      background(200, 200, 200);      
      angleMode(DEGREES);       //將方位度數改為角度模式
      }
    function draw() { 
      fill('lime');
      arc(50, 50, 80, 60, 0, 270);     //繪製橢圓上的弧
      fill('cyan');
      arc(150, 50, 60, 80, 0, 270);   //繪製橢圓上的弧
      }
  </script>
</body>
</html>

因為一律改為角度模式, 所以可將 angleMode(DEGREES) 移到 setup() 內. 結果如下 :





15. 繪製頂點連線之圖形 : 

以連接頂點繪製圖形是以記錄器的方式來設定, 首先需呼叫 beginShape() 起始記錄器, 然後用一連串的 vertex(x, y) 來記錄頂點, 最後呼叫 endShape() 或 endShape(CLOSE) 結束記錄器, 這時所記錄之頂點座標就會寫入圖像緩衝器進行繪圖. 如果呼叫 endShape() 時有傳入 CLOSE 常數, 則會將起點與終點連結起來變成封閉圖形, 例如 : 



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(200, 100);      
      background(200, 200, 200);
      }
    function draw() { 
      fill('PaleGreen');
      beginShape();          //開始記錄圖形
      vertex(10, 50);         //頂點座標
      vertex(30, 30);
      vertex(30, 40);
      vertex(90, 40);
      vertex(90, 60);
      vertex(30, 60);
      vertex(30, 70);
      endShape();               //結束記錄器開始繪圖 (不連接起點與終點)
      fill('cyan');
      beginShape();            //開始記錄圖形
      vertex(110, 50);         //頂點座標
      vertex(130, 30);
      vertex(130, 40);
      vertex(190, 40);
      vertex(190, 60);
      vertex(130, 60);
      vertex(130, 70);
      endShape(CLOSE);   //結束記錄器開始繪圖, 連結起點與終點
      }
  </script>
</body>
</html>

此例利用記錄頂點座標繪製了兩個折線圖, 第一個結束記錄器時沒有將 CLOSE 傳入 endShape(), 所以起點與終點不會連起來; 而右邊的圖形則有, 故形成一個封閉圖形, 但不論有無封閉, 都可以用 fill() 填滿背景顏色, 結果如下 : 




下面是利用頂點連線繪製的交通號誌單行道 :



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(200, 100);      
      background(200, 200, 200);
      noStroke();     //無邊線
      }
    function draw() { 
      fill('navy');
      rect(10, 30, 80, 40);    //外框矩形
      fill('white');
      beginShape();    //開始記錄箭頭頂點
      vertex(20, 45);
      vertex(70, 45);
      vertex(70, 40);
      vertex(80, 50);  //箭頭尖端
      vertex(70, 60);
      vertex(70, 55);
      vertex(20, 55);
      endShape(CLOSE);   //結束記錄頂點
      fill('navy');
      rect(130, 10, 40, 80);    //外框矩形
      fill('white');
      beginShape();    //開始記錄箭頭頂點
      vertex(145, 80);
      vertex(145, 30);
      vertex(140, 30);
      vertex(150, 20);  //箭頭尖端
      vertex(160, 30);
      vertex(155, 30);
      vertex(155, 80);
      endShape(CLOSE);    //結束記錄頂點
      }
  </script>
</body>
</html>

此例在 setup() 中添加 noStroke() 表示無需邊線, 在 draw() 中先後繪製向右與向上箭頭之單行道, 要先繪製較大的外框, 然後記錄箭頭的頂點位置, 結果如下 :




參考 :



16. 繪製文字 : 

在畫布上繪製文字需要呼叫下列內建函式 :
  • text(str, x, y) :
    str 為欲繪製之字串, (x, y) 為開始繪製之起始座標 (即第一個字元). 
  • textSize(px) : 
    設定字型大小. 
  • textAlign(halign [, valign]) : 
    文字對齊方式, halign=LEFT(預設)/CENTER/RIGHT, valign=TOP/CENTER/BOTTOM(預設)
  • textStyle(style) :
    文字格式 (可能被 CSS 覆蓋), style=NORMAL(預設)/BOLD/ITALIC/BOLDITALIC
另外搭配 fill(color) 可設定文字填滿顏色, stroke(color) 可設定邊線顏色, strokeWeight(px) 可設定文字邊線厚度, 例如 :



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(200, 100);      
      background(200, 200, 200);
      }
    function draw() { 
      fill('red');
      textSize(25);
      stroke('black');
      text("Hello", 100, 50);
      }
  </script>
</body>
</html>

此例設定自座標 (100, 50) 開始輸出字串, 結果如下 :




可見預設的水平對齊方式為 LEFT, 垂直對齊方式為 BOTTOM. 職此之故, 若將開始繪製座標改成 (0. 0) 可能會讓輸出的字串消失不見, 例如 : 



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(200, 100);      
      background(200, 200, 200);
      }
    function draw() { 
      fill('red');
      textSize(25);
      stroke('black');
      text("Hello", 0, 0);    //座標預設對齊字串左下角
      }
  </script>
</body>
</html>

此例與上例差別僅在繪製字串的起始座標改為左上角, 結果如下 :




文字似乎不見了, 其實只露出一點點下緣而已, 原因是字串預設以左下角對齊 (halign 預設 LEFT, valign 預設 BOTTOM). 若要在左上角輸出時能完整顯示字串, 需用 textAlign() 將對其方式改為左上角 (LEFT, TOP), 例如 :



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(200, 100);      
      background(200, 200, 200);
      }
    function draw() { 
      fill('red');
      textSize(25);
      textAlign(LEFT, TOP);    //座標對齊字串左上角
      stroke('black');
      text("Hello", 0, 0);   
      }
  </script>
</body>
</html>

此例與上例差別只是用 textAlign(LEFT, TOP) 將座標對齊字串左上角, 結果如下 :




內建函式 frameRate() 會傳回目前的圖框率設定值, 此資訊可以用 text() 顯示在畫布上, 例如 : 


測試 16-4 : 印出圖框率 [看原始碼]  

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(200, 100);
      textSize(14);
      textAlign(CENTER, CENTER);
      }
    function draw() { 
      background(200, 200, 200);    //刷新背景 (避免輸出資料不斷覆疊)
      fill('blue');
      var fps=parseInt(frameRate(), 10);
      text("frameRate()=" + fps, width/2, height/2);
      }
  </script>
</body>
</html>

此例在畫布中央顯示圖框率設定值, 因預設是 60 FPS, 故其值會在 59, 60 不斷變化. 注意此例之 background() 與上面諸例不同, 它是放在 draw() 內, 原因是透過刷新背景可避免輸出的 59 或 60 不斷疊在一起, 結果如下 :




同樣地, 內建變數 frameCount (累積圖框數) 也可以這樣顯示 :


測試 16-5 : 印出累計圖框數 [看原始碼]  

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>p5.js test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
  <script>
    function setup() {
      createCanvas(200, 100);
      textSize(14);
      textAlign(CENTER, CENTER);
      }
    function draw() { 
      background(200, 200, 200);
      fill('blue');
      text("frameCount=" + frameCount, width/2, height/2);   //輸出累計圖框數
      }
  </script>
</body>
</html>

此程式與上例類似, frameCount 本身是整數, 不需要用到 parseInt() 轉成整數, 結果如下 :



沒有留言:

張貼留言