From 3ee98f3bac24fd1c70a9de3e0fbe774e762c25b3 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 26 Jun 2009 15:31:34 +0000 Subject: Add lots of comments, and some code fixes discovered in the process. --- libdimension/array.c | 25 +++++++++- libdimension/camera.c | 10 ++++ libdimension/canvas.c | 9 ++-- libdimension/color.c | 6 ++- libdimension/cube.c | 4 ++ libdimension/dimension.h | 4 ++ libdimension/dimension/array.h | 14 +++--- libdimension/dimension/camera.h | 7 ++- libdimension/dimension/canvas.h | 7 +-- libdimension/dimension/color.h | 6 +-- libdimension/dimension/cube.h | 6 +-- libdimension/dimension/error.h | 19 ++++---- libdimension/dimension/geometry.h | 4 +- libdimension/dimension/object.h | 7 +-- libdimension/dimension/png.h | 4 ++ libdimension/dimension/progress.h | 14 +++++- libdimension/dimension/raytrace.h | 4 ++ libdimension/dimension/scene.h | 6 +-- libdimension/dimension/sphere.h | 6 +-- libdimension/error.c | 1 + libdimension/object.c | 2 + libdimension/png.c | 36 +++++++++----- libdimension/progress.c | 31 ++++++------ libdimension/raytrace.c | 15 ++++-- libdimension/scene.c | 2 + libdimension/sphere.c | 7 ++- libdimensionxx/cookie-fopencookie.cpp | 52 +++++++++++++++++++-- libdimensionxx/cookie-tmpfile.cpp | 83 +++++++++++++++++++++++++++------ libdimensionxx/dimensionxx/array.hpp | 7 ++- libdimensionxx/dimensionxx/canvas.hpp | 4 +- libdimensionxx/dimensionxx/color.hpp | 4 +- libdimensionxx/dimensionxx/cookie.hpp | 4 +- libdimensionxx/dimensionxx/error.hpp | 6 +-- libdimensionxx/dimensionxx/geometry.hpp | 55 +++++++++++++++++++++- libdimensionxx/dimensionxx/object.hpp | 4 +- libdimensionxx/dimensionxx/png.hpp | 4 +- libdimensionxx/dimensionxx/progress.hpp | 4 +- libdimensionxx/progress.cpp | 25 ++++++++-- tests/error.c | 2 + tests/png.c | 3 ++ tests/pngxx.cpp | 3 ++ tests/raytrace.c | 2 + tests/tests.c | 1 + tests/tests.h | 1 + tests/testsxx.cpp | 1 + tests/testsxx.hpp | 1 + tests/warning.c | 2 + 47 files changed, 401 insertions(+), 123 deletions(-) diff --git a/libdimension/array.c b/libdimension/array.c index f8f3c58..82588aa 100644 --- a/libdimension/array.c +++ b/libdimension/array.c @@ -26,6 +26,7 @@ static void dmnsn_array_get_impl(const dmnsn_array *array, size_t i, void *obj); static void dmnsn_array_set_impl(dmnsn_array *array, size_t i, const void *obj); +/* Allocate a new array - guaranteed not to fail if it returns */ dmnsn_array * dmnsn_new_array(size_t obj_size) { @@ -35,11 +36,13 @@ dmnsn_new_array(size_t obj_size) array->length = 0; array->capacity = 4; /* Start with capacity of 4 */ + /* Allocate the memory */ array->ptr = malloc(array->capacity*array->obj_size); if (!array->ptr) { dmnsn_error(DMNSN_SEVERITY_HIGH, "Array allocation failed."); } + /* Allocate the read-write lock */ array->rwlock = malloc(sizeof(pthread_rwlock_t)); if (!array->rwlock || pthread_rwlock_init(array->rwlock, NULL) != 0) { dmnsn_error(DMNSN_SEVERITY_HIGH, "Array rwlock allocation failed."); @@ -49,6 +52,7 @@ dmnsn_new_array(size_t obj_size) return array; } +/* Delete the array */ void dmnsn_delete_array(dmnsn_array *array) { if (array) { if (pthread_rwlock_destroy(array->rwlock) != 0) { @@ -61,6 +65,7 @@ void dmnsn_delete_array(dmnsn_array *array) { } } +/* Push obj to the end of the array, atomically */ void dmnsn_array_push(dmnsn_array *array, const void *obj) { @@ -69,6 +74,7 @@ dmnsn_array_push(dmnsn_array *array, const void *obj) dmnsn_array_unlock(array); } +/* Pop obj from the end of the array, atomically */ void dmnsn_array_pop(dmnsn_array *array, void *obj) { @@ -76,11 +82,12 @@ dmnsn_array_pop(dmnsn_array *array, void *obj) dmnsn_array_wrlock(array); size = dmnsn_array_size_unlocked(array); - dmnsn_array_get_impl(array, size - 1, obj); - dmnsn_array_resize_unlocked(array, size - 1); + dmnsn_array_get_impl(array, size - 1, obj); /* Copy the object */ + dmnsn_array_resize_unlocked(array, size - 1); /* Shrink the array */ dmnsn_array_unlock(array); } +/* Get the i'th object, bailing out if i is out of range */ void dmnsn_array_get(const dmnsn_array *array, size_t i, void *obj) { @@ -89,6 +96,7 @@ dmnsn_array_get(const dmnsn_array *array, size_t i, void *obj) dmnsn_array_unlock(array); } +/* Set the i'th object, expanding the array if necessary */ void dmnsn_array_set(dmnsn_array *array, size_t i, const void *obj) { @@ -97,6 +105,7 @@ dmnsn_array_set(dmnsn_array *array, size_t i, const void *obj) dmnsn_array_unlock(array); } +/* Get the size of the array, atomically */ size_t dmnsn_array_size(const dmnsn_array *array) { @@ -109,6 +118,7 @@ dmnsn_array_size(const dmnsn_array *array) return size; } +/* Set the size of the array, atomically */ void dmnsn_array_resize(dmnsn_array *array, size_t length) { @@ -117,6 +127,7 @@ dmnsn_array_resize(dmnsn_array *array, size_t length) dmnsn_array_unlock(array); } +/* Thread-unsafe range-checked element access */ void * dmnsn_array_at(dmnsn_array *array, size_t i) { @@ -126,16 +137,19 @@ dmnsn_array_at(dmnsn_array *array, size_t i) return (char *)array->ptr + array->obj_size*i; } +/* Get the size non-atomically, for manual locking */ size_t dmnsn_array_size_unlocked(const dmnsn_array *array) { return array->length; } +/* Set the size non-atomically, for manual locking */ void dmnsn_array_resize_unlocked(dmnsn_array *array, size_t length) { if (length > array->capacity) { + /* Resize if we don't have enough capacity */ array->capacity = length*2; /* We are greedy */ array->ptr = realloc(array->ptr, array->obj_size*array->capacity); if (!array->ptr) { @@ -146,6 +160,7 @@ dmnsn_array_resize_unlocked(dmnsn_array *array, size_t length) array->length = length; } +/* Set a manual read-lock */ void dmnsn_array_rdlock(const dmnsn_array *array) { @@ -157,6 +172,7 @@ dmnsn_array_rdlock(const dmnsn_array *array) } } +/* Set a manual write-lock */ void dmnsn_array_wrlock(dmnsn_array *array) { @@ -168,6 +184,7 @@ dmnsn_array_wrlock(dmnsn_array *array) } } +/* Unset a manual lock */ void dmnsn_array_unlock(const dmnsn_array *array) { @@ -178,19 +195,23 @@ dmnsn_array_unlock(const dmnsn_array *array) } } +/* Actual "get" implementation */ static void dmnsn_array_get_impl(const dmnsn_array *array, size_t i, void *obj) { if (i >= dmnsn_array_size_unlocked(array)) { + /* Range check failed */ dmnsn_error(DMNSN_SEVERITY_HIGH, "Array index out of bounds."); } memcpy(obj, array->ptr + array->obj_size*i, array->obj_size); } +/* Actual "set" implementation */ static void dmnsn_array_set_impl(dmnsn_array *array, size_t i, const void *obj) { if (i >= dmnsn_array_size_unlocked(array)) { + /* Resize if i is out of range */ dmnsn_array_resize_unlocked(array, i + 1); } memcpy(array->ptr + array->obj_size*i, obj, array->obj_size); diff --git a/libdimension/camera.c b/libdimension/camera.c index 282daaf..c353cbe 100644 --- a/libdimension/camera.c +++ b/libdimension/camera.c @@ -21,12 +21,14 @@ #include "dimension.h" #include /* For malloc */ +/* Allocate a new dummy camera */ dmnsn_camera * dmnsn_new_camera() { return malloc(sizeof(dmnsn_camera)); } +/* Free a dummy camera */ void dmnsn_delete_camera(dmnsn_camera *camera) { @@ -35,11 +37,15 @@ dmnsn_delete_camera(dmnsn_camera *camera) /* Perspective camera */ +/* Perspective camera ray callback */ static dmnsn_line dmnsn_perspective_camera_ray_fn(const dmnsn_camera *camera, const dmnsn_canvas *canvas, unsigned int x, unsigned int y); +/* Create a new perspective camera. Rays are aimed from the origin to a screen + located on the z = 1 frame, from (-0.5, -0.5) to (0.5, 0.5). Rays are then + transformed by the matrix `trans'. */ dmnsn_camera * dmnsn_new_perspective_camera(dmnsn_matrix trans) { @@ -57,6 +63,7 @@ dmnsn_new_perspective_camera(dmnsn_matrix trans) return camera; } +/* Delete a perspective camera */ void dmnsn_delete_perspective_camera(dmnsn_camera *camera) { @@ -74,7 +81,10 @@ dmnsn_perspective_camera_ray_fn(const dmnsn_camera *camera, dmnsn_matrix *trans = (dmnsn_matrix *)camera->ptr; dmnsn_line l; + /* Rays originate at the origin, oddly enough */ l.x0 = dmnsn_vector_construct(0.0, 0.0, 0.0); + + /* Aim at the z = 1 plane */ l.n.x = ((double)x)/(canvas->x - 1) - 0.5; l.n.y = ((double)y)/(canvas->y - 1) - 0.5; l.n.z = 1.0; diff --git a/libdimension/canvas.c b/libdimension/canvas.c index 516c978..13b1e9e 100644 --- a/libdimension/canvas.c +++ b/libdimension/canvas.c @@ -32,8 +32,6 @@ dmnsn_new_canvas(unsigned int x, unsigned int y) dmnsn_canvas *canvas = malloc(sizeof(dmnsn_canvas)); if (canvas) { - /* *canvas exists */ - /* Set the width and height */ canvas->x = x; canvas->y = y; @@ -60,6 +58,7 @@ dmnsn_new_canvas(unsigned int x, unsigned int y) /* pthread_rwlock_init failed. Destroy the locks we've already made, free the canvas, and return NULL. We leak memory if destruction fails (i.e. someone is somehow using an rwlock already). */ + for (l = 0; l < j; ++l) { for (k = 0; k < x; ++k) { if (pthread_rwlock_destroy(&canvas->rwlocks[l*x + k]) != 0) { @@ -96,8 +95,6 @@ dmnsn_delete_canvas(dmnsn_canvas *canvas) unsigned int i, j; if (canvas) { - /* *canvas exists */ - /* Destroy the rwlocks */ for (i = 0; i < canvas->x; ++i) { for (j = 0; j < canvas->y; ++j) { @@ -121,7 +118,7 @@ dmnsn_get_pixel(const dmnsn_canvas *canvas, unsigned int x, unsigned int y) { dmnsn_color color; dmnsn_rdlock_pixel(canvas, x, y); - color = canvas->pixels[y*canvas->x + x]; + color = canvas->pixels[y*canvas->x + x]; dmnsn_unlock_pixel(canvas, x, y); return color; } @@ -132,7 +129,7 @@ dmnsn_set_pixel(dmnsn_canvas *canvas, unsigned int x, unsigned int y, dmnsn_color color) { dmnsn_wrlock_pixel(canvas, x, y); - canvas->pixels[y*canvas->x + x] = color; + canvas->pixels[y*canvas->x + x] = color; dmnsn_unlock_pixel(canvas, x, y); } diff --git a/libdimension/color.c b/libdimension/color.c index 42ade8b..4aede1f 100644 --- a/libdimension/color.c +++ b/libdimension/color.c @@ -21,7 +21,7 @@ #include "dimension.h" #include /* For pow(), sqrt() */ -/* sRGB white point (D50) */ +/* sRGB white point (CIE D50) */ const dmnsn_CIE_XYZ dmnsn_whitepoint = { .X = 0.9504060171449392, .Y = 0.9999085943425312, .Z = 1.089062231497274 }; @@ -42,7 +42,8 @@ dmnsn_color_from_xyY(dmnsn_CIE_xyY xyY) dmnsn_color ret = { .X = xyY.Y*xyY.x/xyY.y, .Y = xyY.Y, .Z = xyY.Y*(1.0 - xyY.x - xyY.y)/xyY.y, - .filter = 0.0, .trans = 0.0 }; + .filter = 0.0, + .trans = 0.0 }; return ret; } @@ -267,6 +268,7 @@ dmnsn_color_add(dmnsn_color color1, dmnsn_color color2) Lab.b = Lab1.b + Lab2.b; ret = dmnsn_color_from_Lab(Lab, dmnsn_whitepoint); + /* Waited average of transparencies by intensity */ ret.filter = (Lab1.L*color1.filter + Lab2.L*color2.filter)/Lab.L; ret.trans = (Lab1.L*color1.trans + Lab2.L*color2.trans)/Lab.L; diff --git a/libdimension/cube.c b/libdimension/cube.c index 197a0f8..bb71d0b 100644 --- a/libdimension/cube.c +++ b/libdimension/cube.c @@ -27,6 +27,7 @@ static dmnsn_array *dmnsn_cube_intersections_fn(const dmnsn_object *cube, static int dmnsn_cube_inside_fn(const dmnsn_object *cube, dmnsn_vector point); +/* Allocate a new cube object */ dmnsn_object * dmnsn_new_cube() { @@ -38,12 +39,14 @@ dmnsn_new_cube() return cube; } +/* Delete a cube */ void dmnsn_delete_cube(dmnsn_object *cube) { dmnsn_delete_object(cube); } +/* Intersections callback for a cube */ static dmnsn_array * dmnsn_cube_intersections_fn(const dmnsn_object *cube, dmnsn_line line) { @@ -104,6 +107,7 @@ dmnsn_cube_intersections_fn(const dmnsn_object *cube, dmnsn_line line) return array; } +/* Inside callback for a cube */ static int dmnsn_cube_inside_fn(const dmnsn_object *cube, dmnsn_vector point) { diff --git a/libdimension/dimension.h b/libdimension/dimension.h index 5820200..5e262e3 100644 --- a/libdimension/dimension.h +++ b/libdimension/dimension.h @@ -18,6 +18,10 @@ * . * *************************************************************************/ +/* + * libdimension - a library for photo-realistic 3-D rendering + */ + #ifndef DIMENSION_H #define DIMENSION_H diff --git a/libdimension/dimension/array.h b/libdimension/dimension/array.h index d0693fc..0af4406 100644 --- a/libdimension/dimension/array.h +++ b/libdimension/dimension/array.h @@ -18,14 +18,14 @@ * . * *************************************************************************/ -#ifndef DIMENSION_ARRAY_H -#define DIMENSION_ARRAY_H - /* * Simple thread-safe generalized arrays, for returning variable-length arrays * from functions, and other fun stuff. */ +#ifndef DIMENSION_ARRAY_H +#define DIMENSION_ARRAY_H + #include /* For pthread_rwlock_t */ #include /* For size_t */ @@ -37,24 +37,26 @@ typedef struct { pthread_rwlock_t *rwlock; } dmnsn_array; +/* Array allocation never returns NULL - if dmnsn_new_array, it succeeded */ dmnsn_array *dmnsn_new_array(size_t obj_size); void dmnsn_delete_array(dmnsn_array *array); +/* Thread-safe atomic array access */ + void dmnsn_array_push(dmnsn_array *array, const void *obj); void dmnsn_array_pop(dmnsn_array *array, void *obj); - void dmnsn_array_get(const dmnsn_array *array, size_t i, void *obj); void dmnsn_array_set(dmnsn_array *array, size_t i, const void *obj); size_t dmnsn_array_size(const dmnsn_array *array); void dmnsn_array_resize(dmnsn_array *array, size_t length); -/* Manual locking */ - +/* Non-atomic operations for manual locking */ void *dmnsn_array_at(dmnsn_array *array, size_t i); size_t dmnsn_array_size_unlocked(const dmnsn_array *array); void dmnsn_array_resize_unlocked(dmnsn_array *array, size_t length); +/* Manual locking */ void dmnsn_array_rdlock(const dmnsn_array *array); void dmnsn_array_wrlock(dmnsn_array *array); void dmnsn_array_unlock(const dmnsn_array *array); diff --git a/libdimension/dimension/camera.h b/libdimension/dimension/camera.h index 95b36fa..9b06359 100644 --- a/libdimension/dimension/camera.h +++ b/libdimension/dimension/camera.h @@ -18,13 +18,13 @@ * . * *************************************************************************/ -#ifndef DIMENSION_CAMERA_H -#define DIMENSION_CAMERA_H - /* * A camera. */ +#ifndef DIMENSION_CAMERA_H +#define DIMENSION_CAMERA_H + /* Forward-declare dmnsn_camera */ typedef struct dmnsn_camera dmnsn_camera; @@ -47,7 +47,6 @@ void dmnsn_delete_camera(dmnsn_camera *camera); /* A perspective camera, at the origin, looking at (0, 0, 1). The feild of view is the section of the plane z = 1 from (-0.5, -0.5) to (0.5, 0.5). Rays are transformed by the transformation matrix `trans'. */ - dmnsn_camera *dmnsn_new_perspective_camera(dmnsn_matrix trans); void dmnsn_delete_perspective_camera(dmnsn_camera *camera); diff --git a/libdimension/dimension/canvas.h b/libdimension/dimension/canvas.h index f05264b..50cc612 100644 --- a/libdimension/dimension/canvas.h +++ b/libdimension/dimension/canvas.h @@ -18,14 +18,15 @@ * . * *************************************************************************/ +/* + * A canvas which is rendered to. + */ + #ifndef DIMENSION_CANVAS_H #define DIMENSION_CANVAS_H #include -/* - * A canvas which is rendered to. - */ typedef struct { unsigned int x, y; diff --git a/libdimension/dimension/color.h b/libdimension/dimension/color.h index ab6efa3..f1964c3 100644 --- a/libdimension/dimension/color.h +++ b/libdimension/dimension/color.h @@ -18,13 +18,13 @@ * . * *************************************************************************/ -#ifndef DIMENSION_COLOR_H -#define DIMENSION_COLOR_H - /* * Types to represent color. */ +#ifndef DIMENSION_COLOR_H +#define DIMENSION_COLOR_H + /* Internally, we use CIE 1931 XYZ color. */ typedef struct { double X, Y, Z; diff --git a/libdimension/dimension/cube.h b/libdimension/dimension/cube.h index e7f2b90..e55ec74 100644 --- a/libdimension/dimension/cube.h +++ b/libdimension/dimension/cube.h @@ -18,13 +18,13 @@ * . * *************************************************************************/ -#ifndef DIMENSION_CUBE_H -#define DIMENSION_CUBE_H - /* * A cube, axis-aligned, from (-1, -1, -1) to (1, 1, 1) */ +#ifndef DIMENSION_CUBE_H +#define DIMENSION_CUBE_H + dmnsn_object *dmnsn_new_cube(); void dmnsn_delete_cube(dmnsn_object *cube); diff --git a/libdimension/dimension/error.h b/libdimension/dimension/error.h index 3204cb2..cd7dd81 100644 --- a/libdimension/dimension/error.h +++ b/libdimension/dimension/error.h @@ -18,23 +18,26 @@ * . * *************************************************************************/ -#ifndef DIMENSION_ERROR_H -#define DIMENSION_ERROR_H - /* - * Error handling. + * Error handling. Errors are reported at a given severity by the dmnsn_error() + * macro at a given severity, which prints a warning if it is below the set + * resilience, or prints an error and exits if it's at or above the set + * resilience. */ +#ifndef DIMENSION_ERROR_H +#define DIMENSION_ERROR_H + typedef enum { DMNSN_SEVERITY_LOW, /* Only die on low resilience */ DMNSN_SEVERITY_MEDIUM, /* Die on low or medium resilience */ DMNSN_SEVERITY_HIGH /* Always die */ } dmnsn_severity; -/* Use this to report an error */ -#define dmnsn_error(severity, str) \ - dmnsn_report_error((dmnsn_severity)severity, __PRETTY_FUNCTION__, __LINE__, \ - str) +/* Use this macro to report an error */ +#define dmnsn_error(severity, str) \ + dmnsn_report_error((dmnsn_severity)(severity), __PRETTY_FUNCTION__, __LINE__,\ + (str)) /* Called by dmnsn_error() - don't call directly */ void dmnsn_report_error(dmnsn_severity severity, diff --git a/libdimension/dimension/geometry.h b/libdimension/dimension/geometry.h index 95692c6..c635fd9 100644 --- a/libdimension/dimension/geometry.h +++ b/libdimension/dimension/geometry.h @@ -19,7 +19,7 @@ *************************************************************************/ /* - * Core geometric types like scalars, vectors, and rays. + * Core geometric types like vectors, matricies, and rays. */ #ifndef DIMENSION_GEOMETRY_H @@ -73,6 +73,6 @@ dmnsn_line dmnsn_matrix_line_mul(dmnsn_matrix lhs, dmnsn_line rhs); /* A point on a line, defined by x0 + t*n */ dmnsn_vector dmnsn_line_point(dmnsn_line l, double t); /* Solve for the t value such that x0 + t*n = x */ -double dmnsn_line_index(dmnsn_line l, dmnsn_vector x); +double dmnsn_line_index(dmnsn_line l, dmnsn_vector x); #endif /* DIMENSION_GEOMETRY_H */ diff --git a/libdimension/dimension/object.h b/libdimension/dimension/object.h index 589ac29..d18f426 100644 --- a/libdimension/dimension/object.h +++ b/libdimension/dimension/object.h @@ -18,13 +18,13 @@ * . * *************************************************************************/ -#ifndef DIMENSION_OBJECT_H -#define DIMENSION_OBJECT_H - /* * Objects. */ +#ifndef DIMENSION_OBJECT_H +#define DIMENSION_OBJECT_H + /* Forward-declare dmnsn_object */ typedef struct dmnsn_object dmnsn_object; @@ -46,6 +46,7 @@ struct dmnsn_object { dmnsn_object_inside_fn *inside_fn; }; +/* Allocate a dummy object */ dmnsn_object *dmnsn_new_object(); void dmnsn_delete_object(dmnsn_object *object); diff --git a/libdimension/dimension/png.h b/libdimension/dimension/png.h index 05ff79a..858f79f 100644 --- a/libdimension/dimension/png.h +++ b/libdimension/dimension/png.h @@ -18,6 +18,10 @@ * . * *************************************************************************/ +/* + * Support for exporting/importing canvases to/from PNG files + */ + #ifndef DIMENSION_PNG_H #define DIMENSION_PNG_H diff --git a/libdimension/dimension/progress.h b/libdimension/dimension/progress.h index a865c8f..7958ab9 100644 --- a/libdimension/dimension/progress.h +++ b/libdimension/dimension/progress.h @@ -18,6 +18,13 @@ * . * *************************************************************************/ +/* + * An interface for asynchronous tasks. *_async() versions of functions + * return a dmnsn_progress* object which can indicate the progress of the + * background task, and wait for task completion. The task's return value + * is returned as an int from dmnsn_finish_progress(). + */ + #ifndef DIMENSION_PROGRESS_H #define DIMENSION_PROGRESS_H @@ -43,6 +50,7 @@ typedef struct { } dmnsn_progress; dmnsn_progress *dmnsn_new_progress(); +/* For failed returns from *_async() functions */ void dmnsn_delete_progress(dmnsn_progress *progress); /* This joins the worker thread and returns it's integer return value in @@ -50,10 +58,14 @@ void dmnsn_delete_progress(dmnsn_progress *progress); int dmnsn_finish_progress(dmnsn_progress *progress); double dmnsn_get_progress(const dmnsn_progress *progress); +/* Wait for the progress to be >= prog, in a better way than spinlocking */ void dmnsn_wait_progress(const dmnsn_progress *progress, double prog); +/* Create a new level of loop nesting */ void dmnsn_new_progress_element(dmnsn_progress *progress, unsigned int total); +/* Increment the progress counter; should only be called from innermost loop */ void dmnsn_increment_progress(dmnsn_progress *progress); -void dmnsn_progress_done(dmnsn_progress *progress); +/* Instantly complete the progress */ +void dmnsn_done_progress(dmnsn_progress *progress); #endif /* DIMENSION_PROGRESS_H */ diff --git a/libdimension/dimension/raytrace.h b/libdimension/dimension/raytrace.h index a69bb72..b58a39b 100644 --- a/libdimension/dimension/raytrace.h +++ b/libdimension/dimension/raytrace.h @@ -18,6 +18,10 @@ * . * *************************************************************************/ +/* + * Render a scene by raytracing + */ + #ifndef DIMENSION_RAYTRACE_H #define DIMENSION_RAYTRACE_H diff --git a/libdimension/dimension/scene.h b/libdimension/dimension/scene.h index a6f2e5d..35087a7 100644 --- a/libdimension/dimension/scene.h +++ b/libdimension/dimension/scene.h @@ -18,13 +18,13 @@ * . * *************************************************************************/ -#ifndef DIMENSION_SCENE_H -#define DIMENSION_SCENE_H - /* * A scene. */ +#ifndef DIMENSION_SCENE_H +#define DIMENSION_SCENE_H + typedef struct { dmnsn_color background; dmnsn_array *objects; diff --git a/libdimension/dimension/sphere.h b/libdimension/dimension/sphere.h index 2547965..d7a8f54 100644 --- a/libdimension/dimension/sphere.h +++ b/libdimension/dimension/sphere.h @@ -18,13 +18,13 @@ * . * *************************************************************************/ -#ifndef DIMENSION_SPHERE_H -#define DIMENSION_SPHERE_H - /* * A sphere object, of radius 1, centered at the origin. */ +#ifndef DIMENSION_SPHERE_H +#define DIMENSION_SPHERE_H + dmnsn_object *dmnsn_new_sphere(); void dmnsn_delete_sphere(dmnsn_object *sphere); diff --git a/libdimension/error.c b/libdimension/error.c index a8c7ec7..df3ade3 100644 --- a/libdimension/error.c +++ b/libdimension/error.c @@ -63,6 +63,7 @@ dmnsn_get_resilience() return resilience; } +/* Set the resilience, thread-safely */ void dmnsn_set_resilience(dmnsn_severity resilience) { diff --git a/libdimension/object.c b/libdimension/object.c index 75a82b6..dddcd61 100644 --- a/libdimension/object.c +++ b/libdimension/object.c @@ -21,6 +21,7 @@ #include "dimension.h" #include /* For malloc */ +/* Allocate a dummy object */ dmnsn_object * dmnsn_new_object() { @@ -31,6 +32,7 @@ dmnsn_new_object() return object; } +/* Free a dummy object */ void dmnsn_delete_object(dmnsn_object *object) { diff --git a/libdimension/png.c b/libdimension/png.c index b8cfabc..6bb1f13 100644 --- a/libdimension/png.c +++ b/libdimension/png.c @@ -25,6 +25,8 @@ #include #include +/* Payload to store function arguments for thread callbacks */ + typedef struct { dmnsn_progress *progress; const dmnsn_canvas *canvas; @@ -37,6 +39,7 @@ typedef struct { FILE *file; } dmnsn_png_read_payload; +/* Thread callbacks */ static void *dmnsn_png_write_canvas_thread(void *ptr); static void *dmnsn_png_read_canvas_thread(void *ptr); @@ -67,11 +70,11 @@ dmnsn_png_write_canvas_async(const dmnsn_canvas *canvas, FILE *file) payload->canvas = canvas; payload->file = file; + /* Create the worker thread */ if (pthread_create(&progress->thread, NULL, &dmnsn_png_write_canvas_thread, payload) != 0) { - dmnsn_error(DMNSN_SEVERITY_MEDIUM, - "Creating png writing worker thread failed."); + free(payload); dmnsn_delete_progress(progress); return NULL; } @@ -108,11 +111,11 @@ dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, FILE *file) payload->canvas = canvas; payload->file = file; + /* Create the worker thread */ if (pthread_create(&progress->thread, NULL, &dmnsn_png_read_canvas_thread, payload) != 0) { - dmnsn_error(DMNSN_SEVERITY_MEDIUM, - "Creating png writing worker thread failed."); + free(payload); dmnsn_delete_progress(progress); return NULL; } @@ -121,10 +124,14 @@ dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, FILE *file) return progress; } +/* Actual implementations */ static int dmnsn_png_write_canvas_impl(dmnsn_progress *progress, const dmnsn_canvas *canvas, FILE *file); static dmnsn_canvas *dmnsn_png_read_canvas_impl(dmnsn_progress *progress, FILE *file); + +/* Thread callbacks */ + static void * dmnsn_png_write_canvas_thread(void *ptr) { @@ -134,7 +141,8 @@ dmnsn_png_write_canvas_thread(void *ptr) *retval = dmnsn_png_write_canvas_impl(payload->progress, payload->canvas, payload->file); } - dmnsn_progress_done(payload->progress); + dmnsn_done_progress(payload->progress); + free(payload); return retval; } @@ -146,12 +154,14 @@ dmnsn_png_read_canvas_thread(void *ptr) if (retval) { *payload->canvas = dmnsn_png_read_canvas_impl(payload->progress, payload->file); - *retval = payload->canvas ? 0 : 1; + *retval = payload->canvas ? 0 : 1; /* Fail if it returned NULL */ } - dmnsn_progress_done(payload->progress); + dmnsn_done_progress(payload->progress); + free(payload); return retval; } +/* Actually write the PNG file */ static int dmnsn_png_write_canvas_impl(dmnsn_progress *progress, const dmnsn_canvas *canvas, FILE *file) @@ -272,9 +282,11 @@ static pthread_key_t progress_key; static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER; static int progress_key_init = 0; +/* Callback to increment the progress after a row has been read */ static void dmnsn_png_read_row_callback(png_structp png_ptr, png_uint_32 row, int pass); +/* Actually read a PNG file */ static dmnsn_canvas * dmnsn_png_read_canvas_impl(dmnsn_progress *progress, FILE *file) { @@ -301,9 +313,7 @@ dmnsn_png_read_canvas_impl(dmnsn_progress *progress, FILE *file) if (progress_key_init == 0) { if (pthread_key_create(&progress_key, NULL) != 0) { - /* High severity because dmnsn_png_read_row_callback will surely segfault - if it can't get the dmnsn_progress* from the key */ - dmnsn_error(DMNSN_SEVERITY_HIGH, + dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't create thread-specific pointer."); } @@ -311,7 +321,7 @@ dmnsn_png_read_canvas_impl(dmnsn_progress *progress, FILE *file) } if (pthread_setspecific(progress_key, progress) != 0) { - dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't set thread-specific pointer."); + dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't set thread-specific pointer."); } if (pthread_mutex_unlock(&progress_mutex) != 0) { @@ -506,5 +516,7 @@ static void dmnsn_png_read_row_callback(png_structp png_ptr, png_uint_32 row, int pass) { dmnsn_progress *progress = pthread_getspecific(progress_key); - dmnsn_increment_progress(progress); + if (progress) { + dmnsn_increment_progress(progress); + } } \ No newline at end of file diff --git a/libdimension/progress.c b/libdimension/progress.c index 29469a6..f25af74 100644 --- a/libdimension/progress.c +++ b/libdimension/progress.c @@ -32,6 +32,8 @@ dmnsn_new_progress() progress->elements = dmnsn_new_array(sizeof(dmnsn_progress_element)); dmnsn_array_push(progress->elements, &element); + /* Allocate space for the condition variable and mutex */ + progress->cond = malloc(sizeof(pthread_cond_t)); if (!progress->cond) { dmnsn_delete_array(progress->elements); @@ -139,22 +141,22 @@ void dmnsn_wait_progress(const dmnsn_progress *progress, double prog) { if (pthread_mutex_lock(progress->mutex) != 0) { - dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't lock condition mutex."); - } - - while (dmnsn_get_progress(progress) < prog) { - if (pthread_cond_wait(progress->cond, progress->mutex) != 0) { - dmnsn_error(DMNSN_SEVERITY_MEDIUM, - "Couldn't wait on condition variable."); + dmnsn_error(DMNSN_SEVERITY_LOW, "Couldn't lock condition mutex."); + } else { + while (dmnsn_get_progress(progress) < prog) { + if (pthread_cond_wait(progress->cond, progress->mutex) != 0) { + dmnsn_error(DMNSN_SEVERITY_LOW, + "Couldn't wait on condition variable."); + } } - } - if (pthread_mutex_unlock(progress->mutex) != 0) { - dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't unlock condition mutex."); + if (pthread_mutex_unlock(progress->mutex) != 0) { + dmnsn_error(DMNSN_SEVERITY_LOW, "Couldn't unlock condition mutex."); + } } } -/* A new level of algorithmic nesting */ +/* Start a new level of algorithmic nesting */ void dmnsn_new_progress_element(dmnsn_progress *progress, unsigned int total) { @@ -173,13 +175,14 @@ dmnsn_increment_progress(dmnsn_progress *progress) dmnsn_array_wrlock(progress->elements); size = dmnsn_array_size_unlocked(progress->elements); element = dmnsn_array_at(progress->elements, size - 1); - ++element->progress; + ++element->progress; /* Increment the last element */ while (element->progress >= element->total && size > 1) { + /* As long as the last element is complete, pop it */ --size; dmnsn_array_resize_unlocked(progress->elements, size); element = dmnsn_array_at(progress->elements, size - 1); - ++element->progress; + ++element->progress; /* Increment the next element */ } if (pthread_cond_broadcast(progress->cond) != 0) { @@ -190,7 +193,7 @@ dmnsn_increment_progress(dmnsn_progress *progress) /* Immediately set to 100% completion */ void -dmnsn_progress_done(dmnsn_progress *progress) +dmnsn_done_progress(dmnsn_progress *progress) { dmnsn_progress_element *element; diff --git a/libdimension/raytrace.c b/libdimension/raytrace.c index bd423c2..aa3a445 100644 --- a/libdimension/raytrace.c +++ b/libdimension/raytrace.c @@ -21,13 +21,17 @@ #include "dimension.h" #include /* For sysconf */ +/* Payload type for passing arguments to worker thread */ + typedef struct { dmnsn_progress *progress; dmnsn_scene *scene; } dmnsn_raytrace_payload; +/* Thread callback */ static void *dmnsn_raytrace_scene_thread(void *ptr); +/* Raytrace a scene */ void dmnsn_raytrace_scene(dmnsn_scene *scene) { @@ -35,6 +39,7 @@ dmnsn_raytrace_scene(dmnsn_scene *scene) dmnsn_finish_progress(progress); } +/* Raytrace a scene in the background */ dmnsn_progress * dmnsn_raytrace_scene_async(dmnsn_scene *scene) { @@ -54,8 +59,7 @@ dmnsn_raytrace_scene_async(dmnsn_scene *scene) if (pthread_create(&progress->thread, NULL, &dmnsn_raytrace_scene_thread, payload) != 0) { - dmnsn_error(DMNSN_SEVERITY_MEDIUM, - "Creating raytracing worker thread failed."); + free(payload); dmnsn_delete_progress(progress); return NULL; } @@ -64,9 +68,11 @@ dmnsn_raytrace_scene_async(dmnsn_scene *scene) return progress; } +/* Actual raytracing implementation */ static void dmnsn_raytrace_scene_impl(dmnsn_progress *progress, dmnsn_scene *scene); +/* Thread callback */ static void * dmnsn_raytrace_scene_thread(void *ptr) { @@ -76,7 +82,8 @@ dmnsn_raytrace_scene_thread(void *ptr) dmnsn_raytrace_scene_impl(payload->progress, payload->scene); *retval = 0; } - dmnsn_progress_done(payload->progress); + dmnsn_done_progress(payload->progress); + free(payload); return retval; } @@ -116,6 +123,7 @@ dmnsn_raytrace_scene_impl(dmnsn_progress *progress, dmnsn_scene *scene) /* Test for intersections with objects */ intersections = (*object->intersections_fn)(object, ray_trans); + /* Find the closest intersection */ for (l = 0; l < dmnsn_array_size(intersections); ++l) { dmnsn_array_get(intersections, l, &t_temp); if (t_temp < t || t == 0.0) t = t_temp; @@ -123,6 +131,7 @@ dmnsn_raytrace_scene_impl(dmnsn_progress *progress, dmnsn_scene *scene) dmnsn_delete_array(intersections); } + /* Shade according to distance from camera */ if (t != 0.0) { sRGB.R = 1.0 - (t - 2.25)/2.25; sRGB.G = sRGB.R; diff --git a/libdimension/scene.c b/libdimension/scene.c index 048ad45..9405f03 100644 --- a/libdimension/scene.c +++ b/libdimension/scene.c @@ -21,6 +21,7 @@ #include "dimension.h" #include /* For malloc */ +/* Allocate an empty scene */ dmnsn_scene * dmnsn_new_scene() { @@ -30,6 +31,7 @@ dmnsn_new_scene() return scene; } +/* Free a scene */ void dmnsn_delete_scene(dmnsn_scene *scene) { diff --git a/libdimension/sphere.c b/libdimension/sphere.c index 5a4d1a9..f6950dc 100644 --- a/libdimension/sphere.c +++ b/libdimension/sphere.c @@ -22,11 +22,13 @@ #include /* For malloc */ #include /* For sqrt */ +/* Sphere object callbacks */ static dmnsn_array *dmnsn_sphere_intersections_fn(const dmnsn_object *sphere, dmnsn_line line); static int dmnsn_sphere_inside_fn(const dmnsn_object *sphere, dmnsn_vector point); +/* Allocate a new sphere */ dmnsn_object * dmnsn_new_sphere() { @@ -38,12 +40,14 @@ dmnsn_new_sphere() return sphere; } +/* Free a sphere */ void dmnsn_delete_sphere(dmnsn_object *sphere) { dmnsn_delete_object(sphere); } +/* Return a list of insersections of `line' with a sphere */ static dmnsn_array * dmnsn_sphere_intersections_fn(const dmnsn_object *sphere, dmnsn_line line) { @@ -66,8 +70,9 @@ dmnsn_sphere_intersections_fn(const dmnsn_object *sphere, dmnsn_line line) return array; } +/* Return whether a point is inside a sphere (x**2 + y**2 + z**2 < 1.0) */ static int dmnsn_sphere_inside_fn(const dmnsn_object *sphere, dmnsn_vector point) { - return sqrt(point.x*point.x + point.y*point.y + point.z*point.z) < 1.0; + return point.x*point.x + point.y*point.y + point.z*point.z < 1.0; } diff --git a/libdimensionxx/cookie-fopencookie.cpp b/libdimensionxx/cookie-fopencookie.cpp index 78b6e8b..070b038 100644 --- a/libdimensionxx/cookie-fopencookie.cpp +++ b/libdimensionxx/cookie-fopencookie.cpp @@ -21,6 +21,7 @@ #include "dimensionxx.hpp" #ifndef _GNU_SOURCE +// For fopencookie() # define _GNU_SOURCE #endif #include @@ -99,7 +100,7 @@ namespace Dimension } if (streams->is_output()) { - // If we have an output stream, seek it too + // If we have an output stream, seek it switch (whence) { case SEEK_SET: streams->ostr().seekp(*offset, std::ios::beg); @@ -164,14 +165,55 @@ namespace Dimension // Close the file FILE_Cookie::~FILE_Cookie() { std::fclose(m_file); } + // Get the FILE* FILE* FILE_Cookie::file() { return m_file; } const FILE* FILE_Cookie::file() const { return m_file; } bool FILE_Cookie::is_input() const { return m_istr; } bool FILE_Cookie::is_output() const { return m_ostr; } - std::istream& FILE_Cookie::istr() { return *m_istr; } - const std::istream& FILE_Cookie::istr() const { return *m_istr; } - std::ostream& FILE_Cookie::ostr() { return *m_ostr; } - const std::ostream& FILE_Cookie::ostr() const { return *m_ostr; } + // Get the C++ streams + + std::istream& + FILE_Cookie::istr() + { + if (is_input()) { + return *m_istr; + } else { + throw Dimension_Error("Attempted to get input stream from non-input" + " FILE_Cookie."); + } + } + + const std::istream& + FILE_Cookie::istr() const + { + if (is_input()) { + return *m_istr; + } else { + throw Dimension_Error("Attempted to get input stream from non-input" + " FILE_Cookie."); + } + } + + std::ostream& + FILE_Cookie::ostr() + { + if (is_output()) { + return *m_ostr; + } else { + throw Dimension_Error("Attempted to get output stream from non-input" + " FILE_Cookie."); + } + } + + const std::ostream& FILE_Cookie::ostr() const + { + if (is_output()) { + return *m_ostr; + } else { + throw Dimension_Error("Attempted to get output stream from non-input" + " FILE_Cookie."); + } + } } diff --git a/libdimensionxx/cookie-tmpfile.cpp b/libdimensionxx/cookie-tmpfile.cpp index ec320d8..6a41f09 100644 --- a/libdimensionxx/cookie-tmpfile.cpp +++ b/libdimensionxx/cookie-tmpfile.cpp @@ -27,74 +27,85 @@ namespace Dimension { namespace { + // Write an input stream completely to a FILE*; this works poorly for + // console input, which may not have an EOF in the near future void write_cookie(FILE* file, std::istream& istr) { + const unsigned int bs = 8192; // Bytes to read at once unsigned int count, pos; - const unsigned int bs = 8192; char buffer[bs]; - pos = istr.tellg(); - istr.seekg(0); + pos = istr.tellg(); // Get the stream's current position + istr.seekg(0); // Seek to the beginning while (true) { + // Read the whole stream into `file', `bs' bytes at a time istr.read(buffer, bs); count = istr.gcount(); if (count != bs) { if (istr.eof()) { + // We reached EOF; write the last count bytes fwrite(buffer, 1, count, file); break; } else { + // Some other error throw Dimension_Error("Error reading from input stream."); } } + // Write the next `bs' bytes fwrite(buffer, 1, bs, file); } - fseek(file, pos, SEEK_SET); + fseek(file, pos, SEEK_SET); // Seek to the stream's initial position } + // Read a C++ stream completely from a file void read_cookie(std::ostream& ostr, FILE* file) { + const unsigned int bs = 8192; // Bytes to read at a time unsigned int count, pos; - const unsigned int bs = 8192; char buffer[bs]; - pos = ftell(file); - rewind(file); + pos = ftell(file); // Get the initial position + rewind(file); // Seek to the beginning while (true) { count = fread(buffer, 1, bs, file); if (count != bs) { if (feof(file)) { + // Reached EOF, write the last `count' bytes ostr.write(buffer, count); break; } else { + // Some other error throw Dimension_Error("Error reading from temporary file."); } } + // Write the next `bs' bytes ostr.write(buffer, bs); } - ostr.seekp(pos); + ostr.seekp(pos); // Seek to the initial position of `file' } } // Make an input FILE_Cookie FILE_Cookie::FILE_Cookie(std::istream& istr) - : m_file(tmpfile()), m_istr(&istr), m_ostr(0) + : m_file(std::tmpfile()), m_istr(&istr), m_ostr(0) { if (!m_file) { throw Dimension_Error("Error opening temporary file for C++/C I/O" " interface."); } + // Write the input stream to the temporary file write_cookie(m_file, *m_istr); } // Make an output FILE_Cookie FILE_Cookie::FILE_Cookie(std::ostream& ostr) - : m_file(tmpfile()), m_istr(0), m_ostr(&ostr) + : m_file(std::tmpfile()), m_istr(0), m_ostr(&ostr) { if (!m_file) { throw Dimension_Error("Error opening temporary file for C++/C I/O" @@ -104,13 +115,14 @@ namespace Dimension // Make an I/O FILE_Cookie FILE_Cookie::FILE_Cookie(std::iostream& iostr) - : m_file(tmpfile()), m_istr(&iostr), m_ostr(&iostr) + : m_file(std::tmpfile()), m_istr(&iostr), m_ostr(&iostr) { if (!m_file) { throw Dimension_Error("Error opening temporary file for C++/C I/O" " interface."); } + // Write the input stream to the temporary file write_cookie(m_file, *m_istr); } @@ -123,14 +135,55 @@ namespace Dimension std::fclose(m_file); } + // Get the FILE* FILE* FILE_Cookie::file() { return m_file; } const FILE* FILE_Cookie::file() const { return m_file; } bool FILE_Cookie::is_input() const { return m_istr; } bool FILE_Cookie::is_output() const { return m_ostr; } - std::istream& FILE_Cookie::istr() { return *m_istr; } - const std::istream& FILE_Cookie::istr() const { return *m_istr; } - std::ostream& FILE_Cookie::ostr() { return *m_ostr; } - const std::ostream& FILE_Cookie::ostr() const { return *m_ostr; } + // Get the C++ streams + + std::istream& + FILE_Cookie::istr() + { + if (is_input()) { + return *m_istr; + } else { + throw Dimension_Error("Attempted to get input stream from non-input" + " FILE_Cookie."); + } + } + + const std::istream& + FILE_Cookie::istr() const + { + if (is_input()) { + return *m_istr; + } else { + throw Dimension_Error("Attempted to get input stream from non-input" + " FILE_Cookie."); + } + } + + std::ostream& + FILE_Cookie::ostr() + { + if (is_output()) { + return *m_ostr; + } else { + throw Dimension_Error("Attempted to get output stream from non-input" + " FILE_Cookie."); + } + } + + const std::ostream& FILE_Cookie::ostr() const + { + if (is_output()) { + return *m_ostr; + } else { + throw Dimension_Error("Attempted to get output stream from non-input" + " FILE_Cookie."); + } + } } diff --git a/libdimensionxx/dimensionxx/array.hpp b/libdimensionxx/dimensionxx/array.hpp index dfbf1c8..735e17a 100644 --- a/libdimensionxx/dimensionxx/array.hpp +++ b/libdimensionxx/dimensionxx/array.hpp @@ -18,14 +18,14 @@ * . * *************************************************************************/ +// dmnsn_array* wrapper. + #ifndef DIMENSIONXX_ARRAY_HPP #define DIMENSIONXX_ARRAY_HPP #include // For tr1::shared_ptr #include // For size_t -// dmnsn_array* wrapper. - namespace Dimension { // RAII scoped read-lock @@ -148,6 +148,8 @@ namespace Dimension return ret; } + // Access the underlying dmnsn_array* + template dmnsn_array* Array::dmnsn() @@ -170,6 +172,7 @@ namespace Dimension return *m_array; } + // Release the dmnsn_array*, if we are the only Array holding it template dmnsn_array* Array::release() diff --git a/libdimensionxx/dimensionxx/canvas.hpp b/libdimensionxx/dimensionxx/canvas.hpp index eda9ad3..f9e77e5 100644 --- a/libdimensionxx/dimensionxx/canvas.hpp +++ b/libdimensionxx/dimensionxx/canvas.hpp @@ -18,11 +18,11 @@ * . * *************************************************************************/ +// dmnsn_canvas* wrapper. + #ifndef DIMENSIONXX_CANVAS_HPP #define DIMENSIONXX_CANVAS_HPP -// dmnsn_canvas* wrapper. - namespace Dimension { // Base canvas class. Wraps a dmnsn_canvas*. diff --git a/libdimensionxx/dimensionxx/color.hpp b/libdimensionxx/dimensionxx/color.hpp index c79f86b..9289479 100644 --- a/libdimensionxx/dimensionxx/color.hpp +++ b/libdimensionxx/dimensionxx/color.hpp @@ -18,11 +18,11 @@ * . * *************************************************************************/ +// Wrappers for libdimension colors. + #ifndef DIMENSIONXX_COLOR_HPP #define DIMENSIONXX_COLOR_HPP -// Wrappers for libdimension colors. - namespace Dimension { // Forward declarations diff --git a/libdimensionxx/dimensionxx/cookie.hpp b/libdimensionxx/dimensionxx/cookie.hpp index df26b93..0d8b4da 100644 --- a/libdimensionxx/dimensionxx/cookie.hpp +++ b/libdimensionxx/dimensionxx/cookie.hpp @@ -18,11 +18,11 @@ * . * *************************************************************************/ +// Some internal magic to use C FILE* I/O with C++ streams. + #ifndef DIMENSIONXX_COOKIE_HPP #define DIMENSIONXX_COOKIE_HPP -// Some internal magic to use C FILE* I/O with C++ streams. - #include #include #include diff --git a/libdimensionxx/dimensionxx/error.hpp b/libdimensionxx/dimensionxx/error.hpp index 99670cb..7fb0e53 100644 --- a/libdimensionxx/dimensionxx/error.hpp +++ b/libdimensionxx/dimensionxx/error.hpp @@ -18,14 +18,14 @@ * . * *************************************************************************/ -#ifndef DIMENSIONXX_ERROR_HPP -#define DIMENSIONXX_ERROR_HPP - // Wrappers for libdimension error handling, and an exception class. // dmnsn_error is still used by libdimensionxx whenever an exception shouldn't // be thrown, like in destructors, and whenever libdimension or libdimension-* // use it internally. Exceptions are thrown otherwise to report errors. +#ifndef DIMENSIONXX_ERROR_HPP +#define DIMENSIONXX_ERROR_HPP + #include #include #include diff --git a/libdimensionxx/dimensionxx/geometry.hpp b/libdimensionxx/dimensionxx/geometry.hpp index f623eda..606f83a 100644 --- a/libdimensionxx/dimensionxx/geometry.hpp +++ b/libdimensionxx/dimensionxx/geometry.hpp @@ -18,15 +18,17 @@ * . * *************************************************************************/ +// Wrappers for geometric types (Vectors, Matricies, Lines (rays)). + #ifndef DIMENSIONXX_GEOMETRY_HPP #define DIMENSIONXX_GEOMETRY_HPP -// Wrappers for geometric types (Vectors, Matricies, Lines (rays)). - #include namespace Dimension { + class Vector; + // Wrapper for dmnsn_matrix class Matrix { @@ -56,6 +58,12 @@ namespace Dimension // Get the wrapped matrix dmnsn_matrix dmnsn() const { return m_matrix; } + // Special constructors + static inline Matrix identity(); + static inline Matrix scale(const Vector& factor); + static inline Matrix translation(const Vector& d); + static inline Matrix rotation(const Vector& theta); + private: dmnsn_matrix m_matrix; }; @@ -126,6 +134,49 @@ namespace Dimension dmnsn_line m_line; }; + // Matrix operators + + inline Matrix + operator*(const Matrix& lhs, const Matrix& rhs) + { + // This order is important! + Matrix r = rhs; + r *= lhs; + return r; + } + + inline Matrix + inverse(const Matrix& M) + { + return Matrix(dmnsn_matrix_inverse(M.dmnsn())); + } + + // Special Matrix constructors + + inline Matrix + Matrix::identity() + { + return Matrix(dmnsn_identity_matrix()); + } + + inline Matrix + Matrix::scale(const Vector& factor) + { + return Matrix(dmnsn_scale_matrix(factor.dmnsn())); + } + + inline Matrix + Matrix::translation(const Vector& d) + { + return Matrix(dmnsn_translation_matrix(d.dmnsn())); + } + + inline Matrix + Matrix::rotation(const Vector& theta) + { + return Matrix(dmnsn_rotation_matrix(theta.dmnsn())); + } + // Vector operators inline Vector diff --git a/libdimensionxx/dimensionxx/object.hpp b/libdimensionxx/dimensionxx/object.hpp index 3b2c6c1..40dcb25 100644 --- a/libdimensionxx/dimensionxx/object.hpp +++ b/libdimensionxx/dimensionxx/object.hpp @@ -18,11 +18,11 @@ * . * *************************************************************************/ +// dmnsn_object* wrapper. + #ifndef DIMENSIONXX_OBJECT_HPP #define DIMENSIONXX_OBJECT_HPP -// dmnsn_object* wrapper. - namespace Dimension { // Abstract base object class. Wraps a dmnsn_object*. diff --git a/libdimensionxx/dimensionxx/png.hpp b/libdimensionxx/dimensionxx/png.hpp index d768287..980840c 100644 --- a/libdimensionxx/dimensionxx/png.hpp +++ b/libdimensionxx/dimensionxx/png.hpp @@ -18,11 +18,11 @@ * . * *************************************************************************/ +// C++ wrapper for libdimension PNG support. PNG_Canvas derives from Canvas. + #ifndef DIMENSIONXX_PNG_HPP #define DIMENSIONXX_PNG_HPP -// C++ wrapper for libdimension PNG support. PNG_Canvas derives from Canvas. - #include #include diff --git a/libdimensionxx/dimensionxx/progress.hpp b/libdimensionxx/dimensionxx/progress.hpp index b9df870..3fff1bc 100644 --- a/libdimensionxx/dimensionxx/progress.hpp +++ b/libdimensionxx/dimensionxx/progress.hpp @@ -18,14 +18,14 @@ * . * *************************************************************************/ +// dmnsn_progress* wrapper. + #ifndef DIMENSIONXX_PROGRESS_HPP #define DIMENSIONXX_PROGRESS_HPP #include // For tr1::shared_ptr #include -// dmnsn_canvas* wrapper. - namespace Dimension { // Base class for persisting objects diff --git a/libdimensionxx/progress.cpp b/libdimensionxx/progress.cpp index ca74571..0a32b72 100644 --- a/libdimensionxx/progress.cpp +++ b/libdimensionxx/progress.cpp @@ -25,14 +25,17 @@ namespace Dimension Persist_Base::~Persist_Base() { } + // Construct a dmnsn_progress* wrapper Progress::Progress(dmnsn_progress* progress) : m_progress(new dmnsn_progress*(progress)) { } + // Construct a dmnsn_progress* wrapper, with a known persister Progress::Progress(dmnsn_progress* progress, const Persister& persister) : m_progress(new dmnsn_progress*(progress)), m_persister(persister) { } + // Finish the progress if not yet finished and we are unique Progress::~Progress() { if (m_progress && m_progress.unique()) { @@ -45,45 +48,55 @@ namespace Dimension } } + // Get the current progress double Progress::progress() const { return dmnsn_get_progress(dmnsn()); } + // Wait until progress() >= progress void Progress::wait(double progress) const { dmnsn_wait_progress(dmnsn(), progress); } + // Start a new level of loop nesting void Progress::new_element(unsigned int total) { dmnsn_new_progress_element(dmnsn(), total); } + // Increment the progress void Progress::increment() { dmnsn_increment_progress(dmnsn()); } + // Immediately finish the progress void Progress::done() { - dmnsn_progress_done(dmnsn()); + dmnsn_done_progress(dmnsn()); } + // Wait for progress completion void Progress::finish() { - if (m_progress.unique()) { - dmnsn_finish_progress(dmnsn()); - m_progress.reset(); - } else { + if (!m_progress) { + throw Dimension_Error("Attempt to finish Progress twice."); + } + + if (!m_progress.unique()) { throw Dimension_Error("Attempt to finish non-unique Progress."); } + + dmnsn_finish_progress(dmnsn()); + m_progress.reset(); // Don't try again } // Access the set of persisted objects @@ -93,6 +106,8 @@ namespace Dimension return m_persister; } + // Access the underlying dmnsn_progress* + dmnsn_progress* Progress::dmnsn() { diff --git a/tests/error.c b/tests/error.c index 7bd8ef6..fd9fc75 100644 --- a/tests/error.c +++ b/tests/error.c @@ -18,6 +18,8 @@ * . * *************************************************************************/ +/* Make sure that errors terminate us - this test should fail */ + #include "tests.h" #include diff --git a/tests/png.c b/tests/png.c index b80af04..3d46f38 100644 --- a/tests/png.c +++ b/tests/png.c @@ -18,6 +18,8 @@ * . * *************************************************************************/ +/* Test PNG file I/O */ + #include "tests.h" #include #include @@ -36,6 +38,7 @@ main() { unsigned int i, j; const unsigned int x = 333, y = 300; + /* Set the resilience low for tests */ dmnsn_set_resilience(DMNSN_SEVERITY_LOW); canvas = dmnsn_new_canvas(3*x, y); diff --git a/tests/pngxx.cpp b/tests/pngxx.cpp index 4a99370..c7cf0e0 100644 --- a/tests/pngxx.cpp +++ b/tests/pngxx.cpp @@ -18,12 +18,15 @@ * . * *************************************************************************/ +// Test C++ PNG file I/O + #include "testsxx.hpp" #include int main() { + // Set the resilience low for tests Dimension::resilience(Dimension::SEVERITY_LOW); const unsigned int width = 333, height = 300; diff --git a/tests/raytrace.c b/tests/raytrace.c index 7ea28da..83e6b2b 100644 --- a/tests/raytrace.c +++ b/tests/raytrace.c @@ -32,6 +32,7 @@ main() { dmnsn_color color; dmnsn_matrix trans; + /* Set the resilience low for tests */ dmnsn_set_resilience(DMNSN_SEVERITY_LOW); scene = dmnsn_new_scene(); @@ -80,6 +81,7 @@ main() { progress = dmnsn_png_write_canvas_async(scene->canvas, file); progressbar("Writing PNG file: ", progress); dmnsn_finish_progress(progress); + fclose(file); dmnsn_delete_cube(cube); dmnsn_delete_sphere(sphere); diff --git a/tests/tests.c b/tests/tests.c index 67e5ee1..b938d39 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -20,6 +20,7 @@ #include "tests.h" +/* Print a progress bar of the progress of `progress' */ void progressbar(const char *str, const dmnsn_progress *progress) { diff --git a/tests/tests.h b/tests/tests.h index c6b0de6..49e814c 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -21,4 +21,5 @@ #include "../libdimension/dimension.h" #include +/* Print a progress bar of the progress of `progress' */ void progressbar(const char *str, const dmnsn_progress *progress); diff --git a/tests/testsxx.cpp b/tests/testsxx.cpp index a4a7d7b..532b3dc 100644 --- a/tests/testsxx.cpp +++ b/tests/testsxx.cpp @@ -20,6 +20,7 @@ #include "testsxx.hpp" +// Print a progress bar of the progress of `progress' std::ostream& operator<<(std::ostream& ostr, const Dimension::Progress& progress) { diff --git a/tests/testsxx.hpp b/tests/testsxx.hpp index 43c7593..f7b0bc6 100644 --- a/tests/testsxx.hpp +++ b/tests/testsxx.hpp @@ -21,5 +21,6 @@ #include "../libdimensionxx/dimensionxx.hpp" #include +// Print a progress bar of the progress of `progress' std::ostream& operator<<(std::ostream& ostr, const Dimension::Progress& progress); diff --git a/tests/warning.c b/tests/warning.c index 80199c9..b2170d4 100644 --- a/tests/warning.c +++ b/tests/warning.c @@ -18,6 +18,8 @@ * . * *************************************************************************/ +// Make sure warnings don't kill us - this test should pass + #include "tests.h" #include -- cgit v1.2.3