Física Computacional - FSC-5705

só um divisor

Visual Python

O visual Python o Vpython, como é conhecido, é uma biblioteca mantida principalmente por Bruce Sherwood um Físico preocupado com criar um ambiente de programação 3D simples que permita poder dar aulas de Física I, II, III e IV utilizando programas animados (ver Matter & Interactions). O resultado desse esforço resultou em um software livre, simples e extremadamente útil para o estudo de Física, se comparado com outras opções (a pior de todas opengl).

Figura 01: logo marca do Vpython.

Para usar o Vpython você tem que ter um computador com suporte a aceleração gráfica, computadores novos em geral tem algum suporte. Se você tem instalado um debian o derivado você deve instalar os drivers adequados (o da ATI se chama fglrx e o Nvidia, no ubuntu, se chama nvidia-current) isso aumenta a performance das maquinas. Se utiliza ubuntu instale o mesa-utils e teste a performance, por exemplo:

[usuario@python: ]# sudo apt-get install mesa-utils
[sudo] password for usuario:
[usuario@python: ]# glxspheres
Polygons in scene: 62464
Visual ID of window: 0x20
Context is Direct
OpenGL Renderer: GeForce GT 540M/PCIe/SSE2
128.465120 frames/sec - 143.367074 Mpixels/sec
100.307232 frames/sec - 111.942871 Mpixels/sec
97.568290 frames/sec - 108.886212 Mpixels/sec
rodando glxspheres
Figura 02: Teste de performance da placa de vídeo.

Essa configuração é suficiente para rodar, no Lab. de informática da Física a performance pode ser 10 vezes maior.

O universo

sistema de coordenadasQuando você faz um desenho com Vpython por padrão ele coloca o centro do eixo de coordenadas no meio da janela. A orientação dos eixos é tal que o eixo $z$ positivo está "saindo do plano" definido pela tela do computador, o eixo $x$ positivo está para a direita e o $y$ positivo para acima.

Definindo a cena

Por cena me refiro as propriedades da janela que será utilizada para apresentar a animação. Ainda que não seja obrigatório definir as propriedades da cena, a sua definição dá um controle refinado do aspecto que terá a animação, mas repetindo, não é necessário pois por padrão o Vpython ajusta o tamanho da cena para se adequar aos parâmetros utilizados na simulação.

Para definir a cena utilizamos a função scene, essa função define varias propriedades que podem ser alteradas, mas estamos interessados, no momento, só por alguma delas: posição $(x,y)$ no monitor onde aparecera a janela (x e y), altura da janela (height), largura (width), posição de observação da câmera ou centro (center), cor de fundo (background), titulo da janela (title), a alcance da região de interesse (range e/ou scale). No exemplo a seguir vou mostrar o desenho

      #!/usr/bin/env python
      #-*- coding: utf-8 -*-

      import visual as vis

      #Forma extendida de definir a cena
      vis.scene.title      = "Nossa primeira scena"
      vis.scene.x          = 0
      vis.scene.x          = 0
      vis.scene.width      = 400
      vis.scene.height     = 400
      vis.scene.background = (0,0,0)
      vis.scene.center     = (0,0,0)
      vis.scene.range      = (10,10,10)

      #Define o espaço de coordenadas
      vis.arrow(pos=(0,0,0), axis=(10,0,0), shaftwidth=0.5)
      vis.arrow(pos=(0,0,0), axis=(0,10,0), shaftwidth=0.5)
      vis.arrow(pos=(0,0,0), axis=(0,0,10), shaftwidth=0.5)

      #define os nomes dos eixos
      vis.label(pos=(9,0,1), box=0, opacity=0, text='X')
      vis.label(pos=(1,9,0), box=0, opacity=0, text='Y')
      vis.label(pos=(1,0,9), box=0, opacity=0, text='Z')
      
definindo o universo
Figura 04: colocando o centro na origem: (a) saída do script, (b) girando a cena com o botão direito do mouse.

