#include #include #include #include #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; 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); int compareAngle(const void *x, const void *y); int computeLimitCurve(); void draw(void *data); point_t intersect(point_t a, point_t b, point_t c, point_t d); GraphicsInfo G; double parameter = 2.8; 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 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; } draw(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); gsl_matrix_set(cartan, 0, 1, 2*s*cos(a3)); gsl_matrix_set(cartan, 0, 2, 2/s*cos(a2)); gsl_matrix_set(cartan, 1, 0, 2/s*cos(a3)); gsl_matrix_set(cartan, 1, 1, -2); gsl_matrix_set(cartan, 1, 2, 2*s*cos(a1)); gsl_matrix_set(cartan, 2, 0, 2*s*cos(a2)); gsl_matrix_set(cartan, 2, 1, 2/s*cos(a1)); gsl_matrix_set(cartan, 2, 2, -2); } void initializeTriangleGenerators(gsl_matrix **gen, gsl_matrix *cartan) { LOOP(i) gsl_matrix_set_identity(gen[i]); 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) { 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; } void draw_segment(point_t a, point_t b) { cairo_move_to(G.ctx, a.x, a.y); cairo_line_to(G.ctx, b.x, b.y); cairo_stroke(G.ctx); } void draw_polygon(int sides, point_t a, point_t b, point_t c, point_t d) // only quadrilaterals so far { draw_segment(a, b); draw_segment(b, c); draw_segment(c, d); draw_segment(d, a); } void draw_box(const char *word, char base) { // lower stack[i] reserved for linalg.c is used by multiply_*() gsl_matrix *tmp1 = ws->stack[10]; gsl_matrix *conj = ws->stack[12]; gsl_matrix *conjinv = ws->stack[13]; gsl_matrix *coxeter[3] = { ws->stack[14], ws->stack[15], ws->stack[16] }; // fixed points of coxeter elements gsl_matrix_set_identity(conj); gsl_matrix_set_identity(conjinv); for(int i = 0; i < strlen(word); i++) { if(word[i] == ' ') continue; multiply_right(conj, gen[word[i]-'a'], ws); multiply_left(gen[word[i]-'a'], conjinv, ws); } for(int i = 0; i < 3; i++) { multiply_many(ws, tmp1, 5, conj, gen[(base-'A'+i)%3], gen[(base-'A'+i+1)%3], gen[(base-'A'+i+2)%3], conjinv); eigenvectors(tmp1, coxeter[i], ws); multiply_left(cob, coxeter[i], ws); } point_t p_a[3],p_m[3],p_r[3],p_i[4]; LOOP(i) p_a[i].x = gsl_matrix_get(coxeter[i], 0, 0) / gsl_matrix_get(coxeter[i], 2, 0); LOOP(i) p_a[i].y = gsl_matrix_get(coxeter[i], 1, 0) / gsl_matrix_get(coxeter[i], 2, 0); LOOP(i) p_m[i].x = gsl_matrix_get(coxeter[i], 0, 1) / gsl_matrix_get(coxeter[i], 2, 1); LOOP(i) p_m[i].y = gsl_matrix_get(coxeter[i], 1, 1) / gsl_matrix_get(coxeter[i], 2, 1); LOOP(i) p_r[i].x = gsl_matrix_get(coxeter[i], 0, 2) / gsl_matrix_get(coxeter[i], 2, 2); LOOP(i) p_r[i].y = gsl_matrix_get(coxeter[i], 1, 2) / gsl_matrix_get(coxeter[i], 2, 2); p_i[0] = intersect(p_a[1], p_r[1], p_m[0], p_r[0]); p_i[1] = intersect(p_a[0], p_r[0], p_a[2], p_r[2]); p_i[2] = intersect(p_a[1], p_m[1], p_a[2], p_r[2]); p_i[3] = intersect(p_a[0], p_r[0], p_a[1], p_m[1]); draw_polygon(4, p_a[1], p_i[0], p_r[0], p_i[3]); } int computeLimitCurve() { int p = 5, q = 5, r = 5; int k = 2, l = 2, m = 2; 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]); if(!eigenvectors(coxeter, coxeter_fixedpoints, ws)) 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, 0)/gsl_matrix_get(fixedpoints, 2, 0), gsl_matrix_get(fixedpoints, 1, 0)/gsl_matrix_get(fixedpoints, 2, 0)); } // 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); multiply_many(ws, coxeter, 3, gen[0], gen[1], gen[2]); if(!eigenvectors(coxeter, coxeter_fixedpoints, ws)) 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] = gsl_matrix_get(fixedpoints, 0, 0)/gsl_matrix_get(fixedpoints, 2, 0); limit_curve[3*i+1] = gsl_matrix_get(fixedpoints, 1, 0)/gsl_matrix_get(fixedpoints, 2, 0); } qsort(limit_curve, n_group_elements, 3*sizeof(double), compareAngle); return 1; } void draw(void *data) { struct timeval current_time; gettimeofday(¤t_time, 0); double start_time = current_time.tv_sec + current_time.tv_usec*1e-6; cairo_push_group(G.ctx); cairo_set_source_rgb(G.ctx, 1, 1, 1); cairo_paint(G.ctx); cairo_set_matrix(G.ctx, &G.matrix); if(limit_curve_valid) { int last_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(G.ctx, &x, &y); if(-x < G.width && x < 3*G.width && -y < G.height && y < 3*G.height) { if(!last_inside) { cairo_move_to(G.ctx, limit_curve[3*i], limit_curve[3*i+1]); } else { cairo_line_to(G.ctx, limit_curve[3*i], limit_curve[3*i+1]); } last_inside = 1; } else { last_inside = 0; } } cairo_set_line_width(G.ctx, 1.0/G.scalefactor); cairo_set_line_join(G.ctx, CAIRO_LINE_JOIN_BEVEL); cairo_set_source_rgb(G.ctx, 0, 0, 0); cairo_stroke(G.ctx); cairo_set_source_rgb(G.ctx, 1, 0, 0); // draw_box("c", 'C'); draw_box("", 'B'); draw_box("a", 'A'); // draw_box("", 'C'); cairo_set_source_rgb(G.ctx, 0, 0, 1); draw_box("ca", 'A'); draw_box("cac", 'C'); draw_box("caca", 'A'); draw_box("cacac", 'C'); draw_box("acac", 'A'); draw_box("aca", 'C'); cairo_set_source_rgb(G.ctx, 0, 1, 0); draw_box("ac", 'C'); draw_box("aca", 'A'); draw_box("acac", 'C'); draw_box("acaca", 'A'); draw_box("caca", 'C'); draw_box("cac", 'A'); } cairo_identity_matrix(G.ctx); cairo_move_to(G.ctx, 15, 30); cairo_set_source_rgb(G.ctx, 0, 0, 0); cairo_set_font_size(G.ctx, 16); char buf[100]; sprintf(buf, "t = %.8f", parameter); cairo_show_text(G.ctx, buf); cairo_pop_group_to_source(G.ctx); cairo_paint(G.ctx); cairo_surface_flush(G.sfc); 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); } int main() { setup(); limit_curve_valid = computeLimitCurve(); if(!initCairo(0, KeyPressMask, 200, 200, "Triangle group", &G)) return 1; 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; startTimer(&G); while(!checkEvents(&G, processEvent, draw, 0)) { waitUpdateTimer(&G); } destroyCairo(&G); destroy(); return 0; }