2021年5月9日 星期日

試做馬鈴薯燉紅燒肉

因為前兩周買了一包胡蘿蔔與馬鈴薯, 放冰箱太久容易軟掉, 上回拜拜的豬肉也還沒用掉, 恰好來做馬鈴薯燉紅燒肉, 參考食譜是楊桃美食的 : 





材料 :
  • 五花肉  適量
  • 馬鈴薯 兩顆
  • 胡蘿蔔 1條
  • 蔥 兩條
  • 薑 一塊
  • 辣椒 一條
  • 蒜頭 適量
  • 冰糖 適量
  • 醬油 適量
  • 米酒 適量
做法 : 
  1. 胡蘿蔔, 馬鈴薯切塊 (不要太小) 備用. 
  2. 五花肉切塊, 薑切片, 辣椒切段, 蔥切段備用.
  3. 起油鍋, 放下薑片, 辣椒, 蔥段拌炒香後放入五花肉塊煎至兩面微焦. 
  4. 先放入胡蘿蔔塊, 加入適量米酒, 醬油, 冰糖, 加水蓋過食材表面, 蓋上鍋蓋中火煮 10~15 分鐘後, 放入馬鈴薯, 蓋上鍋蓋再煮至馬鈴薯熟透即可, 期間盡量不要翻攪避免馬鈴薯糊掉. 
晚餐實作結果成功 :









肥大叔也做過這道菜, 大同小異, 只是程序略微不同而已, 參考 :





2021 年第 19 周記事

上週將 p5.js 初階做個大致結尾後, 本周回到 Python 的學習, 由於感到 Numpy 在後續機器學習中的重要角色, 所以回到去年的斷點, 重新複習了陣列的建立, 希望未來兩周能把 Numpy 學完, 再把學到一半的 Matplotlib 也做個完結. 

而湘雲老師的網站本周都未再動手, 前天突然想到其實用樹莓派就可以架網站, 不須花 1~2 萬元去買一台小 PC, 樹莓派更好攜帶. 打算用閒置的 Pi B+ 那塊來測試, 使用 NGINX + Django 架構, 調試好後交湘雲老師拿回學校接上網路線, 我再用 VNC Connect 遠端維護. 可以的話再叫老張買片 Pi 4B 替換, 效能較優.

本周評鑑結束, 這兩年規模已不像之前龐大, 當天解決免去惱人的飯局. 還記得七年前第一次當委員, 飯局中接到母親電話, 說頸部又腫起來, 我馬上就回鄉下去, 由同事代理第二天評鑑工作, 因為母親就開始住院, 最後一次. 今天是母親節, 但我沒法幫媽慶祝, 只能傳祝福訊息給媽生前最親的小阿姨跟左營阿姨, 以及小舅媽, 

週五姊姊回到高雄, 晚上去海港吃飯, 菁菁把之前當保母的薪水, 過年特留的壓歲錢, 加上我給的零用錢合起來約一萬多, 去銀樓買了一個金項鍊給水某, 還製作了一個相片集卡片 (難怪最近跟我要舊相片). 我說那你私房錢全用光了, 父親節怎麼辦? 呵呵, 其實我只要卡片就好了. 

因為虹彩炎使左眼視力模糊, 經一周來點眼科開的眼藥水已改善, 但左視界仍有一層薄霧使得兩眼焦距無法協調, 對閱讀學習造成不適. 醫師說這是免疫力下降的問題, 我查了資料, 嚴重可致青光眼或失明, 加上護理師提到可能造成不可逆的視力受損, 令我感到恐懼. 因此最近我都十點半就寢, 精神也好很多了. 明日要再回診. 

市圖還書 4 本 (韓語, 資料視覺化等)

因水某的借書證已 30 本滿檔. 為了騰出空間領預約書, 先還下列書 :
No.2 韓語文法書寫得簡單易懂, 是去年追韓劇時借到現在, 只看了一點點, 我連日文都沒時間複習, 何況學韓語, 故先還了. No. 3 是 Oreilly 出版的, 也是去年學 Matplotlib 時借的, 但裡面沒有任何程式碼, 是廣泛介紹資料視覺化方法的書, 在煩惱不知如何呈現資料時可以翻一翻查找一下. No. 4 已看完並做了筆記. 下周持續清理久借的書. 

2021年5月8日 星期六

好書 : 多找個籃子放雞蛋

這本書是還書時偶然在書架上看到的, 其實就是講斜槓的概念, 值得現在的年輕人一讀, 對我這個衣食無虞, 但仍保有年輕向上之心的中年大叔而言也是很受用 (雖然晚了點) : 