A fim de entender a cena também são desenhados os eixos coordenados e os nomes dos eixos. Para rotar uma cena basta clicar com o botão direito do mouse e rotar para onde você desejar. Observe a cor do fundo, ela está dada em porcentagem de (vermelho, verde, azul), por isso se chama de RGB, onde ela é contada de $0$ até $1$ (100%), nessa nomenclatura preto é $(0,0,0)$ e branco $(1,1,1)$. Observe que o centro foi definido no centro do sistema de coordenadas o alcance (range) foi colocado em 10 para cada lado, isso é como supor que todo o universo está dentro de um cubo de (10,10,10). Observe a posição da janela definida com $x$ e $y$, a origem desse sistema é o canto superior direito do monitor, $x$ é positivo para direita e $y$ é positivo para baixo; a quantidade de pontos no monitor está dado pela resolução que monitor consegue suportar, atualmente quase todos os monitores suportam $1024\times 768$ mas um full HD vai para $1920\times 1080$. As vezes só queremos ver o movimento no plano, nesse caso poderíamos definir o centro $(x_{max}/2, y_{max}/2, 0)$ e o alcance em $(x_{max}/2, y_{max}/2, z_{max})$, no exemplo acima se colocamos vis.scene.center = (5,5,0) e vis.scene.range = (5,5,10) obteríamos:

definindo o universo
Figura 05: colocando o centro no meio do plano xy: (a) saída do script, (b) girando a cena com o botão direito do mouse.

Existe uma forma rápida para definir todas as propriedades da cena e esse forma é ilustrada no script a seguir:

      #!/usr/bin/env python
      #-*- coding: utf-8 -*-

      import visual as vis

      #forma compacta de definir a cena
      vis.display(title="Nossa primeira scena", x=0, y=0,\
        width=400, height=400, background=(0,0,0),\
        center=(0,0,0), range=(10,10,10))

      #Define o espaço de coordenadas
      vis.arrow(pos=(0,0,0), axis=(10,0,0), shaftwidth=0.5)
      vis.arrow(pos=(0,0,0), axis=(0,10,0), shaftwidth=0.5)
      vis.arrow(pos=(0,0,0), axis=(0,0,10), shaftwidth=0.5)

      #define os nomes dos eixos
      vis.label(pos=(9,0,1), box=0, opacity=0, text='X')
      vis.label(pos=(1,9,0), box=0, opacity=0, text='Y')
      vis.label(pos=(1,0,9), box=0, opacity=0, text='Z')
      

O resultado é exatamente o mesmo com a única diferencia aparente de ficar mais compato mas na verdade esse forma de definir tem uma outra vantagem ainda maior, trabalhar com varias cenas. Todos os objetos tem a propriedade poder ser definido em qual cena estão alocados, para isso basta definir display adequado, caso isso não seja feito ele será colocado na última cena definida, vejamos um exemplo:

      #!/usr/bin/env python
      #-*- coding: utf-8 -*-

      import visual as vis

      #definindo janela 1
      cena1 = vis.display(title="Janela 1", x=0, y=0,\
        width=400, height=400, background=(0,0,0),\
        center=(0,0,0), range=(10,10,10))

      #definindo janela 2
      cena2 = vis.display(title="Janela 2", x=410, y=0,\
        width=400, height=400, background=(0,0,0),\
        center=(0,0,0), range=(10,10,10))

      #colocando o sistema de coordenadas na cena1
      vis.arrow(pos=(0,0,0), axis=(10,0,0), shaftwidth=0.5, display=cena1)
      vis.arrow(pos=(0,0,0), axis=(0,10,0), shaftwidth=0.5, display=cena1)
      vis.arrow(pos=(0,0,0), axis=(0,0,10), shaftwidth=0.5, display=cena1)

      vis.label(pos=(9,1,0), box=0, opacity=0, text='X', display=cena1)
      vis.label(pos=(1,9,0), box=0, opacity=0, text='Y', display=cena1)
      vis.label(pos=(1,0,9), box=0, opacity=0, text='Z', display=cena1)

      #colocando o sistema de coordenadas na cena2
      vis.arrow(pos=(0,0,0), axis=(10,0,0), shaftwidth=0.5, display=cena2)
      vis.arrow(pos=(0,0,0), axis=(0,10,0), shaftwidth=0.5, display=cena2)
      vis.arrow(pos=(0,0,0), axis=(0,0,10), shaftwidth=0.5, display=cena2)

      vis.label(pos=(9,1,0), box=0, opacity=0, text='X', display=cena2)
      vis.label(pos=(1,9,0), box=0, opacity=0, text='Y', display=cena2)
      vis.label(pos=(1,0,9), box=0, opacity=0, text='Z', display=cena2) 
      
