LCOV - code coverage report
Current view: top level - elsa/generators - EllipseGenerator.cpp (source / functions) Hit Total Coverage
Test: coverage-all.lcov Lines: 88 96 91.7 %
Date: 2024-05-16 04:22:26 Functions: 6 6 100.0 %

          Line data    Source code
       1             : #include "EllipseGenerator.h"
       2             : #include "Timer.h"
       3             : #include "Logger.h"
       4             : 
       5             : #include <cmath>
       6             : #include <stdexcept>
       7             : 
       8             : namespace elsa
       9             : {
      10             :     template <typename data_t>
      11             :     void EllipseGenerator<data_t>::drawFilledEllipse2d(DataContainer<data_t>& dc, data_t amplitude,
      12             :                                                        Vec2 const& center, Vec2 sizes, data_t angle)
      13         177 :     {
      14             :         // sanity check
      15         177 :         if (dc.getDataDescriptor().getNumberOfDimensions() != 2)
      16           0 :             throw InvalidArgumentError(
      17           0 :                 "EllipseGenerator::drawFilledEllipse2d: can only work on 2d DataContainers");
      18             : 
      19             :         // don't draw anything if size is 0
      20         177 :         if (sizes[0] == 0 && sizes[1] == 0)
      21          80 :             return;
      22             : 
      23             :         // convert to radians
      24          97 :         auto angleRad = angle * pi<double> / 180.0f;
      25             : 
      26             :         // special case: circle or no rotation
      27          97 :         if (sizes[0] == sizes[1] || std::fmod(angle, 180.0) == 0) {
      28          57 :             drawShearedFilledEllipse2d(dc, amplitude, center, sizes, {1, 0});
      29          57 :             return;
      30          57 :         }
      31             : 
      32             :         // convert rotation by angle into shearing
      33          40 :         auto theta = std::atan2(static_cast<real_t>(-sizes[1]) * std::tan(angleRad), sizes[0]);
      34             : 
      35          40 :         Vec2 shear;
      36          40 :         shear[0] = static_cast<index_t>(
      37          40 :             std::floor((static_cast<real_t>(sizes[0]) * std::cos(theta) * std::cos(angleRad))
      38          40 :                        - (static_cast<real_t>(sizes[1]) * std::sin(theta) * std::sin(angleRad))));
      39          40 :         shear[1] = static_cast<index_t>(
      40          40 :             std::floor((static_cast<real_t>(sizes[0]) * std::cos(theta) * std::sin(angleRad))
      41          40 :                        + (static_cast<real_t>(sizes[1]) * std::sin(theta) * std::cos(angleRad))));
      42             : 
      43          40 :         Vec2 shearedSizes;
      44          40 :         shearedSizes[0] = std::abs(shear[0]);
      45          40 :         shearedSizes[1] = sizes[1] * sizes[0] / shearedSizes[0];
      46             : 
      47          40 :         drawShearedFilledEllipse2d(dc, amplitude, center, shearedSizes, shear);
      48          40 :     }
      49             : 
      50             :     template <typename data_t>
      51             :     void EllipseGenerator<data_t>::drawShearedFilledEllipse2d(DataContainer<data_t>& dc,
      52             :                                                               data_t amplitude, Vec2 const& center,
      53             :                                                               Vec2 sizes, Vec2 const& shear)
      54          97 :     {
      55          97 :         auto twoSizeXSquared = 2 * sizes[0] * sizes[0];
      56          97 :         auto twoSizeYSquared = 2 * sizes[1] * sizes[1];
      57             : 
      58             :         // setup first ellipse part where major axis of "advance" is the y axis
      59          97 :         auto x = sizes[0];
      60          97 :         index_t y = 0;
      61             : 
      62          97 :         auto xChange = sizes[1] * sizes[1] * (1 - 2 * sizes[0]);
      63          97 :         auto yChange = sizes[0] * sizes[0];
      64             : 
      65          97 :         index_t ellipseError = 0;
      66          97 :         auto xStop = twoSizeYSquared * sizes[0];
      67          97 :         index_t yStop = 0;
      68             : 
      69             :         // draw the first ellipse part
      70         736 :         while (xStop >= yStop) {
      71         639 :             drawShearedLinePairs2d(dc, amplitude, center, x, y, shear);
      72         639 :             y += 1;
      73         639 :             yStop += twoSizeXSquared;
      74         639 :             ellipseError += yChange;
      75         639 :             yChange += twoSizeXSquared;
      76             : 
      77             :             // check if x update is necessary
      78         639 :             if ((2 * ellipseError + xChange) > 0) {
      79         246 :                 x -= 1;
      80         246 :                 xStop -= twoSizeYSquared;
      81         246 :                 ellipseError += xChange;
      82         246 :                 xChange += twoSizeYSquared;
      83         246 :             }
      84         639 :         }
      85             : 
      86             :         // setup second ellipse part where major axis of "advance" is the x axis
      87          97 :         x = 0;
      88          97 :         y = sizes[1];
      89             : 
      90          97 :         xChange = sizes[1] * sizes[1];
      91          97 :         yChange = sizes[0] * sizes[0] * (1 - 2 * sizes[1]);
      92             : 
      93          97 :         ellipseError = 0;
      94          97 :         xStop = 0;
      95          97 :         yStop = twoSizeXSquared * sizes[1];
      96             : 
      97             :         // draw the second ellipse part
      98         652 :         while (xStop < yStop) {
      99         555 :             x += 1;
     100         555 :             xStop += twoSizeYSquared;
     101         555 :             ellipseError += xChange;
     102         555 :             xChange += twoSizeYSquared;
     103             : 
     104             :             // check if y update is necessary
     105         555 :             if ((2 * ellipseError + yChange) > 0) {
     106             :                 // we only draw once the y axis is updated, to avoid line overlays (since we draw
     107             :                 // lines along x axis), else we would have multiple lines stacking up the amplitude
     108             :                 // (which is additive)
     109         254 :                 drawShearedLinePairs2d(dc, amplitude, center, x - 1, y, shear);
     110             : 
     111         254 :                 y -= 1;
     112         254 :                 yStop -= twoSizeXSquared;
     113         254 :                 ellipseError += yChange;
     114         254 :                 yChange += twoSizeXSquared;
     115         254 :             }
     116         555 :         }
     117          97 :     }
     118             : 
     119             :     template <typename data_t>
     120             :     void EllipseGenerator<data_t>::drawShearedLinePairs2d(DataContainer<data_t>& dc,
     121             :                                                           data_t amplitude, Vec2 center,
     122             :                                                           index_t xOffset, index_t yOffset,
     123             :                                                           Vec2 shear)
     124         893 :     {
     125         893 :         IndexVector_t coord(2);
     126             : 
     127             :         // draw the line along the x axis
     128       40954 :         for (index_t x = center[0] - xOffset; x <= center[0] + xOffset; ++x) {
     129       40061 :             auto shearTerm = (x - center[0]) * shear[1] / shear[0];
     130       40061 :             coord[0] = x;
     131       40061 :             coord[1] = center[1] + yOffset + shearTerm;
     132             :             // flip y axis
     133       40061 :             coord[1] = dc.getDataDescriptor().getNumberOfCoefficientsPerDimension()[1] - coord[1];
     134             : 
     135             :             // bounds check coord just to be sure (we're not performance critical here anyway)
     136       40061 :             if (coord[0] < 0
     137       40061 :                 || coord[0] >= dc.getDataDescriptor().getNumberOfCoefficientsPerDimension()[0])
     138           0 :                 throw InvalidArgumentError("EllipseGenerator::drawShearedLinePairs2d: drawing "
     139           0 :                                            "coordinate (x) out of bounds");
     140       40061 :             if (coord[1] < 0
     141       40061 :                 || coord[1] >= dc.getDataDescriptor().getNumberOfCoefficientsPerDimension()[1])
     142           0 :                 throw InvalidArgumentError("EllipseGenerator::drawShearedLinePairs2d: drawing "
     143           0 :                                            "coordinate (y) out of bounds");
     144             : 
     145       40061 :             dc(coord) += amplitude;
     146             : 
     147       40061 :             if (yOffset != 0) {
     148       38558 :                 coord[1] = center[1] - yOffset + shearTerm;
     149             :                 // flip y axis
     150       38558 :                 coord[1] =
     151       38558 :                     dc.getDataDescriptor().getNumberOfCoefficientsPerDimension()[1] - coord[1];
     152             : 
     153       38558 :                 if (coord[1] < 0
     154       38558 :                     || coord[1] >= dc.getDataDescriptor().getNumberOfCoefficientsPerDimension()[1])
     155           0 :                     throw InvalidArgumentError("EllipseGenerator::drawShearedLinePairs2d: drawing "
     156           0 :                                                "coordinate (y) out of bounds");
     157             : 
     158       38558 :                 dc(coord) += amplitude;
     159       38558 :             }
     160       40061 :         }
     161         893 :     }
     162             : 
     163             :     // ------------------------------------------
     164             :     // explicit template instantiation
     165             :     template class EllipseGenerator<float>;
     166             :     template class EllipseGenerator<double>;
     167             : 
     168             : } // namespace elsa

Generated by: LCOV version 1.14