# Poradnik tworzenia modułów

Ten poradnik poprowadzi Cię przez podstawy tworzenia niestandardowego modułu, który można wykonać w całości w aplikacji internetowej, bez potrzeby korzystania z zewnętrznego środowiska programistycznego.

Funkcja "Moduł" w Symulacji Promieni optycznych umożliwia tworzenie modułowych kombinacji obiektów z niestandardowymi parametrami, niestandardowymi punktami kontrolnymi i tablicami obiektów. Funkcja ta rozszerza możliwości tego symulatora poprzez łączenie, specjalizację lub reparametryzację obiektów utworzonych przez istniejące narzędzia w celu stworzenia nowych narzędzi. Na przykład moduł `CircleSource` (patrz Narzędzia -> Inne -> Importuj moduły) łączy szereg źródeł punktowych utworzonych przez istniejące narzędzie "Źródło punktowe (<360°)" wzdłuż okręgu, aby utworzyć narzędzie "Źródło kołowe", które nie istniało w symulatorze. Moduł `FresnelLens` specjalizuje narzędzie "Szkła-> Równanie niestandardowe", dzięki czemu równanie reprezentuje określoną krzywą soczewki Fresnela sparametryzowaną przez liczbę pierścieni, tworząc w ten sposób wyspecjalizowane narzędzie "Soczewka Fresnela", które również nie istniało wcześniej. Oprócz tworzenia nowych narzędzi, funkcja ta może również sprawić, że niektóre demonstracje optyki będą bardziej interaktywne. Na przykład, przeciągając trzeci punkt kontrolny modułu `BeamExpander`, można bezpośrednio zobaczyć, jak położenie wspólnego ogniska dwóch soczewek wpływa na szerokość wiązki, bez konieczności indywidualnego dostosowywania ogniskowych każdej z nich.

Należy pamiętać, że nie wszystkie niestandardowe punkty kontrolne wymagają modułu. Niektóre proste przypadki można osiągnąć za pomocą funkcji "uchwyt" (patrz sekcja "Grupuj, obracaj i skaluj obiekty" w wyskakującym okienku pomocy w prawym dolnym rogu symulatora). Ponieważ tworzenie modułów jest znacznie bardziej skomplikowane niż tworzenie uchwytów, należy najpierw sprawdzić, czy dany przypadek można osiągnąć za pomocą funkcji "uchwyt" przed rozważeniem utworzenia modułu. Zobacz tutaj dla nietrywialnego przykładu niestandardowego punktu kontrolnego (wyciąganie dwóch plastikowych toreb z wody) bez użycia modułu.

## Wbudowany edytor JSON

Ta aplikacja nie posiada obecnie wizualnego interfejsu do tworzenia modułów, dlatego musisz bezpośrednio edytować plik JSON sceny.

Możesz włączyć wbudowany edytor JSON, klikając rozwijaną listę "ustawienia" w prawym górnym rogu aplikacji, a następnie zaznaczając "Pokaż edytor JSON". Edytor kodu powinien pojawić się po lewej stronie aplikacji, z kodem JSON bieżącej sceny. Upewnij się, że masz wystarczająco duży ekran, ponieważ ta funkcja nie działa dobrze na urządzeniach mobilnych.

Podczas edycji sceny za pomocą zwykłego wizualnego edytora scen, kod w edytorze JSON będzie odpowiednio aktualizowany, z podświetloną zmienioną częścią. I odwrotnie, bezpośrednia edycja kodu w edytorze JSON spowoduje odpowiednią aktualizację sceny. Jeśli nie jesteś zaznajomiony z JSON lub jakimkolwiek innym formatem tekstowym danych, możesz chcieć pobawić się nim przez chwilę.

W szczególności, gdy dodajesz obiekt do sceny, jest on dodawany do tablicy `objs`. A jeśli zmienisz niektóre z jego właściwości na wartości inne niż domyślne, pojawią się one jako pary klucz-wartość w tym obiekcie.