duas janelas
Figura 06: Implementando duas janelas.

Trabalhando com vetores

Como vimos nos exemplos anteriores podemos definir objetos dentro de nossa cena. Todos esses objetos tem em comum que foi utilizado um trio de número para definir a suas propriedades, por exemplo a zeta que representa o eixo $x$ na cena 2 no último programa define a posição do inicio como sendo $(0,0,0)$ e do fim como sendo $(10,0,0)$ - linha 26 - onde se coloca a cabeça da zeta. Esse trio de número são vetores de Vpython, na verdade a linha 26 do último script deveria ter sido escrita como

      vis.arrow(pos=vis.vector(0,0,0), axis=vis.vector(10,0,0),\
        shaftwidth=0.5, display=cena2)
      

onde se utiliza explicitamente um vetor Vpython. Afortunadamente o Vpython entende que você quer definir um vetor e ele converte o trio de número para objetos vetoriais. Como vetores são os "números" naturais dentro de um sistema 3D e devido a que Vpython é pensado para trabalhar com simulações Física, são definidas as principais operações matemática que se realizam com vetores como:

formas geométrica

O Vpython define algumas formas geométricas uteis para a criação de animações e para utilizar alguma delas basta digitar o nome e definir as propriedades relevantes à forma, por exemplo, posição, largura, comprimento, etc. Ainda que cada forma possua um conjunto próprio de parâmetros que a define, todas compartilham algumas propriedades em comum:

onde vis é o nome com que o módulo visual foi importado. Agora vejamos individualmente cada um dos objetos

Setas

A seta é um elemento essencial para animações feitas por um físico pois são o simbolo de um vetor. A definição da seta é

vec1 = vis.arrow(pos=(1,1,1), axis=(1,0,0), shaftwidth=1)

aqui pos e axis são vetores Vpython, e dizem respeito à posição do rabinho e comprimento (é direção) da seta, respetivamente. shaftwidth informa a largura de toda a seta, por padrão a largura é 0.1*comprimento.

Preste atenção ao fato de que axis não é o ponto final do vetor, é como se fosse o vetor traçado desde a origem, $(0,0,0)$ até o ponto axis e deslocado em pos tanto o ponto inicial como o final. Veja o exemplo para esclarecer.

Note que você pode atribuir o objeto a uma variável, assim se durante a simulação você quer mudar de posição o objeto somente tem que escrever: vec1.pos = vis.vector(2,2,2), ou simplesmente, vec1.pos = (2,2,2), por exemplo.

      #!/usr/bin/env python
      #-*- coding: utf-8 -*-

      import visual as vis

      vis.display(title="Soma de vetores", x=0, y=0,\
        width=400, height=400, background=(1,1,1))

      origem = vis.vector(0,0,0)
      a      = vis.vector(1,1,1)
      b      = vis.vector(-1,1,-1)
      c      = a + b

      modCd2 = vis.mag(c) / 2.0
      centro = vis.vector(modCd2, modCd2, modCd2)

      vis.display(title="Soma de vetores", x=0, y=0,\
        width=400, height=400, background=(1,1,1), \
        center=centro, range=centro )

      print c

      vecA = vis.arrow(pos=origem, axis=a, color=(1,0,0), shaftwidth=0.2)
      vecB = vis.arrow(pos=a,      axis=b, color=(0,1,0), shaftwidth=0.2)
      vecC = vis.arrow(pos=origem, axis=c, color=(0,0,1), shaftwidth=0.2)      
      
