Как сделать снежинку коха

Обновлено: 05.07.2024

Author Image

В Бостоне была необыкновенно теплая зима, но мы все-таки дождались первого снегопада. Наблюдая за снегопадом в окно, я задумался о снежинках и о том, что их структуру совсем непросто описать математически. Существует однако одна, особого рода снежинка, известная как снежинка Коха, которая может быть описана сравнительно просто. Сегодня мы рассмотрим, как её форма может быть построена с помощью Среды разработки приложений COMSOL Multiphysics.

Создание снежинки Коха

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

  1. Начнем с равностороннего треугольника, который фактически является нулевой итерации снежинки Коха.
  2. Найдем центральную точку на каждом ребре текущей снежинки.
  3. В центре каждого ребра, добавим выступающий наружу равносторонний треугольник со стороной равной 1/3 длины текущего ребра.
  4. Определим следующую итерацию снежинки Коха, чтобы оказаться снаружи с внешней стороны предыдущей снежинки и всех добавленных треугольников.
  5. Повторим шаги 2-4 столько раз, сколько потребуется.

Данная процедура иллюстрируется на рисунке ниже для первых четырех итераций отрисовки снежинки.

Изображение первых четырех итераций снежинки Коха.


Первые четыре итерации снежинки Коха. Изображение от Wxs — собственная работа. Лицензия CC BY-SA 3.0, в Wikimedia Commons.

Построение геометрии снежинки Коха

Поскольку теперь мы знаем, какой алгоритм использовать, то давайте посмотрим, как создать такую структуру с помощью Среды разработки приложений COMSOL Multiphysics. Мы откроем новый файл и создадим двумерный объект геометрическая часть (geometry part) в узле Глобальные определения. Для этого объекта зададим пять входных параметров: длина стороны равностороннего треугольника; х– и y– координаты средней точки основания; и компоненты вектора нормали, направленной от середины основания к противолежащей вершине, как показано на рисунках ниже.

Иллюстрация, показывающая пять параметров для равностороннего треугольника.


Пять параметров, используемых для задания размера, положения и ориентации равностороннего треугольника.


Задание входных параметров геометрической части. Полигональный примитив используется для построения равностороннего треугольника.

Скриншот, показывающий как вращать Геометрическую часть в среде COMSOL Multiphysics.


Объект может поворачиваться вокруг центра нижнего ребра.

Изображение, показывающее перемещение объекта относительно начала отсчета.


Объект можно переместить относительно начала отсчета.

Теперь, когда мы определили геометрическую часть, используем его один раз в разделе Геометрия. Этот одиночный треугольник эквивалентен нулевой итерации снежинки Коха, и теперь давайте используем Среду разработки приложений для создания снежинок более сложной формы.

Разметка пользовательского интерфейса приложения в Среде разработки приложений

Приложение имеет очень простой пользовательский интерфейс. Оно содержит только два компонента, с которыми пользователь может взаимодействовать: Ползунок (Слайдер) (отмеченный как 1 на рисунке ниже), с помощью которого можно задавать число итераций нужных для создания снежинки, и Кнопка (метка 2), по нажатию которой создается и отображается получившаяся геометрия. Имеются также Текстовая надпись (метка 3) и Отображение (Показ) данных (метка 4), которые показывают число заданных итераций, а также окно Графики (метка 5), в котором выводится итоговая геометрия.

Изображение пользовательского интерфейса приложения для построения снежинки Коха.


Приложение имеет одну единственную форму с пятью компонентами.

В приложении есть два Определения (Declarations), одно из которых определяет целое значение, называемое Iterations , и, по умолчанию, равное нулю, но которое может быть изменено пользователем. Определяется также 1D-массив чисел двойной точности с именем Center . Единственный элемент в массиве имеет значение 0.5, которое используется для нахождения центральной точки каждого ребра. Это значение никогда не изменяется.

Изображение, показывающее настройки для двух Определений (declarations).


Настройки для двух Определений.

