annotate src/Finder.cpp @ 246:aac9ad4064ea subsequence tip

Fix incorrect handling of silent tail in the non-subsequence MATCH phase; some debug output changes
author Chris Cannam
date Fri, 24 Jul 2020 14:29:55 +0100
parents 39fe8728e1ca
children
rev   line source
cannam@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
cannam@0 2
cannam@0 3 /*
cannam@0 4 Vamp feature extraction plugin using the MATCH audio alignment
cannam@0 5 algorithm.
cannam@0 6
cannam@0 7 Centre for Digital Music, Queen Mary, University of London.
Chris@236 8 Copyright (c) 2007-2020 Simon Dixon, Chris Cannam, and Queen Mary
Chris@230 9 University of London, Copyright (c) 2014-2015 Tido GmbH.
cannam@0 10
cannam@0 11 This program is free software; you can redistribute it and/or
cannam@0 12 modify it under the terms of the GNU General Public License as
cannam@0 13 published by the Free Software Foundation; either version 2 of the
cannam@0 14 License, or (at your option) any later version. See the file
cannam@0 15 COPYING included with this distribution for more information.
cannam@0 16 */
cannam@0 17
Chris@220 18 //#define DEBUG_FINDER 1
Chris@220 19 //#define PERFORM_ERROR_CHECKS 1
Chris@220 20
cannam@0 21 #include "Finder.h"
cannam@0 22
Chris@30 23 #include "Path.h"
Chris@30 24
Chris@30 25 #include <algorithm>
Chris@92 26 #include <iomanip>
Chris@30 27
Chris@72 28 using namespace std;
cannam@0 29
Chris@72 30 Finder::Finder(Matcher *pm)
cannam@0 31 {
Chris@72 32 m_m = pm;
Chris@72 33 m_duration1 = -1;
Chris@72 34 m_duration2 = -1;
cannam@0 35 } // constructor
cannam@0 36
cannam@0 37 Finder::~Finder()
cannam@0 38 {
cannam@0 39 }
cannam@0 40
Chris@60 41 void
Chris@154 42 Finder::setMatcher(Matcher *pm)
Chris@154 43 {
Chris@155 44 cerr << "Finder::setMatcher: finder " << this << ", matcher " << pm << endl;
Chris@154 45 m_m = pm;
Chris@154 46 }
Chris@154 47
Chris@154 48 void
Chris@60 49 Finder::setDurations(int d1, int d2)
Chris@60 50 {
Chris@140 51 #ifdef DEBUG_FINDER
Chris@140 52 cerr << "*** setDurations: " << d1 << ", " << d2 << endl;
Chris@140 53 #endif
Chris@72 54 m_duration1 = d1;
Chris@72 55 m_duration2 = d2;
Chris@60 56 }
Chris@60 57
Chris@154 58 bool
Chris@191 59 Finder::getBestRowCost(int row, int &bestCol, normpathcost_t &min)
Chris@154 60 {
Chris@172 61 if (!m_m->isRowAvailable(row)) return false;
Chris@205 62 pair<int, int> colRange = m_m->getColRangeForRow(row);
Chris@172 63 if (colRange.first >= colRange.second) return false;
Chris@154 64 for (int index = colRange.first; index < colRange.second; index++) {
Chris@191 65 normpathcost_t tmp = m_m->getNormalisedPathCost(row, index);
Chris@154 66 if (index == colRange.first || tmp < min) {
Chris@154 67 min = tmp;
Chris@154 68 bestCol = index;
Chris@154 69 }
Chris@154 70 }
Chris@154 71 return true;
Chris@154 72 }
Chris@154 73
Chris@154 74 bool
Chris@191 75 Finder::getBestColCost(int col, int &bestRow, normpathcost_t &min)
Chris@154 76 {
Chris@154 77 if (!m_m->isColAvailable(col)) return false;
Chris@205 78 pair<int, int> rowRange = m_m->getRowRangeForCol(col);
Chris@154 79 if (rowRange.first >= rowRange.second) return false;
Chris@191 80 bestRow = rowRange.first;
Chris@154 81 for (int index = rowRange.first; index < rowRange.second; index++) {
Chris@191 82 normpathcost_t tmp = m_m->getNormalisedPathCost(index, col);
Chris@191 83 if (index == rowRange.first) {
Chris@191 84 min = tmp;
Chris@191 85 } else if (tmp < min) {
Chris@154 86 min = tmp;
Chris@154 87 bestRow = index;
Chris@154 88 }
Chris@154 89 }
Chris@154 90 return true;
Chris@154 91 }
Chris@154 92
Chris@147 93 void
Chris@147 94 Finder::getBestEdgeCost(int row, int col,
Chris@147 95 int &bestRow, int &bestCol,
Chris@191 96 normpathcost_t &min)
cannam@0 97 {
Chris@191 98 min = m_m->getNormalisedPathCost(row, col);
Chris@72 99
Chris@147 100 bestRow = row;
Chris@147 101 bestCol = col;
Chris@72 102
Chris@205 103 pair<int, int> rowRange = m_m->getRowRangeForCol(col);
Chris@72 104 if (rowRange.second > row+1) {
Chris@72 105 rowRange.second = row+1; // don't cheat by looking at future :)
Chris@72 106 }
Chris@72 107 for (int index = rowRange.first; index < rowRange.second; index++) {
Chris@191 108 normpathcost_t tmp = m_m->getNormalisedPathCost(index, col);
cannam@0 109 if (tmp < min) {
cannam@0 110 min = tmp;
cannam@0 111 bestRow = index;
cannam@0 112 }
cannam@0 113 }
Chris@72 114
Chris@205 115 pair<int, int> colRange = m_m->getColRangeForRow(row);
Chris@72 116 if (colRange.second > col+1) {
Chris@72 117 colRange.second = col+1; // don't cheat by looking at future :)
Chris@72 118 }
Chris@72 119 for (int index = colRange.first; index < colRange.second; index++) {
Chris@191 120 normpathcost_t tmp = m_m->getNormalisedPathCost(row, index);
cannam@0 121 if (tmp < min) {
cannam@0 122 min = tmp;
cannam@0 123 bestCol = index;
cannam@0 124 bestRow = row;
cannam@0 125 }
cannam@0 126 }
Chris@147 127 }
Chris@72 128
Chris@181 129 advance_t
Chris@171 130 Finder::getExpandDirection()
Chris@171 131 {
Chris@171 132 return getExpandDirection(m_m->getFrameCount() - 1,
Chris@171 133 m_m->getOtherFrameCount() - 1);
Chris@171 134 }
Chris@171 135
Chris@181 136 advance_t
Chris@147 137 Finder::getExpandDirection(int row, int col)
Chris@147 138 {
Chris@147 139 // To determine which direction to expand the search area in, we
Chris@147 140 // look at the path costs along the leading edges of the search
Chris@147 141 // area (the final row and column within the area). We find the
Chris@147 142 // lowest path cost within the final row, and the lowest within
Chris@147 143 // the final column, and we compare them. If the row is cheaper
Chris@147 144 // then we expand by adding another row next to it; if the column
Chris@147 145 // is cheaper then we expand by adding another column next to
Chris@147 146 // it. (The overall lowest path cost across the row and column
Chris@147 147 // represents the best alignment we have within the entire search
Chris@147 148 // area given the data available and the assumption that the piece
Chris@147 149 // is not ending yet.)
Chris@147 150
Chris@147 151 int bestRow = row;
Chris@147 152 int bestCol = col;
Chris@215 153 normpathcost_t bestCost = INVALID_PATHCOST;
Chris@147 154
Chris@155 155 // cerr << "Finder " << this << "::getExpandDirection: ";
Chris@155 156
Chris@147 157 getBestEdgeCost(row, col, bestRow, bestCol, bestCost);
Chris@147 158
Chris@147 159 // cerr << "at [" << row << "," << col << "] (cost " << m_m->getPathCost(row, col) << ") blocksize = " << m_m->getBlockSize() << " best is [" << bestRow << "," << bestCol << "] (cost " << bestCost << ")" << endl;
Chris@135 160
Chris@45 161 if (bestRow == row) {
Chris@45 162 if (bestCol == col) {
Chris@181 163 return AdvanceBoth;
Chris@45 164 } else {
Chris@181 165 return AdvanceThis;
Chris@45 166 }
Chris@45 167 } else if (bestCol == col) {
Chris@181 168 return AdvanceOther;
Chris@45 169 } else {
Chris@181 170 return AdvanceNone;
Chris@45 171 }
Chris@73 172 }
cannam@0 173
cannam@0 174 void
cannam@0 175 Finder::recalculatePathCostMatrix(int r1, int c1, int r2, int c2)
cannam@0 176 {
Chris@72 177 int prevRowStart = 0, prevRowStop = 0;
Chris@72 178
Chris@72 179 for (int r = r1; r <= r2; r++) {
Chris@72 180
Chris@205 181 pair<int, int> colRange = m_m->getColRangeForRow(r);
Chris@72 182
Chris@72 183 int rowStart = max(c1, colRange.first);
Chris@72 184 int rowStop = min(c2 + 1, colRange.second);
Chris@72 185
Chris@72 186 for (int c = rowStart; c < rowStop; c++) {
Chris@72 187
Chris@181 188 advance_t dir = AdvanceNone;
Chris@188 189 pathcost_t straightIncrement = m_m->getDistance(r, c);
Chris@188 190 pathcost_t diagIncrement = pathcost_t(straightIncrement *
Chris@188 191 m_m->getDiagonalWeight());
Chris@72 192
Chris@72 193 if (r > r1) { // not first row
Chris@215 194 pathcost_t min = INVALID_PATHCOST;
Chris@72 195 if ((c > prevRowStart) && (c <= prevRowStop)) {
Chris@72 196 // diagonal from (r-1,c-1)
Chris@188 197 min = m_m->getPathCost(r-1, c-1) + diagIncrement;
Chris@181 198 dir = AdvanceBoth;
Chris@72 199 }
Chris@72 200 if ((c >= prevRowStart) && (c < prevRowStop)) {
Chris@72 201 // vertical from (r-1,c)
Chris@188 202 pathcost_t cost = m_m->getPathCost(r-1, c) + straightIncrement;
Chris@215 203 if ((min == INVALID_PATHCOST) || (cost < min)) {
Chris@72 204 min = cost;
Chris@181 205 dir = AdvanceThis;
Chris@72 206 }
Chris@72 207 }
Chris@72 208 if (c > rowStart) {
Chris@72 209 // horizontal from (r,c-1)
Chris@188 210 pathcost_t cost = m_m->getPathCost(r, c-1) + straightIncrement;
Chris@215 211 if ((min == INVALID_PATHCOST) || (cost < min)) {
Chris@72 212 min = cost;
Chris@181 213 dir = AdvanceOther;
Chris@72 214 }
Chris@72 215 }
Chris@72 216
Chris@72 217 m_m->setPathCost(r, c, dir, min);
Chris@72 218
Chris@72 219 } else if (c > rowStart) { // first row
Chris@72 220 // horizontal from (r,c-1)
Chris@181 221 m_m->setPathCost(r, c, AdvanceOther,
Chris@188 222 m_m->getPathCost(r, c-1) + straightIncrement);
Chris@72 223 }
Chris@72 224 }
Chris@72 225
Chris@72 226 prevRowStart = rowStart;
Chris@72 227 prevRowStop = rowStop;
cannam@0 228 }
Chris@72 229 }
Chris@30 230
Chris@82 231 #ifdef PERFORM_ERROR_CHECKS
Chris@81 232 Finder::ErrorPosition
Chris@81 233 Finder::checkPathCostMatrix()
Chris@81 234 {
Chris@81 235 ErrorPosition err;
Chris@81 236
Chris@81 237 int r1 = 0;
Chris@81 238 int c1 = 0;
Chris@81 239 int r2 = m_m->getFrameCount() - 1;
Chris@81 240 int c2 = m_m->getOtherFrameCount() - 1;
Chris@81 241
Chris@81 242 if (r2 < r1 || c2 < c1) {
Chris@81 243 return err;
Chris@81 244 }
Chris@81 245
Chris@81 246 int prevRowStart = 0, prevRowStop = 0;
Chris@81 247
Chris@81 248 for (int r = r1; r <= r2; r++) {
Chris@81 249
Chris@205 250 pair<int, int> colRange = m_m->getColRangeForRow(r);
Chris@81 251
Chris@81 252 int rowStart = max(c1, colRange.first);
Chris@81 253 int rowStop = min(c2 + 1, colRange.second);
Chris@81 254
Chris@81 255 for (int c = rowStart; c < rowStop; c++) {
Chris@81 256
Chris@188 257 advance_t dir = AdvanceNone;
Chris@212 258 pathcost_t updateTo = INVALID_PATHCOST;
Chris@188 259 distance_t distance = m_m->getDistance(r, c);
Chris@188 260 pathcost_t straightIncrement = distance;
Chris@188 261 pathcost_t diagIncrement = pathcost_t(distance * m_m->getDiagonalWeight());
Chris@188 262 err.distance = distance;
Chris@81 263
Chris@95 264 if (r > r1) { // not first row
Chris@215 265 pathcost_t min = INVALID_PATHCOST;
Chris@81 266 if ((c > prevRowStart) && (c <= prevRowStop)) {
Chris@81 267 // diagonal from (r-1,c-1)
Chris@188 268 min = m_m->getPathCost(r-1, c-1) + diagIncrement;
Chris@81 269 err.prevCost = m_m->getPathCost(r-1, c-1);
Chris@181 270 dir = AdvanceBoth;
Chris@81 271 }
Chris@81 272 if ((c >= prevRowStart) && (c < prevRowStop)) {
Chris@81 273 // vertical from (r-1,c)
Chris@188 274 pathcost_t cost = m_m->getPathCost(r-1, c) + straightIncrement;
Chris@215 275 if ((min == INVALID_PATHCOST) || (cost < min)) {
Chris@81 276 min = cost;
Chris@81 277 err.prevCost = m_m->getPathCost(r-1, c);
Chris@181 278 dir = AdvanceThis;
Chris@81 279 }
Chris@81 280 }
Chris@81 281 if (c > rowStart) {
Chris@81 282 // horizontal from (r,c-1)
Chris@188 283 pathcost_t cost = m_m->getPathCost(r, c-1) + straightIncrement;
Chris@215 284 if ((min == INVALID_PATHCOST) || (cost < min)) {
Chris@81 285 min = cost;
Chris@81 286 err.prevCost = m_m->getPathCost(r, c-1);
Chris@181 287 dir = AdvanceOther;
Chris@81 288 }
Chris@81 289 }
Chris@81 290
Chris@81 291 updateTo = min;
Chris@81 292
Chris@82 293 } else { // first row
Chris@82 294
Chris@82 295 if (c > rowStart) {
Chris@82 296 // horizontal from (r,c-1)
Chris@188 297 updateTo = m_m->getPathCost(r, c-1) + straightIncrement;
Chris@83 298 err.prevCost = m_m->getPathCost(r, c-1);
Chris@181 299 dir = AdvanceOther;
Chris@82 300 }
Chris@81 301 }
Chris@81 302
Chris@181 303 if (dir != AdvanceNone) {
Chris@86 304 if (m_m->getAdvance(r, c) != dir) {
Chris@86 305 err.type = ErrorPosition::WrongAdvance;
Chris@86 306 err.r = r;
Chris@86 307 err.c = c;
Chris@86 308 err.costWas = m_m->getPathCost(r, c);
Chris@86 309 err.costShouldBe = updateTo;
Chris@86 310 err.advanceWas = m_m->getAdvance(r, c);
Chris@86 311 err.advanceShouldBe = dir;
Chris@86 312 return err;
Chris@86 313 }
Chris@84 314 if (m_m->getPathCost(r, c) != updateTo) {
Chris@84 315 err.type = ErrorPosition::WrongCost;
Chris@84 316 err.r = r;
Chris@84 317 err.c = c;
Chris@84 318 err.costWas = m_m->getPathCost(r, c);
Chris@84 319 err.costShouldBe = updateTo;
Chris@84 320 err.advanceWas = m_m->getAdvance(r, c);
Chris@84 321 err.advanceShouldBe = dir;
Chris@82 322 return err;
Chris@82 323 }
Chris@82 324 } else {
Chris@82 325 // AdvanceNone should occur only at r = r1, c = c1
Chris@82 326 if (r != r1 || c != c1) {
Chris@82 327 err.type = ErrorPosition::NoAdvance;
Chris@82 328 err.r = r;
Chris@82 329 err.c = c;
Chris@82 330 err.costWas = m_m->getPathCost(r, c);
Chris@82 331 err.costShouldBe = updateTo;
Chris@84 332 err.advanceWas = m_m->getAdvance(r, c);
Chris@84 333 err.advanceShouldBe = dir;
Chris@82 334 return err;
Chris@82 335 }
Chris@81 336 }
Chris@81 337 }
Chris@81 338
Chris@81 339 prevRowStart = rowStart;
Chris@81 340 prevRowStop = rowStop;
Chris@81 341 }
Chris@81 342
Chris@81 343 return err;
Chris@82 344 }
Chris@81 345
Chris@92 346 void
Chris@92 347 Finder::checkAndReport()
Chris@30 348 {
Chris@92 349 cerr << "Finder: Checking path-cost matrix..." << endl;
Chris@82 350 ErrorPosition err = checkPathCostMatrix();
Chris@92 351 if (err.type == ErrorPosition::NoError) {
Chris@92 352 cerr << "No errors found" << endl;
Chris@92 353 } else {
Chris@82 354 cerr << "\nWARNING: Checking path-cost matrix returned mismatch:" << endl;
Chris@92 355 cerr << "Type: " << err.type << ": ";
Chris@92 356 switch (err.type) {
Chris@92 357 case ErrorPosition::NoError: break;
Chris@92 358 case ErrorPosition::WrongCost: cerr << "WrongCost"; break;
Chris@92 359 case ErrorPosition::WrongAdvance: cerr << "WrongAdvance"; break;
Chris@92 360 case ErrorPosition::NoAdvance: cerr << "NoAdvance"; break;
Chris@92 361 }
Chris@92 362 cerr << endl;
Chris@84 363 cerr << "At row " << err.r << ", column " << err.c
Chris@84 364 << "\nShould be advancing "
Chris@84 365 << Matcher::advanceToString(err.advanceShouldBe)
Chris@84 366 << ", advance in matrix is "
Chris@84 367 << Matcher::advanceToString(err.advanceWas)
Chris@83 368 << "\nPrev cost " << err.prevCost
Chris@191 369 << " plus distance " << distance_print_t(err.distance)
Chris@191 370 << " [perhaps diagonalised] gives "
Chris@84 371 << err.costShouldBe << ", matrix contains " << err.costWas
Chris@83 372 << endl;
Chris@83 373 cerr << "Note: diagonal weight = " << m_m->getDiagonalWeight() << endl;
Chris@83 374 cerr << endl;
Chris@92 375
Chris@95 376 int w(4);
Chris@95 377 int ww(15);
Chris@92 378
Chris@92 379 cerr << "Distance matrix leading up to this point:" << endl;
Chris@95 380 cerr << setprecision(12) << setw(w) << "";
Chris@92 381 for (int i = -4; i <= 0; ++i) {
Chris@95 382 cerr << setw(ww) << i;
Chris@92 383 }
Chris@92 384 cerr << endl;
Chris@92 385 for (int j = -4; j <= 0; ++j) {
Chris@92 386 cerr << setw(w) << j;
Chris@92 387 for (int i = -4; i <= 0; ++i) {
Chris@191 388 cerr << setw(ww)
Chris@191 389 << distance_print_t(m_m->getDistance(err.r + j, err.c + i));
Chris@92 390 }
Chris@92 391 cerr << endl;
Chris@92 392 }
Chris@92 393 cerr << endl;
Chris@92 394
Chris@92 395 cerr << "Cost matrix leading up to this point:" << endl;
Chris@92 396 cerr << setw(w) << "";
Chris@92 397 for (int i = -4; i <= 0; ++i) {
Chris@95 398 cerr << setw(ww) << i;
Chris@92 399 }
Chris@92 400 cerr << endl;
Chris@92 401 for (int j = -4; j <= 0; ++j) {
Chris@92 402 cerr << setw(w) << j;
Chris@92 403 for (int i = -4; i <= 0; ++i) {
Chris@95 404 cerr << setw(ww) << m_m->getPathCost(err.r + j, err.c + i);
Chris@92 405 }
Chris@92 406 cerr << endl;
Chris@92 407 }
Chris@92 408 cerr << endl;
Chris@82 409 }
Chris@92 410 }
Chris@92 411 #endif
Chris@92 412
Chris@182 413 pathcost_t
Chris@163 414 Finder::getOverallCost()
Chris@163 415 {
Chris@163 416 int ex = m_m->getOtherFrameCount() - 1;
Chris@163 417 int ey = m_m->getFrameCount() - 1;
Chris@163 418
Chris@163 419 if (ex < 0 || ey < 0) {
Chris@163 420 return 0;
Chris@163 421 }
Chris@163 422
Chris@165 423 return m_m->getPathCost(ey, ex);
Chris@163 424 }
Chris@163 425
Chris@92 426 int
Chris@92 427 Finder::retrievePath(bool smooth, vector<int> &pathx, vector<int> &pathy)
Chris@92 428 {
Chris@92 429 pathx.clear();
Chris@92 430 pathy.clear();
Chris@92 431
Chris@92 432 #ifdef PERFORM_ERROR_CHECKS
Chris@92 433 checkAndReport();
Chris@82 434 #endif
Chris@82 435
Chris@72 436 int ex = m_m->getOtherFrameCount() - 1;
Chris@72 437 int ey = m_m->getFrameCount() - 1;
Chris@69 438
Chris@69 439 if (ex < 0 || ey < 0) {
Chris@69 440 return 0;
Chris@69 441 }
Chris@66 442
Chris@66 443 int x = ex;
Chris@66 444 int y = ey;
Chris@30 445
Chris@140 446 #ifdef DEBUG_FINDER
Chris@140 447 cerr << "*** retrievePath: smooth = " << smooth << endl;
Chris@140 448 cerr << "*** retrievePath: before: x = " << x << ", y = " << y << endl;
Chris@140 449 #endif
Chris@140 450
Chris@72 451 if (m_duration2 > 0 && m_duration2 < m_m->getOtherFrameCount()) {
Chris@72 452 x = m_duration2 - 1;
Chris@60 453 }
Chris@72 454 if (m_duration1 > 0 && m_duration1 < m_m->getFrameCount()) {
Chris@72 455 y = m_duration1 - 1;
Chris@60 456 }
Chris@60 457
Chris@72 458 if (!m_m->isAvailable(y, x)) {
Chris@66 459 // Path did not pass through the expected end point --
Chris@66 460 // probably means the pieces are substantially different in
Chris@66 461 // the later bits. Reset the expected end point to the end of
Chris@66 462 // both files including any trailing silence.
Chris@66 463 cerr << "NOTE: Path did not pass through expected end point, inputs are probably significantly different" << endl;
Chris@66 464 x = ex;
Chris@66 465 y = ey;
Chris@66 466 }
Chris@66 467
Chris@55 468 recalculatePathCostMatrix(0, 0, y, x);
Chris@55 469
Chris@140 470 #ifdef DEBUG_FINDER
Chris@140 471 cerr << "*** retrievePath: start: x = " << x << ", y = " << y << endl;
Chris@140 472 #endif
Chris@66 473
Chris@72 474 while (m_m->isAvailable(y, x) && (x > 0 || y > 0)) {
Chris@30 475
Chris@33 476 // cerr << "x = " << x << ", y = " << y;
Chris@33 477
Chris@30 478 pathx.push_back(x);
Chris@30 479 pathy.push_back(y);
Chris@30 480
Chris@72 481 switch (m_m->getAdvance(y, x)) {
Chris@181 482 case AdvanceThis:
Chris@70 483 // cerr << ", going down (dist = " << getDistance() << ")" << endl;
Chris@33 484 y--;
Chris@33 485 break;
Chris@181 486 case AdvanceOther:
Chris@70 487 // cerr << ", going left (dist = " << getDistance() << ")" << endl;
Chris@33 488 x--;
Chris@33 489 break;
Chris@181 490 case AdvanceBoth:
Chris@70 491 // cerr << ", going diag (dist = " << getDistance() << ")" << endl;
Chris@33 492 x--;
Chris@33 493 y--;
Chris@33 494 break;
Chris@181 495 case AdvanceNone: // this would indicate a bug, but we wouldn't want to hang
Chris@69 496 cerr << "WARNING: Neither matcher advanced in path backtrack at (" << x << "," << y << ")" << endl;
Chris@33 497 if (x > y) {
Chris@33 498 x--;
Chris@33 499 } else {
Chris@33 500 y--;
Chris@33 501 }
Chris@33 502 break;
Chris@30 503 }
Chris@30 504 }
Chris@30 505
Chris@72 506 if (x > 0 || y > 0) {
Chris@72 507 cerr << "WARNING: Ran out of available path at (" << y << "," << x
Chris@72 508 << ")!" << endl;
Chris@72 509 }
Chris@72 510
Chris@72 511 reverse(pathx.begin(), pathx.end());
Chris@72 512 reverse(pathy.begin(), pathy.end());
Chris@30 513
Chris@31 514 if (smooth) {
Chris@180 515 int smoothedLen = Path().smooth
Chris@180 516 (pathx, pathy, static_cast<int>(pathx.size()));
Chris@31 517 return smoothedLen;
Chris@31 518 } else {
Chris@180 519 return static_cast<int>(pathx.size());
Chris@31 520 }
Chris@30 521 }
Chris@30 522