triangle_group_limit_set/draw.c

408 lines
9.6 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);
gsl_vector *tmp2 = getTempVector(ctx);
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, 2);
}
int fixedPoints(DrawingContext *ctx, const char *word, vector_t *out)
{
gsl_matrix *tmp = getTempMatrix(ctx);
gsl_matrix *ev = getTempMatrix(ctx);
gsl_matrix **gen = getTempMatrices(ctx, 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, 5);
return count;
}
void transformFrameStd(DrawingContext *ctx, vector_t *x, gsl_matrix *out)
{
gsl_matrix *tmp = getTempMatrix(ctx);
gsl_vector *fourth = getTempVector(ctx);
gsl_vector *lambda = getTempVector(ctx);
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, 1);
releaseTempVectors(ctx, 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 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);
drawSegment(ctx, prev, current);
}
drawSegment(ctx, current, first);
va_end(args);
}
void drawTriangle(DrawingContext *ctx, const char *word)
{
vector_t p[3];
fixedPoints(ctx, word, p);
drawPolygon(ctx, 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, 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 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_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));
}