logo
+7 (495) 997-37-74
Москва, ул.Международная, 15

Работа в Processing со строками и вывод текста

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

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

Строки (String) в Processing

В своей основе String — это просто удобный способ хранения массива символов. Если бы у нас не было класса String, то нам, вероятно, пришлось бы написать такой код:

char[] text = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};

Гораздо проще создать специализированный объект String:

String text = "Hello World";

Из вышесказанного видно, что String — это не что иное, как список (набор) символов (которые и представляют собой хранимую строку). Но String — это объект, который кроме данных содержит также и методы для работы с ними.

Например, метод charAt() возвращает отдельный символ строки с заданным индексом. Обратите внимание, что строки похожи на массивы в том смысле, что первым символом является символ с индексом номер 0.

String message = "message";
char c = message.charAt(3);
println(c); // результат 's'

Другим полезным методом является метод length(), который возвращает количество символов в строке.

String message = "строка";
println(message.length()); // результат равен 6-и

Мы также можем изменить содержимое строки, преобразовав все символы в верхний регистр, используя метод toUpperCase(), или в нижний регистр при помощи toLowerCase().

String uppercase = message.toUpperCase();
println(uppercase); 

Почему мы просто не использовали «message.toUpperCase()» и не напечатали переменную «message»? Вместо этого мы присвоили результат «message.toUpperCase()» новой переменной с другим именем «upppercase».

Это происходит потому, что String является особым неизменяемым видом объекта. Как только мы создаём String, он остаётся неизменным. Когда мы хотим изменить String, мы должны создать новый объект этого типа.

Ещё один метод объекта String — это метод equals() (равенство). Строки можно сравнить при помощи оператора «==»:

String one = "hello";
String two = "hello";
println(one == two);

Но тут нужно иметь в виду, что когда оператор «==» используется с объектами, то он сравнивает адреса памяти для каждого объекта. Даже если они содержат одни и те же данные (но являются различными экземплярами объекта) применение оператора «==» может привести к ложному результату сравнения. Функция equals() позволяет корректно проверить, содержат ли два объекта String одну и ту же последовательность символов, независимо от того где эти данные хранятся в памяти.

String one = "hello";
String two = "hello";
println(one.equals(two));

Хотя в данном случае оба вышеперечисленных метода возвращают правильный результат, безопаснее использовать equals(). В зависимости от того, как строковые объекты создаются в скетче, оператор «==» работает не всегда корректно.

Еще одной особенностью работы со строками является их конкатенация (объединение). Строки можно соединить при помощи оператора «+». Плюс обычно означает сложение (чисел), но при использовании строк этот оператор означает соединение.

String helloworld = "Hello" + "World";

Переменные также могут быть включены в строку с помощью конкатенации.

int x = 10;
String message = "Значение равно: " + x;

Список всех методов объекта String:

charAt() — возвращает символ, находящийся в позиции с указанным индексом

equals() — сравнивает строку с указанным объектом

indexOf() — возвращает значение индекса первого вхождения подстроки во входной строке

length() — возвращает количество символов во входной строке

substring() — возвращает новую строку как часть исходной строки

toLowerCase() — конвертирует все символы в нижний регистр

toUpperCase() — конвертирует все символы в верхний регистр

Тут ещё нужно заметить, что реализация работы со строками в Processing является подмножеством реализации работы со строками в Java. При желании вы можете более детально изучить этот вопрос, обратившись к документации по Java.

А далее мы переходим к рассмотрению вывода текста на дисплей в Processing.

Вывод текста

Здесь нужно различать два вида вывода текста в Processing: это вывод на дисплей (собственно вывод текста в скетчах) и вывод диагностических сообщений в консоль среды программирования Processing.

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

println(mouseX);

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

println("Код выполнен");

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

Шаг 1. Объявить объект типа PFont.

PFont f;

Шаг 2. Создать шрифт, указав имя шрифта и функцию createFont().

Это должно быть сделано только один раз, обычно в setup(). Загрузка шрифта в память является медленной операцией и может серьезно повлиять на производительность скетча, поэтому не рекомендуется использовать её в функции draw().

Список доступных системных шрифтов можно просмотреть с помощью функции PFont.list(). Из-за ограничений в Java не все шрифты могут использоваться и некоторые могут работать с одной операционной системой, и не работать с другими. При предоставлении общего доступа к скетчу другим пользователям или его размещении в интернете может потребоваться включить версию шрифта .ttf или .otf в каталог

data

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

f = createFont("Arial", 16, true);

Шаг 3. Определить шрифт с помощью textFont().

Функция textFont() принимает один или два аргумента (переменную шрифта и его размер). Если размер шрифта не указан, шрифт будет отображаться в первоначально загруженном размере. По возможности функция text() использует собственный шрифт, а не битовую версию, созданную с помощью createFont(), что позволяет динамически масштабировать шрифт.

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

textFont(f, 36);

Шаг 4. Определить цвет, используя функцию fill().

