Zrób animację iluzji koła


84

Twoim zadaniem jest animowanie iluzji tego kręgu . Wygląda na to, że punkty obracają się wewnątrz koła, ale w rzeczywistości poruszają się one w linii prostej.

wprowadź opis zdjęcia tutaj

Kryteria

  • Wynik musi być animowany. Sposób, w jaki animacja jest nieistotna, może wygenerować .gif, może narysować okno, ekran urządzenia lub cokolwiek innego.
  • To konkurs popularności, więc możesz chcieć dodać dodatkowe funkcje do swojego programu, aby uzyskać więcej głosów pozytywnych, na przykład zmieniając liczbę punktów.
  • Zwycięzca jest najbardziej pozytywnie ocenioną ważną odpowiedzią 7 dni po ostatnim ważnym zgłoszeniu.
  • Odpowiedzi, które faktycznie wprowadzą punkty poruszające się po liniach prostych, a nie odwrotnie, są mile widziane

„Zwycięzca jest najbardziej pozytywnie oceniany po 7 dniach”. Więc jeśli ktoś publikuje coś co 6 dni, aż gwiazdy umrą, nie mamy zwycięzcy?
Kevin L.

3
@KevinL, co jest mało prawdopodobne, i nie sądzę, aby te 15 dodatkowych powtórzeń były tak ważne w porównaniu do wszystkich głosów pozytywnych, które można uzyskać od tego pytania, które trafiają na szczyt co 6 dni.
Martin Ender

1
Czasami zastanawiam się, czy niektórzy ludzie robią to tylko po to, aby wykonać pracę ...
Daniel Pendergast

3
„Wygląda na to, że punkty obracają się wewnątrz koła, ale tak naprawdę poruszają się po liniach prostych.”, A może naprawdę obracają się wewnątrz koła i wydają się poruszać w liniach prostych ...
coredump

1
Nie mogę ... wyrzucić mi z głowy tej animacji ... zwłaszcza wersji 3-punktowej!
Thomas

Odpowiedzi:


126

Python 3.4

Korzystanie z modułu żółwia. Żółwie mają różne kolory i zawsze skierowane są w tym samym kierunku, dzięki czemu można łatwo zobaczyć, jak poruszają się po prostych liniach, skupiając się tylko na jednym z nich. Mimo to iluzja kół jest nadal silna.

11 żółwi

Iluzja wciąż wydaje się dość silna nawet przy 3 lub 4 żółwiach:

3 żółwie4 żółwie

Liczba klatek na sekundę jest znacznie zmniejszona dla wszystkich tych przykładów GIF, ale wydaje się, że nie umniejsza iluzji. Lokalne uruchomienie kodu zapewnia płynniejszą animację.

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:]))

Dla kontrastu oto kilka, które naprawdę się obracają:

23 żółwie pętlowe23 żółwie koniczyny

... czy oni?

Kod można uruchomić z 5 opcjonalnymi argumentami: zapełnianie, rozdzielczość, pętle, odwracanie i wiersze.

  • population to liczba żółwi
  • resolution to rozdzielczość czasowa (liczba klatek animacji na powtórzenie)
  • loopsokreśla, ile razy żółwie zapętlają się na sobie. Domyślna wartość 1 daje standardowy okrąg, inne liczby nieparzyste dają tę liczbę pętli w ciągu żółwi, podczas gdy liczby parzyste dają ciąg żółwi odłączonych na końcach, ale wciąż z iluzją zakrzywionego ruchu.
  • flipjeśli niezerowa powoduje, że żółwie obracają się w kierunku swojej podróży powrotnej (zgodnie z sugestią azylu , aby nigdy nie cofały się). Domyślnie utrzymują stały kierunek, aby uniknąć rozproszenia wzrokowego w punktach końcowych.
  • lines jeśli niezerowa wyświetla linie, po których poruszają się żółwie, dla zachowania spójności z przykładowym obrazem w pytaniu.

Przykłady z flipzestawem, zi bez lines. Zostawiłem swój główny przykład powyżej bez odwrócenia, ponieważ wolę nie wykonywać sporadycznego skoku, ale krawędź koła wygląda gładko z wyrównanymi wszystkimi żółwiami, więc istnieje możliwość wyboru stylu, który preferują podczas biegania kod.

11 żółwi z klapką i liniami11 żółwi z klapką

