diff --git a/Makefile b/Makefile index 033c2c7..4f13fc4 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ -HEADERS=triangle.h linalg.h queue.h initcairo.h +HEADERS=triangle.h linalg.h queue.h initcairo.h main.h -#SPECIAL_OPTIONS=-O0 -g -D_DEBUG +SPECIAL_OPTIONS=-O0 -g -D_DEBUG #SPECIAL_OPTIONS=-O3 -pg -funroll-loops -fno-inline -SPECIAL_OPTIONS=-O3 -flto -funroll-loops -Winline -Wall -Wno-unused-function -Werror=implicit-function-declaration +#SPECIAL_OPTIONS=-O3 -flto -funroll-loops -Winline -Wall -Wno-unused-function -Werror=implicit-function-declaration #SPECIAL_OPTIONS= CAIRO_OPTIONS=$(shell pkg-config --cflags cairo) @@ -11,8 +11,8 @@ OPTIONS=$(GENERAL_OPTIONS) $(CAIRO_OPTIONS) $(SPECIAL_OPTIONS) all: limit_set -limit_set: limit_set.o linalg.o triangle.o initcairo.o - gcc $(OPTIONS) -o limit_set limit_set.o linalg.o triangle.o initcairo.o -lm -lgsl -lcblas -lcairo -lX11 +limit_set: limit_set.o linalg.o triangle.o initcairo.o draw.o main.o + gcc $(OPTIONS) -o limit_set limit_set.o linalg.o triangle.o initcairo.o draw.o main.o -lm -lgsl -lcblas -lcairo -lX11 linalg.o: linalg.c $(HEADERS) gcc $(OPTIONS) -c linalg.c @@ -26,5 +26,11 @@ limit_set.o: limit_set.c $(HEADERS) initcairo.o: initcairo.c $(HEADERS) gcc $(OPTIONS) -c initcairo.c +draw.o: draw.c $(HEADERS) + gcc $(OPTIONS) -c draw.c + +main.o: main.c $(HEADERS) + gcc $(OPTIONS) -c main.c + clean: - rm -f limit_set linalg.o triangle.o limit_set.o + rm -f limit_set linalg.o triangle.o limit_set.o draw.o main.o diff --git a/draw.c b/draw.c new file mode 100644 index 0000000..bed3dbd --- /dev/null +++ b/draw.c @@ -0,0 +1,407 @@ +#include "main.h" + +// level 0: helper functions + +vector_t cross(vector_t a, vector_t b) +{ + vector_t result; + result.x[0] = a.x[1]*b.x[2] - a.x[2]*b.x[1]; + result.x[1] = a.x[2]*b.x[0] - a.x[0]*b.x[2]; + result.x[2] = a.x[0]*b.x[1] - a.x[1]*b.x[0]; + return result; +} + +vector_t apply(DrawingContext *ctx, gsl_matrix *m, vector_t x) +{ + gsl_vector *tmp = getTempVector(ctx); + gsl_vector *tmp2 = getTempVector(ctx); + vector_t out; + + LOOP(i) gsl_vector_set(tmp, i, x.x[i]); + gsl_blas_dgemv(CblasNoTrans, 1.0, m, tmp, 0.0, tmp2); + LOOP(i) out.x[i] = gsl_vector_get(tmp2, i); + + releaseTempVectors(ctx, 2); +} + +int fixedPoints(DrawingContext *ctx, const char *word, vector_t *out) +{ + gsl_matrix *tmp = getTempMatrix(ctx); + gsl_matrix *ev = getTempMatrix(ctx); + gsl_matrix **gen = getTempMatrices(ctx, 3); + + initializeTriangleGenerators(gen, ctx->cartan); + + gsl_matrix_set_identity(tmp); + for(int i = 0; i < strlen(word); i++) { + if(word[i] == ' ') + continue; + multiply_right(tmp, gen[word[i]-'a'], ctx->ws); + } + int count = real_eigenvectors(tmp, ev, ctx->ws); + + LOOP(i) LOOP(j) out[i].x[j] = gsl_matrix_get(ev, j, i); + + releaseTempMatrices(ctx, 5); + + return count; +} + +void transformFrameStd(DrawingContext *ctx, vector_t *x, gsl_matrix *out) +{ + gsl_matrix *tmp = getTempMatrix(ctx); + gsl_vector *fourth = getTempVector(ctx); + gsl_vector *lambda = getTempVector(ctx); + int s; + + LOOP(i) LOOP(j) gsl_matrix_set(out, j, i, x[i].x[j]); + gsl_matrix_memcpy(tmp, out); + gsl_linalg_LU_decomp(tmp, ctx->ws->permutation, &s); + gsl_linalg_LU_solve(tmp, ctx->ws->permutation, fourth, lambda); + + LOOP(i) LOOP(j) *gsl_matrix_ptr(out, i, j) *= gsl_vector_get(lambda, j); + + gsl_matrix_fprintf(stdout, out, "%f"); + + releaseTempMatrices(ctx, 1); + releaseTempVectors(ctx, 2); +} + +// level 1: the elementary drawing functions, drawPoint, drawSegment2d + +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->dim->scalefactor); + cairo_stroke(C); + cairo_restore(C); +} + +void drawSegment2d(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); +} + +// level 2: drawVector, drawCovector, drawSegment + +static point_t vectorToPoint(DrawingContext *ctx, vector_t in) +{ + double x[3]; + point_t out; + + LOOP(i) x[i] = 0.0; + LOOP(i) LOOP(j) x[i] += gsl_matrix_get(ctx->cob, i, j) * in.x[j]; + + out.x = x[0] / x[2]; + out.y = x[1] / x[2]; + + return out; +} + +void drawVector(DrawingContext *ctx, vector_t v) +{ + drawPoint(ctx, vectorToPoint(ctx, v)); +} + +static void drawImplicitLine(DrawingContext *ctx, double a, double b, double c) +{ + double norm, lambda; + point_t m, s, xminus, xplus; + m.x = ctx->dim->center_x; + m.y = ctx->dim->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->dim->radius * b / norm; + xminus.y = s.y + ctx->dim->radius * a / norm; + xplus.x = s.x + ctx->dim->radius * b / norm; + xplus.y = s.y - ctx->dim->radius * a / norm; + + drawSegment2d(ctx, xminus, xplus); +} + +void drawCovector(DrawingContext *ctx, vector_t v) +{ + double x[3]; + double cofactor; + + LOOP(i) x[i] = 0.0; + LOOP(i) LOOP(j) { + cofactor = gsl_matrix_get(ctx->cob, (i+1)%3, (j+1)%3) * gsl_matrix_get(ctx->cob, (i+2)%3, (j+2)%3) + - gsl_matrix_get(ctx->cob, (i+1)%3, (j+2)%3) * gsl_matrix_get(ctx->cob, (i+2)%3, (j+1)%3); + x[i] += cofactor * v.x[j]; + } + + drawImplicitLine(ctx, x[0], x[1], x[2]); +} + +void drawSegment(DrawingContext *ctx, vector_t a, vector_t b) +{ + drawSegment2d(ctx, vectorToPoint(ctx, a), vectorToPoint(ctx, b)); +} + +// level 3: boxes and polygons + +void drawPolygon(DrawingContext *ctx, int sides, ...) +{ + va_list args; + vector_t first, prev, current; + + va_start(args, sides); + + first = va_arg(args, vector_t); + current = first; + for(int i = 0; i < sides-1; i++) { + prev = current; + current = va_arg(args, vector_t); + drawSegment(ctx, prev, current); + } + drawSegment(ctx, current, first); + + va_end(args); +} + +void drawTriangle(DrawingContext *ctx, const char *word) +{ + vector_t p[3]; + + fixedPoints(ctx, word, p); + drawPolygon(ctx, 3, p[0], p[1], p[2]); +} + +void drawBox(DrawingContext *ctx, const char *word1, const char *word2) +{ + vector_t p[2][3],i[2]; + + fixedPoints(ctx, word1, p[0]); + fixedPoints(ctx, word2, p[1]); + + // intersect attracting line with neutral line of the other element + for(int j = 0; j < 2; j++) + i[j] = cross(cross(p[j%2][0],p[j%2][1]),cross(p[(j+1)%2][0],p[(j+1)%2][2])); + + drawPolygon(ctx, 4, p[0][0], i[0], p[1][0], i[1]); +} + +void drawBoxStd(DrawingContext *ctx, const char *word, char base) +{ + char word1[100]; + char word2[100]; + + int len = strlen(word); + if(len*2 + 4 > 100) + return; + + for(int i = 0; i < len; i++) { + word1[i] = word1[2*len+2-i] = word[i]; + word2[i] = word2[2*len+2-i] = word[i]; + } + word1[2*len+3] = 0; + word2[2*len+3] = 0; + + LOOP(i) word1[len+i] = (base-'A'+6+i+1)%3+'a'; + LOOP(i) word2[len+i] = (base-'A'+6-i-1)%3+'a'; + +// printf("Words: %s %s\n", word1, word2); + + drawBox(ctx, word1, word2); +} + +// level 4: draw the actual image components + +void drawReflectors(DrawingContext *ctx) +{ + vector_t v[3]; + + cairo_set_source_rgb(ctx->cairo, 0, 0, 0); + + LOOP(i) LOOP(j) { v[i].x[j] = (i==j) ? 1.0 : 0.0; } + LOOP(i) drawVector(ctx, v[i]); + + LOOP(i) LOOP(j) v[i].x[j] = gsl_matrix_get(ctx->cartan, i, j); + LOOP(i) drawCovector(ctx, v[i]); +} + +void drawAttractors(DrawingContext *ctx) +{ + vector_t p[3][3]; + vector_t l[3][3]; + + fixedPoints(ctx, "abc", p[0]); + fixedPoints(ctx, "bca", p[1]); + fixedPoints(ctx, "cab", p[2]); + + double color[3][3] = {{1,0,0},{0,0.7,0},{0,0,1}}; + + LOOP(i) LOOP(j) l[i][j] = cross(p[i][(3-j)%3], p[i][(4-j)%3]); + + LOOP(i) LOOP(j) { + cairo_set_source_rgb(ctx->cairo, color[i][0], color[i][1], color[i][2]); + drawVector(ctx, p[i][j]); + } + + LOOP(i) LOOP(j) { + cairo_set_source_rgb(ctx->cairo, color[i][0], color[i][1], color[i][2]); + drawCovector(ctx, l[i][j]); + } +} + +void drawBoxes(DrawingContext *ctx) +{ + cairo_t *C = ctx->cairo; + + /* + cairo_set_source_rgb(C, 1, 0, 0); + drawTriangle(ctx, "abc"); + cairo_set_source_rgb(C, 0, 0, 1); + drawTriangle(ctx, "aca abc aca"); + drawTriangle(ctx, "acac abc caca"); + drawTriangle(ctx, "acaca abc acaca"); + cairo_set_source_rgb(C, 0, 0.8, 0); + drawTriangle(ctx, "cac abc cac"); + drawTriangle(ctx, "caca abc acac"); + drawTriangle(ctx, "cacac abc cacac"); + */ + + 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(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'); + + 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'); + + drawBoxStd(ctx, "caca cb", 'B'); + drawBoxStd(ctx, "caca cbc", 'C'); + drawBoxStd(ctx, "caca cbcb", 'B'); + drawBoxStd(ctx, "caca bcbc", 'C'); + drawBoxStd(ctx, "caca bcb", 'B'); + drawBoxStd(ctx, "caca bc", 'C'); + + cairo_set_source_rgb(C, 1, 0, 1); + 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'); + + cairo_set_source_rgb(C, 0, 1, 0); +// drawBoxStd(ctx, "ca bc", 'C'); + drawBoxStd(ctx, "cabc ba", 'A'); + drawBoxStd(ctx, "cabc bab", 'B'); + drawBoxStd(ctx, "cabc baba", 'A'); + drawBoxStd(ctx, "cabc abab", 'B'); + drawBoxStd(ctx, "cabc aba", 'A'); + drawBoxStd(ctx, "cabc ab", 'B'); +} + +void drawLimitCurve(DrawingContext *ctx) +{ + cairo_t *C = ctx->cairo; + + cairo_save(C); + + int previous_inside = 0; + for(int i = 0; i < ctx->n_group_elements; i++) { + double x = ctx->limit_curve[3*i]; + double y = ctx->limit_curve[3*i+1]; + + cairo_user_to_device(C, &x, &y); + + if(-x < ctx->dim->width && x < 3*ctx->dim->width && -y < ctx->dim->height && y < 3*ctx->dim->height) { + if(ctx->limit_with_lines) { + if(!previous_inside) + cairo_move_to(C, ctx->limit_curve[3*i], ctx->limit_curve[3*i+1]); + else + cairo_line_to(C, ctx->limit_curve[3*i], ctx->limit_curve[3*i+1]); + } else { + cairo_move_to(C, ctx->limit_curve[3*i], ctx->limit_curve[3*i+1]); + cairo_close_path(C); + } + previous_inside = 1; + } else { + previous_inside = 0; + } + } + + if(!ctx->limit_with_lines) { // draw dots instead of lines + cairo_set_line_cap(C, CAIRO_LINE_CAP_ROUND); + cairo_set_line_width(C, 3.0/ctx->dim->scalefactor); + } + + cairo_set_source_rgb(C, 0, 0, 0); + cairo_stroke(C); + + cairo_restore(C); +} + +void drawText(DrawingContext *ctx) +{ + cairo_move_to(ctx->cairo, 15, 30); + cairo_set_source_rgb(ctx->cairo, 0, 0, 0); + char buf[100]; + sprintf(buf, "t = exp(%.8f) = %.8f", log(ctx->parameter), ctx->parameter); + cairo_show_text(ctx->cairo, buf); +} + +// level 5: put everything together + +void draw(DrawingContext *ctx) +{ + cairo_t *C = ctx->cairo; + + cairo_set_source_rgb(C, 1, 1, 1); + cairo_paint(C); + + cairo_set_matrix(C, &ctx->dim->matrix); + + // defaults; use save/restore whenever these are changed + cairo_set_line_width(C, 1.0/ctx->dim->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(ctx->limit_curve_valid) { + if(ctx->show_limit) + drawLimitCurve(ctx); + + if(ctx->show_boxes) + drawBoxes(ctx); + + if(ctx->show_attractors) + drawAttractors(ctx); + + if(ctx->show_reflectors) + drawReflectors(ctx); + } + + cairo_identity_matrix(C); // text is in screen coordinates + + drawText(ctx); + + cairo_surface_flush(cairo_get_target(C)); +} diff --git a/initcairo.c b/initcairo.c index bab7b4f..c16bba2 100644 --- a/initcairo.c +++ b/initcairo.c @@ -12,28 +12,11 @@ static Bool alwaysTruePredicate(Display *display, XEvent *event, XPointer arg) return true; } -// this computes center, radius and scalefactor out of ctx->matrix -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; + DimensionsInfo *dim = malloc(sizeof(DimensionsInfo)); + info->dim = dim; mask |= StructureNotifyMask | ExposureMask | KeyPressMask | ButtonPressMask | PointerMotionMask; @@ -65,10 +48,11 @@ GraphicsInfo *initCairo(int screen, int mask, int width, int height, const char int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); info->buffer = malloc(stride*height); info->buffer_surface = cairo_image_surface_create_for_data(info->buffer, CAIRO_FORMAT_ARGB32, width, height, stride); - info->context->cairo = cairo_create(info->buffer_surface); - cairo_matrix_init_identity(&info->context->matrix); - info->context->width = width; - info->context->height = height; + info->buffer_context = cairo_create(info->buffer_surface); + cairo_matrix_init_identity(&info->dim->matrix); + info->dim->width = width; + info->dim->height = height; + updateDimensions(info->dim); info->wm_delete_window = XInternAtom(info->display, "WM_DELETE_WINDOW", 0); info->wm_protocols = XInternAtom(info->display, "WM_PROTOCOLS", 0); @@ -81,7 +65,7 @@ GraphicsInfo *initCairo(int screen, int mask, int width, int height, const char void destroyCairo(GraphicsInfo *info) { - cairo_destroy(info->context->cairo); + cairo_destroy(info->buffer_context); cairo_destroy(info->front_context); cairo_surface_destroy(info->surface); cairo_surface_destroy(info->buffer_surface); @@ -89,7 +73,7 @@ void destroyCairo(GraphicsInfo *info) XDestroyWindow(info->display, info->win); XFreeColormap(info->display, info->cmap); XCloseDisplay(info->display); - free(info->context); + free(info->dim); free(info); } @@ -119,7 +103,7 @@ void waitUpdateTimer(GraphicsInfo *info) info->frames++; } -int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingContext *)) +int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(void *)) { int state; static int last_x, last_y; @@ -133,17 +117,18 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon 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->context->width = ev->xconfigure.width; - info->context->height = ev->xconfigure.height; - cairo_xlib_surface_set_size(info->surface, info->context->width, info->context->height); + info->dim->width = ev->xconfigure.width; + info->dim->height = ev->xconfigure.height; + updateDimensions(info->dim); + cairo_xlib_surface_set_size(info->surface, info->dim->width, info->dim->height); - cairo_destroy(info->context->cairo); + cairo_destroy(info->buffer_context); cairo_surface_destroy(info->buffer_surface); free(info->buffer); - stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, info->context->width); - info->buffer = malloc(stride*info->context->height); - info->buffer_surface = cairo_image_surface_create_for_data(info->buffer, CAIRO_FORMAT_ARGB32, info->context->width, info->context->height, stride); - info->context->cairo = cairo_create(info->buffer_surface); + stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, info->dim->width); + info->buffer = malloc(stride*info->dim->height); + info->buffer_surface = cairo_image_surface_create_for_data(info->buffer, CAIRO_FORMAT_ARGB32, info->dim->width, info->dim->height, stride); + info->buffer_context = cairo_create(info->buffer_surface); return STATUS_REDRAW; @@ -160,7 +145,7 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon 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->context->matrix, &info->context->matrix, &transform); + cairo_matrix_multiply(&info->dim->matrix, &info->dim->matrix, &transform); status = STATUS_REDRAW; } else if(ev->xbutton.button == 5) { cairo_matrix_t transform; @@ -168,12 +153,13 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon 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->context->matrix, &info->context->matrix, &transform); + cairo_matrix_multiply(&info->dim->matrix, &info->dim->matrix, &transform); status = STATUS_REDRAW; } last_x = ev->xbutton.x; last_y = ev->xbutton.y; + updateDimensions(info->dim); return status; case MotionNotify: @@ -184,7 +170,7 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon cairo_matrix_t transform; cairo_matrix_init_identity(&transform); cairo_matrix_translate(&transform, dx, dy); - cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform); + cairo_matrix_multiply(&info->dim->matrix, &info->dim->matrix, &transform); status = STATUS_REDRAW; } else if(ev->xmotion.state & Button1Mask && ev->xmotion.state & ShiftMask) { double width = (double) cairo_xlib_surface_get_width(info->surface); @@ -199,7 +185,7 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon cairo_matrix_translate(&transform, width/2, height/2); cairo_matrix_rotate(&transform, angle); cairo_matrix_translate(&transform, -width/2, -height/2); - cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform); + cairo_matrix_multiply(&info->dim->matrix, &info->dim->matrix, &transform); status = STATUS_REDRAW; } @@ -219,7 +205,7 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon return STATUS_NOTHING; } -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 +int checkEvents(GraphicsInfo *info, int (*process)(GraphicsInfo*, XEvent*), void (*draw)(void *)) // get any events from the queue and the server, process them if neccessary, quit if wanted { XEvent ev; int x11_fd; @@ -264,3 +250,20 @@ int checkEvents(GraphicsInfo *info, int (*process)(GraphicsInfo*, XEvent*), void return status; } + +// this computes center, radius and scalefactor out of ctx->matrix +void updateDimensions(DimensionsInfo *dim) +{ + double det = dim->matrix.xx * dim->matrix.yy - dim->matrix.xy * dim->matrix.yx; + double cx = (double)dim->width/2; + double cy = (double)dim->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 -= dim->matrix.x0; + cy -= dim->matrix.y0; + dim->center_x = ( dim->matrix.yy * cx - dim->matrix.xy * cy) / det; + dim->center_y = (- dim->matrix.yx * cx + dim->matrix.xx * cy) / det; + dim->radius = r; + dim->scalefactor = sqrt(det); +} diff --git a/initcairo.h b/initcairo.h index 83514ec..7fc695c 100644 --- a/initcairo.h +++ b/initcairo.h @@ -17,7 +17,6 @@ #define STATUS_QUIT 2 typedef struct { - cairo_t *cairo; cairo_matrix_t matrix; unsigned int width; unsigned int height; @@ -27,10 +26,9 @@ typedef struct { double center_x; double center_y; double radius; -} DrawingContext; +} DimensionsInfo; typedef struct { - DrawingContext *context; Display *display; Window win; Colormap cmap; @@ -40,11 +38,13 @@ typedef struct { cairo_t *front_context; cairo_surface_t *buffer_surface; + cairo_t *buffer_context; unsigned char *buffer; struct timeval start_time; unsigned long frames; double elapsed, frametime; + DimensionsInfo *dim; } GraphicsInfo; GraphicsInfo *initCairo(int screen, int mask, int width, int height, const char *name); @@ -52,7 +52,7 @@ void destroyCairo(GraphicsInfo *info); void startTimer(GraphicsInfo *info); void waitUpdateTimer(GraphicsInfo *info); -int checkEvents(GraphicsInfo *info, int (*process)(GraphicsInfo*, XEvent*), void (*draw)(DrawingContext *)); -void updateDimensions(DrawingContext *ctx); +int checkEvents(GraphicsInfo *info, int (*process)(GraphicsInfo*, XEvent*), void (*draw)(void *)); +void updateDimensions(DimensionsInfo *ctx); #endif diff --git a/limit_set.c b/limit_set.c index fd5facb..03770f5 100644 --- a/limit_set.c +++ b/limit_set.c @@ -1,59 +1,9 @@ -#include -#include -#include -#include -#include -#include +#include "main.h" -#include "initcairo.h" -#include "triangle.h" -#include "linalg.h" - -#define LOOP(i) for(int i = 0; i < 3; i++) - -typedef struct { - double x; - double y; -} point_t; - -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, ...); -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 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; -double *limit_curve; // x, y, angle triples -int limit_curve_valid = 0; -groupelement_t *group; -workspace_t *ws; -gsl_matrix *gen[3]; -gsl_matrix **matrices; -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 limit_with_lines = 1; -int use_repelling = 1; +static int compareAngle(const void *x, const void *y) +{ + return ((double*)x)[2] > ((double*)y)[2] ? 1 : -1; +} void cartanMatrix(gsl_matrix *cartan, double a1, double a2, double a3, double s) { @@ -76,622 +26,73 @@ 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); } -// intersect the lines a-b and c-d -point_t intersect(point_t a, point_t b, point_t c, point_t d) +int computeLimitCurve(DrawingContext *ctx) { - point_t res; - double t = ((c.y-d.y)*(c.x-a.x) - (c.x-d.x)*(c.y-a.y)) / ((b.x-a.x)*(c.y-d.y) - (b.y-a.y)*(c.x-d.x)); - res.x = (1-t)*a.x + t*b.x; - res.y = (1-t)*a.y + t*b.y; - return res; -} + workspace_t *ws = ctx->ws; + gsl_matrix *cartan_pos = getTempMatrix(ctx); + gsl_matrix *cob_pos = getTempMatrix(ctx); + gsl_matrix *coxeter_pos = getTempMatrix(ctx); + gsl_matrix *coxeter_fixedpoints_pos = getTempMatrix(ctx); + gsl_matrix *fixedpoints_pos = getTempMatrix(ctx); + gsl_matrix *coxeter = getTempMatrix(ctx); + gsl_matrix *coxeter_fixedpoints = getTempMatrix(ctx); + gsl_matrix *fixedpoints = getTempMatrix(ctx); + gsl_matrix **gen = getTempMatrices(ctx, 3); + gsl_matrix **elements = getTempMatrices(ctx, ctx->n_group_elements); + groupelement_t *group = ctx->group; + int success = 0; -void drawPoint(DrawingContext *ctx, point_t p) -{ - cairo_t *C = ctx->cairo; + int column = ctx->use_repelling ? 2 : 0; + ctx->limit_curve_valid = 0; - 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); -} + // do first in the Fuchsian positive case to get the angles + cartanMatrix(cartan_pos, M_PI/ctx->p[0], M_PI/ctx->p[1], M_PI/ctx->p[2], 1.0); + initializeTriangleGenerators(gen, cartan_pos); + gsl_matrix_set_identity(elements[0]); + for(int i = 1; i < ctx->n_group_elements; i++) + multiply(elements[group[i].parent->id], gen[group[i].letter], elements[i]); + diagonalize_symmetric_form(cartan_pos, cob_pos, ws); + multiply_many(ws, coxeter_pos, 3, gen[0], gen[1], gen[2]); + int ev_count_pos = real_eigenvectors(coxeter_pos, coxeter_fixedpoints_pos, ws); -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); -} + if(ev_count_pos != 3) + goto error_out; -void drawLine(DrawingContext *ctx, point_t a, point_t b) -{ - // not implemented - 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 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 drawPolygon(DrawingContext *ctx, int sides, ...) -{ - va_list args; - point_t first, prev, current; - - va_start(args, sides); - - first = va_arg(args, point_t); - current = first; - for(int i = 0; i < sides-1; i++) { - prev = current; - current = va_arg(args, point_t); - drawSegment(ctx, prev, current); - } - drawSegment(ctx, current, first); - - va_end(args); -} - -int fixedPoints(const char *word, point_t *out) -{ - gsl_matrix *tmp = ws->stack[10]; - gsl_matrix *ev = ws->stack[11]; - - gsl_matrix_set_identity(tmp); - for(int i = 0; i < strlen(word); i++) { - if(word[i] == ' ') - continue; - multiply_right(tmp, gen[word[i]-'a'], ws); - } - int count = real_eigenvectors(tmp, ev, ws); - multiply_left(cob, ev, ws); - - LOOP(i) out[i].x = gsl_matrix_get(ev, 0, i) / gsl_matrix_get(ev, 2, i); - LOOP(i) out[i].y = gsl_matrix_get(ev, 1, i) / gsl_matrix_get(ev, 2, i); - - return count; -} - -void drawAttractorConnection(DrawingContext *ctx, const char *word1, const char *word2) -{ - point_t p1[3], p2[3]; - - fixedPoints(word1, p1); - fixedPoints(word2, p2); - - drawSegment(ctx, p1[0], p2[0]); -} - -void drawTriangle(DrawingContext *ctx, const char *word) -{ - point_t p[3]; - - fixedPoints(word, p); - drawPolygon(ctx, 3, p[0], p[1], p[2]); -} - -void drawBox(DrawingContext *ctx, const char *word1, const char *word2) -{ - point_t p[2][3],i[2]; - - fixedPoints(word1, p[0]); - fixedPoints(word2, p[1]); - - // intersect attracting line with neutral line of the other element - for(int j = 0; j < 2; j++) - i[j] = intersect(p[j%2][0],p[j%2][1],p[(j+1)%2][0],p[(j+1)%2][2]); - - drawPolygon(ctx, 4, p[0][0], i[0], p[1][0], i[1]); -} - -void drawBoxStd(DrawingContext *ctx, const char *word, char base) -{ - char word1[100]; - char word2[100]; - - int len = strlen(word); - if(len*2 + 4 > 100) - return; - - for(int i = 0; i < len; i++) { - word1[i] = word1[2*len+2-i] = word[i]; - word2[i] = word2[2*len+2-i] = word[i]; - } - word1[2*len+3] = 0; - word2[2*len+3] = 0; - - LOOP(i) word1[len+i] = (base-'A'+6+i+1)%3+'a'; - LOOP(i) word2[len+i] = (base-'A'+6-i-1)%3+'a'; - -// printf("Words: %s %s\n", word1, word2); - - 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; - int k = 2, l = 2, m = 2; - - int column = use_repelling ? 2 : 0; - - generate_triangle_group(group, n_group_elements, p, q, r); - - // do first in the Fuchsian case to get the angles - cartanMatrix(cartan, M_PI/p, M_PI/q, M_PI/r, 1.0); - initializeTriangleGenerators(gen, cartan); - gsl_matrix_set_identity(matrices[0]); - for(int i = 1; i < n_group_elements; i++) - multiply(matrices[group[i].parent->id], gen[group[i].letter], matrices[i]); - diagonalize_symmetric_form(cartan, cob, ws); - multiply_many(ws, coxeter, 3, gen[0], gen[1], gen[2]); - int ev_count_fuchsian = real_eigenvectors(coxeter, coxeter_fixedpoints, ws); - - if(ev_count_fuchsian != 3) - return 0; - - for(int i = 0; i < n_group_elements; i++) { - multiply_many(ws, fixedpoints, 3, cob, matrices[i], coxeter_fixedpoints); - limit_curve[3*i+2] = atan2( - gsl_matrix_get(fixedpoints, 0, column)/gsl_matrix_get(fixedpoints, 2, column), - gsl_matrix_get(fixedpoints, 1, column)/gsl_matrix_get(fixedpoints, 2, column)); + for(int i = 0; i < ctx->n_group_elements; i++) { + multiply_many(ws, fixedpoints_pos, 3, cob_pos, elements[i], coxeter_fixedpoints_pos); + ctx->limit_curve[3*i+2] = atan2( + gsl_matrix_get(fixedpoints_pos, 0, column)/gsl_matrix_get(fixedpoints_pos, 2, column), + gsl_matrix_get(fixedpoints_pos, 1, column)/gsl_matrix_get(fixedpoints_pos, 2, column)); } - // now to it again to calculate x and y coordinates - cartanMatrix(cartan, M_PI*k/p, M_PI*l/q, M_PI*m/r, parameter); - initializeTriangleGenerators(gen, cartan); - gsl_matrix_set_identity(matrices[0]); - for(int i = 1; i < n_group_elements; i++) - multiply(matrices[group[i].parent->id], gen[group[i].letter], matrices[i]); - gsl_matrix_memcpy(cob, cartan); // is this a good choice of basis -// gsl_matrix_set_identity(cob); + // now do it again to calculate x and y coordinates + initializeTriangleGenerators(gen, ctx->cartan); + gsl_matrix_set_identity(elements[0]); + for(int i = 1; i < ctx->n_group_elements; i++) + multiply(elements[group[i].parent->id], gen[group[i].letter], elements[i]); multiply_many(ws, coxeter, 3, gen[0], gen[1], gen[2]); int ev_count = real_eigenvectors(coxeter, coxeter_fixedpoints, ws); if(ev_count == 1) column = 0; if(ev_count == 0) - return 0; + goto error_out; - for(int i = 0; i < n_group_elements; i++) { - multiply_many(ws, fixedpoints, 3, cob, matrices[i], coxeter_fixedpoints); + for(int i = 0; i < ctx->n_group_elements; i++) { + multiply_many(ws, fixedpoints, 3, ctx->cob, elements[i], coxeter_fixedpoints); - limit_curve[3*i] = gsl_matrix_get(fixedpoints, 0, column)/gsl_matrix_get(fixedpoints, 2, column); - limit_curve[3*i+1] = gsl_matrix_get(fixedpoints, 1, column)/gsl_matrix_get(fixedpoints, 2, column); + ctx->limit_curve[3*i ] = gsl_matrix_get(fixedpoints, 0, column)/gsl_matrix_get(fixedpoints, 2, column); + ctx->limit_curve[3*i+1] = gsl_matrix_get(fixedpoints, 1, column)/gsl_matrix_get(fixedpoints, 2, column); } - qsort(limit_curve, n_group_elements, 3*sizeof(double), compareAngle); + qsort(ctx->limit_curve, ctx->n_group_elements, 3*sizeof(double), compareAngle); - return 1; -} - -void drawBoxes(DrawingContext *ctx) -{ - cairo_t *C = ctx->cairo; - - cairo_set_source_rgb(C, 1, 0, 0); - drawTriangle(ctx, "abc"); - cairo_set_source_rgb(C, 0, 0, 1); - drawTriangle(ctx, "aca abc aca"); - drawTriangle(ctx, "acac abc caca"); - drawTriangle(ctx, "acaca abc acaca"); - cairo_set_source_rgb(C, 0, 0.8, 0); - drawTriangle(ctx, "cac abc cac"); - drawTriangle(ctx, "caca abc acac"); - drawTriangle(ctx, "cacac abc cacac"); - - /* - 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(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'); - - 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'); - - drawBoxStd(ctx, "caca cb", 'B'); - drawBoxStd(ctx, "caca cbc", 'C'); - drawBoxStd(ctx, "caca cbcb", 'B'); - drawBoxStd(ctx, "caca bcbc", 'C'); - drawBoxStd(ctx, "caca bcb", 'B'); - drawBoxStd(ctx, "caca bc", 'C'); - - cairo_set_source_rgb(C, 1, 0, 1); - 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'); - - cairo_set_source_rgb(C, 0, 1, 0); -// drawBoxStd(ctx, "ca bc", 'C'); - drawBoxStd(ctx, "cabc ba", 'A'); - drawBoxStd(ctx, "cabc bab", 'B'); - drawBoxStd(ctx, "cabc baba", 'A'); - drawBoxStd(ctx, "cabc abab", 'B'); - drawBoxStd(ctx, "cabc aba", 'A'); - drawBoxStd(ctx, "cabc ab", 'B'); - */ -} - -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]; - - fixedPoints("abc", p[0]); - fixedPoints("bca", p[1]); - fixedPoints("cab", p[2]); - - double color[3][3] = {{1,0,0},{0,0.7,0},{0,0,1}}; - - 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) { - 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(DrawingContext *ctx) -{ - cairo_t *C = ctx->cairo; - - 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(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) { - if(show_limit) { - cairo_save(C); - - int previous_inside = 0; - for(int i = 0; i < n_group_elements; i++) { - double x = limit_curve[3*i]; - double y = limit_curve[3*i+1]; - - cairo_user_to_device(C, &x, &y); - - if(-x < ctx->width && x < 3*ctx->width && -y < ctx->height && y < 3*ctx->height) { - if(limit_with_lines) { - if(!previous_inside) - cairo_move_to(C, limit_curve[3*i], limit_curve[3*i+1]); - else - cairo_line_to(C, limit_curve[3*i], limit_curve[3*i+1]); - } else { - cairo_move_to(C, limit_curve[3*i], limit_curve[3*i+1]); - cairo_close_path(C); - } - previous_inside = 1; - } else { - previous_inside = 0; - } - } - - if(!limit_with_lines) { // draw dots instead of lines - cairo_set_line_cap(C, CAIRO_LINE_CAP_ROUND); - cairo_set_line_width(C, 3.0/ctx->scalefactor); - } - - cairo_set_source_rgb(C, 0, 0, 0); - cairo_stroke(C); - - cairo_restore(C); - } - - if(show_boxes) - drawBoxes(ctx); - - if(show_attractors) - drawAttractors(ctx); - - if(show_reflectors) - drawReflectors(ctx); - } - - cairo_identity_matrix(C); - - cairo_move_to(C, 15, 30); - cairo_set_source_rgb(C, 0, 0, 0); - char buf[100]; - sprintf(buf, "t = exp(%.8f) = %.8f", log(parameter), parameter); - cairo_show_text(C, buf); - - cairo_surface_flush(cairo_get_target(C)); -} - -void setup() -{ - n_group_elements = 50000; - 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 += ((double)file.height - (double)screen->height) / 2.0; // recenter vertically - updateDimensions(&file); - - 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 XK_Down: - parameter /= exp(0.002); - limit_curve_valid = computeLimitCurve(); - break; - case XK_Up: - parameter *= exp(0.002); - limit_curve_valid = computeLimitCurve(); - break; - case XK_Left: - parameter /= exp(0.0001); - limit_curve_valid = computeLimitCurve(); - break; - case XK_Right: - parameter *= exp(0.0001); - limit_curve_valid = computeLimitCurve(); - break; - case XK_Page_Down: - parameter /= exp(0.2); - limit_curve_valid = computeLimitCurve(); - break; - case XK_Page_Up: - parameter *= exp(0.2); - 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': - limit_with_lines = !limit_with_lines; - break; - case 'l': - show_limit = !show_limit; - break; - case 'p': - print(info->context); - break; - case 'f': - use_repelling = !use_repelling; - limit_curve_valid = computeLimitCurve(); - } - - return STATUS_REDRAW; - } - - return STATUS_NOTHING; -} - -int main() -{ - GraphicsInfo *info; - - parameter = 3.0; - - setup(); - limit_curve_valid = computeLimitCurve(); - - 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; - - /* - 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; - */ - - updateDimensions(ctx); - - startTimer(info); - - while(1) { - int result = checkEvents(info, processEvent, NULL); - if(result == STATUS_QUIT) - return 0; - else if(result == STATUS_REDRAW) { - struct timeval current_time; - double start_time, intermediate_time, end_time; - gettimeofday(¤t_time, 0); - start_time = current_time.tv_sec + current_time.tv_usec*1e-6; - - updateDimensions(info->context); - draw(info->context); - - gettimeofday(¤t_time, 0); - intermediate_time = current_time.tv_sec + current_time.tv_usec*1e-6; - - cairo_set_source_surface(info->front_context, info->buffer_surface, 0, 0); - cairo_paint(info->front_context); - - gettimeofday(¤t_time, 0); - end_time = current_time.tv_sec + current_time.tv_usec*1e-6; - printf("drawing finished in %.2f milliseconds, of which %.2f milliseconds were buffer switching\n", (end_time - start_time) * 1000, (end_time - intermediate_time) * 1000); - } - waitUpdateTimer(info); - } - - destroyCairo(info); - - destroy(); - - return 0; + ctx->limit_curve_valid = 1; + + success = 1; + +error_out: + releaseTempMatrices(ctx, 11+ctx->n_group_elements); + + return success; } diff --git a/main.c b/main.c new file mode 100644 index 0000000..dac91c1 --- /dev/null +++ b/main.c @@ -0,0 +1,289 @@ +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "initcairo.h" +#include "triangle.h" +#include "linalg.h" + +#define TOGGLE(a) do { (a) = !(a); } while(0) + +DrawingContext *screen_context; + +// setup everything except cairo and dim, which will be provided by the graphics system +void setupContext(DrawingContext *ctx) +{ + ctx->n_group_elements = NUM_GROUP_ELEMENTS; + ctx->p[0] = 5; + ctx->p[1] = 5; + ctx->p[2] = 5; + ctx->k[0] = 2; + ctx->k[1] = 2; + ctx->k[2] = 2; + ctx->parameter = 3.0; + ctx->show_boxes = 0; + ctx->show_attractors = 0; + ctx->show_reflectors = 0; + ctx->show_limit= 1; + ctx->limit_with_lines = 1; + ctx->use_repelling = 0; + + ctx->limit_curve = malloc(3*ctx->n_group_elements*sizeof(double)); + ctx->limit_curve_valid = 0; + + ctx->group = malloc(ctx->n_group_elements*sizeof(groupelement_t)); + generate_triangle_group(ctx->group, ctx->n_group_elements, ctx->p[0], ctx->p[1], ctx->p[2]); + + // the temporary stuff + ctx->cartan = gsl_matrix_alloc(3, 3); + ctx->cob = gsl_matrix_alloc(3, 3); + ctx->ws = workspace_alloc(3); + ctx->tmp = malloc(MAX_TEMP_MATRICES*sizeof(gsl_matrix*)); + for(int i = 0; i < MAX_TEMP_MATRICES; i++) + ctx->tmp[i] = gsl_matrix_alloc(3, 3); + ctx->tmp_used = 0; + ctx->tmp_vec = malloc(MAX_TEMP_MATRICES*sizeof(gsl_vector*)); + for(int i = 0; i < MAX_TEMP_MATRICES; i++) + ctx->tmp_vec[i] = gsl_vector_alloc(3); + ctx->tmp_vec_used = 0; + +} + +void destroyContext(DrawingContext *ctx) +{ + free(ctx->limit_curve); + free(ctx->group); + + gsl_matrix_free(ctx->cartan); + gsl_matrix_free(ctx->cob); + + workspace_free(ctx->ws); + + for(int i = 0; i < MAX_TEMP_MATRICES; i++) + gsl_matrix_free(ctx->tmp[i]); + free(ctx->tmp); + for(int i = 0; i < MAX_TEMP_VECTORS; i++) + gsl_vector_free(ctx->tmp_vec[i]); + free(ctx->tmp_vec); +} + +void updateMatrices(DrawingContext *ctx) +{ + double angle[3]; + LOOP(i) angle[i] = M_PI*ctx->k[i]/ctx->p[i]; + cartanMatrix(ctx->cartan, angle[0], angle[1], angle[2], ctx->parameter); + gsl_matrix_memcpy(ctx->cob, ctx->cartan); // is this a good choice of basis +} + +void print(DrawingContext *screen) +{ + DrawingContext file; + DimensionsInfo dim; + 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)); + + dim.width = screen->dim->width; + dim.height = screen->dim->width / sqrt(2.0); + dim.matrix = screen->dim->matrix; + dim.matrix.y0 += ((double)dim.height - (double)screen->dim->height) / 2.0; // recenter vertically + updateDimensions(&dim); + file.dim = &dim; + + surface = cairo_pdf_surface_create(filename, (double)dim.width, (double)dim.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 XK_Down: + screen_context->parameter /= exp(0.002); + updateMatrices(screen_context); + computeLimitCurve(screen_context); + break; + case XK_Up: + screen_context->parameter *= exp(0.002); + updateMatrices(screen_context); + computeLimitCurve(screen_context); + break; + case XK_Left: + screen_context->parameter /= exp(0.0001); + updateMatrices(screen_context); + computeLimitCurve(screen_context); + break; + case XK_Right: + screen_context->parameter *= exp(0.0001); + updateMatrices(screen_context); + computeLimitCurve(screen_context); + break; + case XK_Page_Down: + screen_context->parameter /= exp(0.2); + updateMatrices(screen_context); + computeLimitCurve(screen_context); + break; + case XK_Page_Up: + screen_context->parameter *= exp(0.2); + updateMatrices(screen_context); + computeLimitCurve(screen_context); + break; + case ' ': + screen_context->parameter = 2.890053638; + updateMatrices(screen_context); + computeLimitCurve(screen_context); + break; + case XK_Return: + screen_context->parameter = 2.76375163; + updateMatrices(screen_context); + computeLimitCurve(screen_context); + break; + case 'm': + printf("matrix.xx = %f;\n", info->dim->matrix.xx); + printf("matrix.xy = %f;\n", info->dim->matrix.xy); + printf("matrix.x0 = %f;\n", info->dim->matrix.x0); + printf("matrix.yx = %f;\n", info->dim->matrix.yx); + printf("matrix.yy = %f;\n", info->dim->matrix.yy); + printf("matrix.y0 = %f;\n", info->dim->matrix.y0); + break; + case 'b': + TOGGLE(screen_context->show_boxes); + break; + case 'a': + TOGGLE(screen_context->show_attractors); + break; + case 'r': + TOGGLE(screen_context->show_reflectors); + break; + case 'L': + TOGGLE(screen_context->limit_with_lines); + break; + case 'l': + TOGGLE(screen_context->show_limit); + break; + case 'p': + print(screen_context); + break; + case 'f': + TOGGLE(screen_context->use_repelling); + computeLimitCurve(screen_context); + } + + return STATUS_REDRAW; + } + + return STATUS_NOTHING; +} + +int main() +{ + GraphicsInfo *info; + + screen_context = malloc(sizeof(DrawingContext)); + setupContext(screen_context); + updateMatrices(screen_context); + computeLimitCurve(screen_context); + + // do stuff +/* + DrawingContext *ctx = screen_context; + gsl_matrix *tmp = getTempMatrix(ctx); + gsl_matrix *ev = getTempMatrix(ctx); + gsl_matrix *evinv = getTempMatrix(ctx); + gsl_matrix **gen = getTempMatrices(ctx, 3); + gsl_matrix *transform = getTempMatrix(ctx); + vector_t eigenvectors[3][3]; + char words[3][4] = {"abc", "bca", "cab"}; + + initializeTriangleGenerators(gen, ctx->cartan); + + gsl_matrix_set_identity(tmp); + for(int j = 0; j < strlen(words[0]); j++) + multiply_right(tmp, gen[words[0][j]-'a'], ctx->ws); + real_eigenvectors(tmp, ev, ctx->ws); + invert(ev, evinv, ctx->ws); + + + LOOP(i) { + gsl_matrix_set_identity(tmp); + for(int j = 0; j < strlen(words[i]); j++) + multiply_right(tmp, gen[words[i][j]-'a'], ctx->ws); + real_eigenvectors(tmp, ev, ctx->ws); + LOOP(j) LOOP(k) eigenvectors[i][j].x[k] = gsl_matrix_get(ev, k, j); + } + + // solve the following: (x + y + z) (l) = lambda w + + return 0; +*/ + + info = initCairo(0, KeyPressMask, 200, 200, "Triangle group"); + if(!info) + return 1; + + info->dim->matrix.xx = 837.930824; + info->dim->matrix.xy = -712.651341; + info->dim->matrix.x0 = 180.427716; + info->dim->matrix.yx = 712.651341; + info->dim->matrix.yy = 837.930824; + info->dim->matrix.y0 = 1412.553240; + updateDimensions(info->dim); + + screen_context->dim = info->dim; + screen_context->cairo = info->buffer_context; + + startTimer(info); + + while(1) { + int result = checkEvents(info, processEvent, NULL); + if(result == STATUS_QUIT) + return 0; + else if(result == STATUS_REDRAW) { + struct timeval current_time; + double start_time, intermediate_time, end_time; + gettimeofday(¤t_time, 0); + start_time = current_time.tv_sec + current_time.tv_usec*1e-6; + + draw(screen_context); + + gettimeofday(¤t_time, 0); + intermediate_time = current_time.tv_sec + current_time.tv_usec*1e-6; + + cairo_set_source_surface(info->front_context, info->buffer_surface, 0, 0); + cairo_paint(info->front_context); + + gettimeofday(¤t_time, 0); + end_time = current_time.tv_sec + current_time.tv_usec*1e-6; + printf("drawing finished in %.2f milliseconds, of which %.2f milliseconds were buffer switching\n", (end_time - start_time) * 1000, (end_time - intermediate_time) * 1000); + } + waitUpdateTimer(info); + } + + free(screen_context); + destroyCairo(info); + destroyContext(screen_context); + + return 0; +} diff --git a/main.h b/main.h new file mode 100644 index 0000000..71b7bab --- /dev/null +++ b/main.h @@ -0,0 +1,126 @@ +#ifndef TRIANGLE_GROUP_MAIN_H +#define TRIANGLE_GROUP_MAIN_H + +#include "triangle.h" +#include "linalg.h" +#include "initcairo.h" + +#undef ERROR +#define ERROR(condition, msg, ...) if(condition){fprintf(stderr, msg, ##__VA_ARGS__); exit(1);} +#define LOOP(i) for(int i = 0; i < 3; i++) + +#define NUM_GROUP_ELEMENTS 50000 +#define MAX_TEMP_MATRICES 60000 +#define MAX_TEMP_VECTORS 100 + +typedef struct { + double x[3]; +} vector_t; + +typedef struct { + double x; + double y; +} point_t; + +typedef struct { + // infos about the screen to draw on + cairo_t *cairo; + DimensionsInfo *dim; + + // a priori parameter + int p[3],k[3]; + double parameter; + int n_group_elements; + int show_boxes; + int show_attractors; + int show_reflectors; + int show_limit; + int limit_with_lines; + int use_repelling; + gsl_matrix *cartan, *cob; + + // precomputed and constant + groupelement_t *group; + + // computed stuff + double *limit_curve; // x, y, angle triples + int limit_curve_valid; + + // temporary; matrices can only be freed from the top, but that's enough for us + workspace_t *ws; + gsl_matrix **tmp; + int tmp_used; + gsl_vector **tmp_vec; + int tmp_vec_used; +} DrawingContext; + +static gsl_matrix **getTempMatrices(DrawingContext *ctx, int n) +{ + ERROR(ctx->tmp_used + n > MAX_TEMP_MATRICES, "Ran out of temporary matrices. Consider increasing MAX_TEMP_MATRICES\n"); + int index = ctx->tmp_used; + ctx->tmp_used += n; + return ctx->tmp + index; +} + +static gsl_matrix *getTempMatrix(DrawingContext *ctx) +{ + return *getTempMatrices(ctx, 1); +} + +static void releaseTempMatrices(DrawingContext *ctx, int n) +{ + ERROR(ctx->tmp_used - n < 0, "Released more matrices then in use\n"); + ctx->tmp_used -= n; +} + +static gsl_vector **getTempVectors(DrawingContext *ctx, int n) +{ + ERROR(ctx->tmp_vec_used + n > MAX_TEMP_VECTORS, "Ran out of temporary vectors. Consider increasing MAX_TEMP_VECTORS\n"); + int index = ctx->tmp_vec_used; + ctx->tmp_vec_used += n; + return ctx->tmp_vec + index; +} + +static gsl_vector *getTempVector(DrawingContext *ctx) +{ + return *getTempVectors(ctx, 1); +} + +static void releaseTempVectors(DrawingContext *ctx, int n) +{ + ERROR(ctx->tmp_vec_used - n < 0, "Released more vectors then in use\n"); + ctx->tmp_vec_used -= n; +} + +// implemented in limit_set.c +void cartanMatrix(gsl_matrix *cartan, double a1, double a2, double a3, double s); +void initializeTriangleGenerators(gsl_matrix **gen, gsl_matrix *cartan); +int computeLimitCurve(DrawingContext *ctx); + +// implemented in draw.c +vector_t cross(vector_t a, vector_t b); +int fixedPoints(DrawingContext *ctx, const char *word, vector_t *out); +void drawPoint(DrawingContext *ctx, point_t p); +void drawSegment2d(DrawingContext *ctx, point_t a, point_t b); +void drawVector(DrawingContext *ctx, vector_t v); +void drawCovector(DrawingContext *ctx, vector_t v); +void drawSegment(DrawingContext *ctx, vector_t a, vector_t b); +void drawPolygon(DrawingContext *ctx, int sides, ...); +void drawTriangle(DrawingContext *ctx, const char *word); +void drawBox(DrawingContext *ctx, const char *word1, const char *word2); +void drawBoxStd(DrawingContext *ctx, const char *word, char base); +void drawReflectors(DrawingContext *ctx); +void drawAttractors(DrawingContext *ctx); +void drawBoxes(DrawingContext *ctx); +void drawLimitCurve(DrawingContext *ctx); +void drawText(DrawingContext *ctx); +void draw(DrawingContext *ctx); + +// implemented in main.c +void setupContext(DrawingContext *ctx); +void destroyContext(DrawingContext *ctx); +void print(DrawingContext *screen); +int processEvent(GraphicsInfo *info, XEvent *ev); +void updateMatrices(DrawingContext *ctx); + +#endif