Как визуализировать толстые 2D-линии в виде многоугольников?

У меня есть путь, состоящий из списка 2D-точек. Я хочу превратить их в полосу треугольников, чтобы визуализировать текстурированную линию заданной толщины (и другие подобные вещи). Таким образом, по сути, список 2D-точек должен стать списком вершин, определяющих контур многоугольника, который при рендеринге будет отображать линию. Проблема заключается в обработке угловых соединений, скосов, заглушек и т. д. Результирующий многоугольник должен быть идеальным в смысле отсутствия перерисовки, чистых соединений и т. д., чтобы его можно было выдавливать или как-то иначе играть с ним.

Существуют ли какие-либо простые ресурсы, которые могут предоставить понимание алгоритма, код или любую другую информацию о том, как сделать это эффективно?

Я абсолютно НЕ хочу иметь полноценную 2D-векторную библиотеку (cairo, antigrain, OpenVG и т. д.) с кривыми, дугами, штрихами и всеми прибамбасами. Я копался в нескольких деревьях исходных текстов для реализаций OpenVG и других вещей, чтобы найти некоторое представление, но все это ужасно запутано.

Я определенно готов закодировать это сам, но есть много вырожденных случаев (маленькие сегменты + толстые ширины + острые углы), которые создают всевозможные проблемы с соединением. Даже небольшая помощь сэкономила бы мне часы попыток справиться с ними всеми.

РЕДАКТИРОВАТЬ: Вот пример одного из тех вырожденных случаев, который вызывает уродство, если вы просто переходите от вершины к вершине. Красный — исходный путь. Оранжевые блоки представляют собой прямоугольники заданной ширины, выровненные по центру каждого сегмента.


person Patrick Hogan    schedule 26.03.2009    source источник
comment
Я тоже этого хотел. В прототипе я сфальсифицировал это с помощью простых прямоугольников между точками, но в конце концов это нужно будет исправить. Надеюсь, ваш вопрос вызовет правильные ответы. В любом случае, вернитесь к нам и расскажите, что вы в итоге сделали.   -  person Adam Davis    schedule 26.03.2009
comment
В ответ на вашу картинку, вы не делите углы пополам, вы идете перпендикулярно своим линиям.   -  person Neil N    schedule 27.03.2009
comment
Да, я знаю. Это предварительный алгоритм соединения. Я просто иллюстрирую проблемный случай, а не то, что происходит, когда вы на самом деле что-то к нему применяете.   -  person Patrick Hogan    schedule 27.03.2009


Ответы (9)


Ну ладно - я пытался решить эту проблему сам. Я потратил два месяца на решение, которое пыталось решить проблему нулевого перерасхода. Как вы уже выяснили, вы не можете иметь дело со всеми вырожденными случаями и в то же время иметь нулевую перерисовку.

Однако вы можете использовать гибридный подход:

Напишите себе подпрограмму, которая проверяет, можно ли без проблем построить соединения из простой геометрии. Для этого вы должны проверить угол соединения, ширину линии и длину соединяемых сегментов линии (сегменты линии, которые короче их ширины, являются PITA). С помощью некоторых эвристик вы сможете разобраться во всех тривиальных случаях.

Я не знаю, как выглядят ваши средние линейные данные, но в моем случае более 90% широких строк не имели вырожденных случаев.

Для всех остальных строк:

Вы, скорее всего, уже выяснили, что если вы допускаете перерисовку, генерировать геометрию намного проще. Сделайте это, и пусть полигональный алгоритм CSG и алгоритм тесселяции сделают всю тяжелую работу.

Я изучил большинство доступных пакетов тесселяции и остановился на тесселяторе GLU. Он был быстрым, надежным, никогда не давал сбоев (в отличие от большинства других алгоритмов). Он был бесплатным, и лицензия позволяла мне включать его в коммерческую программу. Качество и скорость тесселяции в порядке. Вы не получите качество триангуляции Делоне, но поскольку вам просто нужны треугольники для рендеринга, это не проблема.

Поскольку мне не нравился API-интерфейс тесселятора, я взял код тесселяции из бесплатной эталонной реализации SGI OpenGL, переписал весь интерфейс и добавил пулы памяти, чтобы уменьшить количество выделений. На это ушло два дня, но оно того стоило (например, улучшение производительности в пять раз). Кстати, решение оказалось в коммерческой реализации OpenVG :-)

