Faça uma animação de ilusão de círculo


84

Seu trabalho é animar essa ilusão de círculo . Parece que os pontos estão girando dentro do círculo, mas na verdade eles estão apenas se movendo em linhas retas.

insira a descrição da imagem aqui

Critérios

  • O resultado deve ser animado. O modo como você faz a animação é irrelevante, pode gerar um .gif, pode atrair uma janela, alguma tela do dispositivo ou o que for.
  • Este é um concurso de popularidade, portanto, você pode querer adicionar alguns recursos adicionais ao seu programa para obter mais votos positivos, por exemplo, variando o número de pontos.
  • O vencedor é a resposta válida mais votada 7 dias após o último envio válido.
  • As respostas que realmente implementam pontos que se movem em linhas retas e não o contrário são mais bem-vindas

"vencedor é o mais votado válido após 7 dias". Então, se alguém postar algo a cada 6 dias até as estrelas morrerem, não temos vencedor?
Kevin L

3
@KevinL é improvável que isso aconteça e eu não acho que esses 15 representantes extras sejam tão importantes em comparação com todos os upvotes que você obteria com essa pergunta, sendo levada ao topo a cada 6 dias.
Martin Ender

1
Às vezes eu me pergunto se algumas pessoas fazem estes apenas para obter um trabalho feito ...
Daniel Pendergast

3
"Parece que os pontos de rotação dentro do círculo, mas eles são realmente apenas movendo em linha reta.", Ou, talvez eles realmente estão girando dentro de um círculo e parecem se mover em linha reta ...
coredump

1
Não consigo .. tirar essa animação .. da minha cabeça .. especialmente a versão de 3 pontos!
Thomas

Respostas:


126

Python 3.4

Usando o módulo tartaruga. As tartarugas são de cores diferentes e sempre voltadas para a mesma direção, de modo que podem ser facilmente vistas movendo-se em linhas retas, concentrando-se apenas em uma delas. Apesar disso, a ilusão do círculo ainda é forte.

11 tartarugas

A ilusão ainda parece bastante forte, mesmo com apenas 3 ou 4 tartarugas:

3 tartarugas4 tartarugas

A taxa de quadros é reduzida consideravelmente para todos esses exemplos de GIF, mas não parece prejudicar a ilusão. A execução local do código fornece uma animação mais suave.

import turtle
import time
from math import sin, pi
from random import random


def circle_dance(population=11, resolution=480, loops=1, flip=0, lines=0):
    population = int(population)
    resolution = int(resolution)
    radius = 250
    screen = turtle.Screen()
    screen.tracer(0)
    if lines:
        arrange_lines(population, radius)
    turtles = [turtle.Turtle() for i in range(population)]
    for i in range(population):
        dancer = turtles[i]
        make_dancer(dancer, i, population)
    animate(turtles, resolution, screen, loops, flip, radius)


def arrange_lines(population, radius):
    artist = turtle.Turtle()
    for n in range(population):
        artist.penup()
        artist.setposition(0, 0)
        artist.setheading(n / population * 180)
        artist.forward(-radius)
        artist.pendown()
        artist.forward(radius * 2)
    artist.hideturtle()


def make_dancer(dancer, i, population):
    dancer.setheading(i / population * 180)
    dancer.color(random_turtle_colour())
    dancer.penup()
    dancer.shape('turtle')
    dancer.turtlesize(2)


def random_turtle_colour():
    return random() * 0.9, 0.5 + random() * 0.5, random() * 0.7


def animate(turtles, resolution, screen, loops, flip, radius):
    delay = 4 / resolution      # 4 seconds per repetition
    while True:
        for step in range(resolution):
            timer = time.perf_counter()
            phase = step / resolution * 2 * pi
            draw_dancers(turtles, phase, screen, loops, flip, radius)
            elapsed = time.perf_counter() - timer
            adjusted_delay = max(0, delay - elapsed)
            time.sleep(adjusted_delay)


def draw_dancers(turtles, phase, screen, loops, flip, radius):
    population = len(turtles)
    for i in range(population):
        individual_phase = (phase + i / population * loops * pi) % (2*pi)
        dancer = turtles[i]
        if flip:
            if pi / 2 < individual_phase <= 3 * pi / 2:
                dancer.settiltangle(180)
            else:
                dancer.settiltangle(0)
        distance = radius * sin(individual_phase)
        dancer.setposition(0, 0)
        dancer.forward(distance)
    screen.update()


if __name__ == '__main__':
    import sys
    circle_dance(*(float(n) for n in sys.argv[1:]))

Para contraste, aqui estão alguns que realmente rodam:

23 tartarugas23 tartarugas de trevo

... ou eles?

O código pode ser executado com 5 argumentos opcionais: população, resolução, loops, flip e linhas.

  • population é o número de tartarugas
  • resolution é a resolução de tempo (número de quadros de animação por repetição)
  • loopsdetermina quantas vezes as tartarugas retornam a si mesmas. O padrão 1 fornece um círculo padrão, outros números ímpares dão esse número de voltas na sequência de tartarugas, enquanto números pares fornecem uma sequência de tartarugas desconectadas nas extremidades, mas ainda com a ilusão de movimento curvo.
  • flipse diferente de zero, faz com que as tartarugas mudem de direção para a viagem de volta (como sugerido pelo aslum, para que nunca se movam para trás). Como padrão, eles mantêm uma direção fixa para evitar a distração visual nos pontos de extremidade.
  • lines se diferente de zero exibe as linhas nas quais as tartarugas se movem, para consistência com a imagem de exemplo na pergunta.