Компонент "Слайдер" в пользовательском интерфейсе контролирует значение целого числа, параметра Iterations . Скриншот ниже показывает настройки "Слайдера" и значений, которые задаются как целые числа в диапазоне между 0 и 5. Тот же источник (как и для слайдера) также выбран для компонента Отображение данных (Data Display) для отображения на экране приложения числа заданных итераций. Мы ограничиваем потенциального пользователя пятью итерациями, поскольку используемый алгоритм неоптимальный и является не очень эффективным, но достаточно простым для реализации и демонстрации.

Скриншот настроек компонента Cлайдер (slider).


Настройки для компонента "Слайдер".

Далее, посмотрим на настройки для нашей кнопки, показанные на скриншоте ниже. При нажатии кнопки запускаются на исполнение две команды. Сначала вызывается метод CreateSnowFlake . Затем в графическом окне выводится получившаяся геометрия.


Настройки кнопки.

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

Давайте пройдемся по коду "построчно", чтобы понять, какую функцию выполняет каждая строка:

  1. Очищаем все существующие геометрические последовательности, так чтобы мы смогли начать с нуля.
  2. Создаем один экземпляр объекта — нашего "треугольника", используя размер, ориентацию и расположение по умолчанию. Это наша снежинка нулевого порядка с меткой-идентификатором pi1 .
  3. Финализируем геометрию. Данная операция требуется для обновления всех индексов геометрии.
  4. Начнем процесс перебора всех заданных итераций снежинки, используя определение Iterations , как условие для остановки.
  5. Определяем пустой массив строк, UnionList . Каждый элемент массива содержит метку-идентификатор различных геометрических объектов. Длина этого массива равна числу ребер в последней итерации плюс один.
  6. Определяем первый элемент в массиве UnionList . Он является меткой-идентификатором результата предыдущей итерации. Имейте в виду, что нулевая итерация уже создана в строках 1-3. Целочисленное значение iter автоматически преобразуется в строку и добавится в конец строки "pi" .
  7. Перебираем число ребер в ранее сгененрированной снежинке.
  8. Задаем метку идентификатора для нового экземпляра объекта, обращающегося с геометрической части (part instance) "треугольник", который создается на этом ребре. Заметьте, что целочисленные значения iter и edge последовательно добавляются в конец строки pi , метки-идентификатора экземпляра объекта.
  9. Создаем экземпляр объекта "треугольник" и присваиваем ему метку-идентификатор, которая только что была задана.
  10. Указываем, что строки 11-15 относятся к текущему экземпляру объекта (part instance), используя оператор with()/endwith() .
  11. Определяем длину стороны треугольника. Нулевой порядок имеет длину стороны равную единице, так что n- я итерация имеет длину стороны (1/3) n . Функция toString() требуется для приведения (преобразования) типов данных — числа с плавающей точкой в строку.
  12. Задаем x-координату нового треугольника, как центральную точку стороны последней итерации. Метод edgeX задокументирован в Справочном руководстве по программированию в среде COMSOL (COMSOL Programming Reference Manual). Напомним, что Center устанавливается равным 0.5.
  13. Задаем y-координату.
  14. Задаем x-компоненту вектора нормали треугольника. Метод edgeNormal также задокументирован в Справочном руководстве по программированию в среде COMSOL (COMSOL Programming Reference Manual).
  15. Задаем y-компоненту вектора нормали.
  16. Закрываем оператор with()/endwith() .
  17. Добавляем метку-идентификатор текущего треугольника к списку всех объектов.
  18. Закрываем перебор всех ребер.
  19. Создаем Boolean Union (логическое объединение) всех объектов в геометрическую последовательность. Присваиваем метке новое значение piN , где N есть номер следующей итерации. Вокруг (iter+1) требуются круглые скобки, так чтобы увеличенное значение iter преобразовалось в строку.
  20. Указываем, что внутренние границы конечного объекта не сохраняются.
  21. Финализируем геометрию. Последняя операция обновляет все индексы геометрии для следующей итерации снежинки.
  22. Закрываем цикл итераций создания снежинки.

Таким образом, мы охватили все аспекты и элементы нашего приложения. Давайте посмотрим на результаты!