Если вы выполняете рендеринг с помощью OpenGL на ПК, вы можете переместить задание тесселяции/CSG с ЦП на ГП и использовать трюки с трафаретным буфером или z-буфером, чтобы удалить перерисовку. Это намного проще и может быть даже быстрее, чем тесселяция ЦП.

person Nils Pipenbrinck    schedule 27.03.2009
comment
что такое ПИТА? и что вы должны делать, когда ширина линии больше, чем длина сегментов линии? - person moka; 08.06.2011
comment
@moka: PITA = боль в заднице - person aehlke; 19.06.2012
comment
Что это за дегенеративные случаи? Я не понимаю, в чем дело? - person paulm; 31.01.2014

Я только что нашел эту замечательную работу:

http://www.codeproject.com/Articles/226569/Drawing-polylines-by-tessellation

Кажется, он делает именно то, что вам нужно, а его лицензия позволяет использовать его даже в коммерческих приложениях. Кроме того, автор проделал поистине огромную работу по детализации своего метода. Вероятно, в какой-то момент я попробую заменить свою собственную, далеко не идеальную реализацию.

person Boris Dalstein    schedule 27.10.2014
comment
Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится. - person AstroCB; 28.10.2014
comment
@AstroCB: достаточно честно, но вопрос, который задает ОП, на самом деле чрезвычайно сложный вопрос. На самом деле, это в значительной степени открытая исследовательская проблема. Невозможно, чтобы хороший ответ хорошо подходил для Stack Overlow. Ссылка на довольно хороший ресурс, решающий проблему, я думаю, очень актуальный и полезный ответ, и в этом случае нет смысла пытаться обобщить здесь метод, на который у меня все равно нет времени. -1 означает, что ответ бесполезен. Я не понимаю, как бесполезно указывать на хороший ресурс. Это не идеально, конечно, но действительно полезно. - person Boris Dalstein; 12.11.2014
comment
Для справки: я не минусовал (см. здесь), но хотел бы чтобы указать вам на этот вопрос, в котором излагается политика Stack Exchange для таких типов ответов. Комментарий сгенерирован автоматически на основе действия, которое я предпринял в очереди на проверку, где этот пост оказался после того, как кто-то (предположительно тот же человек, который проголосовал за него) пометил его как низкокачественный. - person AstroCB; 12.11.2014
comment
@AstroCB Хорошо, спасибо за разъяснение :) Обратите внимание, что мой ответ все еще значительно длиннее и дает некоторый контекст по сравнению с теми, которые приведены в рекомендациях для ответов по ссылкам. Как бы то ни было, рекомендации — это то, чем они являются: рекомендации. Не правила. - person Boris Dalstein; 12.11.2014
comment
Я работаю над линейным рендерингом около недели с теми же проблемами, что и у OP. Хотя связанная статья/проект (рендерер Vase) довольно хороша, она ВСЕ ЕЩЕ имеет перерисовку в узких углах. Пока я не нашел способа обойти эту проблему. - person SleepProgger; 13.02.2020

Простой метод из головы.

Разделите пополам угол каждой 2d вершины, это создаст красивую линию под углом. Затем двигайтесь вдоль этой линии, как внутрь, так и наружу, на величину вашей «толщины» (или толщины, разделенной на два?), теперь у вас есть внутренняя и внешняя точки многоугольника. Перейдите к следующей точке, повторите тот же процесс, создавая новые точки многоугольника по пути. Затем примените триангуляцию, чтобы получить готовые к рендерингу вершины.

person Neil N    schedule 26.03.2009
comment
Я разобрался с этой частью. :) Проблема в том, что в некоторых случаях эти вершины генерируют перекрывающиеся треугольники. По сути, мне нужно исключить вершины, которые в конечном итоге могут вызвать странные артефакты. - person Patrick Hogan; 27.03.2009
comment
Я не думаю, что вы поняли часть пополам. - person Neil N; 27.03.2009
comment
Я понимаю, но это все еще производит уродство. См. пример изображения, добавленного в сообщение. Каждая сторона этой линии представляет собой отдельную проблему. - person Patrick Hogan; 27.03.2009
comment
Нет, на вашем изображении используются перпендикулярные линии, а не бисекции. Биссекция — это середина между углами. Если ваша базовая линия образует угол 90 градусов, ваше деление пополам составляет 45. Понятно? - person Neil N; 27.03.2009

В итоге мне пришлось запачкать руки и написать небольшой ленточник, чтобы решить аналогичную проблему.

