Blob Blame Raw

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <helianthus.h>




typedef struct {
  double x, y;
  double color[4];
} Vertex;

int cs = 5, vw = 120, vh = 90;
int lx1, ly1, lx2, ly2, lx3, ly3;
Vertex tv1, tv2, tv3;


void grid(int w, int h) {
  saveState();
  strokeWidth(0.1);
  stroke(colorByNameA("gray", 0.5));
  for(int x = 0; x <= w; ++x) line(x, 0, x, h);
  for(int y = 0; y <= h; ++y) line(0, y, w, y);
  restoreState();
}


void dot(int x, int y)
  { rect(x, y, 1, 1); }

void dotColored(int x, int y, double *color) {
  saveState();
  fill(colorByRGBA(color[0], color[1], color[2], color[3]));
  dot(x, y);
  restoreState();
}


void lineBase(int x1, int y1, int x2, int y2) {
	int x_start_line;	// put the lowest x in start line and the highest in end line
	int x_end_line;
	int y_start_line;	// put the lowest y in start line and the highest in end line
	int need_reflect;
	int dist_point_to_line;
	int switch_roles = FALSE;
	int delta_x;
	int delta_y;
	int temp;

	// take care of end cases when the two points share an index value
	// take care of end cases when the two points share an index value// take care of end cases when the two points share an index value// take care of end cases when the two points share an index value
	//	need to take care of the case where the point is outside of the window frame

		// Initialization for the Bershenham algorithm:
	x_start_line = x1, y_start_line = y1;
	x_end_line = x1;
	delta_x = (x2 - x1);
	if (delta_x < 0)
		delta_x = delta_x * (-1);	// make delta_x positive
	delta_y = (y2 - y1);
	if (delta_y < 0)
		delta_y = delta_y * (-1);	// make delta_y positive
	if (delta_x != 0 || delta_y != 0)
	{
		// first case: 0<incline<1 :
		if (delta_y < delta_x)	// check if the incline is a fraction or not, because they are both positive
		{						// meaning -1<incline<0 or 0<incline<1 is the value
			if (x1 < x2)
			{
				x_start_line = x1, y_start_line = y1;
				x_end_line = x2;
				need_reflect = (y1 < y2) ? 1 : -1;	// take care of the case of negative incline
			}
			else	// the start point is p2
			{
				x_start_line = x2, y_start_line = y2;
				x_end_line = x1;
				need_reflect = (y2 < y1) ? 1 : -1;	// take care of the case of negative incline
			}
		}
		else	// the incline is either incline>1 or incline<-1
		{		// so we need to change the x and y roles
			if (y1 < y2)	// the start point is p1 and switch the x and y
			{
				x_start_line = y1;	// swith roles of x and y
				y_start_line = x1;
				x_end_line = y2;
				need_reflect = (x1 < x2) ? 1 : -1;
			}
			else	// the start point is p2 and switch the x and y roles
			{
				x_start_line = y2;
				y_start_line = x2;
				x_end_line = y1;
				need_reflect = (x2 < x1) ? 1 : -1;
			}
			switch_roles = TRUE;
			temp = delta_x;	// switch the delta_x and delta_y because the roles have changed
			delta_x = delta_y;
			delta_y = temp;
		}
		dist_point_to_line = 2 * delta_y - delta_x;
		// Testing Barriers:
		if (x_end_line > vw)	// need to add barrier for viewport height without causing lines to disappear
			x_end_line = vw;
		if (x_start_line < 0)
			x_start_line = 0;
		if (y_start_line < 0)
			y_start_line = 0;
		for (int i = x_start_line; i <= x_end_line; i++)	// go over the incline from start point to end point and draw the line
		{													// put each pixel in the right place
			switch_roles ? dot(y_start_line, i) : dot(i, y_start_line);
			// new stuff for edge walking

			// end of new stuff
			if (dist_point_to_line > 0)
			{
				y_start_line = y_start_line + need_reflect;
				dist_point_to_line = dist_point_to_line - 2 * delta_x;
			}
			dist_point_to_line = dist_point_to_line + 2 * delta_y;
		}	//x_start_line,x_end_line,switch_roles,y_start_line,need_reflect,dist_point_to_line,delta_x,delta_y
	}
}


