| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080 |
- #ifndef ISOLINES_HEADER
- #define ISOLINES_HEADER
- /**
- * Transcription of FindIsolines.java for C++
- *
- * Fast implementation of marching squares
- *
- * AUTHOR: Murphy Stein, Greg Borenstein
- * New York University
- * CREATED: Jan-Sept 2012
- * MODIFIED: Dec 2014 (DGM)
- *
- * LICENSE: BSD
- *
- * Copyright (c) 2012 New York University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by New York Univserity. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- */
- /**
- * This is a fast implementation of the marching squares algorithm for finding isolines (lines of equal color) in an image.
- *
- */
- //qCC_db
- #include <ccLog.h>
- //system
- #include <assert.h>
- #include <cmath>
- #include <vector>
- template< typename T > class Isolines
- {
- protected:
- std::vector<double> m_minx;
- std::vector<double> m_miny;
- std::vector<double> m_maxx;
- std::vector<double> m_maxy;
- std::vector<int> m_cd;
- std::vector<double> m_contourX;
- std::vector<double> m_contourY;
- std::vector<int> m_contourLength;
- std::vector<int> m_contourOrigin;
- std::vector<int> m_contourIndexes;
- std::vector<bool> m_contourClosed;
- int m_w;
- int m_h;
- int m_numContours;
- T m_threshold;
- public:
- //! Default constructor
- Isolines(int w, int h)
- : m_w(std::max(0, w))
- , m_h(std::max(0, h))
- , m_numContours(0)
- , m_threshold(0)
- {
- //DGM: as this is done in the constructor,
- //we don't catch the exception (so that the
- //caller can properly handle the error!)
- //try
- {
- m_cd.resize(static_cast<size_t>(w)*static_cast<size_t>(h), 0);
- }
- //catch (const std::bad_alloc&)
- //{
- // //not enough memory!
- //}
- }
- //! Sets isoline value to trace
- inline void setThreshold(T t) { m_threshold = t; }
- //! Find isolines
- int find(const T* in)
- {
- //createOnePixelBorder(in, m_threshold + 1); //DGM: modifies 'in', the caller will have to do it itself :(
- preCodeImage(in);
- return findIsolines(in);
- }
- //! Returns the number of found contours
- inline int getNumContours() const { return m_numContours; }
- //! Returns the length of a given contour
- inline int getContourLength(int contour) const { return m_contourLength[contour]; }
- //! Returns whether a given contour is closed or not
- inline bool isContourClosed(int contour) const { return m_contourClosed[contour]; }
- //! Returns the given point (x,y) of a given contour
- void getContourPoint(int contour, size_t index, double& x, double& y) const
- {
- assert(static_cast<int>(index) < getContourLength(contour));
- x = getContourX(contour, index);
- y = getContourY(contour, index);
- }
- //! Measures the area delineated by a given contour
- inline double measureArea(int contour) const { return measureArea(contour, 0, getContourLength(contour)); }
- //! Measures the perimeter of a given contour
- inline double measurePerimeter(int contour) const { return measurePerimeter(contour, 0, getContourLength(contour)); }
- //! Creates a single pixel, 0-valued border around the grid
- void createOnePixelBorder(T* in, T borderval) const
- {
- //rows
- {
- int shift = (m_h - 1) * m_w;
- for (int i = 0; i < m_w; i++)
- {
- in[i] = in[i + shift] = borderval;
- }
- }
- //columns
- {
- for (int j = 0; j < m_h; j++)
- {
- in[j*m_w] = in[(j + 1)*m_w - 1] = borderval;
- }
- }
- }
- protected:
- //! Computes a code for each group of 4x4 cells
- /** The code depends only on whether each of four corners is
- above or below threshold.
- **/
- void preCodeImage(const T* in)
- {
- for (int x = 0; x < m_w - 1; x++)
- {
- for (int y = 0; y < m_h - 1; y++)
- {
- int b0(in[ixy(x + 0, y + 1)] < m_threshold ? 1 : 0); //bottom-left is below threshold
- int b1(in[ixy(x + 1, y + 1)] < m_threshold ? 2 : 0); //bottom-right is below threshold
- int b2(in[ixy(x + 1, y + 0)] < m_threshold ? 4 : 0); //top-right is below threshold
- int b3(in[ixy(x + 0, y + 0)] < m_threshold ? 8 : 0); //top-left is below threshold
- m_cd[ixy(x, y)] = b0 | b1 | b2 | b3;
- }
- }
- }
- //! 2x2 cell configuration codes
- enum ConfigurationCodes
- {
- CASE0 = 0,
- CASE1 = 1,
- CASE2 = 2,
- CASE3 = 3,
- CASE4 = 4,
- CASE5 = 5,
- CASE6 = 6,
- CASE7 = 7,
- CASE8 = 8,
- CASE9 = 9,
- CASE10 = 10,
- CASE11 = 11,
- CASE12 = 12,
- CASE13 = 13,
- CASE14 = 14,
- CASE15 = 15,
- VISITED = 16
- };
- //! Entry/exit edges
- enum Edges
- {
- NONE = -1,
- TOP = 0,
- RIGHT = 1,
- BOTTOM = 2,
- LEFT = 3
- };
- void endContour(bool closed, bool alternatePath)
- {
- if (alternatePath)
- {
- //we have to merge this path with the previous one!
- //try //will be taken care of by 'findIsolines'
- //{
- size_t length = m_contourLength.back();
- size_t firstIndex = m_contourOrigin.back();
- m_contourLength.pop_back();
- m_contourOrigin.pop_back();
- m_contourClosed.pop_back();
- //backup the alternate part of the contour
- std::vector<double> subContourX(length), subContourY(length);
- std::vector<int> subContourIndexes(length);
- {
- for (size_t i = 0; i < length; ++i)
- {
- subContourX[i] = m_contourX[firstIndex + i];
- subContourY[i] = m_contourY[firstIndex + i];
- subContourIndexes[i] = m_contourIndexes[firstIndex + i];
- }
- }
- assert(!m_contourLength.empty() && !m_contourOrigin.empty());
- size_t length0 = m_contourLength.back();
- size_t firstIndex0 = m_contourOrigin.back();
- //shift the first part values
- {
- for (int i = static_cast<int>(length0); i >= 0; --i) //we start by end so as to not overwrite values!
- {
- m_contourX[firstIndex0 + length + i] = m_contourX[firstIndex0 + i];
- m_contourX[firstIndex0 + length + i] = m_contourY[firstIndex0 + i];
- m_contourIndexes[firstIndex0 + length + i] = m_contourIndexes[firstIndex0 + i];
- }
- }
- //now copy the second part values
- {
- for (size_t i = 0; i < length; ++i)
- {
- m_contourX[firstIndex0 + i] = subContourX[i];
- m_contourY[firstIndex0 + i] = subContourY[i];
- m_contourIndexes[firstIndex0 + i] = subContourIndexes[i];
- }
- }
- //even though we are merging contours, if we are here it
- //means that the contour was not a proper loop!
- closed = false;
- assert(!m_contourClosed.empty());
- m_contourClosed.back() = false;
- //}
- //catch (const std::bad_alloc&)
- //{
- // return false;
- //}
- }
- //simply update the closed state (just to be sure)
- assert(!m_contourClosed.empty());
- assert(!closed || m_contourLength.back() > 2);
- m_contourClosed.back() = closed;
- //return true;
- }
- //! Searches image for contours from topleft to bottomright corners
- int findIsolines(const T* in)
- {
- //traversal case
- static const int TRAVERSAL[4] = { /*TOP=*/BOTTOM, /*RIGHT=*/LEFT, /*BOTTOM=*/TOP, /*LEFT=*/RIGHT };
- //disambiguation cases (CASES 5 and 10)
- static const int DISAMBIGUATION_UP [4] = { /*TOP=*/LEFT, /*RIGHT=*/BOTTOM, /*BOTTOM=*/RIGHT, /*LEFT=*/TOP };
- static const int DISAMBIGUATION_DOWN[4] = { /*TOP=*/RIGHT, /*RIGHT=*/TOP, /*BOTTOM=*/LEFT, /*LEFT=*/BOTTOM };
- m_contourX.clear();
- m_contourY.clear();
- m_contourLength.clear();
- m_contourOrigin.clear();
- m_contourIndexes.clear();
- m_contourClosed.clear();
- try
- {
- int toEdge = NONE;
- int cellIndex = -1;
- int x = 0, y = 0;
- //mechanism for merging two parts of a non-closed contour
- int altToEdge = NONE;
- int altStartIndex = 0;
- bool alternatePath = false;
- const int maxCellIndex = m_w * (m_h - 1); //DGM: last line is only 0!
- while (cellIndex < maxCellIndex)
- {
- //entry edge
- int fromEdge = (toEdge == NONE ? NONE : TRAVERSAL[toEdge]);
- //exit edge
- toEdge = NONE;
- //alternative exit edge (when starting a new contour)
- if (fromEdge == NONE)
- altToEdge = NONE;
- int currentCellIndex = -1;
- //last isoline is 'finished'
- if (fromEdge == NONE)
- {
- //do we have an alternate path?
- if (altToEdge != NONE && !alternatePath)
- {
- //we know that we are coming from the TOP (case 2 or 13)
- fromEdge = TOP;
- currentCellIndex = altStartIndex + m_w; //same coumn, next row
- alternatePath = true;
- //we start a new (temporary) contour
- m_contourLength.push_back(0);
- m_contourClosed.push_back(false);
- m_contourOrigin.push_back(static_cast<int>(m_contourX.size()) + 1);
- }
- else
- {
- // we have to look for a new starting point
- alternatePath = false;
- altToEdge = NONE;
- //skip empty cells
- while ( ++cellIndex < maxCellIndex
- && (m_cd[cellIndex] == CASE0 || m_cd[cellIndex] == CASE15) )
- {
- }
- if (cellIndex == maxCellIndex)
- break;
- currentCellIndex = cellIndex;
- }
- x = currentCellIndex % m_w;
- y = currentCellIndex / m_w;
- }
- //we have reached a border (bottom or right)
- if (x < 0 || x >= m_w - 1 || y < 0 || y >= m_h - 1)
- {
- //if an isoline was underway, it won't be closed!
- if (fromEdge != NONE)
- {
- endContour(false, alternatePath);
- }
- //toEdge = NONE;
- continue;
- }
- else if (fromEdge != NONE)
- {
- //isoline underway: we have to re-evaluate the
- //current position in the grid
- currentCellIndex = ixy(x, y);
- }
- assert(currentCellIndex >= 0);
- int& currentCode = m_cd[currentCellIndex];
- switch (currentCode)
- {
- case CASE0: // CASE 0
- //we have reached a border!
- //if an isoline was underway, it won't be closed!
- if (fromEdge != NONE)
- {
- endContour(false, alternatePath);
- }
- //toEdge = NONE;
- continue;
- case CASE1: // CASE 1
- case CASE14: // CASE 14
- toEdge = (fromEdge == NONE || fromEdge == LEFT ? BOTTOM : LEFT);
- currentCode |= VISITED;
- break;
- case CASE2: // CASE 2
- case CASE13: // CASE 13
- //if it's a new contour, we have 2 options (RIGHT and BOTTOM)
- if (fromEdge == NONE)
- {
- if (x >= m_w - 2) //can't go on the right!
- {
- if (y >= m_h - 2) //can't go lower
- {
- //toEdge = NONE;
- continue;
- }
- else
- {
- toEdge = BOTTOM;
- }
- }
- else
- {
- //go right by default
- toEdge = RIGHT;
- if (y < m_h - 2)
- {
- //if we can go lower, rembemr this as an alternate rout
- altToEdge = BOTTOM;
- altStartIndex = currentCellIndex;
- }
- }
- }
- else
- {
- toEdge = (fromEdge == BOTTOM ? RIGHT : BOTTOM);
- }
- currentCode |= VISITED;
- break;
- case CASE3: // CASE 3
- case CASE12: // CASE 12
- toEdge = (fromEdge == NONE || fromEdge == LEFT ? RIGHT : LEFT);
- currentCode |= VISITED;
- break;
- case CASE11: // CASE 11
- //assert(fromEdge != NONE);
- case CASE4: // CASE 4
- toEdge = (fromEdge == NONE || fromEdge == TOP ? RIGHT : TOP);
- currentCode |= VISITED;
- break;
- case CASE5: // CASE 5, saddle
- case CASE10: // CASE 10, saddle
- {
- if (fromEdge != NONE)
- {
- //check if we are not looping as the saddle points can't be flagged as VISITED!
- assert(!m_contourOrigin.empty() && !m_contourIndexes.empty());
- if (m_contourIndexes[m_contourOrigin.back()] == currentCellIndex)
- {
- //isoline loop is closed!
- assert(!alternatePath);
- endContour(true, false);
- //no need to look at the alternate path!
- altToEdge = NONE;
- //toEdge = NONE;
- continue;
- }
- }
- double avg = (in[ixy(x + 0, y + 0)]
- + in[ixy(x + 1, y + 0)]
- + in[ixy(x + 0, y + 1)]
- + in[ixy(x + 1, y + 1)]) / 4.0;
- //see http://en.wikipedia.org/wiki/Marching_squares for the disambiguation cases
- bool down = ((currentCode == CASE5 && avg < m_threshold)
- || (currentCode == CASE10 && avg >= m_threshold));
- if (down)
- {
- if (fromEdge == NONE)
- {
- //if we start here, we know that some routes have already been taken (the ones coming from UP or LEFT)
- //we can ignore the cell
- continue;
- }
- else
- {
- toEdge = DISAMBIGUATION_DOWN[fromEdge];
- }
- }
- else
- {
- if (fromEdge == NONE)
- {
- //if we start here, we know that some routes have already been taken (the ones coming from UP or LEFT)
- //it can only be on the right
- if (x < m_w - 2)
- {
- if (m_cd[currentCellIndex + 1] < VISITED)
- toEdge = RIGHT;
- else
- //we can ignore the cell
- continue;
- }
- else
- {
- //we can ignore the cell
- continue;
- }
- }
- else
- {
- toEdge = DISAMBIGUATION_UP[fromEdge];
- }
- }
- }
- break;
- case CASE6: // CASE 6
- //assert(fromEdge != NONE);
- case CASE9: // CASE 9
- toEdge = (fromEdge == NONE || fromEdge == TOP ? BOTTOM : TOP);
- currentCode |= VISITED;
- break;
- case CASE7: // CASE 7
- //assert(fromEdge != NONE);
- case CASE8: // CASE 8
- toEdge = (fromEdge == NONE || fromEdge == LEFT ? TOP : LEFT);
- currentCode |= VISITED;
- break;
- case CASE15: // CASE 15
- //assert(fromEdge == NONE); //apart at the very beginning!
- //toEdge = NONE;
- continue;
- default:
- assert(currentCode > 0 && ((currentCode & VISITED) == VISITED));
- if (fromEdge != NONE)
- {
- //check that we are indeed coming back to the start
- assert(!m_contourOrigin.empty() && !m_contourIndexes.empty());
- if (m_contourIndexes[m_contourOrigin.back()] == currentCellIndex)
- {
- //isoline loop is closed!
- assert(!alternatePath);
- endContour(true, false);
- //no need to look at the alternate path!
- altToEdge = NONE;
- }
- else
- {
- //have we reached a kind of tri-point? (DGM: not sure it's possible)
- endContour(false, alternatePath);
- }
- }
- //toEdge = NONE;
- continue;
- }
- assert(toEdge != NONE);
- if (fromEdge == NONE)
- {
- // starting a new contour
- m_contourLength.push_back(0);
- m_contourClosed.push_back(false);
- m_contourOrigin.push_back(static_cast<int>(m_contourX.size()));
- //ccLog::Print(QString("New contour: #%1 - origin = %2 - (x=%3, y=%4)").arg(m_contourLength.size()).arg(m_contourOrigin.back()).arg(x).arg(y));
- }
- double x2 = 0.0, y2 = 0.0;
- switch (toEdge)
- {
- case TOP:
- x2 = x + LERP(in[ixy(x + 0, y + 0)], in[ixy(x + 1, y + 0)]);
- y2 = y;
- y--;
- break;
- case RIGHT:
- x2 = x + 1;
- y2 = y + LERP(in[ixy(x + 1, y + 0)], in[ixy(x + 1, y + 1)]);
- x++;
- break;
- case BOTTOM:
- x2 = x + LERP(in[ixy(x + 0, y + 1)], in[ixy(x + 1, y + 1)]);
- y2 = y + 1;
- y++;
- break;
- case LEFT:
- x2 = x;
- y2 = y + LERP(in[ixy(x + 0, y + 0)], in[ixy(x + 0, y + 1)]);
- x--;
- break;
- default:
- assert(false);
- continue;
- }
- //if (m_contourLength.back() > 1)
- //{
- // size_t vertCount = m_contourX.size();
- // const double& x0 = m_contourX[vertCount - 2];
- // const double& y0 = m_contourY[vertCount - 2];
- // double& x1 = m_contourX.back();
- // double& y1 = m_contourY.back();
- // double ux = x1 - x0;
- // double uy = y1 - y0;
- // double vx = x2 - x0;
- // double vy = y2 - y0;
- // //test colinearity so as to merge both segments if possible
- // double dotprod = (ux*vx + uy*vy) / sqrt((vx*vx + vy*vy) * (ux*ux + uy*uy));
- // if (fabsl(dotprod - 1.0) < 1.0e-6)
- // {
- // //merge: we replace the last vertex by this one
- // x1 = x2;
- // y1 = y2;
- // m_contourIndexes.back() = currentCellIndex;
- // }
- // else
- // {
- // //new vertex
- // m_contourX.push_back(x2);
- // m_contourY.push_back(y2);
- // m_contourIndexes.push_back(currentCellIndex);
- // m_contourLength.back()++;
- // }
- //}
- //else
- {
- //new vertex
- m_contourX.push_back(x2);
- m_contourY.push_back(y2);
- m_contourIndexes.push_back(currentCellIndex);
- m_contourLength.back()++;
- }
- }
- }
- catch (const std::bad_alloc&)
- {
- //not enough memory
- m_contourX.clear();
- m_contourY.clear();
- m_contourLength.clear();
- m_contourIndexes.clear();
- return -1;
- }
- m_numContours = static_cast<int>(m_contourLength.size());
- computeBoundingBoxes();
- return m_numContours;
- }
- //! LERP between two values
- inline double LERP(T A, T B) const
- {
- T AB = A - B;
- return AB == 0 ? 0 : static_cast<double>(A - m_threshold) / AB;
- }
- inline int getLastIndex() const
- {
- int nc = getNumContours();
- return nc > 0 ? m_contourOrigin[nc - 1] + m_contourLength[nc - 1] : 0;
- }
- inline void setContourX(int contour, int v, double x)
- {
- int o = m_contourOrigin[contour];
- m_contourX[wrap(o + v, o, o + m_contourLength[contour])] = x;
- }
- inline void setContourY(int contour, int v, double y)
- {
- int o = m_contourOrigin[contour];
- m_contourY[wrap(o + v, o, o + m_contourLength[contour])] = y;
- }
- inline int getValidIndex(int contour, int v) const
- {
- int o = m_contourOrigin[contour];
- return wrap(o + v, o, o + m_contourLength[contour]);
- }
- double measureArea(int contour, int first, int last) const
- {
- double area = 0;
- if (getValidIndex(contour, first) == getValidIndex(contour, last))
- last = last - 1;
- double w = 0, h = 0;
- for (int i = first; i < last; i++)
- {
- w = getContourX(contour, i + 1) - getContourX(contour, i);
- h = (getContourY(contour, i + 1) + getContourY(contour, i)) / 2.0;
- area += w * h;
- }
- w = getContourX(contour, first) - getContourX(contour, last);
- h = (getContourY(contour, first) + getContourY(contour, last)) / 2.0;
- area += w * h;
- return area;
- }
- double measureMeanX(int contour) const
- {
- double mean = 0.0;
- int l = getContourLength(contour);
- for (int i = 0; i < l; i++)
- mean += getContourX(contour, i);
- return (l == 0 ? 0 : mean / l);
- }
- double measureMeanY(int contour) const
- {
- double mean = 0.0;
- int l = getContourLength(contour);
- for (int i = 0; i < l; i++)
- mean += getContourY(contour, i);
- return (l == 0 ? 0 : mean / l);
- }
- double measurePerimeter(int contour, int first, int last) const
- {
- if (getValidIndex(contour, first) == getValidIndex(contour, last))
- last = last - 1;
- double perim = 0;
- for (int i = first; i < last; i++)
- perim += measureLength(contour, i);
- perim += measureDistance(contour, first, last);
- return perim;
- }
- double measureNormalX(int contour, int i) const
- {
- double ret = getContourY(contour, i) - getContourY(contour, i + 1);
- ret = ret / measureLength(contour, i);
- return ret;
- }
- double measureNormalY(int contour, int i) const
- {
- double ret = getContourX(contour, i + 1) - getContourX(contour, i);
- ret = ret / measureLength(contour, i);
- return ret;
- }
- double measureNormalY(int contour, int first, int last) const
- {
- double ret = 0;
- for (int i = first; i < last; i++)
- ret += measureNormalY(contour, i);
- return ret;
- }
- double measureNormalX(int contour, int first, int last) const
- {
- double ret = 0;
- for (int i = first; i < last; i++)
- ret += measureNormalX(contour, i);
- return ret;
- }
- double measureAngleChange(int contour, int first, int last) const
- {
- double sum = 0;
- for (int i = first; i <= last; i++)
- sum += measureAngle(contour, i);
- return sum;
- }
- static int wrap(int i, int lo, int hi)
- {
- int l = hi - lo;
- int d = i - lo;
- int w = 0;
- if (d < 0)
- w = hi - ((-d) % l);
- else
- w = lo + (d % l);
- if (w == hi)
- w = lo;
- if (w < lo)
- {
- assert(false);
- printf("went below lo\n");
- }
- else if (w >= hi)
- {
- assert(false);
- printf("went above hi\n");
- }
- return w;
- }
- inline int ixy(int x, int y) const { return x + y * m_w; }
- inline double measureDistance(int contour, int first, int second) const
- {
- double dx = getContourX(contour, first) - getContourX(contour, second);
- double dy = getContourY(contour, first) - getContourY(contour, second);
- return std::sqrt(dx * dx + dy * dy);
- }
- // return length from i to i + 1
- double measureLength(int contour, int i) const
- {
- int lo = m_contourOrigin[contour];
- int n = m_contourLength[contour];
- int hi = lo + n;
- int v1 = wrap(lo + i + 0, lo, hi);
- int v2 = wrap(lo + i + 1, lo, hi);
- double aftx = m_contourX[v2] - m_contourX[v1];
- double afty = m_contourY[v2] - m_contourY[v1];
- return std::sqrt(aftx * aftx + afty * afty);
- }
- // return the relative angle change in radians
- // about the point i (assuming ccw is positive)
- double measureAngle(int contour, int i) const
- {
- double befx = getContourX(contour, i + 0) - getContourX(contour, i - 1);
- double befy = getContourY(contour, i + 0) - getContourY(contour, i - 1);
- double aftx = getContourX(contour, i + 1) - getContourX(contour, i + 0);
- double afty = getContourY(contour, i + 1) - getContourY(contour, i + 0);
- double befl = std::sqrt(befx * befx + befy * befy);
- befx /= befl;
- befy /= befl;
- double aftl = std::sqrt(aftx * aftx + afty * afty);
- aftx /= aftl;
- afty /= aftl;
- double dot = befx * aftx + befy * afty;
- if (dot > 1.0)
- dot = 1.0;
- else if (dot < 0)
- dot = 0;
- double rads = std::acos(dot);
- assert(rads == rads); //otherwise it means that rads is NaN!!!
- if (aftx * befy - afty * befx < 0)
- rads = -rads;
- return rads;
- }
- public:
- inline double getContourX(int contour, int v) const
- {
- int o = m_contourOrigin[contour];
- return m_contourX[wrap(o + v, o, o + m_contourLength[contour])];
- }
- inline double getContourY(int contour, int v) const
- {
- int o = m_contourOrigin[contour];
- return m_contourY[wrap(o + v, o, o + m_contourLength[contour])];
- }
- inline double measureCurvature(int contour, int i) const
- {
- return measureAngle(contour, i) / measureLength(contour, i);
- }
- void findAreas(int window, std::vector<double>& tips)
- {
- tips.resize(m_w * m_h);
- for (int k = 0; k < m_numContours; k++)
- {
- int l = getContourLength(k);
- for (int i = 0; i < l; i++)
- {
- int lo = i - window;
- int hi = i + window;
- tips[getValidIndex(k, i)] = measureArea(k, lo, hi);
- }
- }
- }
- void findRoundedCorners(int window, std::vector<double>& tips)
- {
- tips.resize(m_w * m_h);
- for (int k = 0; k < m_numContours; k++)
- {
- int l = getContourLength(k);
- for (int i = 0; i < l; i++)
- {
- int lo = i - window;
- int hi = i + window;
- tips[getValidIndex(k, i)] = measureArea(k, lo, hi) / measurePerimeter(k, lo, hi);
- }
- }
- }
- int getMaxContour() const
- {
- int maxlength = 0;
- int idx = 0;
- for (int k = 0; k < m_numContours; k++)
- {
- int l = getContourLength(k);
- if (l > maxlength)
- {
- maxlength = l;
- idx = k;
- }
- }
- return idx;
- }
- // POLYGON HIT TESTING ROUTINES
- bool computeBoundingBoxes()
- {
- int numContours = getNumContours();
- if (numContours == 0)
- {
- m_minx.clear();
- m_miny.clear();
- m_maxx.clear();
- m_maxy.clear();
- return true;
- }
- try
- {
- m_minx.resize(numContours);
- m_miny.resize(numContours);
- m_maxx.resize(numContours);
- m_maxy.resize(numContours);
- }
- catch (const std::bad_alloc&)
- {
- //not enough memory!
- return false;
- }
- for (int k = 0; k < numContours; k++)
- {
- int o = m_contourOrigin[k];
- m_minx[k] = m_contourX[o];
- m_miny[k] = m_contourY[o];
- m_maxx[k] = m_contourX[o];
- m_maxy[k] = m_contourY[o];
- for (int i = 1; i < getContourLength(k); i++)
- {
- int j = o + i;
- if (m_contourX[j] < m_minx[k])
- m_minx[k] = m_contourX[j];
- else if (m_contourX[j] > m_maxx[k])
- m_maxx[k] = m_contourX[j];
- if (m_contourY[j] < m_miny[k])
- m_miny[k] = m_contourY[j];
- else if (m_contourY[j] > m_maxy[k])
- m_maxy[k] = m_contourY[j];
- }
- }
- return true;
- }
- inline double getBBMinX(int contour) const { return m_minx[contour]; }
- inline double getBBMaxX(int contour) const { return m_maxx[contour]; }
- inline double getBBMinY(int contour) const { return m_miny[contour]; }
- inline double getBBMaxY(int contour) const { return m_maxy[contour]; }
- bool contains(int k, double x, double y) const
- {
- bool inside = false;
- int l = getContourLength(k);
- for (int i = 0, j = -1; i < l; j = i++)
- {
- double yi = getContourY(k, i);
- double yj = getContourY(k, j);
- if ((yj > y) != (yi > y))
- {
- double xi = getContourX(k, i);
- double xj = getContourX(k, j);
- if (yi == yj)
- {
- if (x < xi)
- inside = !inside;
- }
- else if (x < xi + (y - yi) * (xi - xj) / (yi - yj))
- {
- inside = !inside;
- }
- }
- }
- return inside;
- }
- bool containsContour(int k1, int k2)
- {
- if (!bbIntersect(k1, k2))
- return false;
- double minx = getBBMinX(k2);
- double maxx = getBBMaxX(k2);
- double miny = getBBMinY(k2);
- double maxy = getBBMaxY(k2);
- return ( contains(k1, minx, miny)
- && contains(k1, maxx, miny)
- && contains(k1, maxx, maxy)
- && contains(k1, minx, maxy) );
- }
- inline bool containsBoundingBox(int k, double minx, double miny, double maxx, double maxy) const
- {
- return ( contains(k, minx, miny)
- && contains(k, maxx, miny)
- && contains(k, maxx, maxy)
- && contains(k, minx, maxy) );
- }
- bool contains(const std::vector<double>& polyx, const std::vector<double>& polyy, double x, double y) const
- {
- bool inside = false;
- size_t l = polyx.size();
- if (l < 1)
- return false;
- for (size_t i = 0, j = l - 1; i < l; j = i++)
- {
- double yi = polyy[i];
- double yj = polyy[j];
- if ((yj > y) != (yi > y))
- {
- double xi = polyx[i];
- double xj = polyx[j];
- if (yi == yj)
- {
- if (x < xi)
- inside = !inside;
- }
- else if (x < xi + (y - yi) * (xi - xj) / (yi - yj))
- {
- inside = !inside;
- }
- }
- }
- return inside;
- }
- // intersects contour k1 with contour k2
- bool bbIntersect(int k1, int k2) const
- {
- double minx1 = getBBMinX(k1);
- double maxx1 = getBBMaxX(k1);
- double miny1 = getBBMinY(k1);
- double maxy1 = getBBMaxY(k1);
- double minx2 = getBBMinX(k2);
- double maxx2 = getBBMaxX(k2);
- double miny2 = getBBMinY(k2);
- double maxy2 = getBBMaxY(k2);
- double lt = minx1 > minx2 ? minx1 : minx2;
- double rt = maxx1 < maxx2 ? maxx1 : maxx2;
- double tp = miny1 > miny2 ? miny1 : miny2;
- double bt = maxy1 < maxy2 ? maxy1 : maxy2;
- return (lt < rt && tp < bt);
- }
- bool bbContainsBB(int k1, int k2) const
- {
- double minx1 = getBBMinX(k1);
- double maxx1 = getBBMaxX(k1);
- double miny1 = getBBMinY(k1);
- double maxy1 = getBBMaxY(k1);
- double minx2 = getBBMinX(k2);
- double maxx2 = getBBMaxX(k2);
- double miny2 = getBBMinY(k2);
- double maxy2 = getBBMaxY(k2);
- return (minx1 <= minx2 && maxx1 >= maxx2 && miny1 <= miny2 && maxy1 >= maxy2);
- }
- inline double bbArea(int k) const
- {
- double w = getBBMaxX(k) - getBBMinX(k);
- double h = getBBMaxY(k) - getBBMinY(k);
- return w * h;
- }
- inline double getBBCenterX(int k) const { return (getBBMinX(k) + getBBMaxX(k)) / 2.0; }
- inline double getBBCenterY(int k) const { return (getBBMinY(k) + getBBMaxY(k)) / 2.0; }
- };
- #endif //ISOLINES_HEADER
|