Tutorial para fazer Módulos da Óptica de Raios

Este tutorial vai te guiar pelo processi básico de criação de um módulo personalizado, que pode ser completamente feito dentro do 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 controle personalizados, e malhas de objetos. Esse recurso extende a capacidade deste simulador ao combinar, especializar, ou reparametrizar objetos criados por ferramentas existentes para criar novas 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 tambem não existia anteriormente. Além de fazer novas ferramentas, esse recurso também pode fazer algumas demonstrações de óptica mais interativas. Por exemplo, ao arrastar o terceiro ponto de controle 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 que nem todos pontos de controle personalizados requerem um módulo. Alguns casos simples podem ser feitos com o recurso "ponto de mauseio" (ver a seção "Agrupar, rotacionar e ajustar escala de objetos" no popup de ajuda no canto inferior direito do simulador). Já que fazer um módulo é muito mais complicado que criar um ponto de manuseio, deve-se primeiro checar se o seu caso oise ser feito pelo recurso "ponto de manuseio" antes de considerar fazer um módulo. Veja aqui para um exemplo não trivial de um ponto de controle personalizado (mover dois sacos plásticos pra fora da água) sem utilizar um módulo.

Por outro lado, se o seu caso requer programação complicada (como fazer animações, usar algoritmos complicados para colocar elementos ópticos, ou realizar análise em 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 você achar que são úteis para outras pessoas, mesmo se forem feitos com vibe coding!

Usando Assistente de IABeta

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 em sua descrição, e você pode então copiar o código para o editor JSON (ver abaixo). Você 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 arquivos de instrução e conhecimento para o chatbot estão disponíveis aqui. Se você preferir outro serviço LLM, você pode o fornecer esses arquivos para ensiná-lo a escrever módulos de Óptica de Raios.

O editor JSON incluso

Este app atualmente não possui uma interface visual para criação de módulos, então é preciso diretamente editar o JSON da cena.

Você pode habilitar o editor JSON incluso ao clicar no menu "configurações" no canto superior direito do app, e então marcar "Exibir editor JSON". O editor de código deve aparecer no lado esquerdo do app, com o código JSON da cena atual. Certifique-se que a sua tela é grande o suficiente, já que este recurso não funciona bem em dispositivos móveis.

Conforme você 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 você não for familiar com JSON ou qualquer tipo de formato de dados à base de texto, pode ser útil brincar com isso por um tempo.

Em particular, quando se adiciona um objeto à cena, ele é adicionado à lista objs. E se você modificar algumas de suas propriedades para um valor não padrão, elas aparecem como pares chave-valor nesse objeto.

Se você não quiser que a cena seja automaticamente atualizada ao editar o JSON, você pode habilitar Configurações -> Exibir controles da simulação e desabilitar "Atualizar Automaticamente". E quando a edição estiver pronta, clicar "Atualizar Simulação" (ou "Atualizar Cena" se você não quiser que a simulação rode novamente de imediato).

IMPORTANTE: Nessa página de tutorial, se você não ver o editor de código JSON nos iframes abaixo, por favor habilite-o e recarregue esta página, pois vocÊ precisará de ver o código para entender como funciona.

O básico de um módulo

Vamos ver o nosso primeiro exemplo de módulo.

Você deve ver quatro linhas de texto. Olhando no editor JSON, você 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 colocar os objetos dentro do 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 modulesnão é editável pelo editor de cena visual. Então quando se clica em qualquer um dos últimos dois textos nesse exemplo, você 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 torná-los arrastáveis utilizando pontos de controle mais tarde.

Se você selecionar um objeto de módulo, haverá um botão "Demodulizar" na barra do objeto. Clicar nele irá "expandir" o objeto de módulo em suas partes constituintes, e objs agora irá conter todos os quatro textos. Essa operação não é reversível (mas você pode clicar em "desfazer").

A maneira sugerida de se 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.

Adicionando parâmetros

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 strings "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 string (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), você precisa transformá-la em string para que você 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 você 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 controle deslizante.

Adicionando pontos de controle

Para fazer o objeto do módulo arrastável, precisamos de parametrizar os objetos dentro do módulo usando um conjunto de pontos de controle. Vejamos o exemplo

Aqui modules.ModuleName.numPoints define o número de pontos de controle. As coordenadas dos pontos de controle 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 seção anterior. Note que o índice começa de 1.

Os valores reais das coordenadas dos pontos de controle 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 controle, cada um exibido como dois círculos cinza concêntricos na cena. Se você arrastar em outro lugar no objeto do módulo(como os rótulos de texto), todos os pontos de controle vão mover juntos.

Já que o nosso objeto de módulo agora pode se mover, 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 você pode selecionar isso e clicar em dois pontos no espaço vazio em sequiência para criar outra instância do módulo. Você também pode usar o botão "duplicar" na barra do objeto.

Listas e condicionais

Um módulo mais complicado pode ser construído usando listas e condicionais. Vejamos o exemplo.

Dentro de modules.ExampleModule.objs, quaisquer objetos em uma lista podem ter duas chaves especiais : "for" e "if". O valor da chave "for"é oum uma string no formato "name=start:step:end" definindo uma variável de loop, ou uma lista de várias strings 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 string representando uma expressão de math.js que resulta em um booleano, e o objeto é incluso na lista se, e somente se o booleano for verdadeiro.

Para previnir loop infinito acidental, o número total de iterações de cada loop "for"é limitado pela propriedade maxLoopLength da definição do módulo, cujo valor padrão é 1000. Você pode definir essa propriedade como um valor maior, se necessário.

Usando variáveisBeta

Você 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 strings, cada uma representando uma instrução math.js que define uma variável ou função. Essas 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 controle, e, uma vez definidas, essas variáveis podem ser usadas em modelos de objeto assim como parâmetros e coordenadas de ponto de controle, possibilitndo cálculos mais complexos e reutilizáveis.

Objetos com equações personalizadas inclusasBeta

Para objetos que já possuem entrada de equação personalizada (como Espelho -> Equação Personalizada), a propriedade de equação no JSON é uma sequência de caracteres representando uma equação LaTeX. Para representar essa equação em math.js e se referir a parâmetros de módulo, etc, você precisa de envolver a expressão math.js em um par de crases duplas. Esse bloco expande para uma equação LaTeX com variáveis livres como \(x\) para x e \(\theta_0\) para theta_0, que é diferente de um bloco de crase único que não expera variáveis livres e se expande como número. Aqui está um exemplo de espelho de equação personalizada contendo um parâmetro personalizado phi

Por mais que você possa usar qualquer sintaxe e operações math.js em variáveis e blocos de crase única, para blocos de crase dupla, somente as contrapartes math.js das constantes, operadores e funções suportadas pela entrada de equação do objeto alvo são permitidas (veja a caixa de informação do objeto alvo). Você pode usar funções personalizadas definidas na lista vars, mas a s definições de função deve consistir apenas dessas funções permitidas.

Multiplicação implícita (ex.: 2x) também não é permitida nesse contexto, devido a sua precedência de operadores mal definida em math.js. Por favor, sempre use o operador * explicitamente (ex.: 2*x).

Objetos com parametrização de forma inclusa

Para objetos que já suportam maneiras diferentes de definir 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