Exemplos com flipconjunto, com e sem lines. Eu deixei meu exemplo principal acima sem virar, porque eu prefiro não ter um salto esporádico, mas a borda do círculo parece mais suave com todas as tartarugas alinhadas, então a opção existe para que as pessoas escolham o estilo que preferem ao correr o código.

11 tartarugas com flip e linhas11 tartarugas com flip

Pode não ser imediatamente óbvio como as imagens acima foram todas produzidas a partir desse mesmo código. Em particular, a imagem mais acima, com um loop externo lento e um loop interno rápido (aquele que se parece com um cardióide que alguém acidentalmente derrubou). Escondi a explicação abaixo, caso alguém queira adiar a descoberta enquanto experimenta / pensa.

A animação com um loop interno e externo de tamanhos diferentes foi criada definindo o número de loops para 15 e deixando o número de tartarugas em 23 (muito baixo para representar 15 loops). Usar um grande número de tartarugas resultaria em 15 loops claramente definidos. O uso de poucas tartarugas resulta em alias (pelo mesmo motivo que no processamento e renderização de imagens). Tentar representar uma frequência muito alta resulta na exibição de uma frequência mais baixa, com distorção.

Ao experimentar números diferentes, achei algumas dessas distorções mais interessantes do que os originais mais simétricos, então queria incluir uma aqui ...


18
Eu gosto de tartarugas.
FreeAsInBeer

18
I desembolsou +1 para as tartarugas
MrEngineer13

@ProgramFOX, obrigado pelo destaque da sintaxe! Pesquisei a ajuda e a meta e me convenci de que não tínhamos destaque de sintaxe no código de golfe - estou muito mais feliz com isso agora.
Trichoplax

1
@ aslum que seria uma mudança simples de fazer, mas eu queria que a orientação deles congelasse para realmente enfatizar que eles não se desviam do curso em linha reta. Talvez eu deva adicioná-lo ao código como uma opção para que as pessoas possam escolher a abordagem que preferirem.
Trichoplax

4
+1 - Seria incrível ver uma banda fazendo alguns desses mais engraçados!
Mkistinen

96

C

Resultado:

insira a descrição da imagem aqui

#include <stdio.h>
#include <Windows.h>
#include <Math.h>

int round (double r) { return (r > 0.0) ? (r + 0.5) : (r - 0.5); }
void print (int x, int y, char c) {
    COORD p = { x, y };
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), p);
    printf("%c", c);
}

int main ()
{
    float pi = 3.14159265358979323846;
    float circle = pi * 2;
    int len = 12;
    int hlen = len / 2;
    int cx = 13;
    int cy = 8;
    float w = 11.0;
    float h =  8.0;
    float step = 0.0;

    while (1)
    {
        system("cls"); // xD

        for (int i = 0; i < len; i++)
        {
            float a = (i / (float)len) * circle;
            int x = cx + round(cos(a) * w);
            int y = cy + round(sin(a) * h);
            print(x, y, 'O');

            if (i < hlen) continue;

            step -= 0.05;
            float range = cos(a + step);
            x = cx + round(cos(a) * (w - 1) * range);
            y = cy + round(sin(a) * (h - 1) * range);
            print(x, y, 'O');
        }

        Sleep(100);
    }

    return 0;
}

3
Em alguns quadros, é um pouco complicado. Mas parabéns por fazê-lo em ASCII!
Just just

10
+1 para ASCII esystem("cls"); // xD
Christoph Böhmwalder

1
Isso é lindo.
Trichoplax

1
Este funciona no linux. (embora bastante miseravelmente) #
31828

Comentário obrigatório do odiador: "Este não é C! O padrão não define Sleep, COORD ou SetConsoleCursorPosition!"
user253751

52

SVG (sem Javascript)

Link JSFiddle aqui

<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 380 380" width="380" height="380" version="1.0">
  <g transform="translate(190 190)">
    <circle cx="0" cy="0" r="190" fill="#000"/>
    <line x1="0" y1="-190" x2="0" y2="190" stroke="#fff" stroke-width="1.5"/>
    <line x1="72.71" y1="175.54" x2="-72.71" y2="-175.54" stroke="#fff" stroke-width="1.5"/>
    <line x1="134.35" y1="134.35" x2="-134.35" y2="-134.35" stroke="#fff" stroke-width="1.5"/>
    <line x1="175.54" y1="72.71" x2="-175.54" y2="-72.71" stroke="#fff" stroke-width="1.5"/>
    <line x1="190" y1="0" x2="-190" y2="0" stroke="#fff" stroke-width="1.5"/>
    <line x1="175.54" y1="-72.71" x2="-175.54" y2="72.71" stroke="#fff" stroke-width="1.5"/>
    <line x1="134.35" y1="-134.35" x2="-134.35" y2="134.35" stroke="#fff" stroke-width="1.5"/>
    <line x1="72.71" y1="-175.54" x2="-72.71" y2="175.54" stroke="#fff" stroke-width="1.5"/>
    <g transform="rotate(0)">
      <animateTransform attributeType="xml" attributeName="transform" type="rotate" from="0" to="360" begin="0" dur="8s" repeatCount="indefinite"/>
      <g transform="translate(0 90)">
        <g transform="rotate(0)">
          <animateTransform attributeType="xml" attributeName="transform" type="rotate" from="0" to="-360" begin="0" dur="4s" repeatCount="indefinite"/>
          <circle cx="0" cy="90" r="10" fill="#fff"/>
          <circle cx="63.64" cy="63.64" r="10" fill="#fff"/>
          <circle cx="90" cy="0" r="10" fill="#fff"/>
          <circle cx="63.64" cy="-63.64" r="10" fill="#fff"/>
          <circle cx="0" cy="-90" r="10" fill="#fff"/>
          <circle cx="-63.64" cy="-63.64" r="10" fill="#fff"/>
          <circle cx="-90" cy="0" r="10" fill="#fff"/>
          <circle cx="-63.64" cy="63.64" r="10" fill="#fff"/>
        </g>
      </g>
    </g>
  </g>
