LCOV - code coverage report
Current view: top level - alg/marching_squares - square.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 243 246 98.8 %
Date: 2024-05-02 22:57:13 Functions: 33 35 94.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Marching square algorithm
       4             :  * Purpose:  Core algorithm implementation for contour line generation.
       5             :  * Author:   Oslandia <infos at oslandia dot com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2018, Oslandia <infos at oslandia dot com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : #ifndef MARCHING_SQUARE_SQUARE_H
      29             : #define MARCHING_SQUARE_SQUARE_H
      30             : 
      31             : #include <algorithm>
      32             : #include <cassert>
      33             : #include <cstdint>
      34             : #include <math.h>
      35             : #include "utility.h"
      36             : #include "point.h"
      37             : 
      38             : namespace marching_squares
      39             : {
      40             : 
      41             : struct Square
      42             : {
      43             :     // Bit flags to determine borders around pixel
      44             :     static const uint8_t NO_BORDER = 0;          // 0000 0000
      45             :     static const uint8_t LEFT_BORDER = 1 << 0;   // 0000 0001
      46             :     static const uint8_t LOWER_BORDER = 1 << 1;  // 0000 0010
      47             :     static const uint8_t RIGHT_BORDER = 1 << 2;  // 0000 0100
      48             :     static const uint8_t UPPER_BORDER = 1 << 3;  // 0000 1000
      49             : 
      50             :     // Bit flags for marching square case
      51             :     static const uint8_t ALL_LOW = 0;           // 0000 0000
      52             :     static const uint8_t UPPER_LEFT = 1 << 0;   // 0000 0001
      53             :     static const uint8_t LOWER_LEFT = 1 << 1;   // 0000 0010
      54             :     static const uint8_t LOWER_RIGHT = 1 << 2;  // 0000 0100
      55             :     static const uint8_t UPPER_RIGHT = 1 << 3;  // 0000 1000
      56             :     static const uint8_t ALL_HIGH =
      57             :         UPPER_LEFT | LOWER_LEFT | LOWER_RIGHT | UPPER_RIGHT;    // 0000 1111
      58             :     static const uint8_t SADDLE_NW = UPPER_LEFT | LOWER_RIGHT;  // 0000 0101
      59             :     static const uint8_t SADDLE_NE = UPPER_RIGHT | LOWER_LEFT;  // 0000 1010
      60             : 
      61             :     typedef std::pair<Point, Point> Segment;
      62             :     typedef std::pair<ValuedPoint, ValuedPoint> ValuedSegment;
      63             : 
      64             :     //
      65             :     // An array of segments, at most 3 segments
      66             :     struct Segments
      67             :     {
      68          24 :         Segments() : sz_(0)
      69             :         {
      70           6 :         }
      71             : 
      72      326280 :         Segments(const Segment &first) : sz_(1), segs_()
      73             :         {
      74       81570 :             segs_[0] = first;
      75       81570 :         }
      76             : 
      77        9436 :         Segments(const Segment &first, const Segment &second) : sz_(2), segs_()
      78             :         {
      79        2359 :             segs_[0] = first;
      80        2359 :             segs_[1] = second;
      81        2359 :         }
      82             : 
      83             :         Segments(const Segment &first, const Segment &second,
      84             :                  const Segment &third)
      85             :             : sz_(3), segs_()
      86             :         {
      87             :             segs_[0] = first;
      88             :             segs_[1] = second;
      89             :             segs_[2] = third;
      90             :         }
      91             : 
      92      170217 :         std::size_t size() const
      93             :         {
      94      170217 :             return sz_;
      95             :         }
      96             : 
      97       86294 :         const Segment &operator[](std::size_t idx) const
      98             :         {
      99       86294 :             assert(idx < sz_);
     100       86294 :             return segs_[idx];
     101             :         }
     102             : 
     103             :       private:
     104             :         const std::size_t sz_;
     105             :         /* const */ Segment segs_[3];
     106             :     };
     107             : 
     108      418365 :     Square(const ValuedPoint &upperLeft_, const ValuedPoint &upperRight_,
     109             :            const ValuedPoint &lowerLeft_, const ValuedPoint &lowerRight_,
     110             :            uint8_t borders_ = NO_BORDER, bool split_ = false)
     111      418365 :         : upperLeft(upperLeft_), lowerLeft(lowerLeft_), lowerRight(lowerRight_),
     112             :           upperRight(upperRight_),
     113      418365 :           nanCount((std::isnan(upperLeft.value) ? 1 : 0) +
     114      418365 :                    (std::isnan(upperRight.value) ? 1 : 0) +
     115      418365 :                    (std::isnan(lowerLeft.value) ? 1 : 0) +
     116      418365 :                    (std::isnan(lowerRight.value) ? 1 : 0)),
     117      418365 :           borders(borders_), split(split_)
     118             :     {
     119      418365 :         assert(upperLeft.y == upperRight.y);
     120      418365 :         assert(lowerLeft.y == lowerRight.y);
     121      418365 :         assert(lowerLeft.x == upperLeft.x);
     122      418365 :         assert(lowerRight.x == upperRight.x);
     123      418365 :         assert(!split || nanCount == 0);
     124      418365 :     }
     125             : 
     126        5234 :     Square upperLeftSquare() const
     127             :     {
     128        5234 :         assert(!std::isnan(upperLeft.value));
     129             :         return Square(
     130       10468 :             upperLeft, upperCenter(), leftCenter(), center(),
     131       10468 :             (std::isnan(upperRight.value) ? RIGHT_BORDER : NO_BORDER) |
     132        5234 :                 (std::isnan(lowerLeft.value) ? LOWER_BORDER : NO_BORDER),
     133       10468 :             true);
     134             :     }
     135             : 
     136        5236 :     Square lowerLeftSquare() const
     137             :     {
     138        5236 :         assert(!std::isnan(lowerLeft.value));
     139             :         return Square(
     140       10472 :             leftCenter(), center(), lowerLeft, lowerCenter(),
     141       10472 :             (std::isnan(lowerRight.value) ? RIGHT_BORDER : NO_BORDER) |
     142        5236 :                 (std::isnan(upperLeft.value) ? UPPER_BORDER : NO_BORDER),
     143       10472 :             true);
     144             :     }
     145             : 
     146        5241 :     Square lowerRightSquare() const
     147             :     {
     148        5241 :         assert(!std::isnan(lowerRight.value));
     149             :         return Square(
     150       10482 :             center(), rightCenter(), lowerCenter(), lowerRight,
     151       10482 :             (std::isnan(lowerLeft.value) ? LEFT_BORDER : NO_BORDER) |
     152        5241 :                 (std::isnan(upperRight.value) ? UPPER_BORDER : NO_BORDER),
     153       10482 :             true);
     154             :     }
     155             : 
     156        5231 :     Square upperRightSquare() const
     157             :     {
     158        5231 :         assert(!std::isnan(upperRight.value));
     159             :         return Square(
     160       10462 :             upperCenter(), upperRight, center(), rightCenter(),
     161       10462 :             (std::isnan(lowerRight.value) ? LOWER_BORDER : NO_BORDER) |
     162        5231 :                 (std::isnan(upperLeft.value) ? LEFT_BORDER : NO_BORDER),
     163       10462 :             true);
     164             :     }
     165             : 
     166      407802 :     double maxValue() const
     167             :     {
     168      407802 :         assert(nanCount == 0);
     169      407802 :         return std::max(std::max(upperLeft.value, upperRight.value),
     170      407802 :                         std::max(lowerLeft.value, lowerRight.value));
     171             :     }
     172             : 
     173      407802 :     double minValue() const
     174             :     {
     175      407802 :         assert(nanCount == 0);
     176      407802 :         return std::min(std::min(upperLeft.value, upperRight.value),
     177      407802 :                         std::min(lowerLeft.value, lowerRight.value));
     178             :     }
     179             : 
     180       10473 :     ValuedSegment segment(uint8_t border) const
     181             :     {
     182       10473 :         switch (border)
     183             :         {
     184        2617 :             case LEFT_BORDER:
     185        2617 :                 return ValuedSegment(upperLeft, lowerLeft);
     186        2620 :             case LOWER_BORDER:
     187        2620 :                 return ValuedSegment(lowerLeft, lowerRight);
     188        2615 :             case RIGHT_BORDER:
     189        2615 :                 return ValuedSegment(lowerRight, upperRight);
     190        2621 :             case UPPER_BORDER:
     191        2621 :                 return ValuedSegment(upperRight, upperLeft);
     192             :         }
     193           0 :         assert(false);
     194             :         return ValuedSegment(upperLeft, upperLeft);
     195             :     }
     196             : 
     197             :     // returns segments of contour
     198             :     //
     199             :     // segments are oriented:
     200             :     //   - they form a vector from their first point to their second point.
     201             :     //   - when looking at the vector upward, values greater than the level are
     202             :     //   on the right
     203             :     //
     204             :     //     ^
     205             :     //  -  |  +
     206       83935 :     Segments segments(double level) const
     207             :     {
     208       83935 :         switch (marchingCase(level))
     209             :         {
     210           1 :             case (ALL_LOW):
     211             :                 // debug("ALL_LOW");
     212           1 :                 return Segments();
     213           5 :             case (ALL_HIGH):
     214             :                 // debug("ALL_HIGH");
     215           5 :                 return Segments();
     216        7633 :             case (UPPER_LEFT):
     217             :                 // debug("UPPER_LEFT");
     218        7633 :                 return Segments(Segment(interpolate(UPPER_BORDER, level),
     219       22899 :                                         interpolate(LEFT_BORDER, level)));
     220        4970 :             case (LOWER_LEFT):
     221             :                 // debug("LOWER_LEFT");
     222        4970 :                 return Segments(Segment(interpolate(LEFT_BORDER, level),
     223       14910 :                                         interpolate(LOWER_BORDER, level)));
     224        3089 :             case (LOWER_RIGHT):
     225             :                 // debug("LOWER_RIGHT");
     226        3089 :                 return Segments(Segment(interpolate(LOWER_BORDER, level),
     227        9267 :                                         interpolate(RIGHT_BORDER, level)));
     228        3849 :             case (UPPER_RIGHT):
     229             :                 // debug("UPPER_RIGHT");
     230        3849 :                 return Segments(Segment(interpolate(RIGHT_BORDER, level),
     231       11547 :                                         interpolate(UPPER_BORDER, level)));
     232       20343 :             case (UPPER_LEFT | LOWER_LEFT):
     233             :                 // debug("UPPER_LEFT | LOWER_LEFT");
     234       20343 :                 return Segments(Segment(interpolate(UPPER_BORDER, level),
     235       61029 :                                         interpolate(LOWER_BORDER, level)));
     236        8790 :             case (LOWER_LEFT | LOWER_RIGHT):
     237             :                 // debug("LOWER_LEFT | LOWER_RIGHT");
     238        8790 :                 return Segments(Segment(interpolate(LEFT_BORDER, level),
     239       26370 :                                         interpolate(RIGHT_BORDER, level)));
     240        3706 :             case (LOWER_RIGHT | UPPER_RIGHT):
     241             :                 // debug("LOWER_RIGHT | UPPER_RIGHT");
     242        3706 :                 return Segments(Segment(interpolate(LOWER_BORDER, level),
     243       11118 :                                         interpolate(UPPER_BORDER, level)));
     244        9164 :             case (UPPER_RIGHT | UPPER_LEFT):
     245             :                 // debug("UPPER_RIGHT | UPPER_LEFT");
     246        9164 :                 return Segments(Segment(interpolate(RIGHT_BORDER, level),
     247       27492 :                                         interpolate(LEFT_BORDER, level)));
     248        3308 :             case (ALL_HIGH & ~UPPER_LEFT):
     249             :                 // debug("ALL_HIGH & ~UPPER_LEFT");
     250        3308 :                 return Segments(Segment(interpolate(LEFT_BORDER, level),
     251        9924 :                                         interpolate(UPPER_BORDER, level)));
     252        4073 :             case (ALL_HIGH & ~LOWER_LEFT):
     253             :                 // debug("ALL_HIGH & ~LOWER_LEFT");
     254        4073 :                 return Segments(Segment(interpolate(LOWER_BORDER, level),
     255       12219 :                                         interpolate(LEFT_BORDER, level)));
     256        7521 :             case (ALL_HIGH & ~LOWER_RIGHT):
     257             :                 // debug("ALL_HIGH & ~LOWER_RIGHT");
     258        7521 :                 return Segments(Segment(interpolate(RIGHT_BORDER, level),
     259       22563 :                                         interpolate(LOWER_BORDER, level)));
     260        5124 :             case (ALL_HIGH & ~UPPER_RIGHT):
     261             :                 // debug("ALL_HIGH & ~UPPER_RIGHT");
     262        5124 :                 return Segments(Segment(interpolate(UPPER_BORDER, level),
     263       15372 :                                         interpolate(RIGHT_BORDER, level)));
     264        2359 :             case (SADDLE_NE):
     265             :             case (SADDLE_NW):
     266             :                 // From the two possible saddle configurations, we always return
     267             :                 // the same one.
     268             :                 //
     269             :                 // The classical marching square algorithm says the ambiguity
     270             :                 // should be resolved between the two possible configurations by
     271             :                 // looking at the value of the center point. But in certain
     272             :                 // cases, this may lead to line contours from different levels
     273             :                 // that cross each other and then gives invalid polygons.
     274             :                 //
     275             :                 // Arbitrarily choosing one of the two possible configurations
     276             :                 // is not really that worse than deciding based on the center
     277             :                 // point.
     278        2359 :                 return Segments(Segment(interpolate(LEFT_BORDER, level),
     279        2359 :                                         interpolate(LOWER_BORDER, level)),
     280        2359 :                                 Segment(interpolate(RIGHT_BORDER, level),
     281        7077 :                                         interpolate(UPPER_BORDER, level)));
     282             :         }
     283           0 :         assert(false);
     284             :         return Segments();
     285             :     }
     286             : 
     287             :     template <typename Writer, typename LevelGenerator>
     288      418353 :     void process(const LevelGenerator &levelGenerator, Writer &writer) const
     289             :     {
     290      418353 :         if (nanCount == 4)  // nothing to do
     291       10547 :             return;
     292             : 
     293      418348 :         if (nanCount)  // split in 4
     294             :         {
     295       10546 :             if (!std::isnan(upperLeft.value))
     296        5231 :                 upperLeftSquare().process(levelGenerator, writer);
     297       10546 :             if (!std::isnan(upperRight.value))
     298        5231 :                 upperRightSquare().process(levelGenerator, writer);
     299       10546 :             if (!std::isnan(lowerLeft.value))
     300        5234 :                 lowerLeftSquare().process(levelGenerator, writer);
     301       10545 :             if (!std::isnan(lowerRight.value))
     302        5240 :                 lowerRightSquare().process(levelGenerator, writer);
     303       10542 :             return;
     304             :         }
     305             : 
     306      407802 :         if (writer.polygonize && borders)
     307             :         {
     308       51890 :             for (uint8_t border :
     309             :                  {UPPER_BORDER, LEFT_BORDER, RIGHT_BORDER, LOWER_BORDER})
     310             :             {
     311             :                 // bitwise AND to test which borders we have on the square
     312       41512 :                 if ((border & borders) == 0)
     313       31039 :                     continue;
     314             : 
     315             :                 // convention: for a level = L, store borders for the previous
     316             :                 // level up to (and including) L in the border of level "L". For
     317             :                 // fixed sets of level, this means there is an "Inf" slot for
     318             :                 // borders of the highest level
     319       10473 :                 const ValuedSegment s(segment(border));
     320             : 
     321       10473 :                 Point lastPoint(s.first.x, s.first.y);
     322       10473 :                 Point endPoint(s.second.x, s.second.y);
     323       10473 :                 if (s.first.value > s.second.value)
     324          34 :                     std::swap(lastPoint, endPoint);
     325       10473 :                 bool reverse =
     326       10497 :                     (s.first.value > s.second.value) &&
     327          24 :                     ((border == UPPER_BORDER) || (border == LEFT_BORDER));
     328             : 
     329       10473 :                 auto levelIt =
     330       10473 :                     levelGenerator.range(s.first.value, s.second.value);
     331             : 
     332       10473 :                 auto it = levelIt.begin();  // reused after the for
     333       10503 :                 for (; it != levelIt.end(); ++it)
     334             :                 {
     335          30 :                     const int levelIdx = (*it).first;
     336          30 :                     const double level = (*it).second;
     337             : 
     338          30 :                     const Point nextPoint(interpolate(border, level));
     339          30 :                     if (reverse)
     340           8 :                         writer.addBorderSegment(levelIdx, nextPoint, lastPoint);
     341             :                     else
     342          22 :                         writer.addBorderSegment(levelIdx, lastPoint, nextPoint);
     343          30 :                     lastPoint = nextPoint;
     344             :                 }
     345             :                 // last level (past the end)
     346       10473 :                 if (reverse)
     347          20 :                     writer.addBorderSegment((*it).first, endPoint, lastPoint);
     348             :                 else
     349       10453 :                     writer.addBorderSegment((*it).first, lastPoint, endPoint);
     350             :             }
     351             :         }
     352             : 
     353      407802 :         auto range = levelGenerator.range(minValue(), maxValue());
     354      407798 :         auto it = range.begin();
     355      407798 :         auto itEnd = range.end();
     356      407798 :         auto next = range.begin();
     357      407798 :         ++next;
     358             : 
     359      491721 :         for (; it != itEnd; ++it, ++next)
     360             :         {
     361       83923 :             const int levelIdx = (*it).first;
     362       83923 :             const double level = (*it).second;
     363             : 
     364       83923 :             const Segments segments_ = segments(level);
     365             : 
     366      170205 :             for (std::size_t i = 0; i < segments_.size(); i++)
     367             :             {
     368       86282 :                 const Segment &s = segments_[i];
     369       86282 :                 writer.addSegment(levelIdx, s.first, s.second);
     370             : 
     371       86282 :                 if (writer.polygonize)
     372             :                 {
     373             :                     // the contour is used in the polygon of higher level as
     374             :                     // well
     375             :                     //
     376             :                     // TODO: copying the segment to the higher level is easy,
     377             :                     // but it involves too much memory. We should reuse segment
     378             :                     // contours when constructing polygon rings.
     379        4463 :                     writer.addSegment((*next).first, s.first, s.second);
     380             :                 }
     381             :             }
     382             :         }
     383             :     }
     384             : 
     385             :     const ValuedPoint upperLeft;
     386             :     const ValuedPoint lowerLeft;
     387             :     const ValuedPoint lowerRight;
     388             :     const ValuedPoint upperRight;
     389             :     const int nanCount;
     390             :     const uint8_t borders;
     391             :     const bool split;
     392             : 
     393             :   private:
     394       20942 :     ValuedPoint center() const
     395             :     {
     396             :         return ValuedPoint(
     397       20942 :             .5 * (upperLeft.x + lowerRight.x),
     398       20942 :             .5 * (upperLeft.y + lowerRight.y),
     399       20942 :             ((std::isnan(lowerLeft.value) ? 0 : lowerLeft.value) +
     400       20942 :              (std::isnan(upperLeft.value) ? 0 : upperLeft.value) +
     401       20942 :              (std::isnan(lowerRight.value) ? 0 : lowerRight.value) +
     402       20942 :              (std::isnan(upperRight.value) ? 0 : upperRight.value)) /
     403       20942 :                 (4 - nanCount));
     404             :     }
     405             : 
     406       10470 :     ValuedPoint leftCenter() const
     407             :     {
     408             :         return ValuedPoint(
     409       10470 :             upperLeft.x, .5 * (upperLeft.y + lowerLeft.y),
     410       10470 :             std::isnan(upperLeft.value)
     411             :                 ? lowerLeft.value
     412        7832 :                 : (std::isnan(lowerLeft.value)
     413        7832 :                        ? upperLeft.value
     414       18302 :                        : .5 * (upperLeft.value + lowerLeft.value)));
     415             :     }
     416             : 
     417       10477 :     ValuedPoint lowerCenter() const
     418             :     {
     419             :         return ValuedPoint(
     420       10477 :             .5 * (lowerLeft.x + lowerRight.x), lowerLeft.y,
     421       10477 :             std::isnan(lowerRight.value)
     422             :                 ? lowerLeft.value
     423        7843 :                 : (std::isnan(lowerLeft.value)
     424        7843 :                        ? lowerRight.value
     425       18320 :                        : .5 * (lowerRight.value + lowerLeft.value)));
     426             :     }
     427             : 
     428       10472 :     ValuedPoint rightCenter() const
     429             :     {
     430             :         return ValuedPoint(
     431       10472 :             upperRight.x, .5 * (upperRight.y + lowerRight.y),
     432       10472 :             std::isnan(lowerRight.value)
     433             :                 ? upperRight.value
     434        7838 :                 : (std::isnan(upperRight.value)
     435        7838 :                        ? lowerRight.value
     436       18310 :                        : .5 * (lowerRight.value + upperRight.value)));
     437             :     }
     438             : 
     439       10465 :     ValuedPoint upperCenter() const
     440             :     {
     441             :         return ValuedPoint(
     442       10465 :             .5 * (upperLeft.x + upperRight.x), upperLeft.y,
     443       10465 :             std::isnan(upperLeft.value)
     444             :                 ? upperRight.value
     445        7831 :                 : (std::isnan(upperRight.value)
     446        7831 :                        ? upperLeft.value
     447       18296 :                        : .5 * (upperLeft.value + upperRight.value)));
     448             :     }
     449             : 
     450       83935 :     uint8_t marchingCase(double level) const
     451             :     {
     452      167870 :         return (level < fudge(level, upperLeft.value) ? UPPER_LEFT : ALL_LOW) |
     453      167870 :                (level < fudge(level, lowerLeft.value) ? LOWER_LEFT : ALL_LOW) |
     454       83935 :                (level < fudge(level, lowerRight.value) ? LOWER_RIGHT
     455       83935 :                                                        : ALL_LOW) |
     456      167870 :                (level < fudge(level, upperRight.value) ? UPPER_RIGHT : ALL_LOW);
     457             :     }
     458             : 
     459      172606 :     static double interpolate_(double level, double x1, double x2, double y1,
     460             :                                double y2, bool need_split)
     461             :     {
     462      172606 :         if (need_split)
     463             :         {
     464             :             // The two cases are here to avoid numerical roundup errors, for two
     465             :             // points, we always compute the same interpolation. This condition
     466             :             // is ensured by the order left->right bottom->top in interpole
     467             :             // calls
     468             :             //
     469             :             // To obtain the same value for border (split) and non-border
     470             :             // element, we take the middle value and interpolate from this to
     471             :             // the end
     472      140088 :             const double xm = .5 * (x1 + x2);
     473      140088 :             const double ym = .5 * (y1 + y2);
     474      140088 :             const double fy1 = fudge(level, y1);
     475      140088 :             const double fym = fudge(level, ym);
     476      140088 :             if ((fy1 < level && level < fym) || (fy1 > level && level > fym))
     477             :             {
     478       69847 :                 x2 = xm;
     479       69847 :                 y2 = ym;
     480             :             }
     481             :             else
     482             :             {
     483       70241 :                 x1 = xm;
     484       70241 :                 y1 = ym;
     485             :             }
     486             :         }
     487      172606 :         const double fy1 = fudge(level, y1);
     488      172606 :         const double ratio = (level - fy1) / (fudge(level, y2) - fy1);
     489      172606 :         return x1 * (1. - ratio) + x2 * ratio;
     490             :     }
     491             : 
     492      172606 :     Point interpolate(uint8_t border, double level) const
     493             :     {
     494      172606 :         switch (border)
     495             :         {
     496       40305 :             case LEFT_BORDER:
     497       40305 :                 return Point(upperLeft.x,
     498       40305 :                              interpolate_(level, lowerLeft.y, upperLeft.y,
     499       40305 :                                           lowerLeft.value, upperLeft.value,
     500       80610 :                                           !split));
     501       46071 :             case LOWER_BORDER:
     502       46071 :                 return Point(interpolate_(level, lowerLeft.x, lowerRight.x,
     503       46071 :                                           lowerLeft.value, lowerRight.value,
     504       46071 :                                           !split),
     505       92142 :                              lowerLeft.y);
     506       39900 :             case RIGHT_BORDER:
     507       39900 :                 return Point(upperRight.x,
     508       39900 :                              interpolate_(level, lowerRight.y, upperRight.y,
     509       39900 :                                           lowerRight.value, upperRight.value,
     510       79800 :                                           !split));
     511       46330 :             case UPPER_BORDER:
     512       46330 :                 return Point(interpolate_(level, upperLeft.x, upperRight.x,
     513       46330 :                                           upperLeft.value, upperRight.value,
     514       46330 :                                           !split),
     515       92660 :                              upperLeft.y);
     516             :         }
     517           0 :         assert(false);
     518             :         return Point();
     519             :     }
     520             : };
     521             : }  // namespace marching_squares
     522             : #endif

Generated by: LCOV version 1.14