#include "main.h" // level 0: helper functions int isInsideBB(DrawingContext *ctx, point_t p) { cairo_user_to_device(ctx->cairo, &x, &y); return -p.x < ctx->dim->width && p.x < 3*ctx->dim->width && -p.y < ctx->dim->height && p.y < 3*ctx->dim->height; } 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->ws); gsl_vector *tmp2 = getTempVector(ctx->ws); 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->ws, 2); } int fixedPoints(DrawingContext *ctx, const char *word, vector_t *out) { gsl_matrix *tmp = getTempMatrix(ctx->ws); gsl_matrix *ev = getTempMatrix(ctx->ws); gsl_matrix **gen = getTempMatrices(ctx->ws, 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->ws, 5); return count; } // 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 segments, 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); if(segments) drawSegment(ctx, prev, current); else drawCovector(ctx, cross(prev, current)); } if(segments) drawSegment(ctx, current, first); else drawCovector(ctx, cross(current, first)); va_end(args); } void drawTriangle(DrawingContext *ctx, const char *word) { vector_t p[3]; fixedPoints(ctx, word, p); drawPolygon(ctx, 1, 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, 1, 4, p[0][0], i[0], p[1][0], i[1]); } void drawBoxLines(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, 0, 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]); // (bca, cab) -> (cab, cacabac) -> (cacabac, acacbacac) -> fixedPoints(ctx, "cab", p[2]); // ac cab ca = bca, ac bac ca = acb 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 drawBoxes2(DrawingContext *ctx) { gsl_matrix *tmp = getTempMatrix(ctx->ws); cairo_set_source_rgb(ctx->cairo, 1, 0.5, 0); drawBox(ctx, "ac cab ca", "ac bca ca"); drawBox(ctx, "acac cab caca", "acac bca caca"); drawBox(ctx, "caca cab acac", "caca bca acac"); drawBox(ctx, "ca cab ac", "ca bca ac"); // cacabac, cab cairo_set_source_rgb(ctx->cairo, 1, 0, 0.5); drawBoxLines(ctx, "cab", "bca"); cairo_set_source_rgb(ctx->cairo, 0, 0, 0); drawBox(ctx, "abc", "cab"); vector_t w = {0, 1, 0}; drawCovector(ctx, w); vector_t v[3]; point_t p; cairo_set_source_rgb(ctx->cairo, 1, 0, 0); computeRotationMatrix(ctx, tmp, "ac"); LOOP(i) LOOP(j) v[i].x[j] = gsl_matrix_get(tmp, j, i); for(int k = 0; k < 50; k++) { LOOP(i) w.x[i] = 1.8 * v[2].x[i] + cos(2*k*M_PI/50) * v[0].x[i] + sin(2*k*M_PI/50) * v[1].x[i]; p = vectorToPoint(ctx, w); if(k == 0) cairo_move_to(ctx->cairo, p.x, p.y); else cairo_line_to(ctx->cairo, p.x, p.y); } cairo_set_source_rgb(ctx->cairo, 1, 0, 0); cairo_stroke(ctx->cairo); /* vector_t p[3]; */ /* fixedPoints(ctx, "cab", p); */ /* drawVector(ctx, p[0]); */ /* fixedPoints(ctx, "ca cab ac", p); */ /* drawVector(ctx, p[0]); */ /* fixedPoints(ctx, "caca cab acac", p); */ /* drawVector(ctx, p[0]); */ /* fixedPoints(ctx, "acac cab caca", p); */ /* drawVector(ctx, p[0]); */ /* fixedPoints(ctx, "ac cab ca", p); */ /* drawVector(ctx, p[0]); */ releaseTempMatrices(ctx->ws, 1); } 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_boxes2) drawBoxes2(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)); }