Лучший способ рассчитать индекс символа в строке с учетом смещения в пикселях - PullRequest
1 голос
/ 09 сентября 2011

Смежный вопрос: Получение индекса строки на основе смещения в пикселях

Я знаю, что это близко к этому вопросу, но это не вопрос, как сделать это напрямую, это вопроскак подделать это лучше всего.

Я реализую свое собственное текстовое поле для Windows Forms (потому что RichTextBox отстой) и пытаюсь найти лучший способ, учитывая строки, которые были нарисованы на экране, вычислить, над каким символом находится мышь.Проблема в том, что символы могут быть переменной ширины.

У меня есть две возможности:

  1. Делать Graphics.MeasureCharacterRange каждый раз, когда мышь двигается в режиме бинарного поиска на строке, над которой находится мышь (как предложено в вопросе, связанном сверху)

  2. Сохранение списка смещений для каждого символа каждой строки.

(1) будет иметь плохую производительность, а

(2) будет неэффективно в памяти, плюс ввод текста становится операцией O (n) (потому чтоВы должны отрегулировать смещение каждого символа после него) плюс невозможно сделать точно, потому что Graphics.MeasureCharacterRange не является точным (он возвращает одно значение для одного символа, другое значение для другого символа и совершенно другоезначение [которое не равно двум предыдущим значениям, добавленным вместе] для них обоих вместе в одну строку: например, W будет иметь ширину 16 пикселей и f будет шириной 5 пикселей, но Wf имеет ширину 20 пикселей.Эти цифры отфактический тест.).

Так что я ищу лучшую стратегию для этого, предпочтительно ту, которая требует минимального пространства и O (1) вычислительной сложности (хотя я с радостью обменяю небольшую эффективность памяти на скоростьКПД).

1 Ответ

1 голос
/ 09 сентября 2011

Я не думаю, что вы должны делать O (1). O (1) предполагает, что каждый дополнительный символ влияет на ВСЕ предыдущие символы, чего не будет. В лучшем случае я бы увидел O (1) для каждого слова, которое должно быть безумно быстрым. Похоже, что вам нужен способ хранения; 1 местоположение каждого слова, 2 каждое уникальное слово и 3 ширина каждой буквы в слове. Это значительно уменьшит объем хранилища и увеличит скорость поиска. Может быть что-то вроде:

IEnumerable<TextLocation> TextLocations = ...;

internal class TextLocation
{
    public RectF BoundingBox { get; set; }  //this is relative to the textbox
    public TextWord TextWord { get; set; }
}

internal class TextWord
{
    public string Text { get; set; }
    public IEnumerable<LetterInfo> Letters { get; set; }
}

internal class LetterInfo
{
    public char Letter { get; set; }
    public float left { get; set; }  //these would be relative to the bounding box
    public float right { get; set; } //not to the textbox
}

Тогда вы можете сделать что-то вроде

var tl = TextLocations.FirstOrDefault(x => x.BoundingBox.Left < Mouse.X 
                                           && x.BoundingBox.Right > Mouse.X
                                           && x.BoundingBox.Top < Mouse.Y
                                           && x.BoundingBox.Bottom > Mouse.Y)

if (tl != null)
{
    //tl.TextWord.Text is the Word ("The", "Lazy", "Dog"...)

    var letter = tl.TextWord.Letters
                   .FirstOrDefault(x => Mouse.x - tl.BoundingBox.left > x.left
                                        Mouse.x - tl.BoundingBox.left < x.right);

    if (letter != null)
    {
        // you get the idea
    }                              
}
...