/* beztwist.c 17-Sep-88 ** ** Display some twisting cubic Bezier curves. ** Shows speed of code generated by the Turbo C compiler. ** Works with all 3 resolution modes of the Atari ST. */ #include #include #include "grtypes.h" #define NULL ((void *) 0L) #define NIL ((void *) -1L) typedef struct { SCREEN screen; RESOLUTION res; int planes, words_per_line; COLOR color, col_mask; COORD x_max, y_max; long max_area; } INFO; typedef struct { COORD x[4]; COORD y[4]; int active; } BEZIER; #define N 10 /* length of FIFO queue */ #define FLAT 32 #define CONSOLE 2 #define BITS 5 #define SCALE (1 << BITS) #define SCALE_2 (1 << (BITS-1)) /* draw_line() ** ** Draw a line from point (x_from, y_from) to point (x_to, y_to). ** Use Bresenham's line drawing algorithm with incremental ** screen address computation and inline plotting. (Fast!) */ void draw_line( register INFO *info, COORD x_from, COORD y_from, COORD x_to, COORD y_to) { register SCREEN screen; COORD delta_x, delta_y, incr_left, incr_left_up; COORD count, dist, offset; register COLOR color; register WORD or_mask, and_mask; int flag; delta_y = y_to - y_from; if ((delta_x = x_to - x_from) < 0) { delta_x = -delta_x; delta_y = -delta_y; x_from = x_to; y_from = y_to; } if (delta_y < 0) { offset = -info->words_per_line; delta_y = -delta_y; } else { offset = info->words_per_line; } if (delta_x < delta_y) { flag = 1; count = delta_y; delta_y = delta_x; delta_x = count; } else { flag = 0; count = delta_x; } screen = info->screen + ((y_from * info->words_per_line) + (x_from >> 4) * info->planes); or_mask = 0x8000 >> (x_from & 0x0f); incr_left = 2 * delta_y; dist = incr_left - delta_x; incr_left_up = dist - delta_x; color = info->color; for (;;) { and_mask = ~or_mask; switch (info->res) { case low_res: if (color & 8) screen[3] |= or_mask; else screen[3] &= and_mask; if (color & 4) screen[2] |= or_mask; else screen[2] &= and_mask; case med_res: if (color & 2) screen[1] |= or_mask; else screen[1] &= and_mask; case hi_res: if (color & 1) screen[0] |= or_mask; else screen[0] &= and_mask; default: break; } if (count == 0) break; count--; if (dist >= 0) { /* Move 1 pixel 'left' and 'up'. */ dist += incr_left_up; or_mask >>= 1; screen += offset; } else { /* Move 1 pixel 'left'. */ dist += incr_left; if (flag) screen += offset; else or_mask >>= 1; } if (or_mask == 0x0000) { screen += info->planes; or_mask = 0x8000; } } } /* bezier() ** ** Draw the cubic Bezier curve specified by P0, P1, P2 and P3 ** by subdividing it recursively into sub-curves until it ** can be approximated smoothly by straight line segments. ** We use fixed point arithmetic to achieve increased precision ** in coordinate computations. The control points have been ** scaled with SCALE and 0.5 has been added to the coordinates ** so that we can feed rounded coordinate values to draw_line(). */ void bezier( INFO *info, long x0, long y0, long x1, long y1, long x2, long y2, long x3, long y3) { long v1x, v1y, v2x, v2y, v3x, v3y; long vcp1, vcp2, vcp3, area; long mid_x, mid_y; int code; /* ** Stop the recursion when the size of the area covered ** by the convex hull of the Bezier curve is less than or ** equal to info->max_area. */ /* First, compute direction vectors. */ v3x = x3 - x0; v3y = y3 - y0; v2x = x2 - x0; v2y = y2 - y0; v1x = x1 - x0; v1y = y1 - y0; /* Then, compute vector cross products. */ code = 0; if ((vcp1 = v3x * v2y - v2x * v3y) < 0) code += 4; if ((vcp2 = v3x * v1y - v1x * v3y) < 0) code += 2; if ((vcp3 = v2x * v1y - v1x * v2y) < 0) code += 1; /* Finally, compute size of area covered by convex hull. */ /* We actually compute 2*area, but that doesn't matter much. */ switch (code) { case 0: case 2: case 5: case 7: area = vcp1 + vcp3; break; case 1: case 6: area = vcp2 - vcp3; break; case 3: case 4: area = vcp1 - vcp2; break; default: return; } if (code & 4) area = -area; if (area <= info->max_area) { /* Stop recursion and draw a line from P0 to P3. */ /* Rescale and round coordinates before */ /* feeding them to draw_line(). */ draw_line(info, x0 >> BITS, y0 >> BITS, x3 >> BITS, y3 >> BITS); } else { /* Area is still too big, so subdivide the curve into */ /* two sub-curves and draw these recursively. */ mid_x = (x0 + 3*x1 + 3*x2 + x3) >> 3; mid_y = (y0 + 3*y1 + 3*y2 + y3) >> 3; bezier( info, x0, y0, (x0 + x1) >> 1, (y0 + y1) >> 1, (x0 + 2*x1 + x2) >> 2, (y0 + 2*y1 + y2) >> 2, mid_x, mid_y); bezier( info, mid_x, mid_y, (x1 + 2*x2 + x3) >> 2, (y1 + 2*y2 + y3) >> 2, (x2 + x3) >> 1, (y2 + y3) >> 1, x3, y3); } } /* ** Show some randomly twisting bezier curves. */ int main(argc, argv) int argc; char *argv[]; { INFO info; BEZIER list[N]; BEZIER *q; COORD dx[4], dy[4]; COORD *p; COLOR color; int i, j, k, n; long loop = -1, flat = 0; if (argc >= 2) loop = atoi(argv[1]); if (loop < 1) loop = -1; if (argc >= 3) flat = atoi(argv[2]); info.screen = Physbase(); Setscreen(NIL, NIL, info.res = Getrez()); switch (info.res) { case hi_res: info.x_max = HI_X_MAX; info.y_max = HI_Y_MAX; info.planes = HI_PLANES; info.words_per_line = HI_WORDS_PER_LINE; info.max_area = (flat >= 1 ? flat : FLAT) * SCALE * SCALE; info.col_mask = HI_COLORS - 1; color = HI_COLORS - 1; break; case med_res: info.x_max = MED_X_MAX; info.y_max = MED_Y_MAX; info.planes = MED_PLANES; info.words_per_line = MED_WORDS_PER_LINE; info.max_area = (flat >= 1 ? flat : (3*FLAT)/4) * SCALE * SCALE; info.col_mask = MED_COLORS - 1; color = MED_COLORS - 1; break; case low_res: info.x_max = LOW_X_MAX; info.y_max = LOW_Y_MAX; info.planes = LOW_PLANES; info.words_per_line = LOW_WORDS_PER_LINE; info.max_area = (flat >= 1 ? flat : FLAT/2) * SCALE * SCALE; info.col_mask = LOW_COLORS - 1; color = LOW_COLORS - 1; break; default: return -1; } for (i = 1; i < N; i++) list[i].active = 0; i = 0; q = &(list[0]); q->active = 1; for (k = 0; k < 4; k++) { q->x[k] = Random() % (info.x_max + 1); q->y[k] = Random() % (info.y_max + 1); n = (Random() & 15) - 7; if (n == 0) n = 1; else if (n == 8) n = -1; dx[k] = n; n = (Random() & 15) - 7; if (n == 0) n = 1; else if (n == 8) n = -1; dy[k] = n; } while (loop--) { info.color = color; bezier( &info, q->x[0] * SCALE + SCALE_2, q->y[0] * SCALE + SCALE_2, q->x[1] * SCALE + SCALE_2, q->y[1] * SCALE + SCALE_2, q->x[2] * SCALE + SCALE_2, q->y[2] * SCALE + SCALE_2, q->x[3] * SCALE + SCALE_2, q->y[3] * SCALE + SCALE_2); j = i; i = (i + 1) % N; q = &(list[i]); if (q->active) { info.color = 0; bezier( &info, q->x[0] * SCALE + SCALE_2, q->y[0] * SCALE + SCALE_2, q->x[1] * SCALE + SCALE_2, q->y[1] * SCALE + SCALE_2, q->x[2] * SCALE + SCALE_2, q->y[2] * SCALE + SCALE_2, q->x[3] * SCALE + SCALE_2, q->y[3] * SCALE + SCALE_2); } q->active = 1; for (k = 0; k < 4; k++) { p = &(q->x[k]); *p = list[j].x[k] + dx[k]; if (*p < 0) { *p += 8; dx[k] = -dx[k]; } else if (*p > info.x_max) { *p -= 8; dx[k] = -dx[k]; } p = &(q->y[k]); *p = list[j].y[k] + dy[k]; if (*p < 0) { *p += 8; dy[k] = -dy[k]; } else if (*p > info.y_max) { *p -= 8; dy[k] = -dy[k]; } } if ((loop & 0xff) == 0) { /* change direction */ k = Random() & 3; n = (Random() & 15) - 7; if (n == 0) n = 1; else if (n == 8) n = -1; if (Random() & 1) dx[k] = n; else dy[k] = n; /* change color */ color = (Random() % info.col_mask) + 1; } /* exit if any key pressed */ if (Bconstat(CONSOLE)) loop = 0; } return 0; }