тот, кто этого никогда не делал, может удивиться, зачем я вообще пишу о такой простой фигне. а кто делал, тот может ностальгически вспомнить «прыгающие» линии.
большинство алгоритмов обсечения линии работают отдельно от алгоритмов рисования линии, поэтому для того, чтобы линия не «прыгала» при обсечении, надо нежно состыковывать дробные результаты обсекателя и рисователя. это, тащемта, не то, чтобы сильно сложно, но всё равно хлопотно.
по какому поводу предлагаю вам обсекатель, совмещённый с брезенхэмовой рисовалкой, и избавленый от попрыгушек. он базируется на одной небольшой статье, где рассказано про очевидное и даже доказано, что оно работает. оное очевидное заключается в том, что не попадающая в нужный прямоугольник часть линии не рисуется попиксельно, а тупо скипается за один шаг, сохраняя таким образом нормальные значения брезенхэмовских «корректирующих значений».
поскольку статья свободно не доступна, я не привожу ни названия, ни авторов. те, кто скрывают знания, не заслуживают упоминания.
enum width = 320;
enum height = 200;
void putPixel (int x, int y, uint clr) {
import iv.writer;
writefln!"(%s, %s)"(x, y);
}
private static template swap (alias a, alias b, usize ln=__LINE__) {
enum swap =
"{"
~typeof(a).stringof~" tmp_"~ln.stringof~"_="~a.stringof~";"~
a.stringof~"="~b.stringof~";"~
b.stringof~"=tmp_"~ln.stringof~"_;}";
}
// as the paper on which this code is based in not available to public,
// i say "fuck you!"
// knowledge must be publicly available; the ones who hides the knowledge
// are not deserving any credits.
void drawline (int x0, int y0, int x1, int y1, uint clr) {
// clip rectange
int wx0 = 0, wy0 = 0, wx1 = width-1, wy1 = height-1;
// other vars
int stx, sty; // "steps" for x and y axes
int dsx, dsy; // "lengthes" for x and y axes
int dx2, dy2; // "double lengthes" for x and y axes
int xd, yd; // current coord
int e; // "error" (as in bresenham algo)
int rem;
int term;
int *d0, d1;
// horizontal setup
if (x0 < x1) {
// from left to right
if (x0 > wx1 || x1 < wx0) return; // out of screen
stx = 1; // going right
} else {
// from right to left
if (x1 > wx1 || x0 < wx0) return; // out of screen
stx = -1; // going left
x0 = -x0;
x1 = -x1;
wx0 = -wx0;
wx1 = -wx1;
mixin(swap!(wx0, wx1));
}
// vertical setup
if (y0 < y1) {
// from top to bottom
if (y0 > wy1 || y1 < wy0) return; // out of screen
sty = 1; // going down
} else {
// from bottom to top
if (y1 > wy1 || y0 < wy0) return; // out of screen
sty = -1; // going up
y0 = -y0;
y1 = -y1;
wy0 = -wy0;
wy1 = -wy1;
mixin(swap!(wy0, wy1));
}
dsx = x1-x0;
dsy = y1-y0;
if (dsx < dsy) {
d0 = &yd;
d1 = &xd;
mixin(swap!(x0, y0));
mixin(swap!(x1, y1));
mixin(swap!(dsx, dsy));
mixin(swap!(wx0, wy0));
mixin(swap!(wx1, wy1));
mixin(swap!(stx, sty));
} else {
d0 = &xd;
d1 = &yd;
}
dx2 = 2*dsx;
dy2 = 2*dsy;
xd = x0;
yd = y0;
e = 2*dsy-dsx;
term = x1;
bool xfixed = false;
if (y0 < wy0) {
// clip at top
int temp = dx2*(wy0-y0)-dsx;
xd += temp/dy2;
rem = temp%dy2;
if (xd > wx1) return; // x is moved out of clipping rect, nothing to do
if (xd+1 >= wx0) {
yd = wy0;
e -= rem+dsx;
if (rem > 0) { ++xd; e += dy2; }
xfixed = true;
}
}
if (!xfixed && x0 < wx0) {
// clip at left
int temp = dy2*(wx0-x0);
yd += temp/dx2;
rem = temp%dx2;
if (yd > wy1 || yd == wy1 && rem >= dsx) return;
xd = wx0;
e += rem;
if (rem >= dsx) { ++yd; e -= dx2; }
}
if (y1 > wy1) {
// clip at bottom
int temp = dx2*(wy1-y0)+dsx;
term = x0+temp/dy2;
rem = temp%dy2;
if (rem == 0) --term;
}
if (term > wx1) term = wx1; // clip at right
++term;
if (sty == -1) yd = -yd;
if (stx == -1) { xd = -xd; term = -term; }
dx2 -= dy2;
// draw it; `putPixel()` can omit checks
while (xd != term) {
putPixel(*d0, *d1, clr);
if (e >= 0) {
yd += sty;
e -= dx2;
} else {
e += dy2;
}
xd += stx;
}
}
void main () {
drawline(5, 1, -3, 2, 42);
} |