00001
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00033
00034 #ifndef GROOVX_VISX_SNAKE_CC_UTC20050626084015_DEFINED
00035 #define GROOVX_VISX_SNAKE_CC_UTC20050626084015_DEFINED
00036
00037 #include "snake.h"
00038
00039 #include "geom/geom.h"
00040 #include "geom/vec2.h"
00041
00042 #include "rutz/rand.h"
00043
00044 #include <algorithm>
00045 #include <cmath>
00046
00047 #include "rutz/debug.h"
00048 GVX_DBG_REGISTER
00049 #include "rutz/trace.h"
00050
00051 using geom::vec2d;
00052
00053 namespace
00054 {
00055 const double MAX_DELTA = M_PI/4.0;
00056 const double TEMPERATURE = 0.05;
00057 const double INITIAL_INCREMENT = 0.1;
00058
00059 struct Tuple4
00060 {
00061 Tuple4() {}
00062
00063 Tuple4(double a0, double a1, double a2, double a3)
00064 { arr[0] = a0; arr[1] = a1; arr[2] = a2; arr[3] = a3; }
00065
00066 double operator[](int i) const { return arr[i]; }
00067 double& operator[](int i) { return arr[i]; }
00068
00069 double arr[4];
00070 };
00071
00072 void pickRandom4(int length, int i[], rutz::urand& urand)
00073 {
00074 i[0] = i[1] = i[2] = i[3] = urand.idraw(length);
00075
00076 while (i[1] == i[0])
00077 {
00078 i[1] = urand.idraw(length);
00079 }
00080
00081 while (i[2] == i[0] || i[2] == i[1])
00082 {
00083 i[2] = urand.idraw(length);
00084 }
00085
00086 while (i[3] == i[0] || i[3] == i[1] || i[3] == i[2])
00087 {
00088 i[3] = urand.idraw(length);
00089 }
00090
00091 std::sort(i, i+4);
00092 }
00093
00094 Tuple4 getEdgeLengths(const vec2d no[4])
00095 {
00096 return Tuple4(no[0].distance_to(no[1]),
00097 no[1].distance_to(no[2]),
00098 no[2].distance_to(no[3]),
00099 no[3].distance_to(no[0]));
00100 }
00101
00102 Tuple4 getThetas(const vec2d no[4])
00103 {
00104 return Tuple4(no[0].angle_to(no[1]),
00105 no[1].angle_to(no[2]),
00106 no[2].angle_to(no[3]),
00107 no[3].angle_to(no[0]));
00108 }
00109
00110 Tuple4 getAlphas(const Tuple4& theta)
00111 {
00112 return Tuple4(geom::rad_0_2pi(M_PI - theta[0] + theta[3]),
00113 geom::rad_0_2pi(M_PI - theta[1] + theta[0]),
00114 geom::rad_0_2pi(M_PI - theta[2] + theta[1]),
00115 geom::rad_0_2pi(M_PI - theta[3] + theta[2]));
00116 }
00117
00118
00119 bool squashQuadrangle(const vec2d& old_0, const vec2d& old_1,
00120 const vec2d& old_2, const vec2d& old_3,
00121 vec2d* new_0, vec2d* new_1,
00122 vec2d* new_2, vec2d* new_3,
00123 double new_theta)
00124 {
00125 GVX_TRACE("squashQuadrangle");
00126 {
00127
00128
00129
00130 const double d = old_0.distance_to(old_1);
00131
00132 new_1->x() = old_0.x() + d * cos(new_theta);
00133 new_1->y() = old_0.y() + d * sin(new_theta);
00134 }
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199 const double a = old_3.distance_to(*new_1);
00200 const double b = old_1.distance_to(old_2);
00201 const double c = old_2.distance_to(old_3);
00202
00203 const double cos_ang = (old_3.x() - new_1->x()) / a;
00204 const double sin_ang = (old_3.y() - new_1->y()) / a;
00205
00206 const double a_sq = a*a;
00207 const double b_sq = b*b;
00208 const double c_sq = c*c;
00209
00210 const double det =
00211 2*(a_sq*b_sq + b_sq*c_sq + a_sq*c_sq)
00212 - a_sq*a_sq - b_sq*b_sq - c_sq*c_sq;
00213
00214 if (det < 0)
00215 return false;
00216
00217
00218
00219 const double xp = (a*a + b*b - c*c) / (2.0*a);
00220 const double yp = sqrt(det) / (2.0*a);
00221
00222
00223
00224 const vec2d new_2a
00225 (new_1->x() + xp*cos_ang - yp*sin_ang,
00226 new_1->y() + xp*sin_ang + yp*cos_ang);
00227
00228 const vec2d new_2b
00229 (new_1->x() + xp*cos_ang + yp*sin_ang,
00230 new_1->y() + xp*sin_ang - yp*cos_ang);
00231
00232 const double d_2_2a = new_2a.distance_to(old_2);
00233 const double d_2_2b = new_2b.distance_to(old_2);
00234
00235 *new_0 = old_0;
00236 *new_2 = (d_2_2a < d_2_2b) ? new_2a : new_2b;
00237 *new_3 = old_3;
00238
00239 return true;
00240 }
00241
00242 bool deltaTooBig(const Tuple4& delta)
00243 {
00244 return (fabs(delta[0]) > MAX_DELTA
00245 || fabs(delta[1]) > MAX_DELTA
00246 || fabs(delta[2]) > MAX_DELTA
00247 || fabs(delta[3]) > MAX_DELTA);
00248 }
00249
00250 bool acceptNewDelta(const Tuple4& newdelta, const Tuple4& olddelta,
00251 rutz::urand& urand)
00252 {
00253 const double energy_diff =
00254 newdelta[0]*newdelta[0] - olddelta[0]*olddelta[0]
00255 + newdelta[1]*newdelta[1] - olddelta[1]*olddelta[1]
00256 + newdelta[2]*newdelta[2] - olddelta[2]*olddelta[2]
00257 + newdelta[3]*newdelta[3] - olddelta[3]*olddelta[3];
00258
00259
00260 const double probability = exp(-energy_diff/TEMPERATURE);
00261
00262 return urand.fdraw() <= probability;
00263 }
00264 }
00265
00266 Snake::Snake(int l, double spacing, rutz::urand& urand) :
00267 itsLength(l),
00268 itsElem(itsLength)
00269 {
00270 GVX_TRACE("Snake::Snake");
00271
00272 const double radius = (itsLength * spacing) / (2*M_PI);
00273
00274 const double alpha_start = 2 * M_PI * urand.fdraw();
00275
00276 for (int n = 0; n < itsLength; ++n)
00277 {
00278 const double alpha = alpha_start + 2 * M_PI * n / itsLength;
00279
00280 itsElem[n].set_polar_rad(radius, -alpha);
00281 }
00282
00283 const int ITERS = 400;
00284
00285 for (int count = 0; count < ITERS; ++count)
00286 {
00287 const bool convergeOk = this->jiggle(urand);
00288 if ( !convergeOk )
00289 {
00290 printf("warning: Snake::jiggle failed to converge\n");
00291 break;
00292 }
00293 }
00294
00295
00296 vec2d c;
00297
00298 for (int n = 0; n < itsLength; ++n)
00299 c += itsElem[n];
00300
00301 c /= itsLength;
00302
00303 for (int n = 0; n < itsLength; ++n)
00304 itsElem[n] -= c;
00305 }
00306
00307 Snake::~Snake()
00308 {
00309 GVX_TRACE("Snake::~Snake");
00310 }
00311
00312 GaborArrayElement Snake::getElement(int n) const
00313 {
00314 GVX_TRACE("Snake::getElement");
00315
00316 GVX_ASSERT(n < itsLength);
00317
00318 GaborArrayElement result;
00319
00320 result.type = GaborArrayElement::CONTOUR;
00321 result.pos.x() = 0.5 * (elem(n).x() + elem(n+1).x());
00322 result.pos.y() = 0.5 * (elem(n).y() + elem(n+1).y());
00323 result.theta = geom::rad_0_2pi(-elem(n).angle_to(elem(n+1)));
00324
00325 return result;
00326 }
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361 bool Snake::jiggle(rutz::urand& urand)
00362 {
00363 GVX_TRACE("Snake::jiggle");
00364
00365 int i[4];
00366 pickRandom4(itsLength, i, urand);
00367
00368 const vec2d old_pos[4] =
00369 {
00370 itsElem[i[0]], itsElem[i[1]], itsElem[i[2]], itsElem[i[3]]
00371 };
00372
00373 const Tuple4 old_theta = getThetas(old_pos);
00374 const Tuple4 old_alpha = getAlphas(old_theta);
00375
00376 const Tuple4 old_delta
00377 (geom::rad_npi_pi(elem(i[0]).angle_to(elem(i[0]+1)) - elem(i[0]-1).angle_to(elem(i[0]))),
00378 geom::rad_npi_pi(elem(i[1]).angle_to(elem(i[1]+1)) - elem(i[1]-1).angle_to(elem(i[1]))),
00379 geom::rad_npi_pi(elem(i[2]).angle_to(elem(i[2]+1)) - elem(i[2]-1).angle_to(elem(i[2]))),
00380 geom::rad_npi_pi(elem(i[3]).angle_to(elem(i[3]+1)) - elem(i[3]-1).angle_to(elem(i[3]))));
00381
00382 vec2d new_pos[4];
00383
00384 double increment = INITIAL_INCREMENT;
00385
00386 const int MAX_ITERS = 10000;
00387
00388 int k = 0;
00389
00390 for (; k < MAX_ITERS; ++k)
00391 {
00392 const double incr = urand.booldraw() ? -increment : increment;
00393
00394 const int r = urand.idraw(4);
00395
00396 const double incr_theta = old_theta[r] - incr;
00397
00398 const bool ok =
00399 squashQuadrangle(old_pos[(r+0)%4], old_pos[(r+1)%4],
00400 old_pos[(r+2)%4], old_pos[(r+3)%4],
00401 &new_pos[(r+0)%4], &new_pos[(r+1)%4],
00402 &new_pos[(r+2)%4], &new_pos[(r+3)%4],
00403 incr_theta);
00404
00405 if (!ok)
00406 {
00407 increment *= 0.5;
00408 continue;
00409 }
00410
00411 const Tuple4 new_theta = getThetas(new_pos);
00412 const Tuple4 new_alpha = getAlphas(new_theta);
00413 const Tuple4 new_delta
00414 (old_delta[0] + (old_alpha[0]-new_alpha[0]),
00415 old_delta[1] + (old_alpha[1]-new_alpha[1]),
00416 old_delta[2] + (old_alpha[2]-new_alpha[2]),
00417 old_delta[3] + (old_alpha[3]-new_alpha[3]));
00418
00419 if (deltaTooBig(new_delta))
00420 {
00421 increment *= 0.8;
00422 continue;
00423 }
00424
00425 if (acceptNewDelta(new_delta, old_delta, urand))
00426 break;
00427 }
00428
00429 bool didConverge = (k < MAX_ITERS);
00430
00431 for (int n = 0; n < 4; ++n)
00432 this->transformPath(i[n], new_pos[n],
00433 i[(n+1)%4], new_pos[(n+1)%4]);
00434
00435 for (int n = 0; n < 4; ++n)
00436 {
00437 itsElem[i[n]] = new_pos[n];
00438 }
00439
00440 return didConverge;
00441 }
00442
00443 void Snake::transformPath(int i1, const vec2d& new1,
00444 int i2, const vec2d& new2)
00445 {
00446 GVX_TRACE("Snake::transformPath");
00447
00448 const vec2d old1 = itsElem[i1];
00449 const vec2d old2 = itsElem[i2];
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471 const double d = old1.distance_to(old2);
00472 const double a = old1.angle_to(old2);
00473
00474 const double dp = new1.distance_to(new2);
00475 const double ap = new1.angle_to(new2);
00476
00477 const double diff_a = ap - a;
00478
00479 const double ratio_d = (dp/d);
00480
00481 const double cos_diff_a = cos(diff_a);
00482 const double sin_diff_a = sin(diff_a);
00483
00484 const double a11 = ratio_d * cos_diff_a;
00485 const double a12 = - ratio_d * sin_diff_a;
00486 const double a21 = ratio_d * sin_diff_a;
00487 const double a22 = ratio_d * cos_diff_a;
00488
00489
00490 for (int n = (i1+1)%itsLength; n != i2; n = (n+1)%itsLength)
00491 {
00492
00493
00494
00495
00496
00497
00498 const double diffx = itsElem[n].x() - old1.x();
00499 const double diffy = itsElem[n].y() - old1.y();
00500
00501 itsElem[n].x() = new1.x() + a11*diffx + a12*diffy;
00502 itsElem[n].y() = new1.y() + a21*diffx + a22*diffy;
00503 }
00504 }
00505
00506 static const char __attribute__((used)) vcid_groovx_visx_snake_cc_utc20050626084015[] = "$Id: snake.cc 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
00507 #endif // !GROOVX_VISX_SNAKE_CC_UTC20050626084015_DEFINED