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.

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.

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.

Objetos com equações personalizadas inclusas

Para objetos que já tem entrada de equação personalizada (como Espelho -> Equação Personalizada), a propriedade da equação no JSON é uma string representando uma equação LaTeX, ao invés de uma expressão math.js. Para incluir parâmetros personalizados na equação, você deve usar a mesma sintaxe modelo como se a equação LaTeX fosse um texto normal. Então a parte cercada pelos acentos graves está na expressão math.js, enquanto a parte fora está em LaTeX. Os parâmetros do módulo só podem ser acessados na parte math.js, e as variáveis independentes da equação personalizada, (ex.: \(x\)) só podem ser acessadas na parte LaTeX. Aqui está um exemplo gerando um espelho com a equação \(y=\cos(2\pi x+\phi)\), onde \(\phi\) é um parâmetro do módulo

No futuro, talvez exista uma maneira de unificar a entrada da equação.

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