「線光學模擬」模組製作教學

本教學將引導您完成建立自訂模組的基本步驟。這個功能可完全在本模擬器網頁程式中實現,而不需要其他的開發環境。

「模組」是「線光學模擬」的一個目前處於實驗階段的功能,它允許將物件以模組化的方式組合在一起,其中可包含自訂參數、自訂控制點與物件陣列。本功能透過將本模擬器中現有的工具所建立的物件進行組合、特化或重新參數化,來製作新的工具,以擴充本模擬器的功能。例如,CircleSource 模組(請參見工具->其他->匯入模組)將現有的「點光源(<360°)」工具所建立的一系列點光源沿著一個圓形組合在一起,成為一個「圓形光源」工具,這在模擬器中原本並不存在。 FresnelLens 模組則是將「透光物->自訂函數」工具特化,使函數表示菲涅耳透鏡的特定曲線,由切片數參數化,以製作一個特化的「菲涅耳透鏡」工具,這在模擬器中原本也不存在。除了製作新工具外,這個功能還可以使一些光學演示更具互動性。例如,通過拖曳 BeamExpander 模組的第三個控制點,可以直接觀察兩個透鏡的共同焦點位置如何影響光束寬度,而無需分別調整兩個透鏡的焦距。

注意並非所有的自訂控制點都需要模組。一些簡單的情況可能可以通過「控制桿」功能實現(請參見模擬器右下角的幫助彈出窗口中的「群組、旋轉和縮放物件」部分)。由於製作模組比建立控制桿複雜得多,您應該在考慮製作模組之前檢查您的情況是否可以通過「控制桿」功能實現。比如這個例子展示了一個相對複雜但不必使用模組的自訂控制點(將兩個塑料袋從水中移開)。

內建的 JSON 編輯器

本程式目前沒有用於建立模組的視覺界面,因此您需要直接編輯場景的 JSON 原始碼。

您可以通過點選網頁介面右上角的「設定」下拉選單,然後勾選「顯示 JSON 編輯器」來啟用內建的 JSON 編輯器。原始碼編輯器會出現在介面的左側,並顯示目前場景的 JSON 原始碼。請確保您有足夠大的螢幕,因為這個功能在行動裝置上效果不佳。

當您使用視覺化場景編輯器修改場景時,JSON 編輯器中的原始碼將自動更新,且更改的部分會突出顯示。反之,直接在 JSON 編輯器中編輯原始碼將相應更新場景。如果您不熟悉 JSON 或任何基於文字的資料格式,您可能會希望花一些時間來熟悉它。

特別是,當您將一個物件新增到場景中時,它將被新增到 objs 陣列中。如果您將某些屬性修改為非預設值,它們將作為該物件的鍵值對出現。

注意:如果您在本教學頁面中看不到下面的 iframe 中的 JSON 編輯器,請啟用它並重新載入本頁,因為您需要查看原始碼以了解它的運作方式。

模組的基本概念

讓我們看第一個例子。

您會看到四段文字。通過查看 JSON 編輯器,您將看到前兩段直接位於頂層的 objs 陣列中,但後兩段位於 modules.ExampleModule.objs 中。

module 是一個字典,其中鍵是模組的名稱(在本例中是 ExampleModule),值是該模組的定義。特別是,modules.ExampleModule.objs 陣列描述了該模組中的物件(模板),這與描述場景中的物件的頂層 objs 陣列不同。

要將模組中的物件放到場景中,我們需要一個「模組物件」,它位於頂層 objs 陣列中,本例中是 objs[2],其類型是 ModuleObj,其 module 屬性是模組的名稱。

modules 字典中的模組定義無法由視覺化場景編輯器編輯。因此,當您點選本例中的後兩段文字時,您只選擇了模組物件,而不是模組中的物件。由於本例中模組定義中的文字座標是絕對座標,因此後兩段文字無法被拖曳。我們將在後面學習到如何使用控制點來使它們可被拖曳。

當您選取模組物件時,物件欄上會有一個「取消模組化」按鈕。點選後會將模組物件「展開」為構成此模組的物件,此時 objs 將包含所有四段文字。這個操作是不可逆的(除非按「復原」)。

目前建議製作模組的方式為,首先使用 JSON 編輯器建立一個空的模組,然後使用視覺化場景編輯器新增一些物件,最後使用 JSON 編輯器將這些物件從 objs 剪下並貼上到 modules.ModuleName.objs 中。

