Статья на тему «Формула сердца»


Линия в форме сердца

Линия в форме сердца — это дорожка, образованная фиксированной точкой на окружности, когда она катится по другому касательному кругу с таким же радиусом. Она названа в честь своей формы в виде сердца.
Линия сердца — это тоже своего рода линия моллюсков. Фигура в центре набора Мандельброта — линия сердца. Английское название линии сердца «Cardioid» было опубликовано де Кастийоном в «Philosophical Transactions of the Royal Society» в 1741 году и означает «подобный сердцу».

История [ править | править код ]

Кардиоида впервые встречается в трудах французского учёного Луи Карре (Louis Carrè

, 1705 г.). Название кривой дал в 1741 году Джованни Сальвемини ди Кастиллоне (он упоминается также как
Johann Francesco Melchiore Salvemini Castillon
).

«Спрямление», то есть вычисление длины кривой, выполнил Ла Ир (Philippe de La Hire

), который открыл кривую независимо, в 1708 году. Также независимо описал кардиоиду голландский математик Й. Коерсма (
J. Koersma
, 1741 год). В дальнейшем к кривой проявляли интерес многие видные математики XVIII—XIX веков.

Один и один — получается два. Все одиноки — здесь ты, а там я. Люди всегда одиноки вдвойне сами с собою наедине. Если б их что-то сблизить могло, сразу б из двух получилось одно. Пусть математика сложит сердца — чтобы проделать нам путь до конца.

Уильямс Джей, «Герои Ниоткуда»

Вероятно, пост следовало назвать «Как нарисовать анимированное сердечко ко дню Святого Валентина, используя математику не по назначению». Я отверг это название в пользу более поэтичного: как-никак, надвигается замечательный романтический праздник, который мы, айтишники и прочие нёрды, должны встретить во всеоружии. Я сразу покажу вам результат, а под хабракатом будет много букв о том, как я этого результата достиг.

Дисклеймер

Я осознаю, что красивое мигающее сердечко можно сделать и без малейшего знания математики. Но разве это интересно?

Шаг 1. Параметризуем сердечко.

Для начала нам нужен математический объект, хотя бы отдалённо напоминающий сердечко. К счастью, для меня этот шаг был тривиален: ещё пару лет назад я обнаружил замечательную формулу как раз для такого случая (из эстетических соображений график на рисунке растянут по горизонтали, на самом деле он должен умещаться между -1 и 1).

Формула была обнаружена из следующий соображений: возьмём обыкновенную окружность и представим, что она состоит из желе, будучи при этом жёстко прикреплена к оси ординат. Теперь «подуем» на неё снизу: прибавим к координате игрек некую функцию w(x) = w(x(t)), равную нулю при x=0, монотонно возрастающую при x>0 и чётную по x. После такого «дуновения» половинки окружности сместятся вверх, образуя «выпуклости» сердечка, а благодаря жёсткому креплению к оси Y образуется нижний «хвостик» и верхняя «вмятинка». В данном случае w(x(t)) = |x| 1/2 = |cos(t)| 1/2 . Можете самостоятельно попробовать другую «функцию дуновения» и посмотреть, что из этого выйдет.

Шаг 2. От параметрического задания к неявной функции.

x = cos(t) y = sin(t) + |cos(t)| 1/2 y — |x| 1/2 = sin(t) (y — |x| 1/2 ) 2 + x 2 = 1 f(x,y) = (y — |x| 1/2 ) 2 + x 2 — 1 = 0

Шаг 3. От неявной функции к функции двух переменных. Функция цвета.

Имея на руках f(x,y), мы наконец можем осуществить свою мечту: нарисовать красивую цветную картинку. Для этого нам понадобится ещё одна функция: функция цвета. Она должна принимать вещественный аргумент r и возвращать целое значение от 0 до 255. Также желательно, чтобы она была монотонна на каждой полуоси и имела максимум в точке нуль. В качестве такой функции можно взять, например, эту:

