diff --git a/initcairo.c b/initcairo.c index c7e74ef..cba49bf 100644 --- a/initcairo.c +++ b/initcairo.c @@ -12,8 +12,28 @@ static Bool alwaysTruePredicate(Display *display, XEvent *event, XPointer arg) return true; } -int initCairo(int screen, int mask, int width, int height, const char *name, GraphicsInfo *info) +void updateDimensions(DrawingContext *ctx) { + double det = ctx->matrix.xx * ctx->matrix.yy - ctx->matrix.xy * ctx->matrix.yx; + double cx = (double)ctx->width/2; + double cy = (double)ctx->height/2; + double r = 2*sqrt((cx*cx + cy*cy)/det); // this is not safe anymore if we have non-uniform scaling + + // don't use cairo_device_to_user() since the matrix might not be our CTM yet + cx -= ctx->matrix.x0; + cy -= ctx->matrix.y0; + ctx->center_x = ( ctx->matrix.yy * cx - ctx->matrix.xy * cy) / det; + ctx->center_y = (- ctx->matrix.yx * cx + ctx->matrix.xx * cy) / det; + ctx->radius = r; + ctx->scalefactor = sqrt(det); +} + +GraphicsInfo *initCairo(int screen, int mask, int width, int height, const char *name) +{ + GraphicsInfo *info = malloc(sizeof(GraphicsInfo)); + DrawingContext *ctx = malloc(sizeof(DrawingContext)); + info->context = ctx; + mask |= StructureNotifyMask | ExposureMask | KeyPressMask | ButtonPressMask | PointerMotionMask; info->display = XOpenDisplay(NULL); @@ -34,17 +54,18 @@ int initCairo(int screen, int mask, int width, int height, const char *name, Gra XMapWindow(info->display, info->win); - info->sfc = cairo_xlib_surface_create(info->display, info->win, visual, width, height); - cairo_xlib_surface_set_size(info->sfc, width, height); - info->ctx = cairo_create(info->sfc); - cairo_matrix_init_identity(&info->matrix); - info->scalefactor = 1.0; + info->surface = cairo_xlib_surface_create(info->display, info->win, visual, width, height); + cairo_xlib_surface_set_size(info->surface, width, height); + info->context->cairo = cairo_create(info->surface); + cairo_matrix_init_identity(&info->context->matrix); + info->context->width = width; + info->context->height = height; info->wm_delete_window = XInternAtom(info->display, "WM_DELETE_WINDOW", 0); info->wm_protocols = XInternAtom(info->display, "WM_PROTOCOLS", 0); XSetWMProtocols(info->display, info->win, &info->wm_delete_window, 1); - return 1; + return info; } void destroyCairo(GraphicsInfo *info) @@ -52,6 +73,8 @@ void destroyCairo(GraphicsInfo *info) XDestroyWindow(info->display, info->win); XFreeColormap(info->display, info->cmap); XCloseDisplay(info->display); + free(info->context); + free(info); } void startTimer(GraphicsInfo *info) @@ -80,7 +103,7 @@ void waitUpdateTimer(GraphicsInfo *info) info->frames++; } -int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(cairo_t *, void*), void *data) +int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingContext *)) { int state; static int last_x, last_y; @@ -88,14 +111,15 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(cairo_t *, switch(ev->type) { case Expose: - draw(info->ctx, data); + draw(info->context); break; case ConfigureNotify: printf("ConfigureNotify Event, new dimensions: %d %d %d %d\n", ev->xconfigure.x, ev->xconfigure.y, ev->xconfigure.width, ev->xconfigure.height); - info->width = ev->xconfigure.width; - info->height = ev->xconfigure.height; - cairo_xlib_surface_set_size(info->sfc, info->width, info->height); + info->context->width = ev->xconfigure.width; + info->context->height = ev->xconfigure.height; + cairo_xlib_surface_set_size(info->surface, ev->xconfigure.width, ev->xconfigure.height); + draw(info->context); break; case KeyPress: @@ -111,20 +135,19 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(cairo_t *, cairo_matrix_translate(&transform, ev->xbutton.x, ev->xbutton.y); cairo_matrix_scale(&transform, 5.0/4.0, 5.0/4.0); cairo_matrix_translate(&transform, -ev->xbutton.x, -ev->xbutton.y); - cairo_matrix_multiply(&info->matrix, &info->matrix, &transform); - info->scalefactor *= 5.0/4.0; + cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform); + draw(info->context); } else if(ev->xbutton.button == 5) { cairo_matrix_t transform; cairo_matrix_init_identity(&transform); cairo_matrix_translate(&transform, ev->xbutton.x, ev->xbutton.y); cairo_matrix_scale(&transform, 4.0/5.0, 4.0/5.0); cairo_matrix_translate(&transform, -ev->xbutton.x, -ev->xbutton.y); - cairo_matrix_multiply(&info->matrix, &info->matrix, &transform); - info->scalefactor *= 4.0/5.0; + cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform); + draw(info->context); } last_x = ev->xbutton.x; last_y = ev->xbutton.y; - draw(info->ctx, data); break; case MotionNotify: @@ -135,26 +158,23 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(cairo_t *, cairo_matrix_t transform; cairo_matrix_init_identity(&transform); cairo_matrix_translate(&transform, dx, dy); - cairo_matrix_multiply(&info->matrix, &info->matrix, &transform); - - printf("Button1Motion Event, dx: %d, dy: %d\n", dx, dy); - - draw(info->ctx, data); + cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform); + draw(info->context); } else if(ev->xmotion.state & Button1Mask && ev->xmotion.state & ShiftMask) { + double width = (double) cairo_xlib_surface_get_width(info->surface); + double height = (double) cairo_xlib_surface_get_height(info->surface); + double angle = - atan2((double)ev->xmotion.y - (double)info->height/2, (double)ev->xmotion.x - (double)info->width/2)- - atan2((double)last_y - (double)info->height/2, (double)last_x - (double)info->width/2); + atan2((double)ev->xmotion.y - height/2, (double)ev->xmotion.x - width/2)- + atan2((double)last_y - height/2, (double)last_x - width/2); cairo_matrix_t transform; cairo_matrix_init_identity(&transform); - cairo_matrix_translate(&transform, (double)info->width/2, (double)info->height/2); + cairo_matrix_translate(&transform, width/2, height/2); cairo_matrix_rotate(&transform, angle); - cairo_matrix_translate(&transform, -(double)info->width/2, -(double)info->height/2); - cairo_matrix_multiply(&info->matrix, &info->matrix, &transform); - - printf("Button1Motion Event, angle: %f\n", angle); - - draw(info->ctx, data); + cairo_matrix_translate(&transform, -width/2, -height/2); + cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform); + draw(info->context); } last_x = ev->xmotion.x; last_y = ev->xmotion.y; @@ -172,7 +192,7 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(cairo_t *, } -int checkEvents(GraphicsInfo *info, int (*process)(GraphicsInfo*, XEvent*, void*), void (*draw)(cairo_t *, void*), void *data) // get any events from the queue and the server, process them if neccessary, quit if wanted +int checkEvents(GraphicsInfo *info, int (*process)(GraphicsInfo*, XEvent*), void (*draw)(DrawingContext*)) // get any events from the queue and the server, process them if neccessary, quit if wanted { XEvent ev; @@ -193,10 +213,10 @@ int checkEvents(GraphicsInfo *info, int (*process)(GraphicsInfo*, XEvent*, void* } } */ - if(processStandardEvent(info, &ev, draw, data)) + if(processStandardEvent(info, &ev, draw)) return 1; - if(process(info, &ev, data)) + if(process(info, &ev)) return 1; // quit event queue and application return 0; diff --git a/initcairo.h b/initcairo.h index 9a02aaf..2259707 100644 --- a/initcairo.h +++ b/initcairo.h @@ -13,27 +13,37 @@ #include typedef struct { + cairo_t *cairo; + cairo_matrix_t matrix; + unsigned int width; + unsigned int height; + + // these things will be written by updateDimensions() + double scalefactor; + double center_x; + double center_y; + double radius; +} DrawingContext; + +typedef struct { + DrawingContext *context; Display *display; Window win; Colormap cmap; Atom wm_protocols; Atom wm_delete_window; - cairo_surface_t *sfc; - cairo_t *ctx; + cairo_surface_t *surface; struct timeval start_time; unsigned long frames; double elapsed, frametime; - unsigned int width; - unsigned int height; - cairo_matrix_t matrix; - double scalefactor; } GraphicsInfo; -int initCairo(int screen, int mask, int width, int height, const char *name, GraphicsInfo *info); +GraphicsInfo *initCairo(int screen, int mask, int width, int height, const char *name); void destroyCairo(GraphicsInfo *info); void startTimer(GraphicsInfo *info); void waitUpdateTimer(GraphicsInfo *info); -int checkEvents(GraphicsInfo *info, int (*process)(GraphicsInfo*, XEvent*, void*), void (*draw)(cairo_t *, void*), void *data); +int checkEvents(GraphicsInfo *info, int (*process)(GraphicsInfo*, XEvent*), void (*draw)(DrawingContext *)); +void updateDimensions(DrawingContext *ctx); #endif diff --git a/limit_set.c b/limit_set.c index 12fa631..c32552c 100644 --- a/limit_set.c +++ b/limit_set.c @@ -1,6 +1,8 @@ #include #include +#include #include +#include #include #include "initcairo.h" @@ -14,19 +16,27 @@ typedef struct { double y; } point_t; -int processEvent(GraphicsInfo *info, XEvent *ev, void *data); -void setup(); -void destroy(); void cartanMatrix(gsl_matrix *cartan, double a1, double a2, double a3, double s); void initializeTriangleGenerators(gsl_matrix **gen, gsl_matrix *cartan); +point_t intersect(point_t a, point_t b, point_t c, point_t d); +void drawPoint(DrawingContext *ctx, point_t p); +void drawSegment(DrawingContext *ctx, point_t a, point_t b); +void drawLine(DrawingContext *ctx, point_t a, point_t b); +void drawImplicitLine(DrawingContext *ctx, double a, double b, double c); +void drawPolygon(DrawingContext *ctx, int sides, point_t a, point_t b, point_t c, point_t d); +void drawAttractorConnection(DrawingContext *ctx, const char *word1, const char *word2); +void drawBox(DrawingContext *ctx, const char *word1, const char *word2); +void drawBoxStd(DrawingContext *ctx, const char *word, char base); int compareAngle(const void *x, const void *y); int computeLimitCurve(); -void draw(cairo_t *ctx, void *data); -point_t intersect(point_t a, point_t b, point_t c, point_t d); -void drawBoxStd(cairo_t *ctx, const char *word, char base); -void drawBox(cairo_t *ctx, const char *word1, const char *word2); - -GraphicsInfo G; +void drawBoxes(DrawingContext *ctx); +void drawReflectors(DrawingContext *ctx); +void drawAttractors(DrawingContext *ctx); +void draw(DrawingContext *ctx); +void setup(); +void destroy(); +void print(DrawingContext *screen); +int processEvent(GraphicsInfo *info, XEvent *ev); double parameter; int n_group_elements; @@ -40,97 +50,9 @@ gsl_matrix *cartan, *cob, *coxeter, *coxeter_fixedpoints, *fixedpoints; int show_boxes = 0; int show_attractors = 0; +int show_reflectors = 0; int show_limit = 1; -int processEvent(GraphicsInfo *info, XEvent *ev, void *data) -{ - int state; - unsigned long key; - - switch(ev->type) { - - case KeyPress: - state = ev->xkey.state & (ShiftMask | LockMask | ControlMask); - key = XkbKeycodeToKeysym(ev->xkey.display, ev->xkey.keycode, 0, !!(state & ShiftMask)); - printf("Key pressed: %ld\n", key); - - switch(key) { - case '<': - parameter /= exp(0.002); - limit_curve_valid = computeLimitCurve(); - break; - case '>': - parameter *= exp(0.002); - limit_curve_valid = computeLimitCurve(); - break; - case ' ': - parameter = 2.890053638; - limit_curve_valid = computeLimitCurve(); - break; - case XK_Return: - parameter = 2.76375163; - limit_curve_valid = computeLimitCurve(); - break; - case 'm': - printf("G.matrix.xx = %f;\n", G.matrix.xx); - printf("G.matrix.xy = %f;\n", G.matrix.xy); - printf("G.matrix.x0 = %f;\n", G.matrix.x0); - printf("G.matrix.yx = %f;\n", G.matrix.yx); - printf("G.matrix.yy = %f;\n", G.matrix.yy); - printf("G.matrix.y0 = %f;\n", G.matrix.y0); - printf("G.scalefactor = %f;\n", G.scalefactor); - break; - case 'b': - show_boxes = !show_boxes; - break; - case 'a': - show_attractors = !show_attractors; - break; - case 'l': - show_limit = !show_limit; - } - - draw(G.ctx, 0); - } - - return 0; -} - -void setup() -{ - n_group_elements = 10000; - limit_curve = malloc(3*n_group_elements*sizeof(double)); - group = malloc(n_group_elements*sizeof(groupelement_t)); - ws = workspace_alloc(3); - - LOOP(i) gen[i] = gsl_matrix_alloc(3, 3); - matrices = malloc(n_group_elements*sizeof(gsl_matrix*)); - for(int i = 0; i < n_group_elements; i++) - matrices[i] = gsl_matrix_alloc(3, 3); - cartan = gsl_matrix_alloc(3, 3); - cob = gsl_matrix_alloc(3, 3); - coxeter = gsl_matrix_alloc(3, 3); - coxeter_fixedpoints = gsl_matrix_alloc(3, 3); - fixedpoints = gsl_matrix_alloc(3, 3); -} - -void destroy() -{ - free(limit_curve); - free(group); - workspace_free(ws); - - LOOP(i) gsl_matrix_free(gen[i]); - for(int i = 0; i < n_group_elements; i++) - gsl_matrix_free(matrices[i]); - free(matrices); - gsl_matrix_free(cartan); - gsl_matrix_free(cob); - gsl_matrix_free(coxeter); - gsl_matrix_free(coxeter_fixedpoints); - gsl_matrix_free(fixedpoints); -} - void cartanMatrix(gsl_matrix *cartan, double a1, double a2, double a3, double s) { gsl_matrix_set(cartan, 0, 0, -2); @@ -152,11 +74,6 @@ void initializeTriangleGenerators(gsl_matrix **gen, gsl_matrix *cartan) LOOP(i) LOOP(j) *gsl_matrix_ptr(gen[i], i, j) += gsl_matrix_get(cartan, i, j); } -int compareAngle(const void *x, const void *y) -{ - return ((double*)x)[2] > ((double*)y)[2] ? 1 : -1; -} - // intersect the lines a-b and c-d point_t intersect(point_t a, point_t b, point_t c, point_t d) { @@ -167,31 +84,72 @@ point_t intersect(point_t a, point_t b, point_t c, point_t d) return res; } -void drawPoint(cairo_t *ctx, point_t p) +void drawPoint(DrawingContext *ctx, point_t p) +{ + cairo_t *C = ctx->cairo; + + cairo_save(C); + cairo_move_to(C, p.x, p.y); + cairo_close_path(C); + cairo_set_line_cap(C, CAIRO_LINE_CAP_ROUND); + cairo_set_line_width(C, 10.0/ctx->scalefactor); + cairo_stroke(C); + cairo_restore(C); +} + +void drawSegment(DrawingContext *ctx, point_t a, point_t b) +{ + cairo_t *C = ctx->cairo; + cairo_move_to(C, a.x, a.y); + cairo_line_to(C, b.x, b.y); + cairo_stroke(C); +} + +void drawLine(DrawingContext *ctx, point_t a, point_t b) { // not implemented - cairo_save(ctx); - cairo_move_to(ctx, p.x, p.y); - cairo_close_path(ctx); - cairo_set_line_cap(ctx, CAIRO_LINE_CAP_ROUND); - cairo_set_line_width(ctx, 10.0/G.scalefactor); - cairo_stroke(ctx); - cairo_restore(ctx); + point_t c, xplus, xminus; + double normb; + double scalbc; + + c.x = ctx->center_x; + c.y = ctx->center_y; + b.x -= a.x; + b.y -= a.y; + c.x -= a.x; + c.y -= a.y; + normb = sqrt(b.x*b.x + b.y*b.y); + b.x /= normb; + b.y /= normb; + scalbc = b.x*c.x + b.y*c.y; + xplus.x = a.x + (scalbc + ctx->radius)*b.x; + xplus.y = a.y + (scalbc + ctx->radius)*b.y; + xminus.x = a.x + (scalbc - ctx->radius)*b.x; + xminus.y = a.y + (scalbc - ctx->radius)*b.y; + + drawSegment(ctx, xminus, xplus); } -void drawLine(cairo_t *ctx, point_t a, point_t b) +void drawImplicitLine(DrawingContext *ctx, double a, double b, double c) { // not implemented + double norm, lambda; + point_t m, s, xminus, xplus; + m.x = ctx->center_x; + m.y = ctx->center_y; + lambda = (a*m.x + b*m.y + c)/(a*a + b*b); + s.x = m.x - lambda*a; + s.y = m.y - lambda*b; + norm = sqrt(a*a + b*b); + xminus.x = s.x - ctx->radius * b / norm; + xminus.y = s.y + ctx->radius * a / norm; + xplus.x = s.x + ctx->radius * b / norm; + xplus.y = s.y - ctx->radius * a / norm; + + drawSegment(ctx, xminus, xplus); } -void drawSegment(cairo_t *ctx, point_t a, point_t b) -{ - cairo_move_to(ctx, a.x, a.y); - cairo_line_to(ctx, b.x, b.y); - cairo_stroke(ctx); -} - -void drawPolygon(cairo_t *ctx, int sides, point_t a, point_t b, point_t c, point_t d) // only quadrilaterals so far +void drawPolygon(DrawingContext *ctx, int sides, point_t a, point_t b, point_t c, point_t d) // only quadrilaterals so far { drawSegment(ctx, a, b); drawSegment(ctx, b, c); @@ -217,7 +175,7 @@ void fixedPoints(const char *word, point_t *out) LOOP(i) out[i].y = gsl_matrix_get(ev, 1, i) / gsl_matrix_get(ev, 2, i); } -void drawAttractorConnection(cairo_t *ctx, const char *word1, const char *word2) +void drawAttractorConnection(DrawingContext *ctx, const char *word1, const char *word2) { point_t p1[3], p2[3]; @@ -227,7 +185,7 @@ void drawAttractorConnection(cairo_t *ctx, const char *word1, const char *word2) drawSegment(ctx, p1[0], p2[0]); } -void drawBox(cairo_t *ctx, const char *word1, const char *word2) +void drawBox(DrawingContext *ctx, const char *word1, const char *word2) { point_t p[2][3],i[2]; @@ -241,7 +199,7 @@ void drawBox(cairo_t *ctx, const char *word1, const char *word2) drawPolygon(ctx, 4, p[0][0], i[0], p[1][0], i[1]); } -void drawBoxStd(cairo_t *ctx, const char *word, char base) +void drawBoxStd(DrawingContext *ctx, const char *word, char base) { char word1[100]; char word2[100]; @@ -265,6 +223,11 @@ void drawBoxStd(cairo_t *ctx, const char *word, char base) drawBox(ctx, word1, word2); } +int compareAngle(const void *x, const void *y) +{ + return ((double*)x)[2] > ((double*)y)[2] ? 1 : -1; +} + int computeLimitCurve() { int p = 5, q = 5, r = 5; @@ -314,85 +277,65 @@ int computeLimitCurve() return 1; } -void drawBoxes(cairo_t *ctx) +void drawBoxes(DrawingContext *ctx) { - cairo_set_source_rgb(ctx, 1, 0, 0); - drawBoxStd(ctx, "c", 'C'); - drawBoxStd(ctx, "", 'B'); - drawBoxStd(ctx, "a", 'A'); - drawBoxStd(ctx, "", 'C'); -// drawBoxStd(ctx, "", 'A'); - drawBoxStd(ctx, "b", 'B'); + cairo_t *C = ctx->cairo; - cairo_set_source_rgb(ctx, 0, 0, 1); - drawBoxStd(ctx, "ca", 'A'); - drawBoxStd(ctx, "cac", 'C'); - drawBoxStd(ctx, "caca", 'A'); -// drawBoxStd(ctx, "cacac", 'C'); -// drawBoxStd(ctx, "acac", 'A'); -// drawBoxStd(ctx, "aca", 'C'); + /* + cairo_set_source_rgb(C, 1, 0, 0); + drawBoxStd(ctx, "c", 'C'); + drawBoxStd(ctx, "", 'B'); + drawBoxStd(ctx, "a", 'A'); + drawBoxStd(ctx, "", 'C'); + drawBoxStd(ctx, "b", 'B'); + */ - cairo_set_source_rgb(ctx, 0, 1, 0); - drawBoxStd(ctx, "ac", 'C'); -// drawBoxStd(ctx, "aca", 'A'); - drawBoxStd(ctx, "acac", 'C'); -// drawBoxStd(ctx, "acaca", 'A'); -// drawBoxStd(ctx, "caca", 'C'); -// drawBoxStd(ctx, "cac", 'A'); + cairo_set_source_rgb(C, 0, 0, 0); + drawBoxStd(ctx, "ca", 'A'); + drawBoxStd(ctx, "cac", 'C'); + drawBoxStd(ctx, "caca", 'A'); + drawBoxStd(ctx, "acac", 'C'); + drawBoxStd(ctx, "aca", 'A'); + drawBoxStd(ctx, "ac", 'C'); - cairo_set_source_rgb(ctx, 1, 0, 1); - drawBoxStd(ctx, "aca cb", 'B'); - drawBoxStd(ctx, "aca cbc", 'C'); - drawBoxStd(ctx, "aca cbcb", 'B'); - drawBoxStd(ctx, "aca bcbc", 'C'); - drawBoxStd(ctx, "aca bcb", 'B'); - drawBoxStd(ctx, "aca bc", 'C'); + cairo_set_source_rgb(C, 1, 0, 1); - /* - cairo_set_source_rgb(ctx, 1, 0, 1); - drawAttractorConnection("acb", "acaba"); - drawAttractorConnection("acaba", "acacbca"); - drawAttractorConnection("ac acb ca", "ac acaba ca"); - drawAttractorConnection("ac acaba ca", "ac acacbca ca"); - drawAttractorConnection("acac acb caca", "acac acaba caca"); - drawAttractorConnection("acac acaba caca", "acac acacbca caca"); - drawAttractorConnection("caca acb acac", "caca acaba acac"); -// drawAttractorConnection("caca acaba acac", "caca acacbca acac"); - drawAttractorConnection("ca acb ac", "ca acaba ac"); - drawAttractorConnection("ca acaba ac", "ca acacbca ac"); + /* + drawBoxStd(ctx, "aca cb", 'B'); + drawBoxStd(ctx, "aca cbc", 'C'); + drawBoxStd(ctx, "aca cbcb", 'B'); + drawBoxStd(ctx, "aca bcbc", 'C'); + drawBoxStd(ctx, "aca bcb", 'B'); + drawBoxStd(ctx, "aca bc", 'C'); + */ - cairo_set_source_rgb(ctx, 0, 1, 0); - drawAttractorConnection("cab", "cacbc"); - drawAttractorConnection("cacbc", "cacabac"); - drawAttractorConnection("ca cab ac", "ca cacbc ac"); - drawAttractorConnection("ca cacbc ac", "ca cacabac ac"); - drawAttractorConnection("caca cab acac", "caca cacbc acac"); - drawAttractorConnection("caca cacbc acac", "caca cacabac acac"); - drawAttractorConnection("acac cab caca", "acac cacbc caca"); - drawAttractorConnection("acac cacbc caca", "acac cacabac caca"); -// drawAttractorConnection("ac cab ca", "ac cacbc ca"); - drawAttractorConnection("ac cacbc ca", "ac cacabac ca"); - */ - // finer box test - /* - cairo_set_source_rgb(ctx, 0, 1, 0); - drawBox("acb", "acabacaca"); - drawBox("acabacaca", "acaba"); -// drawBox("acb", "acaba"); - drawBox("acaba", "acacbacac"); - drawBox("acacbacac", "acacbca"); - drawBox("acacbca", "cacacbcac"); - drawBox("cacacbcac", "acacabaca"); - drawBox("acacabaca", "cacabac"); - drawBox("cacabac", "cacabcaca"); - drawBox("cacabcaca", "cacbc"); -// drawBox("cacbc", "cab"); - drawBox("cacbc", "cacbcacac"); - drawBox("cacbcacac", "cab"); // strange - */ + drawBoxStd(ctx, "ca bc", 'C'); + drawBoxStd(ctx, "ca bcb", 'B'); + drawBoxStd(ctx, "ca bcbc", 'C'); + drawBoxStd(ctx, "ca cbcb", 'B'); + drawBoxStd(ctx, "ca cbc", 'C'); + drawBoxStd(ctx, "ca cb", 'B'); } -void drawAttractors(cairo_t *ctx) +void drawReflectors(DrawingContext *ctx) +{ + gsl_matrix *tmp = ws->stack[10]; + gsl_matrix *tmp2 = ws->stack[11]; + point_t p[3]; + + // points + cairo_set_source_rgb(ctx->cairo, 0, 0, 0); + LOOP(i) p[i].x = gsl_matrix_get(cob, 0, i) / gsl_matrix_get(cob, 2, i); + LOOP(i) p[i].y = gsl_matrix_get(cob, 1, i) / gsl_matrix_get(cob, 2, i); + LOOP(i) drawPoint(ctx, p[i]); + + // lines + invert(cob, tmp2, ws); + multiply(cartan, tmp2, tmp); + LOOP(i) drawImplicitLine(ctx, gsl_matrix_get(tmp, i, 0), gsl_matrix_get(tmp, i, 1), gsl_matrix_get(tmp, i, 2)); +} + +void drawAttractors(DrawingContext *ctx) { point_t p[3][3]; @@ -400,30 +343,39 @@ void drawAttractors(cairo_t *ctx) fixedPoints("bca", p[1]); fixedPoints("cab", p[2]); - cairo_set_source_rgb(ctx, 0, 0, 0); + double color[3][3] = {{1,0,0},{0,0.7,0},{0,0,1}}; - LOOP(i) LOOP(j) drawPoint(ctx, p[i][j]); + LOOP(i) LOOP(j) { + cairo_set_source_rgb(ctx->cairo, color[i][0], color[i][1], color[i][2]); + drawPoint(ctx, p[i][j]); + } - LOOP(i) LOOP(j) drawLine(ctx, p[i][j], p[i][(j+1)%3]); + LOOP(i) LOOP(j) { + cairo_set_source_rgb(ctx->cairo, color[i][0], color[i][1], color[i][2]); + drawLine(ctx, p[i][j], p[i][(j+1)%3]); + } } -void draw(cairo_t *ctx, void *data) +void draw(DrawingContext *ctx) { struct timeval current_time; gettimeofday(¤t_time, 0); double start_time = current_time.tv_sec + current_time.tv_usec*1e-6; - cairo_push_group(ctx); - cairo_set_source_rgb(ctx, 1, 1, 1); - cairo_paint(ctx); + cairo_t *C = ctx->cairo; + updateDimensions(ctx); - cairo_set_matrix(ctx, &G.matrix); + cairo_push_group(C); + cairo_set_source_rgb(C, 1, 1, 1); + cairo_paint(C); + + cairo_set_matrix(C, &ctx->matrix); // defaults; use save/restore whenever these are changed - cairo_set_line_width(ctx, 1.0/G.scalefactor); - cairo_set_font_size(ctx, 16); - cairo_set_line_join(ctx, CAIRO_LINE_JOIN_BEVEL); - cairo_set_line_cap(ctx, CAIRO_LINE_CAP_ROUND); + cairo_set_line_width(C, 1.0/ctx->scalefactor); + cairo_set_font_size(C, 16); + cairo_set_line_join(C, CAIRO_LINE_JOIN_BEVEL); + cairo_set_line_cap(C, CAIRO_LINE_CAP_ROUND); if(limit_curve_valid) { @@ -433,13 +385,13 @@ void draw(cairo_t *ctx, void *data) double x = limit_curve[3*i]; double y = limit_curve[3*i+1]; - cairo_user_to_device(ctx, &x, &y); + cairo_user_to_device(C, &x, &y); - if(-x < G.width && x < 3*G.width && -y < G.height && y < 3*G.height) { + if(-x < ctx->width && x < 3*ctx->width && -y < ctx->height && y < 3*ctx->height) { if(!last_inside) { - cairo_move_to(ctx, limit_curve[3*i], limit_curve[3*i+1]); + cairo_move_to(C, limit_curve[3*i], limit_curve[3*i+1]); } else { - cairo_line_to(ctx, limit_curve[3*i], limit_curve[3*i+1]); + cairo_line_to(C, limit_curve[3*i], limit_curve[3*i+1]); } last_inside = 1; } else { @@ -447,8 +399,8 @@ void draw(cairo_t *ctx, void *data) } } - cairo_set_source_rgb(ctx, 0, 0, 0); - cairo_stroke(ctx); + cairo_set_source_rgb(C, 0, 0, 0); + cairo_stroke(C); } if(show_boxes) @@ -456,57 +408,189 @@ void draw(cairo_t *ctx, void *data) if(show_attractors) drawAttractors(ctx); + + if(show_reflectors) + drawReflectors(ctx); } - cairo_identity_matrix(ctx); + cairo_identity_matrix(C); - cairo_move_to(ctx, 15, 30); - cairo_set_source_rgb(ctx, 0, 0, 0); + cairo_move_to(C, 15, 30); + cairo_set_source_rgb(C, 0, 0, 0); char buf[100]; sprintf(buf, "t = %.8f", parameter); - cairo_show_text(ctx, buf); + cairo_show_text(C, buf); - cairo_pop_group_to_source(ctx); - cairo_paint(ctx); - cairo_surface_flush(cairo_get_target(ctx)); + cairo_pop_group_to_source(C); + cairo_paint(C); + cairo_surface_flush(cairo_get_target(C)); gettimeofday(¤t_time, 0); double end_time = current_time.tv_sec + current_time.tv_usec*1e-6; printf("draw() finished in %g seconds\n", end_time - start_time); } +void setup() +{ + n_group_elements = 100000; + limit_curve = malloc(3*n_group_elements*sizeof(double)); + group = malloc(n_group_elements*sizeof(groupelement_t)); + ws = workspace_alloc(3); + + LOOP(i) gen[i] = gsl_matrix_alloc(3, 3); + matrices = malloc(n_group_elements*sizeof(gsl_matrix*)); + for(int i = 0; i < n_group_elements; i++) + matrices[i] = gsl_matrix_alloc(3, 3); + cartan = gsl_matrix_alloc(3, 3); + cob = gsl_matrix_alloc(3, 3); + coxeter = gsl_matrix_alloc(3, 3); + coxeter_fixedpoints = gsl_matrix_alloc(3, 3); + fixedpoints = gsl_matrix_alloc(3, 3); +} + +void destroy() +{ + free(limit_curve); + free(group); + workspace_free(ws); + + LOOP(i) gsl_matrix_free(gen[i]); + for(int i = 0; i < n_group_elements; i++) + gsl_matrix_free(matrices[i]); + free(matrices); + gsl_matrix_free(cartan); + gsl_matrix_free(cob); + gsl_matrix_free(coxeter); + gsl_matrix_free(coxeter_fixedpoints); + gsl_matrix_free(fixedpoints); +} + +void print(DrawingContext *screen) +{ + DrawingContext file; + cairo_surface_t *surface; + + char filename[100]; + time_t t = time(NULL); + strftime(filename, sizeof(filename), "screenshot_%Y%m%d_%H%M%S.pdf", localtime(&t)); + + file.width = screen->width; + file.height = screen->width / sqrt(2.0); + file.matrix = screen->matrix; + file.matrix.y0 += (file.height - screen->height) / 2.0; // recenter vertically + + surface = cairo_pdf_surface_create(filename, (double)file.width, (double)file.height); + file.cairo = cairo_create(surface); + + draw(&file); + + cairo_destroy(file.cairo); + cairo_surface_destroy(surface); + + printf("Wrote sceenshot to file: %s\n", filename); +} + +int processEvent(GraphicsInfo *info, XEvent *ev) +{ + int state; + unsigned long key; + + switch(ev->type) { + + case KeyPress: + state = ev->xkey.state & (ShiftMask | LockMask | ControlMask); + key = XkbKeycodeToKeysym(ev->xkey.display, ev->xkey.keycode, 0, !!(state & ShiftMask)); + printf("Key pressed: %ld\n", key); + + switch(key) { + case '<': + parameter /= exp(0.002); + limit_curve_valid = computeLimitCurve(); + break; + case '>': + parameter *= exp(0.002); + limit_curve_valid = computeLimitCurve(); + break; + case ' ': + parameter = 2.890053638; + limit_curve_valid = computeLimitCurve(); + break; + case XK_Return: + parameter = 2.76375163; + limit_curve_valid = computeLimitCurve(); + break; + case 'm': + printf("info->context->matrix.xx = %f;\n", info->context->matrix.xx); + printf("info->context->matrix.xy = %f;\n", info->context->matrix.xy); + printf("info->context->matrix.x0 = %f;\n", info->context->matrix.x0); + printf("info->context->matrix.yx = %f;\n", info->context->matrix.yx); + printf("info->context->matrix.yy = %f;\n", info->context->matrix.yy); + printf("info->context->matrix.y0 = %f;\n", info->context->matrix.y0); + break; + case 'b': + show_boxes = !show_boxes; + break; + case 'a': + show_attractors = !show_attractors; + break; + case 'r': + show_reflectors = !show_reflectors; + break; + case 'l': + show_limit = !show_limit; + break; + case 'p': + print(info->context); + break; + } + + draw(info->context); + } + + return 0; +} + int main() { + GraphicsInfo *info; + parameter = 3.0; setup(); limit_curve_valid = computeLimitCurve(); - if(!initCairo(0, KeyPressMask, 200, 200, "Triangle group", &G)) + info = initCairo(0, KeyPressMask, 200, 200, "Triangle group"); + if(!info) return 1; + DrawingContext *ctx = info->context; + + + ctx->matrix.xx = 837.930824; + ctx->matrix.xy = -712.651341; + ctx->matrix.x0 = 180.427716; + ctx->matrix.yx = 712.651341; + ctx->matrix.yy = 837.930824; + ctx->matrix.y0 = 1412.553240; + /* - cairo_matrix_init_identity(&G.matrix); - G.matrix.xx = G.matrix.yy = G.scalefactor = 1100.0; - G.matrix.x0 = 1150.0; - G.matrix.y0 = 900.0; + info->context->matrix.xx = 1636.583641; + info->context->matrix.xy = -1391.897150; + info->context->matrix.x0 = 975.772883; + info->context->matrix.yx = 1391.897150; + info->context->matrix.yy = 1636.583641; + info->context->matrix.y0 = 2446.174297; */ - G.matrix.xx = 837.930824; - G.matrix.xy = -712.651341; - G.matrix.x0 = 180.427716; - G.matrix.yx = 712.651341; - G.matrix.yy = 837.930824; - G.matrix.y0 = 1412.553240; - G.scalefactor = 1100.000000; + updateDimensions(ctx); - startTimer(&G); + startTimer(info); - while(!checkEvents(&G, processEvent, draw, 0)) { - waitUpdateTimer(&G); + while(!checkEvents(info, processEvent, draw)) { + waitUpdateTimer(info); } - destroyCairo(&G); + destroyCairo(info); destroy();