Вот векторное решение. Я не пробовал, но концептуально это выглядит нормально.
теория
Я полагаю, вы сохранили форму в виде отрезков. Вот буква А, представленная тремя отрезками.
Я предположил, что пути на чертеже пользователя хранятся в виде списков точек.
Мы можем «раздувать» эти отрезки, чтобы допустить допустимый предел погрешности при проверке на близость : находится ли нарисованный пользователем путь рядом с правильным пределом погрешности линий.
Однако одного этого недостаточно. Мы также должны проверить покрытие : покрывает ли пользовательский рисунок большую часть фигуры. Эти рисунки плохие, потому что, хотя они вписываются в поле ошибки, они пропускают некоторую часть буквы:
Если мы проверим обе эти вещи, мы можем приблизиться, если рисунок игрока хорош.
Реализация
Проверять близость означает только для каждой точки пути пользователя, находить расстояние между этой и каждой линией, составляющей букву, брать самую низкую и проверять, что она меньше допустимой погрешности.
Проверка покрытия более сложна, но вы можете получить очень хорошее приближение с векторной математикой, если для каждого отрезка линии вы найдете ближайший нарисованный пользователем путь (зеленый) и спроецируйте его части (темно-зеленый) на этот отрезок (черный), затем проверьте, насколько хорошо проецируемые векторы (синие) покрывают его:
Чтобы спроецировать вектор a
на другой вектор b
, выполните
projection = dotProduct(a, b) / lengthSquared(b) * b
где dotProduct
вычисляет скалярное произведение двух векторов и lengthSquared
как оно звучит. По сути, это находит скалярное значение того, сколько a
идет в b
направлении и умножается b
на это, чтобы получить вектор в том же направлении. ( Учебное пособие A по обнаружению столкновений Metanet Software имеет хорошую визуализацию этого в Приложении A § проекция .)
Направление проецируемого вектора может не иметь большого значения. Если вы просто суммируете длины спроецированных векторов и сравниваете их с общей длиной отрезка, это скажет вам, какая его часть покрыта. (За исключением странных случаев - см. § Ограничения ниже).
На изображении выше, путь будет охватывать около половины сегмента. Вы можете выбрать любое значение допуска, которое вы хотите.
Ограничения
Изогнутые буквы
Сегменты линий неидеальны: многие буквы изогнуты! Как вы представляете «P» или «O»?
Вы можете использовать много отрезков (возможно, с большей погрешностью).
Вы также могли бы начать использовать кривые Безье вместо линий для более тесной подгонки, но обратите внимание , что найти ближайшую точку на Безье является гораздо более сложным, как и многие другие операции измерения.
Несоответствия
Чрезмерно ослабленные границы допуска для расстояния от линий и покрытия письма могут иметь непреднамеренные последствия.
Например, игрок, возможно, пытался нарисовать здесь «Н».
Петли и перекрытия
Петли или перекрытия на нарисованном игроком пути могут привести к тому, что некоторые части рисунка будут засчитаны дважды при проецировании их на ближайший отрезок.
Эту проблему можно обойти, выполнив более сложную обработку проецируемых векторов, возможно, сохранив точно, где будет находиться проецируемый вектор (сохраните также направление проекции и ближайшую точку на отрезке линии к точке на нарисованной игроком линии) , а затем отвергая новые, которые перекрывают его.
Если игрок нарисовал один путь, и он был обработан, начиная с конца, отмеченного синим кружком, зеленые части этого пути будут приняты, а красные отклонены, потому что их проекция будет перекрываться с некоторыми деталями, обработанными ранее.
Реализация имеет много технических тонкостей, которые, вероятно, принадлежат другому вопросу.
Непредсказуемо авантюрные игроки
Игрок может нарисовать что-то странное, что все еще проходит .
Хотя это можно назвать функцией! :)