Source : 博客來


作者王明聖是一個創業顧問, 在輔導許多團隊創業的過程中了解到上班族辭職創業所遇到的許多困境, 認為這種模式對大多數上班族而言並不適合, 認為一邊保有正職, 一邊從事副業創造薪水以外的收入比較穩當. 作者本人就是 "副業學校" 的品牌創辦人, 推廣透過副業開創自己獲利資產的方法, 參考 :


讀後筆記摘要如下 :
  1. 創業有很大的風險, 但上班族只靠單一薪水風險更大, 只領上班薪水才是最大的風險.
  2. 辭職創業若找不到有效的賺錢模式將承受極大的財務與精神壓力.
  3. 創造副業雖然讓下班後更忙碌, 但越忙碌會讓人的時間利用效率更高. 
  4. 景氣好時上班族不覺得副業重要, 一旦產業風暴來襲就會後悔沒有趁早開始副業. 
  5. 依靠單一薪水就是把雞蛋放在同一個籃子裡, 副業並非創業, 而是將收入來源分散風險而已.
  6. 啟動副業的五個步驟 :
  7. (1). 領悟 : 新冠肺炎讓上班族領悟單一薪水的巨大風險, 必須要有副業開創額外收入.
    (2). 方法 : 粉絲團, 團購, 業配 ...
    (3). 點子 : 先檢視自己有哪些條件可以做為副業的點子, 突發奇想的點子不易實現. 
    (4). 行銷 : 在推出產品之前先做行銷, 聆聽粉絲的聲音培養潛在客戶 (募資的概念).
    (5). 行動 : 不採取行動, 零乘以任何數都是零. 
  8. 美國國稅局統計百萬富翁的七個收入來源 :
    (1). 薪水 (2). 獲利 (3). 利息 (4). 股利 (5). 租金 (6). 投資 (7). 權利金
  9. 千萬不要輕易為了創業 (副業) 而辭職, 因為當老闆可能比上班還慘
  10. 除非自己擁有獨特技術, 也有投資人捧著資金要求投資, 否則創業成功的機會並不高 (不會超過 10%). 媒體往往用動人的故事描寫成功創業者的美好故事, 卻故意忽略倖存者偏差, 因為創業失敗的故事沒有人會想看. 
  11. 副業與創業不同, 不須辭職, 也不需借錢, 可能只需要從薪水中拿出一點點的自有資金來做初期投資. 副業其實也是一種創業形式, 只是規模小很多 (微創業), 賺錢的速度也較慢; 外包接案或寫業配文可以馬上有副業收入, 但若經營自媒體 (例如當 Youtuber ) 或製作線上課程就需要長時間經營才行. 
  12. 哪有工作不委屈, 沒有工作才委屈. 工作中的委屈還可以跟同事或朋友吐苦水; 創業賠錢甚至失敗失業的委屈卻是無人可吐苦水 (朋友都跑光了). 當你遇到工作中的委屈時, 試著想像一下, 你的主管有比你快樂嗎? 其實老闆可能是全公司最不快樂的人.
  13. 你討厭的, 真的是這份工作嗎? 其實你真正討厭的可能是你不知道自己要的是甚麼生活, 而不是現在的工作. 
  14. 在主業之外開創副業才是解決不喜歡正職工作的最好方法, 不要動不動就說老子不幹了. 等你副業收入超過了薪水, 你反而更珍惜白天的工作.
  15. 窮爸爸注重靠教育累積知識與技能以便謀職賺錢; 富爸爸則注重了解金錢運作, 資產, 負債, 以及現金流作用, 讓錢替自己工作.
  16. 副業就是上班族的備胎, 平時可以補充薪水收入, 萬一正職遇到突發狀況, 也可以專心發展副業, 不至於讓生活陷入窘境.
  17. 開創副業不代表不熱愛工作, 即使熱愛正職工作仍然要開創副業, 就跟熱愛工作的人還是會投資理財一樣, 沒有人會認為你投資股票就是不熱愛工作. 在開創副業後反而要更認真工作, 因為你需要薪水為副業投資 老闆就是你的副業投資者. 
  18. 副業不是兼差, 不是下班後為另一個老闆工作, 副業是自己創立的品牌或服務, 是能創造輸入且屬於你的資產. 打工, 賣保險, 外送, Uber 司機, .... 這些都是兼差, 不是副業, 但它們確實能立刻帶來第二份收入. 
  19. 部落客五個常用的賺錢模式 : 谷歌廣告, 聯盟行銷, 業配文, 開箱文產品推薦, 團購. 
  20. 外包接案是最快可賺到錢的副業, 例如影片剪輯等, 以文字工作為主力, 可在 104 或 518 外包網找. 
  21. 對於擁有專門知識的人來說, 開課教學是最適合當作副業的選項. 不要認為自己何德何能可以教別人, 開課也不需要博士學位, 只要比別人早一步學會某個技能就可以去教不懂的人. 完全無經驗的新手講師建議先從各地的社區大學著手, 他們很歡迎各式各樣的講師來提案開課, 雖然鐘點費可能不高, 但可以累積較學經驗. 
  22. 開設線上課程首先要製作簡報, 然後對著電腦一邊解說簡報, 一邊把螢幕與講解錄製下來, 進行剪輯後製之後, 再找合適的線上平台上架. 線上課程很像是拍單元劇, 每一個單元最好是 10~15 分鐘講完一個主題, 每節結束再做個簡單的測驗. 製作線上課程可參考 Terence 製作的 "影像表達力線上課程" (NT$ 2980). 
  23. 線上平台 HAHOW 採用預購方式開課, 等有足夠學生預購課程才接受講師拍攝課程. 而美國 Udemy 則比較像露天蝦皮之類的電商, 是全球最大教學平台, 但價格低講師分佣也較少, 但不需要行銷也會有學生購買. 美國 teacheable.com 畫面都是中文, 信用卡可用台幣支付. 新冠肺炎徹底改變了線上教學的接受度, 是上班族開啟副業的絕佳機會. 