</svg>

Hmmm, tenho certeza de que isso é compatível com as regras, mas fiquei pessoalmente desapontado por você estar fazendo o contrário. Em vez de “Parece que os pontos estão girando dentro do círculo, mas na verdade estão apenas se movendo em linhas retas .”, O seu implementa: “Parece que os pontos [estão] se movendo em linhas retas, mas eles estão realmente apenas girando dentro do círculo . ”
mkoistinen

Resposta mais suave!
Derek #

14
@mkoistinen Entendo o que você quer dizer, mas os pontos realmente estão se movendo em linhas retas. Ele só passa a ser mais fácil de calcular suas posições com duas rotações :-)
ossifrage escrúpulos

Você fez tudo 'manualmente' ou usou algum tipo de editor (sem texto)?
flawr

5
@flawr Eu apenas usei um editor de texto simples e a calculadora no meu telefone para trabalhar os números :-)
ossifrage escrúpulos

47

http://jsfiddle.net/z6vhD/13/

intervaltimealtera o FPS (FPS = 1000 / intervalo).
ballsmuda as # bolas.
maxstepajusta as # etapas em um ciclo, quanto maior, mais 'suave' ela é. 64 deve ser grande o suficiente onde pareça suave.

Modelado como um círculo em movimento, em vez de mover as bolas ao longo das linhas, mas o efeito visual (deveria ser?) O mesmo. Parte do código é bem detalhada, mas não é um código de golfe, então ...

var intervalTime = 40;
var balls = 8;
var maxstep = 64;

var canvas = $('#c').get(0); // 100% necessary jquery
var ctx = canvas.getContext('2d');
var step = 0;

animateWorld = function() {
    createBase();
    step = step % maxstep;
    var centerX = canvas.width/2 + 115 * Math.cos(step * 2 / maxstep * Math.PI);
    var centerY = canvas.height/2 + 115 * Math.sin(step * 2 / maxstep * Math.PI);

    for (var i=0; i<balls; i++) {
        drawCircle(ctx, (centerX + 115 * Math.cos((i * 2 / balls - step * 2 / maxstep) * Math.PI)), (centerY + 115 * Math.sin((i * 2 / balls - step * 2 / maxstep) * Math.PI)), 10, '#FFFFFF');     
    }

    step++;
}

function createBase() {
    drawCircle(ctx, canvas.width/2, canvas.height/2, 240, '#000000');
    for(var i=0; i<balls*2; i++) {
        drawLine(ctx, canvas.width/2, canvas.height/2, canvas.width/2 + 240 * Math.cos(i / balls * Math.PI), canvas.height/2 + 240 * Math.sin(i / balls * Math.PI), '#FFFFFF');
    }
}

function drawLine(context, x1, y1, x2, y2, c) {
    context.beginPath();
    context.moveTo(x1,y1);
    context.lineTo(x2,y2);
    context.lineWidth = 3;
    context.strokeStyle = c;
    context.stroke();
}

function drawCircle(context, x, y, r, c) {
    context.beginPath();
    context.arc(x, y, r, 0, 2*Math.PI);
    context.fillStyle = c;
    context.fill();
}

function drawRect(context, x, y, w, h, c) {
    context.fillStyle = c;
    context.fillRect(x, y, w, h);
}

$(document).ready(function() {
    intervalID = window.setInterval(animateWorld, intervalTime);
});

2
É tão suave! Muito agradável.
Nneonneo 21/07

5
Não use setInterval para animações; em requestAnimationFramevez disso , use . JSFiddle modificado usando requestAnimationFrame.
Klingt.net 22/07

1
Com apenas alguns ajustes de parâmetro, você obtém uma coisa muito diferente .
FreeAsInBeer

@ KevinL Sim, apenas notei isso também. Atualizada.
FreeAsInBeer

1
@FreeAsInBeer Oh, quando você disse algo muito diferente, pensei que você quisesse dizer como os do jsfiddle.net/z6vhD/100 #
Kevin L

41

Animações CSS

Uma solução usando apenas animações css (consulte a animação no JSFiddle - observe que adicionei os prefixos específicos do navegador no violino para que ele funcione nas versões mais recentes).

<body>
    <div id="w1"></div>
    <div id="w2"></div>
    <div id="w3"></div>
    <div id="w4"></div>
    <div id="w5"></div>
    <div id="w6"></div>
    <div id="w7"></div>
    <div id="w8"></div>
</body>


div {
    position: absolute;
    width: 20px;
    height: 20px;
    border-radius: 20px;
    background: red;
    animation-duration: 4s;
    animation-iteration-count: infinite;
    animation-direction: alternate;
    animation-timing-function: ease-in-out;
}

#w1 { animation-name: s1; animation-delay: 0.0s }
#w2 { animation-name: s2; animation-delay: 0.5s }
#w3 { animation-name: s3; animation-delay: 1.0s }
#w4 { animation-name: s4; animation-delay: 1.5s }
#w5 { animation-name: s5; animation-delay: 2.0s }
#w6 { animation-name: s6; animation-delay: 2.5s }
#w7 { animation-name: s7; animation-delay: 3.0s }
#w8 { animation-name: s8; animation-delay: 3.5s }