Изображение приложения для построения снежинки Коха.


Наше простое приложение для построения снежинки Коха.

Мы могли бы расширить наше приложение для записи геометрии в файл, или даже для выполнения дополнительных анализов напрямую. Например, мы могли бы спроектировать фрактальную антенну. Если вас заинтересовала конструкция антенны, познакомьтесь с нашим примером фрактальная антенна Серпинского, или даже сделайте ее макет с нуля.

Попробуйте Самостоятельно

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

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

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

Какое приложение вы бы хотели создать в среде COMSOL Multiphysics? Обращайтесь к нам за помощью.

Вам может понравиться Все решебники

ГДЗ New Millennium 11 класс

ГДЗ Rainbow 9 класс

ГДЗ Разумовская 8 класс

ГДЗ Рудзитис 10 класс

ГДЗ Дидакт. материалы 8 класс

ГДЗ Дорофеев 9 класс

Главная задача сайта: помогать школьникам и родителям в решении домашнего задания. Кроме того, весь материал совершенствуется, добавляются новые сборники решений.

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

В своем рассказе я буду отталкиваться от кода Giorgio Sardo, который в свою очередь базируется на коде David Flanagan.


Все, что описано ниже, вы можете попробовать непосредственно здесь, на Хабре в любом современном браузере со средствами разработки, просто запустив консоль JavaScript. В IE9 достаточно нажать F12 и, если вы хотите тестировать прямо на этой странице, не забудьте перевести браузер в режим Internet Explorer 9 Standards (Alt + 9), т.к. по умолчанию Хабр требует режима IE8.

Проверка поддержки Canvas

Прежде всего, надо начать с того, что нужно убедиться, что браузер поддерживает Canvas, для этого нужно создать элемент Canvas и попробовать добраться до контекста работы:

В первом случае можно двигаться дальше и запускать снежинки.

Создаем холст

Для отрисовки снежинок мы создатим холст (Canvas) на весь экран:

var canvas = document .createElement( 'canvas' );
canvas.style.position = 'fixed' ;
canvas.style.top = '0px' ;
canvas.style.left = '0px' ;
canvas.style.zIndex = '-10' ;
canvas.width = document .body.offsetWidth;
canvas.height = window.innerHeight;

document .body.insertBefore(canvas, document .body.firstChild);

В данном случае мы создаем новый элемент Canvas и задаем ему фиксированное расположение, старясь разместить его так, чтобы он не мещал остальным элементам.

Далее мы получаем контекст для отрисовки:

Снежинка Коха

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

Штука эта фрактальная и удобно рисуется рекурсивно. Чтобы отрисовать треугольник, нужно к каждому из его ребер применить последовательно один и тот же паттерн отрисовки:

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

Для сохранения и восстановления состояния матрицы трансформации используются соответственно функции save() и restore().

По ходу работы нам понадобится конвертировать градусы в радианы (хотя при желании можно и сразу в радианах писать):

Рекурсивная функция для отрисовки одного ребра выглядит так:

function leg(n, len) <
sky.save(); // Сохраняем текущую трансформацию
if (n == 0) < // Нерекурсивный случай - отрисовываем линию
sky.lineTo(len, 0); >
else <
sky.scale(1 / 3, 1 / 3); // Уменьшаем масштаб в 3 раза
leg(n – 1, len); sky.rotate(60 * deg);
leg(n – 1, len); sky.rotate(-120 * deg);
leg(n – 1, len); sky.rotate(60 * deg); leg(n – 1, len); >
sky.restore(); // Восстанавливаем трансформацию
sky.translate(len, 0); // переходим в конец ребра
>

Для запуска отрисовки можно использовать такую функцию:

function drawFlake(x, y, len, n, stroke, fill) <
sky.save(); sky.strokeStyle = stroke;
sky.fillStyle = fill;
sky.beginPath();
sky.translate(x, y);
sky.moveTo(0, 0); leg(n, len); sky.closePath();
sky.fill();
sky.stroke();
sky.restore();
>