Для меня проблема заключалась в том, что я хотел, чтобы толстые линии в OpenGL не имели артефактов, которые я видел с OpenGL на iPhone. Посмотрев на различные решения; кривые Безье и тому подобное - я решил, что, вероятно, проще всего будет сделать свои собственные. Есть несколько разных подходов.

Один из подходов состоит в том, чтобы найти угол пересечения между двумя сегментами, а затем переместиться вдоль этой линии пересечения на определенное расстояние от поверхности и рассматривать это как вершину ленты. Я попробовал это, и это не выглядело интуитивно понятным; ширина ленты будет разной.

Другой подход состоит в том, чтобы фактически вычислить нормаль к поверхности сегментов линии и использовать ее для вычисления идеального края ленты для этого сегмента, а также для реальных тестов пересечения между сегментами ленты. Это работало хорошо, за исключением того, что для острых углов пересечения сегментов ленточных линий были слишком далеко (если угол между сегментами приближался к 180 ').

Я работал над проблемой острого угла с помощью двух подходов. Алгоритм пересечения линий Пола Бурка (который я использовал неоптимизированным способом) предлагал определять, находится ли пересечение внутри сегментов. Поскольку оба сегмента идентичны, мне нужно было проверить только один из сегментов на пересечение. Затем я мог решить, как решить эту проблему; либо подтасовывая лучшую точку между двумя концами, либо надев заглушку - оба подхода выглядят хорошо - подход заглушки может нарушить порядок многоугольника, обращенного вперед / назад для opengl.

См. http://paulbourke.net/geometry/lineline2d/.

См. мой исходный код здесь: https://gist.github.com/1474156

person Anselm Hook    schedule 13.12.2011
comment
Одна из моих любимых проблем с PostScript, и, к сожалению, другие парадигмы рисования следуют его примеру, заключается в том, что скошенные углы рисуются либо как идеальный скос (если он не выходит за пределы скоса), либо как скос. Интересно, почему предел митры нельзя было использовать в качестве точки, чтобы срезать угол митры. - person supercat; 08.06.2013

Меня это тоже интересует, так как я хочу усовершенствовать рисунок моего картографического приложения (Kosmos). дорог. Один обходной путь, который я использовал, — нарисовать полилинию дважды, один раз более толстой линией, а другой — более тонкой, другого цвета. Но на самом деле это не полигон, это просто быстрый способ его симуляции. См. несколько примеров здесь: http://wiki.openstreetmap.org/wiki/Kosmos_Rendering_Help#Rendering_Options

Я не уверен, что это то, что вам нужно.

person Igor Brejc    schedule 26.03.2009

Я думаю, что я достиг бы алгоритма тесселяции. Это правда, что в большинстве случаев, когда они используются, цель состоит в том, чтобы уменьшить количество вершин для оптимизации рендеринга, но в вашем случае вы можете параметризовать, чтобы сохранить все детали - и возможность оптимизации может оказаться полезной.

В Интернете существует множество алгоритмов и кода тесселяции — несколько лет назад я завернул чистый C в DLL для использования с визуализатором ландшафта Delphi, и они не являются редкостью для продвинутых руководств по графическому кодированию и тому подобного.

person Cruachan    schedule 26.03.2009

Посмотрите, может ли помочь триангуляция Делоне.

person GSerg    schedule 26.03.2009

В моем случае я мог позволить себе перерасход. Я просто рисую круги с радиусом = ширина/2 с центром в каждой из вершин полилинии.

Артефакты маскируются таким образом, и это очень легко реализовать, если вы можете жить с «закругленными» углами и некоторым перерисовыванием.

person kikito    schedule 11.01.2010

Судя по вашему изображению, вы рисуете рамку вокруг сегментов линии с включенной FILL и используете оранжевый цвет. Это наверняка создаст плохие овердрафты. Итак, первое, что нужно сделать, это не отображать черную рамку, а цвет заливки может быть непрозрачным.

Почему вы не можете использовать примитив GL_LINES, чтобы делать то, что вы намереваетесь сделать? Вы можете указать ширину, фильтрацию, гладкость, текстуру, что угодно. Вы можете визуализировать все вершины с помощью glDrawArrays(). Я знаю, что это не то, что вы имеете в виду, но поскольку вы сосредоточены на 2D-рисовании, это может быть более простым подходом. (поиск Текстурированные линии и т.д.)

person Ketan    schedule 13.01.2010