@keyframes s1 { from {top: 100px; left:   0px;} to {top: 100px; left: 200px;} } 
@keyframes s2 { from {top:  62px; left:   8px;} to {top: 138px; left: 192px;} } 
@keyframes s3 { from {top:  29px; left:  29px;} to {top: 171px; left: 171px;} } 
@keyframes s4 { from {top:   8px; left:  62px;} to {top: 192px; left: 138px;} } 
@keyframes s5 { from {top:   0px; left: 100px;} to {top: 200px; left: 100px;} } 
@keyframes s6 { from {top:   8px; left: 138px;} to {top: 192px; left:  62px;} } 
@keyframes s7 { from {top:  29px; left: 171px;} to {top: 171px; left:  29px;} } 
@keyframes s8 { from {top:  62px; left: 192px;} to {top: 138px; left:   8px;} } 

3
O Fiddle não funciona para mim no Chrome mais recente = /
mkoistinen

1
@mkoistinen - Você precisa adicionar prefixos diferentes para que ele funcione em navegadores diferentes. ( -webkit-para Webkit e -moz-para Mozilla) Aqui está o mesmo violino com prefixos atualizados: jsfiddle.net/nBCxz/3
Derek 朕 會 功夫

@mkoistinen Você está certo. O novo violino adiciona todos os prefixos de navegador necessários e funciona no Chrome mais recente.
219 Howard

O texto bruto do link está faltando apenas os parênteses de fechamento - ainda é perfeitamente utilizável, apenas para que você saiba se você deseja corrigi-lo (não posso, pois são menos de 6 caracteres para alterar).
Trichoplax

35

Mathematica

Aqui está um envio bastante direto.

animateCircle[n_] := Animate[Graphics[
   Flatten@{
     Disk[],
     White,
     Map[
      (
        phase = #*2 \[Pi]/n;
        line = {Cos[phase], Sin[phase]};
        {Line[{-line, line}],
         Disk[Sin[t + phase]*line, 0.05]}
        ) &,
      Range[n]
      ]
     },
   PlotRange -> {{-1.1, 1.1}, {-1.1, 1.1}}
   ],
  {t, 0, 2 \[Pi]}
  ]

Se você ligar, animateCircle[32]obterá uma animação elegante com 32 linhas e círculos.

insira a descrição da imagem aqui

É completamente tranquilo no Mathematica, mas tive que limitar um pouco o número de quadros para o GIF.

Agora, o que acontece se você colocar dois discos em cada linha? (Ou seja, adicione Disk[-Sin[t + phase]*line, 0.05]à lista dentro do Map.)

insira a descrição da imagem aqui

Você também pode colocá-los fora de fase a 90 ° (use em Cosvez de -Sin):

insira a descrição da imagem aqui


Eu não sei o que falhas você quer dizer, provavelmente você precisa mudar {t, 0, 2 \[Pi]}para, {t, 0, 2 \[Pi] - 2 \[Pi]/60, 2 \[Pi]/60}para que não haja dois quadros idênticos e mudar Animatepara Table. Então você poderá exportar GIF.
swish

@swish Não, na verdade, cria linhas adicionais estranhas que não estão lá e os discos em lugares onde não deveriam estar (e onde nunca estão no resultado real Animate). Vou tentar usar Tablenovamente embora.
Martin Ender

@swish Isso funcionou. Pensei ter tentado algo assim ontem, mas aparentemente não o fiz.
Martin Ender

25

Gráfico de pizza VBScript + VBA + Excel

Isso fará seu processador chorar um pouco, mas parece bonito e acredito que funciona de acordo com as especificações. Usei a resposta de @ Fabricio como um guia para implementar o algoritmo de movimento circular.

EDIT: fez alguns ajustes para melhorar a velocidade de renderização.

Captura de tela do gráfico de pizza

O código:

'Open Excel
Set objX = CreateObject("Excel.Application")
objX.Visible = True
objX.Workbooks.Add

'Populate values
objX.Cells(1, 1).Value = "Lbl"
objX.Cells(1, 2).Value = "Amt"
For fillX = 2 to 17
    objX.Cells(fillX, 1).Value = "V"+Cstr(fillX-1)
    objX.Cells(fillX, 2).Value = "1"
Next

'Create pie
objX.Range("A2:B17").Select
objX.ActiveSheet.Shapes.AddChart.Select
With objX.ActiveChart
    .ChartType = 5 'pieChart
    .SetSourceData  objX.Range("$A$2:$B$17")
    .SeriesCollection(1).Select
End with    

'Format pie
With objX.Selection.Format
    .Fill.ForeColor.RGB = 0 'black
    .Fill.Solid
    .Line.Weight = 2
    .Line.Visible = 1
    .Line.ForeColor.RGB = 16777215 'white
End With

'animation variables
pi = 3.14159265358979323846
circle = pi * 2 : l  = 16.0
hlen = l / 2    : cx = 152.0
cy = 99.0       : w  = 90.0
h  = 90.0       : s  = 0.0
Dim posArry(7,1)

'Animate
While 1 
  For i = 0 to hlen-1
    a = (i / l) * circle
    range = cos(a + s)
    x = cx + cos(a) * w * range
    y = cy + sin(a) * h * range

    If whileInx = 1 Then 
        createOval x, y
    ElseIf whileInx = 2 Then 
        objX.ActiveChart.Legend.Select
    ElseIf whileInx > 2 Then
        ovalName = "Oval "+ Cstr(i+1)
        dx = x - posArry(i,0)
        dy = y - posArry(i,1)
        moveOval ovalName, dx, dy
    End if

    posArry(i,0) = x
    posArry(i,1) = y
  Next

  s=s-0.05
  wscript.Sleep 1000/60 '60fps
  whileInx = 1 + whileInx
Wend

