Сделайте анимацию иллюзии круга

84

Ваша задача - оживить эту иллюзию круга . Это выглядит как точки, вращающиеся внутри круга, но на самом деле они просто движутся по прямым линиям.

введите описание изображения здесь

Характеристики

  • Результат должен быть анимированным. То, как вы делаете анимацию, не имеет значения, она может генерировать .gif, она может рисовать в окне, на экране какого-либо устройства или что-то еще.
  • Это конкурс популярности, поэтому вы можете добавить некоторые дополнительные функции в свою программу, чтобы получить больше голосов, например, изменить количество баллов.
  • Побеждает самый upvoted действительный ответ 7 дней после последнего действительного представления.
  • Ответы, которые фактически реализуют точки, движущиеся по прямым линиям, а не наоборот, более приветствуются
рассекать
источник
«победитель считается самым активным после 7 дней». Так что, если кто-то публикует что-то каждые 6 дней, пока звезды не умрут, у нас нет победителя?
Кевин Л.
3
@KevinL, что вряд ли произойдет, и я не думаю, что эти 15 дополнительных повторений настолько важны по сравнению со всеми ответами, которые вы получите от этого вопроса, который поднимается на вершину каждые 6 дней.
Мартин Эндер
1
Иногда мне кажется, что некоторые люди делают это только для того, чтобы выполнить работу ...
Даниэль Пендергаст,
3
«Похоже, что точки вращаются внутри круга, но на самом деле они просто движутся по прямым линиям», или, может быть, они действительно вращаются внутри круга и, кажется, движутся по прямым линиям ...
coredump
1
Не могу ... убрать эту анимацию ... из головы ... особенно 3-точечная версия!
Томас

Ответы:

126

Python 3.4

Используя модуль черепахи. Черепахи разных цветов, и они всегда смотрят в одном направлении, поэтому можно легко увидеть, что они движутся по прямым линиям, просто сфокусировавшись на одном из них. Несмотря на это, иллюзия круга все еще сильна.

11 черепах

Иллюзия все еще кажется довольно сильной даже с 3 или 4 черепахами:

3 черепахи4 черепахи

Частота кадров значительно уменьшается для всех этих примеров GIF, но это, похоже, не умаляет иллюзии. Запуск кода локально дает более плавную анимацию.

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

Для контраста вот некоторые, которые действительно вращаются:

23 петлевые черепахи23 трилистника черепахи

... или они?

Код можно запустить с 5 необязательными аргументами: заполнение, разрешение, циклы, переворот и строки.

  • population это количество черепах
  • resolution разрешение по времени (количество кадров анимации на повторение)
  • loopsопределяет, сколько раз черепахи возвращаются на себя. Значение по умолчанию 1 дает стандартный круг, другие нечетные числа дают это число петель в цепочке черепах, в то время как четные числа дают цепочку черепах, отсоединенных на концах, но все еще с иллюзией изогнутого движения.
  • flipесли ненулевое значение заставляет черепах перевернуть направление для их возвращения (как предложено aslum чтобы они никогда не двигались назад). По умолчанию они сохраняют фиксированное направление, чтобы избежать визуального отвлечения на конечных точках.
  • lines если ненулевое значение, отображает линии, по которым движутся черепахи, для соответствия с примером изображения в вопросе.

Примеры с flipмножеством, с и без lines. Я оставил свой основной пример выше без переворота, так как я предпочитаю не делать спорадический прыжок, но край круга выглядит более гладким со всеми выровненными черепахами, так что есть возможность выбора людей, какой стиль они предпочитают при беге код.

11 черепах с флипом и линиями11 черепах с флипом

Может быть не сразу очевидно, как все изображения, представленные выше, были получены из этого же кода. В частности, изображение вверху, которое имеет медленную внешнюю петлю и быструю внутреннюю петлю (ту, которая выглядит как кардиоида, которую кто-то случайно уронил). Я скрыл объяснение этого ниже на тот случай, если кто-то захочет отложить это в процессе экспериментов / размышлений.

Анимация с внутренним и внешним циклами разных размеров была создана путем установки числа петель равным 15 и оставления количества черепашек равным 23 (слишком мало, чтобы представлять 15 петель). Использование большого количества черепах привело бы к 15 четко определенным петлям. Использование слишком небольшого количества черепах приводит к появлению псевдонимов (по той же причине, что и при обработке и рендеринге изображений). Попытка представить слишком высокую частоту приводит к отображению более низкой частоты с искажением.

Пробуя разные числа, я нашел некоторые из этих искажений более интересными, чем более симметричные оригиналы, поэтому я хотел бы включить их здесь ...

Trichoplax
источник
18
Мне нравятся черепахи.
FreeAsInBeer
18
Я
выложил
@ProgramFOX спасибо за подсветку синтаксиса! Я искал справку и мета и убедил себя, что у нас нет подсветки синтаксиса в коде гольф - теперь я намного счастливее.
трихоплакс
1
@aslum, это было бы простым изменением, но я хотел, чтобы их ориентация была заморожена, чтобы подчеркнуть, что они не отклоняются от курса по прямой. Может быть, я должен добавить его в код в качестве опции, чтобы люди могли выбрать подход, который они предпочитают.
трихоплакс
4
+1 - Было бы здорово увидеть, как оркестр делает некоторые из этих забавных!
mkoistinen
96

