Кот Фибоначчи
Кот Фибоначчи

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

В фотографии часто используется правило третей и золотое сечение - фраза буквально из статьи которая выдается первой по поиску в Яндексе, при запросе золотое сечение в фотографии. Вообще есть подозрение, что не все фото делаются специально по этому правилу, но итоге почти все "красивые" фото оказываются соответствующими такому правилу, и человек просто любуется таким фото, не подозревая что любуется математикой. Мало того, любуясь фотографиями девушек, я обратил внимание, что и там часто присутствует такое же золотое сечение - женские формы и изгибы тела очень часто хорошо вписываются в золотую спираль. Смотрите сами:

 Как видно изгиб тела практически идеально повторяет форму золотой спирали.
Как видно изгиб тела практически идеально повторяет форму золотой спирали.

Для того чтобы проверять в онлайн режиме соответствие форм на форму спирали Фибоначчи, я придумал сделать букмарклет, который бы рисовал прозрачный canvas поверх фотографий, а на нем рисовалась бы сама спираль, размер которой можно было бы изменять так чтобы она вписывалась куда надо. На мое счастье при поиске генератора спирали Фибоначчи на javascript, я сходу нашел решение в виде ответа на stackoverflow.com, где в конце автор ответа дал ссылку на JSFiddle, где можно погонять спираль и понять как она работает. Важно было сделать чтобы спираль рисовалась не только из центра, а ткуда потребуется, и это решилось вставкой простой конструкции:

canvas.addEventListener("mousedown", function (e) {
    center.x = e.clientX;
    center.y = e.clientY;
}, false);

Для того чтобы спираль рисовалась поверх открытой вебстраницы, нужно добавить элемент canvas, который я сделал по размеру всего окна, и который скролится вместе с вебстраницей (свойство css position:sticky) и рисуется поверх других элементов (z-index:2000):

let el = document.getElementsByTagName("body")[0];
let canvas = document.createElement("canvas");
canvas.id = "can";
el.insertBefore(canvas, el.firstChild);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.zIndex = "2000";
canvas.style.position = "sticky";
canvas.style.top = "0";
ctx = canvas.getContext("2d");

А для того, чтобы сделать выход из режима рисования спирали (по кнопке ESC), нужно добавить в код такую конструкцию:

  document.addEventListener("keydown", function(){
        var x=event.key || event.code;
        if(x=="Escape"){
        	canvas.remove();
        }
    })  

Потом весь код копируется и минимизируется, например при помощи сервиса https://jsminify.org

Полный код
let el = document.getElementsByTagName("body")[0];
let canvas = document.createElement("canvas");
canvas.id = "can";
el.insertBefore(canvas, el.firstChild);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.zIndex = "2000";
canvas.style.position = "sticky";
canvas.style.top = "0";
ctx = canvas.getContext("2d");

// Assume ctx is canvas 2D Context and ready to render to
var width = ctx.canvas.width;
var height = ctx.canvas.height;

var center = {
    x: width / 2,
    y: height / 2
};

canvas.addEventListener("mousedown", function (e) {
    center.x = e.clientX;
    center.y = e.clientY;
}, false);

document.addEventListener("keydown", function(){
        var x=event.key || event.code;
        if(x=="Escape"){
        	canvas.remove();
        }
    })  
canvas.addEventListener('mousemove', function(){
    var mouse = {};

    mouse.x = event.offsetX;
    mouse.y = event.offsetY;
    if(mouse.x === undefined){ // if firefox
        mouse.x = event.clientX;
        mouse.y = event.clientY;
    }

    // Substract offset (so it's centered at 0,0)
    mouse.x -= center.x;
    mouse.y -= center.y;

    drawFibonacciSpiral({x:0, y:0}, mouse);
});

var drawFibonacciSpiral = function(p1, p2){
    ctx.clearRect(0, 0, width, height);

    // Draw coord axis -> center viewport at 0,0
    drawStroke([{x:0, y:-center.y}, {x:0, y:center.y}], center, "gray");
    drawStroke([{x:-center.x, y:0}, {x:center.x, y:0}], center,"gray");

    // Draw spiral -> center viewport at 0,0
    drawStroke(getSpiral(p1, p2, getDistance({x:0,y:0},center)), center);
};

var getDistance = function(p1, p2){
    return Math.sqrt(Math.pow(p1.x-p2.x, 2) + Math.pow(p1.y-p2.y, 2));
};

var getAngle = function(p1, p2){
    return Math.atan2(p2.y-p1.y, p2.x-p1.x);
};