[usuario@python: ]# python somaVetor.py
<0, 2, 0>
soma de vetores
Figura 07: Soma de vetores.

Cubos

Para definir um cubo utilizamos o comando box:

caixa = box(pos=(xo, yo, zo), axis=(a,b,c), length=comprimento,
                     height=
altura, width=largura
)

definição do cubo
Figura 08: propriedades do cubo.

onde pos determina a posição do centro do cubo, axis é um vetor na direção do comprimento (length) da caixa. Se axis não é definido equivale a colocar axis=(L,0,0). Se length não é definido então ele tem o comprimento do vetor axis.

      #!/usr/bin/env python
      #-*- coding: utf-8 -*-

      import visual as vis
      import numpy as np

      centro      = vis.vector(0.0,0.0,0.0)
      centroCena  = centro
      larguraCena = vis.vector(2.0, 2.0, 2.0)

      cena1 = vis.display(title="cubo (1,0,0)", x=0, y=0,\
        width=400, height=400, background=(1,1,1), \
        center=centroCena, range=larguraCena)

      #cria vetores que representam o eixo de coordenadas
      vecX1 = vis.arrow(pos=centro, axis=(1.0,0,0), color=(1,0,0), \
                        shaftwidth=0.025, display=cena1)
      vecY1 = vis.arrow(pos=centro, axis=(0,1.0,0), color=(0,1,0), \
                        shaftwidth=0.025, display=cena1)
      vecZ1 = vis.arrow(pos=centro, axis=(0,0,1.0), color=(0,0,1), \
                        shaftwidth=0.025, display=cena1)

      #cria cubo
      cubo1 = vis.box(pos=centro, axis=(1.0, 0.0, 0.0), length=1.75, \
                      height=1.75, width=1.75, opacity=0.1)

      #----------------------------------------------------------------

      #cena 2
      cena2 = vis.display(title="cubo (1,-1,1)", x=410, y=0,\
        width=400, height=400, background=(1,1,1), \
        center=centroCena, range=larguraCena)

      #cria vetores que representam o eixo de coordenadas
      vecX2 = vis.arrow(pos=centro, axis=(1.0,0,0), color=(1,0,0), \
                        shaftwidth=0.025, display=cena2)
      vecY2 = vis.arrow(pos=centro, axis=(0,1.0,0), color=(0,1,0), \
                        shaftwidth=0.025, display=cena2)
      vecZ2 = vis.arrow(pos=centro, axis=(0,0,1.0), color=(0,0,1), \
                        shaftwidth=0.025, display=cena2)

      #criando cubo
      cubo2 = vis.box(pos=centro, axis=(1.0, -1.0, 1.0), length=1.75, \
                      height=1.75, width=1.75, opacity=0.2)      
      
definição do cubo em Vpython
Figura 09: Cubo de Vpython.

Neste exemplo criamos o mesmo cubo com axis diferente, o efeito foi um que os cubo estão rotado um em relação ao outro. Contudo preste atenção na figura (a), a projeção como o cubo foi desenhado diz qual é o tipo que o Vpython utiliza.

tipos de projeções em OpenGL
Figura 10: Tipos de projeções em OpenGL.

Quando se faz desenho podemos obtar por 2 tipos de projeções: (a) em perspectiva ou (b) ortográfica. Comparando nosso cubo em Vpython com a figura acima vemos que o Vpython desenha os objetos em perspectiva. Não há como mudar esse tipo de projeção, o mais que se pode é tentar simular ela colocando na definição da cena o parâmetro fov=0.0001. fov por padrão é $\pi/3$ radianos (60 graus) e ele define a abertura angular da cena e a profundidade da perspectiva. Esse tipo de projeção tem que ser levada em consideração quando se cria um objeto, como veremos na análise da esfera a perspectiva faz com que ela ocupe toda cena.

