00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #ifndef WFMATH_POLYGON_FUNCS_H
00027 #define WFMATH_POLYGON_FUNCS_H
00028
00029 #include <wfmath/polygon.h>
00030
00031 #include <wfmath/vector.h>
00032 #include <wfmath/point.h>
00033 #include <wfmath/ball.h>
00034
00035 #include <cmath>
00036
00037 #include <cassert>
00038 #include <limits>
00039
00040 namespace WFMath {
00041
00042 template<int dim>
00043 inline _Poly2Orient<dim>& _Poly2Orient<dim>::operator=(const _Poly2Orient<dim>& a)
00044 {
00045 m_origin = a.m_origin;
00046
00047 for(int i = 0; i < 2; ++i)
00048 m_axes[i] = a.m_axes[i];
00049
00050 return *this;
00051 }
00052
00053 template<int dim>
00054 inline bool Polygon<dim>::isEqualTo(const Polygon<dim>& p, CoordType epsilon) const
00055 {
00056
00057
00058
00059 size_t size = m_poly.numCorners();
00060 if(size != p.m_poly.numCorners())
00061 return false;
00062
00063 for(size_t i = 0; i < size; ++i)
00064 if(!Equal(getCorner(i), p.getCorner(i), epsilon))
00065 return false;
00066
00067 return true;
00068 }
00069
00070 template<int dim>
00071 inline Point<dim> _Poly2Orient<dim>::convert(const Point<2>& p) const
00072 {
00073 assert(m_origin.isValid());
00074
00075 Point<dim> out = m_origin;
00076
00077 for(int j = 0; j < 2; ++j) {
00078 if(m_axes[j].isValid())
00079 out += m_axes[j] * p[j];
00080 else
00081 assert(p[j] == 0);
00082 }
00083
00084 out.setValid(p.isValid());
00085
00086 return out;
00087 }
00088
00089 template<int dim>
00090 bool _Poly2Orient<dim>::expand(const Point<dim>& pd, Point<2>& p2, CoordType epsilon)
00091 {
00092 p2[0] = p2[1] = 0;
00093 p2.setValid();
00094
00095 if(!m_origin.isValid()) {
00096 m_origin = pd;
00097 m_origin.setValid();
00098 return true;
00099 }
00100
00101 Vector<dim> shift = pd - m_origin, start_shift = shift;
00102
00103 CoordType bound = shift.sqrMag() * epsilon;
00104
00105 int j = 0;
00106
00107 while(true) {
00108 if(Dot(shift, start_shift) <= bound)
00109 return true;
00110
00111 if(j == 2) {
00112 p2.setValid(false);
00113 return false;
00114 }
00115
00116 if(!m_axes[j].isValid()) {
00117 p2[j] = shift.mag();
00118 m_axes[j] = shift / p2[j];
00119 m_axes[j].setValid();
00120 return true;
00121 }
00122
00123 p2[j] = Dot(shift, m_axes[j]);
00124 shift -= m_axes[j] * p2[j];
00125 ++j;
00126 }
00127 }
00128
00129 template<int dim>
00130 _Poly2Reorient _Poly2Orient<dim>::reduce(const Polygon<2>& poly, size_t skip)
00131 {
00132 if(poly.numCorners() <= ((skip == 0) ? 1 : 0)) {
00133 m_origin.setValid(false);
00134 m_axes[0].setValid(false);
00135 m_axes[1].setValid(false);
00136 return _WFMATH_POLY2REORIENT_NONE;
00137 }
00138
00139 assert(m_origin.isValid());
00140
00141 if(!m_axes[0].isValid())
00142 return _WFMATH_POLY2REORIENT_NONE;
00143
00144
00145
00146 bool still_valid[2] = {false,}, got_ratio = false;
00147 CoordType ratio = std::numeric_limits<CoordType>::max();
00148 CoordType size = std::numeric_limits<CoordType>::max();
00149 CoordType epsilon;
00150 size_t i, end = poly.numCorners();
00151
00152
00153 for(i = 0; i < end; ++i) {
00154 if(i == skip)
00155 continue;
00156 const Point<2> &p = poly[i];
00157 CoordType x = std::fabs(p[0]),
00158 y = std::fabs(p[1]),
00159 max = (x > y) ? x : y;
00160 if(i == 0 || max < size)
00161 size = max;
00162 }
00163 int exponent;
00164 (void) std::frexp(size, &exponent);
00165 epsilon = std::ldexp(numeric_constants<CoordType>::epsilon(), exponent);
00166
00167 i = 0;
00168 if(skip == 0)
00169 ++i;
00170 assert(i != end);
00171 Point<2> first_point = poly[i];
00172 first_point.setValid();
00173
00174 while(++i != end) {
00175 if(i == skip)
00176 continue;
00177
00178 Vector<2> diff = poly[i] - first_point;
00179 if(diff.sqrMag() < epsilon * epsilon)
00180 continue;
00181 if(!m_axes[1].isValid())
00182 return _WFMATH_POLY2REORIENT_NONE;
00183 for(int j = 0; j < 2; ++j) {
00184 if(std::fabs(diff[j]) < epsilon) {
00185 assert(diff[j ? 0 : 1] >= epsilon);
00186 if(still_valid[j ? 0 : 1] || got_ratio)
00187 return _WFMATH_POLY2REORIENT_NONE;
00188 still_valid[j] = true;
00189 }
00190 }
00191
00192 if(still_valid[0] || still_valid[1])
00193 return _WFMATH_POLY2REORIENT_NONE;
00194 CoordType new_ratio = diff[1] / diff[0];
00195 if(!got_ratio) {
00196 ratio = new_ratio;
00197 got_ratio = true;
00198 continue;
00199 }
00200 if(!Equal(ratio, new_ratio))
00201 return _WFMATH_POLY2REORIENT_NONE;
00202 }
00203
00204
00205
00206 if(still_valid[0]) {
00207 assert(m_axes[1].isValid());
00208 assert(!still_valid[1]);
00209 assert(!got_ratio);
00210
00211 m_origin += m_axes[1] * first_point[1];
00212 m_axes[1].setValid(false);
00213 return _WFMATH_POLY2REORIENT_CLEAR_AXIS2;
00214 }
00215
00216 if(still_valid[1]) {
00217 assert(m_axes[1].isValid());
00218 assert(!got_ratio);
00219
00220 m_origin += m_axes[0] * first_point[0];
00221 m_axes[0] = m_axes[1];
00222 m_axes[1].setValid(false);
00223 return _WFMATH_POLY2REORIENT_MOVE_AXIS2_TO_AXIS1;
00224 }
00225
00226
00227 if(!got_ratio) {
00228 m_origin += m_axes[0] * first_point[0];
00229 if(m_axes[1].isValid())
00230 m_origin += m_axes[1] * first_point[1];
00231 m_axes[0].setValid(false);
00232 m_axes[1].setValid(false);
00233 return _WFMATH_POLY2REORIENT_CLEAR_BOTH_AXES;
00234 }
00235
00236 assert(m_axes[1].isValid());
00237
00238
00239
00240
00241 Vector<dim> new0;
00242 new0 = m_axes[0] + m_axes[1] * ratio;
00243 CoordType norm = new0.mag();
00244 new0 /= norm;
00245
00246
00247
00248
00249
00250
00251
00252 m_origin += m_axes[1] * (first_point[1] - ratio * first_point[0]);
00253
00254 m_axes[0] = new0;
00255 m_axes[1].setValid(false);
00256 return _Poly2Reorient(_WFMATH_POLY2REORIENT_SCALE1_CLEAR2, norm);
00257 }
00258
00259 template<int dim>
00260 inline void _Poly2Orient<dim>::rotate(const RotMatrix<dim>& m, const Point<dim>& p)
00261 {
00262 m_origin.rotate(m, p);
00263
00264 for(int j = 0; j < 2; ++j)
00265 m_axes[j] = Prod(m_axes[j], m);
00266 }
00267
00268 template<int dim>
00269 void _Poly2Orient<dim>::rotate2(const RotMatrix<dim>& m, const Point<2>& p)
00270 {
00271 assert(m_origin.isValid());
00272
00273 if(!m_axes[0].isValid()) {
00274 assert(p[0] == 0 && p[1] == 0);
00275 return;
00276 }
00277
00278 Vector<dim> shift = m_axes[0] * p[0];
00279 m_axes[0] = Prod(m_axes[0], m);
00280
00281 if(m_axes[1].isValid()) {
00282 shift += m_axes[1] * p[1];
00283 m_axes[1] = Prod(m_axes[1], m);
00284 }
00285 else
00286 assert(p[1] == 0);
00287
00288 m_origin += shift - Prod(shift, m);
00289 }
00290
00291 template<>
00292 inline void _Poly2Orient<3>::rotate(const Quaternion& q, const Point<3>& p)
00293 {
00294 m_origin.rotate(q, p);
00295
00296 for(int j = 0; j < 2; ++j)
00297 m_axes[j].rotate(q);
00298 }
00299
00300 template<>
00301 inline void _Poly2Orient<3>::rotate2(const Quaternion& q, const Point<2>& p)
00302 {
00303 assert(m_origin.isValid());
00304
00305 if(!m_axes[0].isValid()) {
00306 assert(p[0] == 0 && p[1] == 0);
00307 return;
00308 }
00309
00310 Vector<3> shift = m_axes[0] * p[0];
00311 m_axes[0].rotate(q);
00312
00313 if(m_axes[1].isValid()) {
00314 shift += m_axes[1] * p[1];
00315 m_axes[1].rotate(q);
00316 }
00317 else
00318 assert(p[1] == 0);
00319
00320 m_origin += shift - shift.rotate(q);
00321 }
00322
00323 template<int dim>
00324 AxisBox<dim> Polygon<dim>::boundingBox() const
00325 {
00326 assert(m_poly.numCorners() > 0);
00327
00328 Point<dim> min = m_orient.convert(m_poly[0]), max = min;
00329 bool valid = min.isValid();
00330
00331 for(size_t i = 1; i != m_poly.numCorners(); ++i) {
00332 Point<dim> p = m_orient.convert(m_poly[i]);
00333 valid = valid && p.isValid();
00334 for(int j = 0; j < dim; ++j) {
00335 if(p[j] < min[j])
00336 min[j] = p[j];
00337 if(p[j] > max[j])
00338 max[j] = p[j];
00339 }
00340 }
00341
00342 min.setValid(valid);
00343 max.setValid(valid);
00344
00345 return AxisBox<dim>(min, max, true);
00346 }
00347
00348 template<int dim>
00349 inline Ball<dim> Polygon<dim>::boundingSphere() const
00350 {
00351 Ball<2> b = m_poly.boundingSphere();
00352
00353 return Ball<dim>(m_orient.convert(b.center()), b.radius());
00354 }
00355
00356 template<int dim>
00357 inline Ball<dim> Polygon<dim>::boundingSphereSloppy() const
00358 {
00359 Ball<2> b = m_poly.boundingSphereSloppy();
00360
00361 return Ball<dim>(m_orient.convert(b.center()), b.radius());
00362 }
00363
00364 }
00365
00366 #endif // WFMATH_POLYGON_FUNCS_H