var drawStroke = function(points, offset, strokeColor){
    // Default value
    offset = offset || {x:0,y:0}; // Offset to center on screen
    strokeColor = strokeColor || "black";

    ctx.strokeStyle = strokeColor;
    ctx.beginPath();
    var p = points[0];
    ctx.moveTo(offset.x + p.x, offset.y + p.y);
    for(var i = 1; i < points.length; i++){
        p = points[i];
        ctx.lineTo(offset.x + p.x, offset.y + p.y);
    }
    ctx.stroke();  // draw it all
};

var FibonacciGenerator = function(){
    var thisFibonacci = this;

    // Start with 0 1 2... instead of the real sequence 0 1 1 2...
    thisFibonacci.array = [0, 1, 2];

    thisFibonacci.getDiscrete = function(n){

        // If the Fibonacci number is not in the array, calculate it
        while (n >= thisFibonacci.array.length){
            var length = thisFibonacci.array.length;
            var nextFibonacci = thisFibonacci.array[length - 1] + thisFibonacci.array[length - 2];
            thisFibonacci.array.push(nextFibonacci);
        }

        return thisFibonacci.array[n];
    };

    thisFibonacci.getNumber = function(n){
        var floor = Math.floor(n);
        var ceil = Math.ceil(n);

        if (Math.floor(n) == n){
            return thisFibonacci.getDiscrete(n);
        }

        var a = Math.pow(n - floor, 1.15);

        var fibFloor = thisFibonacci.getDiscrete(floor);
        var fibCeil = thisFibonacci.getDiscrete(ceil);

        return fibFloor + a * (fibCeil - fibFloor);
    };

    return thisFibonacci;
};

var getSpiral = function(pA, pB, maxRadius){
    // 1 step = 1/4 turn or 90º

    var precision = 50; // Lines to draw in each 1/4 turn
    var stepB = 4; // Steps to get to point B

    var angleToPointB = getAngle(pA,pB); // Angle between pA and pB
    var distToPointB = getDistance(pA,pB); // Distance between pA and pB

    var fibonacci = new FibonacciGenerator();

    // Find scale so that the last point of the curve is at distance to pB
    var radiusB = fibonacci.getNumber(stepB);
    var scale = distToPointB / radiusB;

    // Find angle offset so that last point of the curve is at angle to pB
    var angleOffset = angleToPointB - stepB * Math.PI / 2;

    var path = [];

    var i, step , radius, angle, p;

    // Start at the center
    i = step = radius = angle = 0;

    // Continue drawing until reaching maximum radius
    while (radius * scale <= maxRadius){
        p = {
            x: scale * radius * Math.cos(angle + angleOffset) + pA.x,
            y: scale * radius * Math.sin(angle + angleOffset) + pA.y
        };

        path.push(p);

        i++; // Next point
        step = i / precision; // 1/4 turns at point    
        radius = fibonacci.getNumber(step); // Radius of Fibonacci spiral
        angle = step * Math.PI / 2; // Radians at point
    }

    return path;
};

Минимизированный код теперь можно поместить в букмарклет, который можно сгенерировать где-нибудь в интернете (вообще была ссылка на блог, но удалил чтобы не посчитали рекламой). А можно воспользоваться уже готовой ссылкой: fiboCheck. Эту ссылку можно добавить на панель ссылок и если увидел фото кота или красивой девушки, то теперь всегда можешь проверить ее фигуру на золотое сечение, просто щелкнув по ссылке. 

Букмарклет свою работу в принципе выполняет, но есть несколько нареканий: например, нельзя переворачивать спирать, иногда так было бы удобнее, плюс еще заметил что размер спирали ограничивается, если рисовать ее в левой части канваса. Еще обратил внимание что и сама спираль генерируется немного с переломами.

В общем-то на этом и все о чем хотелось бы рассказать. Сильно не пинайте) В конце вот еще немного красивого:

