+++ /dev/null
-#!/usr/bin/nickle
-
-autoimport Cairo;
-autoload PRNG;
-
-int
-sign(int x)
-{
- return x == 0 ? 0 : x < 0 ? -1 : 1;
-}
-
-int X_AXIS = 0;
-int Y_AXIS = 1;
-
-typedef struct {
- int major;
- int minor;
- int sign_major;
- int sign_minor;
- int e;
- int e1;
- int e3;
- bool first;
-} clip_context;
-
-typedef struct {
- int maj1, min1, maj2, min2;
-} clip_box;
-
-typedef struct {
- int x1, y1, x2, y2;
-} box;
-
-typedef struct {
- int x, y;
-} point;
-
-typedef struct {
- int x1, y1, x2, y2;
- box b;
- point[] clipped;
- point[] run;
-} test;
-
-box bounds = { .x1 = 10, .x2 = 30, .y1 = 10, .y2 = 30 };
-
-int
-div_ceil(a, b) {
- a += b;
- assert(a >= 0 && b > 0, "bad divide args %d %d\n", a, b);
- return (a + b - 1) // b - 1;
-}
-
-int
-div_floor_plus_one(a, b) {
- a += b;
- assert(a >= 0 && b > 0, "bad divide args %d %d\n", a, b);
- return a // b;
-}
-
-bool
-clip(*clip_context c, *clip_box b)
-{
- int adjust_major = 0, adjust_minor = 0;
-
- /* Clip major axis */
- if (c->major < b->maj1) {
- if (c->sign_major <= 0)
- return false;
- adjust_major = b->maj1 - c->major;
- } else if (c->major >= b->maj2) {
- if (c->sign_major >= 0)
- return false;
- adjust_major = c->major - (b->maj2-1);
- }
-
- /* Clip minor axis */
- if (c->minor < b->min1) {
- if (c->sign_minor <= 0)
- return false;
- adjust_minor = b->min1 - c->minor;
- } else if (c->minor >= b->min2) {
- if (c->sign_minor >= 0)
- return false;
- adjust_minor = c->minor - (b->min2-1);
- }
-
- /* If unclipped, we're done */
- if (adjust_major == 0 && adjust_minor == 0)
- return true;
-
- /* See how much minor adjustment would happen during
- * a major clip. This is a bit tricky because line drawing
- * isn't symmetrical when the line passes exactly between
- * two pixels, we have to pick which one gets drawn
- */
- int adj_min;
-
- if (!c->first)
- adj_min = div_ceil(c->e + adjust_major * c->e1, -c->e3);
- else
- adj_min = div_floor_plus_one(c->e + adjust_major * c->e1, -c->e3);
-
- /* Compare that to the minor clip and pick
- * the larger amount.
- */
- printf ("\tinitial major %d minor %d error %d e1 %d e3 %d\n", c->major, c->minor, c->e, c->e1, c->e3);
-
- if (adj_min < adjust_minor) {
- printf("\tminor clip dominates %d < %d. adjust major %d -> ",
- adj_min, adjust_minor, adjust_major);
- if (c->first)
- adjust_major = div_ceil(c->e - adjust_minor * c->e3, c->e1);
- else
- adjust_major = div_floor_plus_one(c->e - adjust_minor * c->e3, c->e1);
- printf("%d\n", adjust_major);
- } else {
- printf("\tminor clip dominates %d > %d. adjust minor %d -> ",
- adj_min, adjust_minor, adjust_minor);
- adjust_minor = adj_min;
- printf("%d\n", adjust_minor);
- }
-
- c->e += adjust_major * c->e1 + adjust_minor * c->e3;
-
- c->major += c->sign_major * adjust_major;
- c->minor += c->sign_minor * adjust_minor;
-
- printf ("\tadjust major %d adjust minor %d e %d e1 %d e3 %e\n",
- adjust_major, adjust_minor, c->e, c->e1, c->e3);
-
- if (c->e >= 0)
- printf ("error positive e %d e1 %d e3 %d\n",
- c->e, c->e1, c->e3);
- if (c->e < c->e3)
- printf ("error magnitude too large e %d e1 %d e3 %d\n", c->e, c->e1, c->e3);
-
- return true;
-}
-
-test
-line(int x1, int y1, int x2, int y2, *box b) {
-
- int dx = x2 - x1;
- int dy = y2 - y1;
- int signdx = sign(dx);
- int signdy = sign(dy);
- int adx = abs(dx);
- int ady = abs(dy);
- int axis;
- int e, e1, e2, e3;
- int len;
- clip_context clip_1, clip_2;
- clip_box c;
- bool clipped = false;
- test t = {
- .x1 = x1,
- .y1 = y1,
- .x2 = x2,
- .y2 = y2,
- .b = *b,
- .clipped = (point[...]) {},
- .run = (point[...]) {}
- };
-
- if (adx >= ady) {
- axis = X_AXIS;
- e1 = ady << 1;
- e2 = e1 - (adx << 1);
- e = e1 - adx;
- len = adx;
-
- clip_1.major = x1;
- clip_1.minor = y1;
- clip_2.major = x2;
- clip_2.minor = y2;
- clip_1.sign_major = signdx;
- clip_1.sign_minor = signdy;
-
- c.maj1 = b->x1;
- c.maj2 = b->x2;
- c.min1 = b->y1;
- c.min2 = b->y2;
- } else {
- axis = Y_AXIS;
- e1 = adx << 1;
- e2 = e1 - (ady << 1);
- e = e1 - ady;
- len = ady;
-
- clip_1.major = y1;
- clip_1.minor = x1;
- clip_2.major = y2;
- clip_2.minor = x2;
- clip_1.sign_major = signdy;
- clip_1.sign_minor = signdx;
- c.maj1 = b->y1;
- c.maj2 = b->y2;
- c.min1 = b->x1;
- c.min2 = b->x2;
- }
-
- e3 = e2 - e1;
- e = e - e1;
-
- clip_1.first = true;
- clip_2.first = false;
- clip_2.e = clip_1.e = e;
- clip_2.e1 = clip_1.e1 = e1;
- clip_2.e3 = clip_1.e3 = e3;
- clip_2.sign_major = -clip_1.sign_major;
- clip_2.sign_minor = -clip_1.sign_minor;
-
- printf ("clip start:\n");
- if (!clip(&clip_1, &c))
- clipped = true;
-
- printf("clip end:\n");
- if (!clip(&clip_2, &c))
- clipped = true;
-
- int clip_len;
- int clip_x, clip_y;
- int clip_e;
- int x_major, x_minor;
- int y_major, y_minor;
-
- clip_len = clip_1.sign_major * (clip_2.major - clip_1.major);
- if (clip_len < 0)
- clipped = true;
-
- int x, y;
-
- if (axis == X_AXIS) {
- x = clip_1.major;
- y = clip_1.minor;
- x_major = clip_1.sign_major;
- x_minor = 0;
- y_major = 0;
- y_minor = clip_1.sign_minor;
- } else {
- x = clip_1.minor;
- y = clip_1.major;
- x_major = 0;
- x_minor = clip_1.sign_minor;
- y_major = clip_1.sign_major;
- y_minor = 0;
- }
-
- clip_e = clip_1.e;
-
- if (clipped)
- clip_len = -1;
-
- while (clip_len-- >= 0) {
- t.clipped[dim(t.clipped)] = (point) { .x = x, .y = y };
- x += x_major;
- y += y_major;
- clip_e += e1;
- if (clip_e >= 0) {
- x += x_minor;
- y += y_minor;
- clip_e += e3;
- }
- }
-
- x = x1;
- y = y1;
-
- while (len-- >= 0) {
- if (bounds.x1 <= x && x < bounds.x2 &&
- bounds.y1 <= y && y < bounds.y2) {
- t.run[dim(t.run)] = (point) { .x = x, .y = y };
- }
- x += x_major;
- y += y_major;
- e += e1;
- if (e >= 0) {
- x += x_minor;
- y += y_minor;
- e += e3;
- }
- }
- return t;
-}
-
-void read_events (Cairo::cairo_t cr)
-{
- file event = Cairo::open_event(cr);
-
- while (!File::end(event)) {
- string event_line = File::fgets(event);
- if (String::index(event_line, "delete") >= 0)
- exit(0);
- }
-}
-
-#for (int y = 0; y < 20; y++)
-
-void
-show(cairo_t cr, test t)
-{
- rectangle(cr, 0, 0, 40, 40);
- set_source_rgba(cr, 1, 1, 1, 1);
- fill(cr);
-
- set_source_rgba(cr, 0, 1, 0, .2);
- set_line_width(cr, 0.1);
- for (int x = 0; x < 40; x++) {
- move_to(cr, 0, x);
- line_to(cr, 40, x);
- move_to(cr, x, 0);
- line_to(cr, x, 40);
- }
- stroke(cr);
-
- rectangle(cr, t.b.x1, t.b.y1, t.b.x2 - t.b.x1, t.b.y2 - t.b.y1);
- set_line_width(cr, 0.1);
- set_source_rgba(cr, 0, 0, 0, 1);
- stroke(cr);
-
- move_to(cr, t.x1+.5, t.y1+.5);
- line_to(cr, t.x2+.5, t.y2+.5);
- move_to(cr, t.x2, t.y2);
- line_to(cr, t.x2+1, t.y2+1);
- move_to(cr, t.x2+1, t.y2);
- line_to(cr, t.x2, t.y2+1);
- stroke(cr);
-
- void pixels(point[] pt) {
- for (int i = 0; i < dim(pt); i++) {
- rectangle(cr, pt[i].x, pt[i].y, 1, 1);
- }
- fill(cr);
- }
-
- set_source_rgba(cr, 1, 0, 0, .5);
- pixels(t.clipped);
-
- set_source_rgba(cr, 0, 0, 1, .5);
- pixels(t.run);
-}
-
-bool
-compare(test t)
-{
- if (dim(t.clipped) != dim(t.run))
- return false;
-
- for (int i = 0; i < dim(t.clipped); i++)
- if (t.clipped[i] != t.run[i])
- return false;
- return true;
-}
-
-void
-doit(int i)
-{
- int n;
- *box b = &bounds;
-
- cairo_t cr = new(800, 800);
-
- scale(cr, 20, 20);
-
- for (;;) {
- PRNG::srandom(i);
- int x1 = PRNG::randint(40);
- int x2 = PRNG::randint(40);
- int y1 = PRNG::randint(40);
- int y2 = PRNG::randint(40);
-
- test t = line (x1, y1, x2, y2, &bounds);
- show(cr, t);
- if (!compare(t)) {
- printf("line %d -- %d x %d - %d x %d\n", i, x1, y1, x2, y2);
- gets();
- }
- i++;
- }
-
- read_events(cr);
-}
-
-int i = 0;
-if (dim(argv) > 1)
- i = atoi(argv[1]);
-
-doit(i);