Um último comentário é o uso da opção opacity. Essa opção define se teu objeto é transparente (opacity=0) ou não (opacity=1).

Esferas

O comando sphere cria uma esfera:

esf1 = vis.sphere(pos=(1,1,1), radius=1.0)

o comando pos informa a posição da esfera e o comando radius o raio da esfera.

      #!/usr/bin/env python
      #-*- coding: utf-8 -*-

      import visual as vis

      vis.display(title="Soma de vetores", x=0, y=0,\
        width=400, height=400, background=(1,1,1))


      centro = vis.vector(1.0,1.0,1.0)
      raio   = 1.0

      centroCena  = centro
      larguraCena = vis.vector(1.0, 1.0, 1.0)

      cena1 = vis.display(title="esfera", x=0, y=0,\
        width=400, height=400, background=(1,1,1), \
        center=centroCena, range=larguraCena)

      esfera1 = vis.sphere(pos=centro, radius=raio,\
        color=(0.9,0.9,0.9), display=cena1)


      cena2 = vis.display(title="espaço", x=410, y=0,\
        width=400, height=400, background=(1,1,1), \
        center=centroCena, range=larguraCena)

      vecX = vis.arrow(pos=centro, axis=(1.0,0,0), color=(1,0,0),\
        shaftwidth=0.05, display=cena2)
      vecY = vis.arrow(pos=centro, axis=(0,1.0,0), color=(0,1,0),\
        shaftwidth=0.05, display=cena2)
      vecZ = vis.arrow(pos=centro, axis=(0,0,1.0), color=(0,0,1),\
        shaftwidth=0.05, display=cena2)
      
soma de vetores
Figura 11: Esfera.

Como já tinha sido dito, a projeção afeta o resultado esperado, a esfera preenche toda a cena, duas soluções são possíveis: 1) aumentar a cena e 2) usar o fov pequen, vejamos

 
      #!/usr/bin/env python
      #-*- coding: utf-8 -*-

      import visual as vis
      import numpy as np

      vis.display(title="Soma de vetores", x=0, y=0,\
        width=400, height=400, background=(1,1,1))

      centro = vis.vector(1.0,1.0,1.0)
      raio   = 1.0

      centroCena  = centro
      larguraCena = vis.vector(1.25, 1.25, 1.25)

      cena1 = vis.display(title="esfera", x=0, y=0,\
        width=400, height=400, background=(1,1,1), \
        center=centroCena, range=larguraCena)

      esfera1 = vis.sphere(pos=centro, radius=raio, color=(0.9,0.9,0.9),\
                          opacity=0.25, display=cena1)
      vecX = vis.arrow(pos=centro, axis=(1.0,0,0), color=(1,0,0),\
                      shaftwidth=0.05, display=cena1)
      vecY = vis.arrow(pos=centro, axis=(0,1.0,0), color=(0,1,0),\
                      shaftwidth=0.05, display=cena1)
      vecZ = vis.arrow(pos=centro, axis=(0,0,1.0), color=(0,0,1),\
                      shaftwidth=0.05, display=cena1)

      #--------------------------------------------------------------------

      cena2 = vis.display(title="esfera vista desde (1,1,1)", x=410, y=0,\
        width=400, height=400, background=(1,1,1), \
        center=centroCena, range=larguraCena)

      phi   = np.radians(90+45.0)
      theta = np.radians(180+45.0)
      x = 1.0 * np.sin(phi)*np.cos(theta)
      y = 1.0 * np.sin(phi)*np.sin(theta)
      z = 1.0 * np.cos(phi)

      cena2.forward=vis.vector(x,y,z)

      esfera2 = vis.sphere(pos=centro, radius=raio, color=(0.9,0.9,0.9),\
                          opacity=0.25, display=cena2)
      vecX = vis.arrow(pos=centro, axis=(1.0,0,0), color=(1,0,0),\
                      shaftwidth=0.05, display=cena2)
      vecY = vis.arrow(pos=centro, axis=(0,1.0,0), color=(0,1,0),\
                      shaftwidth=0.05, display=cena2)
      vecZ = vis.arrow(pos=centro, axis=(0,0,1.0), color=(0,0,1),\
                      shaftwidth=0.05, display=cena2)
      
