diff --git a/initcairo.c b/initcairo.c index 296efa1..c7e74ef 100644 --- a/initcairo.c +++ b/initcairo.c @@ -80,7 +80,7 @@ void waitUpdateTimer(GraphicsInfo *info) info->frames++; } -int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(void*), void *data) +int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(cairo_t *, void*), void *data) { int state; static int last_x, last_y; @@ -88,7 +88,7 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(void*), vo switch(ev->type) { case Expose: - draw(data); + draw(info->ctx, data); break; case ConfigureNotify: @@ -124,7 +124,7 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(void*), vo } last_x = ev->xbutton.x; last_y = ev->xbutton.y; - draw(data); + draw(info->ctx, data); break; case MotionNotify: @@ -139,7 +139,7 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(void*), vo printf("Button1Motion Event, dx: %d, dy: %d\n", dx, dy); - draw(data); + draw(info->ctx, data); } else if(ev->xmotion.state & Button1Mask && ev->xmotion.state & ShiftMask) { double angle = atan2((double)ev->xmotion.y - (double)info->height/2, (double)ev->xmotion.x - (double)info->width/2)- @@ -154,7 +154,7 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(void*), vo printf("Button1Motion Event, angle: %f\n", angle); - draw(data); + draw(info->ctx, data); } last_x = ev->xmotion.x; last_y = ev->xmotion.y; @@ -172,7 +172,7 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(void*), vo } -int checkEvents(GraphicsInfo *info, int (*process)(GraphicsInfo*, XEvent*, void*), void (*draw)(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*), void (*draw)(cairo_t *, void*), void *data) // get any events from the queue and the server, process them if neccessary, quit if wanted { XEvent ev; diff --git a/initcairo.h b/initcairo.h index 409a7c0..9a02aaf 100644 --- a/initcairo.h +++ b/initcairo.h @@ -34,6 +34,6 @@ void destroyCairo(GraphicsInfo *info); void startTimer(GraphicsInfo *info); void waitUpdateTimer(GraphicsInfo *info); -int checkEvents(GraphicsInfo *info, int (*process)(GraphicsInfo*, XEvent*, void*), void (*draw)(void*), void *data); +int checkEvents(GraphicsInfo *info, int (*process)(GraphicsInfo*, XEvent*, void*), void (*draw)(cairo_t *, void*), void *data); #endif diff --git a/limit_set.c b/limit_set.c index e527b6b..12fa631 100644 --- a/limit_set.c +++ b/limit_set.c @@ -21,12 +21,14 @@ 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); +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; -double parameter = 2.8; +double parameter; int n_group_elements; double *limit_curve; // x, y, angle triples int limit_curve_valid = 0; @@ -36,6 +38,10 @@ 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_limit = 1; + int processEvent(GraphicsInfo *info, XEvent *ev, void *data) { int state; @@ -65,9 +71,26 @@ int processEvent(GraphicsInfo *info, XEvent *ev, void *data) 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(0); + draw(G.ctx, 0); } return 0; @@ -144,59 +167,102 @@ point_t intersect(point_t a, point_t b, point_t c, point_t d) return res; } -void draw_segment(point_t a, point_t b) +void drawPoint(cairo_t *ctx, point_t p) { - cairo_move_to(G.ctx, a.x, a.y); - cairo_line_to(G.ctx, b.x, b.y); - cairo_stroke(G.ctx); + // 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); } -void draw_polygon(int sides, point_t a, point_t b, point_t c, point_t d) // only quadrilaterals so far +void drawLine(cairo_t *ctx, point_t a, point_t b) { - draw_segment(a, b); - draw_segment(b, c); - draw_segment(c, d); - draw_segment(d, a); + // not implemented } -void draw_box(const char *word, char base) +void drawSegment(cairo_t *ctx, point_t a, point_t b) { - // 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 + cairo_move_to(ctx, a.x, a.y); + cairo_line_to(ctx, b.x, b.y); + cairo_stroke(ctx); +} - gsl_matrix_set_identity(conj); - gsl_matrix_set_identity(conjinv); +void drawPolygon(cairo_t *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); + drawSegment(ctx, c, d); + drawSegment(ctx, d, a); +} + +void 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(conj, gen[word[i]-'a'], ws); - multiply_left(gen[word[i]-'a'], conjinv, ws); + multiply_right(tmp, gen[word[i]-'a'], ws); } + eigenvectors(tmp, ev, ws); + multiply_left(cob, ev, 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); + 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); +} + +void drawAttractorConnection(cairo_t *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 drawBox(cairo_t *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(cairo_t *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; - point_t p_a[3],p_m[3],p_r[3],p_i[4]; + LOOP(i) word1[len+i] = (base-'A'+6+i+1)%3+'a'; + LOOP(i) word2[len+i] = (base-'A'+6-i-1)%3+'a'; - 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); +// printf("Words: %s %s\n", word1, word2); - 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]); + drawBox(ctx, word1, word2); } int computeLimitCurve() @@ -248,78 +314,161 @@ int computeLimitCurve() return 1; } -void draw(void *data) +void drawBoxes(cairo_t *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_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(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(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(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"); + + 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 + */ +} + +void drawAttractors(cairo_t *ctx) +{ + point_t p[3][3]; + + fixedPoints("abc", p[0]); + fixedPoints("bca", p[1]); + fixedPoints("cab", p[2]); + + cairo_set_source_rgb(ctx, 0, 0, 0); + + LOOP(i) LOOP(j) drawPoint(ctx, p[i][j]); + + LOOP(i) LOOP(j) drawLine(ctx, p[i][j], p[i][(j+1)%3]); +} + +void draw(cairo_t *ctx, 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_push_group(ctx); + cairo_set_source_rgb(ctx, 1, 1, 1); + cairo_paint(ctx); - cairo_set_matrix(G.ctx, &G.matrix); + cairo_set_matrix(ctx, &G.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); 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(show_limit) { + 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]; - 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]); + cairo_user_to_device(ctx, &x, &y); + + if(-x < G.width && x < 3*G.width && -y < G.height && y < 3*G.height) { + if(!last_inside) { + cairo_move_to(ctx, limit_curve[3*i], limit_curve[3*i+1]); + } else { + cairo_line_to(ctx, limit_curve[3*i], limit_curve[3*i+1]); + } + last_inside = 1; } else { - cairo_line_to(G.ctx, limit_curve[3*i], limit_curve[3*i+1]); + last_inside = 0; } - last_inside = 1; - } else { - last_inside = 0; } + + cairo_set_source_rgb(ctx, 0, 0, 0); + cairo_stroke(ctx); } - 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); + if(show_boxes) + drawBoxes(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'); + if(show_attractors) + drawAttractors(ctx); } - cairo_identity_matrix(G.ctx); + cairo_identity_matrix(ctx); - cairo_move_to(G.ctx, 15, 30); - cairo_set_source_rgb(G.ctx, 0, 0, 0); - cairo_set_font_size(G.ctx, 16); + cairo_move_to(ctx, 15, 30); + cairo_set_source_rgb(ctx, 0, 0, 0); char buf[100]; sprintf(buf, "t = %.8f", parameter); - cairo_show_text(G.ctx, buf); + cairo_show_text(ctx, buf); - cairo_pop_group_to_source(G.ctx); - cairo_paint(G.ctx); - cairo_surface_flush(G.sfc); + cairo_pop_group_to_source(ctx); + cairo_paint(ctx); + cairo_surface_flush(cairo_get_target(ctx)); gettimeofday(¤t_time, 0); double end_time = current_time.tv_sec + current_time.tv_usec*1e-6; @@ -328,16 +477,28 @@ void draw(void *data) int main() { + parameter = 3.0; + 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; + */ + + 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; startTimer(&G);