2021年5月5日 星期三

好站 : 都會阿嬤的 Tensorflow 2 教學

今天在搜尋 Tensorflow 資料時找到 "都會阿嬤" 的 Tensorflow 2 教學, 內容讓我驚豔, 寫得真好. 雖然目前站內文章還不多, 但每一篇都擲地有聲, 應該花了不少時間編寫, 非常值得一看 : 


觀察文章標籤, 作者的興趣與我幾乎百分百重疊, 機器學習, 物理學, 爬蟲, ... 都是我的最愛, 特別是下面這篇介紹的爬蟲偽裝術, 原來 Python 還有這個好用的第三方套件啊 !


不錯不錯, 我每一篇都要仔細地來閱讀研究. 

好書 : Beginning Machine Learning in the Browser

今天看到這本 Apresss 於 2021 年剛出版的新書 :  



Source : 天瓏


此書主要是介紹如何利用 Tensorflow.js 函式庫在瀏覽器上進行機器學習項目, 例如透過攝影機進行步態分析, 因為裡面也使用了 p5.js, 這讓我非常驚訝, 沒想到 p5.js 還能與機器學習扯上關係 (應該主要是拿來做介面控制).

Tensorflow.js 是 Tensorflow 的 Javascrirpt 版, 開發目的是要在瀏覽器或資源受限的設備 (例如手機平板等嵌入式設備) 上應用 Tensorflow 機器學習技術解決影像辨識問題, 例如人類步態分析 (gait analysis). 

關於 Tensorflow.js 參考 : 


2021年5月4日 星期二

試做五香滷雞腿

我最近常看肥大叔的料理短片, 連續兩周的周日都實作這道 "五香滷雞腿", 因為作法很簡單, 只要滷半小時即可嘗到香噴噴的滷雞腿 : 





材料 :
  • 雞腿 若干
  • 雞翅 若干
  • 豆干 若干
  • 海帶 適量
  • 水煮蛋 適量
  • 冷凍豆腐 一塊 (我另加的)
  • 魚丸 (我另加的)
  • 五香粉 少許
  • 胡椒粉 少許
  • 米酒 少許
  • 醬油 適量
  • 冰糖 適量
  • 薑 適量
  • 蒜頭 適量
  • 蔥 適量
  • 辣椒 一條
做法 : 
  1. 雞腿雞翅洗淨, 川燙五分鐘 (雞腿先雞翅後以免雞翅太熟) 撈起洗淨備用. 
  2. 薑切片, 蔥切段, 起油鍋放蒜頭, 薑片, 辣椒與蔥段下鍋炒香, 加入冰糖, 米酒, 五香粉, 胡椒提味, 再倒約 200 cc 醬油, 放入雞腿, 雞翅, 豆干, 海帶, 水煮蛋等, 倒入一鍋熱水蓋過這些滷料, 開大火沸騰後轉小火滷半小時即可.  
