From f77a53bf817920bfa94c2a6d83d5e7066b157134 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 22 Nov 2010 12:01:14 -0500 Subject: Generisise map implementation. --- dimension/realize.c | 26 ++++---- libdimension/Makefile.am | 2 + libdimension/color_map.c | 74 ++++------------------ libdimension/dimension.h | 1 + libdimension/dimension/map.h | 76 +++++++++++++++++++++++ libdimension/dimension/pigments.h | 34 ++-------- libdimension/map.c | 126 ++++++++++++++++++++++++++++++++++++++ tests/libdimension/render.c | 28 ++++----- 8 files changed, 247 insertions(+), 120 deletions(-) create mode 100644 libdimension/dimension/map.h create mode 100644 libdimension/map.c diff --git a/dimension/realize.c b/dimension/realize.c index 2f74ce0..ddccf89 100644 --- a/dimension/realize.c +++ b/dimension/realize.c @@ -480,29 +480,29 @@ dmnsn_realize_pattern(dmnsn_astnode astnode) return pattern; } -static dmnsn_color_map * +static dmnsn_map * dmnsn_realize_color_list(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_COLOR_LIST, "Expected a color list."); - dmnsn_color_map *color_map = dmnsn_new_color_map(); + dmnsn_map *color_map = dmnsn_new_color_map(); double n = 0.0, i = 1.0/(dmnsn_array_size(astnode.children) - 1); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, entry, astnode.children) { dmnsn_color color = dmnsn_realize_color(*entry); - dmnsn_add_color_map_entry(color_map, n, color); + dmnsn_add_map_entry(color_map, n, &color); n += i; } return color_map; } -static dmnsn_color_map * +static dmnsn_map * dmnsn_realize_color_map(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_COLOR_MAP, "Expected a color_map."); - dmnsn_color_map *color_map = dmnsn_new_color_map(); + dmnsn_map *color_map = dmnsn_new_color_map(); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, entry, astnode.children) { dmnsn_assert(entry->type == DMNSN_AST_COLOR_MAP_ENTRY, @@ -515,7 +515,7 @@ dmnsn_realize_color_map(dmnsn_astnode astnode) double n = dmnsn_realize_float(n_node); dmnsn_color color = dmnsn_realize_color(color_node); - dmnsn_add_color_map_entry(color_map, n, color); + dmnsn_add_map_entry(color_map, n, &color); } return color_map; @@ -528,7 +528,7 @@ dmnsn_realize_pattern_pigment(dmnsn_astnode type, dmnsn_astnode modifiers) "Expected pigment modifiers"); dmnsn_pattern *pattern = dmnsn_realize_pattern(type); - dmnsn_color_map *color_map = NULL; + dmnsn_map *color_map = NULL; /* Set up the color_map */ DMNSN_ARRAY_FOREACH_REVERSE (dmnsn_astnode *, modifier, modifiers.children) { @@ -558,18 +558,18 @@ dmnsn_realize_pattern_pigment(dmnsn_astnode type, dmnsn_astnode modifiers) /* Default checker pattern is blue and green */ if (!color_map) color_map = dmnsn_new_color_map(); - if (dmnsn_array_size(color_map) < 1) - dmnsn_add_color_map_entry(color_map, 0.0, dmnsn_blue); - if (dmnsn_array_size(color_map) < 2) - dmnsn_add_color_map_entry(color_map, 1.0, dmnsn_green); + if (dmnsn_map_size(color_map) < 1) + dmnsn_add_map_entry(color_map, 0.0, &dmnsn_blue); + if (dmnsn_map_size(color_map) < 2) + dmnsn_add_map_entry(color_map, 1.0, &dmnsn_green); break; default: /* Default map is grayscale */ if (!color_map) { color_map = dmnsn_new_color_map(); - dmnsn_add_color_map_entry(color_map, 0.0, dmnsn_black); - dmnsn_add_color_map_entry(color_map, 1.0, dmnsn_white); + dmnsn_add_map_entry(color_map, 0.0, &dmnsn_black); + dmnsn_add_map_entry(color_map, 1.0, &dmnsn_white); } break; } diff --git a/libdimension/Makefile.am b/libdimension/Makefile.am index 86caa57..e56a22b 100644 --- a/libdimension/Makefile.am +++ b/libdimension/Makefile.am @@ -37,6 +37,7 @@ nobase_include_HEADERS = dimension.h \ dimension/lights.h \ dimension/list.h \ dimension/malloc.h \ + dimension/map.h \ dimension/object.h \ dimension/objects.h \ dimension/pattern.h \ @@ -75,6 +76,7 @@ libdimension_la_SOURCES = $(nobase_include_HEADERS) \ light.c \ list.c \ malloc.c \ + map.c \ object.c \ pattern.c \ perspective.c \ diff --git a/libdimension/color_map.c b/libdimension/color_map.c index c794374..f7d800e 100644 --- a/libdimension/color_map.c +++ b/libdimension/color_map.c @@ -25,71 +25,16 @@ #include "dimension.h" -/** An [index, color] pair. */ -typedef struct dmnsn_color_map_entry { - double n; - dmnsn_color color; -} dmnsn_color_map_entry; - -dmnsn_color_map * -dmnsn_new_color_map(void) -{ - return dmnsn_new_array(sizeof(dmnsn_color_map_entry)); -} - -void -dmnsn_delete_color_map(dmnsn_color_map *map) -{ - dmnsn_delete_array(map); -} - -void -dmnsn_add_color_map_entry(dmnsn_color_map *map, double n, dmnsn_color c) -{ - dmnsn_color_map_entry entry = { .n = n, .color = c }; - - /* Sorted insertion */ - size_t i; - dmnsn_color_map_entry *other = dmnsn_array_last(map); - for (i = dmnsn_array_size(map); i-- > 0;) { - if (other->n <= n) - break; - } - - dmnsn_array_insert(map, i + 1, &entry); -} - -dmnsn_color -dmnsn_color_map_value(const dmnsn_color_map *map, double n) +dmnsn_map * +dmnsn_new_color_map() { - dmnsn_color_map_entry *entry = dmnsn_array_first(map); - - double n1, n2 = 0.0; - dmnsn_color c1, c2 = entry->color; - - if (n < n2) { - return c2; - } - - for (; entry <= (dmnsn_color_map_entry *)dmnsn_array_last(map); ++entry) { - n1 = n2; - c1 = c2; - - n2 = entry->n; - c2 = entry->color; - - if (n < n2) { - return dmnsn_color_gradient(c1, c2, (n - n1)/(n2 - n1)); - } - } - - return c2; + return dmnsn_new_map(sizeof(dmnsn_color)); } /** Payload for a color_map pigment. */ typedef struct dmnsn_color_map_payload { dmnsn_pattern *pattern; - dmnsn_color_map *map; + dmnsn_map *map; } dmnsn_color_map_payload; /** Free a color_map payload. */ @@ -97,7 +42,7 @@ static void dmnsn_delete_color_map_payload(void *ptr) { dmnsn_color_map_payload *payload = ptr; - dmnsn_delete_color_map(payload->map); + dmnsn_delete_map(payload->map); dmnsn_delete_pattern(payload->pattern); dmnsn_free(payload); } @@ -107,8 +52,11 @@ static dmnsn_color dmnsn_color_map_pigment_fn(const dmnsn_pigment *pigment, dmnsn_vector v) { const dmnsn_color_map_payload *payload = pigment->ptr; - return dmnsn_color_map_value(payload->map, - dmnsn_pattern_value(payload->pattern, v)); + double n; + dmnsn_color color1, color2; + dmnsn_evaluate_map(payload->map, dmnsn_pattern_value(payload->pattern, v), + &n, &color1, &color2); + return dmnsn_color_gradient(color1, color2, n); } /** color_map initialization callback. */ @@ -122,7 +70,7 @@ dmnsn_color_map_initialize_fn(dmnsn_pigment *pigment) } dmnsn_pigment * -dmnsn_new_color_map_pigment(dmnsn_pattern *pattern, dmnsn_color_map *map) +dmnsn_new_color_map_pigment(dmnsn_pattern *pattern, dmnsn_map *map) { dmnsn_pigment *pigment = dmnsn_new_pigment(); diff --git a/libdimension/dimension.h b/libdimension/dimension.h index d4ce61e..fb8efb5 100644 --- a/libdimension/dimension.h +++ b/libdimension/dimension.h @@ -68,6 +68,7 @@ typedef void dmnsn_free_fn(void *ptr); #include #include #include +#include #include #include #include diff --git a/libdimension/dimension/map.h b/libdimension/dimension/map.h new file mode 100644 index 0000000..1cd0f55 --- /dev/null +++ b/libdimension/dimension/map.h @@ -0,0 +1,76 @@ +/************************************************************************* + * Copyright (C) 2009-2010 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser 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 Library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + + +/** + * @file + * Generic maps (backend for color_maps, pigment_maps, etc.). + */ + +#ifndef DIMENSION_MAP_H +#define DIMENSION_MAP_H + +/** A map. */ +typedef struct dmnsn_map { + dmnsn_free_fn *free_fn; /**< Destructor callback. */ + size_t obj_size; /**< @internal The size of the mapped objects. */ + dmnsn_array *array; /**< @internal The map entries. */ +} dmnsn_map; + +/** + * Create an empty map. + * @param[in] size The size of the objects to store in the map. + * @return A map with no entries. + */ +dmnsn_map *dmnsn_new_map(size_t size); + +/** + * Delete a map. + * @param[in,out] map The map to delete. + */ +void dmnsn_delete_map(dmnsn_map *map); + +/** + * Add an entry (a scalar-object pair) to a color map. + * @param[in,out] map The color map to add to. + * @param[in] n The index of the entry. + * @param[in] object The value of the entry. + */ +void dmnsn_add_map_entry(dmnsn_map *map, double n, const void *obj); + +/** + * Return the number of entries in a map. + * @param[in] map The map to measure. + * @return The size of \p map. + */ +size_t dmnsn_map_size(const dmnsn_map *map); + +/** + * Evaluate a map. + * @param[in] map The map to evaluate. + * @param[in] n The index to evaluate. + * @param[out] val The normalized distance of \p n from \p obj1. + * @param[out] obj1 The first object. + * @param[out] obj2 The second object. + */ +void dmnsn_evaluate_map(const dmnsn_map *map, double n, + double *val, void *obj1, void *obj2); + +#endif /* DIMENSION_MAP_H */ diff --git a/libdimension/dimension/pigments.h b/libdimension/dimension/pigments.h index 68a9c66..687080f 100644 --- a/libdimension/dimension/pigments.h +++ b/libdimension/dimension/pigments.h @@ -41,37 +41,11 @@ dmnsn_pigment *dmnsn_new_solid_pigment(dmnsn_color color); */ dmnsn_pigment *dmnsn_new_canvas_pigment(dmnsn_canvas *canvas); -/** Color map. */ -typedef dmnsn_array dmnsn_color_map; - -/** - * Create an empty color map. - * @return A color map with no entries. - */ -dmnsn_color_map *dmnsn_new_color_map(void); - -/** - * Delete a color map. - * @param[in,out] map The color map to delete. - */ -void dmnsn_delete_color_map(dmnsn_color_map *map); - -/** - * Add an entry (a scalar-color pair) to a color map. - * @param[in,out] map The color map to add to. - * @param[in] n The index of the entry. - * @param[in] c The value of the entry. - */ -void dmnsn_add_color_map_entry(dmnsn_color_map *map, double n, dmnsn_color c); - /** - * Evaluate a color map. - * @param[in] map The map to evaluate. - * @param[in] n The index to evaluate. - * @return The value of the gradient between the the two indicies closest to - * \p n. + * Construct a color map. + * @return An empty color map. */ -dmnsn_color dmnsn_color_map_value(const dmnsn_color_map *map, double n); +dmnsn_map *dmnsn_new_color_map(); /** * A color-mapped pigment. @@ -80,6 +54,6 @@ dmnsn_color dmnsn_color_map_value(const dmnsn_color_map *map, double n); * @return A pigment mapping the pattern to color values. */ dmnsn_pigment *dmnsn_new_color_map_pigment(dmnsn_pattern *pattern, - dmnsn_color_map *map); + dmnsn_map *map); #endif /* DIMENSION_PIGMENTS_H */ diff --git a/libdimension/map.c b/libdimension/map.c new file mode 100644 index 0000000..3ea4b0c --- /dev/null +++ b/libdimension/map.c @@ -0,0 +1,126 @@ +/************************************************************************* + * Copyright (C) 2010 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser 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 Library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Generic maps. + */ + +#include "dimension.h" +#include + +/** An [index, object] pair. */ +typedef struct dmnsn_map_entry { + double n; + char object[]; +} dmnsn_map_entry; + +dmnsn_map * +dmnsn_new_map(size_t size) +{ + dmnsn_map *map = dmnsn_malloc(sizeof(dmnsn_map)); + map->free_fn = NULL; + map->obj_size = size; + map->array = dmnsn_new_array(sizeof(dmnsn_map_entry) + size); + return map; +} + +void +dmnsn_delete_map(dmnsn_map *map) +{ + if (map) { + if (map->free_fn) { + for (size_t i = 0; i < dmnsn_array_size(map->array); ++i) { + dmnsn_map_entry *entry = dmnsn_array_at(map->array, i); + (*map->free_fn)(entry->object); + } + } + + dmnsn_delete_array(map->array); + dmnsn_free(map); + } +} + +void +dmnsn_add_map_entry(dmnsn_map *map, double n, const void *obj) +{ + dmnsn_map_entry *entry = alloca(sizeof(dmnsn_map_entry) + map->obj_size); + entry->n = n; + memcpy(entry->object, obj, map->obj_size); + + /* Sorted insertion */ + size_t i; + for (i = dmnsn_array_size(map->array); i-- > 0;) { + dmnsn_map_entry *other = dmnsn_array_at(map->array, i); + if (other->n <= n) + break; + } + + dmnsn_array_insert(map->array, i + 1, entry); +} + +size_t +dmnsn_map_size(const dmnsn_map *map) +{ + return dmnsn_array_size(map->array); +} + +void +dmnsn_evaluate_map(const dmnsn_map *map, double n, + double *val, void *obj1, void *obj2) +{ + dmnsn_assert(dmnsn_array_size(map->array) > 0, + "Attempt to evaluate empty map."); + + const dmnsn_map_entry *entry = dmnsn_array_first(map->array); + + double n1, n2 = 0.0; + const void *o1, *o2 = entry->object; + + if (n < n2) { + *val = 0.0; + memcpy(obj1, o2, map->obj_size); + memcpy(obj2, o2, map->obj_size); + return; + } + + const dmnsn_map_entry *last = dmnsn_array_last(map->array); + ptrdiff_t skip = sizeof(dmnsn_map_entry) + map->obj_size; + for (; entry <= last; + entry = (const dmnsn_map_entry *)((const char *)entry + skip)) + { + n1 = n2; + o1 = o2; + + n2 = entry->n; + o2 = entry->object; + + if (n < n2) { + *val = (n - n1)/(n2 - n1); + memcpy(obj1, o1, map->obj_size); + memcpy(obj2, o2, map->obj_size); + return; + } + } + + *val = 1.0; + memcpy(obj1, o2, map->obj_size); + memcpy(obj2, o2, map->obj_size); +} diff --git a/tests/libdimension/render.c b/tests/libdimension/render.c index 31518bf..5ba075d 100644 --- a/tests/libdimension/render.c +++ b/tests/libdimension/render.c @@ -71,11 +71,11 @@ dmnsn_new_test_scene(void) /* Sky sphere */ scene->sky_sphere = dmnsn_new_sky_sphere(); dmnsn_pattern *sky_gradient = dmnsn_new_gradient_pattern(dmnsn_y); - dmnsn_color_map *sky_gradient_color_map = dmnsn_new_color_map(); - dmnsn_add_color_map_entry(sky_gradient_color_map, 0.0, dmnsn_orange); + dmnsn_map *sky_gradient_color_map = dmnsn_new_color_map(); + dmnsn_add_map_entry(sky_gradient_color_map, 0.0, &dmnsn_orange); dmnsn_color background = dmnsn_color_from_sRGB((dmnsn_sRGB){ 0.0, 0.1, 0.2 }); background.filter = 0.1; - dmnsn_add_color_map_entry(sky_gradient_color_map, 0.35, background); + dmnsn_add_map_entry(sky_gradient_color_map, 0.35, &background); dmnsn_pigment *sky_pigment = dmnsn_new_color_map_pigment(sky_gradient, sky_gradient_color_map); dmnsn_array_push(scene->sky_sphere->pigments, &sky_pigment); @@ -134,14 +134,14 @@ dmnsn_new_test_scene(void) dmnsn_new_vector(dmnsn_radians(-45.0), 0.0, 0.0) ); dmnsn_pattern *gradient = dmnsn_new_gradient_pattern(dmnsn_y); - dmnsn_color_map *gradient_color_map = dmnsn_new_color_map(); - dmnsn_add_color_map_entry(gradient_color_map, 0.0, dmnsn_red); - dmnsn_add_color_map_entry(gradient_color_map, 1.0/6.0, dmnsn_orange); - dmnsn_add_color_map_entry(gradient_color_map, 2.0/6.0, dmnsn_yellow); - dmnsn_add_color_map_entry(gradient_color_map, 3.0/6.0, dmnsn_green); - dmnsn_add_color_map_entry(gradient_color_map, 4.0/6.0, dmnsn_blue); - dmnsn_add_color_map_entry(gradient_color_map, 5.0/6.0, dmnsn_magenta); - dmnsn_add_color_map_entry(gradient_color_map, 1.0, dmnsn_red); + dmnsn_map *gradient_color_map = dmnsn_new_color_map(); + dmnsn_add_map_entry(gradient_color_map, 0.0, &dmnsn_red); + dmnsn_add_map_entry(gradient_color_map, 1.0/6.0, &dmnsn_orange); + dmnsn_add_map_entry(gradient_color_map, 2.0/6.0, &dmnsn_yellow); + dmnsn_add_map_entry(gradient_color_map, 3.0/6.0, &dmnsn_green); + dmnsn_add_map_entry(gradient_color_map, 4.0/6.0, &dmnsn_blue); + dmnsn_add_map_entry(gradient_color_map, 5.0/6.0, &dmnsn_magenta); + dmnsn_add_map_entry(gradient_color_map, 1.0, &dmnsn_red); arrow->texture = dmnsn_new_texture(); arrow->texture->pigment = dmnsn_new_color_map_pigment(gradient, gradient_color_map); @@ -180,9 +180,9 @@ dmnsn_new_test_scene(void) plane->trans = dmnsn_translation_matrix(dmnsn_new_vector(0.0, -2.0, 0.0)); plane->texture = dmnsn_new_texture(); dmnsn_pattern *checker = dmnsn_new_checker_pattern(); - dmnsn_color_map *checker_color_map = dmnsn_new_color_map(); - dmnsn_add_color_map_entry(checker_color_map, 0.0, dmnsn_black); - dmnsn_add_color_map_entry(checker_color_map, 1.0, dmnsn_white); + dmnsn_map *checker_color_map = dmnsn_new_color_map(); + dmnsn_add_map_entry(checker_color_map, 0.0, &dmnsn_black); + dmnsn_add_map_entry(checker_color_map, 1.0, &dmnsn_white); plane->texture->pigment = dmnsn_new_color_map_pigment(checker, checker_color_map); plane->texture->pigment->quick_color -- cgit v1.2.3