'create circles
sub createOval(posX, posY)
    objX.ActiveChart.Shapes.AddShape(9, posX, posY, 10, 10).Select '9=oval
    objX.Selection.ShapeRange.Line.Visible = 0
    with objX.Selection.ShapeRange.Fill
       .Visible = 1
       .ForeColor.RGB = 16777215 'white
       .solid
    end with
end sub

'move circles
sub moveOval(ovalName, dx, dy)
    with objX.ActiveChart.Shapes(ovalName)      
        .IncrementLeft dx
        .IncrementTop  dy
    end with
end sub

Ele trava na linha 81, erro 80070057, "o elemento com o nome determinado não existe" ou algo assim (traduzido de húngaro, é por isso que não conheço a mensagem de erro exata).
marczellm

Szervusz, @marczellm. Posso reproduzir esse erro quando clico fora do gráfico enquanto ele está "animando". Você precisa permitir que ele se concentre ou o programa irá com erro. Caso contrário, isso pode ser devido a uma incompatibilidade com o Office. Estou no Office 2010 no Win7.
comfortablydrei

Office 2007, Win7. Parece que, no meu caso, o gráfico não tem foco.
Marczellm

21

Excel, 161 bytes

Excel

=2*PI()*(NOW()*24*60*60/A2-FLOOR(NOW()*24*60*60/A2,1))
=ROUND(7*SIN(A1),0)
=ROUND(5*SIN(A1+1*PI()/4),0)
=ROUND(7*SIN(A1+2*PI()/4),0)
=ROUND(5*SIN(A1+3*PI()/4),0)

A2 (ponto final) determina o tempo (segundos) para uma 'revolução' completa.

Cada célula dentro das linhas é uma condicional básica relacionada ao valor da linha correspondente. Por exemplo, K2 é:

 =1*(A5=7)

E a célula central (K9) é:

=1*OR(A5=0,A6=0,A7=0,A8=0)

Forçou a animação pressionando 'delete' em uma célula aleatória para acionar constantemente uma atualização.

Sei que esse é um tópico antigo, mas atividades recentes o levaram ao topo e pareceu atraente por algum motivo. Ouvinte de longa data pcg, chamador pela primeira vez. Seja gentil.


Uau, é incrível que você possa fazer isso com o Excel: D
Beta Decay

15

Apenas por diversão com o PSTricks.

insira a descrição da imagem aqui

\documentclass[preview,border=12pt,multi]{standalone}
\usepackage{pstricks}

\psset{unit=.3}