С

Результат:

введите описание изображения здесь

#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
В некоторых кадрах это немного не так. Но поздравляю за это в ASCII!
Юстфолф
10
+1 за ASCII иsystem("cls"); // xD
Кристоф Бемвальдер
1
Это прекрасно.
Трихоплакс
1
Этот работает на Linux. (хотя довольно печально)
user824294
Обязательный ненавистный комментарий: «Это не C! Стандарт не определяет Sleep, COORD или SetConsoleCursorPosition!»
user253751
52

SVG (без Javascript)

JSFiddle ссылка здесь

<?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>
брезгливый оссифраж
источник
Хммм, я уверен, что это проходит проверку с правилами, но я был лично разочарован тем, что вы на самом деле делаете обратное. Вместо «Похоже, что точки вращаются внутри круга, но на самом деле они просто движутся по прямым линиям» . Ваш реализует: «Похоже, что точки [движутся] по прямым линиям, но на самом деле они просто вращается внутри круга . »
mkoistinen
Плавный ответ!
Дерек 朕 會 功夫
14
@mkoistinen Я понимаю, что вы имеете в виду, но на самом деле движутся по прямым линиям. Просто так проще вычислить их позиции с двумя поворотами :-)
брезгливое оссифраж
Вы делали все это «вручную» или использовали какой-либо (нетекстовый) редактор?
flawr
5
@ Flawr Я только что использовал текстовый редактор и калькулятор в своем телефоне, чтобы вычислить числа :-)
брезгливый оссифраж
47

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

intervaltimeизменяет FPS (FPS = 1000 / интервал времени).
ballsменяет # шарики.
maxstepрегулирует количество шагов в цикле: чем больше, тем «плавнее». 64 должно быть достаточно большим, где это кажется гладким.

Смоделирован как движущийся круг, вместо того, чтобы перемещать шарики по линиям, но визуальный эффект (должен быть?) Одинаков. Часть кода довольно многословна, но это не кодовый гольф, так что ...

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
Это так гладко! Очень хорошо.
nneonneo
5
Не используйте setInterval для анимации, возьмите requestAnimationFrameвместо этого . Изменено использование JSFiddlerequestAnimationFrame .
klingt.net
1
С помощью нескольких настроек параметров вы получаете совершенно другую вещь .
FreeAsInBeer
@KevinL Да, только что заметил это. Обновлено.
FreeAsInBeer
1
@FreeAsInBeer О, когда ты сказал совсем другое, я подумал, что ты имеешь
Кевин Л
41

CSS анимация

Решение, использующее только анимацию CSS (см. Анимацию в JSFiddle - обратите внимание, что я добавил специфичные для браузера префиксы в скрипте, чтобы он мог работать в самых последних версиях).

<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
Скрипка не работает для меня на последнем Chrome = /
mkoistinen
1
@mkoistinen - вы должны добавить разные префиксы, чтобы он работал в разных браузерах. ( -webkit-для Webkit и -moz-для Mozilla) Вот та же скрипка с обновленными префиксами: jsfiddle.net/nBCxz/3
Дерек 功夫 會 功夫
@mkoistinen Ты прав. Новая скрипка добавляет все необходимые браузерные префиксы и работает на последней версии Chrome.
Говард
В необработанном тексте ссылки просто отсутствует закрывающая круглая скобка - он по-прежнему идеально подходит, просто давая вам знать, если вы хотите это исправить (я не могу изменить, так как это менее 6 символов для изменения).
Trichoplax
35

Mathematica

Вот довольно прямолинейное представление.

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

Если вы позвоните, animateCircle[32]вы получите аккуратную анимацию с 32 линиями и кругами.

введите описание изображения здесь

В Mathematica это абсолютно гладко, но мне пришлось немного ограничить количество кадров для GIF.

Что произойдет, если вы поместите два диска в каждую строку? (То есть добавить Disk[-Sin[t + phase]*line, 0.05]в список внутри Map.)

введите описание изображения здесь

Вы также можете поместить их на 90 ° в противофазе (используйте Cosвместо -Sin):

введите описание изображения здесь

Мартин Эндер
источник
Я не знаю, какие глюки вы имеете в виду, вероятно, вам нужно изменить, {t, 0, 2 \[Pi]}чтобы {t, 0, 2 \[Pi] - 2 \[Pi]/60, 2 \[Pi]/60}не было двух одинаковых кадров и изменить Animateна Table. Тогда вы сможете экспортировать GIF.
swish
@swish Нет, на самом деле это делает странные дополнительные строки, которых там нет, и диски в тех местах, где их не должно быть (и где они никогда не находятся в реальном результате Animate). Я попробую использовать Tableснова, хотя.
Мартин Эндер
@ SWISH Это сработало. Я думал, что попробовал что-то подобное вчера, но, видимо, я этого не сделал.
Мартин Эндер
25

