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