% static point
% #1 : half of the number of points
% #2 : ith point
\def\x[#1,#2]{(3*cos(Pi/#1*#2))}
\def\y[#1,#2]{(3*sin(Pi/#1*#2))}

% oscillated point
% #1 : half of the number of points
% #2 : ith point
% #3 : time parameter
\def\X[#1,#2]#3{(\x[#1,#2]*cos(#3+Pi/#1*#2))}
\def\Y[#1,#2]#3{(\y[#1,#2]*cos(#3+Pi/#1*#2))}

% single frame
% #1 : half of the number of points
% #2 : time parameter
\def\Frame#1#2{%
\begin{pspicture}(-3,-3)(3,3)
    \pstVerb{/I2P {AlgParser cvx exec} bind def}%
    \pscircle*{\dimexpr3\psunit+2pt\relax}
    \foreach \i in {1,...,#1}{\psline[linecolor=yellow](!\x[#1,\i] I2P \y[#1,\i] I2P)(!\x[#1,\i] I2P neg \y[#1,\i] I2P neg)}
    \foreach \i in {1,...,#1}{\pscircle*[linecolor=white](!\X[#1,\i]{#2} I2P \Y[#1,\i]{#2} I2P){2pt}}   
\end{pspicture}}

\begin{document}
\foreach \t in {0,...,24}
{   
    \preview
    \Frame{1}{2*Pi*\t/25} \quad \Frame{2}{2*Pi*\t/25} \quad \Frame{3}{2*Pi*\t/25} \quad \Frame{5}{2*Pi*\t/25} \quad \Frame{10}{2*Pi*\t/25}
    \endpreview
}
\end{document}

11

Fortran

Cada quadro é criado como um arquivo gif individual usando o módulo gif Fortran em: http://fortranwiki.org/fortran/show/writegif
Então, trapaceio um pouco usando o ImageMagick para mesclar os gifs individuais em um gif animado.

Fortran

UPDATE: Defina novo = .true. para obter o seguinte:

insira a descrição da imagem aqui

program circle_illusion

use, intrinsic :: iso_fortran_env, only: wp=>real64
use gif_util  !gif writing module from http://fortranwiki.org/fortran/show/writegif

implicit none

logical,parameter :: new = .false.

integer,parameter  :: n        = 500  !550  !size of image (square)     
real(wp),parameter :: rcircle  = n/2  !250  !radius of the big circle
integer,parameter  :: time_sep = 5    !deg

real(wp),parameter :: deg2rad = acos(-1.0_wp)/180.0_wp
integer,dimension(0:n,0:n):: pixel     ! pixel values
integer,dimension(3,0:3)  :: colormap  ! RGB 0:255 for colors 0:ncol    
real(wp),dimension(2)     :: xy
integer,dimension(2)      :: ixy
real(wp)                  :: r,t
integer                   :: i,j,k,row,col,m,n_cases,ang_sep
character(len=10)         :: istr

integer,parameter  :: black = 0
integer,parameter  :: white = 1
integer,parameter  :: red   = 2
integer,parameter  :: gray  = 3    
colormap(:,0) = [0,0,0]          !black
colormap(:,1) = [255,255,255]    !white
colormap(:,2) = [255,0,0]        !red
colormap(:,3) = [200,200,200]    !gray

if (new) then
    ang_sep = 5
    n_cases = 3
else
    ang_sep = 20
    n_cases = 0
end if

do k=0,355,time_sep

    !clear entire image:
    pixel = white      

    if (new) call draw_circle(n/2,n/2,black,n/2)  

    !draw polar grid:    
    do j=0,180-ang_sep,ang_sep
        do i=-n/2, n/2
            call spherical_to_cartesian(dble(i),dble(j)*deg2rad,xy)
            call convert(xy,row,col)
            if (new) then
                pixel(row,col) = gray
            else
                pixel(row,col) = black  
            end if  
        end do
    end do

    !draw dots:
    do m=0,n_cases
        do j=0,360-ang_sep,ang_sep
            r = sin(m*90.0_wp*deg2rad + (k + j)*deg2rad)*rcircle                
            t = dble(j)*deg2rad    
            call spherical_to_cartesian(r,t,xy)
            call convert(xy,row,col)
            if (new) then
                !call draw_circle(row,col,black,10)  !v2
                !call draw_circle(row,col,m,5)       !v2
                call draw_circle(row,col,white,10)   !v3
            else
                call draw_square(row,col,red)        !v1
            end if
        end do
    end do

    !write the gif file for this frame:        
    write(istr,'(I5.3)') k
    call writegif('gifs/test'//trim(adjustl(istr))//'.gif',pixel,colormap)

end do

!use imagemagick to make animated gif from all the frames:
! from: http://thanosk.net/content/create-animated-gif-linux
if (new) then
    call system('convert -delay 5 gifs/test*.gif -loop 0 animated.gif')
else
    call system('convert -delay 10 gifs/test*.gif -loop 0 animated.gif')
end if

!delete individual files:
call system('rm gifs/test*.gif')

contains

    subroutine draw_square(r,c,icolor)

    implicit none
    integer,intent(in) :: r,c  !row,col of center
    integer,intent(in) :: icolor

    integer,parameter :: d = 10 !square size

    pixel(max(0,r-d):min(n,r+d),max(0,c-d):min(n,c+d)) = icolor

    end subroutine draw_square

    subroutine draw_circle(r,c,icolor,d)

    implicit none
    integer,intent(in) :: r,c  !row,col of center
    integer,intent(in) :: icolor
    integer,intent(in) :: d  !diameter

    integer :: i,j

    do i=max(0,r-d),min(n,r+d)
        do j=max(0,c-d),min(n,c+d)
            if (sqrt(dble(i-r)**2 + dble(j-c)**2)<=d) &
                pixel(i,j) = icolor
        end do
    end do

    end subroutine draw_circle

    subroutine convert(xy,row,col)

    implicit none
    real(wp),dimension(2),intent(in) :: xy  !coordinates
    integer,intent(out) :: row,col

    row = int(-xy(2) + n/2.0_wp)
    col = int( xy(1) + n/2.0_wp)

    end subroutine convert

    subroutine spherical_to_cartesian(r,theta,xy)

    implicit none
    real(wp),intent(in) :: r,theta
    real(wp),dimension(2),intent(out) :: xy

    xy(1) = r * cos(theta)
    xy(2) = r * sin(theta)

    end subroutine spherical_to_cartesian

end program circle_illusion

1
Eu gosto do impacto 'squish' para os elementos verticais e horizontais.
Portland Runner

11

Obrigatório Versão C64 .

Copie e cole no seu emulador favorito:

Versão C64

1 print chr$(147)
2 poke 53281,0
3 for p=0 to 7
5 x=int(11+(cos(p*0.78)*10)):y=int(12+(sin(p*0.78)*10))
6 poke 1024+x+(y*40),15
9 next p
10 for sp=2040 to 2047:poke sp,13:next sp
20 for i=0 to 62:read a:poke 832+i,a:next i
30 for i=0 to 7:poke 53287+i,i+1:next i
40 rem activate sprites
50 poke 53269,255
60 an=0.0
70 rem maincycle
75 teta=0.0:k=an
80 for i=0 to 7
90 px=cos(k)*64
92 s=i:x=px*cos(teta): y=px*sin(teta): x=x+100: y=y+137: gosub 210
94 teta=teta+0.392699
95 k=k+0.392699
96 next i
130 an=an+0.1
140 goto 70
150 end
200 rem setspritepos
210 poke 53248+s*2,int(x): poke 53249+s*2,int(y)
220 return
5000 data 0,254,0
5010 data 3,199,128
5020 data 7,0,64
5030 data 12,126,96
5040 data 25,255,48
5050 data 59,7,152
5060 data 52,1,200
5070 data 116,0,204
5080 data 120,0,100
5090 data 120,0,100
5100 data 120,0,100
5110 data 120,0,36
5120 data 104,0,36
5130 data 100,0,108
5140 data 54,0,72
5150 data 51,0,152
5160 data 25,131,16
5170 data 12,124,96
5180 data 4,0,64
5190 data 3,1,128
5200 data 0,254,0

10

Uma versão compacta em javascript, alterando as configurações padrão para algo diferente

http://jsfiddle.net/yZ3DP/1/

HTML:

<canvas id="c" width="400" height="400" />

JavaScript:

var v= document.getElementById('c');
var c= v.getContext('2d');
var w= v.width, w2= w/2;
var num= 28, M2= Math.PI*2, da= M2/num;
draw();
var bw= 10;
var time= 0;
function draw()
{
    v.width= w;
    c.beginPath();
    c.fillStyle= 'black';
    circle(w2,w2,w2);
    c.lineWidth= 1.5;
    c.strokeStyle= c.fillStyle= 'white';
    var a= 0;
    for (var i=0; i< num*2; i++){
        c.moveTo(w2,w2);
        c.lineTo(w2+Math.cos(a)*w2, w2+Math.sin(a)*w2);
        a+= da/2;
    }
    c.stroke();
    a= 0;
    for (var i=0; i< num; i++){
        circle(w2+Math.cos(a)*Math.sin(time+i*Math.PI/num)*(w2-bw), 
               w2+Math.sin(a)*Math.sin(time+i*Math.PI/num)*(w2-bw), bw);
        a+= da/2;
    }
    time+=0.03;
   requestAnimationFrame(draw);
}

function circle(x,y,r)
{
    c.beginPath();
    c.arc(x, y, r, 0, M2);
    c.fill();

}

2
Você fez ... um donut ?? Na verdade, sua animação fica bem com pontos menores (tente bw=10). Edite sua resposta para mostrar seu código. Ah, e enquanto você está nisso, há um bug que você deve corrigir: substitua time+i*0.39*0.29por time+i*Math.PI/numnos cálculos trigonométricos para que as coordenadas sejam calculadas corretamente para qualquer valor de num. (PS Atualizado JSFiddle aqui . E bem-vindo ao codegolf.stackexchange.com) #
ossifrage

Eu só queria fazer algo diferente (como o das tartarugas). Novato aqui em codegolf :) Ah, e obrigado pela fórmula: DI só fiz isso com pressa e tentou valores aleatórios, não parou um minuto para chegar à fórmula correta: P
Diego

1
+1 Pequena alteração para um pouco de diversão visual: http://jsfiddle.net/9TQrm/ ou http://jsfiddle.net/Wrqs4/1/
Portland Runner

4

Minha opinião com Elm . Sou um iniciante total que aceitará PRs com prazer para melhorar esta solução ( GitHub ):

insira a descrição da imagem aqui

Observe que este envio é realmente pontos em movimento em linhas retas:

import Color exposing (..)
import Graphics.Collage exposing (..)
import Graphics.Element exposing (..)
import Time exposing (..)
import Window
import List exposing (..)
import AnimationFrame -- "jwmerrill/elm-animation-frame"
import Debug

-- CONFIG

size = 600
circleSize = 240
dotCount = 12
dotSize = 10
velocity = 0.01

-- MODEL

type alias Dot =
    { x : Float
    , angle : Float
    }

type alias State = List Dot

createDots : State
createDots = map createDot [ 0 .. dotCount - 1 ]

createDot : Int -> Dot
createDot index =
    let angle = toFloat index * pi / dotCount
    in { x = 0
       , angle = angle
       }

-- UPDATE

update : Time -> State -> State
update time dots = map (moveDot time) dots |> Debug.watch "Dots"

moveDot : Time -> Dot -> Dot
moveDot time dot =
  let t = velocity * time / pi
      newX = (-circleSize + dotSize) * cos(t + dot.angle)
  in { dot | x <- newX }

-- VIEW

view : State -> Element
view dots =
   let background = filled black (circle circleSize)
       dotLinePairs = map viewDotWithLine dots
   in collage size size (background :: dotLinePairs)

viewDotWithLine : Dot -> Form
viewDotWithLine dot =
  let dotView = viewDot dot
      lineView = createLineView
  in group [dotView , lineView] |> rotate dot.angle

viewDot : Dot -> Form
viewDot d = alpha 0.8 (filled lightOrange (circle dotSize)) |> move (d.x, 0)

createLineView : Form
createLineView = traced (solid white) (path [ (-size / 2.0, 0) , (size / 2.0, 0) ])

-- SIGNALS

main = Signal.map view (animate createDots)

animate : State -> Signal State
animate dots = Signal.foldp update dots time

time = Signal.foldp (+) 0 AnimationFrame.frame

4
Esse cursor me enganou bem, e o meu nem é preto ou desse tamanho.
cole

2

Second Life LSL

animação início da imagem alfa da tartaruga (clique com o botão direito do mouse abaixo para salvar a imagem)
turtle.png
final da imagem alfa da tartaruga (clique com o botão direito do mouse acima para salvar a imagem)

construindo o objeto:
faça uma raiz prim cilindro tamanho <1, 1, 0,01> fatia 0,49, 0,51, cor < 0, 0, 0>
faça a descrição deste cilindro "8,1,1,1" sem as aspas (muito importante)
faça um cilindro, nomeie-o "cyl", cor <0,25, 0,25, 0,25> alfa 0,5
duplicar o cyl 48 vezes
faça uma caixa, chame-a de "esfera", cor <1, ​​1, 1> transparência 100, exceto a transparência superior 0
coloque sua textura de tartaruga na face 0 da caixa, a tartaruga deve ficar de frente + x
duplicar a caixa 48 vezes
selecione todas as caixas e cilindros, selecione o último cilindro raiz,link (controle L)

coloque estes 2 scripts na raiz:

//script named "dialog"
default
{
    state_entry()
    {

    }

    link_message(integer link, integer num, string msg, key id)
    {
        list msgs = llCSV2List(msg);
        key agent = (key)llList2String(msgs, 0);
        string prompt = llList2String(msgs, 1);
        integer chan = (integer)llList2String(msgs, 2);
        msgs = llDeleteSubList(msgs, 0, 2);
        llDialog(agent, prompt, msgs, chan);
    }
}

//script named "radial animation"
float interval = 0.1;
float originalsize = 1.0;
float rate = 5;
integer maxpoints = 48;
integer points = 23; //1 to 48
integer multiplier = 15;
integer lines;
string url = "https://codegolf.stackexchange.com/questions/34887/make-a-circle-illusion-animation/34891";

list cylinders;
list spheres;
float angle;
integer running;
integer chan;
integer lh;

desc(integer on)
{
    if(on)
    {
        string desc = 
            (string)points + "," +
            (string)multiplier + "," +
            (string)running + "," +
            (string)lines
            ;

        llSetLinkPrimitiveParamsFast(1, [PRIM_DESC, desc]);
    }
    else
    {
        list params = llCSV2List(llList2String(llGetLinkPrimitiveParams(1, [PRIM_DESC]), 0));
        points = (integer)llList2String(params, 0);
        multiplier = (integer)llList2String(params, 1);
        running = (integer)llList2String(params, 2);
        lines = (integer)llList2String(params, 3);
    }    
}

init()
{
    llSetLinkPrimitiveParamsFast(LINK_ALL_OTHERS, [PRIM_POS_LOCAL, ZERO_VECTOR, 
        PRIM_COLOR, ALL_SIDES, <1, 1, 1>, 0]);
    integer num = llGetNumberOfPrims();
    integer i;
    for(i = 2; i <= num; i++)
    {
        string name = llGetLinkName(i);

        if(name == "cyl")
            cylinders += [i];
        else if(name == "sphere")
            spheres += [i];
    }  

    vector size = llGetScale();
    float scale = size.x/originalsize;

    float r = size.x/4;
    vector cylindersize = <0.01*scale, 0.01*scale, r*4>;
    float arc = 180.0/points;

    for(i = 0; i < points; i++)
    {
        float angle = i*arc;
        rotation rot = llEuler2Rot(<0, 90, 0>*DEG_TO_RAD)*llEuler2Rot(<0, 0, angle>*DEG_TO_RAD);

        integer cyl = llList2Integer(cylinders, i);
        integer sphere = llList2Integer(spheres, i);

        llSetLinkPrimitiveParamsFast(1, [PRIM_LINK_TARGET, cyl, PRIM_POS_LOCAL, ZERO_VECTOR, PRIM_ROT_LOCAL, rot, PRIM_SIZE, cylindersize, PRIM_COLOR, ALL_SIDES, <0.25, 0.25, 0.25>, 0.5*lines,
        PRIM_LINK_TARGET, sphere, PRIM_COLOR, ALL_SIDES, <0.25 + llFrand(0.75), 0.25 + llFrand(0.75), 0.25 + llFrand(0.75)>, 1
        ]);
    }
}

run()
{
    vector size = llGetScale();
    float scale = size.x/originalsize;

    float r = size.x/2;
    vector spheresize = <0.06, 0.06, 0.02>*scale;
    float arc = 180.0/points;
    list params;
    integer i;
    for(i = 0; i < points; i++)
    {

        float x = r*llCos((angle + i*arc*multiplier)*DEG_TO_RAD);

        vector pos = <x, 0, 0>*llEuler2Rot(<0, 0, i*arc>*DEG_TO_RAD);
        rotation rot = llEuler2Rot(<0, 0, i*arc>*DEG_TO_RAD);
        integer link = llList2Integer(spheres, i);
        params += [PRIM_LINK_TARGET, link, PRIM_POS_LOCAL, pos,  
            PRIM_ROT_LOCAL, rot,
            PRIM_SIZE, spheresize
            //PRIM_COLOR, ALL_SIDES, <1, 1, 1>, 1
            ];
    }   

    llSetLinkPrimitiveParamsFast(1, params);
}

dialog(key id)
{
    string runningstring;
    if(running)
        runningstring = "notrunning";
    else
        runningstring = "running";

    string linesstring;
    if(lines)
        linesstring = "nolines";
    else
        linesstring = "lines";
    string prompt = "\npoints: " + (string)points + "\nmultiplier: " + (string)multiplier;
    string buttons = runningstring + ",points+,points-,reset,multiplier+,multiplier-," + linesstring + ",www";
    llMessageLinked(1, 0, (string)id + "," + prompt + "," + (string)chan + "," + buttons, "");
    //llDialog(id, prompt, llCSV2List(buttons), chan);
}

default
{
    state_entry()
    {
        chan = (integer)("0x" + llGetSubString((string)llGetKey(), -8, -1));
        lh = llListen(chan, "", "", "");

        desc(FALSE);
        init();
        run();
        llSetTimerEvent(interval);
    }

    on_rez(integer param)
    {
        llListenRemove(lh);
        chan = (integer)("0x" + llGetSubString((string)llGetKey(), -8, -1));
        lh = llListen(chan, "", "", "");
    }

    touch_start(integer total_number)
    {
        key id = llDetectedKey(0);
        dialog(id);
    }

    timer()
    {
        if(!running)
            return;

        angle += rate;
        if(angle > 360)
            angle -= 360;
        else if(angle < 0)
            angle += 360;

        run();
    }

    listen(integer channel, string name, key id, string msg)
    {
        if(msg == "points+")
        {
            if(points < maxpoints)
            {
                points++;
                desc(TRUE);
                llResetScript();            
            }
        }
        else if(msg == "points-")
        {
            if(points > 0)
            {
                points--;
                desc(TRUE);
                llResetScript();
            }
        }        
        else if(msg == "multiplier+")
        {
            multiplier++;
            desc(TRUE);
        }
        else if(msg == "multiplier-")
        {
            multiplier--;
            desc(TRUE);
        }
        else if(msg == "running")
        {
            running = TRUE;
            desc(TRUE);
        }
        else if(msg == "notrunning")
        {
            running = FALSE;
            desc(TRUE);
        }
        else if(msg == "lines")
        {
            lines = TRUE;
            desc(TRUE);
            llResetScript();
        }
        else if(msg == "nolines")
        {
            lines = FALSE;
            desc(TRUE);
            llResetScript();
        }
        else if(msg == "reset")
            llResetScript();
        else if(msg == "www")
            llRegionSayTo(id, 0, url);
        dialog(id);
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.