Обратите внимание, что для отрисовки нужно запустить создание пути, если нужно, закрыть его и только потом сказать, что нужно сделать закраску областей и прорисовку линий. Результат:


Если добавить еще несколько ребер с соответствующими поворотами, получим снежинку:

function drawFlake(x, y, len, n, stroke, fill) <
sky.save(); sky.strokeStyle = stroke;
sky.fillStyle = fill;
sky.beginPath();
sky.translate(x, y);
sky.moveTo(0, 0); leg(n, len); sky.rotate(-120 * deg);
leg(n, len); sky.rotate(-120 * deg);
leg(n, len); sky.closePath();
sky.fill();
sky.stroke();
sky.restore();
>


Создание и перемещение снежинок

Дальше идея довольно прозрачная: 1) создаем пул снежинок, повесив добавление снежинок на таймер, 2) по таймеру меняем положение снежинок и делаем отрисовку.

Добавление снежинок

Дополнительная функция для случайных значений, массив снежинок и максимальное количество. Задаем таймер:

var rand = function (n) < return Math.floor(n * Math.random()); >
var flakes = []; var maxflakes = 20;
var snowspeed = 500;
var snowingTimer = setInterval(createSnowflake, snowspeed);

И собственно само создание снежинок (в нужный момент создание новых снежинок останавливается отчисткой таймера):

function createSnowflake() <
var order = 3;
var size = 10 + rand(50);
var x = rand( document .body.offsetWidth);
var y = window.pageYOffset;

Перемещение снежинок

Тут появляется дополнительная переменная invalidateMeasure, которая устанавливается в true при изменении размера экрана. Ставим таймер на обновление положения и собственно функция перемещения (очищаем экран, обновляем положение –> рисуем снежинки).

var scrollspeed = 64;
setInterval(moveSnowflakes, scrollspeed);

function moveSnowflakes() <
sky.clearRect(0, 0, canvas.width, canvas.height);

var maxy = canvas.height;

