X and Y coord of character

Started by
4 comments, last by MatsK 5 years, 8 months ago

Guys, if I measure the width and height of each line in a gap buffer (lines are delineated by \n), what would be the formula for finding the X and Y coordinate of any given character in that gap buffer?

Something like so:


        /// <summary>
        /// Returns the position of any given character in m_Text.
        /// </summary>
        /// <param name="CharacterIndex">The index of the character.</param>
        /// <returns>THe position of the character indexed by <paramref name="CharacterIndex"/></returns>
        private Vector2 CharaterPosition(int CharacterIndex)
        {
 
        }

 

Advertisement

Update!

Why does this method seem to return the incorrect position? I'm clearly not calculating it correctly, but I don't know what I'm doing wrong.

 

https://pastebin.com/CcR4dZwu

Because it isn't accurate by default. You can make it more accurate (one article of many) but it looks worse.  

Font rendering is a large, complex, nuanced field. In the effort to make glyphs appear in a visually pleasing way, we generally sacrifice accuracy in computing details about how the text appears on screen.

You can go back to the bad old days of fixed width fonts if you want.  But modern fonts with a huge range of glyphs, including fonts with composed glyphs, advanced kerning, and glyphs that render outside what might be considered the regular text box, all of these make mathematics regarding fonts into incredibly complex systems.

Case in point:

 

I̗̘̦͝n͇͇͙v̮̫ok̲̫̙͈i̖͙̭̹̠̞n̡̻̮̣̺g̲͈͙̭͙̬͎ ̰t͔̦h̞̲e̢̤ ͍̬̲͖f̴̘͕̣è͖ẹ̥̩l͖͔͚i͓͚̦͠n͖͍̗͓̳̮g͍ ̨o͚̪͡f̘̣̬ ̖̘͖̟͙̮c҉͔̫͖͓͇͖ͅh̵̤̣͚͔á̗̼͕ͅo̼̣̥s̱͈̺̖̦̻͢.̛̖̞̠̫̰

 

Fonts are fun.

It's not inaccurate, it's outright wrong.  For XPosition, you are taking CharacterIndex, multiplying it by some number, then dividing it by that same number.  In other words, XPosition will only ever equal CharacterIndex.  This can only be accurate if you want your results in terms of characters cells instead of pixels and all text is in a single line and all characters have the same width, in which case you should set YPosition to just 0.

Things your function fails to account for:

  • Multiple lines of text.
  • Variable-width characters.
  • Variable kerning between characters.
  • Even in fixed-width fonts, lots of characters have zero width.  (This includes all so-called combining characters, which merge with the adjacent charcter.)
  • Even in fixed-width fonts, lots of characters have double width.  (This includes all CJK ideographs.)

A friend convinced me it would be a better idea to write this method instead: 


private void UpdateCharacterPositions()
        {
            Vector2 Position = m_TextEditPosition;
            float XPosition = 0;
            if (m_UpdateCharPositions)
            {
                m_CharacterPositions.Clear();
 
                foreach (string Str in TextWithLBreaks.Split("\n".ToCharArray()))
                {
                    XPosition = 0;
                    Position.Y += CapitalCharacterHeight;
 
                    for (int i = 0; i < Str.Length; i++)
                    {
                        XPosition += m_Font.MeasureString(Str.Substring(i, 1)).X;
                        m_CharacterPositions.Add(new Vector2(XPosition + m_TextEditPosition.X, Position.Y));
                    }
                }
 
                m_UpdateCharPositions = false;
            }
        }
 

Now I just need to optimize the hell out of it or minimize the number of times it is being called. Currently it’s being called whenever the cursor goes OOB when deleting a character, as such:


        /// <summary>
        /// Make sure cursor's position is valid and in range.
        /// </summary>
        private void FixCursorPosition()
        {
            if (m_Cursor.Position.X < m_TextEditPosition.X)
                m_Cursor.Position.X = m_TextEditPosition.X;
 
            m_UpdateCharPositions = true;
            UpdateCharacterPositions();
 
            int RealCharIndex = m_Cursor.CharacterIndex - m_NumLinesInText; //Account for \n...
            RealCharIndex = (RealCharIndex < m_CharacterPositions.Count) ?  //Make sure it doesn't overflow.
                RealCharIndex : m_CharacterPositions.Count - 1;
 
            Vector2 IndexPosition = m_CharacterPositions[RealCharIndex];
 
            if (m_Cursor.Position.X != IndexPosition.X || m_Cursor.Position.Y != IndexPosition.Y)
            {
                m_Cursor.Position.X = IndexPosition.X;
                m_Cursor.Position.Y = IndexPosition.Y;
            }
        }

 

This topic is closed to new replies.

Advertisement