Здесь 100 — «магическое число», позднее мы его в полном соответствии с «хорошим стилем программирования» заменим параметром. Теперь для каждой точки (x,y) мы можем задать цвет как rgb(c(f(x,y)), 0, 0). Те точки, которые раньше принадлежали непосредственно графику «сердечка», стали ярко-красными (обратите внимание на неподвижный светлый контур на гифке). По мере удаления от графика цвет будет тускнеть, пока на некотором расстоянии от него не станет чёрным.

Шаг 4. Добавляем параметр, создаём анимацию.

Теперь заменим магическое число 100 параметром k. Новая функция цвета выглядит так:

Пусть k — это некоторая функция времени. Тогда для каждой точки изображения в каждый момент времени мы можем вычислить её цвет (что и является, по сути, математическим определением анимации). Сначала я хотел взять что-нибудь типа k(t) = 80(sin(t)+1). Потом, однако, я понял, что при большом количестве кадров гифка будет весить более 640 килобайт. С другой стороны, при малом количестве кадров нет смысла заморачиваться с аналитическим заданием k(t). В итоге, чтобы добиться пульсирования сердца, я последовательно присвоил k значения 80, 90, 100, 110, 120, 110, 100, 90, а затем изображения, сгенерированные для этих значений, объединил в циклический GIF. В общем-то, всё.

Путем различных преобразований могут быть следующие стили

Идеи решения проблем

  • В декартовой системе координат положительные направления осей x и y являются правым и верхним соответственно, а начало координат находится в середине; в то время как в командной строке положительными направлениями являются правый и нижний, а начало координат — верхний левый угол. Следовательно, требуется преобразование оси координат.
  • Поскольку кардиоидная линия в прямоугольной системе координат является горизонтальной, требуется преобразование оси x <-> y.
  • Поскольку командная строка имеет фиксированный параметр высоты строки, длина строки и столбца с одинаковым количеством символов различается (строка будет намного короче столбца), поэтому ось x консоли необходимо растянуть.

Свойства линейной функции

  1. Область определения функции — множество всех действительных чисел.
  2. Множеством значений функции является множество всех действительных чисел.
  3. График линейной функции — прямая. Для построения прямой достаточно знать две точки. Положение прямой на координатной плоскости зависит от значений коэффициентов k и b.

  4. Функция не имеет ни наибольшего, ни наименьшего значений.
  5. Четность и нечетность линейной функции зависят от значений коэффициентов k и b:

    b ≠ 0, k = 0, значит, y = b — четная;

    b = 0, k ≠ 0, значит, y = kx — нечетная;

    b ≠ 0, k ≠ 0, значит, y = kx + b — функция общего вида;

    b = 0, k = 0, значит, y = 0— как четная, так и нечетная функция.

  6. Свойством периодичности линейная функция не обладает, потому что ее спектр непрерывен.
  7. График функции пересекает оси координат:

    ось абсцисс ОХ — в точке (−b/k; 0);

    ось ординат OY — в точке (0; b).

  8. x = −b/k — является нулем функции.
  9. Если b = 0 и k = 0, то функция y = 0 обращается в ноль при любом значении переменной х.

    Если b ≠ 0 и k = 0, то функция y = b не обращается в нуль ни при каких значениях переменной х.

  10. Функция монотонно возрастает на области определения при k > 0 и монотонно убывает при k < 0.
  11. При k > 0 функция принимает отрицательные значения на промежутке (−∞; −b/k) и положительные значения на промежутке (−b/k; +∞).

    При k < 0 функция принимает отрицательные значения на промежутке (−b/k; +∞) и положительные значения на промежутке (−∞; −b/k).

  12. Коэффициент k характеризует угол, который образует прямая с положительным направлением OX. Поэтому k называют угловым коэффициентом.

    Если k > 0, то этот угол острый, если k < 0 — тупой, если k = 0, то прямая совпадает с осью OX.

Есть два частных случая линейной функции:

  • Если b = 0, то уравнение примет вид y = kx. Такая функция называется прямой пропорциональностью. График — прямая, которая проходит через начало координат.

  • Если k = 0, то уравнение примет вид y = b. График — прямая, которая параллельна оси OX и проходит через точку (0; b).

Код C ++

