Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Curved text drawing  (Read 5138 times)

0 Members and 1 Guest are viewing this topic.

cpolymeris

  • Jr. Member
  • **
  • Posts: 65
    • View Profile
    • Email
Curved text drawing
« on: July 03, 2013, 06:54:30 pm »
So, I am trying to draw some curved text. The results so far:


And my code (C#): EDIT: See wiki.

There are a few problems:
  • It's not *that* great to use one Text object for each character, but I can't think  of another way that still takes advantage of SFML's text loading and rendering.
  • Rendering bugs. I think they are related to using the bounds of the Texts to calculate position (rotation anchor), and they seem to not exactly match the size of the glyph.
  • I wanted to do the rotation on the glyphs instead of on the state's transform, but Text.Transform is not writtable. Why?
  • Ignoring kerning. Left that for when the rest works better.
« Last Edit: July 05, 2013, 07:31:45 am by cpolymeris »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Curved text drawing
« Reply #1 on: July 03, 2013, 07:25:04 pm »
Quote
It's not *that* great to use one Text object for each character, but I can't think  of another way that still takes advantage of SFML's text loading and rendering.
You can use the Font class and build your own vertex array of rotated glyphs (quads). That would be a lot more work, but a lot more efficient (one CurvedText would be equivalent to a single Text).

Quote
I wanted to do the rotation on the glyphs instead of on the state's transform, but Text.Transform is not writtable. Why?
Because SFML classes have a higher level interface made of position, rotation, scale and origin. You can get the resulting transform, but not set it directly. But it's fine to do it with the state's transform.

Quote
Ignoring kerning. Left that for when the rest works better.
If you implement the more efficient technique, it won't be a problem ;)
Laurent Gomila - SFML developer

cpolymeris

  • Jr. Member
  • **
  • Posts: 65
    • View Profile
    • Email
Re: Curved text drawing
« Reply #2 on: July 04, 2013, 06:47:13 am »
Quote
It's not *that* great to use one Text object for each character, but I can't think  of another way that still takes advantage of SFML's text loading and rendering.
You can use the Font class and build your own vertex array of rotated glyphs (quads). That would be a lot more work, but a lot more efficient (one CurvedText would be equivalent to a single Text).

Yeah, that works much better: :)



And it wasn't even *that* much work, since a big part of it was "translating" your Text code:

public class CurvedText : Transformable, Drawable
{
        public CurvedText(
                string str,
                float radius,
                float spacing,
                Font font,
                uint charSize) : this(str, radius, font, charSize)
        {
                Spacing = spacing;
        }

        public CurvedText(
                string str,
                float radius,
                Font font = null,
                uint charSize = 30)
        {
                vertices = new VertexArray(PrimitiveType.Quads);
                DisplayedString = str;
                CharacterSize = charSize;
                Radius = radius;
                Spacing = 0;
                Font = font;
        }

        public void Draw(RenderTarget target, RenderStates states)
        {
                if (Font == null)
                        return;
                states.Transform *= Transform;
                if (Centered)
                        states.Transform *= centerTransform;
                states.Texture = Font.GetTexture(CharacterSize);
                target.Draw(vertices, states);
        }