WAŻNE: Jeśli na tej stronie samouczka nie widzisz edytora kodu JSON w poniższych ramkach iframe, włącz go i odśwież tę stronę, ponieważ musisz widzieć kod, aby zrozumieć, jak to działa.

## Podstawy modułu

Spójrzmy na pierwszy przykład modułu.

Powinny być widoczne cztery linie tekstu. Patrząc na edytor JSON, zobaczysz, że pierwsze dwa znajdują się bezpośrednio w tablicy `objs` najwyższego poziomu, jak zwykle, ale dwa ostatnie znajdują się w `modules.ExampleModule.objs`.

`Moduł` to słownik, w którym kluczem jest nazwa modułu (w tym przypadku `ExampleModule`), a wartością definicja tego modułu. W szczególności tablica `modules.ExampleModule.objs` opisuje (szablon) obiektów w tym module, co różni się od najwyższego poziomu `objs`, który opisuje obiekty w scenie.

Aby umieścić obiekty w ramach modułu na scenie, potrzebujemy "obiektu modułu" w tablicy najwyższego poziomu `objs`, którym w tym przykładzie jest `objs[2]`, którego typem jest `ModuleObj` i którego właściwością `module` jest nazwa modułu.

Definicja modułu w słowniku `modules` nie jest edytowalna przez wizualny edytor scen. Tak więc, gdy klikniesz dowolny z dwóch ostatnich tekstów w tym przykładzie, wybierasz tylko obiekt modułu, a nie obiekty w module. Ponieważ współrzędne tekstów w definicji modułu w tym przykładzie są współrzędnymi bezwzględnymi, ostatnie dwa teksty nie są przeciągalne. Później dowiemy się, jak uczynić je przeciągalnymi za pomocą punktów kontrolnych.

Jeśli wybierzesz obiekt modułu, na pasku obiektów znajdziesz przycisk "Demodułuj". Kliknięcie go "rozwinie" obiekt modułu na jego części składowe, a `objs` będzie teraz zawierać wszystkie cztery teksty. Ta operacja nie jest odwracalna (ale oczywiście można kliknąć "cofnij").

Obecnie sugerowany sposób tworzenia modułu polega na tym, aby najpierw utworzyć pusty moduł za pomocą edytora JSON, utworzyć kilka obiektów za pomocą edytora scen wizualnych, a następnie wyciąć i wkleić obiekty z `objs` do `modules.ModuleName.objs` za pomocą edytora JSON.

## Dodawanie parametrów

Obiekty w module można zdefiniować za pomocą zestawu parametrów. Przyjrzyjmy się prostemu przykładowi

Tutaj `modules.ModuleName.params` jest tablicą ciągów `"name=start:step:end:default"` definiującą nazwę zmiennych i zakres suwaków. Suwaki pojawiają się na pasku obiektów, gdy obiekt modułu jest zaznaczony.

W tablicy `modules.ExampleModule.objs`, dowolne wartości mogą być wyrażone za pomocą tych parametrów. W ciągu znaków (takim jak właściwość `text` etykiety `TextLabel`), równania zmiennych są otoczone parą znaków odwrotnego apostrofu. W przypadku parametrów liczbowych (takich jak właściwość `fontSize` etykiety `TextLabel`), należy utworzyć ciąg znaków, aby można było użyć w nim formatu odwrotnego apostrofu, więc każde równanie jest otoczone parą znaków odwrotnego apostrofu i parą cudzysłowów. Równanie jest obliczane za pomocą math.js (syntax). Zobacz tam dostępną składnię i funkcje, których można użyć w równaniach.

Rzeczywiste wartości parametrów są przechowywane we właściwości `params` obiektu modułu, który, w przeciwieństwie do definicji modułu, może być bezpośrednio edytowany przez edytor scen za pomocą suwaka.

## Dodawanie punktów kontrolnych

Aby obiekt modułu był przeciągalny, musimy sparametryzować obiekty w module za pomocą zestawu punktów kontrolnych. Spójrzmy na przykład