for ( var i = 0; i var flake = flakes[i];
flake.y += flake.vy;
flake.x += flake.vx;

if (flake.y > maxy) flake.y = 0;
if (invalidateMeasure) <
flake.x = rand(canvas.width);
>

drawFlake(flake.x, flake.y, flake.size, flake.order, flake.stroke, flake.fill);

// Иногда меняем боковой ветер
if (rand(4) == 1) flake.vx += (rand(11) - 5) / 10;
if (flake.vx > 2) flake.vx = 2;
if (flake.vx if (invalidateMeasure) invalidateMeasure = false ;
>

Финальный код

Дальше можно добавить еще несколько дополнительных деталей: случайный поворот снежинки и случайный цвет снежинки + детализация в зависимости от размера:

( function () <
if ( document .createElement( 'canvas' ).getContext) <
if ( document .readyState === 'complete' )
snow();
else
window.addEventListener( 'DOMContentLoaded' , snow, false );
>
else <
return ;
>

var deg = Math.PI / 180;
var maxflakes = 20; var flakes = []; var scrollspeed = 64; var snowspeed = 500;
var canvas, sky;
var snowingTimer;
var invalidateMeasure = false ;

function rand (n) <
return Math.floor(n * Math.random());
>

// Запуск снегопада
function snow() <
canvas = document .createElement( 'canvas' );
canvas.style.position = 'fixed' ;
canvas.style.top = '0px' ;
canvas.style.left = '0px' ;
canvas.style.zIndex = '-10' ;

document .body.insertBefore(canvas, document .body.firstChild);
sky = canvas.getContext( '2d' );

snowingTimer = setInterval(createSnowflake, snowspeed);
setInterval(moveSnowflakes, scrollspeed);
window.addEventListener( 'resize' , ResetCanvas, false );
>

// Сброс размеров Canvas
function ResetCanvas() <
invalidateMeasure = true ;
canvas.width = document .body.offsetWidth;
canvas.height = window.innerHeight;
>

// Отрисовка кривой Коха
function leg(n, len) <
sky.save(); // Сохраняем текущую трансформацию
if (n == 0) < // Нерекурсивный случай - отрисовываем линию
sky.lineTo(len, 0); >
else <
sky.scale(1 / 3, 1 / 3); // Уменьшаем масштаб в 3 раза
leg(n - 1, len); sky.rotate(60 * deg);
leg(n - 1, len); sky.rotate(-120 * deg);
leg(n - 1, len); sky.rotate(60 * deg); leg(n - 1, len); >
sky.restore(); // Восстанавливаем трансформацию
sky.translate(len, 0); // переходим в конец ребра
>

// Отрисовка снежинки Коха
function drawFlake(x, y, angle, len, n, stroke, fill) <
sky.save(); sky.strokeStyle = stroke;
sky.fillStyle = fill;
sky.beginPath();
sky.translate(x, y);
sky.moveTo(0, 0); sky.rotate(angle);
leg(n, len);
sky.rotate(-120 * deg);
leg(n, len); sky.rotate(-120 * deg);
leg(n, len); sky.closePath();
sky.fill();
sky.stroke();
sky.restore();
>

// Создание пула снежинок
function createSnowflake() <
var order = 2+rand(2);
var size = 10*order+rand(10);
var x = rand( document .body.offsetWidth);
var y = window.pageYOffset;
var stroke = strokes[rand(strokes.length)];

if (flakes.length > maxflakes) clearInterval(snowingTimer);
>

// Перемещение снежинок
function moveSnowflakes() <
sky.clearRect(0, 0, canvas.width, canvas.height);

var maxy = canvas.height;

for ( var i = 0; i var flake = flakes[i];

flake.y += flake.vy;
flake.x += flake.vx;

if (flake.y > maxy) flake.y = 0;
if (invalidateMeasure) <
flake.x = rand(canvas.width);
>

drawFlake(flake.x, flake.y, flake.angle, flake.size, flake.order, flake.stroke, flake.fill);

// Иногда меняем боковой ветер
if (rand(4) == 1) flake.vx += (rand(11) - 5) / 10;
if (flake.vx > 2) flake.vx = 2;
if (flake.vx if (rand(3) == 1) flake.angle = (rand(13) - 6) / 271;
>
if (invalidateMeasure) invalidateMeasure = false ;
>
> ());

* This source code was highlighted with Source Code Highlighter .

Прогрессия для области снежинки сходится в 8/5 раз от площади исходного треугольника, тогда как прогрессия по периметру снежинки расходится до бесконечности. Следовательно, снежинка имеет конечную площадь, ограниченную бесконечно длинной линией.

Шаг 1:

Нарисуйте равносторонний треугольник. Вы можете нарисовать его с помощью компаса или транспортира, или просто посмотреть на это, если не хотите тратить слишком много времени на рисование снежинки.


Шаг 2:


Разделите каждую сторону на три равные части. Вот почему удобно делить стороны на три.

Шаг 3:


Нарисуйте равносторонний треугольник на каждой средней части. Измерьте длину средней трети, чтобы узнать длину сторон этих новых треугольников.

Шаг 4:


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

Шаг 5:

Нарисуйте равносторонний треугольник на каждой средней части.


Алфавит : F
Константы : +,?
Аксиома : F
Правила производства : F? F + F-F + F

from turtle import *

def snowflake(lengthSide, levels):

snowflake(lengthSide, levels - 1 )

snowflake(lengthSide, levels - 1 )

snowflake(lengthSide, levels - 1 )

snowflake(lengthSide, levels - 1 )

if __name__ = = "__main__" :

Выход:

Чтобы создать полную снежинку с кривой Коха, нам нужно повторить один и тот же рисунок три раза. Итак, давайте попробуем это.

from turtle import *

def snowflake(lengthSide, levels):

snowflake(lengthSide, levels - 1 )

snowflake(lengthSide, levels - 1 )

snowflake(lengthSide, levels - 1 )

snowflake(lengthSide, levels - 1 )

if __name__ = = "__main__" :

for i in range ( 3 ):

Пожалуйста, пишите комментарии, если вы обнаружите что-то неправильное, или вы хотите поделиться дополнительной информацией по обсуждаемой выше теме.

Читайте также: