#include "main.h" #define FMOD(x,y) (fmod(x,y) < 0 ? fmod(x,y) + y : fmod(x,y)) #define ANGLE_DIFF(x,y) (FMOD((x)-(y), 2*M_PI)) #define ANGLE_IN_INTERVAL(a,b,x) (ANGLE_DIFF(x,a) < ANGLE_DIFF(b,a)) #define FLIP(x,y) do {double tmp = x; x = y; y = tmp;} while(0) // level 0: helper functions int isInsideBB(DrawingContext *ctx, point_t p) { cairo_user_to_device(ctx->cairo, &p.x, &p.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(gsl_matrix *m, vector_t x) { vector_t out; LOOP(i) out.x[i] = 0.0; LOOP(i) LOOP(j) out.x[i] += gsl_matrix_get(m, i, j) * x.x[j]; return out; } vector_t apply_transpose(gsl_matrix *m, vector_t x) { vector_t out; LOOP(i) out.x[i] = 0.0; LOOP(i) LOOP(j) out.x[i] += gsl_matrix_get(m, j, i) * x.x[j]; return out; } 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; } int wordEigenvalues(DrawingContext *ctx, const char *word, double *out) { gsl_matrix *tmp = getTempMatrix(ctx->ws); gsl_vector *ev = getTempVector(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_eigenvalues(tmp, ev, ctx->ws); LOOP(i) out[i] = gsl_vector_get(ev, i); releaseTempMatrices(ctx->ws, 4); releaseTempVectors(ctx->ws, 1); 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_arc(C, p.x, p.y, 3.0/ctx->dim->scalefactor, 0, 2*M_PI); cairo_close_path(C); cairo_fill(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 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)); } void drawSegmentWith(DrawingContext *ctx, vector_t a, vector_t b, vector_t line, int contains) { point_t a_ = vectorToPoint(ctx,a); point_t b_ = vectorToPoint(ctx,b); double x[3]; double cofactor; double r, tline, tminus, tplus; double coeff0, coeff1, coeff2; point_t m, xminus, xplus; // multiply line with inverse of cob to get it as implicit line x in chart 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 * line.x[j]; } // t = parameter on segment of intersection with line, s(t) = a + (b-a)*t tline = -(a_.x*x[0] + a_.y*x[1] + x[2])/((b_.x - a_.x)*x[0] + (b_.y - a_.y)*x[1]); if((tline < 0 || tline > 1) != contains) { drawSegment2d(ctx, a_, b_); } else { // need to draw complementary segment // find t so that s(t) is at radius r from center, |s(t)-m| = r m.x = ctx->dim->center_x; m.y = ctx->dim->center_y; r = ctx->dim->radius; // equation is coeff2 t^2 + 2 coeff1 t + coeff0 = 0 coeff0 = (a_.x - m.x)*(a_.x - m.x) + (a_.y - m.y)*(a_.y - m.y) - r*r; coeff1 = (a_.x - m.x)*(b_.x - a_.x) + (a_.y - m.y)*(b_.y - a_.y); coeff2 = (b_.x - a_.x)*(b_.x - a_.x) + (b_.y - a_.y)*(b_.y - a_.y); if(coeff1*coeff1 - coeff0*coeff2 <= 0) return; tplus = (- coeff1 + sqrt(coeff1*coeff1 - coeff0*coeff2))/coeff2; tminus = (- coeff1 - sqrt(coeff1*coeff1 - coeff0*coeff2))/coeff2; xplus.x = a_.x + tplus * (b_.x - a_.x); xplus.y = a_.y + tplus * (b_.y - a_.y); xminus.x = a_.x + tminus * (b_.x - a_.x); xminus.y = a_.y + tminus * (b_.y - a_.y); if(tplus > 1) drawSegment2d(ctx, b_, xplus); if(tminus < 0) drawSegment2d(ctx, a_, xminus); } } // 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); } void drawRotationOrbitFrame(DrawingContext *ctx, gsl_matrix *frame, vector_t start) { vector_t v[3], w; point_t p; double parameter, startangle; int iterations = 200; gsl_matrix *inverse = getTempMatrix(ctx->ws); gsl_vector *start_v = getTempVector(ctx->ws); gsl_vector *start_in_frame = getTempVector(ctx->ws); cairo_t *C = ctx->cairo; // computeRotationMatrix(ctx, frame, word); LOOP(i) LOOP(j) v[i].x[j] = gsl_matrix_get(frame, j, i); LOOP(i) gsl_vector_set(start_v, i, start.x[i]); solve(frame, start_v, start_in_frame, ctx->ws); parameter = gsl_vector_get(start_in_frame, 2); parameter /= sqrt(gsl_vector_get(start_in_frame, 0)*gsl_vector_get(start_in_frame, 0) + gsl_vector_get(start_in_frame, 1)*gsl_vector_get(start_in_frame, 1)); startangle = atan2(gsl_vector_get(start_in_frame, 1), gsl_vector_get(start_in_frame, 0)); int previous_inside = 0; for(int k = 0; k <= iterations; k++) { LOOP(i) w.x[i] = parameter * v[2].x[i] + cos(2*k*M_PI/iterations) * v[0].x[i] + sin(2*k*M_PI/iterations) * v[1].x[i]; p = vectorToPoint(ctx, w); if(isInsideBB(ctx, p)) { if(!previous_inside) cairo_move_to(C, p.x, p.y); else cairo_line_to(C, p.x, p.y); previous_inside = 1; } else { previous_inside = 0; } } cairo_stroke(C); releaseTempMatrices(ctx->ws, 1); releaseTempVectors(ctx->ws, 2); } void drawRotationOrbit(DrawingContext *ctx, const char *word, vector_t start) { gsl_matrix *frame = getTempMatrix(ctx->ws); computeRotationMatrix(ctx, frame, word); drawRotationOrbitFrame(ctx, frame, start); releaseTempMatrices(ctx->ws, 1); } void drawDualRotationOrbit(DrawingContext *ctx, const char *word, vector_t start) { vector_t v[3], w; point_t p; double parameter, startangle; int iterations = 200; gsl_matrix *frame = getTempMatrix(ctx->ws); gsl_matrix *inverse = getTempMatrix(ctx->ws); gsl_vector *start_v = getTempVector(ctx->ws); gsl_vector *start_in_frame = getTempVector(ctx->ws); cairo_t *C = ctx->cairo; computeRotationMatrix(ctx, frame, word); LOOP(i) LOOP(j) v[i].x[j] = gsl_matrix_get(frame, j, i); LOOP(i) gsl_vector_set(start_v, i, start.x[i]); // solve(frame, start_v, start_in_frame, ctx->ws); gsl_blas_dgemv(CblasTrans, 1.0, frame, start_v, 0.0, start_in_frame); parameter = sqrt(gsl_vector_get(start_in_frame, 0)*gsl_vector_get(start_in_frame, 0) + gsl_vector_get(start_in_frame, 1)*gsl_vector_get(start_in_frame, 1)); parameter /= gsl_vector_get(start_in_frame, 2); startangle = atan2(gsl_vector_get(start_in_frame, 1), gsl_vector_get(start_in_frame, 0)); int previous_inside = 0; for(int k = 0; k <= iterations; k++) { LOOP(i) w.x[i] = parameter * v[2].x[i] + cos(2*k*M_PI/iterations) * v[0].x[i] + sin(2*k*M_PI/iterations) * v[1].x[i]; p = vectorToPoint(ctx, w); if(isInsideBB(ctx, p)) { if(!previous_inside) cairo_move_to(C, p.x, p.y); else cairo_line_to(C, p.x, p.y); previous_inside = 1; } else { previous_inside = 0; } } cairo_stroke(C); releaseTempMatrices(ctx->ws, 2); releaseTempVectors(ctx->ws, 2); } void drawArcWithOutput(DrawingContext *ctx, const char *word, vector_t start, vector_type_t starttype, vector_t end, vector_type_t endtype, vector_t third, int contain, vector_t *start_vector_out, vector_t *end_vector_out, int dontdraw) { vector_t v[3], w, w_; point_t p, p_; double radius, angle_start, angle_end, angle_third, angle, angle_end_delta, sign, angle_start_final, angle_end_final, angle_end_other; int iterations = 200; gsl_matrix *frame = getTempMatrix(ctx->ws); gsl_matrix *inverse = getTempMatrix(ctx->ws); gsl_vector *vector = getTempVector(ctx->ws); gsl_vector *vector_in_frame = getTempVector(ctx->ws); cairo_t *C = ctx->cairo; computeRotationMatrix(ctx, frame, word); LOOP(i) LOOP(j) v[i].x[j] = gsl_matrix_get(frame, j, i); LOOP(i) gsl_vector_set(vector, i, start.x[i]); if(starttype == VT_POINT) { solve(frame, vector, vector_in_frame, ctx->ws); radius = sqrt(gsl_vector_get(vector_in_frame, 0)*gsl_vector_get(vector_in_frame, 0) + gsl_vector_get(vector_in_frame, 1)*gsl_vector_get(vector_in_frame, 1)); radius /= fabs(gsl_vector_get(vector_in_frame, 2)); angle_start = atan2(gsl_vector_get(vector_in_frame, 1)/gsl_vector_get(vector_in_frame, 2), gsl_vector_get(vector_in_frame, 0)/gsl_vector_get(vector_in_frame, 2)); } else { gsl_blas_dgemv(CblasTrans, 1.0, frame, vector, 0.0, vector_in_frame); radius = fabs(gsl_vector_get(vector_in_frame, 2)); radius /= sqrt(gsl_vector_get(vector_in_frame, 0)*gsl_vector_get(vector_in_frame, 0) + gsl_vector_get(vector_in_frame, 1)*gsl_vector_get(vector_in_frame, 1)); angle_start = atan2(gsl_vector_get(vector_in_frame, 1)/gsl_vector_get(vector_in_frame, 2), gsl_vector_get(vector_in_frame, 0)/gsl_vector_get(vector_in_frame, 2)); } LOOP(i) gsl_vector_set(vector, i, third.x[i]); solve(frame, vector, vector_in_frame, ctx->ws); angle_third = atan2(gsl_vector_get(vector_in_frame, 1)/gsl_vector_get(vector_in_frame, 2), gsl_vector_get(vector_in_frame, 0)/gsl_vector_get(vector_in_frame, 2)); LOOP(i) gsl_vector_set(vector, i, end.x[i]); if(endtype == VT_POINT) { solve(frame, vector, vector_in_frame, ctx->ws); angle_end = atan2(gsl_vector_get(vector_in_frame, 1)/gsl_vector_get(vector_in_frame, 2), gsl_vector_get(vector_in_frame, 0)/gsl_vector_get(vector_in_frame, 2)); } else { gsl_blas_dgemv(CblasTrans, 1.0, frame, vector, 0.0, vector_in_frame); // this is only the average angle angle_end = atan2(gsl_vector_get(vector_in_frame, 1)/gsl_vector_get(vector_in_frame, 2), gsl_vector_get(vector_in_frame, 0)/gsl_vector_get(vector_in_frame, 2)); angle_end_delta = acos(-fabs(gsl_vector_get(vector_in_frame, 2))/radius/ sqrt(gsl_vector_get(vector_in_frame, 0)*gsl_vector_get(vector_in_frame, 0) + gsl_vector_get(vector_in_frame, 1)*gsl_vector_get(vector_in_frame, 1))); } int previous_inside = 0; for(int i = 0; i < 4; i++) { angle_start_final = angle_start; if(endtype == VT_POINT) { angle_end_final = angle_end; } else { if(i >= 2) { angle_end_final = angle_end - angle_end_delta; angle_end_other = angle_end + angle_end_delta; } else { angle_end_final = angle_end + angle_end_delta; angle_end_other = angle_end - angle_end_delta; } } if(i%2) FLIP(angle_start_final, angle_end_final); if(endtype == VT_LINE && ANGLE_IN_INTERVAL(angle_start_final, angle_end_final, angle_end_other)) continue; if(contain && !ANGLE_IN_INTERVAL(angle_start_final, angle_end_final, angle_third)) continue; if(!contain && ANGLE_IN_INTERVAL(angle_start_final, angle_end_final, angle_third)) continue; break; } // output the start end end point /* LOOP(i) w.x[i] = v[2].x[i] / radius + cos(angle_start_final) * v[0].x[i] + sin(angle_start_final) * v[1].x[i]; p = vectorToPoint(ctx, w); LOOP(i) w.x[i] = v[2].x[i] / radius + cos(angle_end_final) * v[0].x[i] + sin(angle_end_final) * v[1].x[i]; p_ = vectorToPoint(ctx, w); printf("\\draw (%f,%f) -- (%f,%f);\n", p.x, p.y, p_.x, p_.y); */ if(!dontdraw) { for(int k = 0; k <= iterations; k++) { angle = angle_start_final + (double)k/(double)iterations * ANGLE_DIFF(angle_end_final, angle_start_final); LOOP(i) w.x[i] = v[2].x[i] / radius + cos(angle) * v[0].x[i] + sin(angle) * v[1].x[i]; p = vectorToPoint(ctx, w); if(isInsideBB(ctx, p)) { if(!previous_inside) cairo_move_to(C, p.x, p.y); else cairo_line_to(C, p.x, p.y); previous_inside = 1; } else { previous_inside = 0; } } } if(start_vector_out) LOOP(i) start_vector_out->x[i] = v[2].x[i] / radius + cos(angle_start_final) * v[0].x[i] + sin(angle_start_final) * v[1].x[i]; if(end_vector_out) LOOP(i) end_vector_out->x[i] = v[2].x[i] / radius + cos(angle_end_final) * v[0].x[i] + sin(angle_end_final) * v[1].x[i]; cairo_stroke(C); releaseTempMatrices(ctx->ws, 2); releaseTempVectors(ctx->ws, 2); } void drawArc(DrawingContext *ctx, const char *word, vector_t start, vector_type_t starttype, vector_t end, vector_type_t endtype, vector_t third, int contain) { drawArcWithOutput(ctx, word, start, starttype, end, endtype, third, contain, 0, 0, 0); } // 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) { int n = 3; vector_t p[6][3]; vector_t l[6][3]; fixedPoints(ctx, "abc", p[0]); fixedPoints(ctx, "bca", p[1]); fixedPoints(ctx, "cab", p[2]); fixedPoints(ctx, "a cab a", p[3]); fixedPoints(ctx, "b abc b", p[4]); fixedPoints(ctx, "c bca c", p[5]); double color[6][3] = {{1,0,0},{0,0.7,0},{0,0,1},{0,1,1},{0,1,1},{0,1,1}}; for(int i = 0; i < n; i++) LOOP(j) l[i][j] = cross(p[i][(3-j)%3], p[i][(4-j)%3]); for(int i = 0; i < n; i++) LOOP(j) { cairo_set_source_rgb(ctx->cairo, color[i][0], color[i][1], color[i][2]); drawVector(ctx, p[i][j]); } for(int i = 0; i < n; i++) LOOP(j) { cairo_set_source_rgb(ctx->cairo, color[i][0], color[i][1], color[i][2]); drawCovector(ctx, l[i][j]); } } char *conjugate_word(const char *word, int modifier, const char *conj, char *buffer) { int wordlen = strlen(word); int conjlen = strlen(conj); for(int i = 0; i < conjlen; i++) { buffer[i] = conj[i]; buffer[2*conjlen+wordlen-1-i] = conj[i]; } for(int i = 0; i < wordlen; i++) { if(word[i] == ' ') buffer[conjlen+i] = word[i]; else buffer[conjlen+i] = (word[i]+modifier-'a')%3 + 'a'; } buffer[2*conjlen + wordlen] = 0; return buffer; } void drawCurvedBox(DrawingContext *ctx, int base, const char *conj, int style) { vector_t p[11][3]; vector_t l[2][3]; vector_t corner1, corner2; vector_t tmp1, tmp2; char word[100]; int modifier = base - 'A'; conjugate_word("abc", modifier, conj, word); fixedPoints(ctx, word, p[0]); conjugate_word("bca", modifier, conj, word); fixedPoints(ctx, word, p[1]); conjugate_word("b abc b", modifier, conj, word); fixedPoints(ctx, word, p[2]); conjugate_word("ab abc ba", modifier, conj, word); fixedPoints(ctx, word, p[3]); conjugate_word("baca cab acab", modifier, conj, word); fixedPoints(ctx, word, p[4]); conjugate_word("abaca cab acaba", modifier, conj, word); fixedPoints(ctx, word, p[5]); conjugate_word("bca cab acb", modifier, conj, word); fixedPoints(ctx, word, p[6]); conjugate_word("abca cab acba", modifier, conj, word); fixedPoints(ctx, word, p[7]); conjugate_word("abacababa", modifier, conj, word); fixedPoints(ctx, word, p[8]); conjugate_word("bacabab", modifier, conj, word); fixedPoints(ctx, word, p[9]); conjugate_word("cab", modifier, conj, word); fixedPoints(ctx, word, p[10]); LOOP(j) l[0][j] = cross(p[0][(3-j)%3], p[0][(4-j)%3]); LOOP(j) l[1][j] = cross(p[1][(3-j)%3], p[1][(4-j)%3]); LOOP(j) l[10][j] = cross(p[10][(3-j)%3], p[10][(4-j)%3]); // main conic conjugate_word("ab", modifier, conj, word); drawArcWithOutput(ctx, word, p[0][0], VT_POINT, p[2][0], VT_POINT, p[1][0], 0, &tmp1, &tmp2, style != 1); if(style == 2) drawSegmentWith(ctx, tmp1, tmp2, l[10][0], 0); if(style == 3) { drawVector(ctx, tmp1); drawVector(ctx, tmp2); } conjugate_word("ab", modifier, conj, word); drawArcWithOutput(ctx, word, p[1][0], VT_POINT, p[3][0], VT_POINT, p[0][0], 0, &tmp1, &tmp2, style != 1); if(style == 2) drawSegmentWith(ctx, tmp1, tmp2, l[10][0], 0); if(style == 3) { drawVector(ctx, tmp1); drawVector(ctx, tmp2); } conjugate_word("bcabcb", modifier, conj, word); drawArcWithOutput(ctx, word, p[2][0], VT_POINT, l[1][0], VT_LINE, p[6][0], 1, &tmp1, &tmp2, style != 1); // only 1st cutoff if(style == 2) drawSegmentWith(ctx, tmp1, tmp2, l[10][0], 0); corner1 = tmp2; conjugate_word("abcabcba", modifier, conj, word); drawArcWithOutput(ctx, word, p[3][0], VT_POINT, l[0][0], VT_LINE, p[7][0], 1, &tmp1, &tmp2, style != 1); // only 1st cutoff if(style == 2) drawSegmentWith(ctx, tmp1, tmp2, l[10][0], 0); corner2 = tmp2; if(style == 1 || style == 2) { drawSegmentWith(ctx, p[0][0], corner2, l[1][1], 0); drawSegmentWith(ctx, p[1][0], corner1, l[0][1], 0); } else if(style == 3) { drawVector(ctx, corner1); drawVector(ctx, corner2); } } void drawBoxes(DrawingContext *ctx) { gsl_matrix *rot = getTempMatrix(ctx->ws); gsl_matrix **gen = getTempMatrices(ctx->ws, 3); gsl_matrix *frame = getTempMatrix(ctx->ws); cairo_t *C = ctx->cairo; cairo_save(C); vector_t p[22][3]; vector_t l[22][3]; vector_t alpha[6]; vector_t ptmp[3]; char word[100], word2[100]; fixedPoints(ctx, "abc", p[0]); fixedPoints(ctx, "bca", p[1]); fixedPoints(ctx, "cab", p[2]); fixedPoints(ctx, "bacabab", p[3]); fixedPoints(ctx, "bcacabacb", p[4]); cairo_set_line_width(C, 2.0/ctx->dim->scalefactor); cairo_set_source_rgb(C, 0.6, 0.6, 0.6); drawRotationOrbit(ctx, "ab", p[0][0]); drawRotationOrbit(ctx, "bc", p[0][0]); drawRotationOrbit(ctx, "ca", p[0][0]); if(ctx->mode >= 2) { drawRotationOrbit(ctx, "bcabcb", p[1][0]); // bcC drawRotationOrbit(ctx, "abcabcba", p[0][0]); // abcC } cairo_set_source_rgb(C, 0, 0, 0); fixedPoints(ctx, "abababcbaba", p[3]); cairo_set_source_rgb(C, 0, 0, 1); fixedPoints(ctx, "bc bca cb", p[3]); cairo_restore(C); releaseTempMatrices(ctx->ws, 5); } void drawBoxes2(DrawingContext *ctx) { gsl_matrix *rot = getTempMatrix(ctx->ws); gsl_matrix **gen = getTempMatrices(ctx->ws, 3); cairo_t *C = ctx->cairo; cairo_save(C); initializeTriangleGenerators(gen, ctx->cartan); vector_t p[4][3]; fixedPoints(ctx, "abc", p[0]); fixedPoints(ctx, "bca", p[1]); fixedPoints(ctx, "cab", p[2]); cairo_set_line_width(C, 2.5/ctx->dim->scalefactor); cairo_set_source_rgb(C, 0, 0, 0); drawCurvedBox(ctx, 'A', "", 2); if(ctx->mode >= 3 && ctx->mode != 4) { cairo_set_source_rgb(C, 0, 0, 0); drawCurvedBox(ctx, 'B', "", 2); drawCurvedBox(ctx, 'C', "", 2); } if(ctx->mode == 4 || ctx->mode == 5) { cairo_set_source_rgb(C, 0, 0.8, 0.5); drawCurvedBox(ctx, 'A', "", 2); drawCurvedBox(ctx, 'A', "bc", 2); drawCurvedBox(ctx, 'A', "bcbc", 2); drawCurvedBox(ctx, 'A', "bcbcbc", 2); drawCurvedBox(ctx, 'A', "bcbcbcbc", 2); drawCurvedBox(ctx, 'A', "bcbcbcbcbc", 2); drawCurvedBox(ctx, 'A', "bcbcbcbcbcbc", 2); } if(ctx->mode == 6) { cairo_set_source_rgb(C, 0, 0.8, 0.5); drawCurvedBox(ctx, 'C', "abababab", 2); drawCurvedBox(ctx, 'C', "ababababab", 2); } if(ctx->mode >= 6) { cairo_set_source_rgb(C, 0, 0.8, 0.5); drawCurvedBox(ctx, 'C', "ab", 2); drawCurvedBox(ctx, 'C', "abab", 2); } if(ctx->mode >= 8) { cairo_set_source_rgb(C, 0, 0.8, 0.5); drawCurvedBox(ctx, 'C', "b", 2); drawCurvedBox(ctx, 'C', "bab", 2); } if(ctx->mode >= 9) { cairo_set_source_rgb(C, 0.8, 0, 0.5); drawCurvedBox(ctx, 'B', "abca", 2); drawCurvedBox(ctx, 'B', "abcaca", 2); drawCurvedBox(ctx, 'B', "aba", 2); drawCurvedBox(ctx, 'B', "abaca", 2); drawCurvedBox(ctx, 'B', "ababca", 2); drawCurvedBox(ctx, 'B', "ababcaca", 2); drawCurvedBox(ctx, 'B', "ababa", 2); drawCurvedBox(ctx, 'B', "ababaca", 2); drawCurvedBox(ctx, 'B', "bca", 2); drawCurvedBox(ctx, 'B', "bcaca", 2); drawCurvedBox(ctx, 'B', "ba", 2); drawCurvedBox(ctx, 'B', "baca", 2); drawCurvedBox(ctx, 'B', "babca", 2); drawCurvedBox(ctx, 'B', "babcaca", 2); drawCurvedBox(ctx, 'B', "baba", 2); drawCurvedBox(ctx, 'B', "babaca", 2); cairo_restore(C); releaseTempMatrices(ctx->ws, 4); } } void drawRotatedReflectors(DrawingContext *ctx) { gsl_matrix *rot = getTempMatrix(ctx->ws); gsl_matrix **gen = getTempMatrices(ctx->ws, 3); cairo_t *C = ctx->cairo; vector_t fp[3], fp2[3]; vector_t w; vector_t v[3]; cairo_set_source_rgb(C, 0.7, 0.7, 0.7); initializeTriangleGenerators(gen, ctx->cartan); LOOP(i) LOOP(j) v[i].x[j] = gsl_matrix_get(ctx->cartan, i, j); multiply(gen[0], gen[1], rot); for(int j = 0; j < ctx->p[2]; j++) { drawCovector(ctx, v[0]); v[0] = apply_transpose(rot, v[0]); } LOOP(i) LOOP(j) { v[i].x[j] = (i==j) ? 1.0 : 0.0; } for(int j = 0; j < ctx->p[2]; j++) { drawVector(ctx, v[0]); v[0] = apply(rot, v[0]); } fixedPoints(ctx, "cab", fp); fixedPoints(ctx, "cacabac", fp2); drawRotationOrbit(ctx, "ac", fp[0]); releaseTempMatrices(ctx->ws, 4); } void drawDualLimitCurve(DrawingContext *ctx) { cairo_t *C = ctx->cairo; cairo_save(C); cairo_set_source_rgb(C, 0.5, 0.5, 1); int n = 18; vector_t p[n][3]; vector_t l[n][3]; vector_t ptmp[3], ltmp[3]; fixedPoints(ctx, "abc", p[0]); fixedPoints(ctx, "ab abc ba", p[1]); fixedPoints(ctx, "abab abc baba", p[2]); fixedPoints(ctx, "ababab abc bababa", p[3]); fixedPoints(ctx, "abababab abc babababa", p[4]); fixedPoints(ctx, "babababa abc abababab", p[5]); fixedPoints(ctx, "bababa abc ababab", p[6]); fixedPoints(ctx, "baba abc abab", p[7]); fixedPoints(ctx, "ba abc ab", p[8]); fixedPoints(ctx, "bca", p[9]); fixedPoints(ctx, "ab bca ba", p[10]); fixedPoints(ctx, "abab bca baba", p[11]); fixedPoints(ctx, "ababab bca bababa", p[12]); fixedPoints(ctx, "abababab bca babababa", p[13]); fixedPoints(ctx, "babababa bca abababab", p[14]); fixedPoints(ctx, "bababa bca ababab", p[15]); fixedPoints(ctx, "baba bca abab", p[16]); fixedPoints(ctx, "ba bca ab", p[17]); /* fixedPoints(ctx, "abc", p[0]); fixedPoints(ctx, "ac abc ca", p[1]); fixedPoints(ctx, "acac abc caca", p[2]); fixedPoints(ctx, "acacac abc cacaca", p[3]); fixedPoints(ctx, "acacacac abc cacacaca", p[4]); fixedPoints(ctx, "cacacaca abc acacacac", p[5]); fixedPoints(ctx, "cacaca abc acacac", p[6]); fixedPoints(ctx, "caca abc acac", p[7]); fixedPoints(ctx, "ca abc ac", p[8]); fixedPoints(ctx, "bca", p[9]); fixedPoints(ctx, "ac bca ca", p[10]); fixedPoints(ctx, "acac bca caca", p[11]); fixedPoints(ctx, "acacac bca cacaca", p[12]); fixedPoints(ctx, "acacacac bca cacacaca", p[13]); fixedPoints(ctx, "cacacaca bca acacacac", p[14]); fixedPoints(ctx, "cacaca bca acacac", p[15]); fixedPoints(ctx, "caca bca acac", p[16]); fixedPoints(ctx, "ca bca ac", p[17]); */ /* fixedPoints(ctx, "cab", p[2]); fixedPoints(ctx, "b abc b", p[3]); fixedPoints(ctx, "c bca c", p[4]); fixedPoints(ctx, "a cab a", p[5]); */ /* for(int i = 0; i < n; i++) { LOOP(j) l[i][j] = cross(p[i][(3-j)%3], p[i][(4-j)%3]); drawCovector(ctx, l[i][0]); // drawCovector(ctx, l[i][2]); }*/ fixedPoints(ctx, "abc", ptmp); drawCovector(ctx, cross(ptmp[0], ptmp[1])); fixedPoints(ctx, "bca", ptmp); drawCovector(ctx, cross(ptmp[0], ptmp[1])); fixedPoints(ctx, "cab", ptmp); drawCovector(ctx, cross(ptmp[0], ptmp[1])); fixedPoints(ctx, "babcb", ptmp); drawCovector(ctx, cross(ptmp[0], ptmp[1])); fixedPoints(ctx, "cbcac", ptmp); drawCovector(ctx, cross(ptmp[0], ptmp[1])); fixedPoints(ctx, "acaba", ptmp); drawCovector(ctx, cross(ptmp[0], ptmp[1])); cairo_restore(C); } void drawLimitCurve(DrawingContext *ctx) { int close_points = 0; cairo_t *C = ctx->cairo; cairo_save(C); // cairo_set_source_rgb(C, 0.6, 0.6, 0.6); cairo_set_source_rgb(C, 0, 0, 0); if(ctx->limit_with_lines) { int previous_inside = 0; cairo_new_path(C); for(int i = 0; i < ctx->limit_curve_count; i++) { point_t p; p.x = ctx->limit_curve[3*i]; p.y = ctx->limit_curve[3*i+1]; if(isInsideBB(ctx, p)) { if(!previous_inside) cairo_move_to(C, p.x, p.y); else cairo_line_to(C, p.x, p.y); previous_inside = 1; } else { previous_inside = 0; } } cairo_stroke(C); } else { for(int i = 0; i < ctx->limit_curve_count; i++) { point_t p; p.x = ctx->limit_curve[3*i]; p.y = ctx->limit_curve[3*i+1]; if(isInsideBB(ctx, p)) { cairo_arc(C, p.x, p.y, 2.0/ctx->dim->scalefactor, 0, 2*M_PI); cairo_close_path(C); cairo_fill(C); } } } cairo_restore(C); } void drawCoxeterOrbit(DrawingContext *ctx) { gsl_matrix *rot = getTempMatrix(ctx->ws); gsl_matrix **gen = getTempMatrices(ctx->ws, 3); gsl_vector *eval = getTempVector(ctx->ws); gsl_matrix *coxeter_fixedpoints = getTempMatrix(ctx->ws); gsl_vector *startpoint_coxeterbasis = getTempVector(ctx->ws); gsl_vector *startpoint_globalbasis = getTempVector(ctx->ws); gsl_vector *startpoint_drawbasis = getTempVector(ctx->ws); gsl_matrix **elements = getTempMatrices(ctx->ws, ctx->n_group_elements); cairo_t *C = ctx->cairo; vector_t cox[3][3]; vector_t abcb[3]; double ev[3]; vector_t v, start; point_t p; int first = 1; cairo_save(C); initializeTriangleGenerators(gen, ctx->cartan); cairo_set_source_rgb(C, 0, 0, 1); fixedPoints(ctx, "abc", cox[0]); fixedPoints(ctx, "bca", cox[1]); fixedPoints(ctx, "cab", cox[2]); // fixedPoints(ctx, "babc", abcb); wordEigenvalues(ctx, "abc", ev); LOOP(i) LOOP(j) gsl_matrix_set(coxeter_fixedpoints, j, i, cox[0][i].x[j]); initializeTriangleGenerators(gen, ctx->cartan); gsl_matrix_set_identity(elements[0]); for(int i = 1; i < ctx->n_group_elements; i++) multiply(gen[ctx->group[i].letter], elements[ctx->group[i].parent->id], elements[i]); //printf("coxeter eigenvalues: %f %f %f\n", ev[0], ev[1], ev[2]); // LOOP(i) gsl_vector_set(startpoint_globalbasis, i, cox[1][0].x[i]); gsl_vector_set(startpoint_drawbasis, 0, ctx->marking.x); gsl_vector_set(startpoint_drawbasis, 1, ctx->marking.y); gsl_vector_set(startpoint_drawbasis, 2, 1); solve(ctx->cob, startpoint_drawbasis, startpoint_globalbasis, ctx->ws); solve(coxeter_fixedpoints, startpoint_globalbasis, startpoint_coxeterbasis, ctx->ws); // LOOP(i) start.x[i] = gsl_vector_get(startpoint_coxeterbasis, i); LOOP(i) start.x[i] = gsl_vector_get(startpoint_globalbasis, i); /* for(int t = -1000; t < 1000; t++) { LOOP(i) v.x[i] = 0; LOOP(i) LOOP(j) v.x[j] += pow(fabs(ev[i]),t*0.01)*start.x[i]*cox[0][i].x[j]; p = vectorToPoint(ctx, v); if(first) { cairo_move_to(C, p.x, p.y); first = 0; } else { cairo_line_to(C, p.x, p.y); } } cairo_stroke(C); */ for(int i = 0; i < ctx->n_group_elements; i++) { v = apply(elements[i], start); drawVector(ctx, v); // ctx->limit_curve[3*i+2] = atan2( // gsl_matrix_get(fixedpoints_pos, 2, column)/gsl_matrix_get(fixedpoints_pos, 0, column), // gsl_matrix_get(fixedpoints_pos, 1, column)/gsl_matrix_get(fixedpoints_pos, 0, column)); } /* for(int t = -20; t < 20; t++) { LOOP(i) v.x[i] = 0; LOOP(i) LOOP(j) v.x[j] += (ev[i]<0&&t%2?-1:1)*pow(fabs(ev[i]),t/3.0)*start.x[i]*cox[0][i].x[j]; drawVector(ctx, v); }*/ cairo_set_source_rgb(C, 0, 0, 0); // LOOP(i) drawVector(ctx, abcb[i]); cairo_restore(C); releaseTempMatrices(ctx->ws, 5 + ctx->n_group_elements); releaseTempVectors(ctx->ws, 4); } 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, marking = (%.5f, %.5f)", log(ctx->parameter), ctx->parameter, ctx->marking.x, ctx->marking.y); 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_count >= 0) { if(ctx->show_limit) drawLimitCurve(ctx); if(ctx->show_dual_limit) drawDualLimitCurve(ctx); if(ctx->show_attractors) drawAttractors(ctx); if(ctx->show_rotated_reflectors) drawRotatedReflectors(ctx); if(ctx->show_reflectors) drawReflectors(ctx); if(ctx->show_boxes) drawBoxes(ctx); if(ctx->show_boxes2) drawBoxes2(ctx); if(ctx->show_marking) { cairo_set_source_rgb(C, 0, 0, 1); // drawPoint(ctx, ctx->marking); } if(ctx->show_coxeter_orbit) drawCoxeterOrbit(ctx); } cairo_identity_matrix(C); // text is in screen coordinates if(ctx->show_text) // drawText(ctx); cairo_surface_flush(cairo_get_target(C)); }