esfera 2
Figura 12: Esfera num campo maior.

neste exemplo aumentamos o campo da cena e agora a esfera coube perfeitamente e para mostrar uma outra variável de cena que pode ser modificada: foi movida a linha de visão da câmera para a direção definida pelo vetor $\vec{c} = \vec{r} - \vec{r}_o$, onde $\vec{r}_o = 0\hat{r}+0\hat{\phi}+0\hat{\theta}$ é o centro da cena e $\vec{r} = \hat{r}+\frac{3\pi}{4}\hat{\phi}+\frac{5\pi}{4}\hat{\theta}$

Cilindros

O cilindro fica definido por 3 parâmetros, posição da base (pos), comprimento do cilindro (axis) e raio do cilindro (radius):

cil1 = vis.cylinder(pos=(0,0,0), axis=(10,0,0) radius=1)

cilindro
Figura 13: Cilindro em Vpython.

Vejamos um exemplo

 
      #!/usr/bin/env python
      #-*- coding: utf-8 -*-

      import visual as vis
      import numpy as np

      centroCena  = vis.vector(0.0,0.0,0.0)
      larguraCena = vis.vector(1.5, 1.5, 1.5)

      phi   = np.radians(90+35.0)
      theta = np.radians(180+45.0)
      x = 1.0 * np.sin(phi)*np.cos(theta)
      y = 1.0 * np.sin(phi)*np.sin(theta)
      z = 1.0 * np.cos(phi)
      
      dirCamera=vis.vector(x,y,z)

      cena1 = vis.display(title="Cilindro", x=0, y=0,\
        width=400, height=400, background=(1,1,1), \
        center=centroCena, range=larguraCena, forward=dirCamera)

      #cria vetores que representam o eixo de coordenadas
      iniVector = vis.vector(0,0,0)

      vecX1 = vis.arrow(pos=iniVector, axis=(1.0,0,0), color=(1,0,0),\
                        shaftwidth=0.05, display=cena1)
      vecY1 = vis.arrow(pos=iniVector, axis=(0,1.0,0), color=(0,1,0),\
                        shaftwidth=0.05, display=cena1)
      vecZ1 = vis.arrow(pos=iniVector, axis=(0,0,1.0), color=(0,0,1),\
                        shaftwidth=0.05, display=cena1)

      #cria cilindro
      posBaseCil     = vis.vector(0,0,0)
      comprimentoCil = vis.vector(0.0, 0.70, 0.0)
      radCil         = 0.35

      cil1 = vis.cylinder(pos=posBaseCil, axis= comprimentoCil,\
                          radius=radCil, opacity=0.25, display=cena1)

      #-----------------------------------------------------------------------

      cena2 = vis.display(title="Disco", x=410, y=0,\
        width=400, height=400, background=(1,1,1), \
        center=centroCena, range=larguraCena, forward=dirCamera)

      #cria vetores que representam o eixo de coordenadas
      iniVector = vis.vector(0,0,0)

      vecX1 = vis.arrow(pos=iniVector, axis=(1.0,0,0), color=(1,0,0),\
                        shaftwidth=0.05, display=cena2)
      vecY1 = vis.arrow(pos=iniVector, axis=(0,1.0,0), color=(0,1,0),\
                        shaftwidth=0.05, display=cena2)
      vecZ1 = vis.arrow(pos=iniVector, axis=(0,0,1.0), color=(0,0,1),\
                        shaftwidth=0.05, display=cena2)

      #cria cilindro
      posBaseCil     = vis.vector(0,0,0)
      comprimentoCil = vis.vector(0.0, 0.10, 0.0)
      radCil         = 1.0

      cil1 = vis.cylinder(pos=posBaseCil, axis= comprimentoCil,\
                          radius=radCil, opacity=0.25, display=cena2)
      
