698 lines
18 KiB
C
698 lines
18 KiB
C
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#include <cairo/cairo-pdf.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;
|
|
|
|
void cartanMatrix(gsl_matrix *cartan, double a1, double a2, double a3, double s);
|
|
void initializeTriangleGenerators(gsl_matrix **gen, gsl_matrix *cartan);
|
|
point_t intersect(point_t a, point_t b, point_t c, point_t d);
|
|
void drawPoint(DrawingContext *ctx, point_t p);
|
|
void drawSegment(DrawingContext *ctx, point_t a, point_t b);
|
|
void drawLine(DrawingContext *ctx, point_t a, point_t b);
|
|
void drawImplicitLine(DrawingContext *ctx, double a, double b, double c);
|
|
void drawPolygon(DrawingContext *ctx, int sides, ...);
|
|
void drawAttractorConnection(DrawingContext *ctx, const char *word1, const char *word2);
|
|
void drawBox(DrawingContext *ctx, const char *word1, const char *word2);
|
|
void drawBoxStd(DrawingContext *ctx, const char *word, char base);
|
|
int compareAngle(const void *x, const void *y);
|
|
int computeLimitCurve();
|
|
void drawBoxes(DrawingContext *ctx);
|
|
void drawReflectors(DrawingContext *ctx);
|
|
void drawAttractors(DrawingContext *ctx);
|
|
void draw(DrawingContext *ctx);
|
|
void setup();
|
|
void destroy();
|
|
void print(DrawingContext *screen);
|
|
int processEvent(GraphicsInfo *info, XEvent *ev);
|
|
|
|
double parameter;
|
|
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 show_boxes = 0;
|
|
int show_attractors = 0;
|
|
int show_reflectors = 0;
|
|
int show_limit = 1;
|
|
int limit_with_lines = 1;
|
|
int use_repelling = 1;
|
|
|
|
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);
|
|
}
|
|
|
|
// 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 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->scalefactor);
|
|
cairo_stroke(C);
|
|
cairo_restore(C);
|
|
}
|
|
|
|
void drawSegment(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);
|
|
}
|
|
|
|
void drawLine(DrawingContext *ctx, point_t a, point_t b)
|
|
{
|
|
// not implemented
|
|
point_t c, xplus, xminus;
|
|
double normb;
|
|
double scalbc;
|
|
|
|
c.x = ctx->center_x;
|
|
c.y = ctx->center_y;
|
|
b.x -= a.x;
|
|
b.y -= a.y;
|
|
c.x -= a.x;
|
|
c.y -= a.y;
|
|
normb = sqrt(b.x*b.x + b.y*b.y);
|
|
b.x /= normb;
|
|
b.y /= normb;
|
|
scalbc = b.x*c.x + b.y*c.y;
|
|
xplus.x = a.x + (scalbc + ctx->radius)*b.x;
|
|
xplus.y = a.y + (scalbc + ctx->radius)*b.y;
|
|
xminus.x = a.x + (scalbc - ctx->radius)*b.x;
|
|
xminus.y = a.y + (scalbc - ctx->radius)*b.y;
|
|
|
|
drawSegment(ctx, xminus, xplus);
|
|
}
|
|
|
|
void drawImplicitLine(DrawingContext *ctx, double a, double b, double c)
|
|
{
|
|
// not implemented
|
|
double norm, lambda;
|
|
point_t m, s, xminus, xplus;
|
|
m.x = ctx->center_x;
|
|
m.y = ctx->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->radius * b / norm;
|
|
xminus.y = s.y + ctx->radius * a / norm;
|
|
xplus.x = s.x + ctx->radius * b / norm;
|
|
xplus.y = s.y - ctx->radius * a / norm;
|
|
|
|
drawSegment(ctx, xminus, xplus);
|
|
}
|
|
|
|
void drawPolygon(DrawingContext *ctx, int sides, ...)
|
|
{
|
|
va_list args;
|
|
point_t first, prev, current;
|
|
|
|
va_start(args, sides);
|
|
|
|
first = va_arg(args, point_t);
|
|
current = first;
|
|
for(int i = 0; i < sides-1; i++) {
|
|
prev = current;
|
|
current = va_arg(args, point_t);
|
|
drawSegment(ctx, prev, current);
|
|
}
|
|
drawSegment(ctx, current, first);
|
|
|
|
va_end(args);
|
|
}
|
|
|
|
int fixedPoints(const char *word, point_t *out)
|
|
{
|
|
gsl_matrix *tmp = ws->stack[10];
|
|
gsl_matrix *ev = ws->stack[11];
|
|
|
|
gsl_matrix_set_identity(tmp);
|
|
for(int i = 0; i < strlen(word); i++) {
|
|
if(word[i] == ' ')
|
|
continue;
|
|
multiply_right(tmp, gen[word[i]-'a'], ws);
|
|
}
|
|
int count = real_eigenvectors(tmp, ev, ws);
|
|
multiply_left(cob, ev, ws);
|
|
|
|
LOOP(i) out[i].x = gsl_matrix_get(ev, 0, i) / gsl_matrix_get(ev, 2, i);
|
|
LOOP(i) out[i].y = gsl_matrix_get(ev, 1, i) / gsl_matrix_get(ev, 2, i);
|
|
|
|
return count;
|
|
}
|
|
|
|
void drawAttractorConnection(DrawingContext *ctx, const char *word1, const char *word2)
|
|
{
|
|
point_t p1[3], p2[3];
|
|
|
|
fixedPoints(word1, p1);
|
|
fixedPoints(word2, p2);
|
|
|
|
drawSegment(ctx, p1[0], p2[0]);
|
|
}
|
|
|
|
void drawTriangle(DrawingContext *ctx, const char *word)
|
|
{
|
|
point_t p[3];
|
|
|
|
fixedPoints(word, p);
|
|
drawPolygon(ctx, 3, p[0], p[1], p[2]);
|
|
}
|
|
|
|
void drawBox(DrawingContext *ctx, const char *word1, const char *word2)
|
|
{
|
|
point_t p[2][3],i[2];
|
|
|
|
fixedPoints(word1, p[0]);
|
|
fixedPoints(word2, p[1]);
|
|
|
|
// intersect attracting line with neutral line of the other element
|
|
for(int j = 0; j < 2; j++)
|
|
i[j] = intersect(p[j%2][0],p[j%2][1],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);
|
|
}
|
|
|
|
int compareAngle(const void *x, const void *y)
|
|
{
|
|
return ((double*)x)[2] > ((double*)y)[2] ? 1 : -1;
|
|
}
|
|
|
|
int computeLimitCurve()
|
|
{
|
|
int p = 5, q = 5, r = 5;
|
|
int k = 2, l = 2, m = 2;
|
|
|
|
int column = use_repelling ? 2 : 0;
|
|
|
|
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]);
|
|
int ev_count_fuchsian = real_eigenvectors(coxeter, coxeter_fixedpoints, ws);
|
|
|
|
if(ev_count_fuchsian != 3)
|
|
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, column)/gsl_matrix_get(fixedpoints, 2, column),
|
|
gsl_matrix_get(fixedpoints, 1, column)/gsl_matrix_get(fixedpoints, 2, column));
|
|
}
|
|
|
|
// 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]);
|
|
int ev_count = real_eigenvectors(coxeter, coxeter_fixedpoints, ws);
|
|
|
|
if(ev_count == 1)
|
|
column = 0;
|
|
if(ev_count == 0)
|
|
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, column)/gsl_matrix_get(fixedpoints, 2, column);
|
|
limit_curve[3*i+1] = gsl_matrix_get(fixedpoints, 1, column)/gsl_matrix_get(fixedpoints, 2, column);
|
|
}
|
|
|
|
qsort(limit_curve, n_group_elements, 3*sizeof(double), compareAngle);
|
|
|
|
return 1;
|
|
}
|
|
|
|
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 drawReflectors(DrawingContext *ctx)
|
|
{
|
|
gsl_matrix *tmp = ws->stack[10];
|
|
gsl_matrix *tmp2 = ws->stack[11];
|
|
point_t p[3];
|
|
|
|
// points
|
|
cairo_set_source_rgb(ctx->cairo, 0, 0, 0);
|
|
LOOP(i) p[i].x = gsl_matrix_get(cob, 0, i) / gsl_matrix_get(cob, 2, i);
|
|
LOOP(i) p[i].y = gsl_matrix_get(cob, 1, i) / gsl_matrix_get(cob, 2, i);
|
|
LOOP(i) drawPoint(ctx, p[i]);
|
|
|
|
// lines
|
|
invert(cob, tmp2, ws);
|
|
multiply(cartan, tmp2, tmp);
|
|
LOOP(i) drawImplicitLine(ctx, gsl_matrix_get(tmp, i, 0), gsl_matrix_get(tmp, i, 1), gsl_matrix_get(tmp, i, 2));
|
|
}
|
|
|
|
void drawAttractors(DrawingContext *ctx)
|
|
{
|
|
point_t p[3][3];
|
|
|
|
fixedPoints("abc", p[0]);
|
|
fixedPoints("bca", p[1]);
|
|
fixedPoints("cab", p[2]);
|
|
|
|
double color[3][3] = {{1,0,0},{0,0.7,0},{0,0,1}};
|
|
|
|
LOOP(i) LOOP(j) {
|
|
cairo_set_source_rgb(ctx->cairo, color[i][0], color[i][1], color[i][2]);
|
|
drawPoint(ctx, p[i][j]);
|
|
}
|
|
|
|
LOOP(i) LOOP(j) {
|
|
cairo_set_source_rgb(ctx->cairo, color[i][0], color[i][1], color[i][2]);
|
|
drawLine(ctx, p[i][j], p[i][(j+1)%3]);
|
|
}
|
|
}
|
|
|
|
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->matrix);
|
|
|
|
// defaults; use save/restore whenever these are changed
|
|
cairo_set_line_width(C, 1.0/ctx->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(limit_curve_valid) {
|
|
if(show_limit) {
|
|
cairo_save(C);
|
|
|
|
int previous_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(C, &x, &y);
|
|
|
|
if(-x < ctx->width && x < 3*ctx->width && -y < ctx->height && y < 3*ctx->height) {
|
|
if(limit_with_lines) {
|
|
if(!previous_inside)
|
|
cairo_move_to(C, limit_curve[3*i], limit_curve[3*i+1]);
|
|
else
|
|
cairo_line_to(C, limit_curve[3*i], limit_curve[3*i+1]);
|
|
} else {
|
|
cairo_move_to(C, limit_curve[3*i], limit_curve[3*i+1]);
|
|
cairo_close_path(C);
|
|
}
|
|
previous_inside = 1;
|
|
} else {
|
|
previous_inside = 0;
|
|
}
|
|
}
|
|
|
|
if(!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->scalefactor);
|
|
}
|
|
|
|
cairo_set_source_rgb(C, 0, 0, 0);
|
|
cairo_stroke(C);
|
|
|
|
cairo_restore(C);
|
|
}
|
|
|
|
if(show_boxes)
|
|
drawBoxes(ctx);
|
|
|
|
if(show_attractors)
|
|
drawAttractors(ctx);
|
|
|
|
if(show_reflectors)
|
|
drawReflectors(ctx);
|
|
}
|
|
|
|
cairo_identity_matrix(C);
|
|
|
|
cairo_move_to(C, 15, 30);
|
|
cairo_set_source_rgb(C, 0, 0, 0);
|
|
char buf[100];
|
|
sprintf(buf, "t = exp(%.8f) = %.8f", log(parameter), parameter);
|
|
cairo_show_text(C, buf);
|
|
|
|
cairo_surface_flush(cairo_get_target(C));
|
|
}
|
|
|
|
void setup()
|
|
{
|
|
n_group_elements = 50000;
|
|
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 print(DrawingContext *screen)
|
|
{
|
|
DrawingContext file;
|
|
cairo_surface_t *surface;
|
|
|
|
char filename[100];
|
|
time_t t = time(NULL);
|
|
strftime(filename, sizeof(filename), "screenshot_%Y%m%d_%H%M%S.pdf", localtime(&t));
|
|
|
|
file.width = screen->width;
|
|
file.height = screen->width / sqrt(2.0);
|
|
file.matrix = screen->matrix;
|
|
file.matrix.y0 += ((double)file.height - (double)screen->height) / 2.0; // recenter vertically
|
|
updateDimensions(&file);
|
|
|
|
surface = cairo_pdf_surface_create(filename, (double)file.width, (double)file.height);
|
|
file.cairo = cairo_create(surface);
|
|
|
|
draw(&file);
|
|
|
|
cairo_destroy(file.cairo);
|
|
cairo_surface_destroy(surface);
|
|
|
|
printf("Wrote sceenshot to file: %s\n", filename);
|
|
}
|
|
|
|
int processEvent(GraphicsInfo *info, XEvent *ev)
|
|
{
|
|
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 XK_Down:
|
|
parameter /= exp(0.002);
|
|
limit_curve_valid = computeLimitCurve();
|
|
break;
|
|
case XK_Up:
|
|
parameter *= exp(0.002);
|
|
limit_curve_valid = computeLimitCurve();
|
|
break;
|
|
case XK_Left:
|
|
parameter /= exp(0.0001);
|
|
limit_curve_valid = computeLimitCurve();
|
|
break;
|
|
case XK_Right:
|
|
parameter *= exp(0.0001);
|
|
limit_curve_valid = computeLimitCurve();
|
|
break;
|
|
case XK_Page_Down:
|
|
parameter /= exp(0.2);
|
|
limit_curve_valid = computeLimitCurve();
|
|
break;
|
|
case XK_Page_Up:
|
|
parameter *= exp(0.2);
|
|
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;
|
|
case 'm':
|
|
printf("info->context->matrix.xx = %f;\n", info->context->matrix.xx);
|
|
printf("info->context->matrix.xy = %f;\n", info->context->matrix.xy);
|
|
printf("info->context->matrix.x0 = %f;\n", info->context->matrix.x0);
|
|
printf("info->context->matrix.yx = %f;\n", info->context->matrix.yx);
|
|
printf("info->context->matrix.yy = %f;\n", info->context->matrix.yy);
|
|
printf("info->context->matrix.y0 = %f;\n", info->context->matrix.y0);
|
|
break;
|
|
case 'b':
|
|
show_boxes = !show_boxes;
|
|
break;
|
|
case 'a':
|
|
show_attractors = !show_attractors;
|
|
break;
|
|
case 'r':
|
|
show_reflectors = !show_reflectors;
|
|
break;
|
|
case 'L':
|
|
limit_with_lines = !limit_with_lines;
|
|
break;
|
|
case 'l':
|
|
show_limit = !show_limit;
|
|
break;
|
|
case 'p':
|
|
print(info->context);
|
|
break;
|
|
case 'f':
|
|
use_repelling = !use_repelling;
|
|
limit_curve_valid = computeLimitCurve();
|
|
}
|
|
|
|
return STATUS_REDRAW;
|
|
}
|
|
|
|
return STATUS_NOTHING;
|
|
}
|
|
|
|
int main()
|
|
{
|
|
GraphicsInfo *info;
|
|
|
|
parameter = 3.0;
|
|
|
|
setup();
|
|
limit_curve_valid = computeLimitCurve();
|
|
|
|
info = initCairo(0, KeyPressMask, 200, 200, "Triangle group");
|
|
if(!info)
|
|
return 1;
|
|
|
|
DrawingContext *ctx = info->context;
|
|
|
|
ctx->matrix.xx = 837.930824;
|
|
ctx->matrix.xy = -712.651341;
|
|
ctx->matrix.x0 = 180.427716;
|
|
ctx->matrix.yx = 712.651341;
|
|
ctx->matrix.yy = 837.930824;
|
|
ctx->matrix.y0 = 1412.553240;
|
|
|
|
/*
|
|
info->context->matrix.xx = 1636.583641;
|
|
info->context->matrix.xy = -1391.897150;
|
|
info->context->matrix.x0 = 975.772883;
|
|
info->context->matrix.yx = 1391.897150;
|
|
info->context->matrix.yy = 1636.583641;
|
|
info->context->matrix.y0 = 2446.174297;
|
|
*/
|
|
|
|
updateDimensions(ctx);
|
|
|
|
startTimer(info);
|
|
|
|
while(1) {
|
|
int result = checkEvents(info, processEvent, NULL);
|
|
if(result == STATUS_QUIT)
|
|
return 0;
|
|
else if(result == STATUS_REDRAW) {
|
|
struct timeval current_time;
|
|
double start_time, intermediate_time, end_time;
|
|
gettimeofday(¤t_time, 0);
|
|
start_time = current_time.tv_sec + current_time.tv_usec*1e-6;
|
|
|
|
updateDimensions(info->context);
|
|
draw(info->context);
|
|
|
|
gettimeofday(¤t_time, 0);
|
|
intermediate_time = current_time.tv_sec + current_time.tv_usec*1e-6;
|
|
|
|
cairo_set_source_surface(info->front_context, info->buffer_surface, 0, 0);
|
|
cairo_paint(info->front_context);
|
|
|
|
gettimeofday(¤t_time, 0);
|
|
end_time = current_time.tv_sec + current_time.tv_usec*1e-6;
|
|
printf("drawing finished in %.2f milliseconds, of which %.2f milliseconds were buffer switching\n", (end_time - start_time) * 1000, (end_time - intermediate_time) * 1000);
|
|
}
|
|
waitUpdateTimer(info);
|
|
}
|
|
|
|
destroyCairo(info);
|
|
|
|
destroy();
|
|
|
|
return 0;
|
|
}
|