我去全聯沒找到五香粉, 但有五香滷包, 其實買萬用滷包也可以 (其實更香). 豆干與海帶最好一次用完, 因為放冰箱幾天可能會發霉. 我另外還加了魚丸與吃不完放上層變冰塊的冷凍豆腐 (要切塊), 剛好放滿整鍋. 






滷的途中可以再加些醬油調色調味.

2021年5月3日 星期一

2021年5月2日 星期日

2021 年第 18 周記事

周一幫湘雲老師在 GitHub 申請了帳號並設定網站, 簡單地做了個網站雛型, 但考量要放許多語料音檔, GitHub 免費帳號只有 500 MB 恐怕不敷使用, 老張建議還是買台小電腦做專用伺服器算了, 反正學校網址也申請好了. 穿透防火牆須固定 IP 問題已解決, 用 teamviewer 即可. 

週三去年度體檢時覺得左眼痠痛, 晚上去李錦輝那裏看, 說是結膜炎, 拿了眼藥水回來點了兩天痠痛退了, 但起床卻霧霧的, 過些時候才漸清, 再去給眼科看, 說可能是免疫力下降引起虹膜炎, 更改藥水後左眼仍有雲在飄, 過兩天再去複診. 眼睛有毛病真的很不方便, 焦距受影響只靠右眼怪怪的. 現在要早睡, 手機十一點就寢鈴每天響, 但總是拖到 12 點過, 要改. 

本周終於將 p5.js 大致測完了, 整整用掉了我四月份的所有空閒時間, 雖然暫停了進行中的 Python 學習, 但覺得收穫很多, 掌握了此函式庫的基本技法. 我深知此函式庫再往下走水很深, 特別是需要一些數學技巧, 互動藝術與數學在 p5.js 深深關聯在一起, 創意與數學是進階的不二法門.  

五月份開始要重回 Python 懷抱, 開啟機器學習的旅程了!

好書 : TensorFlow 自然語言處理

最近從母校借到一本 NLP 的好書 : 

# TensorFlow 自然語言處理 (碁峰, 藍子軒譯)


Source : 博客來


此書譯自 Packt 出版的 "Natural Language Processing with TensorFlow (Thushan Ganegedara, 2018)" 一書, 雖然已是三年前的書了, 但作者精煉的文筆與循序漸進的鋪陳, 仍是學習自然語言處理的一本入門好書. 


Source : 天瓏


由於原書撰寫時 TensorFlow 2.0 尚未釋出, 因此本書仍使用 v1 版的 session.run() 等用法, 閱讀時需自行改為 v2 用法. 書中原始碼可在碁峰網站下載 :


我剛讀完第一章, 覺得 "so far so good", 繼續向第二章挺進!  

2021年5月1日 星期六

p5.js 學習筆記索引

四月初開始一時興起研究了一下互動繪圖函式庫 Processing (Java-based) 的網頁版 p5.js, 覺得非常簡單好學, 即使還不熟 Javascript, 只要會呼叫函式即可繪製出精美的網頁圖形, 我讀了官網的線上說明文件後便進行了一些基本的繪圖測試, 並把結果記錄在如下幾篇文章中 : 


p5.js 是廣受藝術界人士愛用的網頁繪圖工具, 值得繼續學習研究 :

~~進行中~~

p5.js 學習筆記 (九) : 動態圖形 (下)

前兩篇屬於互動類的動態圖形, 分別使用滑鼠與鍵盤控制圖形之繪製, 本篇則是要測試自動類的動態圖形, 主要是透過隨機 (亂數) 函式 random() 與雜訊函式 noise().


3. 利用隨機函式畫動態圖 : 

Javascript 的隨機函式 Math.random() 會傳回一個值域為 0~1 之間, 精度為小數點後 16 位之偽隨機數, p5.js 以將其包裝為更好用的內建函式 random(), 並提供其他相關的函式如下表 : 


 random([min=0, max=1]) 傳回介於 min (含, 預設 0) 與 max (不含) 之間的隨機浮點數
 random(arr) 從陣列 arr 的元素中隨機挑一個傳回
 randomSeed(seed) 設定隨機種子 seed 數值使 random() 傳回固定序列隨機數
 randomGaussian([mean, std]) 傳回平均值 mean 標準差 std 之常態或高斯分布隨機數


p5.js 的 random() 函式的參數是可有可無的, 用法如下 :
  • random() :
    不傳入任何參數時, 傳回值為 0~1 之間的浮點數亂數.
  • random(max) :
    只傳入一個參數時為 max, 傳回值 0~max 間之浮點亂數.  
  • random(min, max) :
    傳入兩個參數時, 前為 min 後為 max, 傳回值 min~max 間之浮點亂數.
