15 KiB
title | description | sections | tags | canonical_url | url_translated | title_translated | date | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Рисуем сердечко в консоли | Напишем два варианта алгоритма на Java для вывода сердечка в консоль в форме текстового изображения — поздравим женщин с восьмым марта. Нарисуем график... |
|
|
/ru/2023/03/08/drawing-heart-in-console.html | /en/2023/03/08/drawing-heart-in-console.html | Drawing heart in console | 2023.03.08 |
Напишем два варианта алгоритма на Java для вывода сердечка в консоль в форме текстового изображения — поздравим женщин с восьмым марта. Нарисуем график функции в форме сердечка и в дополнение нарисуем символ сердечко в форме картинки, а картинку выведем текстом — консольное поздравление с восьмым марта.
{% include heading.html text="График в форме сердечка" hash="heart-shaped-graph" %}
Нарисуем два полукруга и один полуромб, заполненные внутри и снаружи. В предыдущем примере мы выводили [график функции в консоль]({{ '/ru/2023/02/05/function-graph-in-console.html' | relative_url }}) — формулы для окружности и для ромба возьмём из него, а в этом примере для заполнения фигуры внутри, в формуле вместо знака равно подставляем знак меньше, а для заполнения снаружи, наоборот, — знак больше. Условий получится много, в отличие от предыдущего примера.
Нарисуем картинку для наглядности.
{% include picture.html src="/img/heart-graph.png" background=true alt="График в форме сердечка — это два полукруга и один полуромб" caption="Два полукруга и один полуромб" %}
Выводим верхнюю часть фигуры, нижнюю часть фигуры, закрашиваем в шахматном порядке и выводим координатные оси. Получаем несколько текстовых изображений, которые выглядят следующим образом.
Радиус: 5, внутри/снаружи/оси: true/true/true.
· · · · · ↑y · · · · ·
· · o o o o o · ¦ · o o o o o · ·
· o * * o · o * * o ·
· o * * * o ¦ o * * * o ·
o * * * * o * * * * o
· o * * * * * o * * * * * o ·
--o --* --* --* --* --+---* --* --* --* --o >x
· o * * * * ¦ * * * * o ·
· o * * * * * * * o ·
· · o * * * ¦ * * * o · ·
· · o * * * * * o · ·
· · · o * * ¦ * * o · · ·
· · · o * * * o · · ·
· · · · o * ¦ * o · · · ·
· · · · o * o · · · ·
· · · · · o ¦ o · · · · ·
· · · · · o · · · · ·
· · · · · · ¦ · · · · · ·
{% capture collapsed_md %}
Радиус: 4, внутри/снаружи/оси: false/true/false.
· · · · · · · · · ·
· o o o o o · o o o o o ·
· o o o o o o o o ·
o o o o o o o
· o o o ·
o o
· o o ·
· o o ·
· · o o · ·
· · o o · ·
· · · o o · · ·
· · · o o · · ·
· · · · o o · · · ·
· · · · o · · · ·
· · · · · · · · · ·
Радиус: 3, внутри/снаружи/оси: true/false/false.
o o o o o o
o * * o o * * o
o * * * o * * * o
o * * * * * o
o * * * * o
o * * * o
o * * o
o * o
o o
o
Радиус: 2, внутри/снаружи/оси: false/false/false.
o o o o o o
o o o
o o
o o
o o
o o
o
{% endcapture %} {%- include collapsed_block.html summary="Полный вывод" content=collapsed_md -%}
Обходим диапазон координат двумя вложенными циклами for
: сначала по оси y
и затем по оси x
. Каждую
точку проверяем на соответствие условиям и выводим. В верхней части рисуем два полукруга и опционально
закрашиваем их внутри/снаружи. В нижней части рисуем полуромб и также опционально закрашиваем внутри/снаружи.
/**
* @param r радиус
* @param gap отступ
* @param in заполнение внутри
* @param out заполнение снаружи
* @param axes координатные оси
*/
public static void printHeartGraph(
int r, int gap, boolean in, boolean out, boolean axes) {
// границы текстового изображения
int xMax = 2*r+gap, xMin = -xMax;
int yMax = r+gap, yMin = -r-yMax;
System.out.println( // заголовок с параметрами
"Радиус: "+r+", внутри/снаружи/оси: "+in+"/"+out+"/"+axes+".");
// вывод в консоль построчно слева направо сверху вниз
for (int y = yMax; y >= yMin; y--) {
for (int x = xMin; x <= xMax; x++) {
double[] circle = { // две окружности левая/правая
Math.round(Math.sqrt(Math.pow(x+r,2)+Math.pow(y,2))), // левая
Math.round(Math.sqrt(Math.pow(x-r,2)+Math.pow(y,2)))}; // правая
int rhombus = Math.abs(x)+Math.abs(y); // ромб
boolean inCh = in && (x+y)%2 == 0; // шахматный порядок внутри
boolean outCh = out && (x+y)%2 == 0; // шахматный порядок снаружи
// каждую точку проверяем на соответствие условиям и выводим
if (axes && y == 0 && x == 0)
System.out.print("+-"); // начало координат
else if (axes && y == 0 && x == xMax)
System.out.print(">x"); // максимум оси абсцисс (x)
else if (axes && x == 0 && y == yMax)
System.out.print("↑y"); // максимум оси ординат (y)
else if (y > 0 && (circle[0] == r || circle[1] == r))
System.out.print("o "); // два полукруга, верх
else if (y > 0 && inCh && (circle[0] < r || circle[1] < r))
System.out.print("* "); // верх внутри
else if (y > 0 && outCh && (circle[0] > r && circle[1] > r))
System.out.print("· "); // верх снаружи
else if (y <= 0 && rhombus == 2*r)
System.out.print("o "); // полуромб, низ
else if (y <= 0 && inCh && rhombus < 2*r)
System.out.print("* "); // низ внутри
else if (y <= 0 && outCh && rhombus > 2*r)
System.out.print("· "); // низ снаружи
else if (axes && y == 0)
System.out.print("--"); // ось абсцисс (x)
else if (axes && x == 0)
System.out.print("¦ "); // ось ординат (y)
else
System.out.print(" "); // пустое место
} // переход на новую строку
System.out.println();
}
}
// запускаем программу и выводим результат
public static void main(String[] args) {
printHeartGraph(5, 1, true, true, true);
printHeartGraph(4, 1, false, true, false);
printHeartGraph(3, 0, true, false, false);
printHeartGraph(2, 0, false, false, false);
}
{% include heading.html text="Текст картинкой и картинка текстом" hash="text-as-picture-and-picture-as-text" %}
В предыдущем примере мы [рисовали простую капчу]({{ '/ru/2023/01/03/drawing-simple-captcha.html' | relative_url }}) — алгоритм отрисовки шрифта возьмём из него, только на этот раз нарисуем бинарное чёрно-белое изображение моноширинным шрифтом, сглаживание не используем. Символ сердечко в форме картинки выглядит следующим образом.
{% include picture.html src="/img/heart-monospaced-plain-22.bmp" alt="Символ сердечко, шрифт моноширинный, обычный, 22" title="Символ сердечко, шрифт моноширинный, обычный, 22" %}
Поскольку символ находится в середине строки, то на полученном изображении половина пикселей пустые. Обходим пиксели построчно и выводим только непустые строки, то есть центральную часть изображения.
Monospaced.plain, 22, символы: ♡
o o o
o o o o o o o
o o o o
o o o
o o o
o o
o o
o o
o o
o o
o o
o o
o o
o
{% capture collapsed_md %}
Monospaced.plain, 28, символы: ♡
o o
o o o o o o o o
o o o o
o o o o
o o o
o o o
o o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
o
Monospaced.plain, 36, символы: ♡
o o o o o o o
o o o o o o o
o o o o
o o o o
o o o o
o o o
o o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
{% endcapture %} {%- include collapsed_block.html summary="Полный вывод" content=collapsed_md -%}
Рисуем строку текста в виде чёрно-белого изображения, затем обходим пиксели этого изображения и выводим их текстом в консоль построчно. Рисуем только центральную часть изображения с текстом, пустые строки пикселей не выводим.
// рисуем текст в форме картинки и картинку в форме текста
public static void printTextImage(String str, Font font) {
FontRenderContext ctx = // контекст отображения шрифта
new FontRenderContext(font.getTransform(), false, false);
// получаем размеры картинки с текстом при отрисовке
Rectangle bnd = font.getStringBounds(str, ctx).getBounds();
// создаём новое бинарное чёрно-белое изображение
BufferedImage image = new BufferedImage(
bnd.width, bnd.height, BufferedImage.TYPE_BYTE_BINARY);
// включаем режим редактирования нового изображения
Graphics2D graphics = image.createGraphics();
// шрифт для отрисовки, сглаживание не используем
graphics.setFont(font);
// рисуем картинку с текстом
graphics.drawString(str, bnd.x, -bnd.y);
// отключаем режим редактирования
graphics.dispose();
// выводим заголовок
System.out.println(
font.getFontName()+", "+font.getSize()+", символы: "+str);
// обходим пиксели построчно и выводим непустые строки
for (int y = 0; y < bnd.height; y++) {
StringBuilder line = new StringBuilder();
for (int x = 0; x < bnd.width; x++)
line.append(image.getRGB(x, y) == -1 ? "o " : " ");
// рисуем только непустые строки
if (line.indexOf("o") != -1) System.out.println(line);
}
}
// запускаем программу и выводим результат
public static void main(String[] args) {
printTextImage("♡", new Font(Font.MONOSPACED, Font.PLAIN, 22));
printTextImage("♡", new Font(Font.MONOSPACED, Font.PLAIN, 28));
printTextImage("♡", new Font(Font.MONOSPACED, Font.PLAIN, 36));
}
В последнем примере используется библиотека Java AWT.
{% capture collapsed_md %}
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.image.BufferedImage;
{% endcapture %} {%- include collapsed_block.html summary="Необходимые импорты" content=collapsed_md -%}