#include #include #include #include #include #include #include #include #include #include "gragl.h" #define SUBWINDOWS 64 static Plot2dGroup * target = NULL; static struct { int last_x; int last_y; int state; } mouse; static int subwindows[SUBWINDOWS]; static void enable_multi_sample() { glEnable(GL_MULTISAMPLE); glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST); } static void draw_function(double x, double y, double size, double step, Function * function) { glColor3fv(function->color); glLineWidth(function->width); glBegin(GL_LINES); for (double i = 0; i < size; i += step) { Scalar current = function->func(i - x); Scalar next = function->func(i + step - x); if (current.valid) { glVertex2f(i, current.v + y); glVertex2f(i + step, next.v + y); } } glEnd(); } void draw_axis(double x, double y) { glColor3fv(COLOR_WHITE); glLineWidth(2); glBegin(GL_LINES); glVertex2f(x, y); glVertex2f(x + 1, y); glVertex2f(x, y); glVertex2f(x, y + 1); glVertex2f(x + 1, y); glVertex2f(x + 0.9, y + 0.1); glVertex2f(x + 1, y + 0); glVertex2f(x + 0.9, y - 0.1); glVertex2f(x, y + 1); glVertex2f(x + 0.1, y + 0.9); glVertex2f(x, y + 1); glVertex2f(x - 0.1, y + 0.9); glEnd(); } void draw_grid(double x, double y, double size) { glColor3f(0.2, 0.2, 0.2); glLineWidth(2); glBegin(GL_LINES); for (double i = 0; i < size; ++i) { double xpos = fmod(x + i, size); if (xpos < 0) xpos += size; glVertex2f(xpos, -size / 2); glVertex2f(xpos, size / 2); } for (double i = 0; i < size; ++i) { double ypos = fmod(y + i, size); if (ypos < 0) ypos += size; glVertex2f(0, ypos - size / 2); glVertex2f(size, ypos - size / 2); } glEnd(); glColor3f(0.5, 0.5, 0.5); glBegin(GL_LINES); glVertex2f(0, y); glVertex2f(size, y); glVertex2f(x, -size / 2); glVertex2f(x, size / 2); glEnd(); glColor3f(0.7, 0.7, 0.7); glBegin(GL_LINE_LOOP); glVertex2f(0, -size / 2); glVertex2f(size, -size / 2); glVertex2f(size, size / 2); glVertex2f(0, size / 2); glEnd(); } static void update() { glClearColor(COLOR_BLACK[0], COLOR_BLACK[1], COLOR_BLACK[2], COLOR_BLACK[3]); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); int window = glutGetWindow(); int plot_id = 0; while (subwindows[plot_id] != window) ++plot_id; Plot2d * plot = &(target->plots[plot_id]); plot->width = glutGet(GLUT_WINDOW_WIDTH); plot->height = glutGet(GLUT_WINDOW_HEIGHT); double translate = -(plot->rsize / 2); double zoom = 2 / (plot->rsize); glScalef(zoom, zoom, zoom); glTranslatef(translate, 0, 0); draw_grid(plot->x, plot->y, plot->rsize); draw_axis(plot->x, plot->y); for (Function *f = plot->function; f < plot->function + plot->nfuncs; ++f) { draw_function(plot->x, plot->y, plot->rsize, plot->step, f); } glTranslatef(-translate, 0, 0); glScalef(1/zoom, 1/zoom, 1/zoom); glutSwapBuffers(); } static void parent_update() { glClearColor(COLOR_DARKER[0], COLOR_DARKER[1], COLOR_DARKER[2], COLOR_DARKER[3]); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glutSwapBuffers(); int width = glutGet(GLUT_WINDOW_WIDTH); int height = glutGet(GLUT_WINDOW_HEIGHT); int min = width > height ? height : width; unsigned n_plots = ((unsigned)sqrt(target->n)); int plot_dim = min / (int)n_plots - (int)target->padding / 4; int padding_x = (width - plot_dim * n_plots) / 2; int padding_y = (height - plot_dim * n_plots) / 2; plot_dim = plot < 0 ? 1 : plot_dim; for (unsigned i = 0; i < SUBWINDOWS && subwindows[i] > 0; ++i) { glutSetWindow(subwindows[i]); glutReshapeWindow( plot_dim - target->padding / 2, plot_dim - target->padding / 2 ); glutPositionWindow( padding_x + (i % n_plots) * plot_dim + target->padding / 2, padding_y + (i / n_plots) * plot_dim + target->padding / 2 ); } } static void mouse_wheel(int wheel, int direction, int x, int y) { int window = glutGetWindow(); int plot_id = 0; while (subwindows[plot_id] != window) ++plot_id; Plot2d *plot = target->plots + plot_id; plot->rsize += direction * MAX(log(plot->rsize), 0.00001); glutPostRedisplay(); } static void mouse_move(int x, int y) { int window = glutGetWindow(); int plot_id = 0; while (subwindows[plot_id] != window) ++plot_id; Plot2d *plot = target->plots + plot_id; double delta_x = x - mouse.last_x; double delta_y = y - mouse.last_y; plot->x += delta_x / plot->width * plot->rsize; plot->y -= delta_y / plot->width * plot->rsize; mouse.last_x = x; mouse.last_y = y; glutPostRedisplay(); } static void mouse_button(int button, int state, int x, int y) { mouse.state = state; mouse.last_x = x; mouse.last_y = y; if (state) { glutSetCursor(GLUT_CURSOR_INHERIT); } else { glutSetCursor(GLUT_CURSOR_INFO); } } static void resize(int width, int height) { glutReshapeWindow(MAX(width, 10), MAX(width, 10)); } void plot(Plot2dGroup * group) { target = group; char * arg[1] = { "" }; int argc = 1; glutInit(&argc, arg); glutInitWindowSize(500, 500); glutSetOption(GLUT_MULTISAMPLE, 8); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE); int parent = glutCreateWindow("GRAGL"); for (int i = 0; i < group->n; ++i) { int id = glutCreateSubWindow(parent, 1, 1, 1, 1); subwindows[i] = id; glutSetWindow(id); glutDisplayFunc(update); glutMouseFunc(mouse_button); glutMotionFunc(mouse_move); glutMouseWheelFunc(mouse_wheel); } glutSetWindow(parent); glutDisplayFunc(parent_update); glutReshapeFunc(resize); enable_multi_sample(); glutMainLoop(); } Plot2dGroup * create_plot2d_group(int num_args, ...) { Plot2dGroup * group = (Plot2dGroup*)malloc(sizeof(Plot2dGroup)); group->plots = (Plot2d*)malloc(sizeof(Plot2d) * num_args); group->n = num_args; group->padding = 20; va_list ap; va_start(ap, num_args); for (int i = 0; i < num_args; ++i) { group->plots[i] = va_arg(ap, Plot2d); } va_end(ap); return group; } void free_plot2d_group(Plot2dGroup * group) { free(group->plots); free(group); }