Assuming that you have read Drawing 2d text

Here is a WebGL example showing what’s taking place in Sketch.h and Sketch.cpp from the DrawingLotsOf2dText project’s source folder.

If you run the example, you can see a linear spiral with the lyrics of 8 songs by the Smiths. There is about 9000 glyphs, and the text engine is capable of rendering even more at 60fps.

Sketch.cpp

Inside setup()

vector<u16string> songs;
for (auto &name : {"song1.txt", "song2.txt", "song3.txt", "song4.txt", "song5.txt", "song6.txt", "song7.txt", "song8.txt"})
{
  auto lines = utils::readLines<u16string>(InputSource::resource(name));

  u16string text;
  for (const auto &line : lines)
  {
      text += line + u" ";
  }

  songs.push_back(text);
}

We create 8 strings. Each of them is made of all the lines of a song, separated by spaces.

Then we load our font, and we create a FontSequence. Note that this is done only once, i.e. it is a static sequence. The effect of rotation comes from the camera.

font->beginSequence(sequence, true);

float offset = 0;
bool red = false;
for (const auto &song : songs)
{
  glm::vec4 color = red ? glm::vec4(0.75f, 0, 0, 1) : glm::vec4(0, 0, 0, 1);
  font->setColor(color);

  offset = drawTextSpiral(*font, song, 30, 900, 45, offset, XFont::ALIGN_MIDDLE);
  red ^= true;
}

font->endSequence();

When calling beginSequence(): if we pass true as second parameter, it indicates that it will be possible to use a different color for each glyph.

Then, for each song:

  • We define the current color, alternating between black or red.
  • We call the drawTextSpiral() function and assign its return value to offset, so that the next song can be placed just after the current song.

Let’s take a look at drawTextSpiral:

float Sketch::drawTextSpiral(XFont &font, const u16string &text, float r1, float r2, float turns, float offset, chr::XFont::Alignment alignY)
{
  float l = TWO_PI * turns;
  float dr = (r2 - r1) / l;

  float offsetY = font.getOffsetY(alignY);
  Matrix matrix;

  for (auto c : text)
  {
    auto glyphIndex = font.getGlyphIndex(c);
    float halfWidth = font.getGlyphAdvance(glyphIndex) / 2;
    offset += halfWidth;

    if (glyphIndex >= 0)
    {
      float r = sqrtf(r1 * r1 + 2 * dr * offset);
      float d = (r - r1) / dr;

      matrix
        .setTranslate(-sinf(-d) * r, +cosf(-d) * r)
        .rotateZ(-d);

      font.addGlyph(matrix, glyphIndex, -halfWidth, offsetY);
    }

    offset += halfWidth;
  }

  return offset;
}

A linear spiral is defined by two radius values and a number of turns. For any offset on the spiral, it’s possible to calculate linearly the right position and angle.

As always, we use a 4x4 Matrix. For each character in the text, we translate it to the right offset on the spiral and rotate it to the right angle. The idea of using halfWidth is to properly draw the glyph from its horizontal center.