        protected void UpdateGeometry()
        {
                if (font == null)
                        return;
                vertices.Clear();
                float hspace = Font.GetGlyph(' ', CharacterSize, true).Advance;
                float vspace = Font.GetLineSpacing(CharacterSize);
                float previousX = 0;
                float x = 0;
                float y = CharacterSize;
                char previousChar = '\0';
                float angleCovered = 0;

                Transform transform = Transform.Identity;

                foreach (char c in DisplayedString)
                {
                        x += Font.GetKerning(previousChar, c, CharacterSize) + Spacing;
                        previousChar = c;

                        switch (c)
                        {
                                case ' ':
                                        x += hspace;
                                        continue;
                                case '\t':
                                        x += 4 * hspace;
                                        continue;
                                case '\n':
                                        y += vspace;
                                        previousX = 0;
                                        angleCovered = 0;
                                        transform = Transform.Identity;
                                        x = 0;
                                        continue;
                                case '\v':
                                        y += vspace * 4;
                                        continue;
                        }

                        Glyph g = Font.GetGlyph(c, CharacterSize, false);
                        float x0 = x  + g.Bounds.Left;
                        float y0 = y  + g.Bounds.Top;
                        float x1 = x0 + g.Bounds.Width;
                        float y1 = y0 + g.Bounds.Height;
                        float u0 =      g.TextureRect.Left;
                        float v0 =      g.TextureRect.Top;
                        float u1 = u0 + g.TextureRect.Width;
                        float v1 = v0 + g.TextureRect.Height;

                        float angle = 2f * (float)Math.Atan((x - previousX) / 2f / Radius)
                                * 180f / (float)Math.PI;
                        angleCovered += angle;

                        transform.Rotate(angle, x0 - g.Bounds.Width / 2f, Radius / 2f);

                        Vector2f topLeft         = new Vector2f(x0, y0);
                        Vector2f bottomLeft  = new Vector2f(x0, y1);
                        Vector2f topRight        = new Vector2f(x1, y0);
                        Vector2f bottomRight = new Vector2f(x1, y1);

                        topLeft         = transform.TransformPoint(topLeft);
                        topRight        = transform.TransformPoint(topRight);
                        bottomLeft      = transform.TransformPoint(bottomLeft);
                        bottomRight = transform.TransformPoint(bottomRight);

                        vertices.Append(new Vertex(topLeft,     Color, new Vector2f(u0, v0)));
                        vertices.Append(new Vertex(topRight,    Color, new Vector2f(u1, v0)));
                        vertices.Append(new Vertex(bottomRight, Color, new Vector2f(u1, v1)));
                        vertices.Append(new Vertex(bottomLeft,  Color, new Vector2f(u0, v1)));

                        previousX = x;
                        x+= g.Advance;
                }

                centerTransform = Transform.Identity;
                centerTransform.Rotate(-angleCovered / 2f, 0, Radius);
                centerTransform.Translate(0, -Radius / 2f);

        }

        public float Radius
        {
                get     { return radius; }
                set
                {
                        radius = value;
                        UpdateGeometry();
                }
        }

        public string DisplayedString
        {
                get     { return displayedString; }
                set
                {
                        displayedString = value;
                        UpdateGeometry();
                }
        }

        public Font Font
        {
                get     { return font; }
                set
                {
                        font = value;
                        UpdateGeometry();
                }
        }

        public uint CharacterSize
        {
                get     { return characterSize; }
                set
                {
                        characterSize = value;
                        UpdateGeometry();
                }
        }

        public Color Color
        {
                get { return color; }
                set
                {
                        color = value;
                        for (uint i = 0; i < vertices.VertexCount; i++)
                        {
                                Vertex v = vertices[i];
                                v.Color = color;
                                vertices[i] = v;
                        }
                }
        }

        public float Spacing
        {
                get { return spacing; }
                set
                {
                        spacing = value;
                        UpdateGeometry();
                }
        }

        public bool Centered
        {
                get { return centered; }
                set
                {
                        centered = value;
                }
        }

        VertexArray vertices;
        float radius;
        string displayedString;
        Font font;
        uint characterSize;
        Color color;
        float spacing;
        bool centered;
        Transform centerTransform;
}

It has some bugs (like handling of multiple lines), but meh... for now it's good enough for my purposes.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Curved text drawing
« Reply #3 on: July 04, 2013, 08:09:12 am »
Great :)
Laurent Gomila - SFML developer

Lo-X

  • Hero Member
  • *****
  • Posts: 618
    • View Profile
    • My personal website, with CV, portfolio and projects
Re: Curved text drawing
« Reply #4 on: July 04, 2013, 12:20:42 pm »
Nice work =) I'll perhaps need such a thing, do you mind if I use your code (translated in C++) in my project(s) ?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Curved text drawing
« Reply #5 on: July 04, 2013, 12:40:55 pm »
You could also consider putting it on the wiki :)
Laurent Gomila - SFML developer

cpolymeris

  • Jr. Member
  • **
  • Posts: 65
    • View Profile
    • Email
Re: Curved text drawing
« Reply #6 on: July 04, 2013, 03:35:21 pm »
Nice work =) I'll perhaps need such a thing, do you mind if I use your code (translated in C++) in my project(s) ?

Of course not. Please do, and let me know if you find any bugs. (Btw, IANAL, but I don't think you need to ask my permission if you are going to rewrite it in a different languange, anyways.)

You could also consider putting it on the wiki :)

Will do.
EDIT: Done. I also fixed a bug with the color of the text.
« Last Edit: July 04, 2013, 04:35:17 pm by cpolymeris »