void lineNew(int x1, int y1, int x2, int y2) {
  if (y1 > y2) { int x = x1, y = y1; x1 = x2; y1 = y2; x2 = x; y2 = y; }

	int x = x1;
	int dx = x2 - x1;
	int dy = y2 - y1;
	if (dy > 0) {
    int sign_x = dx < 0 ? -1 : 1;
    int step_x = dx/dy;
    int sub_step_x = abs(dx)%dy;
    int sub_step_accum = -dy/2;
    for(int y = y1; y < y2; ++y) {
      dot(x, y);

      x += step_x;
      sub_step_accum += sub_step_x;
      if (sub_step_accum > 0) {
        sub_step_accum -= dy;
        x += sign_x;
      }
    }
  } else {
    dot(x1, y1);
  }

	dot(x2, y2);
}


void row(int y, int x1, int x2) {
  if (x1 > x2) { int x = x1; x1 = x2; x2 = x; }
  if (x1 < 0) x1 = 0;
  if (x2 > vw) x2 = vw;
  for(int x = x1; x < x2; ++x) dot(x, y);
}


void triangle(int vx1, int vy1, int vx2, int vy2, int vx3, int vy3) {
  if (vy1 > vy3) { int x = vx1, y = vy1; vx1 = vx3; vy1 = vy3; vx3 = x; vy3 = y; }
  if (vy2 > vy3) { int x = vx2, y = vy2; vx2 = vx3; vy2 = vy3; vx3 = x; vy3 = y; }
  if (vy1 > vy2) { int x = vx1, y = vy1; vx1 = vx2; vy1 = vy2; vx2 = x; vy2 = y; }

	// split triangle to two triangles by the middle vertex
	//
	//       *__
	//      /    --__    - top triangle here
	//     /-------__-*  - split triangle by this line
	//    /    __--
	//   / __--          - bottom triangle here
	//  *--

	// include pixels at left and top sides, but exclude at right and bottom
	// it required to seamless clue neighboring triangles without overlapping

	if (vy1 >= vy3) return;
	if (vy1 >= vh || vy3 <= 0) return;
	if (vx1 >= vw && vx2 >= vw && vx3 >= vw) return;
	if (vx1 <= 0  && vx2 <= 0  && vx3 <= 0 ) return;

	// configure line from top vertex to bottom vertex
	int x1 = vx1;
	int dx1 = vx3 - vx1;
	int dy1 = vy3 - vy1;
  int sign_x1 = dx1 < 0 ? -1 : 1;
  int step_x1 = dx1/dy1;
  int sub_step_x1 = abs(dx1)%dy1;
  int sub_step_accum1 = -dy1/2;

  // draw top triangle
	if (vy2 > vy1) {
    // configure line from top vertex to middle vertex
    int x2 = vx1;
    int dx2 = vx2 - vx1;
    int dy2 = vy2 - vy1;
    int sign_x2 = dx2 < 0 ? -1 : 1;
    int step_x2 = dx2/dy2;
    int sub_step_x2 = abs(dx2)%dy2;
    int sub_step_accum2 = -dy2/2;

    // skip invisible rows
    int y1 = vy1, y2 = vy2;
    if (y1 < 0) {
      const int rows = (y2 < 0 ? y2 : 0) - y1;
      y1 += rows;
      x1 += rows*step_x1;
      sub_step_accum1 += rows*sub_step_x1;
      if (sub_step_accum1 > 0) {
        const int count = sub_step_accum1/dy1 + 1;
        sub_step_accum1 -= count*dy1;
        x1 += count*sign_x1;
      }

      x2 += rows*step_x2;
      sub_step_accum2 += rows*sub_step_x2;
      if (sub_step_accum2 > 0) {
        const int count = sub_step_accum2/dy2 + 1;
        sub_step_accum2 -= count*dy2;
        x2 += count*sign_x2;
      }
    }
    if (y2 > vh) y2 = vh;

    // iterate over visible rows
    for(int y = y1; y < y2; ++y) {
      row(y, x1, x2);

      // increment line 1
      x1 += step_x1;
      sub_step_accum1 += sub_step_x1;
      if (sub_step_accum1 > 0)
        { sub_step_accum1 -= dy1; x1 += sign_x1; }

      // increment line 2
      x2 += step_x2;
      sub_step_accum2 += sub_step_x2;
      if (sub_step_accum2 > 0)
        { sub_step_accum2 -= dy2; x2 += sign_x2; }
    }
  }

  // draw bottom triangle
	if (vy3 > vy2) {
    // configure line from middle vertex to bottom vertex
    int x2 = vx2;
    int dx2 = vx3 - vx2;
    int dy2 = vy3 - vy2;
    int sign_x2 = dx2 < 0 ? -1 : 1;
    int step_x2 = dx2/dy2;
    int sub_step_x2 = abs(dx2)%dy2;
    int sub_step_accum2 = -dy2/2;

    // skip invisible rows
    int y1 = vy2, y2 = vy3;
    if (y1 < 0) {
      const int rows = -y1;
      y1 += rows;
      x1 += rows*step_x1;
      sub_step_accum1 += rows*sub_step_x1;
      if (sub_step_accum1 > 0) {
        const int count = sub_step_accum1/dy1 + 1;
        sub_step_accum1 -= count*dy1;
        x1 += count*sign_x1;
      }

      x2 += rows*step_x2;
      sub_step_accum2 += rows*sub_step_x2;
      if (sub_step_accum2 > 0) {
        const int count = sub_step_accum2/dy2 + 1;
        sub_step_accum2 -= count*dy2;
        x2 += count*sign_x2;
      }
    }
    if (y2 > vh) y2 = vh;

    // iterate over visible rows
    for(int y = y1; y < y2; ++y) {
      row(y, x1, x2);

      // increment line 1
      x1 += step_x1;
      sub_step_accum1 += sub_step_x1;
      if (sub_step_accum1 > 0)
        { sub_step_accum1 -= dy1; x1 += sign_x1; }

      // increment line 2
      x2 += step_x2;
      sub_step_accum2 += sub_step_x2;
      if (sub_step_accum2 > 0)
        { sub_step_accum2 -= dy2; x2 += sign_x2; }
    }
  }
}