Może nie być od razu oczywiste, jak powyższe obrazy zostały utworzone z tego samego kodu. W szczególności obraz dalej w górę, który ma powolną pętlę zewnętrzną i szybką pętlę wewnętrzną (tę, która wygląda jak kardioida, którą ktoś przypadkowo upuścił). Ukryłem wyjaśnienie tego poniżej na wypadek, gdyby ktoś chciał opóźnić odkrycie podczas eksperymentowania / myślenia.

Animacja z wewnętrzną i zewnętrzną pętlą o różnych rozmiarach została stworzona przez ustawienie liczby pętli na 15 i pozostawienie liczby żółwi na 23 (zbyt niska, aby reprezentować 15 pętli). Użycie dużej liczby żółwi dałoby 15 jasno określonych pętli. Użycie zbyt małej liczby żółwi powoduje aliasing (z tego samego powodu, co w przypadku przetwarzania i renderowania obrazu). Próba przedstawienia zbyt wysokiej częstotliwości powoduje wyświetlenie niższej częstotliwości z zniekształceniem.

Próbując różnych liczb, zauważyłem, że niektóre z tych zniekształceń są bardziej interesujące niż bardziej symetryczne oryginały, więc chciałem je tutaj zamieścić ...


18
Lubię żółwie.
FreeAsInBeer,


@ProgramFOX dzięki za wyróżnianie składni! Przeszukałem pomoc i meta i przekonałem się, że nie mamy podświetlania składni na temat golfa kodowego - jestem teraz znacznie bardziej zadowolony.
trichoplax

1
@aslum byłaby to prosta zmiana, ale chciałem, aby ich orientacja została zamrożona, aby naprawdę podkreślić, że nie zbaczają z linii prostej. Może powinienem dodać go do kodu jako opcję, aby ludzie mogli wybrać preferowane podejście.
trichoplax

4
+1 - Byłoby wspaniale widzieć, jak maszerujący zespół robi te zabawne!
mkoistinen,

96

do

Wynik:

wprowadź opis zdjęcia tutaj

#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
W niektórych klatkach jest to trochę nie w porządku. Ale gratulacje za zrobienie tego w ASCII!
justhalf

10
+1 dla ASCII isystem("cls"); // xD
Christoph Böhmwalder

1
To jest piękne.
trichoplax

1
Ten działa na systemie Linux. (choć dość nieszczęśliwie)
user824294

Obowiązkowy komentarz hejtera: „To nie jest C! Standard nie definiuje opcji Sleep, COORD ani SetConsoleCursorPosition!”
immibis

52

SVG (bez Javascript)

Link do JSFiddle tutaj

<?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, jestem pewien, że to spełnia zasady, ale osobiście byłem rozczarowany, że w rzeczywistości robisz coś przeciwnego. Zamiast „Wygląda na to, że punkty obracają się wewnątrz koła, ale tak naprawdę poruszają się po liniach prostych .”, Twoja implementuje: „Wygląda na to, że punkty poruszają się po liniach prostych, ale w rzeczywistości po prostu wiruje wewnątrz koła . ”
mkoistinen

Płynniejsza odpowiedź!
Derek 朕 會 功夫

14
@mkoistinen Rozumiem, co masz na myśli, ale punkty naprawdę poruszają się w linii prostej. Po prostu łatwiej jest obliczyć ich pozycje za pomocą dwóch obrotów :-)
piskliwy ossifrage

Czy robiłeś to wszystko „ręcznie”, czy korzystałeś z jakiegokolwiek edytora (nietekstowego)?
flawr

5
@flawr Właśnie użyłem prostego edytora tekstu i kalkulatora w moim telefonie, aby obliczyć liczby :-)
piskliwy ossifrage

47

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

intervaltimezmienia FPS (FPS = 1000 / interwał).
ballszmienia # piłki.
maxstepdostosowuje liczbę kroków w cyklu, tym większe jest „wygładzenie”. 64 powinno być wystarczająco duże tam, gdzie wydaje się gładkie.

Modelowany jako ruch koła, zamiast przesuwania kulek wzdłuż linii, ale efekt wizualny (powinien być?) Taki sam. Część kodu jest dość szczegółowa, ale to nie jest golf golfowy, więc ...

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
To takie gładkie! Bardzo dobrze.
nneonneo