#include #include using namespace std; #define X_DIVIDED_BY_Y 0.5 #define MAX_X (35.0 / X_DIVIDED_BY_Y) #define MAX_Y 35.0 #define THRESHOLD 0.5 #define A 13 char getSentenceChar(const char *sentence, int &index) { while(true) { if (index >= strlen(sentence)) { index = 0; } char c = sentence[index++]; if(‘ ‘ == c) { index++; } else { return c; } } } inline float getX(float x) { return (x — MAX_X / 2) * X_DIVIDED_BY_Y; } inline float getY(float y) { return MAX_Y / 7.0 — y; } bool func(float x, float y) { return (pow(x, 2) + pow(y, 2) + A * x — A * sqrt(pow(x, 2) + pow(y, 2))) < THRESHOLD; } void main(int argc, char** argv) { const char *LOVE_SENTENCE = «No rose, no diamond ring, that is the simple and romantic love stories in college. The graduates have to face the approaching of June, a time to farewell their beloved. When their future is confronted with love, which one is more important? What will the lovers do in June?»; int sentenceIndex = 0; for (int y = 0; y <= MAX_Y; y++) { for (int x = 0; x <= MAX_X; x++) { cout<<(func(getY(y), getX(x)) ? getSentenceChar(LOVE_SENTENCE, sentenceIndex) : ‘.’); } cout<

Код Java

package com.example.demo; public class BenevolenceDemo { private static final float X_DIVIDED_BY_Y = 0.5f; private static final float MAX_X = 35f / X_DIVIDED_BY_Y; private static final float MAX_Y = 35f; private static final float THRESHOLD = 0.5f; private static final float A = 13; private static final String LOVE_SENTENCE = «No rose, no diamond ring, that is the simple and romantic love stories in college. The graduates have to face the approaching of June, a time to farewell their beloved. When their future is confronted with love, which one is more important? What will the lovers do in June?»; private static int sentenceIndex = 0; private static char getSentenceChar() { while(true) { if (sentenceIndex >= LOVE_SENTENCE.length()) { sentenceIndex = 0; } char c = LOVE_SENTENCE.charAt(sentenceIndex++); if(‘ ‘ == c) { sentenceIndex++; } else { return c; } } } public static void main(String[] args) { for (int y = 0; y <= MAX_Y; y++) { for (int x = 0; x <= MAX_X; x++) { System.out.print(func(getY(y), getX(x)) ? getSentenceChar() : ‘=’); } System.out.println(); } } public static final float getX(float x) { return (x — MAX_X / 2) * X_DIVIDED_BY_Y; } public static final float getY(float y) { return MAX_Y / 7f — y; } public static boolean func(float x, float y) { return (Math.pow(x, 2) + Math.pow(y, 2) + A * x — A * Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))) < THRESHOLD; } }
Заявление об авторских правах: укажите источник для перепечатки — http: //blog.csdn.net/chy555chy/article https://blog.csdn.net/chy555chy/article/details/52743565

