triangle_group_limit_set/limit_set.c

354 lines
9.6 KiB
C

#include <math.h>
#include <stdio.h>
#include <sys/time.h>
#include <X11/XKBlib.h>
#include "initcairo.h"
#include "triangle.h"
#include "linalg.h"
#define LOOP(i) for(int i = 0; i < 3; i++)
typedef struct {
double x;
double y;
} point_t;
int processEvent(GraphicsInfo *info, XEvent *ev, void *data);
void setup();
void destroy();
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);
point_t intersect(point_t a, point_t b, point_t c, point_t d);
GraphicsInfo G;
double parameter = 2.8;
int n_group_elements;
double *limit_curve; // x, y, angle triples
int limit_curve_valid = 0;
groupelement_t *group;
workspace_t *ws;
gsl_matrix *gen[3];
gsl_matrix **matrices;
gsl_matrix *cartan, *cob, *coxeter, *coxeter_fixedpoints, *fixedpoints;
int processEvent(GraphicsInfo *info, XEvent *ev, void *data)
{
int state;
unsigned long key;
switch(ev->type) {
case KeyPress:
state = ev->xkey.state & (ShiftMask | LockMask | ControlMask);
key = XkbKeycodeToKeysym(ev->xkey.display, ev->xkey.keycode, 0, !!(state & ShiftMask));
printf("Key pressed: %ld\n", key);
switch(key) {
case '<':
parameter /= exp(0.002);
limit_curve_valid = computeLimitCurve();
break;
case '>':
parameter *= exp(0.002);
limit_curve_valid = computeLimitCurve();
break;
case ' ':
parameter = 2.890053638;
limit_curve_valid = computeLimitCurve();
break;
case XK_Return:
parameter = 2.76375163;
limit_curve_valid = computeLimitCurve();
break;
}
draw(0);
}
return 0;
}
void setup()
{
n_group_elements = 10000;
limit_curve = malloc(3*n_group_elements*sizeof(double));
group = malloc(n_group_elements*sizeof(groupelement_t));
ws = workspace_alloc(3);
LOOP(i) gen[i] = gsl_matrix_alloc(3, 3);
matrices = malloc(n_group_elements*sizeof(gsl_matrix*));
for(int i = 0; i < n_group_elements; i++)
matrices[i] = gsl_matrix_alloc(3, 3);
cartan = gsl_matrix_alloc(3, 3);
cob = gsl_matrix_alloc(3, 3);
coxeter = gsl_matrix_alloc(3, 3);
coxeter_fixedpoints = gsl_matrix_alloc(3, 3);
fixedpoints = gsl_matrix_alloc(3, 3);
}
void destroy()
{
free(limit_curve);
free(group);
workspace_free(ws);
LOOP(i) gsl_matrix_free(gen[i]);
for(int i = 0; i < n_group_elements; i++)
gsl_matrix_free(matrices[i]);
free(matrices);
gsl_matrix_free(cartan);
gsl_matrix_free(cob);
gsl_matrix_free(coxeter);
gsl_matrix_free(coxeter_fixedpoints);
gsl_matrix_free(fixedpoints);
}
void cartanMatrix(gsl_matrix *cartan, double a1, double a2, double a3, double s)
{
gsl_matrix_set(cartan, 0, 0, -2);
gsl_matrix_set(cartan, 0, 1, 2*s*cos(a3));
gsl_matrix_set(cartan, 0, 2, 2/s*cos(a2));
gsl_matrix_set(cartan, 1, 0, 2/s*cos(a3));
gsl_matrix_set(cartan, 1, 1, -2);
gsl_matrix_set(cartan, 1, 2, 2*s*cos(a1));
gsl_matrix_set(cartan, 2, 0, 2*s*cos(a2));
gsl_matrix_set(cartan, 2, 1, 2/s*cos(a1));
gsl_matrix_set(cartan, 2, 2, -2);
}
void initializeTriangleGenerators(gsl_matrix **gen, gsl_matrix *cartan)
{
LOOP(i) gsl_matrix_set_identity(gen[i]);
LOOP(i) LOOP(j) *gsl_matrix_ptr(gen[i], i, j) += gsl_matrix_get(cartan, i, j);
}
int compareAngle(const void *x, const void *y)
{
return ((double*)x)[2] > ((double*)y)[2] ? 1 : -1;
}
// intersect the lines a-b and c-d
point_t intersect(point_t a, point_t b, point_t c, point_t d)
{
point_t res;
double t = ((c.y-d.y)*(c.x-a.x) - (c.x-d.x)*(c.y-a.y)) / ((b.x-a.x)*(c.y-d.y) - (b.y-a.y)*(c.x-d.x));
res.x = (1-t)*a.x + t*b.x;
res.y = (1-t)*a.y + t*b.y;
return res;
}
void draw_segment(point_t a, point_t b)
{
cairo_move_to(G.ctx, a.x, a.y);
cairo_line_to(G.ctx, b.x, b.y);
cairo_stroke(G.ctx);
}
void draw_polygon(int sides, point_t a, point_t b, point_t c, point_t d) // only quadrilaterals so far
{
draw_segment(a, b);
draw_segment(b, c);
draw_segment(c, d);
draw_segment(d, a);
}
void draw_box(const char *word, char base)
{
// 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
gsl_matrix_set_identity(conj);
gsl_matrix_set_identity(conjinv);
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);
}
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);
}
point_t p_a[3],p_m[3],p_r[3],p_i[4];
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);
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]);
}
int computeLimitCurve()
{
int p = 5, q = 5, r = 5;
int k = 2, l = 2, m = 2;
generate_triangle_group(group, n_group_elements, p, q, r);
// do first in the Fuchsian case to get the angles
cartanMatrix(cartan, M_PI/p, M_PI/q, M_PI/r, 1.0);
initializeTriangleGenerators(gen, cartan);
gsl_matrix_set_identity(matrices[0]);
for(int i = 1; i < n_group_elements; i++)
multiply(matrices[group[i].parent->id], gen[group[i].letter], matrices[i]);
diagonalize_symmetric_form(cartan, cob, ws);
multiply_many(ws, coxeter, 3, gen[0], gen[1], gen[2]);
if(!eigenvectors(coxeter, coxeter_fixedpoints, ws))
return 0;
for(int i = 0; i < n_group_elements; i++) {
multiply_many(ws, fixedpoints, 3, cob, matrices[i], coxeter_fixedpoints);
limit_curve[3*i+2] = atan2(
gsl_matrix_get(fixedpoints, 0, 0)/gsl_matrix_get(fixedpoints, 2, 0),
gsl_matrix_get(fixedpoints, 1, 0)/gsl_matrix_get(fixedpoints, 2, 0));
}
// now to it again to calculate x and y coordinates
cartanMatrix(cartan, M_PI*k/p, M_PI*l/q, M_PI*m/r, parameter);
initializeTriangleGenerators(gen, cartan);
gsl_matrix_set_identity(matrices[0]);
for(int i = 1; i < n_group_elements; i++)
multiply(matrices[group[i].parent->id], gen[group[i].letter], matrices[i]);
gsl_matrix_memcpy(cob, cartan); // is this a good choice of basis
// gsl_matrix_set_identity(cob);
multiply_many(ws, coxeter, 3, gen[0], gen[1], gen[2]);
if(!eigenvectors(coxeter, coxeter_fixedpoints, ws))
return 0;
for(int i = 0; i < n_group_elements; i++) {
multiply_many(ws, fixedpoints, 3, cob, matrices[i], coxeter_fixedpoints);
limit_curve[3*i] = gsl_matrix_get(fixedpoints, 0, 0)/gsl_matrix_get(fixedpoints, 2, 0);
limit_curve[3*i+1] = gsl_matrix_get(fixedpoints, 1, 0)/gsl_matrix_get(fixedpoints, 2, 0);
}
qsort(limit_curve, n_group_elements, 3*sizeof(double), compareAngle);
return 1;
}
void draw(void *data)
{
struct timeval current_time;
gettimeofday(&current_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_set_matrix(G.ctx, &G.matrix);
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(-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]);
} else {
cairo_line_to(G.ctx, limit_curve[3*i], limit_curve[3*i+1]);
}
last_inside = 1;
} else {
last_inside = 0;
}
}
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);
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');
}
cairo_identity_matrix(G.ctx);
cairo_move_to(G.ctx, 15, 30);
cairo_set_source_rgb(G.ctx, 0, 0, 0);
cairo_set_font_size(G.ctx, 16);
char buf[100];
sprintf(buf, "t = %.8f", parameter);
cairo_show_text(G.ctx, buf);
cairo_pop_group_to_source(G.ctx);
cairo_paint(G.ctx);
cairo_surface_flush(G.sfc);
gettimeofday(&current_time, 0);
double end_time = current_time.tv_sec + current_time.tv_usec*1e-6;
printf("draw() finished in %g seconds\n", end_time - start_time);
}
int main()
{
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;
startTimer(&G);
while(!checkEvents(&G, processEvent, draw, 0)) {
waitUpdateTimer(&G);
}
destroyCairo(&G);
destroy();
return 0;
}