PC_graph/ps_graph.c
2024-12-23 16:27:06 +01:00

189 lines
5.2 KiB
C

#include "ps_graph.h"
#include <math.h>
#define TEMP_BUF_SIZE 128
/* rozměry grafu */
#define GRAPH_SIZE 400
#define HALF_GRAPH_SIZE (GRAPH_SIZE / 2)
#define GRAPH_CENTER_X 306
#define GRAPH_CENTER_Y 500
/* písmo */
#define FONT "Helvetica"
#define FONT_SIZE_HEADER 15
#define FONT_SIZE_LABEL 12
#define FONT_SIZE_VALUE 9
#define GRID_STEP (GRAPH_SIZE / 10)
/* posuny popisků */
#define HEADER_OFFSET_Y (10)
#define X_VAL_OFFSET_X (0)
#define X_VAL_OFFSET_Y (-13)
#define Y_VAL_OFFSET_X (-5)
#define Y_VAL_OFFSET_Y (-3)
#define X_LABEL_OFFSET_X (0)
#define X_LABEL_OFFSET_Y (-30)
#define Y_LABEL_OFFSET_X (-40)
#define Y_LABEL_OFFSET_Y (-3)
/* počet částí grafu funkce */
#define FUNCTION_SEGMENTS 1000
/* koeficienty pro zarovnání textu */
#define ALIGN_CENTER (-0.5)
#define ALIGN_RIGHT (-1.0)
static void generate_text_align(FILE *file, int x, int y, double alignment, const char *str) {
fprintf(file,
"newpath\n"
/* posun na cílovou pozici */
"%d %d moveto\n"
/* vložení dvou řetězců s popiskem */
"(%s) dup\n"
/* zjištění šířky a výšky řetežce, odstranění výšky ze zásobníku (nezajímá) */
"stringwidth pop\n"
/* koeficient pro zarovnání */
"%f mul\n"
/* posun o šířku doleva */
"0 rmoveto\n"
/* zobrazení řetězce */
"show\n"
"\n",
x, y, str, alignment
);
}
static void set_font(FILE *file, const char *font, int size) {
fprintf(file,
"/%s findfont\n"
"%d scalefont\n"
"setfont\n",
font, size
);
}
static void generate_grid(FILE *file, const struct graph_range *range) {
int i;
fprintf(file,
"2 setlinewidth\n"
"/gridline {\n"
" .5 setlinewidth\n"
" .5 setgray\n"
" [3 3] 0 setdash\n"
" newpath\n"
" moveto\n"
" rlineto\n"
" stroke\n"
"} def\n"
"\n"
);
for (i = -HALF_GRAPH_SIZE + GRID_STEP; i < HALF_GRAPH_SIZE; i += GRID_STEP) {
fprintf(file, "%5d 0 %5d %5d gridline\n", GRAPH_SIZE, -HALF_GRAPH_SIZE, i);
fprintf(file, " 0 %5d %5d %5d gridline\n", GRAPH_SIZE, i, -HALF_GRAPH_SIZE);
}
set_font(file, FONT, FONT_SIZE_VALUE);
/* hodnoty */
for (i = -HALF_GRAPH_SIZE; i <= HALF_GRAPH_SIZE; i += GRID_STEP) {
double inorm = (double)i / (double)GRAPH_SIZE + 0.5;
double xval = range->xmax * inorm + range->xmin * (1.0 - inorm);
double yval = range->ymax * inorm + range->ymin * (1.0 - inorm);
char buf[TEMP_BUF_SIZE];
/* hodnota osy y */
snprintf(buf, TEMP_BUF_SIZE, "%.2f", yval);
generate_text_align(file, -HALF_GRAPH_SIZE + Y_VAL_OFFSET_X, i + Y_VAL_OFFSET_Y, ALIGN_RIGHT, buf);
/* hodnota osy x */
snprintf(buf, TEMP_BUF_SIZE, "%.2f", xval);
generate_text_align(file, i + X_VAL_OFFSET_X, -HALF_GRAPH_SIZE + X_VAL_OFFSET_Y, ALIGN_CENTER, buf);
}
set_font(file, FONT, FONT_SIZE_LABEL);
generate_text_align(file, -HALF_GRAPH_SIZE + Y_LABEL_OFFSET_X, Y_LABEL_OFFSET_Y, ALIGN_RIGHT, "y");
generate_text_align(file, X_LABEL_OFFSET_X, -HALF_GRAPH_SIZE + X_LABEL_OFFSET_Y, ALIGN_CENTER, "x");
}
static void generate_function(FILE *file, const struct expr_node *node, const struct graph_range *range) {
int i;
int first = 1;
fprintf(file,
"2 setlinewidth\n"
"0 .3 .7 setrgbcolor\n"
"newpath\n"
);
for (i = 0; i < FUNCTION_SEGMENTS; ++i) {
const char *cmd = "lineto";
double xpos, ypos;
double x, y;
x = range->xmin + (range->xmax - range->xmin) * (double)i / (double)FUNCTION_SEGMENTS;
if (node_eval(node, x, &y) != EVAL_OK) {
first = 1;
continue;
}
xpos = (double)(-HALF_GRAPH_SIZE) + (x - range->xmin) / (range->xmax - range->xmin) * (double)GRAPH_SIZE;
ypos = (double)(-HALF_GRAPH_SIZE) + (y - range->ymin) / (range->ymax - range->ymin) * (double)GRAPH_SIZE;
if (first) {
cmd = "moveto";
first = 0;
}
fprintf(file, "%.2f %.2f %s\n", xpos, ypos, cmd);
}
fprintf(file,
"stroke\n"
);
}
void ps_generate_graph(FILE *file, const struct expr_node *node, const struct graph_range *range, const char *function) {
char buf[TEMP_BUF_SIZE];
fprintf(file, "%d %d translate\n", GRAPH_CENTER_X, GRAPH_CENTER_Y);
if (function) {
set_font(file, FONT, FONT_SIZE_HEADER);
snprintf(buf, TEMP_BUF_SIZE, "y = %s", function);
generate_text_align(file, 0, HALF_GRAPH_SIZE + HEADER_OFFSET_Y, ALIGN_CENTER, buf);
}
generate_grid(file, range);
fprintf(file,
"[] 0 setdash\n"
"newpath\n"
"%d %d moveto\n"
"%d 0 rlineto\n"
"0 %d rlineto\n"
"%d 0 rlineto\n"
"closepath\n"
"gsave\n"
"clip\n",
-HALF_GRAPH_SIZE, -HALF_GRAPH_SIZE, GRAPH_SIZE, GRAPH_SIZE, -GRAPH_SIZE
);
generate_function(file, node, range);
fprintf(file,
"grestore\n"
"2 setlinewidth\n"
"0 0 0 setrgbcolor\n"
"stroke\n"
);
}