fill(255); // задан белый цвет

Шаг 5. Использовать функцию text() для непосредственно вывода текста.

Эта функция использует три аргумента — отображаемый текст и координаты x и y для отображения этого текста.

text("Hello World", 10, 10);

Теперь все шаги вместе и шаблон простого скетча вывода текста на дисплей:

PFont f;
  
void setup() {
  size(200,200);
  f = createFont("Arial", 16, true);
}

void draw() {
  background(255);
  textFont(f, 16);
  fill(0);
  text("Hello World", 10, 10);
}

Шрифты также можно создавать с помощью пунктов меню «Tools/Create Font» среды программирования Processing. Это приведет к созданию и размещению файла шрифта VLW в каталоге data скетча, который можно загрузить в объект PFount с помощью функции loadFont().

f = loadFont("ArialMT-16.vlw");

Анимация текста

Теперь рассмотрим функции Processing, связанные с отображением текста:

textAlign() — задает выравнивание текста RIGHT, LEFT или CENTER.

Пример использования выравнивание текста:

PFont f;

void setup() {
  size(400,200);
  f = createFont("Arial", 16, true); 
}

void draw() {
  background(255);

  stroke(175);
  line(width/2, 0, width/2, height);

  textFont(f);       
  fill(0);

  textAlign(CENTER); text("This text is centered.", width/2, 60); 
  textAlign(LEFT);   text("This text is left aligned.", width/2, 100); 
  textAlign(RIGHT);  text("This text is right aligned.", width/2, 140); 
}

textWidth() — вычисляет и возвращает ширину любого символа или всей текстовой строки.

Допустим, мы хотим создать бегущую строку новостей, где текст прокручивается по нижней части экрана слева направо. Когда заголовок новостей выходит за пределы окна, он снова появляется в правой части и снова прокручивается. Если мы знаем местоположение X начала текста и ширину этого текста, мы можем определить, когда он больше не находится в поле зрения. Функция textWidth() сообщает нам эту ширину.

Для начала объявим переменные заголовка, шрифта и местоположения x, инициализируя их в setup().

String headline = "Длинная строка для прокручивания";
PFont f;
float x;  // горизонтальное положение строки

void setup() {
  f = createFont("Arial", 16, true);
  x = width; // ширина экрана
}

В функции draw() текст отображается в соответствующем переменной x месте.

textFont(f, 16);        
textAlign(LEFT);
text(headline, x, 180); 

Мы изменяем x на некоторую величину (значение скорости прокрутки).

x = x - 3;

Поскольку текст выровнен по левому краю, когда х равен нулю, он доступен для просмотра на экране. Текст будет невидимым, если x меньше 0 минус ширина текста (см. рисунок ниже). Когда это так, мы сбрасываем x обратно в правую сторону окна.

float w = textWidth(headline);

if (x < -w) {
  x = width; 
}

Вот полный пример скетча:

String[] headlines = {
  "Processing downloads break downloading record.", 
  "New study shows computer programming lowers cholesterol.",
  };

PFont f;
float x;
int index = 0;

void setup() {
  size(400, 200);
  f = createFont("Arial", 16, true);  
  x = width; 
}

void draw() {
  background(255);
  fill(0);

  // Display headline at x  location
  textFont(f, 16);        
  textAlign(LEFT);
  text(headlines[index], x, 180); 

  // Decrement x
  x = x - 3;

  // If x is less than the negative width, then it is off the screen
  float w = textWidth(headlines[index]);

  if (x < -w) {
    x = width; 
    index = (index + 1) % headlines.length;
  }
}

Помимо функций textAlign() и textWidth(), Processing также имеет и другие функции для вывода и работы с текстом: textLeading(), textMode(), textSize().

Вращение текста

К тексту также могут быть применены преобразования и вращение. Например, чтобы повернуть текст вокруг его центра, переместите его в начальную точку, используя textAlign(CENTER) перед отображением текста.

Пример вращения текста:

PFont f;
String message = "Текст для вращения";
float theta;

void setup() {
  size(200, 200);
  f = createFont("Arial", 20, true);
}

void draw() { 
  background(255);
  fill(0);
  textFont(f);
  translate(width/2, height/2);
  rotate(theta);
  textAlign(CENTER);            
  text(message, 0, 0);
  theta += 0.05;
}

Здесь: theta — это переменная для хранения текущего угла поворота текста.

Посимвольное отображение текста

В некоторых графических приложениях (например, демо и Processing-арте) требуется посимвольное отображение текста. Функция text() тут нам не поможет, поскольку выводит всю строку целиком. Решение состоит в циклическом проходе по строке и отображении каждого символа по отдельности.

Начнем с простого примера, отображающего весь текст сразу.

PFont f;
String message = "Each character is written individually.";

void setup() {
  size(400, 200);
  f = createFont("Arial", 20, true);
}

void draw() { 
  background(255);
  fill(0);
  textFont(f);         
  text(message, 10, height/2);
}

