diff options
-rw-r--r-- | dimension/main.c | 9 | ||||
-rw-r--r-- | dimension/parse.c | 231 | ||||
-rw-r--r-- | dimension/parse.h | 20 | ||||
-rw-r--r-- | dimension/realize.c | 50 | ||||
-rw-r--r-- | dimension/realize.h | 3 | ||||
-rw-r--r-- | dimension/tokenize.c | 2 | ||||
-rw-r--r-- | tests/dimension/Makefile.am | 24 | ||||
-rw-r--r-- | tests/dimension/box.pov | 24 | ||||
-rwxr-xr-x | tests/dimension/box.sh | 22 | ||||
-rwxr-xr-x | tests/dimension/numeric.sh | 2 |
10 files changed, 360 insertions, 27 deletions
diff --git a/dimension/main.c b/dimension/main.c index 990ee14..5d921e7 100644 --- a/dimension/main.c +++ b/dimension/main.c @@ -145,11 +145,14 @@ main(int argc, char **argv) { * Now we render the scene */ + if (dmnsn_png_optimize_canvas(scene->canvas) != 0) { + fprintf(stderr, "WARNING: Couldn't optimize canvas for PNG\n"); + } + if (dmnsn_raytrace_scene(scene) != 0) { - dmnsn_delete_scene(scene); + dmnsn_delete_realized_scene(scene); dmnsn_error(DMNSN_SEVERITY_HIGH, "Error rendering scene."); } - dmnsn_delete_scene(scene); /* Open the output file */ output_file = fopen(output, "wb"); @@ -163,6 +166,6 @@ main(int argc, char **argv) { } fclose(output_file); - /* Clean up and exit! */ + dmnsn_delete_realized_scene(scene); return EXIT_SUCCESS; } diff --git a/dimension/parse.c b/dimension/parse.c index 2c1b67c..c868d95 100644 --- a/dimension/parse.c +++ b/dimension/parse.c @@ -20,17 +20,244 @@ #include "parse.h" #include "tokenize.h" #include "utility.h" +#include <stdlib.h> +#include <stdio.h> +#include <locale.h> + +static int +dmnsn_parse_expect(dmnsn_token_type type, const dmnsn_array *tokens, + unsigned int *ip) +{ + dmnsn_token token; + + if (*ip < dmnsn_array_size(tokens)) { + dmnsn_array_get(tokens, *ip, &token); + if (token.type != type) { + dmnsn_diagnostic(token.filename, token.line, token.col, + "Expected '%s', found '%s'", + dmnsn_token_name(type), + dmnsn_token_name(token.type)); + return 1; + } + } else { + fprintf(stderr, "Expected '%s', found end of file\n", + dmnsn_token_name(type)); + return 1; + } + + ++*ip; + return 0; +} + +static int +dmnsn_parse_float(const dmnsn_array *tokens, unsigned int *ip, + dmnsn_array *astree) +{ + unsigned int i = *ip; + + if (i >= dmnsn_array_size(tokens)) { + fprintf(stderr, "Expected '%s' or '%s', found end of file\n", + dmnsn_token_name(DMNSN_T_INTEGER), + dmnsn_token_name(DMNSN_T_FLOAT)); + return 1; + } + + int negative = 0; + dmnsn_token token; + dmnsn_array_get(tokens, i, &token); + + if (token.type == DMNSN_T_MINUS) { + negative = 1; + + ++i; + if (i >= dmnsn_array_size(tokens)) { + fprintf(stderr, "Expected '%s' or '%s', found end of file\n", + dmnsn_token_name(DMNSN_T_INTEGER), + dmnsn_token_name(DMNSN_T_FLOAT)); + return 1; + } + dmnsn_array_get(tokens, i, &token); + } + + double *value = malloc(sizeof(double)); + if (!value) + dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't allocate room for float."); + + if (token.type == DMNSN_T_INTEGER || token.type == DMNSN_T_FLOAT) { + *value = strtod(token.value, NULL); + ++i; + } else { + fprintf(stderr, "Expected '%s' or '%s', found '%s'\n", + dmnsn_token_name(DMNSN_T_INTEGER), + dmnsn_token_name(DMNSN_T_FLOAT), + dmnsn_token_name(token.type)); + return 1; + } + + if (negative) + *value *= -1.0; + + dmnsn_astnode astnode; + astnode.type = DMNSN_AST_FLOAT; + astnode.children = NULL; + astnode.ptr = value; + + dmnsn_array_push(astree, &astnode); + *ip = i; + return 0; +} + +static int +dmnsn_parse_vector(const dmnsn_array *tokens, unsigned int *ip, + dmnsn_array *astree) +{ + unsigned int i = *ip; + + if (dmnsn_parse_expect(DMNSN_T_LESS, tokens, &i) != 0) + return 1; + + dmnsn_astnode astnode; + astnode.type = DMNSN_AST_VECTOR; + astnode.children = dmnsn_new_array(sizeof(dmnsn_astnode)); + astnode.ptr = NULL; + + /* First element */ + if (dmnsn_parse_float(tokens, &i, astnode.children) != 0) + goto bailout; + + if (dmnsn_parse_expect(DMNSN_T_COMMA, tokens, &i) != 0) + goto bailout; + + /* Second element */ + if (dmnsn_parse_float(tokens, &i, astnode.children) != 0) + goto bailout; + + if (dmnsn_parse_expect(DMNSN_T_COMMA, tokens, &i) != 0) + goto bailout; + + /* Third element */ + if (dmnsn_parse_float(tokens, &i, astnode.children) != 0) + goto bailout; + + if (dmnsn_parse_expect(DMNSN_T_GREATER, tokens, &i) != 0) + goto bailout; + + dmnsn_array_push(astree, &astnode); + *ip = i; + return 0; + + bailout: + dmnsn_delete_astree(astnode.children); + return 1; +} + +static int +dmnsn_parse_box(const dmnsn_array *tokens, unsigned int *ip, + dmnsn_array *astree) +{ + unsigned int i = *ip; + + if (dmnsn_parse_expect(DMNSN_T_BOX, tokens, &i) != 0) + return 1; + if (dmnsn_parse_expect(DMNSN_T_LBRACE, tokens, &i) != 0) + return 1; + + dmnsn_astnode astnode; + astnode.type = DMNSN_AST_BOX; + astnode.children = dmnsn_new_array(sizeof(dmnsn_astnode)); + astnode.ptr = NULL; + + /* First corner */ + if (dmnsn_parse_vector(tokens, &i, astnode.children) != 0) + goto bailout; + + if (dmnsn_parse_expect(DMNSN_T_COMMA, tokens, &i) != 0) + goto bailout; + + /* Second corner */ + if (dmnsn_parse_vector(tokens, &i, astnode.children) != 0) + goto bailout; + + if (dmnsn_parse_expect(DMNSN_T_RBRACE, tokens, &i) != 0) + goto bailout; + + dmnsn_array_push(astree, &astnode); + *ip = i; + return 0; + + bailout: + dmnsn_delete_astree(astnode.children); + return 1; +} dmnsn_array * -dmnsn_parse(dmnsn_array *tokens) +dmnsn_parse(const dmnsn_array *tokens) { + unsigned int i; + dmnsn_array *astree = dmnsn_new_array(sizeof(dmnsn_astnode)); + dmnsn_token token; + + /* Save the current locale */ + char *lc_ctype = strdup(setlocale(LC_CTYPE, NULL)); + char *lc_numeric = strdup(setlocale(LC_NUMERIC, NULL)); + + /* Set the locale to `C' to make strtoul(), etc. consistent */ + setlocale(LC_CTYPE, "C"); + setlocale(LC_NUMERIC, "C"); + + for (i = 0; i < dmnsn_array_size(tokens); ++i) { + dmnsn_array_get(tokens, i, &token); + + if (token.type == DMNSN_T_BOX) { + if (dmnsn_parse_box(tokens, &i, astree) != 0) { + dmnsn_diagnostic(token.filename, token.line, token.col, + "Invalid box", + dmnsn_token_name(DMNSN_T_BOX), + dmnsn_token_name(token.type)); + goto bailout; + } + } else { + dmnsn_diagnostic(token.filename, token.line, token.col, + "Expected '%s', found '%s'", + dmnsn_token_name(DMNSN_T_BOX), + dmnsn_token_name(token.type)); + goto bailout; + } + } + + /* Restore the original locale */ + setlocale(LC_CTYPE, lc_ctype); + setlocale(LC_NUMERIC, lc_numeric); + free(lc_ctype); + free(lc_numeric); + + return astree; + + bailout: + /* Restore the original locale */ + setlocale(LC_CTYPE, lc_ctype); + setlocale(LC_NUMERIC, lc_numeric); + free(lc_ctype); + free(lc_numeric); + + dmnsn_delete_astree(astree); return NULL; } void dmnsn_delete_astree(dmnsn_array *astree) { - dmnsn_delete_array(astree); + unsigned int i; + dmnsn_astnode node; + + if (astree) { + for (i = 0; i < dmnsn_array_size(astree); ++i) { + dmnsn_array_get(astree, i, &node); + dmnsn_delete_astree(node.children); + free(node.ptr); + } + dmnsn_delete_array(astree); + } } void diff --git a/dimension/parse.h b/dimension/parse.h index d51144a..aa6ee58 100644 --- a/dimension/parse.h +++ b/dimension/parse.h @@ -19,8 +19,26 @@ #include "../libdimension/dimension.h" +typedef enum { + DMNSN_AST_FLOAT, + DMNSN_AST_VECTOR, + DMNSN_AST_BOX, +} dmnsn_astnode_type; + +typedef struct dmnsn_astnode dmnsn_astnode; + +struct dmnsn_astnode { + dmnsn_astnode_type type; + + /* Child nodes */ + dmnsn_array *children; + + /* Generic data pointer */ + void *ptr; +}; + /* The workhorse */ -dmnsn_array *dmnsn_parse(dmnsn_array *tokens); +dmnsn_array *dmnsn_parse(const dmnsn_array *tokens); /* Free an abstract syntax tree */ void dmnsn_delete_astree(dmnsn_array *astree); diff --git a/dimension/realize.c b/dimension/realize.c index 7f5e385..67536e6 100644 --- a/dimension/realize.c +++ b/dimension/realize.c @@ -22,7 +22,53 @@ #include "utility.h" dmnsn_scene * -dmnsn_realize(dmnsn_array *astree) +dmnsn_realize(const dmnsn_array *astree) { - return dmnsn_new_scene(); + dmnsn_scene *scene = dmnsn_new_scene(); + if (!scene) { + return NULL; + } + + /* Background color */ + dmnsn_sRGB background_sRGB = { .R = 0.0, .G = 0.05, .B = 0.1 }; + scene->background = dmnsn_color_from_sRGB(background_sRGB); + + /* Allocate a canvas */ + scene->canvas = dmnsn_new_canvas(768, 480); + if (!scene->canvas) { + dmnsn_delete_realized_scene(scene); + return NULL; + } + + /* Set up the transformation matrix for the perspective camera */ + dmnsn_matrix trans = dmnsn_scale_matrix( + dmnsn_vector_construct( + ((double)scene->canvas->x)/scene->canvas->y, 1.0, 1.0 + ) + ); + trans = dmnsn_matrix_mul( + dmnsn_translation_matrix(dmnsn_vector_construct(0.0, 0.0, -4.0)), + trans + ); + trans = dmnsn_matrix_mul( + dmnsn_rotation_matrix(dmnsn_vector_construct(0.0, 1.0, 0.0)), + trans + ); + + /* Create a perspective camera */ + scene->camera = dmnsn_new_perspective_camera(); + if (!scene->camera) { + dmnsn_delete_realized_scene(scene); + return NULL; + } + + return scene; +} + +void +dmnsn_delete_realized_scene(dmnsn_scene *scene) +{ + dmnsn_delete_camera(scene->camera); + dmnsn_delete_canvas(scene->canvas); + dmnsn_delete_scene(scene); } diff --git a/dimension/realize.h b/dimension/realize.h index fa2b6eb..73ea109 100644 --- a/dimension/realize.h +++ b/dimension/realize.h @@ -19,4 +19,5 @@ #include "../libdimension/dimension.h" -dmnsn_scene *dmnsn_realize(dmnsn_array *astree); +dmnsn_scene *dmnsn_realize(const dmnsn_array *astree); +void dmnsn_delete_realized_scene(dmnsn_scene *scene); diff --git a/dimension/tokenize.c b/dimension/tokenize.c index 1a74d44..acd48f5 100644 --- a/dimension/tokenize.c +++ b/dimension/tokenize.c @@ -1124,7 +1124,7 @@ dmnsn_token_name(dmnsn_token_type token_type) dmnsn_token_map(DMNSN_T_NOT_EQUAL, "!="); /* Numeric values */ - dmnsn_token_map(DMNSN_T_INTEGER, "int"); + dmnsn_token_map(DMNSN_T_INTEGER, "integer"); dmnsn_token_map(DMNSN_T_FLOAT, "float"); /* Keywords */ diff --git a/tests/dimension/Makefile.am b/tests/dimension/Makefile.am index 8bf7550..36d0989 100644 --- a/tests/dimension/Makefile.am +++ b/tests/dimension/Makefile.am @@ -19,27 +19,19 @@ INCLUDES = -I$(top_srcdir)/libdimension -TESTS = punctuation.sh numeric.sh strings.sh labels.sh directives.sh +TESTS = punctuation.sh numeric.sh strings.sh labels.sh directives.sh box.sh TESTS_ENVIRONMENT = top_builddir=$(top_builddir) -punctuation.sh: - cp $(srcdir)/punctuation.sh . - -numeric.sh: - cp $(srcdir)/numeric.sh . - -strings.sh: - cp $(srcdir)/strings.sh . - -labels.sh: - cp $(srcdir)/labels.sh . - -directives.sh: - cp $(srcdir)/directives.sh . +%.sh: + cp $(srcdir)/$@ . EXTRA_DIST = $(TESTS) \ punctuation.pov \ numeric.pov \ strings.pov \ labels.pov \ - directives.pov + directives.pov \ + box.pov + +clean-local: + rm *.png diff --git a/tests/dimension/box.pov b/tests/dimension/box.pov new file mode 100644 index 0000000..47b1cde --- /dev/null +++ b/tests/dimension/box.pov @@ -0,0 +1,24 @@ +/************************************************************************* + * Copyright (C) 2009 Tavian Barnes <tavianator@gmail.com> * + * * + * This file is part of The Dimension Test Suite. * + * * + * The Dimension Test Suite is free software; you can redistribute it * + * and/or modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Test Suite is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *************************************************************************/ + +// Render a box + +box { + <-1, -1, -1>, <1, 1, 1> +} diff --git a/tests/dimension/box.sh b/tests/dimension/box.sh new file mode 100755 index 0000000..244b6fa --- /dev/null +++ b/tests/dimension/box.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +######################################################################### +# Copyright (C) 2009 Tavian Barnes <tavianator@gmail.com> # +# # +# This file is part of The Dimension Test Suite. # +# # +# The Dimension Test Suite is free software; you can redistribute it # +# and/or modify it under the terms of the GNU General Public License as # +# published by the Free Software Foundation; either version 3 of the # +# License, or (at your option) any later version. # +# # +# The Dimension Test Suite is distributed in the hope that it will be # +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty # +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # +# General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see <http://www.gnu.org/licenses/>. # +######################################################################### + +${top_builddir}/dimension/dimension -o box.png ${srcdir}/box.pov diff --git a/tests/dimension/numeric.sh b/tests/dimension/numeric.sh index b64c218..d4b4d89 100755 --- a/tests/dimension/numeric.sh +++ b/tests/dimension/numeric.sh @@ -20,7 +20,7 @@ ######################################################################### numeric=$(${top_builddir}/dimension/dimension --tokenize ${srcdir}/numeric.pov) -numeric_exp='((int "1") (int "123456789") (int "01234567") (int "0x123456789") - (int "0x01") (float ".1") (float "0.1") (float "1.0") (float "0.123456789") - (float "0.123456789") < (int "1") , (float "2.2") , - (float "3.03") >)' +numeric_exp='((integer "1") (integer "123456789") (integer "01234567") (integer "0x123456789") - (integer "0x01") (float ".1") (float "0.1") (float "1.0") (float "0.123456789") - (float "0.123456789") < (integer "1") , (float "2.2") , - (float "3.03") >)' if [ "$numeric" != "$numeric_exp" ]; then echo "numeric.pov tokenized as \"$numeric\"" >&2 |