也可以將一個陣列傳入 random(), 則傳回值是隨機從陣列中挑選的一個元素. 函式 randomGussian() 則是根據指定平均值與變異數之常態 (高斯) 機率分布來傳回隨機數, 其參數也是可有可無的, 用法如下 :
  • randomGaussian() :
    不傳入任何參數時, 表示使用平均值=0, 標準差=1 的常態分布密度函數.
  • randomGaussian(mean) :
    只傳入一個參數時, 表示使用平均值=mean, 標準差=1 的常態分布密度函數. 
  • randomGaussian(mean, std) :
    傳入兩個參數時, 表示使用平均值=mean, 標準差=std 的常態分布密度函數.
而 randomSeed(seed) 則是用來設定隨機種子, 有設定隨機種子為固定常數的話, 每次重新執行程式將傳回相同序列的偽隨機數; 反之沒設的話每次執行得到的偽隨機數序列會不同. 
 
例如 : 



<!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>
    var arr=[0,1,2,3,4,5,6,7,8,9];
    function setup() {
      createCanvas(350, 120);
      background(1, 70, 100);
      fill('orange');
      frameRate(1);      //將圖框速率設為每秒 1 框
      textSize(16);
      }
    function draw() { 
      background(1, 70, 100);
      text('random():', 10, 20);
      text(random(), 90, 20);
      text('random(0,10):', 10, 40);
      var rnd1=random(0,10);     //傳回 0~10 間的隨機浮點數
      text(rnd1, 120, 40);
      text('parseInt(random(0,10)):', 10, 60);  //取整數部分
      text(parseInt(rnd1), 180, 60);
      text('random([0,1,...10]):', 10, 80); 
      text(random(arr), 150, 80);      //從 [0, 1, 2, ...10] 陣列中隨機挑一個元素傳回
      text('randomGaussian():', 10, 100);
      text(random(randomGaussian()), 150, 100);  //常態分佈隨機數
      }
  </script>
</body>
</html>

此例使用 text() 在畫布上顯示 random() 與 randomGaussian() 的亂數, 注意, 在 setup() 中刻意用 frameRate() 函式將圖框率設為每秒一框, 這樣才不會太快來不及觀察數字的跳動, 結果如下 : 




下面是將上面程式加上 randomSeed() 固定隨機種子的結果 :



<!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>
    var arr=[0,1,2,3,4,5,6,7,8,9];
    function setup() {
      createCanvas(350, 120);
      background(1, 70, 100);
      fill('orange');
      frameRate(1);
      textSize(16);
      randomSeed(2);      //設定隨機種子, 固定隨機序列
      }
    function draw() { 
      background(1, 70, 100);
      text('random():', 10, 20);
      text(random(), 90, 20);
      text('random(0,10):', 10, 40);
      var rnd1=random(0,10); 
      text(rnd1, 120, 40);
      text('parseInt(random(0,10)):', 10, 60);
      text(parseInt(rnd1), 180, 60);
      text('random([0,1,...10]):', 10, 80);
      text(random(arr), 150, 80);
      text('randomGaussian():', 10, 100);
      text(random(randomGaussian()), 150, 100);
      }
  </script>
</body>
</html>

此例除了添加 randomSeed() 指另外, 程式碼與上面範例完全一樣, 但每次重新整理網頁時, 這些亂數出現的序列數值完全一樣, 通常用來作為示範隨機實驗之用, 學習者可以得到與示範者雷同的結果, 下面是此程式第二個隨機序列結果 :




下面是用亂數畫直線的例子 : 



<!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>
    var arr=[0,1,2,3,4,5,6,7,8,9];
    function setup() {
      createCanvas(400, 200);
      strokeWeight(5);  //線條粗細=5px
      frameRate(1);  //圖框率每秒 5 框
      }
    function draw() { 
      background(1, 70, 100);
      for (var i=20; i < width-20; i += 5) {   //掃描 x 軸, 步階=5px
        var r=random(256);     //亂數取得 0~255 之紅色碼
        var g=random(256);    //亂數取得 0~255 之綠色碼
        var b=random(256);    //亂數取得 0~255 之藍色碼
        stroke(r, g, b);     //用亂數設定線條顏色
        line(i, 20, i, 180);    //沿 x 軸 y=180 畫直線
        }
      }
  </script>
</body>
</html>

此例使用 random(256) 產生 0~255 (不含 256) 的 R, G, B 三原色色碼用來設定畫筆的隨機顏色, 然後沿著 y=180 的 x 軸繪製高度 160 px 的直線, 同樣用 frameRate(1) 將圖框率降為每秒一框以免變化太快, 結果如下 : 




