Chris@101: // Boost.Polygon library transform.hpp header file Chris@16: Chris@16: // Copyright (c) Intel Corporation 2008. Chris@16: // Copyright (c) 2008-2012 Simonson Lucanus. Chris@16: // Copyright (c) 2012-2012 Andrii Sydorchuk. Chris@16: Chris@16: // See http://www.boost.org for updates, documentation, and revision history. Chris@16: // Use, modification and distribution is subject to the Boost Software License, Chris@16: // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: #ifndef BOOST_POLYGON_TRANSFORM_HPP Chris@16: #define BOOST_POLYGON_TRANSFORM_HPP Chris@16: Chris@16: #include "isotropy.hpp" Chris@16: Chris@16: namespace boost { Chris@16: namespace polygon { Chris@16: // Transformation of Coordinate System. Chris@16: // Enum meaning: Chris@16: // Select which direction_2d to change the positive direction of each Chris@16: // axis in the old coordinate system to map it to the new coordiante system. Chris@16: // The first direction_2d listed for each enum is the direction to map the Chris@16: // positive horizontal direction to. Chris@16: // The second direction_2d listed for each enum is the direction to map the Chris@16: // positive vertical direction to. Chris@16: // The zero position bit (LSB) indicates whether the horizontal axis flips Chris@16: // when transformed. Chris@16: // The 1st postion bit indicates whether the vertical axis flips when Chris@16: // transformed. Chris@16: // The 2nd position bit indicates whether the horizontal and vertical axis Chris@16: // swap positions when transformed. Chris@16: // Enum Values: Chris@16: // 000 EAST NORTH Chris@16: // 001 WEST NORTH Chris@16: // 010 EAST SOUTH Chris@16: // 011 WEST SOUTH Chris@16: // 100 NORTH EAST Chris@16: // 101 SOUTH EAST Chris@16: // 110 NORTH WEST Chris@16: // 111 SOUTH WEST Chris@16: class axis_transformation { Chris@16: public: Chris@16: enum ATR { Chris@101: #ifdef BOOST_POLYGON_ENABLE_DEPRECATED Chris@101: EN = 0, Chris@101: WN = 1, Chris@101: ES = 2, Chris@101: WS = 3, Chris@101: NE = 4, Chris@101: SE = 5, Chris@101: NW = 6, Chris@101: SW = 7, Chris@101: #endif Chris@16: NULL_TRANSFORM = 0, Chris@16: BEGIN_TRANSFORM = 0, Chris@101: EAST_NORTH = 0, Chris@101: WEST_NORTH = 1, FLIP_X = 1, Chris@101: EAST_SOUTH = 2, FLIP_Y = 2, Chris@101: WEST_SOUTH = 3, FLIP_XY = 3, Chris@101: NORTH_EAST = 4, SWAP_XY = 4, Chris@101: SOUTH_EAST = 5, ROTATE_LEFT = 5, Chris@101: NORTH_WEST = 6, ROTATE_RIGHT = 6, Chris@101: SOUTH_WEST = 7, FLIP_SWAP_XY = 7, Chris@16: END_TRANSFORM = 7 Chris@16: }; Chris@16: Chris@16: // Individual axis enum values indicate which axis an implicit individual Chris@16: // axis will be mapped to. Chris@16: // The value of the enum paired with an axis provides the information Chris@16: // about what the axis will transform to. Chris@16: // Three individual axis values, one for each axis, are equivalent to one Chris@16: // ATR enum value, but easier to work with because they are independent. Chris@16: // Converting to and from the individual axis values from the ATR value Chris@16: // is a convenient way to implement tranformation related functionality. Chris@16: // Enum meanings: Chris@16: // PX: map to positive x axis Chris@16: // NX: map to negative x axis Chris@16: // PY: map to positive y axis Chris@16: // NY: map to negative y axis Chris@16: enum INDIVIDUAL_AXIS { Chris@16: PX = 0, Chris@16: NX = 1, Chris@16: PY = 2, Chris@16: NY = 3 Chris@16: }; Chris@16: Chris@16: axis_transformation() : atr_(NULL_TRANSFORM) {} Chris@16: explicit axis_transformation(ATR atr) : atr_(atr) {} Chris@16: axis_transformation(const axis_transformation& atr) : atr_(atr.atr_) {} Chris@16: Chris@16: explicit axis_transformation(const orientation_2d& orient) { Chris@16: const ATR tmp[2] = { Chris@16: NORTH_EAST, // sort x, then y Chris@16: EAST_NORTH // sort y, then x Chris@16: }; Chris@16: atr_ = tmp[orient.to_int()]; Chris@16: } Chris@16: Chris@16: explicit axis_transformation(const direction_2d& dir) { Chris@16: const ATR tmp[4] = { Chris@16: SOUTH_EAST, // sort x, then y Chris@16: NORTH_EAST, // sort x, then y Chris@16: EAST_SOUTH, // sort y, then x Chris@16: EAST_NORTH // sort y, then x Chris@16: }; Chris@16: atr_ = tmp[dir.to_int()]; Chris@16: } Chris@16: Chris@16: // assignment operator Chris@16: axis_transformation& operator=(const axis_transformation& a) { Chris@16: atr_ = a.atr_; Chris@16: return *this; Chris@16: } Chris@16: Chris@16: // assignment operator Chris@16: axis_transformation& operator=(const ATR& atr) { Chris@16: atr_ = atr; Chris@16: return *this; Chris@16: } Chris@16: Chris@16: // equivalence operator Chris@16: bool operator==(const axis_transformation& a) const { Chris@16: return atr_ == a.atr_; Chris@16: } Chris@16: Chris@16: // inequivalence operator Chris@16: bool operator!=(const axis_transformation& a) const { Chris@16: return !(*this == a); Chris@16: } Chris@16: Chris@16: // ordering Chris@16: bool operator<(const axis_transformation& a) const { Chris@16: return atr_ < a.atr_; Chris@16: } Chris@16: Chris@16: // concatenate this with that Chris@16: axis_transformation& operator+=(const axis_transformation& a) { Chris@16: bool abit2 = (a.atr_ & 4) != 0; Chris@16: bool abit1 = (a.atr_ & 2) != 0; Chris@16: bool abit0 = (a.atr_ & 1) != 0; Chris@16: bool bit2 = (atr_ & 4) != 0; Chris@16: bool bit1 = (atr_ & 2) != 0; Chris@16: bool bit0 = (atr_ & 1) != 0; Chris@16: int indexes[2][2] = { Chris@16: { (int)bit2, (int)(!bit2) }, Chris@16: { (int)abit2, (int)(!abit2) } Chris@16: }; Chris@16: int zero_bits[2][2] = { Chris@16: {bit0, bit1}, {abit0, abit1} Chris@16: }; Chris@16: int nbit1 = zero_bits[0][1] ^ zero_bits[1][indexes[0][1]]; Chris@16: int nbit0 = zero_bits[0][0] ^ zero_bits[1][indexes[0][0]]; Chris@16: indexes[0][0] = indexes[1][indexes[0][0]]; Chris@16: indexes[0][1] = indexes[1][indexes[0][1]]; Chris@16: int nbit2 = indexes[0][0] & 1; // swap xy Chris@16: atr_ = (ATR)((nbit2 << 2) + (nbit1 << 1) + nbit0); Chris@16: return *this; Chris@16: } Chris@16: Chris@16: // concatenation operator Chris@16: axis_transformation operator+(const axis_transformation& a) const { Chris@16: axis_transformation retval(*this); Chris@16: return retval+=a; Chris@16: } Chris@16: Chris@16: // populate_axis_array writes the three INDIVIDUAL_AXIS values that the Chris@16: // ATR enum value of 'this' represent into axis_array Chris@16: void populate_axis_array(INDIVIDUAL_AXIS axis_array[]) const { Chris@16: bool bit2 = (atr_ & 4) != 0; Chris@16: bool bit1 = (atr_ & 2) != 0; Chris@16: bool bit0 = (atr_ & 1) != 0; Chris@16: axis_array[1] = (INDIVIDUAL_AXIS)(((int)(!bit2) << 1) + bit1); Chris@16: axis_array[0] = (INDIVIDUAL_AXIS)(((int)(bit2) << 1) + bit0); Chris@16: } Chris@16: Chris@16: // it is recommended that the directions stored in an array Chris@16: // in the caller code for easier isotropic access by orientation value Chris@16: void get_directions(direction_2d& horizontal_dir, Chris@16: direction_2d& vertical_dir) const { Chris@16: bool bit2 = (atr_ & 4) != 0; Chris@16: bool bit1 = (atr_ & 2) != 0; Chris@16: bool bit0 = (atr_ & 1) != 0; Chris@16: vertical_dir = direction_2d((direction_2d_enum)(((int)(!bit2) << 1) + !bit1)); Chris@16: horizontal_dir = direction_2d((direction_2d_enum)(((int)(bit2) << 1) + !bit0)); Chris@16: } Chris@16: Chris@16: // combine_axis_arrays concatenates this_array and that_array overwriting Chris@16: // the result into this_array Chris@16: static void combine_axis_arrays(INDIVIDUAL_AXIS this_array[], Chris@16: const INDIVIDUAL_AXIS that_array[]) { Chris@16: int indexes[2] = { this_array[0] >> 1, this_array[1] >> 1 }; Chris@16: int zero_bits[2][2] = { Chris@16: { this_array[0] & 1, this_array[1] & 1 }, Chris@16: { that_array[0] & 1, that_array[1] & 1 } Chris@16: }; Chris@16: this_array[0] = (INDIVIDUAL_AXIS)((int)this_array[0] | Chris@16: ((int)zero_bits[0][0] ^ Chris@16: (int)zero_bits[1][indexes[0]])); Chris@16: this_array[1] = (INDIVIDUAL_AXIS)((int)this_array[1] | Chris@16: ((int)zero_bits[0][1] ^ Chris@16: (int)zero_bits[1][indexes[1]])); Chris@16: } Chris@16: Chris@16: // write_back_axis_array converts an array of three INDIVIDUAL_AXIS values Chris@16: // to the ATR enum value and sets 'this' to that value Chris@16: void write_back_axis_array(const INDIVIDUAL_AXIS this_array[]) { Chris@16: int bit2 = ((int)this_array[0] & 2) != 0; // swap xy Chris@16: int bit1 = ((int)this_array[1] & 1); Chris@16: int bit0 = ((int)this_array[0] & 1); Chris@16: atr_ = ATR((bit2 << 2) + (bit1 << 1) + bit0); Chris@16: } Chris@16: Chris@16: // behavior is deterministic but undefined in the case where illegal Chris@16: // combinations of directions are passed in. Chris@16: axis_transformation& set_directions(const direction_2d& horizontal_dir, Chris@16: const direction_2d& vertical_dir) { Chris@16: int bit2 = (static_cast(horizontal_dir).to_int()) != 0; Chris@16: int bit1 = !(vertical_dir.to_int() & 1); Chris@16: int bit0 = !(horizontal_dir.to_int() & 1); Chris@16: atr_ = ATR((bit2 << 2) + (bit1 << 1) + bit0); Chris@16: return *this; Chris@16: } Chris@16: Chris@16: // transform the three coordinates by reference Chris@16: template Chris@16: void transform(coordinate_type& x, coordinate_type& y) const { Chris@16: int bit2 = (atr_ & 4) != 0; Chris@16: int bit1 = (atr_ & 2) != 0; Chris@16: int bit0 = (atr_ & 1) != 0; Chris@16: x *= -((bit0 << 1) - 1); Chris@16: y *= -((bit1 << 1) - 1); Chris@16: predicated_swap(bit2 != 0, x, y); Chris@16: } Chris@16: Chris@16: // invert this axis_transformation Chris@16: axis_transformation& invert() { Chris@16: int bit2 = ((atr_ & 4) != 0); Chris@16: int bit1 = ((atr_ & 2) != 0); Chris@16: int bit0 = ((atr_ & 1) != 0); Chris@16: // swap bit 0 and bit 1 if bit2 is 1 Chris@16: predicated_swap(bit2 != 0, bit0, bit1); Chris@16: bit1 = bit1 << 1; Chris@16: atr_ = (ATR)(atr_ & (32+16+8+4)); // mask away bit0 and bit1 Chris@16: atr_ = (ATR)(atr_ | bit0 | bit1); Chris@16: return *this; Chris@16: } Chris@16: Chris@16: // get the inverse axis_transformation of this Chris@16: axis_transformation inverse() const { Chris@16: axis_transformation retval(*this); Chris@16: return retval.invert(); Chris@16: } Chris@16: Chris@16: private: Chris@16: ATR atr_; Chris@16: }; Chris@16: Chris@16: // Scaling object to be used to store the scale factor for each axis. Chris@16: // For use by the transformation object, in that context the scale factor Chris@16: // is the amount that each axis scales by when transformed. Chris@16: template Chris@16: class anisotropic_scale_factor { Chris@16: public: Chris@16: anisotropic_scale_factor() { Chris@16: scale_[0] = 1; Chris@16: scale_[1] = 1; Chris@16: } Chris@16: anisotropic_scale_factor(scale_factor_type xscale, Chris@16: scale_factor_type yscale) { Chris@16: scale_[0] = xscale; Chris@16: scale_[1] = yscale; Chris@16: } Chris@16: Chris@16: // get a component of the anisotropic_scale_factor by orientation Chris@16: scale_factor_type get(orientation_2d orient) const { Chris@16: return scale_[orient.to_int()]; Chris@16: } Chris@16: Chris@16: // set a component of the anisotropic_scale_factor by orientation Chris@16: void set(orientation_2d orient, scale_factor_type value) { Chris@16: scale_[orient.to_int()] = value; Chris@16: } Chris@16: Chris@16: scale_factor_type x() const { Chris@16: return scale_[HORIZONTAL]; Chris@16: } Chris@16: Chris@16: scale_factor_type y() const { Chris@16: return scale_[VERTICAL]; Chris@16: } Chris@16: Chris@16: void x(scale_factor_type value) { Chris@16: scale_[HORIZONTAL] = value; Chris@16: } Chris@16: Chris@16: void y(scale_factor_type value) { Chris@16: scale_[VERTICAL] = value; Chris@16: } Chris@16: Chris@16: // concatination operator (convolve scale factors) Chris@16: anisotropic_scale_factor operator+(const anisotropic_scale_factor& s) const { Chris@16: anisotropic_scale_factor retval(*this); Chris@16: return retval += s; Chris@16: } Chris@16: Chris@16: // concatinate this with that Chris@16: const anisotropic_scale_factor& operator+=( Chris@16: const anisotropic_scale_factor& s) { Chris@16: scale_[0] *= s.scale_[0]; Chris@16: scale_[1] *= s.scale_[1]; Chris@16: return *this; Chris@16: } Chris@16: Chris@16: // transform this scale with an axis_transform Chris@16: anisotropic_scale_factor& transform(axis_transformation atr) { Chris@16: direction_2d dirs[2]; Chris@16: atr.get_directions(dirs[0], dirs[1]); Chris@16: scale_factor_type tmp[2] = {scale_[0], scale_[1]}; Chris@16: for (int i = 0; i < 2; ++i) { Chris@16: scale_[orientation_2d(dirs[i]).to_int()] = tmp[i]; Chris@16: } Chris@16: return *this; Chris@16: } Chris@16: Chris@16: // scale the two coordinates Chris@16: template Chris@16: void scale(coordinate_type& x, coordinate_type& y) const { Chris@16: x = scaling_policy::round( Chris@16: (scale_factor_type)x * get(HORIZONTAL)); Chris@16: y = scaling_policy::round( Chris@16: (scale_factor_type)y * get(HORIZONTAL)); Chris@16: } Chris@16: Chris@16: // invert this scale factor to give the reverse scale factor Chris@16: anisotropic_scale_factor& invert() { Chris@16: x(1/x()); Chris@16: y(1/y()); Chris@16: return *this; Chris@16: } Chris@16: Chris@16: private: Chris@16: scale_factor_type scale_[2]; Chris@16: }; Chris@16: Chris@16: // Transformation object, stores and provides services for transformations. Chris@16: // Consits of axis transformation, scale factor and translation. Chris@16: // The tranlation is the position of the origin of the new coordinate system of Chris@16: // in the old system. Coordinates are scaled before they are transformed. Chris@16: template Chris@16: class transformation { Chris@16: public: Chris@16: transformation() : atr_(), p_(0, 0) {} Chris@16: explicit transformation(axis_transformation atr) : atr_(atr), p_(0, 0) {} Chris@16: explicit transformation(axis_transformation::ATR atr) : atr_(atr), p_(0, 0) {} Chris@16: transformation(const transformation& tr) : atr_(tr.atr_), p_(tr.p_) {} Chris@16: Chris@16: template Chris@16: explicit transformation(const point_type& p) : atr_(), p_(0, 0) { Chris@16: set_translation(p); Chris@16: } Chris@16: Chris@16: template Chris@16: transformation(axis_transformation atr, Chris@16: const point_type& p) : atr_(atr), p_(0, 0) { Chris@16: set_translation(p); Chris@16: } Chris@16: Chris@16: template Chris@16: transformation(axis_transformation atr, Chris@16: const point_type& referencePt, Chris@16: const point_type& destinationPt) : atr_(), p_(0, 0) { Chris@16: transformation tmp(referencePt); Chris@16: transformation rotRef(atr); Chris@16: transformation tmpInverse = tmp.inverse(); Chris@16: point_type decon(referencePt); Chris@16: deconvolve(decon, destinationPt); Chris@16: transformation displacement(decon); Chris@16: tmp += rotRef; Chris@16: tmp += tmpInverse; Chris@16: tmp += displacement; Chris@16: (*this) = tmp; Chris@16: } Chris@16: Chris@16: // equivalence operator Chris@16: bool operator==(const transformation& tr) const { Chris@16: return (atr_ == tr.atr_) && (p_ == tr.p_); Chris@16: } Chris@16: Chris@16: // inequivalence operator Chris@16: bool operator!=(const transformation& tr) const { Chris@16: return !(*this == tr); Chris@16: } Chris@16: Chris@16: // ordering Chris@16: bool operator<(const transformation& tr) const { Chris@16: return (atr_ < tr.atr_) || ((atr_ == tr.atr_) && (p_ < tr.p_)); Chris@16: } Chris@16: Chris@16: // concatenation operator Chris@16: transformation operator+(const transformation& tr) const { Chris@16: transformation retval(*this); Chris@16: return retval+=tr; Chris@16: } Chris@16: Chris@16: // concatenate this with that Chris@16: const transformation& operator+=(const transformation& tr) { Chris@16: coordinate_type x, y; Chris@16: transformation inv = inverse(); Chris@16: inv.transform(x, y); Chris@16: p_.set(HORIZONTAL, p_.get(HORIZONTAL) + x); Chris@16: p_.set(VERTICAL, p_.get(VERTICAL) + y); Chris@16: // concatenate axis transforms Chris@16: atr_ += tr.atr_; Chris@16: return *this; Chris@16: } Chris@16: Chris@16: // get the axis_transformation portion of this Chris@16: axis_transformation get_axis_transformation() const { Chris@16: return atr_; Chris@16: } Chris@16: Chris@16: // set the axis_transformation portion of this Chris@16: void set_axis_transformation(const axis_transformation& atr) { Chris@16: atr_ = atr; Chris@16: } Chris@16: Chris@16: // get the translation Chris@16: template Chris@16: void get_translation(point_type& p) const { Chris@16: assign(p, p_); Chris@16: } Chris@16: Chris@16: // set the translation Chris@16: template Chris@16: void set_translation(const point_type& p) { Chris@16: assign(p_, p); Chris@16: } Chris@16: Chris@16: // apply the 2D portion of this transformation to the two coordinates given Chris@16: void transform(coordinate_type& x, coordinate_type& y) const { Chris@16: y -= p_.get(VERTICAL); Chris@16: x -= p_.get(HORIZONTAL); Chris@16: atr_.transform(x, y); Chris@16: } Chris@16: Chris@16: // invert this transformation Chris@16: transformation& invert() { Chris@16: coordinate_type x = p_.get(HORIZONTAL), y = p_.get(VERTICAL); Chris@16: atr_.transform(x, y); Chris@16: x *= -1; Chris@16: y *= -1; Chris@16: p_ = point_data(x, y); Chris@16: atr_.invert(); Chris@16: return *this; Chris@16: } Chris@16: Chris@16: // get the inverse of this transformation Chris@16: transformation inverse() const { Chris@16: transformation ret_val(*this); Chris@16: return ret_val.invert(); Chris@16: } Chris@16: Chris@16: void get_directions(direction_2d& horizontal_dir, Chris@16: direction_2d& vertical_dir) const { Chris@16: return atr_.get_directions(horizontal_dir, vertical_dir); Chris@16: } Chris@16: Chris@16: private: Chris@16: axis_transformation atr_; Chris@16: point_data p_; Chris@16: }; Chris@16: } // polygon Chris@16: } // boost Chris@16: Chris@16: #endif // BOOST_POLYGON_TRANSFORM_HPP