void rowNew(int y, int x1, int x2, double *color, double *inc) {
  if (x1 < x2) {
    if (x1 >= vw || x2 <= 0) return;
    if (x1 < 0) {
      int skip = -x1;
      for(int i = 0; i < 4; ++i) color[i] += skip*inc[i];
      x1 += skip;
    }
    if (x2 > vw) x2 = vw;
    for(int x = x1; x < x2; ++x) {
      dotColored(x, y, color);
      for(int i = 0; i < 4; ++i) color[i] += inc[i];
    }
  } else {
    if (x2 >= vw || x1 <= 0) return;
    if (x1 > vw) {
      int skip = vw - x1;
      for(int i = 0; i < 4; ++i) color[i] += skip*inc[i];
      x1 += skip;
    }
    if (x2 < 0) x2 = 0;
    for(int x = x1-1; x >= x2; --x) {
      for(int i = 0; i < 4; ++i) color[i] -= inc[i];
      dotColored(x, y, color);
    }
  }
}


void triangleNew(const Vertex *v1, const Vertex *v2, const Vertex *v3) {
  if (v1->y > v3->y) { const Vertex *v = v1; v1 = v3; v3 = v; }
  if (v2->y > v3->y) { const Vertex *v = v2; v2 = v3; v3 = v; }
  if (v1->y > v2->y) { const Vertex *v = v1; v1 = v2; v2 = v; }

	// split triangle to two triangles by the middle vertex
	//
	//       *__
	//      /    --__    - top triangle here
	//     /-------__-*  - split triangle by this line
	//    /    __--
	//   / __--          - bottom triangle here
	//  *--

	// include pixels at left and top sides, but exclude at right and bottom
	// it required to seamless clue neighboring triangles without overlapping

	const int vx1 = (int)floor(v1->x);
	const int vy1 = (int)floor(v1->y);
	const int vx2 = (int)floor(v2->x);
	const int vy2 = (int)floor(v2->y);
	const int vx3 = (int)floor(v3->x);
	const int vy3 = (int)floor(v3->y);

	const int dx31 = vx3 - vx1;
	const int dy31 = vy3 - vy1;

	const int dx21 = vx2 - vx1;
	const int dy21 = vy2 - vy1;

	const int dx32 = vx3 - vx2;
	const int dy32 = vy3 - vy2;

	if (dy31 <= 0) return;
	if (vy1 >= vh || vy3 <= 0) return;
	if (vx1 >= vw && vx2 >= vw && vx3 >= vw) return;
	if (vx1 <= 0  && vx2 <= 0  && vx3 <= 0 ) return;

	// calculate area
	const int double_area = dx31*dy31 - dx21*dy21 + dx32*dy32 - 2*dx32*dy31;
	if (!double_area) return;
	const double one_div_2a = 1.0/double_area;

	// configure line from top vertex to bottom vertex
  const int sign_x1 = dx31 < 0 ? -1 : 1;
  const int step_x1 = dx31/dy31;
  const int sub_step_x1 = abs(dx31)%dy31;
	int x1 = vx1;
  int sub_step_accum1 = -dy31/2;

  // configure interpolation coefficients for the middle vertex
	const double k2_dx = one_div_2a*dy31;
	const double k2_dy = one_div_2a*(dy31*step_x1 - dx31);
  double k2 = 0;

  // draw top triangle
	if (vy2 > vy1) {
    // configure line from top vertex to middle vertex
    const int sign_x2 = dx21 < 0 ? -1 : 1;
    const int step_x2 = dx21/dy21;
    const int sub_step_x2 = abs(dx21)%dy21;
    int x2 = vx1;
    int sub_step_accum2 = -dy21/2;

    // configure interpolation coefficients for the bottom vertex
    const double k3_dx = -one_div_2a*dy21;
    const double k3_dy = -one_div_2a*(dy21*step_x1 - dx21);
    double color[4], inc[4];
    double k3 = 0;
    for(int i = 0; i < 4; ++i)
      inc[i] = k2_dx*v2->color[i] + k3_dx*v3->color[i] - (k2_dx + k3_dx)*v1->color[i];

    // skip invisible rows
    int y1 = vy1, y2 = vy2;
    if (y1 < 0) {
      const int rows = (y2 < 0 ? y2 : 0) - y1;
      y1 += rows;
      x1 += rows*step_x1;
      k2 += rows*k2_dy;
      k3 += rows*k3_dy;
      sub_step_accum1 += rows*sub_step_x1;
      if (sub_step_accum1 > 0) {
        const int count = sub_step_accum1/dy31 + 1;
        sub_step_accum1 -= count*dy31;
        x1 += count*sign_x1;
        k2 += count*sign_x1*k2_dx;
        k3 += count*sign_x1*k3_dx;
      }

      x2 += rows*step_x2;
      sub_step_accum2 += rows*sub_step_x2;
      if (sub_step_accum2 > 0) {
        const int count = sub_step_accum2/dy21 + 1;
        sub_step_accum2 -= count*dy21;
        x2 += count*sign_x2;
      }
    }
    if (y2 > vh) y2 = vh;

    // iterate over visible rows
    for(int y = y1; y < y2; ++y) {
      double k1 = 1 - k2 - k3;
      for(int i = 0; i < 4; ++i)
        color[i] = k1*v1->color[i] + k2*v2->color[i] + k3*v3->color[i];
      rowNew(y, x1, x2, color, inc);

      // increment line 1
      x1 += step_x1;
      k2 += k2_dy;
      k3 += k3_dy;
      sub_step_accum1 += sub_step_x1;
      if (sub_step_accum1 > 0) {
        sub_step_accum1 -= dy31;
        x1 += sign_x1;
        k2 += sign_x1*k2_dx;
        k3 += sign_x1*k3_dx;
      }

      // increment line 2
      x2 += step_x2;
      sub_step_accum2 += sub_step_x2;
      if (sub_step_accum2 > 0)
        { sub_step_accum2 -= dy21; x2 += sign_x2; }
    }
  }

  // draw bottom triangle
	if (vy3 > vy2 && vy2 < vh) {
    // configure line from middle vertex to bottom vertex
    const int sign_x2 = dx32 < 0 ? -1 : 1;
    const int step_x2 = dx32/dy32;
    const int sub_step_x2 = abs(dx32)%dy32;
    int sub_step_accum2 = -dy32/2;
    int x2 = vx2;

    // configure interpolation coefficients for the top vertex
    const double k1_dx = -one_div_2a*dy32;
    const double k1_dy = -one_div_2a*(dy32*step_x1 - dx32);
    double color[4], inc[4];
    double k1 = -one_div_2a*dy32*(x1 - vx2);
    for(int i = 0; i < 4; ++i)
      inc[i] = k1_dx*v1->color[i] + k2_dx*v2->color[i] - (k1_dx + k2_dx)*v3->color[i];

    // skip invisible rows
    int y1 = vy2, y2 = vy3;
    if (y1 < 0) {
      const int rows = -y1;
      y1 += rows;
      x1 += rows*step_x1;
      k1 += rows*k1_dy;
      k2 += rows*k2_dy;
      sub_step_accum1 += rows*sub_step_x1;
      if (sub_step_accum1 > 0) {
        const int count = sub_step_accum1/dy31 + 1;
        sub_step_accum1 -= count*dy31;
        x1 += count*sign_x1;
        k1 += count*sign_x1*k1_dx;
        k2 += count*sign_x1*k2_dx;
      }

      x2 += rows*step_x2;
      sub_step_accum2 += rows*sub_step_x2;
      if (sub_step_accum2 > 0) {
        const int count = sub_step_accum2/dy32 + 1;
        sub_step_accum2 -= count*dy32;
        x2 += count*sign_x2;
      }
    }
    if (y2 > vh) y2 = vh;

    // iterate over visible rows
    for(int y = y1; y < y2; ++y) {
      double k3 = 1 - k1 - k2;
      for(int i = 0; i < 4; ++i)
        color[i] = k1*v1->color[i] + k2*v2->color[i] + k3*v3->color[i];
      rowNew(y, x1, x2, color, inc);

      // increment line 1
      x1 += step_x1;
      k1 += k1_dy;
      k2 += k2_dy;
      sub_step_accum1 += sub_step_x1;
      if (sub_step_accum1 > 0) {
        sub_step_accum1 -= dy31;
        x1 += sign_x1;
        k1 += sign_x1*k1_dx;
        k2 += sign_x1*k2_dx;
      }

      // increment line 2
      x2 += step_x2;
      sub_step_accum2 += sub_step_x2;
      if (sub_step_accum2 > 0)
        { sub_step_accum2 -= dy32; x2 += sign_x2; }
    }
  }
}


