本教學將引導您完成建立自訂模組的基本步驟。這個功能可完全在本模擬器網頁程式中實現,而不需要其他的開發環境。
「模組」是「線光學模擬」的一個目前處於實驗階段的功能,它允許將物件以模組化的方式組合在一起,其中可包含自訂參數、自訂控制點與物件陣列。本功能透過將本模擬器中現有的工具所建立的物件進行組合、特化或重新參數化,來製作新的工具,以擴充本模擬器的功能。例如,CircleSource
模組(請參見工具->其他->匯入模組)將現有的「點光源(<360°)」工具所建立的一系列點光源沿著一個圓形組合在一起,成為一個「圓形光源」工具,這在模擬器中原本並不存在。 FresnelLens
模組則是將「透光物->自訂函數」工具特化,使函數表示菲涅耳透鏡的特定曲線,由切片數參數化,以製作一個特化的「菲涅耳透鏡」工具,這在模擬器中原本也不存在。除了製作新工具外,這個功能還可以使一些光學演示更具互動性。例如,通過拖曳 BeamExpander
模組的第三個控制點,可以直接觀察兩個透鏡的共同焦點位置如何影響光束寬度,而無需分別調整兩個透鏡的焦距。
注意並非所有的自訂控制點都需要模組。一些簡單的情況可能可以通過「控制桿」功能實現(請參見模擬器右下角的幫助彈出窗口中的「群組、旋轉和縮放物件」部分)。由於製作模組比建立控制桿複雜得多,您應該在考慮製作模組之前檢查您的情況是否可以通過「控制桿」功能實現。比如這個例子展示了一個相對複雜但不必使用模組的自訂控制點(將兩個塑料袋從水中移開)。
本程式目前沒有用於建立模組的視覺界面,因此您需要直接編輯場景的 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
陣列中,任何值都可以使用這些參數來表示。在字串中(例如 TextLabel
的 text
屬性)中,帶有參數的數學式被反引號包圍。對於數值參數(例如 TextLabel
的 fontSize
屬性),您需要將其改為字串,以便您可以在其中使用反引號格式。因此每條數學式都被一對反引號和一對引號包圍。這些數學式將使用 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(請使用英文)。