crypt of decay - обсечение линий и брезенхэм [entries|archive|friends|userinfo]
ketmar

[ userinfo | ljr userinfo ]
[ archive | journal archive ]

обсечение линий и брезенхэм [Jan. 23rd, 2015|11:41 pm]
Previous Entry Add to Memories Tell A Friend Next Entry
тот, кто этого никогда не делал, может удивиться, зачем я вообще пишу о такой простой фигне. а кто делал, тот может ностальгически вспомнить «прыгающие» линии.

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

по какому поводу предлагаю вам обсекатель, совмещённый с брезенхэмовой рисовалкой, и избавленый от попрыгушек. он базируется на одной небольшой статье, где рассказано про очевидное и даже доказано, что оно работает. оное очевидное заключается в том, что не попадающая в нужный прямоугольник часть линии не рисуется попиксельно, а тупо скипается за один шаг, сохраняя таким образом нормальные значения брезенхэмовских «корректирующих значений».

поскольку статья свободно не доступна, я не привожу ни названия, ни авторов. те, кто скрывают знания, не заслуживают упоминания.

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);
}
Linkmeow!