/* render-me * Written in 2012 by * Andrew Poelstra * * To the extent possible under law, the author(s) have dedicated all * copyright and related and neighboring rights to this software to * the public domain worldwide. This software is distributed without * any warranty. * * You should have received a copy of the CC0 Public Domain Dedication * along with this software. * If not, see . */ #include #include #include #include #include #include #define OUTPUT_SIZE 5000 #define OUTPUT_FILE "output.png" #define INITIAL_VECTOR_SIZE 1000 #define ROOT3 1.73205080756887729352 double *smear_data (double *list, unsigned side_len); double project_xy (int xidx, int zidx, double grid_unitxy, double grid_unitz) { xidx *= grid_unitxy; zidx *= grid_unitz; return OUTPUT_SIZE - xidx - zidx; } void draw_cube_cell (gdImagePtr img, int xidx, int yidx, int zidx, double grid_unitxy, double grid_unitz, int col, int fill) { gdPoint points[4]; /* Front face */ points[0].x = project_xy (xidx, zidx, grid_unitxy, grid_unitz); points[0].y = project_xy (yidx + 1, zidx, grid_unitxy, grid_unitz); points[1].x = project_xy (xidx, zidx, grid_unitxy, grid_unitz); points[1].y = project_xy (yidx, zidx, grid_unitxy, grid_unitz) - 1; points[2].x = project_xy (xidx + 1, zidx, grid_unitxy, grid_unitz); points[2].y = project_xy (yidx, zidx, grid_unitxy, grid_unitz) - 1; points[3].x = project_xy (xidx + 1, zidx, grid_unitxy, grid_unitz); points[3].y = project_xy (yidx + 1, zidx, grid_unitxy, grid_unitz); if (fill) gdImageFilledPolygon (img, points, 4, col); else gdImagePolygon (img, points, 4, col); /* Right face */ points[2].x = project_xy (xidx, zidx + 1, grid_unitxy, grid_unitz); points[2].y = project_xy (yidx, zidx + 1, grid_unitxy, grid_unitz) - 1; points[3].x = project_xy (xidx, zidx + 1, grid_unitxy, grid_unitz); points[3].y = project_xy (yidx + 1, zidx + 1, grid_unitxy, grid_unitz); if (fill) gdImageFilledPolygon (img, points, 4, col); else gdImagePolygon (img, points, 4, col); /* Top face */ points[1].x = project_xy (xidx + 1, zidx, grid_unitxy, grid_unitz); points[1].y = project_xy (yidx + 1, zidx, grid_unitxy, grid_unitz); points[2].x = project_xy (xidx + 1, zidx + 1, grid_unitxy, grid_unitz); points[2].y = project_xy (yidx + 1, zidx + 1, grid_unitxy, grid_unitz); if (fill) gdImageFilledPolygon (img, points, 4, col); else gdImagePolygon (img, points, 4, col); /* Left face */ points[0].x = project_xy (xidx + 1, zidx, grid_unitxy, grid_unitz); points[0].y = project_xy (yidx, zidx, grid_unitxy, grid_unitz); points[3].x = project_xy (xidx + 1, zidx + 1, grid_unitxy, grid_unitz); points[3].y = project_xy (yidx, zidx + 1, grid_unitxy, grid_unitz); if (fill) gdImageFilledPolygon (img, points, 4, col); else gdImagePolygon (img, points, 4, col); /* Bottom face */ points[1].x = project_xy (xidx, zidx, grid_unitxy, grid_unitz); points[1].y = project_xy (yidx, zidx, grid_unitxy, grid_unitz); points[2].x = project_xy (xidx, zidx + 1, grid_unitxy, grid_unitz); points[2].y = project_xy (yidx, zidx + 1, grid_unitxy, grid_unitz); if (fill) gdImageFilledPolygon (img, points, 4, col); else gdImagePolygon (img, points, 4, col); /* Back face */ points[0].x = project_xy (xidx + 1, zidx + 1, grid_unitxy, grid_unitz); points[0].y = project_xy (yidx + 1, zidx + 1, grid_unitxy, grid_unitz); points[1].x = project_xy (xidx, zidx + 1, grid_unitxy, grid_unitz); points[1].y = project_xy (yidx + 1, zidx + 1, grid_unitxy, grid_unitz); if (fill) gdImageFilledPolygon (img, points, 4, col); else gdImagePolygon (img, points, 4, col); } /* Lookup the value at position (x, y, z), where the * value is obtained from 'data', a 1D array containing * naturally ordered 3D data. */ void draw_box (gdImagePtr img, double value, unsigned pos, unsigned side_len) { double grid_unitxy = (double) OUTPUT_SIZE / side_len / (1 + 1/ROOT3); double grid_unitz = (double) OUTPUT_SIZE / side_len / (1 + ROOT3); int xidx = (pos / side_len / side_len); int yidx = (pos / side_len) % side_len; int zidx = pos % side_len; int col = gdTrueColorAlpha (60, 220, 0, 127 - (int) (100 * value)); int col1 = gdTrueColorAlpha (60, 0, 220, 127 - (int) (100 * value)); draw_cube_cell (img, xidx, yidx, zidx, grid_unitxy, grid_unitz, col, 1); draw_cube_cell (img, side_len - xidx, yidx, zidx, grid_unitxy, grid_unitz, col1, 1); /* draw_cube_cell (img, xidx, yidx, zidx, grid_unitxy, grid_unitz, gdTrueColorAlpha (0, 0, 0, 126), 1); draw_cube_cell (img, xidx, yidx, zidx, grid_unitxy, grid_unitz, gdTrueColorAlpha (0, 0, 0, 50), 0); */ } /* WARN: To simplify the appearance of the code and therefore * make it easier to read/analyze, I will not be checking * for any resource allocation errors. * * So if you run out of memory, this will crash and burn. */ int main (void) { unsigned i = 0, n = 0, rt_n = 0; unsigned vect_size = INITIAL_VECTOR_SIZE; double *raw_list = malloc (vect_size * sizeof *raw_list); double max_val = 0.0; gdImagePtr img = gdImageCreateTrueColor (OUTPUT_SIZE, OUTPUT_SIZE); /** Read in the data * After this loop, 'n' will be the size of the vector. */ while (scanf (" %lf", &raw_list[n]) == 1) { if (raw_list[n] > max_val) max_val = raw_list[n]; if (n == vect_size) { vect_size *= 2; raw_list = realloc (raw_list, vect_size * sizeof *raw_list); } ++n; } /* Determine cube root of n */ for (rt_n = 0; (rt_n * rt_n * rt_n) < n; ++rt_n); if ((rt_n * rt_n * rt_n) != n) fprintf (stderr, "ERR: vector length %u is not a cube.\n", n); /* This cube root is our side length. */ printf ("Obtained data for a %d^3 graph. Maximum value is %g.\n", rt_n, max_val); /* Smear the data a few times to increase resolution */ for (i = 0; i < 2; ++i) { double *tmp = smear_data (raw_list, rt_n); if (tmp) { free (raw_list); raw_list = tmp; rt_n = rt_n * 2 - 1; n = rt_n * rt_n * rt_n; } } /* Draw the graph */ gdImageFill (img, 0, 0, gdTrueColor (255, 255, 255)); for (i = 0; i < n; ++i) { double val = raw_list[i] / max_val; draw_box (img, val, i, rt_n); } /* Output the PNG file */ { FILE *fh = fopen (OUTPUT_FILE, "wb"); if (fh) gdImagePng (img, fh); else fprintf (stderr, "Failed to open ``%s'' for writing!", OUTPUT_FILE); fclose (fh); } printf ("Done drawing. Thanks for waiting. :)\n"); /* Cleanup */ free (raw_list); return 0; }