Tutaj `modules.ModuleName.numPoints` definiuje liczbę punktów kontrolnych. Współrzędne punktów kontrolnych to (`x_1`, `y_1`), (`x_2`, `y_2`), itd. i są używane w taki sam sposób jak parametry w `modules.ExampleModule.objs`, jak opisano w poprzedniej sekcji. Należy zauważyć, że indeks zaczyna się od 1.

Rzeczywiste wartości współrzędnych punktów kontrolnych są przechowywane we właściwości `points` obiektu modułu, które, w przeciwieństwie do zakodowanych na stałe współrzędnych w przykładzie 1, mogą być edytowane przez wizualny edytor sceny poprzez przeciąganie punktów kontrolnych, z których każdy jest wyświetlany jako dwa koncentryczne szare okręgi na scenie. Jeśli przeciągniesz w inne miejsce obiekt modułu (np. przeciągniesz etykiety tekstowe), wszystkie punkty kontrolne przesuną się razem.

Ponieważ nasz obiekt modułu może się teraz poruszać, tworzenie wielu wystąpień jest teraz dość łatwe, jak w zwykłych narzędziach. Nazwa modułu jest wyświetlana w menu Narzędzia -> Inne i można ją wybrać, a następnie kliknąć kolejno dwa punkty w pustej przestrzeni dla dwóch punktów kontrolnych, aby utworzyć kolejne wystąpienie modułu. Możesz także użyć przycisku “Powiel” na pasku obiektów.

## Tablice i instrukcje warunkowe

Bardziej skomplikowany moduł można zbudować przy użyciu tablic i instrukcji warunkowych. Spójrzmy na przykład

W obrębie `modules.ExampleModule.objs`, każdy obiekt w tablicy może mieć dwa specjalne klucze: `"for"` i `"if"`. Wartością klucza `"for"` jest albo łańcuch w formacie `"name=start:step:end"` definiujący zmienną pętli, albo tablica kilku łańcuchów tego formatu opisująca wielowymiarową pętlę. Taki obiekt w tablicy jest powielany kilka razy zgodnie ze zmiennymi pętli. Wartość klucza `"if"` jest łańcuchem reprezentującym wyrażenie math.js, które ma wartość logiczną, a taki obiekt jest zawarty w tablicy wtedy i tylko wtedy, gdy wartość logiczna to prawda.

Aby zapobiec przypadkowemu utworzeniu nieskończonej pętli, całkowita liczba iteracji każdej pętli `"for"` jest ograniczona przez właściwość `maxLoopLength` definicji modułu, której wartość domyślna wynosi 1000. W razie potrzeby można ustawić tę właściwość na większą wartość.

## Obiekty z wbudowanymi niestandardowymi równaniami

W przypadku obiektów, które mają już wprowadzone niestandardowe równanie (takich jak Zwierciadła -> Równanie niestandardowe), właściwość równania w JSON jest ciągiem reprezentującym równanie LaTeX, a nie wyrażenie math.js. Aby uwzględnić parametry niestandardowe w równaniu, należy użyć tej samej składni szablonu, jak gdyby równanie LaTeX było zwykłym tekstem. Tak więc część zamknięta w nawiasach klamrowych jest w wyrażeniu math.js, podczas gdy część na zewnątrz jest w LaTeX. Dostęp do parametrów modułu można uzyskać tylko w części math.js, a dostęp do zmiennych niezależnych równania niestandardowego (np. \(x\)) można uzyskać tylko w części LaTeX. Oto przykład generowania zwierciadła z równaniem \(y=\cos(2\pi x+\phi)\), gdzie \(\phi\) jest parametrem modułu

W przyszłości może pojawić się możliwość ujednolicenia wprowadzania równań.

## Obiekty z wbudowaną parametryzacją kształtu

Dla obiektów, które już obsługują różne sposoby definiowania swojego kształtu (obecnie tylko Szkła -> Soczewka sferyczna). Istnieje specjalna składnia JSON dla takich obiektów, która może być użyta w definicji modułu, nawet jeśli są one zawsze zdefiniowane przez kształt w tablicy `objs` najwyższego poziomu. Oto przykład