summaryrefslogtreecommitdiffstats
path: root/dimension
diff options
context:
space:
mode:
Diffstat (limited to 'dimension')
-rw-r--r--dimension/main.c9
-rw-r--r--dimension/parse.c231
-rw-r--r--dimension/parse.h20
-rw-r--r--dimension/realize.c50
-rw-r--r--dimension/realize.h3
-rw-r--r--dimension/tokenize.c2
6 files changed, 305 insertions, 10 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 */