下面範例則是在畫布上繪製橢圓, 同樣利用 random() 產生隨機色碼與橢圓寬徑與高徑, 


測試 3-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>
    var arr=[0,1,2,3,4,5,6,7,8,9];
    function setup() {
      createCanvas(400, 300);
      noStroke();
      frameRate(1);
      }
    function draw() { 
      background(1, 70, 100);
      for(var x=20; x<=width-20; x += 20){    //沿 x 軸遞增圓心位置
        for(var y=20; y<=height-20; y +=20){  //沿 y 軸遞增圓心位置
          var r=random(256);     //亂數取得 0~255 之紅色碼
          var g=random(256);    //亂數取得 0~255 之綠色碼
          var b=random(256);    //亂數取得 0~255 之藍色碼
          fill(r, g, b);   //填滿橢圓顏色
          var w=20 * random();    //亂數取得 0~1 隨機值計算橢圓寬徑 
          var h=20 * random();     //亂數取得 0~1 隨機值計算橢圓高徑
          ellipse(x, y, w, h);   //繪製橢圓
          }
        }
      }
  </script>
</body>
</html>

此例同樣用 random(256) 取得亂數 RGB 色碼, 用來傳給 fill() 填滿橢圓顏色; 另外橢圓之寬徑與高徑也各自用一個 random() 取得 0~1 的亂數乘以 20 作為該橢圓之寬徑與高徑, 結果如下 :  




可見每一個圖框中橢圓大小與顏色都不同, 圖框變換時也隨機變換造型. 

上面範例刻意將圖框率放慢以便觀察變化, 下面則以預設每秒 60 圖框來看看用亂數畫直線的跳動情形, 為了簡化起見, 此例只畫一條直線 : 



<!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>
    var x=0;
    function setup() {
      createCanvas(400, 300);
      stroke('yellow');
      strokeWeight(2);
      }
    function draw() { 
      background(1, 70, 100);
      x=random()*width;      //用亂數計算隨機 x 座標
      line(x, 0, x, height);     //繪製垂直線
      }
  </script>
</body>
</html>

結果如下 : 




可見每秒 60 幅的圖框率讓直線看起來像是隨機出現的直線, 變化非常快速且突兀. 

還有一種稱為雜訊或噪音 (noise) 的隨機序列, 其隨機特徵是體現於每次程序的重新執行與無限大的座標空間, 而非程序內的函式呼叫, 沿著座標軸變化所得到的噪音值可用來繪製平滑變化的動態圖.

下面這個是我從書上看到改寫的有趣的範例,  :



<!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, 300);
      }
    function draw() {
      background(1, 70, 100);
      stroke('orange');
      for (var x = 20; x < width; x += 20) {
        var mx = mouseX / 10;          //依據滑鼠 x 座標計算 x 軸偏移量
        var dx1=random(-mx, mx);   //利用正負偏移量取得 x 軸隨機偏移量 (起點)
        var dx2=random(-mx, mx);   //利用正負偏移量取得 x 軸隨機偏移量 (終點)
        line(x + dx1, 20, x - dx2, height - 20);    //利用 x 軸隨機偏移量繪製直線
        }
      }
  </script>
</body>
</html>

此例以滑鼠 x 座標為計算基準, 除以 10 作為偏移量, 然後以正負偏移量用 random() 分別計算直線起訖點的隨機偏移量, 然後以此掃描 x 軸繪製直線, 因此滑鼠在最左邊 x 接近 0, 垂直線很穩定; 當滑鼠越向右, 偏移量的上下範圍越大, 使得直線因為圖框掃描看起來越斗越大, 結果如下 : 





下面則是沿 y 軸抖動的版本 :


 
<!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, 300);
      }
    function draw() {
      background(1, 70, 100);
      stroke('orange');
      for (var y = 20; y < height; y += 20) {
        var my = mouseY / 10;           //依據滑鼠 y 座標計算 y 軸偏移量
        var dy1=random(-my, my);    //利用正負偏移量取得 y 軸隨機偏移量 (起點)
        var dy2=random(-my, my);    //利用正負偏移量取得 y 軸隨機偏移量 (終點)
        line(20, y + dy1, width - 20, y - dy2);     //利用 x 軸隨機偏移量繪製直線
        }
      }
  </script>
</body>
</html>

此例只是把上例的 x, y 對調而已, 結果如下 :





下面是從畫布中央開始隨機畫圓形的範例 :


測試 3-8 : 隨機堆疊的圓 [看原始碼] 