cilindro 2
Figura 14: Cilindro e disco em Vpython.

Molas

Para definir uma mola é necessário informar a posição (pos), o eixo(axis), o número de voltas(coils), espessura do fio (thickness) e raio da mola (radius).

mol1 = vis.helix(pos=(0,0,0), axis=(10,0,0), radius=1, coils=3, thickness=1)

      
      #!/usr/bin/env python
      #-*- coding: utf-8 -*-

      import visual as vis
      import numpy as np

      centroCena  = vis.vector(0.0,0.0,0.0)
      larguraCena = vis.vector(1.5, 1.5, 1.5)

      phi   = np.radians(90+35.0)
      theta = np.radians(180+45.0)
      x = 1.0 * np.sin(phi)*np.cos(theta)
      y = 1.0 * np.sin(phi)*np.sin(theta)
      z = 1.0 * np.cos(phi)
      
      dirCamera=vis.vector(x,y,z)

      cena1 = vis.display(title="Mola", x=0, y=0,\
        width=400, height=400, background=(1,1,1), \
        center=centroCena, range=larguraCena, forward=dirCamera)

      #cria vetores que representam o eixo de coordenadas
      iniVector = vis.vector(0,0,0)

      vecX1 = vis.arrow(pos=iniVector, axis=(1.0,0,0), color=(1,0,0),\
                        shaftwidth=0.05, display=cena1)
      vecY1 = vis.arrow(pos=iniVector, axis=(0,1.0,0), color=(0,1,0),\
                        shaftwidth=0.05, display=cena1)
      vecZ1 = vis.arrow(pos=iniVector, axis=(0,0,1.0), color=(0,0,1),\
                        shaftwidth=0.05, display=cena1)

      #cria Mola
      posBaseMol     = vis.vector(0,0,0)
      comprimentoMol = vis.vector(0.0, 0.70, 0.0)
      radMol         = 0.35
      espFio         = 0.1
      numVoltas      = 4

      Mol1 = vis.helix(pos=posBaseMol, axis=comprimentoMol, \
                      radius=radMol, coils=numVoltas, thickness=espFio)
      
Mola
Figura 15: Molas em Vpython.

Curva

Curva desenha uma linha de radio radius entre os pontos definidos em pos, exemplo, desenhando um quadrado:

quad1 = vis.curve(pos=[(0,0), (0,1), (1,1), (1,0), (0,0)], radius=0.2)

Para adicionar mais pontos à curva fazemos:

quad1 = vis.curve(pos=[(0,0)], radius=0.2)
quad1.append(pos=(0,1))