void setTriangle(int x1, int y1, int x2, int y2, int x3, int y3) {
  tv1.x = lx1 = x1; tv1.y = ly1 = y1;
  tv2.x = lx2 = x2; tv2.y = ly2 = y2;
  tv3.x = lx3 = x3; tv3.y = ly3 = y3;
  printf("setTriangle(%2d, %2d, %2d, %2d, %2d, %2d);\n", lx1, ly1, lx2, ly2, lx3, ly3);
}

void setLine(int x1, int y1, int x2, int y2) {
  lx1 = x1; ly1 = y1;
  lx2 = x2; ly2 = y2;
  printf("setLine(%2d, %2d, %2d, %2d);\n", lx1, ly1, lx2, ly2);
}

void generateLine() {
  setLine( randomNumber(0, vw-1), randomNumber(0, vh-1),
           randomNumber(0, vw-1), randomNumber(0, vh-1) );
}

void generateTriangle() {
  setTriangle( randomNumber(-vw/2, vw+vw/2), randomNumber(-vh/2, vh+vh/2),
               randomNumber(-vw/2, vw+vw/2), randomNumber(-vh/2, vh+vh/2),
               randomNumber(-vw/2, vw+vw/2), randomNumber(-vh/2, vh+vh/2) );
}


void init() {
  tv1.color[0] = tv1.color[3] = 1;
  tv2.color[1] = tv2.color[3] = 1;
  tv3.color[2] = tv3.color[3] = 1;

  //generateLine();
  //setLine( 2, 18, 14,  1);
  //setLine(14,  1,  2, 18);
  //setLine( 0,  0, 12,  7);
  //generateTriangle();
  setTriangle(94, 155, 116, 46, 236, 96);
}


void deinit() { }


void draw() {
  double w = windowGetWidth();
  double h = windowGetHeight();

  saveState();
  translate(0.5*w, 0.5*h);
  zoom(cs);
  translate(-0.5*vw, -0.5*vh);
  grid(vw, vh);

  if (keyWentDown("space")) generateTriangle();


  noStroke();
  fill(colorByNameA("blue", 0.5));
  triangle(lx1, ly1, lx2, ly2, lx3, ly3);
  //triangleNew(&tv1, &tv2, &tv3);

  stroke(colorByName("black"));
  strokeWidth(0.1);
  dotColored(lx1, ly1, tv1.color);
  dotColored(lx2, ly2, tv2.color);
  dotColored(lx3, ly3, tv3.color);

  restoreState();
}


int main() {
  windowSetVariableFrameRate();
  windowSetResizable(TRUE);
  windowSetSize(2*cs*vw, 2*cs*vh);
  windowSetInit(&init);
  windowSetDeinit(&deinit);
  windowSetDraw(&draw);
  windowRun();
}