Также существует класс BufferedImage для записи
import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.util.Random; import java.math.BigDecimal; import static java.lang.Math.*; public class Heart 2 { long start = System.currentTimeMillis(); private Frame f = new Frame(«Линия в форме сердца»); //Размер холста private final int SIZE=600; // Переписываем paint () private MyCanvas area = new MyCanvas(); private BufferedImage image = new BufferedImage(SIZE,SIZE,BufferedImage.TYPE_INT_RGB); private Graphics g = image.getGraphics(); // Шаг цикла private final double STEP = 0.00001; private void init(){ area.setPreferredSize(new Dimension(SIZE,SIZE)); // рисуем белый фон g.setColor(Color.white); g.fillRect(0, 0, SIZE, SIZE); // Рисуем две оси координат g.setColor(Color.black); g.drawLine(0,SIZE/2,SIZE,SIZE/2); g.drawLine(SIZE/2,0,SIZE/2,SIZE); // Рассчитываем координаты // Новые координаты int x1, y1,x2, y2; for (double t = -Math.PI; t < Math.PI; t = t + STEP){ g.setColor(Color.black); x1 = axisSystem(axisX(t)); y1 = axisSystem(axisY(t)); x2 = axisSystem(axisX(t+STEP)); y2 = axisSystem(axisY(t+STEP)); // Исходная кривая горизонтальна. Вывод настраивается так, чтобы быть красивым, а нарисованный — вертикальным. Если вы рисуете другие функции, вы должны изменить его //g.drawLine(x1, y1, x2, y2); g.drawLine(y1, SIZE-x1, y2, SIZE-x2); } // закрываем окно f.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0); } }); long end = System.currentTimeMillis(); g.setColor(Color.red); g.setFont(new Font(«Times», Font.BOLD,15)); g.drawString(«Значение шага:» + BigDecimal.valueOf(STEP) , 20, 30); g.drawString(«Время рисовать:» + (end — start) + «миллисекунда», 20, 50); area.repaint(); f.add(area); f.pack(); f.setVisible(true); } // Вычислить новое значение координаты X соответствующей линии в форме сердца в соответствии с t и изменить его, если вы рисуете другие функции private double axisX(double t){ return 2*cos(t) — cos(2*t); } // Вычислить новое значение координаты Y соответствующей линии в форме сердца в соответствии с t и изменить его, если вы рисуете другие функции private double axisY(double t){ return 2*sin(t) — sin(2*t); } // Преобразуем новые координаты в системные, если нарисованы другие функции, их нужно изменить private int axisSystem(double d){ return (int)((0.5+d/8)*SIZE); } private class MyCanvas extends Canvas{ public void paint(Graphics g){ g.drawImage(image, 0, 0, NULL); } } public static void main(String[] args){ new Сердце 2 (). Init (); } }

Свойства [ править | править код ]

  • Кардиоида является частным случаем улитки Паскаля
  • Кардиоида является частным случаем синусоидальной спирали
  • Кардиоида — алгебраическая кривая четвёртого порядка.
  • Кардиоида имеет один касп.
  • Длина дуги одного витка кардиоиды, заданной формулой в полярных координатах

r = 2 a ( 1 − cos ⁡ φ ) равна: L = 2 ∫ 0 π r ( φ ) 2 + ( r ′ ( φ ) ) 2 d φ = ⋯ = 8 a ∫ 0 π 1 2 ( 1 − cos ⁡ φ ) d φ = 8 a ∫ 0 π sin ⁡ ( φ 2 ) d φ = 16 a ^+(r'(varphi ))^2>>>;dvarphi =cdots =8aint _2>0>^>(1-cos varphi )>>;dvarphi =8aint _2>1>0>^sin(>)dvarphi =16a>

  • Площадь фигуры, ограниченной кардиоидой, заданной формулой в полярных координатах

r = 2 a ( 1 − cos ⁡ φ ) равна: S = 2 ⋅ 1 2 ∫ 0 π ( r ( φ ) ) 2 d φ = ∫ 0 π 4 a 2 ( 1 − cos ⁡ φ ) 2 d φ = ⋯ = 4 a 2 ⋅ 3 2 π = 6 π a 2 1>2>>int _2>0>^>;dvarphi =int _2>0>^(1-cos varphi )^2>>;dvarphi =cdots =4a^2>cdot 3>2>>pi =6pi a^2>>.

Радиус кривизны любой линии:

ρ ( φ ) = [ r ( φ ) 2 + r ˙ ( φ ) 2 ] 3 / 2 r ( φ ) 2 + 2 r ˙ ( φ ) 2 − r ( φ ) r ¨ ( φ ) .

2>+>(varphi )^2> ight]^>+2>(varphi )^2>-r(varphi )>(varphi )>> .>

Что даёт для кардиоиды заданной уравнением в полярных координатах:

r ( φ ) = 2 a ( 1 − cos ⁡ φ ) = 4 a sin 2 ⁡ φ 2 ,

2>2>>,> ρ ( φ ) = ⋯ = [ 16 a 2 sin 2 ⁡ φ 2 ] 3 2 24 a 2 sin 2 ⁡ φ 2 = 8 3 a sin ⁡ φ 2 . sin ^2>2>>]^3>2>>>sin ^2>2>>>>=>asin 3>8>2>> .>

Рейтинг
( 1 оценка, среднее 5 из 5 )
Понравилась статья? Поделиться с друзьями:
Для любых предложений по сайту: [email protected]