Мы можем переписать код для отображения каждого символа в цикле, используя функцию charAt().

String message = "Each character is written individually.";

int x = 10; 
for (int i = 0; i < message.length(); i++) {
  text(message.charAt(i), x, height/2);
  x += 10; 
}

Вызов функции text() для отображения каждого отдельного символа даст нам больше возможностей для раскраски, изменения размера и размещения символов текста. Приведенный выше код имеет довольно большой недостаток — местоположение x увеличивается (строго) на 10 пикселей для каждого символа. Хотя это и работает, но на самом деле ширина каждого символа не равна точно десяти пикселям.

Правильный интервал может быть задан с помощью функции textWidth(), как показано в приведенном ниже коде. Обратите внимание, в этом примере вычисляется правильный интервал, даже если каждый символ имеет случайный размер.

PFont f;
String message = "Each character is written individually.";

void setup() {
  size(400, 150);
  f = createFont("Arial", 20, true);
}

void draw() { 
  background(255);
  fill(0);
  textFont(f);         
  int x = 10;
  for (int i = 0; i < message.length(); i++) {
    textSize(random(12, 36));
    text(message.charAt(i), x, height/2);
    x += textWidth(message.charAt(i)); 
  }
  noLoop();
}

Эта методика «буква за буквой» также может применяться в случае, если символы строки перемещаются независимо друг от друга. В следующем примере объектно-ориентированная конструкция позволяет представить каждый символ в виде объекта Letter, позволяя ему одновременно отображаться в нужном месте, а также независимо перемещаться по экрану.

PFont f;
String message = "click mouse to shake it up";
// An array of Letter objects
Letter[] letters;

void setup() {
  size(260, 200);
  // Load the font
  f = createFont("Arial", 20, true);
  textFont(f);
  
  letters = new Letter[message.length()];
  int x = 16;
  for (int i = 0; i < message.length(); i++) {
    letters[i] = new Letter(x,100,message.charAt(i)); 
    x += textWidth(message.charAt(i));
  }
}

void draw() { 
  background(255);
  for (int i = 0; i < letters.length; i++) {
    letters[i].display();
    
    if (mousePressed) {
      letters[i].shake();
    } else {
      letters[i].home();
    }
  }
}

class Letter {
  char letter;
  float homex, homey;
  float x, y;

  Letter (float x_, float y_, char letter_) {
    homex = x = x_;
    homey = y = y_;
    letter = letter_; 
  }

  void display() {
    fill(0);
    textAlign(LEFT);
    text(letter, x, y);
  }

  void shake() {
    x += random(-2, 2);
    y += random(-2, 2);
  }

  void home() {
    x = homex;
    y = homey; 
  }
}

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

Код:

PFont f;
float r = 100;
float w = 40;
float h = 40;

void setup() {
  size(320, 320);
  smooth();
}

void draw() {
  background(255);
  
  translate(width / 2, height / 2);
  noFill();
  stroke(0);
  ellipse(0, 0, r*2, r*2);

  int totalBoxes = 10;
  float arclength = 0;
  
  for (int i = 0; i < totalBoxes; i++) {
    arclength += w/2;
    float theta = arclength / r;     
    
    pushMatrix();
    translate(r*cos(theta), r*sin(theta));
    rotate(theta);
    fill(0,100);
    rectMode(CENTER);
    rect(0,0,w,h);  
    popMatrix();
    arclength += w/2;
  }
}

Нам нужно заменить каждый ящик символом из строки, который помещается внутри него. И поскольку все символы не имеют одинаковой ширины, вместо использования переменной "w", которая остается постоянной, каждое поле будет иметь переменную ширину вдоль кривой в соответствии с функцией textWidth().

Код:

String message = "text along a curve";

PFont f;
float r = 100;

void setup() {
  size(320, 320);
  f = createFont("Georgia", 40, true);
  textFont(f);
  textAlign(CENTER);
  smooth();
}

void draw() {
  background(255);

  translate(width / 2, height / 2);
  noFill();
  stroke(0);
  ellipse(0, 0, r*2, r*2);

  float arclength = 0;

  for (int i = 0; i < message.length(); i++) {
    char currentChar = message.charAt(i);
    float w = textWidth(currentChar);

    arclength += w/2;
    float theta = PI + arclength / r;    

    pushMatrix();
    translate(r*cos(theta), r*sin(theta));
    rotate(theta + PI/2);
    fill(0);
    text(currentChar, 0, 0);
    popMatrix();
    arclength += w/2;
  }
}

Заключение

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

Ссылки по теме

Система программирования Processing

Processing и Ардуино. Работа по Serial

Сетевые возможности Processing

Работа Processing в браузере и на сайте

Веб-сервер на Processing. Часть 1 — Код сервера

Веб-сервер на Processing. Часть 2 — Файлы и код на HTML и CSS

Работа в Processing со строками и вывод текста

Работа в Processing с изображениями и отдельными пикселями