// Copyright 2002, 2004, 2007 David Hilvert , // /* This file is part of the Anti-Lamenessing Engine. The Anti-Lamenessing Engine 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 Anti-Lamenessing Engine 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 the Anti-Lamenessing Engine; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * trans_multi.h: Represent multiple transformations, affecting different * regions of a scene. */ #ifndef __trans_multi_h__ #define __trans_multi_h__ #include "trans_abstract.h" #include "trans_single.h" struct trans_multi : public trans_abstract { public: struct multi_coordinate { int degree; int x; int y; public: int operator<(const multi_coordinate &mc) const { if (degree < mc.degree || degree == mc.degree && y < mc.y || degree == mc.degree && y == mc.y && x < mc.x) return 1; return 0; } }; private: static unsigned int _multi; static ale_pos _multi_decomp; static ale_real _multi_improvement; typedef unsigned short index_t; std::vector trans_stack; std::vector coord_stack; std::map coordinate_map; int use_multi; index_t current_element; index_t orig_ref_height, orig_ref_width; index_t cur_ref_height, cur_ref_width; point cur_offset; point orig_offset; index_t *spatio_elem_map; index_t *spatio_elem_map_r; void push_element() { assert (trans_stack.size() > 0); if (++current_element == trans_stack.size()) trans_stack.push_back(trans_stack.back()); } trans_multi() : trans_stack() { use_multi = 0; current_element = 0; orig_ref_height = 0; orig_ref_width = 0; cur_ref_height = 0; cur_ref_width = 0; spatio_elem_map = NULL; spatio_elem_map_r = NULL; } public: static void set_md(double d) { if (!(d > 1)) d = 1; _multi_decomp = d; } static void set_multi(const char *type) { if (!strcmp(type, "none")) { _multi = 0; } else if (!strcmp(type, "local")) { _multi = 1; } else if (!strcmp(type, "fill")) { _multi = 2; } else if (!strcmp(type, "llocal")) { _multi = 3; } else if (!strcmp(type, "global")) { _multi = 4; } } static void set_mi(double d) { _multi_improvement = d; } /* * Calculate euclidean identity transform for a given image. */ static struct trans_multi eu_identity(const image *i = NULL, ale_pos scale_factor = 1) { struct trans_multi r; multi_coordinate mc; mc.degree = 0; mc.x = 0; mc.y = 0; r.input_width = i ? i->width() : 2; r.input_height = i ? i->height() : 2; r.scale_factor = scale_factor; r.trans_stack.push_back(trans_single::eu_identity(i, scale_factor)); r.coord_stack.push_back(mc); r.coordinate_map[mc] = r.trans_stack.size() - 1; r.current_element = 0; return r; } /* * Generate an array of identity transformations. */ static trans_multi *new_eu_identity_array(unsigned int size) { trans_multi *result = new trans_multi[size]; for (unsigned int i = 0; i < size; i++) result[i] = eu_identity(); return result; } /* * Calculate projective transformation parameters from a euclidean * transformation. */ void eu_to_gpt() { for (unsigned int t = 0; t < trans_stack.size(); t++) trans_stack[t].eu_to_gpt(); } /* * Calculate projective identity transform for a given image. */ static trans_multi gpt_identity(const image *i, ale_pos scale_factor) { struct trans_multi r = eu_identity(i, scale_factor); r.eu_to_gpt(); return r; } trans_multi &operator=(const trans_multi &tm) { this->trans_abstract::operator=(*((trans_abstract *) &tm)); trans_stack = tm.trans_stack; coord_stack = tm.coord_stack; coordinate_map = tm.coordinate_map; use_multi = tm.use_multi; current_element = tm.current_element; orig_ref_height = tm.orig_ref_height; orig_ref_width = tm.orig_ref_width; cur_ref_height = tm.cur_ref_height; cur_ref_width = tm.cur_ref_width; cur_offset = tm.cur_offset; orig_offset = tm.orig_offset; free(spatio_elem_map); free(spatio_elem_map_r); spatio_elem_map = NULL; spatio_elem_map_r = NULL; size_t cur_size = cur_ref_width * cur_ref_height * sizeof(index_t); if (cur_size > 0 && tm.spatio_elem_map) { spatio_elem_map = (index_t *) malloc(cur_size); assert (spatio_elem_map); memcpy(spatio_elem_map, tm.spatio_elem_map, cur_size); } cur_size = input_height * input_width * sizeof(index_t); if (cur_size > 0 && tm.spatio_elem_map_r) { spatio_elem_map_r = (index_t *) malloc(cur_size); assert (spatio_elem_map_r); memcpy(spatio_elem_map_r, tm.spatio_elem_map_r, cur_size); } return *this; } trans_multi(const trans_multi &tm) : trans_stack() { spatio_elem_map = NULL; spatio_elem_map_r = NULL; operator=(tm); } ~trans_multi() { free(spatio_elem_map); free(spatio_elem_map_r); } trans_single get_element(index_t index) const { assert (index < trans_stack.size()); return trans_stack[index]; } trans_single get_element(multi_coordinate m) { assert(coordinate_map.count(m)); index_t index = coordinate_map[m]; return get_element(index); } index_t get_index(multi_coordinate m) { assert(coordinate_map.count(m)); return coordinate_map[m]; } int exists(multi_coordinate m) { return coordinate_map.count(m); } trans_single get_current_element() const { return get_element(current_element); } void set_element(index_t index, trans_single t) { assert (index < trans_stack.size()); trans_stack[index] = t; } void set_current_element(trans_single t) { set_element(current_element, t); } void set_current_element(const trans_multi &t) { set_element(current_element, t.get_current_element()); } index_t get_current_index() const { return current_element; } multi_coordinate get_current_coordinate() const { return coord_stack[current_element]; } multi_coordinate get_coordinate(index_t i) const { assert(i < trans_stack.size()); return coord_stack[i]; } void set_current_index(index_t i) { assert (i < trans_stack.size()); current_element = i; } /* * Set the bounds of the reference image after incorporation * of the original frame. */ void set_original_bounds(const image *i) { assert (orig_ref_width == 0); assert (orig_ref_height == 0); orig_ref_height = i->height(); orig_ref_width = i->width(); orig_offset = i->offset(); assert (orig_ref_width != 0); assert (orig_ref_height != 0); } static multi_coordinate parent_mc(multi_coordinate mc) { multi_coordinate result; assert (mc.degree > 0); if (mc.degree == 1) { result.degree = 0; result.x = 0; result.y = 0; return result; } result.degree = mc.degree - 1; result.x = (int) floor((double) mc.x / (double) 2); result.y = (int) floor((double) mc.y / (double) 2); return result; } index_t parent_index(index_t i) { multi_coordinate mc = coord_stack[i]; multi_coordinate mcp = parent_mc(mc); index_t result = coordinate_map[mcp]; return result; } /* * Set the bounds of the reference image after incorporation * of the most recent frame. */ void set_current_bounds(const image *i) { use_multi = 0; free(spatio_elem_map); free(spatio_elem_map_r); spatio_elem_map = NULL; spatio_elem_map_r = NULL; cur_ref_height = i->height(); cur_ref_width = i->width(); cur_offset = i->offset(); int d; ale_pos div; for (d = 1, div = 2; orig_ref_height / div >= _multi_decomp && orig_ref_width / div >= _multi_decomp && _multi > 0; d++, div *= 2) { ale_pos height_scale = orig_ref_height / div; ale_pos width_scale = orig_ref_width / div; for (int i = floor((cur_offset[0] - orig_offset[0]) / height_scale); i < ceil((cur_offset[0] - orig_offset[0] + cur_ref_height) / height_scale); i++) for (int j = floor((cur_offset[1] - orig_offset[1]) / width_scale); j < ceil((cur_offset[1] - orig_offset[1] + cur_ref_width) / width_scale); j++) { multi_coordinate c; c.degree = d; c.x = j; c.y = i; if (!coordinate_map.count(c)) { multi_coordinate parent = parent_mc(c); assert (coordinate_map.count(parent)); trans_stack.push_back(trans_stack[coordinate_map[parent]]); coord_stack.push_back(c); coordinate_map[c] = trans_stack.size() - 1; } } } } index_t stack_depth() const { return trans_stack.size(); } struct elem_bounds_int_t { unsigned int imin, imax, jmin, jmax; int satisfies_min_dim(unsigned int min_dimension) { if (imax - imin < min_dimension || jmax - jmin < min_dimension) return 0; return 1; } }; struct elem_bounds_t { ale_pos imin, imax, jmin, jmax; elem_bounds_int_t scale_to_bounds(unsigned int height, unsigned int width) { elem_bounds_t e; elem_bounds_int_t f; e = *this; e.imin *= height; e.imax *= height; e.jmin *= width; e.jmax *= width; if (e.imin > 0) f.imin = (unsigned int) floor(e.imin); else f.imin = 0; if (e.imax < height) f.imax = (unsigned int) ceil(e.imax); else f.imax = height; if (e.jmin > 0) f.jmin = (unsigned int) floor(e.jmin); else f.jmin = 0; if (e.jmax < width) f.jmax = (unsigned int) ceil(e.jmax); else f.jmax = width; return f; } }; elem_bounds_t elem_bounds(int e) const { elem_bounds_t result; result.imin = cur_offset[0] - orig_offset[0]; result.imax = result.imin + cur_ref_height; result.jmin = cur_offset[1] - orig_offset[1]; result.jmax = result.jmin + cur_ref_width; if (e > 0) { multi_coordinate mc = coord_stack[e]; ale_pos height_scale = orig_ref_height / pow(2, mc.degree); ale_pos width_scale = orig_ref_width / pow(2, mc.degree); if (height_scale * mc.y > result.imin) result.imin = height_scale * mc.y; if (height_scale * (mc.y + 1) < result.imax) result.imax = height_scale * (mc.y + 1); if (width_scale * mc.x > result.jmin) result.jmin = width_scale * mc.x; if (width_scale * (mc.x + 1) < result.jmax) result.jmax = width_scale * (mc.x + 1); } result.imin -= cur_offset[0] - orig_offset[0]; result.imax -= cur_offset[0] - orig_offset[0]; result.jmin -= cur_offset[1] - orig_offset[1]; result.jmax -= cur_offset[1] - orig_offset[1]; result.imin /= cur_ref_height; result.imax /= cur_ref_height; result.jmin /= cur_ref_width; result.jmax /= cur_ref_width; return result; } elem_bounds_t elem_bounds() const { return elem_bounds(current_element); } private: int check_multi(int i, int j, pixel value, const image *cur_ref, const image *input, index_t check_index) { int result = 0; const pixel &rp = value; index_t index = check_index; trans_single t = get_element(index); point p0 = point(cur_offset[0] + i, cur_offset[1] + j); point p = t.unscaled_inverse_transform(p0); if (!input->in_bounds(p)) return result; trans_single s = get_element(spatio_elem_map[cur_ref_width * i + j]); point q = s.unscaled_inverse_transform(p0); pixel pt = t.get_tonal_multiplier(p0); pixel qt = s.get_tonal_multiplier(p0); if (input->in_bounds(q)) { pixel ip1 = input->get_bl(p); pixel ip0 = input->get_bl(q); ale_real diff1 = (pt * ip1 - rp).norm(); ale_real diff0 = (qt * ip0 - rp).norm(); /* * 0.99 factor is for cycle avoidance (e.g., in * filling). */ if (diff1 < diff0 * 0.99 /* * (1 - _multi_improvement) */ || _multi == 3) { result = 1; spatio_elem_map[cur_ref_width * i + j] = index; } } int ii = (int) p[0]; int jj = (int) p[1]; if (ii < 0 || (unsigned int) ii >= input_height || jj < 0 || (unsigned int) jj >= input_width) return result; trans_single u = get_element(spatio_elem_map_r[input_width * ii + jj]); point r = u.transform_unscaled(p); pixel ut = u.get_tonal_multiplier(r); if (cur_ref->in_bounds(r - cur_offset)) { pixel ip1 = input->get_bl(p); pixel rp0 = cur_ref->get_bl(r - cur_offset); ale_real diff1 = (pt * ip1 - rp).norm(); ale_real diff0 = (ut * ip1 - rp0).norm(); /* * 0.99 factor is probably not necessary, but * is included for symmetry with cycle-avoidance * factor. */ if (diff1 < diff0 * 0.99 /* * (1 - _multi_improvement) */ || _multi == 3) { spatio_elem_map_r[input_width * ii + jj] = index; } } return result; } void assign_multi_global_best(const image *cur_ref, const image *input) { for (unsigned int i = 0; i < cur_ref_height; i++) for (unsigned int j = 0; j < cur_ref_width; j++) { pixel rp = cur_ref->get_pixel(i, j); for (index_t index = 0; index < coordinate_map.size(); index++) check_multi(i, j, rp, cur_ref, input, index); } } void assign_multi_best(const image *cur_ref, const image *input) { for (unsigned int i = 0; i < cur_ref_height; i++) for (unsigned int j = 0; j < cur_ref_width; j++) { pixel rp = cur_ref->get_pixel(i, j); int d; ale_pos div; for (d = 1, div = 2; ; d++, div *= 2) { ale_pos height_scale = orig_ref_height / div; ale_pos width_scale = orig_ref_width / div; multi_coordinate c; c.degree = d; c.y = floor((cur_offset[0] - orig_offset[0] + i) / height_scale); c.x = floor((cur_offset[1] - orig_offset[1] + j) / width_scale); if (!coordinate_map.count(c)) break; index_t index = coordinate_map[c]; check_multi(i, j, rp, cur_ref, input, index); } } } void fill_multi_init(unsigned char *update_map) { for (unsigned int l = 0; l < coord_stack.size(); l++) { elem_bounds_int_t b = elem_bounds(l).scale_to_bounds(cur_ref_height, cur_ref_width); for (unsigned int i = b.imin; i < b.imax; i++) { update_map[i * cur_ref_width + b.jmin] |= (1 | 2 | 4); update_map[i * cur_ref_width + (b.jmax - 1)] |= (8 | 16 | 32); } for (unsigned int j = b.jmin; j < b.jmax; j++) { update_map[b.imin * cur_ref_width + j] |= (1 | 64 | 8); update_map[(b.imax - 1) * cur_ref_width + j] |= (4 | 128 | 32); } } for (unsigned int i = 0; i < cur_ref_height; i++) update_map[cur_ref_width * cur_ref_height + i] = 1; for (unsigned int j = 0; j < cur_ref_width; j++) update_map[cur_ref_width * cur_ref_height + cur_ref_height + j] = 1; } int step_fill_multi(unsigned char *update_map, const image *cur_ref, const image *input) { if (cur_ref_height == 0 || cur_ref_width == 0) return 0; unsigned int i_min, i_max, j_min, j_max; int result = 0; i_min = cur_ref_height; i_max = cur_ref_height; j_min = cur_ref_width; j_max = cur_ref_width; for (unsigned int i = 0; i < cur_ref_height; i++) if (update_map[cur_ref_width * cur_ref_height + i]) { i_min = i; i_max = i + 1; break; } for (unsigned int i = cur_ref_height - 1; i >= i_max; i--) if (update_map[cur_ref_width * cur_ref_height + i]) { i_max = i + 1; break; } for (unsigned int j = 0; j < cur_ref_width; j++) if (update_map[cur_ref_width * cur_ref_height + cur_ref_height + j]) { j_min = j; j_max = j + 1; break; } for (unsigned int j = cur_ref_width - 1; j >= j_max; j--) if (update_map[cur_ref_width * cur_ref_height + cur_ref_height + j]) { j_max = j + 1; break; } if (!(i_min < i_max) || !(j_min < j_max)) return 0; for (unsigned int i = i_min; i < i_max; i++) update_map[cur_ref_width * cur_ref_height + i] = 0; for (unsigned int j = j_min; j < j_max; j++) update_map[cur_ref_width * cur_ref_height + cur_ref_height + j] = 0; for (unsigned int i = i_min; i < i_max; i++) for (unsigned int j = j_min; j < j_max; j++) { int o = cur_ref_width * i + j; if (!update_map[o]) continue; pixel rp = cur_ref->get_pixel(i, j); int n = o - cur_ref_width; int s = o + cur_ref_width; int e = o + 1; int w = o - 1; int ne = n + 1; int nw = n - 1; int se = s + 1; int sw = s - 1; int dirs[8] = { nw, w, sw, ne, e, se, n, s }; int comp_dirs[8] = { 5, 4, 3, 2, 1, 0, 7, 6 }; for (int di = 0; di < 8; di++) { if (!(update_map[o] & (1 << di))) continue; int d = dirs[di]; if (d < 0 || (unsigned int) d >= cur_ref_width * cur_ref_height) continue; if (spatio_elem_map[d] == spatio_elem_map[o]) continue; int changed = check_multi(i, j, rp, cur_ref, input, spatio_elem_map[d]); if (!changed) continue; for (int ddi = 0; ddi < 8; ddi++) { int dd = dirs[ddi]; if (dd < 0 || (unsigned int) dd >= cur_ref_width * cur_ref_height) continue; if (spatio_elem_map[dd] == spatio_elem_map[o]) continue; result |= 1; update_map[dd] |= (1 << comp_dirs[ddi]); update_map[cur_ref_height * cur_ref_width + dd / cur_ref_width] = 1; update_map[cur_ref_height * cur_ref_width + cur_ref_height + dd % cur_ref_width] = 1; } } update_map[o] = 0; } return result; } void fill_multi(const image *cur_ref, const image *input) { unsigned char *update_map = (unsigned char *) calloc( cur_ref_height * cur_ref_width + cur_ref_height + cur_ref_width, sizeof(unsigned char)); fill_multi_init(update_map); while (step_fill_multi(update_map, cur_ref, input)); free(update_map); } public: void set_multi(const image *cur_ref, const image *input) { assert(use_multi == 0); assert(spatio_elem_map == NULL); assert(spatio_elem_map_r == NULL); use_multi = 1; spatio_elem_map = (index_t *) calloc( cur_ref_height * cur_ref_width, sizeof(index_t)); assert(spatio_elem_map); spatio_elem_map_r = (index_t *) calloc( input_height * input_width, sizeof(index_t)); assert(spatio_elem_map_r); if (_multi == 4) { assign_multi_global_best(cur_ref, input); } else { assign_multi_best(cur_ref, input); if (_multi == 2) fill_multi(cur_ref, input); } /* * All scale factors should be identical. */ scale_factor = trans_stack[0].scale(); } /* * Returns non-zero if the transformation might be non-Euclidean. */ int is_projective() const { return trans_stack[current_element].is_projective(); } /* * Transformation at point in the domain */ trans_single t_at_point(struct point p) const { if (!use_multi) return trans_stack[current_element]; int ii = (int) p[0]; int jj = (int) p[1]; if (ii < 0 || (unsigned int) ii >= input_height || jj < 0 || (unsigned int) jj >= input_width) return trans_stack[0]; return trans_stack[spatio_elem_map_r[input_width * ii + jj]]; } /* * Transformation at point in the co-domain. */ trans_single t_at_inv_point(struct point p) const { if (!use_multi) return trans_stack[current_element]; int i = (int) (p[0] - cur_offset[0]); int j = (int) (p[1] - cur_offset[1]); if (i < 0 || (unsigned int) i >= cur_ref_height || j < 0 || (unsigned int) j >= cur_ref_width) return trans_stack[0]; return trans_stack[spatio_elem_map[cur_ref_width * i + j]]; } /* * Projective/Euclidean transformations */ struct point pe(struct point p) const { if (!use_multi) return trans_stack[current_element].pe(p); int ii = (int) p[0]; int jj = (int) p[1]; if (ii < 0 || (unsigned int) ii >= input_height || jj < 0 || (unsigned int) jj >= input_width) return trans_stack[0].pe(p); return trans_stack[spatio_elem_map_r[input_width * ii + jj]].pe(p); } /* * Inverse transformations */ struct point pei(struct point p) const { if (!use_multi) return trans_stack[current_element].pei(p); int i = (int) (p[0] - cur_offset[0]); int j = (int) (p[1] - cur_offset[1]); if (i < 0 || (unsigned int) i >= cur_ref_height || j < 0 || (unsigned int) j >= cur_ref_width) return trans_stack[0].pei(p); return trans_stack[spatio_elem_map[cur_ref_width * i + j]].pei(p); } pixel get_tonal_multiplier(struct point p) const { if (!use_multi) return trans_stack[current_element].get_tonal_multiplier(p); int i = (int) (p[0] - cur_offset[0]); int j = (int) (p[1] - cur_offset[1]); if (i < 0 || (unsigned int) i >= cur_ref_height || j < 0 || (unsigned int) j >= cur_ref_width) return trans_stack[0].get_tonal_multiplier(p); return trans_stack[spatio_elem_map[cur_ref_width * i + j]].get_tonal_multiplier(p); } pixel get_inverse_tonal_multiplier(struct point p) const { if (!use_multi) return trans_stack[current_element].get_inverse_tonal_multiplier(p); int i = (int) p[0]; int j = (int) p[1]; if (i < 0 || (unsigned int) i >= input_height || j < 0 || (unsigned int) j >= input_width) return trans_stack[0].get_inverse_tonal_multiplier(p); return trans_stack[spatio_elem_map_r[input_width * i + j]].get_inverse_tonal_multiplier(p); } void set_tonal_multiplier(pixel p) { trans_stack[current_element].set_tonal_multiplier(p); } /* * Modify a euclidean transform in the indicated manner. */ void eu_modify(int i1, ale_pos diff) { trans_stack[current_element].eu_modify(i1, diff); } /* * Rotate about a given point in the original reference frame. */ void eu_rotate_about_scaled(point center, ale_pos diff) { trans_stack[current_element].eu_rotate_about_scaled(center, diff); } /* * Modify all euclidean parameters at once. */ void eu_set(ale_pos eu[3]) { trans_stack[current_element].eu_set(eu); } /* * Get the specified euclidean parameter */ ale_pos eu_get(int param) const { return trans_stack[current_element].eu_get(param); } /* * Modify a projective transform in the indicated manner. */ void gpt_modify(int i1, int i2, ale_pos diff) { trans_stack[current_element].gpt_modify(i1, i2, diff); } /* * Modify a projective transform according to the group operation. */ void gr_modify(int i1, int i2, ale_pos diff) { trans_stack[current_element].gr_modify(i1, i2, diff); } /* * Modify all projective parameters at once. */ void gpt_set(point x[4]) { trans_stack[current_element].gpt_set(x); } void gpt_set(point x1, point x2, point x3, point x4) { trans_stack[current_element].gpt_set(x1, x2, x3, x4); } void snap(ale_pos interval) { trans_stack[current_element].snap(interval); } /* * Get the specified projective parameter */ point gpt_get(int point) const { return trans_stack[current_element].gpt_get(point); } /* * Get the specified projective parameter */ ale_pos gpt_get(int point, int dim) { return trans_stack[current_element].gpt_get(point, dim); } /* * Translate by a given amount */ void translate(point p) { trans_stack[current_element].translate(p); } /* * Rotate by a given amount about a given point. */ void rotate(point p, ale_pos degrees) { trans_stack[current_element].rotate(p, degrees); } void reset_memos() { for (unsigned int t = 0; t < trans_stack.size(); t++) trans_stack[t].reset_memos(); } /* * Rescale a transform with a given factor. */ void specific_rescale(ale_pos factor) { /* * Ensure that no maps exist. */ assert (use_multi == 0); assert (spatio_elem_map == NULL); assert (spatio_elem_map_r == NULL); trans_stack[current_element].rescale(factor); } /* * Set the dimensions of the image. */ void specific_set_dimensions(const image *im) { for (unsigned int t = 0; t < trans_stack.size(); t++) trans_stack[t].set_dimensions(im); } void map_area(point p, point *q, ale_pos d[2]) { t_at_point(p / scale_factor).map_area(p, q, d); } void map_area_unscaled(point p, point *q, ale_pos d[2]) { t_at_point(p).map_area_unscaled(p, q, d); } void unscaled_map_area_inverse(point p, point *q, ale_pos d[2]) { t_at_inv_point(p).unscaled_map_area_inverse(p, q, d); } /* * Modify all projective parameters at once. Accommodate bugs in the * version 0 transformation file handler (ALE versions 0.4.0p1 and * earlier). This code is only called when using a transformation data * file created with an old version of ALE. */ void gpt_v0_set(point x[4]) { trans_stack[current_element].gpt_v0_set(x); } /* * Modify all euclidean parameters at once. Accommodate bugs in the * version 0 transformation file handler (ALE versions 0.4.0p1 and * earlier). This code is only called when using a transformation data * file created with an old version of ALE. */ void eu_v0_set(ale_pos eu[3]) { trans_stack[current_element].eu_v0_set(eu); } void debug_output() { for (unsigned int t = 0; t < trans_stack.size(); t++) trans_stack[t].debug_output(); } }; #endif