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.
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.
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 modules
nã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.
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.
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.
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.
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.
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