VBScript + VBA + круговая диаграмма Excel

Это заставит ваш процессор немного плакать, но выглядит красиво, и я считаю, что он работает в соответствии со спецификацией. Я использовал ответ @ Fabricio в качестве руководства для реализации алгоритма кругового движения.

РЕДАКТИРОВАТЬ: внесены некоторые изменения, чтобы улучшить скорость рендеринга.

Скриншот круговой диаграммы

Код:

'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
comfortablydrei
источник
У меня происходит сбой в строке 81, ошибка 80070057, «элемент с указанным именем не существует» или что-то в этом роде (перевод с венгерского, поэтому я не знаю точного сообщения об ошибке).
marczellm
Szervusz, @marczellm. Я могу воспроизвести эту ошибку, когда нажимаю за пределами графика, пока он «анимируется». Вы должны разрешить фокусировку, иначе программа выдаст ошибку. В противном случае это может быть связано с несовместимостью с Office. Я нахожусь на Office 2010 на Win7.
комфортно
Офис 2007, Win7. Похоже, в моем случае график вообще не фокусируется.
Marczellm
21

Excel, 161 байт

превосходить

=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 (период) определяет время (секунды) для полного «оборота».

Каждая ячейка в строках является основным условием, относящимся к значению соответствующей строки. Например, К2 это:

 =1*(A5=7)

И центральная ячейка (K9):

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

Вынудили анимацию, удерживая «delete» в случайной ячейке, чтобы постоянно вызывать обновление.

Я знаю, что это старая тема, но недавняя активность вывела ее на вершину, и она почему-то показалась привлекательной. Долгое время слушатель pcg, впервые звонящий. Быть нежным.

qoou
источник
Вау, это невероятно, что вы можете сделать это с помощью Excel: D
Beta Decay
15

Просто для удовольствия с PSTricks.

введите описание изображения здесь

\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

Фортран

Каждый кадр создается как отдельный gif-файл с использованием gif-модуля Fortran по адресу: http://fortranwiki.org/fortran/show/writegif.
Затем я немного изменяю, используя ImageMagick для объединения отдельных gif-файлов в один анимированный gif.

Фортран

ОБНОВЛЕНИЕ: Установите новый = .true. чтобы получить следующее:

введите описание изображения здесь

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
Мне нравится эффект «сдавливания» для вертикальных и горизонтальных элементов.
Портлендский бегун
11

Обязательная версия C64 .

Скопируйте и вставьте в ваш любимый эмулятор:

Версия 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

Компактная версия JavaScript, изменение настроек по умолчанию на что-то другое

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
Вы сделали ... пончик ?? На самом деле ваша анимация выглядит хорошо с небольшими точками (попробуйте bw=10). Пожалуйста, измените свой ответ, чтобы показать свой код. Да, и в то время как вы на это, есть ошибка , вы должны исправить: заменить time+i*0.39*0.29с time+i*Math.PI/numв расчетах тригонометрических так , что координаты вычисляются правильно для любого значения num. (PS Обновленный JSFiddle здесь . И добро пожаловать на codegolf.stackexchange.com)
брезгливый оссифраж
Я просто хотел сделать что-то другое (например, черепаху). Новичок здесь, в Codegolf :) О, и спасибо за формулу: D Я просто сделал это в спешке и пробовал случайные значения, не останавливаясь ни минуты, чтобы добраться до правильной формулы: P
Диего
1
+1 Небольшое изменение для небольшого визуального веселья: http://jsfiddle.net/9TQrm/ или http://jsfiddle.net/Wrqs4/1/
Портлендский бегун
4

Мой взять с вязом . Я абсолютный новичок, который с радостью примет PR для улучшения этого решения ( GitHub ):

введите описание изображения здесь

Обратите внимание, что это представление действительно перемещает точки по прямым линиям:

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
Этот курсор обманул меня хорошо, а мой даже не черный или такого размера.
Коул
2

Second Life LSL

анимация начало альфа-изображения черепахи (щелкните правой кнопкой мыши ниже, чтобы сохранить изображение)
turtle.png
конец альфа-изображения черепахи (щелкните правой кнопкой мыши выше, чтобы сохранить изображение),

создающего объект:
создайте корневой цилиндр первичного цилиндра размером <1, 1, 0,01> срез 0,49, 0,51, цвет < 0, 0, 0>
сделать описание этого цилиндра "8,1,1,1" без кавычек (очень важно)
сделать цилиндр, назовите его "цил", цвет <0,25, 0,25, 0,25> альфа 0,5
дублируйте Если 48 раз
сделать коробку, назовите ее «сфера», цвет <1, 1, 1> прозрачность 100, за исключением верхней прозрачности 0,
поместите текстуру вашей черепахи на лицо 0 коробки, черепаха должна стоять на лице + x
продублировать коробку 48 раз
выберите все коробки и цилиндры, убедитесь, что последний цилиндр выбран последним,ссылка (контроль L)

поместите эти 2 сценария в корень:

//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);
    }
}
Маккарп Мавендорф
источник