Mercurial > MadButterfly
view src/coord.c @ 902:e86b4d56ddea
Parse font-style and font-weight of SVG text
author | Thinker K.F. Li <thinker@codemud.net> |
---|---|
date | Fri, 01 Oct 2010 12:20:26 +0800 |
parents | 586e50f82c1f |
children |
line wrap: on
line source
// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*- // vim: sw=4:ts=8:sts=4 /*! \brief Implement coordination tranform mechanism. * \file * This file implements coordination transforming for containers. */ #include <stdio.h> #include <string.h> #include <math.h> #include "mb_types.h" #define ASSERT(x) /* To keep possibility of changing type of aix */ #define MUL(a, b) ((a) * (b)) #define ADD(a, b) ((a) + (b)) #define DIV(a, b) ((a) / (b)) #define SUB(a, b) ((a) - (b)) static void mul_matrix(co_aix *m1, co_aix *m2, co_aix *dst) { dst[0] = ADD(MUL(m1[0], m2[0]), MUL(m1[1], m2[3])); dst[1] = ADD(MUL(m1[0], m2[1]), MUL(m1[1], m2[4])); dst[2] = ADD(ADD(MUL(m1[0], m2[2]), MUL(m1[1], m2[5])), m1[2]); dst[3] = ADD(MUL(m1[3], m2[0]), MUL(m1[4], m2[3])); dst[4] = ADD(MUL(m1[3], m2[1]), MUL(m1[4], m2[4])); dst[5] = ADD(ADD(MUL(m1[3], m2[2]), MUL(m1[4], m2[5])), m1[5]); } void matrix_mul(co_aix *m1, co_aix *m2, co_aix *dst) { co_aix *_dst = dst; co_aix fake_dst[6]; if(m1 == dst || m2 == dst) _dst = fake_dst; mul_matrix(m1, m2, _dst); if(m1 == dst || m2 == dst) { dst[0] = fake_dst[0]; dst[1] = fake_dst[1]; dst[2] = fake_dst[2]; dst[3] = fake_dst[3]; dst[4] = fake_dst[4]; dst[5] = fake_dst[5]; } } void matrix_trans_pos(co_aix *matrix, co_aix *x, co_aix *y) { co_aix nx, ny; nx = ADD(ADD(MUL(matrix[0], *x), MUL(matrix[1], *y)), matrix[2]); ny = ADD(ADD(MUL(matrix[3], *x), MUL(matrix[4], *y)), matrix[5]); *x = nx; *y = ny; } /*! \brief Compute aggregated transform matrix. * * Base on parent's aggregated matrix if it is existed, or use transform * matrix as aggregated matrix. */ static void compute_transform_function(coord_t *visit) { if(!coord_is_root(visit)) mul_matrix(visit->parent->aggr_matrix, visit->matrix, visit->aggr_matrix); else memcpy(visit->aggr_matrix, visit->matrix, sizeof(visit->matrix)); } void compute_aggr_of_coord(coord_t *coord) { compute_transform_function(coord); } /*! \brief Compute aggregated transform matrix for cached coord. * * \sa \ref img_cache */ static void compute_transform_function_cached(coord_t *visit) { co_aix *p_matrix; co_aix cache_p_matrix[6]; co_aix cache_scale_x, cache_scale_y; if(!coord_is_root(visit)) { p_matrix = coord_get_aggr_matrix(visit->parent); cache_scale_x = sqrtf(p_matrix[0] * p_matrix[0] + p_matrix[3] * p_matrix[3]); cache_scale_y = sqrtf(p_matrix[1] * p_matrix[1] + p_matrix[4] * p_matrix[4]); cache_p_matrix[0] = cache_scale_x; cache_p_matrix[1] = 0; cache_p_matrix[2] = 0; cache_p_matrix[3] = 0; cache_p_matrix[4] = cache_scale_y; cache_p_matrix[5] = 0; mul_matrix(cache_p_matrix, visit->matrix, visit->aggr_matrix); } else { memcpy(visit->aggr_matrix, visit->matrix, sizeof(visit->matrix)); } } void compute_aggr_of_cached_coord(coord_t *coord) { compute_transform_function_cached(coord); } void compute_aggr(coord_t *coord) { if(coord->flags & COF_OWN_CANVAS) compute_transform_function_cached(coord); else compute_transform_function(coord); } void compute_reverse(co_aix *orig, co_aix *reverse) { co_aix working[6]; co_aix factor; #define VEC_MAC(src, factor, dst) \ do { \ (dst)[0] += (src)[0] * (factor); \ (dst)[1] += (src)[1] * (factor); \ (dst)[2] += (src)[2] * (factor); \ } while(0) reverse[0] = 1; reverse[1] = 0; reverse[2] = 0; reverse[3] = 0; reverse[4] = 1; reverse[5] = 0; memcpy(working, orig, sizeof(co_aix) * 6); factor = -working[3] / working[0]; VEC_MAC(working, factor, working + 3); VEC_MAC(reverse, factor, reverse + 3); factor = -working[1] / working[4]; VEC_MAC(working + 3, factor, working); VEC_MAC(reverse + 3, factor, reverse); reverse[2] = -working[2]; reverse[5] = -working[5]; factor = 1 / working[0]; reverse[0] *= factor; reverse[1] *= factor; reverse[2] *= factor; factor = 1 / working[4]; reverse[3] *= factor; reverse[4] *= factor; reverse[5] *= factor; } /*! \brief Update aggregate matrices of elements under a sub-tree. * * A subtree is specified by the root of it. All elements in the subtree * are effected by that changes of matrix of the subtree root. * * \todo Remove update_aggr_matrix() since it is out of date and * no one use it. */ void update_aggr_matrix(coord_t *start) { coord_t *visit, *child, *next; compute_transform_function(start); visit = start; while(visit) { child = STAILQ_HEAD(visit->children); while(child) { compute_transform_function(child); child = STAILQ_NEXT(coord_t, sibling, child); } if(STAILQ_HEAD(visit->children)) visit = STAILQ_HEAD(visit->children); else if(STAILQ_NEXT(coord_t, sibling, visit)) visit = STAILQ_NEXT(coord_t, sibling, visit); else { next = NULL; while(visit->parent && visit->parent != start) { visit = visit->parent; if(STAILQ_NEXT(coord_t, sibling, visit)) { next = STAILQ_NEXT(coord_t, sibling, visit); break; } } visit = next; } } } /*! \brief Initialize a coord object. * * The object is cleared and matrix was initialized to ID. * The object is be a children of specified parent. */ void coord_init(coord_t *co, coord_t *parent) { memset(co, 0, sizeof(coord_t)); if(parent) { /* insert at tail of children list. */ co->parent = parent; STAILQ_INS_TAIL(parent->children, coord_t, sibling, co); } mb_obj_init(co, MBO_COORD); co->matrix[0] = 1; co->matrix[4] = 1; co->aggr_matrix[0] = 1; co->aggr_matrix[4] = 1; co->cur_area = &co->areas[0]; co->last_area = &co->areas[1]; } void coord_trans_pos(coord_t *co, co_aix *x, co_aix *y) { co_aix nx, ny; nx = ADD(ADD(MUL(co->aggr_matrix[0], *x), MUL(co->aggr_matrix[1], *y)), co->aggr_matrix[2]); ny = ADD(ADD(MUL(co->aggr_matrix[3], *x), MUL(co->aggr_matrix[4], *y)), co->aggr_matrix[5]); *x = nx; *y = ny; } co_aix coord_trans_size(coord_t *co, co_aix sz) { co_aix x, y; x = MUL(co->aggr_matrix[0], sz); y = MUL(co->aggr_matrix[3], sz); return sqrt(x * x + y * y); } /*! * \note Coords, marked with COF_SKIP_TRIVAL (for temporary), and * descendants of them will not be trivaled and the flag with be removed * after skipping them. */ coord_t *preorder_coord_subtree(coord_t *root, coord_t *last) { coord_t *next = NULL; ASSERT(last != NULL); if((!(last->flags & COF_SKIP_TRIVAL)) && STAILQ_HEAD(last->children)) { next = STAILQ_HEAD(last->children); if(!(next->flags & COF_SKIP_TRIVAL)) return next; } else { next = last; } do { next->flags &= ~COF_SKIP_TRIVAL; while(next != root && STAILQ_NEXT(coord_t, sibling, next) == NULL) next = next->parent; if(next == root) return NULL; next = STAILQ_NEXT(coord_t, sibling, next); } while(next->flags & COF_SKIP_TRIVAL); return next; } coord_t *postorder_coord_subtree(coord_t *root, coord_t *last) { coord_t *next; if(root == last) return NULL; if(last == NULL) { /* Go most left leaf. */ next = root; while(STAILQ_HEAD(next->children)) next = STAILQ_HEAD(next->children); return next; } next = last; if(STAILQ_NEXT(coord_t, sibling, next) == NULL) /* most right */ return next->parent; /* Go most left leaf of right sibling sub-tree. */ next = STAILQ_NEXT(coord_t, sibling, next); while(STAILQ_HEAD(next->children)) next = STAILQ_HEAD(next->children); return next; } #ifdef UNITTEST #include <CUnit/Basic.h> void test_update_aggr_matrix(void) { coord_t elms[6]; co_aix x, y; coord_init(elms, NULL); coord_init(elms + 1, elms); coord_init(elms + 2, elms); coord_init(elms + 3, elms + 1); coord_init(elms + 4, elms + 1); coord_init(elms + 5, elms + 2); /* | 2 -1 0 | * | 0 1 0 | * | 0 0 1 | */ elms[0].matrix[0] = 2; elms[0].matrix[1] = -1; /* | 1 3 0 | * | 5 1 0 | * | 0 0 1 | */ elms[1].matrix[1] = 3; elms[1].matrix[3] = 5; update_aggr_matrix(elms); /* | -3 5 0 | * | 5 1 0 | * | 0 0 1 | */ CU_ASSERT(elms[3].aggr_matrix[0] == -3); CU_ASSERT(elms[3].aggr_matrix[1] == 5); CU_ASSERT(elms[3].aggr_matrix[2] == 0); CU_ASSERT(elms[3].aggr_matrix[3] == 5); CU_ASSERT(elms[3].aggr_matrix[4] == 1); CU_ASSERT(elms[3].aggr_matrix[5] == 0); CU_ASSERT(elms[4].aggr_matrix[0] == -3); CU_ASSERT(elms[4].aggr_matrix[1] == 5); CU_ASSERT(elms[4].aggr_matrix[2] == 0); CU_ASSERT(elms[4].aggr_matrix[3] == 5); CU_ASSERT(elms[4].aggr_matrix[4] == 1); CU_ASSERT(elms[4].aggr_matrix[5] == 0); CU_ASSERT(elms[5].aggr_matrix[0] == 2); CU_ASSERT(elms[5].aggr_matrix[1] == -1); CU_ASSERT(elms[5].aggr_matrix[2] == 0); CU_ASSERT(elms[5].aggr_matrix[3] == 0); CU_ASSERT(elms[5].aggr_matrix[4] == 1); CU_ASSERT(elms[5].aggr_matrix[5] == 0); x = 50; y = 99; coord_trans_pos(elms + 5, &x, &y); CU_ASSERT(x == 1); CU_ASSERT(y == 99); } void test_preorder_coord_subtree(void) { coord_t elms[6]; coord_t *last; coord_init(elms, NULL); coord_init(elms + 1, elms); coord_init(elms + 2, elms); coord_init(elms + 3, elms + 1); coord_init(elms + 4, elms + 1); coord_init(elms + 5, elms + 2); last = elms; last = preorder_coord_subtree(elms, last); CU_ASSERT(last == elms + 1); last = preorder_coord_subtree(elms, last); CU_ASSERT(last == elms + 3); last = preorder_coord_subtree(elms, last); CU_ASSERT(last == elms + 4); last = preorder_coord_subtree(elms, last); CU_ASSERT(last == elms + 2); last = preorder_coord_subtree(elms, last); CU_ASSERT(last == elms + 5); last = preorder_coord_subtree(elms, last); CU_ASSERT(last == NULL); } CU_pSuite get_coord_suite(void) { CU_pSuite suite; suite = CU_add_suite("Suite_coord", NULL, NULL); CU_ADD_TEST(suite, test_update_aggr_matrix); CU_ADD_TEST(suite, test_preorder_coord_subtree); return suite; } #endif