5
Nie używaj setInterval do animacji, requestAnimationFramezamiast tego weź . Zmodyfikowano użycie JSFiddlerequestAnimationFrame .
klingt.net

1
Wystarczy kilka poprawek parametrów, aby uzyskać zupełnie inną rzecz .
FreeAsInBeer,

@KevinL Tak, właśnie to zauważyłem. Zaktualizowano
FreeAsInBeer,

1
@FreeAsInBeer Och, kiedy powiedziałeś coś zupełnie innego, pomyślałem, że masz na myśli takie jak te w jsfiddle.net/z6vhD/100
Kevin L

41

Animacje CSS

Rozwiązanie wykorzystujące tylko animacje css (patrz animacja na JSFiddle - zauważ, że dodałem przedrostki specyficzne dla przeglądarki w skrzypcach, aby mogło działać w najnowszych wersjach).

<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
Fiddle nie działa dla mnie w najnowszym Chrome = /
mkoistinen

1
@mkoistinen - Musisz dodać różne prefiksy, aby działał w różnych przeglądarkach. ( -webkit-dla bazujących na Webkit i -moz-Mozilli) Oto to samo skrzypce ze zaktualizowanymi prefiksami: jsfiddle.net/nBCxz/3
Derek 朕 會 功夫

@mkoistinen Masz rację. Nowe skrzypce dodaje wszystkie niezbędne prefiksy przeglądarki i działa w najnowszym Chrome.
Howard

W nieprzetworzonym tekście linku brakuje tylko nawiasu zamykającego - nadal jest on w pełni użyteczny, po prostu powiadamiając cię na wypadek, gdybyś chciał go naprawić (nie mogę, ponieważ zmiana jest mniejsza niż 6 znaków).
trichoplax

35

Matematyka

Oto całkiem proste przesłanie.

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]}
  ]

Jeśli zadzwonisz animateCircle[32], otrzymasz ładną animację z 32 liniami i okręgami.

wprowadź opis zdjęcia tutaj

W Mathematica jest całkowicie gładka, ale musiałem trochę ograniczyć liczbę klatek na GIF.

Co się stanie, jeśli umieścisz dwie płyty w każdej linii? (To znaczy, dodaj Disk[-Sin[t + phase]*line, 0.05]do listy w Map.)

wprowadź opis zdjęcia tutaj

Możesz także ustawić je o 90 ° poza fazą (użyj Coszamiast -Sin):

wprowadź opis zdjęcia tutaj


Nie wiem, co masz na myśli, prawdopodobnie musisz się zmienić, {t, 0, 2 \[Pi]}aby {t, 0, 2 \[Pi] - 2 \[Pi]/60, 2 \[Pi]/60}nie było dwóch identycznych ramek i zmienić Animatena Table. Następnie będziesz mógł wyeksportować GIF.
swish

@swish Nie, tak naprawdę renderuje dziwne dodatkowe wiersze, których nie ma, a dyski w miejscach, w których nie powinny być (i gdzie nigdy nie są w rzeczywistości Animate). Spróbuję jednak użyć Tableponownie.
Martin Ender

@swish To działało. Myślałem, że spróbowałem wczoraj czegoś takiego, ale najwyraźniej nie.
Martin Ender

25

Wykres kołowy VBScript + VBA + Excel

To sprawi, że twój procesor trochę płacze, ale wygląda ładnie i uważam, że działa zgodnie ze specyfikacją. Użyłem odpowiedzi @ Fabricio jako przewodnika po implementacji algorytmu ruchu koła.

EDYCJA: Wprowadzono pewne poprawki, aby poprawić szybkość renderowania.

Zrzut ekranu wykresu kołowego

Kod:

'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

Awarie dla mnie w linii 81, błąd 80070057, „element o podanej nazwie nie istnieje” lub coś takiego (przetłumaczone z węgierskiego, dlatego nie znam dokładnego komunikatu o błędzie).
marczellm

Szervusz, @marczellm. Mogę odtworzyć ten błąd, kiedy klikam poza wykresem, gdy jest on „animowany”. Musisz pozwolić mu się skupić, w przeciwnym razie program popełni błąd. W przeciwnym razie może to wynikać z niezgodności z pakietem Office. Jestem na Office 2010 na Win7.
komfortowodrei

Office 2007, Win7. Wygląda na to, że w moim przypadku wykres w ogóle się nie skupia.
marczellm

21

