2010-11-16 2 views
0

Я создаю библиотеку геометрии, и я смущен, каким должен быть тип возврата функции, которая вычисляет пересечение сегмента с другим сегментом. Возвращаемое значение иногда является точкой, а иногда и сегментом (с перекрытием), а иногда и пустым. По моему мнению, может быть 3 способа решить эту проблему следующим образом: 1. вернуть союз (сегмент, нуль, точка) 2. вернуть сегмент с первой точкой == вторая точка, когда пересечение является одной точкой, и оба точки как NAN, когда пересечение является пустым множеством 3. верните вектор (с 0 элементами для пустого набора, 1 элемент для pnt и 2 элемента для сегмента)Хороший дизайн для создания геометрии (относительно использования объединения или нет)?

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

Ниже приведен мой код для справки (чей возвращаемый тип является вектором)

vector<pnt> seg::inter(seg z){ 
vector<pnt> ans; 
if(p1==p2){if(z.doesinter(p1)){ans.pb(p1);}} 
else if(z.p1==z.p2){ 
if(doesinter(z.p1)) ans.pb(z.p1);} 
else{ 
pnt p1p2=(p2-p1); 
pnt q1=p1p2*pnt(0,1); 
long double h1,h2; 
if(abs((z.p2-z.p1).dot(q1))<=eps){ 
pnt r1((z.p1-p1)/(p2-p1)),r2((z.p2-p1)/(p2-p1)); 
if(abs(r1.y)<=eps){//colinear case 
h1=r1.x; 
h2=r2.x; 
if(h1>h2)swap(h1,h2); 
if(h2>=0&&h1<=1){//add eps 
h1=max(0.0L,h1);h2=min(1.0L,h2); 
ans.pb(p1+p1p2*h1); 
if(doublecompare(h1,h2)==-1)ans.pb(p1+p1p2*h2);}}} 
else{ 
h1 = ((p1-z.p1).dot(q1))/((z.p2-z.p1).dot(q1)); 
pnt q2 = (z.p2-z.p1)*pnt(0,1); 
h2 = ((z.p1-p1).dot(q2))/((p2-p1).dot(q2)); 
if(h1+eps>=0&&h1-eps<=1&&h2+eps>=0&&h2-eps<=1) ans.pb(z.p1+(z.p2-z.p1)*h1);}} 
return ans;} 
+4

Я сожалею, но этот код выглядит ужасно. Как вы можете это прочитать? Вставьте некоторые пробелы и отступы и, возможно, нечетные комментарии. –

ответ

3

Мое предложение состоит в том, чтобы создать специализированный класс пересечения, который может обрабатывать все случаи. Тогда вы можете вернуть экземпляр этого класса. Внутри класса может быть, например, векторное представление (с теми же конечными точками, если пересечение является одной точкой, как вы предполагали), и может иметь методы для определения того, в каком случае это фактически (bool isIntersecting(), isSegment() и т. Д.).

Для более сложного дизайна вы можете сделать этот абстракционный класс Intersection абстрактным и предоставить специализированные реализации для NoIntersection, PointIntersection и SegmentIntersection с различным представлением внутренних данных.

+0

+1: Я считаю, что это хорошее решение - лучше, чем союз или даже подход типа Variant, предложенный Matthieu M. – AAT

1

«Идея» union идеально подходит, он, естественно, выражает все случаи. Однако я бы рекомендовал не использовать union языка C напрямую, потому что это низкоуровневая конструкция, которая заставит вас найти ошибки.

Вместо этого вы должны использовать Boost.Variant.

В принципе, вариант представляет собой комбинацию из двух элементов: тега и union, причем тег используется для указания того, какой член союза используется в данный момент. Будучи классом C++, это C++ известно (не похоже на объединение), и поэтому вы не столкнетесь с ограничениями на тип объектов, которые вы можете вставить, и вы также не столкнетесь с неопределенным поведением.

typedef boost::Variant<NoneType, Point, Segment> IntersectionType; 

Разумеется, вы также можете обернуть это в класс, чтобы открыть более богатый интерфейс.

+0

+1 для унижающего «союза»! Это совсем не типично. Некоторая форма Вариант, который имеет типы accessovers лучше. – AAT

Смежные вопросы