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: 2025-12-13 23:48:27 Functions: 32 32 100.0 %

          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             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : #ifndef MARCHING_SQUARE_SQUARE_H
      13             : #define MARCHING_SQUARE_SQUARE_H
      14             : 
      15             : #include <algorithm>
      16             : #include <cassert>
      17             : #include <cmath>
      18             : #include <cstdint>
      19             : #include <math.h>
      20             : #include "utility.h"
      21             : #include "point.h"
      22             : 
      23             : namespace marching_squares
      24             : {
      25             : 
      26             : struct Square
      27             : {
      28             :     // Bit flags to determine borders around pixel
      29             :     static const uint8_t NO_BORDER = 0;          // 0000 0000
      30             :     static const uint8_t LEFT_BORDER = 1 << 0;   // 0000 0001
      31             :     static const uint8_t LOWER_BORDER = 1 << 1;  // 0000 0010
      32             :     static const uint8_t RIGHT_BORDER = 1 << 2;  // 0000 0100
      33             :     static const uint8_t UPPER_BORDER = 1 << 3;  // 0000 1000
      34             : 
      35             :     // Bit flags for marching square case
      36             :     static const uint8_t ALL_LOW = 0;           // 0000 0000
      37             :     static const uint8_t UPPER_LEFT = 1 << 0;   // 0000 0001
      38             :     static const uint8_t LOWER_LEFT = 1 << 1;   // 0000 0010
      39             :     static const uint8_t LOWER_RIGHT = 1 << 2;  // 0000 0100
      40             :     static const uint8_t UPPER_RIGHT = 1 << 3;  // 0000 1000
      41             :     static const uint8_t ALL_HIGH =
      42             :         UPPER_LEFT | LOWER_LEFT | LOWER_RIGHT | UPPER_RIGHT;    // 0000 1111
      43             :     static const uint8_t SADDLE_NW = UPPER_LEFT | LOWER_RIGHT;  // 0000 0101
      44             :     static const uint8_t SADDLE_NE = UPPER_RIGHT | LOWER_LEFT;  // 0000 1010
      45             : 
      46             :     typedef std::pair<Point, Point> Segment;
      47             :     typedef std::pair<ValuedPoint, ValuedPoint> ValuedSegment;
      48             : 
      49             :     //
      50             :     // An array of segments, at most 3 segments
      51             :     struct Segments
      52             :     {
      53          24 :         Segments() : sz_(0)
      54             :         {
      55           6 :         }
      56             : 
      57      347244 :         Segments(const Segment &first) : sz_(1), segs_()
      58             :         {
      59       86811 :             segs_[0] = first;
      60       86811 :         }
      61             : 
      62       11196 :         Segments(const Segment &first, const Segment &second) : sz_(2), segs_()
      63             :         {
      64        2799 :             segs_[0] = first;
      65        2799 :             segs_[1] = second;
      66        2799 :         }
      67             : 
      68             :         Segments(const Segment &first, const Segment &second,
      69             :                  const Segment &third)
      70             :             : sz_(3), segs_()
      71             :         {
      72             :             segs_[0] = first;
      73             :             segs_[1] = second;
      74             :             segs_[2] = third;
      75             :         }
      76             : 
      77      182019 :         std::size_t size() const
      78             :         {
      79      182019 :             return sz_;
      80             :         }
      81             : 
      82       92415 :         const Segment &operator[](std::size_t idx) const
      83             :         {
      84       92415 :             assert(idx < sz_);
      85       92415 :             return segs_[idx];
      86             :         }
      87             : 
      88             :       private:
      89             :         const std::size_t sz_;
      90             :         /* const */ Segment segs_[3];
      91             :     };
      92             : 
      93      825860 :     Square(const ValuedPoint &upperLeft_, const ValuedPoint &upperRight_,
      94             :            const ValuedPoint &lowerLeft_, const ValuedPoint &lowerRight_,
      95             :            uint8_t borders_ = NO_BORDER, bool split_ = false)
      96      825860 :         : upperLeft(upperLeft_), lowerLeft(lowerLeft_), lowerRight(lowerRight_),
      97             :           upperRight(upperRight_),
      98      825860 :           nanCount((std::isnan(upperLeft.value) ? 1 : 0) +
      99      825860 :                    (std::isnan(upperRight.value) ? 1 : 0) +
     100      825860 :                    (std::isnan(lowerLeft.value) ? 1 : 0) +
     101      825860 :                    (std::isnan(lowerRight.value) ? 1 : 0)),
     102      825860 :           borders(borders_), split(split_)
     103             :     {
     104      825860 :         assert(upperLeft.y == upperRight.y);
     105      825860 :         assert(lowerLeft.y == lowerRight.y);
     106      825860 :         assert(lowerLeft.x == upperLeft.x);
     107      825860 :         assert(lowerRight.x == upperRight.x);
     108      825860 :         assert(!split || nanCount == 0);
     109      825860 :     }
     110             : 
     111       10446 :     Square upperLeftSquare() const
     112             :     {
     113       10446 :         assert(!std::isnan(upperLeft.value));
     114             :         return Square(
     115       20892 :             upperLeft, upperCenter(), leftCenter(), center(),
     116       20892 :             (std::isnan(upperRight.value) ? RIGHT_BORDER : NO_BORDER) |
     117       10446 :                 (std::isnan(lowerLeft.value) ? LOWER_BORDER : NO_BORDER),
     118       20892 :             true);
     119             :     }
     120             : 
     121       10445 :     Square lowerLeftSquare() const
     122             :     {
     123       10445 :         assert(!std::isnan(lowerLeft.value));
     124             :         return Square(
     125       20890 :             leftCenter(), center(), lowerLeft, lowerCenter(),
     126       20890 :             (std::isnan(lowerRight.value) ? RIGHT_BORDER : NO_BORDER) |
     127       10445 :                 (std::isnan(upperLeft.value) ? UPPER_BORDER : NO_BORDER),
     128       20890 :             true);
     129             :     }
     130             : 
     131       10447 :     Square lowerRightSquare() const
     132             :     {
     133       10447 :         assert(!std::isnan(lowerRight.value));
     134             :         return Square(
     135       20894 :             center(), rightCenter(), lowerCenter(), lowerRight,
     136       20894 :             (std::isnan(lowerLeft.value) ? LEFT_BORDER : NO_BORDER) |
     137       10447 :                 (std::isnan(upperRight.value) ? UPPER_BORDER : NO_BORDER),
     138       20894 :             true);
     139             :     }
     140             : 
     141       10443 :     Square upperRightSquare() const
     142             :     {
     143       10443 :         assert(!std::isnan(upperRight.value));
     144             :         return Square(
     145       20886 :             upperCenter(), upperRight, center(), rightCenter(),
     146       20886 :             (std::isnan(lowerRight.value) ? LOWER_BORDER : NO_BORDER) |
     147       10443 :                 (std::isnan(upperLeft.value) ? LEFT_BORDER : NO_BORDER),
     148       20886 :             true);
     149             :     }
     150             : 
     151      804788 :     double maxValue() const
     152             :     {
     153      804788 :         assert(nanCount == 0);
     154      804788 :         return std::max(std::max(upperLeft.value, upperRight.value),
     155      804788 :                         std::max(lowerLeft.value, lowerRight.value));
     156             :     }
     157             : 
     158      804788 :     double minValue() const
     159             :     {
     160      804788 :         assert(nanCount == 0);
     161      804788 :         return std::min(std::min(upperLeft.value, upperRight.value),
     162      804788 :                         std::min(lowerLeft.value, lowerRight.value));
     163             :     }
     164             : 
     165       26009 :     ValuedSegment segment(uint8_t border) const
     166             :     {
     167       26009 :         switch (border)
     168             :         {
     169        6501 :             case LEFT_BORDER:
     170        6501 :                 return ValuedSegment(upperLeft, lowerLeft);
     171        6504 :             case LOWER_BORDER:
     172        6504 :                 return ValuedSegment(lowerLeft, lowerRight);
     173        6499 :             case RIGHT_BORDER:
     174        6499 :                 return ValuedSegment(lowerRight, upperRight);
     175        6505 :             case UPPER_BORDER:
     176        6505 :                 return ValuedSegment(upperRight, upperLeft);
     177             :         }
     178           0 :         assert(false);
     179             :         return ValuedSegment(upperLeft, upperLeft);
     180             :     }
     181             : 
     182             :     // returns segments of contour
     183             :     //
     184             :     // segments are oriented:
     185             :     //   - they form a vector from their first point to their second point.
     186             :     //   - when looking at the vector upward, values greater than the level are
     187             :     //   on the right
     188             :     //
     189             :     //     ^
     190             :     //  -  |  +
     191       89616 :     Segments segments(double level) const
     192             :     {
     193       89616 :         switch (marchingCase(level))
     194             :         {
     195           1 :             case (ALL_LOW):
     196             :                 // debug("ALL_LOW");
     197           1 :                 return Segments();
     198           5 :             case (ALL_HIGH):
     199             :                 // debug("ALL_HIGH");
     200           5 :                 return Segments();
     201        9265 :             case (UPPER_LEFT):
     202             :                 // debug("UPPER_LEFT");
     203        9265 :                 return Segments(Segment(interpolate(UPPER_BORDER, level),
     204       27795 :                                         interpolate(LEFT_BORDER, level)));
     205        5808 :             case (LOWER_LEFT):
     206             :                 // debug("LOWER_LEFT");
     207        5808 :                 return Segments(Segment(interpolate(LEFT_BORDER, level),
     208       17424 :                                         interpolate(LOWER_BORDER, level)));
     209        3758 :             case (LOWER_RIGHT):
     210             :                 // debug("LOWER_RIGHT");
     211        3758 :                 return Segments(Segment(interpolate(LOWER_BORDER, level),
     212       11274 :                                         interpolate(RIGHT_BORDER, level)));
     213        4405 :             case (UPPER_RIGHT):
     214             :                 // debug("UPPER_RIGHT");
     215        4405 :                 return Segments(Segment(interpolate(RIGHT_BORDER, level),
     216       13215 :                                         interpolate(UPPER_BORDER, level)));
     217        8975 :             case (UPPER_LEFT | LOWER_LEFT):
     218             :                 // debug("UPPER_LEFT | LOWER_LEFT");
     219        8975 :                 return Segments(Segment(interpolate(UPPER_BORDER, level),
     220       26925 :                                         interpolate(LOWER_BORDER, level)));
     221       12243 :             case (LOWER_LEFT | LOWER_RIGHT):
     222             :                 // debug("LOWER_LEFT | LOWER_RIGHT");
     223       12243 :                 return Segments(Segment(interpolate(LEFT_BORDER, level),
     224       36729 :                                         interpolate(RIGHT_BORDER, level)));
     225        5840 :             case (LOWER_RIGHT | UPPER_RIGHT):
     226             :                 // debug("LOWER_RIGHT | UPPER_RIGHT");
     227        5840 :                 return Segments(Segment(interpolate(LOWER_BORDER, level),
     228       17520 :                                         interpolate(UPPER_BORDER, level)));
     229       12322 :             case (UPPER_RIGHT | UPPER_LEFT):
     230             :                 // debug("UPPER_RIGHT | UPPER_LEFT");
     231       12322 :                 return Segments(Segment(interpolate(RIGHT_BORDER, level),
     232       36966 :                                         interpolate(LEFT_BORDER, level)));
     233        4124 :             case (ALL_HIGH & ~UPPER_LEFT):
     234             :                 // debug("ALL_HIGH & ~UPPER_LEFT");
     235        4124 :                 return Segments(Segment(interpolate(LEFT_BORDER, level),
     236       12372 :                                         interpolate(UPPER_BORDER, level)));
     237        4721 :             case (ALL_HIGH & ~LOWER_LEFT):
     238             :                 // debug("ALL_HIGH & ~LOWER_LEFT");
     239        4721 :                 return Segments(Segment(interpolate(LOWER_BORDER, level),
     240       14163 :                                         interpolate(LEFT_BORDER, level)));
     241        9287 :             case (ALL_HIGH & ~LOWER_RIGHT):
     242             :                 // debug("ALL_HIGH & ~LOWER_RIGHT");
     243        9287 :                 return Segments(Segment(interpolate(RIGHT_BORDER, level),
     244       27861 :                                         interpolate(LOWER_BORDER, level)));
     245        6063 :             case (ALL_HIGH & ~UPPER_RIGHT):
     246             :                 // debug("ALL_HIGH & ~UPPER_RIGHT");
     247        6063 :                 return Segments(Segment(interpolate(UPPER_BORDER, level),
     248       18189 :                                         interpolate(RIGHT_BORDER, level)));
     249        2799 :             case (SADDLE_NE):
     250             :             case (SADDLE_NW):
     251             :                 // From the two possible saddle configurations, we always return
     252             :                 // the same one.
     253             :                 //
     254             :                 // The classical marching square algorithm says the ambiguity
     255             :                 // should be resolved between the two possible configurations by
     256             :                 // looking at the value of the center point. But in certain
     257             :                 // cases, this may lead to line contours from different levels
     258             :                 // that cross each other and then gives invalid polygons.
     259             :                 //
     260             :                 // Arbitrarily choosing one of the two possible configurations
     261             :                 // is not really that worse than deciding based on the center
     262             :                 // point.
     263        2799 :                 return Segments(Segment(interpolate(LEFT_BORDER, level),
     264        2799 :                                         interpolate(LOWER_BORDER, level)),
     265        2799 :                                 Segment(interpolate(RIGHT_BORDER, level),
     266        8397 :                                         interpolate(UPPER_BORDER, level)));
     267             :         }
     268           0 :         assert(false);
     269             :         return Segments();
     270             :     }
     271             : 
     272             :     template <typename Writer, typename LevelGenerator>
     273      825848 :     void process(const LevelGenerator &levelGenerator, Writer &writer) const
     274             :     {
     275      825848 :         if (nanCount == 4)  // nothing to do
     276       21060 :             return;
     277             : 
     278      825847 :         if (nanCount)  // split in 4
     279             :         {
     280       21059 :             if (!std::isnan(upperLeft.value))
     281       10443 :                 upperLeftSquare().process(levelGenerator, writer);
     282       21059 :             if (!std::isnan(upperRight.value))
     283       10443 :                 upperRightSquare().process(levelGenerator, writer);
     284       21059 :             if (!std::isnan(lowerLeft.value))
     285       10443 :                 lowerLeftSquare().process(levelGenerator, writer);
     286       21059 :             if (!std::isnan(lowerRight.value))
     287       10446 :                 lowerRightSquare().process(levelGenerator, writer);
     288       21059 :             return;
     289             :         }
     290             : 
     291      804788 :         if (writer.polygonize && borders)
     292             :         {
     293      129130 :             for (uint8_t border :
     294             :                  {UPPER_BORDER, LEFT_BORDER, RIGHT_BORDER, LOWER_BORDER})
     295             :             {
     296             :                 // bitwise AND to test which borders we have on the square
     297      103304 :                 if ((border & borders) == 0)
     298       77295 :                     continue;
     299             : 
     300             :                 // convention: for a level = L, store borders for the previous
     301             :                 // level up to (and including) L in the border of level "L". For
     302             :                 // fixed sets of level, this means there is an "Inf" slot for
     303             :                 // borders of the highest level
     304       26009 :                 const ValuedSegment s(segment(border));
     305             : 
     306       26009 :                 Point lastPoint(s.first.x, s.first.y);
     307       26009 :                 Point endPoint(s.second.x, s.second.y);
     308       26009 :                 if (s.first.value > s.second.value)
     309        1622 :                     std::swap(lastPoint, endPoint);
     310       26009 :                 bool reverse =
     311       27171 :                     (s.first.value > s.second.value) &&
     312        1162 :                     ((border == UPPER_BORDER) || (border == LEFT_BORDER));
     313             : 
     314       26009 :                 auto levelIt =
     315       26009 :                     levelGenerator.range(s.first.value, s.second.value);
     316             : 
     317       26009 :                 auto it = levelIt.begin();  // reused after the for
     318       26173 :                 for (; it != levelIt.end(); ++it)
     319             :                 {
     320         164 :                     const int levelIdx = (*it).first;
     321         164 :                     const double level = (*it).second;
     322             : 
     323         164 :                     const Point nextPoint(interpolate(border, level));
     324         164 :                     if (reverse)
     325          42 :                         writer.addBorderSegment(levelIdx, nextPoint, lastPoint);
     326             :                     else
     327         122 :                         writer.addBorderSegment(levelIdx, lastPoint, nextPoint);
     328         164 :                     lastPoint = nextPoint;
     329             :                 }
     330             :                 // last level (past the end)
     331       26009 :                 if (reverse)
     332        1016 :                     writer.addBorderSegment((*it).first, endPoint, lastPoint);
     333             :                 else
     334       24993 :                     writer.addBorderSegment((*it).first, lastPoint, endPoint);
     335             :             }
     336             :         }
     337             : 
     338      804788 :         auto range = levelGenerator.range(minValue(), maxValue());
     339      804788 :         auto it = range.begin();
     340      804788 :         auto itEnd = range.end();
     341      804788 :         auto next = range.begin();
     342      804788 :         ++next;
     343             : 
     344      894392 :         for (; it != itEnd; ++it, ++next)
     345             :         {
     346       89604 :             const int levelIdx = (*it).first;
     347       89604 :             const double level = (*it).second;
     348             : 
     349       89604 :             const Segments segments_ = segments(level);
     350             : 
     351      182007 :             for (std::size_t i = 0; i < segments_.size(); i++)
     352             :             {
     353       92403 :                 const Segment &s = segments_[i];
     354       92403 :                 writer.addSegment(levelIdx, s.first, s.second);
     355             : 
     356       92403 :                 if (writer.polygonize)
     357             :                 {
     358             :                     // the contour is used in the polygon of higher level as
     359             :                     // well
     360             :                     //
     361             :                     // TODO: copying the segment to the higher level is easy,
     362             :                     // but it involves too much memory. We should reuse segment
     363             :                     // contours when constructing polygon rings.
     364       16124 :                     writer.addSegment((*next).first, s.first, s.second);
     365             :                 }
     366             :             }
     367             :         }
     368             :     }
     369             : 
     370             :     const ValuedPoint upperLeft;
     371             :     const ValuedPoint lowerLeft;
     372             :     const ValuedPoint lowerRight;
     373             :     const ValuedPoint upperRight;
     374             :     const int nanCount;
     375             :     const uint8_t borders;
     376             :     const bool split;
     377             : 
     378             :   private:
     379       41781 :     ValuedPoint center() const
     380             :     {
     381             :         return ValuedPoint(
     382       41781 :             .5 * (upperLeft.x + lowerRight.x),
     383       41781 :             .5 * (upperLeft.y + lowerRight.y),
     384       41781 :             ((std::isnan(lowerLeft.value) ? 0 : lowerLeft.value) +
     385       41781 :              (std::isnan(upperLeft.value) ? 0 : upperLeft.value) +
     386       41781 :              (std::isnan(lowerRight.value) ? 0 : lowerRight.value) +
     387       41781 :              (std::isnan(upperRight.value) ? 0 : upperRight.value)) /
     388       41781 :                 (4 - nanCount));
     389             :     }
     390             : 
     391       20891 :     ValuedPoint leftCenter() const
     392             :     {
     393             :         return ValuedPoint(
     394       20891 :             upperLeft.x, .5 * (upperLeft.y + lowerLeft.y),
     395       20891 :             std::isnan(upperLeft.value)
     396             :                 ? lowerLeft.value
     397       15626 :                 : (std::isnan(lowerLeft.value)
     398       15626 :                        ? upperLeft.value
     399       36517 :                        : .5 * (upperLeft.value + lowerLeft.value)));
     400             :     }
     401             : 
     402       20892 :     ValuedPoint lowerCenter() const
     403             :     {
     404             :         return ValuedPoint(
     405       20892 :             .5 * (lowerLeft.x + lowerRight.x), lowerLeft.y,
     406       20892 :             std::isnan(lowerRight.value)
     407             :                 ? lowerLeft.value
     408       15628 :                 : (std::isnan(lowerLeft.value)
     409       15628 :                        ? lowerRight.value
     410       36520 :                        : .5 * (lowerRight.value + lowerLeft.value)));
     411             :     }
     412             : 
     413       20890 :     ValuedPoint rightCenter() const
     414             :     {
     415             :         return ValuedPoint(
     416       20890 :             upperRight.x, .5 * (upperRight.y + lowerRight.y),
     417       20890 :             std::isnan(lowerRight.value)
     418             :                 ? upperRight.value
     419       15626 :                 : (std::isnan(upperRight.value)
     420       15626 :                        ? lowerRight.value
     421       36516 :                        : .5 * (lowerRight.value + upperRight.value)));
     422             :     }
     423             : 
     424       20889 :     ValuedPoint upperCenter() const
     425             :     {
     426             :         return ValuedPoint(
     427       20889 :             .5 * (upperLeft.x + upperRight.x), upperLeft.y,
     428       20889 :             std::isnan(upperLeft.value)
     429             :                 ? upperRight.value
     430       15625 :                 : (std::isnan(upperRight.value)
     431       15625 :                        ? upperLeft.value
     432       36514 :                        : .5 * (upperLeft.value + upperRight.value)));
     433             :     }
     434             : 
     435       89616 :     uint8_t marchingCase(double level) const
     436             :     {
     437      179232 :         return (level < fudge(upperLeft.value, level) ? UPPER_LEFT : ALL_LOW) |
     438      179232 :                (level < fudge(lowerLeft.value, level) ? LOWER_LEFT : ALL_LOW) |
     439       89616 :                (level < fudge(lowerRight.value, level) ? LOWER_RIGHT
     440       89616 :                                                        : ALL_LOW) |
     441      179232 :                (level < fudge(upperRight.value, level) ? UPPER_RIGHT : ALL_LOW);
     442             :     }
     443             : 
     444      184982 :     static double interpolate_(double level, double x1, double x2, double y1,
     445             :                                double y2, bool need_split)
     446             :     {
     447      184982 :         if (need_split)
     448             :         {
     449             :             // The two cases are here to avoid numerical roundup errors, for two
     450             :             // points, we always compute the same interpolation. This condition
     451             :             // is ensured by the order left->right bottom->top in interpole
     452             :             // calls
     453             :             //
     454             :             // To obtain the same value for border (split) and non-border
     455             :             // element, we take the middle value and interpolate from this to
     456             :             // the end
     457      178986 :             const double xm = .5 * (x1 + x2);
     458      178986 :             const double ym = .5 * (y1 + y2);
     459      178986 :             const double fy1 = fudge(y1, level);
     460      178986 :             const double fym = fudge(ym, level);
     461      178986 :             if ((fy1 < level && level < fym) || (fy1 > level && level > fym))
     462             :             {
     463       88933 :                 x2 = xm;
     464       88933 :                 y2 = ym;
     465             :             }
     466             :             else
     467             :             {
     468       90053 :                 x1 = xm;
     469       90053 :                 y1 = ym;
     470             :             }
     471             :         }
     472      184982 :         const double fy1 = fudge(y1, level);
     473      184982 :         const double ratio = (level - fy1) / (fudge(y2, level) - fy1);
     474      184982 :         return x1 * (1. - ratio) + x2 * ratio;
     475             :     }
     476             : 
     477      184982 :     Point interpolate(uint8_t border, double level) const
     478             :     {
     479      184982 :         switch (border)
     480             :         {
     481       51341 :             case LEFT_BORDER:
     482       51341 :                 return Point(upperLeft.x,
     483       51341 :                              interpolate_(level, lowerLeft.y, upperLeft.y,
     484       51341 :                                           lowerLeft.value, upperLeft.value,
     485      102682 :                                           !split));
     486       41211 :             case LOWER_BORDER:
     487       41211 :                 return Point(interpolate_(level, lowerLeft.x, lowerRight.x,
     488       41211 :                                           lowerLeft.value, lowerRight.value,
     489       41211 :                                           !split),
     490       82422 :                              lowerLeft.y);
     491       50934 :             case RIGHT_BORDER:
     492       50934 :                 return Point(upperRight.x,
     493       50934 :                              interpolate_(level, lowerRight.y, upperRight.y,
     494       50934 :                                           lowerRight.value, upperRight.value,
     495      101868 :                                           !split));
     496       41496 :             case UPPER_BORDER:
     497       41496 :                 return Point(interpolate_(level, upperLeft.x, upperRight.x,
     498       41496 :                                           upperLeft.value, upperRight.value,
     499       41496 :                                           !split),
     500       82992 :                              upperLeft.y);
     501             :         }
     502           0 :         assert(false);
     503             :         return Point();
     504             :     }
     505             : };
     506             : }  // namespace marching_squares
     507             : #endif

Generated by: LCOV version 1.14