На практике графические объекты всегда отображаются на конечном растре, границы которого соответствуют границам экрана или внеэкранного буфера. Растеризация на конечном растре требует возможности отсечения растеризуемого объекта относительно границ растра, т.е. удаления частей растеризуемого объекта, лежащих за пределами растра. Выполнение алгоритма
Можно было бы просто проверять, лежит ли пиксель внутри растра непосредственно перед изменением его цвета. К сожалению, такое решение в большинстве случаев слишком неэффективно. Во-первых, подобная проверка сама по себе чрезвычайно замедлит растеризацию. Во-вторых, в тех случаях, когда значительная часть объекта лежит вне растра, растеризация для этой области будет выполняться вхолостую и отсечение подобных областей опять-таки ускорит вычисления.
В этом разделе мы рассмотрим алгоритмы отсечения отрезка и многоугольника относительно границ прямоугольника (иногда мы будем называть этот прямоугольник "окном").
Алгоритм Сазерлэнда-Коэна осуществляет отсечение отрезка относительно прямоугольника со сторонами, параллельными координатным осям.
Пронумеруем стороны прямоугольника так, как это показано на рис. 5.1, и классифицируем все точки в зависимости от их положения по отношению к отсекающим прямым 1, 2, 3, 4. Для этого сопоставим каждой области, на которые разбивают плоскость прямые, 4 -битный код.
(рис 5.1) Алгоритм Сазерлэнда-Коэна.Установим эти биты следующим образом:
| 0 бит | если точка лежит левее прямой 1 ( x < xлево ) |
| 1 бит | если точка лежит ниже прямой 2 ( y < yниз ) |
| 2 бит | если точка лежит правее прямой 3 ( x > xправо ) |
| 3 бит | если точка лежит выше прямой 4 ( y > yверх ) |
Тогда для отрезка AB на рис. 5.1 будем иметь: код точки A = 0011, код точки B = 0100.
Пусть A - код точки-начала отрезка, B - код точки-конца отрезка. Рассмотрим три возможных случая.
A = B = 0000. Этот случай означает, что обе точки лежат внутри прямоугольника (т. е. отсечение не требуется).1 или 2, то необходимо находить точки пересечения с некоторыми из отсекающих прямых (для прямых, которые пересекает AB, соответствующий бит в A xor B установлен в 1 ). Для этого разобьем отрезок найденными точками пересечения и затем применим тот же анализ кодов концов для полученных подотрезков.Один из возможных вариантов реализации окончательного алгоритма приведен ниже:
// (xлево, yниз, xправо, yверх) - отсекающий прямоугольник;
// Функция код(<точка>) возвращает битовый код,
// значение которого описано выше;
Отсечь( отрезок AB )
{
while(( код(A) | код(B) ) AND (!( код(A) код(B) )))
{
if( код(A) == 0) // т.е. A внутри
// меняем координаты местами,
// чтобы A всегда лежала снаружи
поменять(A,B);
if( код(A) 1 ) // т.е. A лежит слева
{
A.x += (B.y - A.y)*(xлево-A.x)/(B.x - A.x);
A.y = xлево;
}
else if( код(A) 2 ) // т.е. A лежит сверху
{
A.x += (B.x - A.x)*(yверх-A.y)/(B.y - A.y);
A.y = yверх;
}
else if( код(A) 4 ) // т.е. A лежит справа
{
A.y += (B.y - A.y)*(xправо-A.x)/(B.x - A.x);
A.x = xправо;
}
else if( код(A) 8 ) // т.е. A лежит снизу
{
A.x += (B.x - A.x)*(yниз -A.y)/(B.y - A.y);
A.y = yниз;
}
}
return AB; // Вернуть отсеченный отрезок
}
Алгоритм Сазерлэнда-Коэна может быть обобщен на случай отсечения отрезка параллелепипедом в трехмерном пространстве. В этом случае код, конечно, уже будет длины 6 (по числу отсекающих граней).
Алгоритм решает ту же задачу: отсечение отрезка относительно прямоугольника. Случаи, в которых отрезок лежит целиком снаружи или внутри окна, выявим методом, изложенным в алгоритме Сазерлэнда-Коэна.
В общем случае возьмем среднюю точку нашего отрезка AB (обозначим ее C1 ) и применим к отрезкам AC1 и C1B этот алгоритм рекурсивно. Следующие средние точки отрезков AC1 и C1B обозначим C2 и C3 соответственно, см. рис. 5.2. И так далее, пока размер оставшихся отрезков не станет меньше размера пикселя.
В примере на рис. 5.2 после второй итерации отрезок AC2 будет отсечен, и для него дальнейшее половинное деление производиться не будет; C1C3 лежит целиком внутри области отсечения, он будет нарисован, и для него дальнейшее половинное деление также производиться не будет. Остальные части будем делить дальше.