Комментарии (30)


  1. datacompboy
    11.05.2024 19:03
    +44

    Каждый раз когда вижу эти спирали к фотографиям, прям слышу как где-то кричит сова.


    1. Aquahawk
      11.05.2024 19:03
      +10

      +100. Это как фигуры в техническом анализе. Никто и никогда не показал нормальных критериев, пока всё на глазок типа ок.


    1. w00dLAN
      11.05.2024 19:03
      +3

      Сова тоже вписывается в спираль Фибоначчи :-)


      1. datacompboy
        11.05.2024 19:03
        +3

        Нинада!!!!


      1. sophist
        11.05.2024 19:03
        +2

        *натягивается на


    1. Vsevo10d
      11.05.2024 19:03

      Ещё правила третей!!


  1. deamondz
    11.05.2024 19:03
    +12

    Hidden text

    не знал, что "практически идеально" это процентов 10 от видимой линии


    1. TsarS
      11.05.2024 19:03
      +9

      О, а там, оказывается, какая то линия была.


    1. SuperCat911
      11.05.2024 19:03

      Хм, если фотку дорисовать, будет лучше?


    1. GospodinKolhoznik
      11.05.2024 19:03
      +7

      А вот если девушка прибавит в весе килограмм 40-50, то линия будет ложиться вообще идеально!


      1. w00dLAN
        11.05.2024 19:03
        +7

        Анекдот сразу вспоминается:

        - И ничего я не толстая! Мне Саша говорит, что у меня идеальная фигура.

        - Оля, он математик, для него идеальная фигура – это шар.


        1. DvoiNic
          11.05.2024 19:03
          +6

          • какая ты у меня компактная!

          • маленькая и аккуратная, да?

          • нет, замкнутая и ограниченная!

            ©


    1. w00dLAN
      11.05.2024 19:03
      +1

      Если долго смотреть на фото, то можно увидеть спираль.


    1. Vsevo10d
      11.05.2024 19:03
      +2

      Если бы эта же шаболда легла с вытянутыми ногами, то видимо стала бы вообще непривлекательной. Что за УГ уровня фишек.нет тут постят?


  1. MountainGoat
    11.05.2024 19:03
    +5

    Можно в InvokeAI загрузить эту спираль, и сгенерировать кота так, чтобы точно соответствовал кривой.


  1. SquareRootOfZero
    11.05.2024 19:03
    +7

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


  1. AndreySitaev
    11.05.2024 19:03
    +2

    Надеюсь, пост - ирония. А то полно любителей углядеть магию божественных сечений во всем, что между два и полтора.


  1. alexzzam
    11.05.2024 19:03
    +3

    Когда говорят про золотое сечение, подразумевают соотношение двух или более размеров. А если рисовать спираль и предполагать, что что-то под неё подходит, оно должно подходить на двух или более витках. А так сама спираль (примерно и на глазок) похожа в любом своём месте на дугу окружности и если считать, что приложить одно место спирали к одному круглому месту на фоточке это "подходит", то да, она подходит ко всему, в чём есть что-то круглое )


    1. Zhuikoff
      11.05.2024 19:03
      +1

      В спирали два параметра и есть, один стоя другой лёжа. Так что все честно)))


  1. kozlov_de
    11.05.2024 19:03
    +7

    Просто автор любит котиков и эротические фото.

    Спиральки им в укромные места засовывает, шалун


    1. Kenya-West
      11.05.2024 19:03

      Котов-то за шо...


  1. zubrbonasus
    11.05.2024 19:03
    +3

    У художников то, что вы называете спираль Фибоначчи называют Золотое сечение. Художники утверждают: чтобы картина была привлекательной для зрителя достаточно элементы картины расположить по золотому сечению. Золотое сечение применимо и к фотографии. Если сюжет фотографии расположить так что часть его будет вписываться в Золотое сечение - фотография будет привлекательной для зрителя и поэтому Золотое сечение очень часто используется в художественных произведениях и дизайне.


    1. randomsimplenumber
      11.05.2024 19:03
      +2

      Если сюжет фотографии расположить так что часть его будет вписываться в Золотое сечение - фотография будет привлекательной для зрителя

      Даже если горизонт завален? ;)

      Может, наоборот, в привлекательной фотке можно найти золотое сечение. А в очень привлекательной даже спираль Фибоначчи ;))


      1. Squoworode
        11.05.2024 19:03
        +1

        Даже если горизонт завален? ;)

        Да, но только если угол поворота в радианах равен коэффициенту золотого сечения.


    1. AndreySitaev
      11.05.2024 19:03

      Нескромный вопрос: Вам, случайно, не приплачивают за каждое упоминение Золотого сечения?


    1. Weron2 Автор
      11.05.2024 19:03

      Да, я с этой спиралью в первую очередь познакомился как одним из вариантов золотого сечения.


  1. Mionardele
    11.05.2024 19:03
    +1

    Если наложить спираль золотого сечения на картинку выбирая произвольно масштаб и положение то на любой картине хотя бы одна дуга хорошо впишется. И в искусстве мало кто использовал золотое сечение (например Леонардо да Винчи).

    Также в природе десяток подходящих примеров из миллионов это слабый аргумент.


    1. Weron2 Автор
      11.05.2024 19:03

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

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


      1. randomsimplenumber
        11.05.2024 19:03

        Вот вам идея для программы: ищет на произвольной картинке границу, в которую максимально точно вписывается спираль максимальной длины. Все, можно ранжировать по индексу Фибоначчи;)


  1. TemArtem
    11.05.2024 19:03

    Я зашел на pornhabr?