500 lines
12 KiB
C
500 lines
12 KiB
C
#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->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;
|
|
}
|
|
|
|
void transformFrameStd(DrawingContext *ctx, vector_t *x, gsl_matrix *out)
|
|
{
|
|
gsl_matrix *tmp = getTempMatrix(ctx->ws);
|
|
gsl_vector *fourth = getTempVector(ctx->ws);
|
|
gsl_vector *lambda = getTempVector(ctx->ws);
|
|
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->ws, 1);
|
|
releaseTempVectors(ctx->ws, 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 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]);
|
|
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 drawBoxes2(DrawingContext *ctx)
|
|
{
|
|
/*
|
|
cairo_set_source_rgb(ctx->cairo, 1, 0.5, 0);
|
|
drawBoxStd(ctx, "a", 'A');
|
|
drawBoxStd(ctx, "ca", 'A');
|
|
drawBoxStd(ctx, "aca", 'A');
|
|
drawBoxStd(ctx, "caca", 'A');
|
|
drawBoxStd(ctx, "acaca", 'A');
|
|
cairo_set_source_rgb(ctx->cairo, 0, 0.5, 1);
|
|
drawBoxStd(ctx, "acac", 'A');
|
|
drawBoxStd(ctx, "cac", 'A');
|
|
drawBoxStd(ctx, "ac", 'A');
|
|
drawBoxStd(ctx, "c", 'A');
|
|
drawBoxStd(ctx, "", 'A');
|
|
*/
|
|
|
|
/*
|
|
cairo_set_source_rgb(ctx->cairo, 0, 0.5, 1);
|
|
drawBox(ctx, "abc", "cab");
|
|
// drawBox(ctx, "ba abc ab", "ba cab ab");
|
|
drawBox(ctx, "baba abc abab", "baba cab abab");
|
|
drawBox(ctx, "abab abc baba", "abab cab baba");
|
|
drawBox(ctx, "ab abc ba", "ab cab ba");
|
|
|
|
cairo_set_source_rgb(ctx->cairo, 1, 0.5, 0);
|
|
drawBox(ctx, "b abc b", "b cab b");
|
|
drawBox(ctx, "bab abc bab", "bab cab bab");
|
|
drawBox(ctx, "babab abc babab", "babab cab babab");
|
|
drawBox(ctx, "aba abc aba", "aba cab aba");
|
|
// drawBox(ctx, "a abc a", "a cab a");
|
|
|
|
cairo_set_source_rgb(ctx->cairo, 0, 0.5, 1);
|
|
drawBox(ctx, "bca", "abc");
|
|
// drawBox(ctx, "cb bca bc", "cb abc bc");
|
|
drawBox(ctx, "cbcb bca bcbc", "cbcb abc bcbc");
|
|
drawBox(ctx, "bcbc bca cbcb", "bcbc abc cbcb");
|
|
drawBox(ctx, "bc bca cb", "bc abc cb");
|
|
|
|
cairo_set_source_rgb(ctx->cairo, 1, 0.5, 0);
|
|
drawBox(ctx, "c bca c", "c abc c");
|
|
drawBox(ctx, "cbc bca cbc", "cbc abc cbc");
|
|
drawBox(ctx, "cbcbc bca cbcbc", "cbcbc abc cbcbc");
|
|
drawBox(ctx, "bcb bca bcb", "bcb abc bcb");
|
|
// drawBox(ctx, "b bca b", "b abc b");
|
|
*/
|
|
|
|
// cairo_set_source_rgb(ctx->cairo, 1, 0, 0);
|
|
// drawBoxLines(ctx, "bca", "abc");
|
|
|
|
cairo_set_source_rgb(ctx->cairo, 1, 0.5, 0);
|
|
drawBoxLines(ctx, "ac cab ca", "ac bca ca");
|
|
drawBoxLines(ctx, "acac cab caca", "acac bca caca");
|
|
drawBoxLines(ctx, "caca cab acac", "caca bca acac");
|
|
drawBoxLines(ctx, "ca cab ac", "ca bca ac");
|
|
drawBoxLines(ctx, "cab", "bca");
|
|
|
|
cairo_set_source_rgb(ctx->cairo, 0, 0, 0);
|
|
drawBoxLines(ctx, "abc", "cab");
|
|
|
|
cairo_set_source_rgb(ctx->cairo, 1, 0.5, 0);
|
|
// drawBox(ctx, "a cab a", "a bca a");
|
|
// drawBox(ctx, "aca cab aca", "aca bca aca");
|
|
// drawBox(ctx, "acaca cab acaca", "acaca bca acaca");
|
|
// drawBox(ctx, "cac cab cac", "cac bca cac");
|
|
// drawBox(ctx, "c cab c", "c bca c");
|
|
|
|
}
|
|
|
|
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));
|
|
}
|