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

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

「線光學模擬」的「模組」功能允許將物件以模組化的方式組合在一起，其中可包含自訂參數、自訂控制點與物件陣列。本功能透過將本模擬器中現有的工具所建立的物件進行組合、特化或重新參數化，來製作新的工具，以擴充本模擬器的功能。例如，`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` 陣列中，任何值都可以使用這些參數來表示。在字串中（例如 `TextLabel` 的 `text` 屬性）中，帶有參數的數學式被反引號包圍。對於數值參數（例如 `TextLabel` 的 `fontSize` 屬性），您需要將其改為字串，以便您可以在其中使用反引號格式。因此每條數學式都被一對反引號和一對引號包圍。這些數學式將使用 math.js 來計算（語法）。請參見該網頁以了解您可以在數學式中使用的語法和函數。

參數的實際值位於模組物件的 `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` 陣列中這類的物件總是以形狀來定義。以下是一個例子