Este tutorial vai guiar-lo pelo processo básico de criação de um módulo personalizado, que pode ser completamente feito dentro da app web, sem necessidade de qualquer ambiente de desenvolvimento externo.
O recurso "Módulo" da Simulação de Óptica de Raios permite a criação de combinações modulares de objetos com parâmetros personalizados, pontos de controlo personalizados e malhas de objetos. Este recurso estende a capacidade deste simulador ao combinar, especializar, ou reparametrizar objetos criados por ferramentas existentes para criar ferramentas. Por exemplo, o módulo CircleSource
(ver Ferramentas -> Outros -> Importar módulo) combina uma série de fontes pontuais criadas pela ferramenta existente "Fonte pontual (<360°)" ao redor de um círculo, para fazer uma ferramenta "fonte circular" que não existia no simulador. O módulo FresnelLens
especializa a ferramenta "Glass->Custom equation", para que a equação represente uma curva específica da lente Fresnel parametrizada pelo número de fatias, assim fazendo uma ferramenta especializada "Lente Fresnel", que também não existia anteriormente. Além de fazer novas ferramentas, este recurso também pode fazer algumas demonstrações de óptica mais interativas. Por exemplo, ao arrastar o terceiro ponto de controlo do módulo BeamExpander
, pode-se ver diretamente como a posição do ponto focal em comum das duas lentes afeta a largura do raio, sem necessitar ajustar os comprimentos focais das duas lentes individualmente.
Note that not all custom control points require a module. Some simple cases can be achieved by the "handle" feature (see the "Group, rotate, and scale objects" section in the help popup at the bottom right corner of the simulator). Since making a module is much more complicated than creating a handle, you should first check if your case can be achieved by the "handle" feature before considering making a module. See here for a non-trivial example of a custom control point (moving two plastic bags out of water) without using a module.
Por outro lado, se o seu caso requer programação complicada (como fazer animações, usar algoritmos complicados para posicionar elementos óticos, ou analisar mapas de irradiância), por favor use as ferramentas de integração e escreva programas em Python ou Julia.
Sinta-se bem-vindo para contribuir os seus módulos para a lista de "Importar módulos" se achar que são úteis para outras pessoas, mesmo se forem feitos com vibe coding!
O Ray Optics Coder no ChatGPT pode ajudá-lo a escrever módulos de Óptica de Raios. Ele gera o código JSON para a cena contendo o módulo baseado na sua descrição e depois pode copiar o código para o editor JSON (ver abaixo). Também pode usá-lo para editar módulos existentes ou modularizar cenas.
O código gerado pelo chatbot pode não funcionar diretamente. Se um erro ocorrer, copiar e colar a mensagem de erro para o chatbot pode ajudar. Para módulos mais complicados, pode ser útil ler o tutorial abaixo e escrever o código ao menos parcialmente por conta própria.
Os ficheiros de instrução e conhecimento para o chatbot estão disponíveis aqui. Se preferir outro serviço LLM, pode fornecer esses ficheiros para ensiná-lo a escrever módulos de Ótica de Raios.
Esta app atualmente não possui uma interface visual para criação de módulos, então é preciso diretamente editar o JSON da cena.
Pode ativar o editor JSON incluso ao clicar no menu "configurações" no canto superior direito da app e depois marcar "Exibir editor JSON". O editor de código deve aparecer no lado esquerdo da app, com o código JSON da cena atual. Certifique-se que o seu ecrã é grande o suficiente, já que este recurso não funciona bem em dispositivos móveis.
Conforme edita a cena utilizando o editor visual de cena, o código no editor JSON vai atualizar automaticamente, com a parte modificada destacada. Do mesmo modo, editar o código no editor JSON diretamente vai atualizar a cena automaticamente. Se não conhecer JSON ou qualquer tipo de formato de dados à base de texto, pode ser útil brincar com isso por algum tempo.
Em particular, quando adiciona um objeto à cena é adicionado à lista objs
. E se modificar algumas das suas propriedades para um valor não padrão, elas aparecem como pares chave-valor nesse objeto.
If you do not want the scene to be automatically updated when you edit the JSON, you can enable Settings -> Show simulator controls and disable "Auto Refresh". And when the editing is done, click "Refresh Simulation" (or "Refresh Scene" if you don't want the simulation to be rerun immediately).
IMPORTANTE: Nessa página de tutorial, se não ver o editor de código JSON nos iframes abaixo, por favor ative-o e recarregue esta página, pois precisará de ver o código para perceber como funciona.
Vamos ver o nosso primeiro exemplo de módulo.
Deve ver quatro linhas de texto. Olhando no editor JSON, verá que as primeira duas estão diretamente na lista objs
de nível superior como normal, mas as últimas duas estão em modules.ExampleModule.objs
ao invés disso.
O modules
é um dicionário onde a chave é o nome do módulo (neste caso ExampleModule
) e o valor é a definição do módulo. Em específico, a lista modules.ExampleModule.objs
descreve (o padrão de) objetos dentro desse módulo, que é diferente do objs
do nível superior que descreve objetos na cena.
Para posicionar os objetos no módulo na cena, precisamos de um "objeto do módulo" na lista objs
de nível superior, que é objs[2]
nesse exemplo, cujo tipo é ModuleObj
e cuja propriedade module
é o nome do módulo.
A definição de módulo no dicionário modules
não é editável pelo editor de cena visual. Então, quando clicar em qualquer um dos últimos dois textos nesse exemplo, seleciona apenas o objeto do módulo e não os objetos no módulo. Já que as coordenadas dos textos na definição do módulo neste exemplo são coordenadas absolutas, os últimos dois textos não são arrastáveis. Aprenderemos como fazê-los arrastáveis através de pontos de controlo mais tarde.
Se selecionar um objeto de módulo, haverá um botão "Demodulizar" na barra do objeto. Clicar nele irá "expandir" o objeto de módulo nas suas partes constituintes e objs
agora irá conter todos os quatro textos. Esta operação não é reversível (mas pode clicar em "desfazer").
A maneira sugerida de criar um módulo atualmente é primeiro criar um módulo vazio utilizando o editor JSON, criar alguns objetos usando o editor de cena visual e então cortar e colar os objetos de objs
para modules.ModuleName.objs
utilizando o editor JSON.
Os objetos dentro do módulo podem ser definidos por um conjunto de parâmetros. Vamos ver um exemplo simples
Aqui, modules.Modulename.params
é uma lista de cadeias "name=start:step:end:defaul"
defining the name of the variables and the range of the sliders. The sliders appear on the object bar when the module object is selected.
Dentro da lista modules.ExampleModule.objs
, quaisquer valores podem ser expressados usando esses parâmetros. Dentro de uma cadeia (como a propriedade text
de um TextLabel
), as equações das variáveis são cercadas por um par de acentos graves. Para parâmetros de número(como a propriedade fontSize
de um TextLabel
), precisa transformá-la em cadeia para que possa usar o formato de acento grave, então cada equação é cercada por um par de acentos graves e um par de aspas. As equações são avaliadas com math.js (syntax). Veja lá a sintaxe disponível e funções que pode usar nas equações.
Os valores reais dos parâmetros ficam armazenados na propriedade params
do objeto do módulo, que, diferentemente da definição do módulo, pode ser diretamente editada pelo editor de cena usando o controlo deslizante.
Para fazer o objeto do módulo arrastável, precisamos de parametrizar os objetos dentro do módulo usando um conjunto de pontos de controlo. Vejamos o exemplo
Aqui modules.ModuleName.numPoints
define a quantidade de pontos de controlo. As coordenadas dos pontos de controlo são (x_1
, y_1
), (x_2
, y_2
), etc. e são utilizadas do mesmo modo que os parâmetros dentro de modules.ExampleModule.objs
como descrito pela secção anterior. Note que o índice começa de 1.
Os valores reais das coordenadas dos pontos de controlo são armazenados na propriedade points
do objeto do módulo, que, diferentemente das coordenadas definidas diretamente no Exemplo 1, podem ser editadas no editor visual de cena arrastando os pontos de controlo, cada um exibido como dois círculos cinzento concêntricos na cena. Se arrastar para outro lugar no objeto do módulo (como os rótulos de texto), todos os pontos de controlo vão mover juntos.
Já que o nosso objeto de módulo agora pode mover-se, agora é bem fácil de se criar múltiplas instâncias como as ferramentas normais. O nome do módulo é exibido no menu Ferramentas - Outros e pode selecionar isso e clicar em dois pontos no espaço vazio em sequência para criar outra instância do módulo. Também pode usar o botão "duplicar" na barra do objeto.
Um módulo mais complicado pode ser construído usando listas e condicionais. Vejamos o exemplo.
Em modules.ExampleModule.objs
, quaisquer objetos numa lista podem ter duas chaves especiais : "for"
e "if"
. O valor da chave "for"
ou é uma cadeia no formato "name=start:step:end"
definindo uma variável de loop, ou uma lista de várias cadeias desse formato descrevendo um loop multidimensional. Tal objeto na lista é duplicado várias vezes de acordo com as variáveis do loop. O valor da chave "if"
é uma cadeia representando uma expressão de math.js que resulta num booleano e o objeto é incluso na lista se, e somente se o booleano for verdadeiro.
Para prevenir um loop infinito acidental, a quantidade total de iterações de cada loop "for"
é limitado pela propriedade maxLoopLength
da definição do módulo, cujo valor padrão é 1000. Pode definir esta propriedade como um valor maior, se necessário.
Pode definir variáveis matemáticas e funções que podem ser usadas no seu módulo. Vamos ver um exemplo
modules.ModuleName.vars
é uma lista de cadeias, cada uma representando uma instrução math.js que define uma variável ou função. Estas definições são avaliadas sequencialmente, então definições posteriores podem referenciar anteriores.
Definições de variáveis tem acesso total a todos os parâmetros e coordenadas de ponto de controlo e, uma vez definidas, estas variáveis podem ser usadas em modelos de objeto assim como parâmetros e coordenadas de ponto de controlo, possibilitndo cálculos mais complexos e reutilizáveis.
For objects that already have custom equation input (such as Mirror -> Custom Equation), the equation property in the JSON is a string representing a LaTeX equation. To represent such an equation in math.js and refer to module parameters, etc, you need to wrap the math.js expression in a pair of double backticks. Such a block expands to a LaTeX equation with free variables like \(x\) for x
and \(\theta_0\) for theta_0
, which is different from a single-backtick block which expects no free variables and expands to a number. Here is an example of a custom equation mirror containing a custom parameter phi
Although you can use any math.js syntax and operations in variables and single-backtick blocks, for double-backtick blocks, only the math.js counterparts of the constants, operators, and functions supported by the equation input of the target object are allowed (see the info box of the target object). You can use custom functions defined in the vars
array, but the function definitions must ultimately consist of those allowed operations.
Implicit multiplication (e.g. 2x
) is also not allowed in this context, due to its ill-defined operator precedence in math.js. Please always use the *
operator explicitly (e.g. 2*x
).
Para objetos que já suportam maneiras diferentes de definir a sua forma (atualmente só Vidro -> Lente esférica). Existe sintaxe JSON especial para esses objetos que podem ser usados dentro da definição do módulo, mesmo que sejam sempre definidos por forma na lista objs
de nível principal. Aqui está um exemplo