Vejamos alguns exemplos

      #!/usr/bin/env python
      #-*- coding: utf-8 -*-

      import visual as vis
      import numpy as np

      centroCena  = vis.vector(0.0,0.0,0.0)
      larguraCena = vis.vector(1.2, 1.2, 1.2)

      phi   = np.radians(90+45.0)
      theta = np.radians(180+45.0)
      x = 1.0 * np.sin(phi)*np.cos(theta)
      y = 1.0 * np.sin(phi)*np.sin(theta)
      z = 1.0 * np.cos(phi)
      
      dirCamera=vis.vector(x,y,z)

      cena1 = vis.display(title="linhas", x=0, y=0,\
        width=400, height=400, background=(1,1,1), \
        center=centroCena, range=larguraCena, forward=dirCamera)

      #cria vetores que representam o eixo de coordenadas
      iniVector = vis.vector(0,0,0)

      vecX1 = vis.arrow(pos=iniVector, axis=(1.0,0,0), color=(1,0,0),\
                        shaftwidth=0.05, display=cena1)
      vecY1 = vis.arrow(pos=iniVector, axis=(0,1.0,0), color=(0,1,0),\
                        shaftwidth=0.05, display=cena1)
      vecZ1 = vis.arrow(pos=iniVector, axis=(0,0,1.0), color=(0,0,1),\
                        shaftwidth=0.05, display=cena1)

      #cria linha
      linha1 = vis.curve(pos=(0,0,0), radius=0.05)
      linha1.append(pos=(0.5,0.0,0))
      linha1.append(pos=(0.5,0.5,0))
      linha1.append(pos=(0.0,0.5,0))
      linha1.append(pos=(0,0,0))

      #-------------------------------------------------------------------

      larguraCena = (2.5*np.pi, 2.5*np.pi, 2.5*np.pi)

      cena2 = vis.display(title="linhas", x=410, y=0,\
        width=400, height=400, background=(1,1,1), \
        center=centroCena, range=larguraCena, forward=dirCamera)

      dpi = 2*np.pi
      vecX1 = vis.arrow(pos=iniVector, axis=(dpi,0,0), color=(1,0,0),\
                        shaftwidth=0.1, display=cena2)
      vecY1 = vis.arrow(pos=iniVector, axis=(0,dpi,0), color=(0,1,0),\
                        shaftwidth=0.1, display=cena2)
      vecZ1 = vis.arrow(pos=iniVector, axis=(0,0,dpi), color=(0,0,1),\
                        shaftwidth=0.1, display=cena2)

      espiral = vis.curve( color = vis.color.cyan, radius=0.25 )
      for t in np.arange(0, 2*np.pi, 0.1):
          espiral.append( pos=(np.sin(6.0*t),np.cos(6.0*t), t) )
      
Mola
Figura 16: linhas em Vpython.

curve só desenha 1000 pontos, você pode escolher quantos pontos vai imprimir no máximo (contando do ultimo para atrás) utilizando a opção retain=Numero de pontos.

Animações

Para fazer a animação é muito simples, basta mudar a posição do objeto no tempo, mantendo a forma e definir a frequência (rate(f)) a qual sera atualizada a tela (ou seja a cada $t=1/f$ segundo é desenhado o novo ponto), vejamos um exemplo

      #!/usr/bin/env python
      #-*- coding: utf-8 -*-

      import visual as vis
      import numpy as np

      centroCena  = vis.vector(0.0,0.0,0.0)
      larguraCena = vis.vector(1.2, 1.2, 1.2)

      phi   = np.radians(90+45.0)
      theta = np.radians(180+45.0)
      x = 1.0 * np.sin(phi)*np.cos(theta)
      y = 1.0 * np.sin(phi)*np.sin(theta)
      z = 1.0 * np.cos(phi)
      
      dirCamera=vis.vector(x,y,z)

      larguraCena = (2.5*np.pi, 2.5*np.pi, 2.5*np.pi)

      cena2 = vis.display(title="linhas", x=410, y=0,\
        width=400, height=400, background=(1,1,1), \
        center=centroCena, range=larguraCena, forward=dirCamera)

      iniVector = vis.vector(0,0,0)
      dpi = 2*np.pi

      vecX1 = vis.arrow(pos=iniVector, axis=(dpi,0,0), color=(1,0,0),\
                        shaftwidth=0.1, display=cena2)
      vecY1 = vis.arrow(pos=iniVector, axis=(0,dpi,0), color=(0,1,0),\
                        shaftwidth=0.1, display=cena2)
      vecZ1 = vis.arrow(pos=iniVector, axis=(0,0,dpi), color=(0,0,1),\
                        shaftwidth=0.1, display=cena2)

      bola = vis.sphere(radius=1.0)

      espiral = vis.curve( color = vis.color.cyan, radius=0.25)
      for t in np.arange(0, 2*np.pi, 0.01):
          
          vis.rate(60)
          
          x = 2.0*np.sin(6.0*t)
          y = 2.0*np.cos(6.0*t)
          z = t
          novaPos = vis.vector(x, y, z)
          
          bola.pos = novaPos
          espiral.append( pos=novaPos,  retain=50 )