/* xdraw.c * by Nathan Laredo (laredo@gnu.org) * * If you didn't find a GPL here, it should have been here. * * gcc -Wall -O2 -o xdraw xdraw.c -L/usr/X11R6/lib -lX11 */ #include #include #include #include #include #include #include static int segment[32768]; /* start index of each segment */ static XPoint points[65536]; /* points in each line segment */ static float real_x[65536]; /* real x value */ static float real_y[65536]; /* real y value */ static int numsegments; /* number of line segments */ static int xmin, xmax, ymin, ymax; /* for clipping */ static int reject = 0; /* rejecting point flag */ /* current transform matrix */ static float a = 1.0, b = 0.0, c = 0.0, d = 1.0, u = 0.0, v = 0.0; Display *mydisplay; Window mywindow; GC mygc; XEvent myevent; char tmpbuf[128]; void init_display(int argc, char **argv) { unsigned long myforeground, mybackground; XSizeHints myhint; XGCValues gcvals; int myscreen; if (!getenv("DISPLAY")) { fprintf(stderr, "DISPLAY not set.\n"); exit(1); } if (!(mydisplay = XOpenDisplay(getenv("DISPLAY")))) { fprintf(stderr, "Unable to open display.\n"); exit(1); } myscreen = DefaultScreen(mydisplay); mybackground = WhitePixel(mydisplay, myscreen); myforeground = BlackPixel(mydisplay, myscreen); myhint.width = 612; myhint.height = 792; myhint.x = myhint.y = 0; myhint.max_width = 612; myhint.max_height = 792; myhint.min_width = 612; myhint.min_height = 792; myhint.flags = PSize | PMinSize | PMaxSize | PPosition; mywindow = XCreateSimpleWindow(mydisplay, DefaultRootWindow(mydisplay), myhint.x, myhint.y, myhint.width, myhint.height, 0, 0, mybackground); sprintf(tmpbuf, "xdraw"); XSetStandardProperties(mydisplay, mywindow, tmpbuf, tmpbuf, None, argv, argc, &myhint); gcvals.foreground = myforeground; gcvals.background = mybackground; mygc = XCreateGC(mydisplay, mywindow, GCForeground | GCBackground, &gcvals); XSelectInput(mydisplay, mywindow, ExposureMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask); XMapRaised(mydisplay, mywindow); xmin = ymin = 0; xmax = myhint.width; ymax = myhint.height; } static void transform_point(int index) { /* CTM: * * | a b 0 | * | c d 0 | * | u v 1 | * * In postscript: [ a b c d u v ] * x' = ax + cy + u * y' = bx + dy + v */ float x = real_x[index], y = real_y[index], X, Y; X = a * x + c * y + u; Y = b * x + d * y + v; points[index].x = X; points[index].y = Y; } static void inverse_transform_point(int index) { /* CTM operation: * x' = ax + cy + u * y' = bx + dy + v * * Inverse of CTM operation: * u' = u - x' * v' = v - y' * y = (bu' - av') / (ad - bc) * x = (cv' - u'd) / (ad - bc) */ float X = points[index].x, Y = points[index].y, x, y, U, V; float k = (a * d - b * c); U = u - X; V = v - Y; y = (b * U - a * V) / k; x = (c * V - U * d) / k; real_x[index] = x; real_y[index] = y; } static void print_data(void) { static int page = 0; int i, j, pos = 0; FILE *psfile; if (!(psfile = fopen("/tmp/xd.ps", "a"))) { perror("/tmp/xd.ps"); psfile = stderr; }; page++; fprintf(psfile, "%%!PS\n" "%%%%Page: %d\n" "[ %.3f %.3f %.3f %.3f %.3f %.3f ] pop\n" "1 -1 scale 0 -792 translate\n", page, a, b, c, d, u, v); fprintf(psfile, "/L { lineto } bind def\n" "/M { moveto } bind def\n" "/N { newpath } bind def\n" "/S { stroke } bind def\n"); for (i = 1; i < numsegments; i++) { if (segment[i + 1] <= segment[i]) break; j = segment[i]; pos += fprintf(psfile, "N %.3f %.3f M ", real_x[j], real_y[j]); for (; j < segment[i + 1]; j++) { if (pos > 102) { /* keep lines short */ fprintf(psfile, "\n"); pos = 0; } pos += fprintf(psfile, "%.3f %.3f L ", real_x[j], real_y[j]); } fprintf(psfile, "S\n"); pos = 0; } fprintf(psfile, "showpage\n"); if (psfile != stderr) fclose(psfile); } static void handle_expose_event(void) { int i; for (i = 0; i < numsegments; i++) { if (segment[i + 1] <= segment[i]) break; XDrawLines(mydisplay, mywindow, mygc, &points[segment[i]], segment[i + 1] - segment[i], CoordModeOrigin); } } int xbase = 0, ybase = 0; static void handle_button_press(void) { xbase = myevent.xbutton.x; ybase = myevent.xbutton.y; if (myevent.xbutton.button == Button3 && (myevent.xbutton.state&15)) { print_data(); return; } if (myevent.xbutton.button == Button3) { if (numsegments > 1) numsegments--; XClearArea(mydisplay, mywindow, 0, 0, 0, 0, True); return; } if (myevent.xbutton.button == Button1) { int i = numsegments; int index = segment[numsegments]; points[index].x = myevent.xmotion.x; points[index].y = myevent.xmotion.y; inverse_transform_point(index); /* store real point */ if (real_x[index] > 612.0 || real_y[index] > 792.0 || real_x[index] < 0.0 || real_y[index] < 0.0) { reject++; return; /* reject point */ } reject = 0; numsegments++; segment[numsegments]++; XDrawLines(mydisplay, mywindow, mygc, &points[segment[i]], segment[i + 1] - segment[i], CoordModeOrigin); return; } if (myevent.xbutton.button == Button5 && (myevent.xbutton.state&15)) { int i; float co, si, A, B, C, D; co = cos(M_PI/45); si = sin(M_PI/45); /* Matrix multiply: * | a b 0 | | co si 0 | * | c d 0 | x |-si co 0 | * | u v 1 | | 0 0 1 | */ A = a * co - b * si; B = a * si + b * co; C = c * co - d * si; D = c * si + d * co; a = A; b = B; c = C; d = D; for (i = 0; i < segment[numsegments]; i++) transform_point(i); XClearArea(mydisplay, mywindow, 0, 0, 0, 0, True); return; } if (myevent.xbutton.button == Button4 && (myevent.xbutton.state&15)) { int i; float co, si, A, B, C, D; co = cos(-M_PI/45); si = sin(-M_PI/45); A = a * co - b * si; B = a * si + b * co; C = c * co - d * si; D = c * si + d * co; a = A; b = B; c = C; d = D; for (i = 0; i < segment[numsegments]; i++) transform_point(i); XClearArea(mydisplay, mywindow, 0, 0, 0, 0, True); return; } if (myevent.xbutton.button == Button5) { int i; /* Matrix multiply: * | a b 0 | | s 0 0 | * | c d 0 | x | 0 s 0 | * | u v 1 | | 0 0 1 | */ a *= 1.1; b *= 1.1; c *= 1.1; d *= 1.1; for (i = 0; i < segment[numsegments]; i++) transform_point(i); XClearArea(mydisplay, mywindow, 0, 0, 0, 0, True); return; } if (myevent.xbutton.button == Button4) { int i; a *= 1.0/1.1; b *= 1.0/1.1; c *= 1.0/1.1; d *= 1.0/1.1; for (i = 0; i < segment[numsegments]; i++) transform_point(i); XClearArea(mydisplay, mywindow, 0, 0, 0, 0, True); return; } } static void handle_motion_event(void) { if (myevent.xmotion.state & Button1Mask) { int i = numsegments - 1; int index = segment[numsegments]; points[index].x = myevent.xmotion.x; points[index].y = myevent.xmotion.y; inverse_transform_point(index); if (real_x[index] > 612.0 || real_y[index] > 792.0 || real_x[index] < 0.0 || real_y[index] < 0.0) { segment[numsegments + 1] = segment[numsegments]; reject++; } else { if (reject) { /* back inside from outside */ i++; numsegments++; } reject = 0; segment[numsegments]++; XDrawLines(mydisplay, mywindow, mygc, &points[segment[i]], segment[i + 1] - segment[i], CoordModeOrigin); } } if (myevent.xmotion.state & Button2Mask) { int i; u += myevent.xmotion.x - xbase; v += myevent.xmotion.y - ybase; for (i = 0; i < segment[numsegments]; i++) transform_point(i); XClearArea(mydisplay, mywindow, 0, 0, 0, 0, False); handle_expose_event(); } xbase = myevent.xmotion.x; ybase = myevent.xmotion.y; } static void handle_button_release(void) { segment[numsegments + 1] = segment[numsegments]; reject = 0; } int main(int argc, char **argv) { /* draw bounding box */ real_x[0] = real_x[1] = real_y[0] = real_y[3] = real_x[4] = real_y[4] = -1.0; real_y[1] = real_y[2] = 792.0; real_x[2] = real_x[3] = 612.0; for (numsegments = 0; numsegments < 5; numsegments++) { transform_point(numsegments); segment[numsegments] = 0; } numsegments = 1; segment[1] = segment[2] = 5; /* bounding box is 4 points */ init_display(argc, argv); while (numsegments < 32768 && segment[numsegments] < 65536) { XNextEvent(mydisplay, &myevent); switch (myevent.type) { case Expose: handle_expose_event(); break; case MotionNotify: handle_motion_event(); break; case ButtonPress: handle_button_press(); break; case ButtonRelease: handle_button_release(); break; default: fprintf(stderr, "unexpected event 0x%x\n", myevent.type); } } print_data(); exit(0); }