(рис 5.2) Алгоритм средней точки(рис 5.2) Алгоритм средней точкиОтсечь( отрезок AB ) { if( длина AB меньше размера пикселя ) return; if( AB лежит вне отсекающего прямоугольника ) return; if( AB лежит внутри отсекающего прямоугольника ) { Отобразить AB; return; } Отсечь (A, (A+B)/2); Отсечь ((A+B)/2),B); }
Алгоритм средней точки прост, но не очень эффективен на практике, так как требует большой глубины рекурсии. В большинстве случаев алгоритм Сазерлэнда-Коэна предпочтительнее. Прием с делением отрезка пополам может оказаться эффективным при отсечении относительно сложной непрямоугольной области, для которой проверить принадлежность точки к окну существенно проще, чем найти пересечение отрезка с границей.
Алгоритм средней точки также обобщается на случай трехмерного пространства тривиальным образом.
Алгоритм Цируса-Бека [25] отсекает отрезок P1P2, используя его параметрическое представление:
Данный алгоритм применим не только в случае отсечения прямоугольником со сторонами, параллельными осям координат, но и в случае, если отсекаемая область ограничена любым выпуклым многоугольником.
Рассмотрим отдельно ребро Ei отсекающего многоугольника. Нормаль к нему ориентируем во внешнюю сторону отсекающего многоугольника, для этого удобно считать, что точки отсекающего контура обходятся против часовой стрелки; тогда если ребро - это $$\overrightarrow{P_{Ei1}P_{Ei2}}$$, то нормаль NEi будет пропорциональна (yEi2-yEi1, xEi1- xEi2), см. рис. 5.3.
Тогда область, отсекаемая прямой, на которой лежит ребро (обозначим ее Li ), соответствует точкам P, для (P-PEi),NEi) > 0, где PEi - любая точка на ребре Ei. Точка пересечения прямой, на которой лежит отрезок с отсекающей прямой Li, находится из уравнения
((Ps(t) - PEi),NEi) = 0.
Разрешая его, получаем, что
$$t = -\frac{((P_1 - P_{Ei}),N_{Ei})}{((P_2 - P_1),N_{Ei})}$$в том случае, если $$(P_2 - P_1,N_{Ei}) \ne 0$$. Если же ((P2 - P1),NEi) = 0 это означает, что отсекаемый отрезок параллелен Li и не существует единственной точки их пересечения. Такие случаи алгоритм игнорирует.
(рис 5.3) Пересечение отрезка с ребром.Для алгоритма Цируса-Бека также важно в каком направлении (внутрь отсекающего многоугольника или из него) проходит точка при движении по отрезку от P1 к P2, т.е. при изменении t от 0 до 1. Это определяется знаком ((P2 - P1),NEi). Будем обозначать такие точки пересечения как:
| Потенциально входящие (ПВх): | ((P2 - P1),NEi) < 0 |
| Потенциально выходящие (ПВых): | ((P2 - P1,NEi) > 0 |
После того как рассчитаны координаты t для всех возможных пересечений с прямыми Li, следует выбрать максимальную координату из потенциально входящих пересечений tВхMax и минимальную из потенциально выходящих tВыхMin. Если прямая, на которой лежит отрезок P1P2, пересекает отсекающий многоугольник, то tВхMax < tВыхMin. В этом случае, если пересечение $$[t_1, t_2] = [t_{ВхMax}, t_{ВыхMin}] \cap [0, 1]$$ непусто, то Ps(t1)Ps(t2) будет искомым отсеченным отрезком. В противном случае отрезок полностью лежит вне отсекаемой области.
(рис 5.4) Возможные случаи пересечений в алгоритме Цируса-Бека.Вычислить NEi и взять PEi = PEi1 для каждого ребра.
Отсечь(отрезок P1P2)
{
if(P1 == P2)
отсечь как точку;
else
{
D = P2 - P1;
t_Вх = 0;
t_Вых = 1;
foreach( ребро Ei из отсекающего многоугольника )
{
dp = (D,NEi);
if( dp != 0 ) // случай dp == 0 игнорируется
{
$$$t = -\frac{((P_1 - P_{Ei}), N_{Ei})}{dp};$$$if( dp < 0 )
{
if( t > t_Вх )
t_Вх = t;
}
else // dp > 0
{
if( t < t_Вых )
t_Вых = t;
}
}
}
if( t_Вх < t_Вых )
return отрезок Ps(t_Вх)Ps(t_Вых);
else
return '
}
}
Алгоритм Цируса-Бека также может быть обобщен до случая отсечения отрезка произвольным выпуклым многогранником в трехмерном пространстве.
Алгоритм Лианга-Барского [39] является более эффективным вариантом алгоритма Цируса-Бека в случае, если отсекающий многоугольник - это прямоугольник со сторонами, параллельными осям координат. В этом случае вычисление промежуточных величин упрощается ( P1 = (x1, y1),P2 = (x2, y2) ):
| Ребро | NEi | PEi | ((P2 - P1),NEi) | t |
|---|---|---|---|---|
| левое: x = xлево |
(-1, 0) | (xлево, yверх) | x1 - x2 | $$-\frac{(x_{лево}-x_1)}{x_1-x_2}$$ |
| нижнее: y = yниз |
(0,-1) | (xлево, yниз) | y1 - y2 | $$-\frac{(y_{низ}-y_1)}{y_1-y_2}$$ |
| правое: x = xправо |
(1, 0) | (xправо, yниз) | x2 - x1 | $$-\frac{(x_1-x_{право})}{x_2-x_1}$$ |
| верхнее: y = yверх |
(0, 1) | (xправо, yверх) | y2 - y1 | $$-\frac{(y_1-y_{верх})}{y_2-y_1}$$ |
// (xлево, yниз, xправо, yверх) - отсекающий прямоугольник;
Отсечь(отрезок P1P2)
{
dx = P2.x - P1.x;
dy = P2.y - P1.y;
if( dx == 0 AND dy == 0 )
отсечь как точку;
else
{
t_Вх = 0;
t_Вых = 1;
// Отсечь_t может модифицировать t_Вх и t_Вых
// ( означает передачу по адресу)
if( Отсечь_t( dx, xлево - P1.x, t_Вх, t_Вых ) )
if( Отсечь_t( -dx, P1.x - xправо, t_Вх, t_Вых ) )
if( Отсечь_t( dy, yниз - P1.y, t_Вх, t_Вых ) )
if( Отсечь_t( -dy, P1.y - yверх, t_Вх, t_Вых ) )
{
if( t_Вх > 0 )
{
P1.x = P1.x + dx*t_Вх;
P1.y = P1.y + dy*t_Вх;
}
if( t_Вых < 1 )
{
P2.x = P1.x + dx*t_Вых;
P2.y = P1.y + dy*t_Вых;
}
return отрезок P1P2;
}
return '
}
}
// Проверить пересечение с ребром
// denom, num - 'знаменатель' и 'числитель' выражения (5.2)
// t_Вх и t_Вых передаются по адресу (обозначено *),
// они могут быть модифицированы;
// возвращает логическую переменную:
// true - продолжать отcечение
// false - закончить отcечение,
// отрезок полностью вне прямоугольника
bool Отсечь_t( denom, num, *t_Вх, *t_Вых )
{
if( denom > 0 ) // Потенциально входящее пересечение
{
t = num / denom;
if( t > t_Вых )
// t_ВхMax > t_ВыхMin: отрезок полностью снаружи
return false;
else if( t > t_Вх ) // Модифицируем t_Вх
t_Вх = t;
}
else if( denom < 0 ) // Потенциально выходящее пересечение
{
t = num / denom;
if( t < t_Вх )
// t_ВхMax > t_ВыхMin: отрезок полностью снаружи
return false;
else if( t < t_Вых ) // Модифицируем t_Вых
t_Вых = t;
}
else if( num > 0 )
// отрезок параллелен ребру и полностью снаружи
return false;
return true;
}
Эксперименты, проведенные авторами на случайных наборах данных [39], показали, что их алгоритм на 25-62% быстрее алгоритма Сазерлэнда-Коэна. Поэтому в настоящее время алгоритм Лианга-Барского получил большее распространение.
Рассмотрим теперь задачу отсечения многоугольника относительно прямоугольника и алгоритм Сазерлэнда-Ходжмана [48], позволяющий проводить такое отсечение.
Ключевым моментом в этом алгоритме является сведение задачи отсечения прямоугольником к задаче отсечения полуплоскостями. Действительно, прямоугольник представляется в виде пересечения четырех полуплоскостей (по сути, этим же представлением мы пользовались и в алгоритме Сазерлэнда-Коэна). Поэтому достаточно поочередно отсечь части многоугольника, лежащие вне каждой полуплоскости.
Пусть многоугольник задан своими вершинами: P1P2 . . . PN. $$\overrightarrow{P_1P_2}, . . . \overrightarrow{P_kP_{k+1}}, . . . \overrightarrow{P_{N-1}P_N}, \overrightarrow{P_NP_1}$$ - направленные ребра этого многоугольника.
Относительно произвольной полуплоскости $$\Pi$$ каждое направленное ребро $$\overrightarrow{P_kP_{k+1}}$$ может находиться в следующих положениях:
Следующий алгоритм выводит в качестве результата вершины отсеченного многоугольника, обходя исходные вершины.
Отсечь P[1]...P[N] относительно полуплоскости
{
//L - граница полуплоскости
for( i = 1; i <= N; i++ )
{
j = i+1;
if( j == N+1 )
j = 1;
if( P[i]P[j] пересекает L )
{
I = пересечение( P[i]P[j], L );
вывести(I);
}
if( P[j]видима )
вывести(P[j]);
}
}
Данный алгоритм выводит вершины отсеченного многоугольника в порядке обхода. Пример работы алгоритма Сазерлэнда-Ходжмана приведен на рис. 5.5.
После проведения отсечения четырьмя полуплоскостями получается многоугольник, отсеченный относительно прямоугольника. Заметим, что данный алгоритм очевидным образом применим для отсечения любого выпуклого многоугольного окна - достаточно представить это окно в виде пересечений полуплоскостей.

(рис 5.6) Работа алгоритма Сазерлэнда-Ходжмана.(рис 5.5) Пример некорректного отсечения алгоритмом Сазерлэнда-Ходжмана. Слева - исходный многоугольник. Справа - результат отсечения, содержащий лишнее ребро P5P6
Практически единственным недостатком алгоритма Сазерлэнда-Ходжмана является не совсем корректная обработка случаев, когда результатом отсечения являются несколько изолированных многоугольников. Результат алгоритма Сазерлэнда-Ходжмана в этом случае содержит многоугольники и связывающие их отрезки (рис. 5.6).
На практике графические объекты всегда отображаются на конечном растре, границы которого соответствуют границам экрана или внеэкранного буфера. Растеризация на конечном растре требует возможности отсечения растеризуемого объекта относительно границ растра, т.е. удаления частей растеризуемого объекта, лежащих за пределами растра. Выполнение алгоритма
Можно было бы просто проверять, лежит ли пиксель внутри растра непосредственно перед изменением его цвета. К сожалению, такое решение в большинстве случаев слишком неэффективно. Во-первых, подобная проверка сама по себе чрезвычайно замедлит растеризацию. Во-вторых, в тех случаях, когда значительная часть объекта лежит вне растра, растеризация для этой области будет выполняться вхолостую и отсечение подобных областей опять-таки ускорит вычисления.
В этом разделе мы рассмотрим алгоритмы отсечения отрезка и многоугольника относительно границ прямоугольника (иногда мы будем называть этот прямоугольник "окном").
Алгоритм Сазерлэнда-Коэна осуществляет отсечение отрезка относительно прямоугольника со сторонами, параллельными координатным осям.
Пронумеруем стороны прямоугольника так, как это показано на рис. 5.1, и классифицируем все точки в зависимости от их положения по отношению к отсекающим прямым 1, 2, 3, 4. Для этого сопоставим каждой области, на которые разбивают плоскость прямые, 4 -битный код.
(рис 5.1) Алгоритм Сазерлэнда-Коэна.Установим эти биты следующим образом:
| 0 бит | если точка лежит левее прямой 1 ( x < xлево ) |
| 1 бит | если точка лежит ниже прямой 2 ( y < yниз ) |
| 2 бит | если точка лежит правее прямой 3 ( x > xправо ) |
| 3 бит | если точка лежит выше прямой 4 ( y > yверх ) |
Тогда для отрезка AB на рис. 5.1 будем иметь: код точки A = 0011, код точки B = 0100.
Пусть A - код точки-начала отрезка, B - код точки-конца отрезка. Рассмотрим три возможных случая.
A = B = 0000. Этот случай означает, что обе точки лежат внутри прямоугольника (т. е. отсечение не требуется).1 или 2, то необходимо находить точки пересечения с некоторыми из отсекающих прямых (для прямых, которые пересекает AB, соответствующий бит в A xor B установлен в 1 ). Для этого разобьем отрезок найденными точками пересечения и затем применим тот же анализ кодов концов для полученных подотрезков.Один из возможных вариантов реализации окончательного алгоритма приведен ниже:
// (xлево, yниз, xправо, yверх) - отсекающий прямоугольник;
// Функция код(<точка>) возвращает битовый код,
// значение которого описано выше;
Отсечь( отрезок AB )
{
while(( код(A) | код(B) ) AND (!( код(A) код(B) )))
{
if( код(A) == 0) // т.е. A внутри
// меняем координаты местами,
// чтобы A всегда лежала снаружи
поменять(A,B);
if( код(A) 1 ) // т.е. A лежит слева
{
A.x += (B.y - A.y)*(xлево-A.x)/(B.x - A.x);
A.y = xлево;
}
else if( код(A) 2 ) // т.е. A лежит сверху
{
A.x += (B.x - A.x)*(yверх-A.y)/(B.y - A.y);
A.y = yверх;
}
else if( код(A) 4 ) // т.е. A лежит справа
{
A.y += (B.y - A.y)*(xправо-A.x)/(B.x - A.x);
A.x = xправо;
}
else if( код(A) 8 ) // т.е. A лежит снизу
{
A.x += (B.x - A.x)*(yниз -A.y)/(B.y - A.y);
A.y = yниз;
}
}
return AB; // Вернуть отсеченный отрезок
}
Алгоритм Сазерлэнда-Коэна может быть обобщен на случай отсечения отрезка параллелепипедом в трехмерном пространстве. В этом случае код, конечно, уже будет длины 6 (по числу отсекающих граней).
Алгоритм решает ту же задачу: отсечение отрезка относительно прямоугольника. Случаи, в которых отрезок лежит целиком снаружи или внутри окна, выявим методом, изложенным в алгоритме Сазерлэнда-Коэна.
В общем случае возьмем среднюю точку нашего отрезка AB (обозначим ее C1 ) и применим к отрезкам AC1 и C1B этот алгоритм рекурсивно. Следующие средние точки отрезков AC1 и C1B обозначим C2 и C3 соответственно, см. рис. 5.2. И так далее, пока размер оставшихся отрезков не станет меньше размера пикселя.
В примере на рис. 5.2 после второй итерации отрезок AC2 будет отсечен, и для него дальнейшее половинное деление производиться не будет; C1C3 лежит целиком внутри области отсечения, он будет нарисован, и для него дальнейшее половинное деление также производиться не будет. Остальные части будем делить дальше.

(рис 5.2) Алгоритм средней точки(рис 5.2) Алгоритм средней точкиОтсечь( отрезок AB ) { if( длина AB меньше размера пикселя ) return; if( AB лежит вне отсекающего прямоугольника ) return; if( AB лежит внутри отсекающего прямоугольника ) { Отобразить AB; return; } Отсечь (A, (A+B)/2); Отсечь ((A+B)/2),B); }
Алгоритм средней точки прост, но не очень эффективен на практике, так как требует большой глубины рекурсии. В большинстве случаев алгоритм Сазерлэнда-Коэна предпочтительнее. Прием с делением отрезка пополам может оказаться эффективным при отсечении относительно сложной непрямоугольной области, для которой проверить принадлежность точки к окну существенно проще, чем найти пересечение отрезка с границей.
Алгоритм средней точки также обобщается на случай трехмерного пространства тривиальным образом.
Алгоритм Цируса-Бека [25] отсекает отрезок P1P2, используя его параметрическое представление:
Данный алгоритм применим не только в случае отсечения прямоугольником со сторонами, параллельными осям координат, но и в случае, если отсекаемая область ограничена любым выпуклым многоугольником.
Рассмотрим отдельно ребро Ei отсекающего многоугольника. Нормаль к нему ориентируем во внешнюю сторону отсекающего многоугольника, для этого удобно считать, что точки отсекающего контура обходятся против часовой стрелки; тогда если ребро - это $$\overrightarrow{P_{Ei1}P_{Ei2}}$$, то нормаль NEi будет пропорциональна (yEi2-yEi1, xEi1- xEi2), см. рис. 5.3.
Тогда область, отсекаемая прямой, на которой лежит ребро (обозначим ее Li ), соответствует точкам P, для (P-PEi),NEi) > 0, где PEi - любая точка на ребре Ei. Точка пересечения прямой, на которой лежит отрезок с отсекающей прямой Li, находится из уравнения
((Ps(t) - PEi),NEi) = 0.
Разрешая его, получаем, что
$$t = -\frac{((P_1 - P_{Ei}),N_{Ei})}{((P_2 - P_1),N_{Ei})}$$в том случае, если $$(P_2 - P_1,N_{Ei}) \ne 0$$. Если же ((P2 - P1),NEi) = 0 это означает, что отсекаемый отрезок параллелен Li и не существует единственной точки их пересечения. Такие случаи алгоритм игнорирует.
(рис 5.3) Пересечение отрезка с ребром.Для алгоритма Цируса-Бека также важно в каком направлении (внутрь отсекающего многоугольника или из него) проходит точка при движении по отрезку от P1 к P2, т.е. при изменении t от 0 до 1. Это определяется знаком ((P2 - P1),NEi). Будем обозначать такие точки пересечения как:
| Потенциально входящие (ПВх): | ((P2 - P1),NEi) < 0 |
| Потенциально выходящие (ПВых): | ((P2 - P1,NEi) > 0 |
После того как рассчитаны координаты t для всех возможных пересечений с прямыми Li, следует выбрать максимальную координату из потенциально входящих пересечений tВхMax и минимальную из потенциально выходящих tВыхMin. Если прямая, на которой лежит отрезок P1P2, пересекает отсекающий многоугольник, то tВхMax < tВыхMin. В этом случае, если пересечение $$[t_1, t_2] = [t_{ВхMax}, t_{ВыхMin}] \cap [0, 1]$$ непусто, то Ps(t1)Ps(t2) будет искомым отсеченным отрезком. В противном случае отрезок полностью лежит вне отсекаемой области.
(рис 5.4) Возможные случаи пересечений в алгоритме Цируса-Бека.Вычислить NEi и взять PEi = PEi1 для каждого ребра.
Отсечь(отрезок P1P2)
{
if(P1 == P2)
отсечь как точку;
else
{
D = P2 - P1;
t_Вх = 0;
t_Вых = 1;
foreach( ребро Ei из отсекающего многоугольника )
{
dp = (D,NEi);
if( dp != 0 ) // случай dp == 0 игнорируется
{
$$$t = -\frac{((P_1 - P_{Ei}), N_{Ei})}{dp};$$$if( dp < 0 )
{
if( t > t_Вх )
t_Вх = t;
}
else // dp > 0
{
if( t < t_Вых )
t_Вых = t;
}
}
}
if( t_Вх < t_Вых )
return отрезок Ps(t_Вх)Ps(t_Вых);
else
return '
}
}
Алгоритм Цируса-Бека также может быть обобщен до случая отсечения отрезка произвольным выпуклым многогранником в трехмерном пространстве.
Алгоритм Лианга-Барского [39] является более эффективным вариантом алгоритма Цируса-Бека в случае, если отсекающий многоугольник - это прямоугольник со сторонами, параллельными осям координат. В этом случае вычисление промежуточных величин упрощается ( P1 = (x1, y1),P2 = (x2, y2) ):
| Ребро | NEi | PEi | ((P2 - P1),NEi) | t |
|---|---|---|---|---|
| левое: x = xлево |
(-1, 0) | (xлево, yверх) | x1 - x2 | $$-\frac{(x_{лево}-x_1)}{x_1-x_2}$$ |
| нижнее: y = yниз |
(0,-1) | (xлево, yниз) | y1 - y2 | $$-\frac{(y_{низ}-y_1)}{y_1-y_2}$$ |
| правое: x = xправо |
(1, 0) | (xправо, yниз) | x2 - x1 | $$-\frac{(x_1-x_{право})}{x_2-x_1}$$ |
| верхнее: y = yверх |
(0, 1) | (xправо, yверх) | y2 - y1 | $$-\frac{(y_1-y_{верх})}{y_2-y_1}$$ |
// (xлево, yниз, xправо, yверх) - отсекающий прямоугольник;
Отсечь(отрезок P1P2)
{
dx = P2.x - P1.x;
dy = P2.y - P1.y;
if( dx == 0 AND dy == 0 )
отсечь как точку;
else
{
t_Вх = 0;
t_Вых = 1;
// Отсечь_t может модифицировать t_Вх и t_Вых
// ( означает передачу по адресу)
if( Отсечь_t( dx, xлево - P1.x, t_Вх, t_Вых ) )
if( Отсечь_t( -dx, P1.x - xправо, t_Вх, t_Вых ) )
if( Отсечь_t( dy, yниз - P1.y, t_Вх, t_Вых ) )
if( Отсечь_t( -dy, P1.y - yверх, t_Вх, t_Вых ) )
{
if( t_Вх > 0 )
{
P1.x = P1.x + dx*t_Вх;
P1.y = P1.y + dy*t_Вх;
}
if( t_Вых < 1 )
{
P2.x = P1.x + dx*t_Вых;
P2.y = P1.y + dy*t_Вых;
}
return отрезок P1P2;
}
return '
}
}
// Проверить пересечение с ребром
// denom, num - 'знаменатель' и 'числитель' выражения (5.2)
// t_Вх и t_Вых передаются по адресу (обозначено *),
// они могут быть модифицированы;
// возвращает логическую переменную:
// true - продолжать отcечение
// false - закончить отcечение,
// отрезок полностью вне прямоугольника
bool Отсечь_t( denom, num, *t_Вх, *t_Вых )
{
if( denom > 0 ) // Потенциально входящее пересечение
{
t = num / denom;
if( t > t_Вых )
// t_ВхMax > t_ВыхMin: отрезок полностью снаружи
return false;
else if( t > t_Вх ) // Модифицируем t_Вх
t_Вх = t;
}
else if( denom < 0 ) // Потенциально выходящее пересечение
{
t = num / denom;
if( t < t_Вх )
// t_ВхMax > t_ВыхMin: отрезок полностью снаружи
return false;
else if( t < t_Вых ) // Модифицируем t_Вых
t_Вых = t;
}
else if( num > 0 )
// отрезок параллелен ребру и полностью снаружи
return false;
return true;
}
Эксперименты, проведенные авторами на случайных наборах данных [39], показали, что их алгоритм на 25-62% быстрее алгоритма Сазерлэнда-Коэна. Поэтому в настоящее время алгоритм Лианга-Барского получил большее распространение.
Рассмотрим теперь задачу отсечения многоугольника относительно прямоугольника и алгоритм Сазерлэнда-Ходжмана [48], позволяющий проводить такое отсечение.
Ключевым моментом в этом алгоритме является сведение задачи отсечения прямоугольником к задаче отсечения полуплоскостями. Действительно, прямоугольник представляется в виде пересечения четырех полуплоскостей (по сути, этим же представлением мы пользовались и в алгоритме Сазерлэнда-Коэна). Поэтому достаточно поочередно отсечь части многоугольника, лежащие вне каждой полуплоскости.
Пусть многоугольник задан своими вершинами: P1P2 . . . PN. $$\overrightarrow{P_1P_2}, . . . \overrightarrow{P_kP_{k+1}}, . . . \overrightarrow{P_{N-1}P_N}, \overrightarrow{P_NP_1}$$ - направленные ребра этого многоугольника.
Относительно произвольной полуплоскости $$\Pi$$ каждое направленное ребро $$\overrightarrow{P_kP_{k+1}}$$ может находиться в следующих положениях:
Следующий алгоритм выводит в качестве результата вершины отсеченного многоугольника, обходя исходные вершины.
Отсечь P[1]...P[N] относительно полуплоскости
{
//L - граница полуплоскости
for( i = 1; i <= N; i++ )
{
j = i+1;
if( j == N+1 )
j = 1;
if( P[i]P[j] пересекает L )
{
I = пересечение( P[i]P[j], L );
вывести(I);
}
if( P[j]видима )
вывести(P[j]);
}
}
Данный алгоритм выводит вершины отсеченного многоугольника в порядке обхода. Пример работы алгоритма Сазерлэнда-Ходжмана приведен на рис. 5.5.
После проведения отсечения четырьмя полуплоскостями получается многоугольник, отсеченный относительно прямоугольника. Заметим, что данный алгоритм очевидным образом применим для отсечения любого выпуклого многоугольного окна - достаточно представить это окно в виде пересечений полуплоскостей.

(рис 5.6) Работа алгоритма Сазерлэнда-Ходжмана.(рис 5.5) Пример некорректного отсечения алгоритмом Сазерлэнда-Ходжмана. Слева - исходный многоугольник. Справа - результат отсечения, содержащий лишнее ребро P5P6
Практически единственным недостатком алгоритма Сазерлэнда-Ходжмана является не совсем корректная обработка случаев, когда результатом отсечения являются несколько изолированных многоугольников. Результат алгоритма Сазерлэнда-Ходжмана в этом случае содержит многоугольники и связывающие их отрезки (рис. 5.6).
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.