@@ -44,33 +44,52 @@ inline double GetPhase(const std::complex<double>& v)
4444 return phase;
4545}
4646
47- ComplexToAMP8IPHS8I::ComplexToAMP8IPHS8I (const six::AmplitudeTable * pAmplitudeTable)
47+ static std::array< long double , UINT8_MAX + 1 > make_magnitudes (const six::AmplitudeTable* pAmplitudeTable)
4848{
49- // Be careful with indexing so that we don't wrap-around in the loops.
50- for (uint16_t i = 0 ; i <= UINT8_MAX; i++)
49+ std::array< long double , UINT8_MAX + 1 > retval{};
50+ for (uint16_t i = 0 ; i <= UINT8_MAX; i++) // Be careful with indexing so that we don't wrap-around in the loops.
5151 {
5252 // AmpPhase -> Complex
5353 ImageData::AMP8I_PHS8I_t v;
54- v.first = gsl::narrow<uint8_t >(i);
55- v.second = v.first ;
54+ v.first = v.second = gsl::narrow<uint8_t >(i);
5655 const auto complex = Utilities::from_AMP8I_PHS8I (v.first , v.second , pAmplitudeTable);
57- magnitudes[i] = {
58- std::abs (complex )
59- };
56+ retval[i] = std::abs (complex );
57+ }
58+ return retval;
59+ }
60+
61+ static std::array<long double , UINT8_MAX + 1 > get_magnitudes (const six::AmplitudeTable* pAmplitudeTable)
62+ {
63+ std::array<long double , UINT8_MAX + 1 > retval{};
64+ if (pAmplitudeTable == nullptr )
65+ {
66+ static auto magnitudes = make_magnitudes (nullptr ); // OK to cache, won't change
67+ retval = magnitudes;
68+ }
69+ else
70+ {
71+ retval = make_magnitudes (pAmplitudeTable);
6072 }
6173
6274 // I don't know if we can guarantee that the amplitude table is non-decreasing.
6375 // Check to verify property at runtime.
64- if (!std::is_sorted (magnitudes.begin (), magnitudes.end ())) {
76+ if (!std::is_sorted (retval.begin (), retval.end ()))
77+ {
6578 throw std::runtime_error (" magnitudes must be sorted" );
6679 }
80+ return retval;
81+ }
82+
83+ ComplexToAMP8IPHS8I::ComplexToAMP8IPHS8I (const six::AmplitudeTable *pAmplitudeTable)
84+ {
85+ magnitudes = get_magnitudes (pAmplitudeTable);
6786
6887 const auto p0 = GetPhase (Utilities::from_AMP8I_PHS8I (1 , 0 , pAmplitudeTable));
6988 const auto p1 = GetPhase (Utilities::from_AMP8I_PHS8I (1 , 1 , pAmplitudeTable));
7089 assert (p0 == 0 );
7190 assert (p1 > p0);
7291 phase_delta = p1 - p0;
73- for (size_t i = 0 ; i <= UINT8_MAX; i++)
92+ for (size_t i = 0 ; i <= UINT8_MAX; i++) // Be careful with indexing so that we don't wrap-around in the loops.
7493 {
7594 long double y, x;
7695 math::SinCos (p0 + gsl::narrow_cast<long double >(i) * phase_delta, y, x);
@@ -91,33 +110,34 @@ ComplexToAMP8IPHS8I::ComplexToAMP8IPHS8I() : ComplexToAMP8IPHS8I(nullptr /*pAmpl
91110template <typename TIter>
92111static uint8_t nearest (const TIter& begin, const TIter& end, long double value)
93112{
94- auto it = std::lower_bound (begin, end, value);
113+ const auto it = std::lower_bound (begin, end, value);
95114 if (it == begin) return 0 ;
96- auto prev_it = std::prev (it);
97- auto nearest = (it == end || value - *prev_it <= *it - value) ? prev_it : it;
98- assert (std::distance (begin, nearest) <= std::numeric_limits<uint8_t >::max ());
99- return static_cast <uint8_t >(std::distance (begin, nearest));
115+
116+ const auto prev_it = std::prev (it);
117+ const auto nearest = (it == end || value - *prev_it <= *it - value) ? prev_it : it;
118+ const auto distance = std::distance (begin, nearest);
119+ assert (distance <= std::numeric_limits<uint8_t >::max ());
120+ return gsl::narrow<uint8_t >(distance);
100121}
101122
102123ImageData::AMP8I_PHS8I_t ComplexToAMP8IPHS8I::nearest_neighbor (const std::complex <float > &v) const
103124{
104- ImageData::AMP8I_PHS8I_t ans ;
125+ ImageData::AMP8I_PHS8I_t retval ;
105126
106127 // Phase is determined via arithmetic because it's equally spaced.
107128 // There's an intentional conversion to zero when we cast 256 -> uint8. That wrap around
108129 // handles cases that are close to 2PI.
109- ans .second = gsl::narrow_cast<uint8_t >(std::round (GetPhase (v) / phase_delta));
130+ retval .second = gsl::narrow_cast<uint8_t >(std::round (GetPhase (v) / phase_delta));
110131
111132 // We have to do a 1D nearest neighbor search for magnitude.
112133 // But it's not the magnitude of the input complex value - it's the projection of
113134 // the complex value onto the ray of candidate magnitudes at the selected phase.
114- // I .e. dot product.
115- auto && direction = phase_directions[ans .second ];
116- const auto projection = direction .real () * gsl::narrow_cast< long double >( v.real ()) + direction .imag () * gsl::narrow_cast< long double >( v.imag () );
135+ // i .e. dot product.
136+ auto && phase_direction = phase_directions[retval .second ];
137+ const auto projection = phase_direction .real () * v.real () + phase_direction .imag () * v.imag ();
117138 // assert(std::abs(projection - std::abs(v)) < 1e-5); // TODO ???
118- ans .first = nearest (magnitudes.begin (), magnitudes.end (), projection);
119- return ans ;
139+ retval .first = nearest (magnitudes.begin (), magnitudes.end (), projection);
140+ return retval ;
120141}
121-
122142}
123143}
0 commit comments