Excel, 161 bajtów

Przewyższać

=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 (kropka) określa czas (w sekundach) pełnego „obrotu”.

Każda komórka w liniach jest podstawowym warunkiem związanym z wartością odpowiedniej linii. Na przykład K2 to:

 =1*(A5=7)

A komórka środkowa (K9) to:

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

Wymuś animację, przytrzymując klawisz „usuń” na losowej komórce, aby stale uruchamiać odświeżanie.

Wiem, że jest to stary temat, ale ostatnie działania doprowadziły go na szczyt i z jakiegoś powodu wydawało się to atrakcyjne. Słuchacz PCG od dawna, osoba dzwoniąca po raz pierwszy. Bądź delikatny.


Wow, to niesamowite, że możesz to zrobić za pomocą Excela: D
Beta Decay

15

Tylko dla zabawy z PSTricks.

wprowadź opis zdjęcia tutaj

\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

Każda ramka jest tworzona jako osobny plik gif przy użyciu modułu Fortran gif pod adresem : http://fortranwiki.org/fortran/show/writegif
Następnie oszukiwam za pomocą ImageMagick, aby scalić poszczególne gify w jeden animowany gif.

Fortran

AKTUALIZACJA: Ustaw nowy = .prawda. aby uzyskać następujące:

wprowadź opis zdjęcia tutaj

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
Podoba mi się efekt „squish” dla elementów pionowych i poziomych.
Portland Runner,

11

Obowiązkowa wersja C64 .

Skopiuj i wklej w swoim ulubionym emulatorze:

Wersja 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

Kompaktowa wersja javascript, zmieniająca ustawienia domyślne na coś innego

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
Zrobiłeś ... pączka ? W rzeczywistości twoja animacja wygląda ładnie z mniejszymi kropkami (spróbuj bw=10). Edytuj swoją odpowiedź, aby wyświetlić kod. Aha, i podczas gdy jesteś na to, że to błąd należy naprawić zamienić time+i*0.39*0.29ze time+i*Math.PI/numw obliczeniach trygonometrycznych tak, że współrzędne są obliczone prawidłowo dla każdej wartości num. (PS Zaktualizowano tutaj JSFiddle . Witamy w codegolf.stackexchange.com)
ossifrage

Chciałem tylko stworzyć coś innego (jak żółwie). Nowicjusz tutaj w codegolf :) Och, i dziękuję za formułę: DI po prostu spieszyłem się i wypróbowałem losowe wartości, nie zatrzymałem się na minutę, aby dostać się do prawidłowej formuły: P
Diego

1
+1 Mała zmiana dla małej wizualnej zabawy: http://jsfiddle.net/9TQrm/ lub http://jsfiddle.net/Wrqs4/1/
Portland Runner

4

Moje podejście do Wiązu . Jestem całkowicie początkującym, który chętnie zaakceptuje PR w celu ulepszenia tego rozwiązania ( GitHub ):

wprowadź opis zdjęcia tutaj

Zauważ, że to przesłanie naprawdę porusza punkty na liniach prostych:

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
Ten kursor oszukał mnie dobrze, a mój nie jest nawet czarny ani taki rozmiar.
cole

2

Second Life LSL

animacja początek obrazu alfa żółwia (kliknij prawym przyciskiem myszy poniżej, aby zapisać obraz)
turtle.png
koniec obrazu alfa żółwia (prawym przyciskiem myszy powyżej, aby zapisać obraz)

budowanie obiektu:
zrób korzeń pierwotny rozmiar cylindra <1, 1, 0,01> plasterek 0,49, 0,51, kolor < 0, 0, 0>
uczyń opis tego cylindra „8,1,1,1” bez cudzysłowów (bardzo ważne)
utwórz cylinder, nazwij go „cyl”, kolor <0,25, 0,25, 0,25> alfa 0,5
zduplikuj cyl 48 razy
stwórz pudełko, nazwij je „kula”, kolor <1, ​​1, 1> przezroczystość 100 oprócz górnej przezroczystości 0
umieść teksturę żółwia na powierzchni 0 pudełka, żółw powinien stawić czoła + x
powielić pole 48 razy
zaznacz wszystkie pudełka i cylindry, pamiętaj, aby wybrać cylinder główny jako ostatni,link (kontrola L)

umieść te 2 skrypty w katalogu głównym:

//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);
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.