<!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>
    var speed=2;
    var x;
    var y;
    function setup() {
      createCanvas(400, 300);
      background(1, 70, 100);
      stroke('red');
      fill('cyan');
      x=width/2;
      y=height/2;
      }
    function draw() {
      if(x > width - 10) {
        xmin=5 * speed;
        xmax=speed;
        }
      else if(x < 10) {
        xmin=speed;
        xmax=5 * speed;
        }
      else {
        xmin=speed;
        xmax=speed;
        }
      if(y > height - 10) {
        ymin=5 * speed;
        ymax=speed;
        }
      else if(y < 10) {
        ymin=speed;
        ymax=5 * speed;
        }
      else {
        ymin=speed;
        ymax=speed;
        }
      x += random(-xmin, xmax);
      y += random(-ymin, ymax);
      d=random(20);
      circle(x, y, d);
      }
  </script>
</body>
</html>

此例原始構想來自書上, 但書上的範例有缺陷, 隨機圓可能衝出畫布而消失, 即使加上 constrain() 函式來限制也可能讓圓像鬼打牆一樣沿邊跑, 故此處加上 if else 敘述來限制, 當圓心 x 座標靠近畫布右邊界小於 10 px 時就將 random() 的左邊限擴大 5 倍; 反之靠近畫布左邊小於 10 px 時則將 random() 的右邊限擴大五倍, 對於 y 軸也是如此限制, 這樣就不會出界也不會沿邊打轉了, 結果如下 : 





4. 利用雜訊函式畫動態圖 : 

雜訊 (噪音) 的概念與亂數類似, 它是無限多個像波一樣平滑變化的隨機數, 其變化序列較自然和諧與平滑, 不像 randon() 的隨機序列轉變那麼生硬突兀, 其演算法由 Ken Perlin 於 1980 年代所發明, 稱為 Perlin 雜訊, 由於其變化特性較符合自然界的現象, 常用於計算機圖學或動畫製作中. 

p5.js 的雜訊相關函式如下表 : 


 noise(x, [y, z]) 傳回指定座標軸之 Perlin 噪音值 (0~1)
 noiseSeed(seed) 設定雜訊種子以固定傳回的雜訊序列


Perlin 雜訊在理論上是定義於無限的 n 維空間, 但 p5.js 僅提供 1D (x 軸), 2D (x, y 軸), 至多 3D (x, y, x 軸) 的雜訊序列. 

每次傳入相同的座標呼叫 noise() 時, 它將傳回相同的 0~1 之雜訊值; 但若重新執行程式則會得到不同的雜訊值. 如果想要在重新執行程式時得到相同的雜訊序列, 可先呼叫 noiseSeed() 設定雜訊種子. 

由於 noise() 傳回的值是 0~1 的浮點數, 故實務上需要將其放大, 通常會使用映射函式 map() 來達成此目的, 此函式參數如下 :

map(x, amin, amax, bmin, bmax)

其中 x 是要映射的變數, amin 與 amax 是原來的大小範圍, bmin 與 bmax 則是映射後的範圍. 對於 noise() 函式來說就是 map(noise(), 0, 1, bmin, bmax), 例如要將雜訊映射至色碼 (0~255) 的話, 就是 
map(noise(), 0, 1, 0, 255). 



<!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>
    var arr=[0,1,2,3,4,5,6,7,8,9];
    function setup() {
      createCanvas(350, 120);
      background(1, 70, 100);
      fill('orange');
      frameRate(1);
      textSize(16);
      }
    function draw() { 
      background(1, 70, 100);
      text('noise(0.1):', 10, 20);
      text(noise(0.1), 90, 20);                         //1D 雜訊 (x 軸)
      text('noise(0.1):', 10, 40);
      text(noise(0.1), 90, 40);
      text('noise(100, 200):', 10, 60);             //2D 雜訊 (x, y 軸)
      text(noise(100, 200), 130, 60);
      text('noise(1, 2, 3):', 10, 80);    
      text(noise(1, 2, 3), 110, 80);                 //3D 雜訊 (x, y, z 軸)
      text('noise(100, 200, 300):', 10, 100);
      text(noise(100, 200, 300), 170, 100);
      }
  </script>
</body>
</html>

此例測試了 1D/2D/3D 的雜訊值, 結果如下 :




連續呼叫 noise(0.1) 都傳回同樣的值, 且 draw() 迴圈不斷執行這些值都不會變, 可見在同一個程序中傳入相同參數時 noise() 函式會傳回固定的值, 不像 random() 每次呼叫都會傳回不同的值, 但如果重新載入網頁執行新的程序, 則這些值就會改變, 雜訊的隨機性在此. 