新增參數

模組中的物件可以由一組參數來定義。讓我們看一個簡單的例子:

這裡 modules.ModuleName.params 是一個由形如 "名稱=起始值:增量:終止值:預設值"的字串構成的陣列,定義了變數的名稱和數值滑桿的範圍。當選取模組物件時,滑桿將出現在物件欄上。

modules.ExampleModule.objs 陣列中,任何值都可以使用這些參數來表示。在字串中(例如 TextLabeltext 屬性)中,帶有參數的數學式被反引號包圍。對於數值參數(例如 TextLabelfontSize 屬性),您需要將其改為字串,以便您可以在其中使用反引號格式。因此每條數學式都被一對反引號和一對引號包圍。這些數學式將使用 math.js 來計算(https://mathjs.org/docs/reference/functions/evaluate.html)。請參見該網頁以了解您可以在數學式中使用的語法和函數。

參數的實際值位於模組物件的 params 屬性中。這部分與模組定義不同,其可以通過滑桿直接由視覺化場景編輯器編輯。

新增控制點

為了使模組物件可被拖曳,我們需要使用一組控制點來對模組中的物件位置進行參數化。讓我們看一個例子:

這裡 modules.ModuleName.numPoints 描述了控制點的數量。控制點座標之符號為 (x_1, y_1)、(x_2, y_2) 等等。其用在 modules.ExampleModule.objs 中的方式與前一節所述之參數相同。請注意索引從 1 開始。

控制點的實際值位於模組物件的 controlPoints 屬性中,其可以直接由視覺化場景編輯器編輯,而非如第一個例子中寫死在模組定義中的座標。每個控制點在場景中顯示為兩個同心的灰色圓形,並且可以被拖曳。若您拖曳模組物件的其他地方(如文字標籤),則所有的控制點會一起移動。

由於我們的模組物件已經可以移動,我們可以很容易地建立多個實例,就像在本模擬器中其他的工具中一樣。模組的名稱顯示在工具->其他選單中,您可以選擇它,然後按兩個點來指定兩個控制點的位置,就可建立此模組的另一個實例。您也可以使用物件欄上的「複製」按鈕。

陣列與條件

使用陣列和條件語法,可以建立更複雜的模組。讓我們看一個例子:

modules.ExampleModule.objs 中,任何陣列中的物件都可以有兩個特殊的鍵: "for""if""for" 鍵的值是一個描述迴圈變數的字串,格式為 "名稱=起始值:增量:終止值",或者是一個包含多個這種格式的字串的陣列,描述多維迴圈。這樣的物件在陣列中根據迴圈變數被複製多次。"if" 鍵的值是則是一個表示布林值的字串,使得該物件只在布林值為真時才會包含在陣列中。

為了防止意外的無窮迴圈,每個 "for" 迴圈的總迭代次數由模組定義中的 maxLoopLength 屬性限制,其預設值為 1000。如果需要,您可以將此屬性設定為更大的值。

內建自訂數學式功能的物件

對於已經具有自訂數學式輸入的物件(例如鏡子 -> 自訂函數),JSON 中的數學式屬性是一個表示 LaTeX 數學式的字串,而不是 math.js 表達式。要在數學式中包含自訂參數,您必須將 LaTeX 數學式視為普通字串,並使用普通字串的模板語法。因此,反引號括起來的部分是 math.js 表達式,而反引號外部的則是 LaTeX 數學式。模組參數只能在 math.js 的部分中使用,而物件內建的自訂函數的自變量(例如 \(x\))只能在 LaTeX 的部分中使用。以下例子產生一個形狀為 \(y=\cos(2\pi x+\phi)\) 的鏡子,其中 \(\phi\) 為模組參數:

未來可能會有一種統一的方式來輸入數學式。

內建形狀參數化功能的物件

對於已經支援使用不同方式定義其形狀的物件(目前僅有透光物 -> 球面透鏡),有特殊的 JSON 語法可用於在模組定義中用這些方式來定義該物件,即使在頂層 objs 陣列中這類的物件總是以形狀來定義。以下是一個例子:

貢獻

欲貢獻您的模組,請參見貢獻模組

討論

見 GitHub 上的 Discussion #145(請使用英文)。