如果用 noiseSeed() 設定雜訊種子, 則隨機序列會被固定, 只要除入 noise() 的參數不變, 即使重整網頁啟動新的程序也會得到一樣序列的值, 例如 :



<!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>
    var arr=[0,1,2,3,4,5,6,7,8,9];
    function setup() {
      createCanvas(350, 120);
      background(1, 70, 100);
      fill('orange');
      frameRate(1);
      textSize(16);
      noiseSeed(1);    //設定雜訊種子
      }
    function draw() { 
      background(1, 70, 100);
      text('noise(0.1):', 10, 20);
      text(noise(0.1), 90, 20);
      text('noise(0.1):', 10, 40);
      text(noise(0.1), 90, 40);
      text('noise(100, 200):', 10, 60);
      text(noise(100, 200), 130, 60);
      text('noise(1, 2, 3):', 10, 80);
      text(noise(1, 2, 3), 110, 80);
      text('noise(100, 200, 300):', 10, 100);
      text(noise(100, 200, 300), 170, 100);
      }
  </script>
</body>
</html>

此例與前一個範例的差別是在 setup() 中多了一個 noiseSeed() 設定雜訊種子, 所以重載網頁還是會得到相同的值 (因為傳入參數相同), 關掉網頁重新開啟也是不變, 結果如下 :




接著來看看 noise() 的自然噪音效果與 random() 的隨機效果有何差別. 下面範例改用 noise() 來繪製上面測試 3-4 以 random() 畫的亂數 x 座標直線 : 



<!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>
    var dx=0;
    function setup() {
      createCanvas(400, 300);
      stroke('yellow');
      strokeWeight(2);
      }
    function draw() { 
      background(1, 70, 100);
      x=noise(dx)*width;
      line(x, 0, x, height);
      dx += 0.01;
      }
  </script>
</body>
</html>

此例設定了一個沿 x 軸增量的變數 dx, 傳入 noise() 取得 x 軸的雜訊值 (0~1), 將其乘以 width 後得到直線的 x 座標用來繪製垂直線, 然後在每個 draw() 迴圈中將 dx 增量 0.01, 因此傳入 noise() 的值會是 0, 0.01, 0.02, 0.03, .... 這樣的序列, 這些值會傳回不同的雜訊值, 從而得到不同的 x 座標, 結果如下 : 




可見 noise() 的變化較平滑, 不像 random() 那樣突兀, 適合用來在動畫中模擬動作的推移. 

下面這個測試改編自官網範例, 利用滑鼠座標位置與雜訊繪製隨滑鼠移動而變化之圖形 :



<!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>
    var r=0.02;   //坐標軸縮放比率
    function setup() {
      createCanvas(400, 300);
      }
    function draw() { 
      background('black');
      for (var x=0; x < width; x++) {    //走訪 x 軸
        var n=noise((mouseX + x) * r, mouseY * r);    //計算 2D 雜訊
        stroke(n*255);
        line(x, mouseY + n * 80, x, height);  //畫直線
        }
      }
  </script>
</body>
</html>

此例設定了一個全域變數 r 來調整座標軸縮放比率, 以計算 2D 雜訊值 n, 並利用 n 來設定畫筆灰階顏色, 由於 noise() 會對相同輸入座標傳回同樣的值, 因此滑鼠不動, 圖形也不會動, 結果如下 :




Num Lock 之亂

昨晚正在打 p5.js 筆記時, 不知為何輸入此例的例字時變成 "4ˋ", 檢查輸入法仍然是中文啊, 以為是筆電秀逗了, 今早起床後重新開機, 但怪碼依舊出現. 推敲例字按鍵確實與 "4ˋ" 有關, 看來是在英數字模式 , 就懷疑是否誤按了 Num Lock 鍵 (與 F12 共用), 按了一下 Num Lock 果然就可輸入中文了, 要不是推測正確, 恐怕會被這亂碼磨到瘋掉, 哈哈哈.

我猜有可能是要按 Fn+F12 看網頁原始碼, 但 Fn 鍵沒按確實, 變成 Num Lock 的效果, 這 Num Lock 鍵我根本不會用到, 鍵盤上就已經有數字鍵啊! 真搞不懂還要設這個鍵幹嘛, 徒增疑惑而已. 還好我英明神武一猜中的, 要不然進度不知要被拖多久. (這就是為什麼離開座位最好把筆電蓋上, 否則被貓咪跳上去亂踩也可能踩到 Num Lock 鍵).