|
| 1 | +#include "EbsdLib/Utilities/GriddedColorKey.hpp" |
| 2 | + |
| 3 | +#include <algorithm> |
| 4 | +#include <cmath> |
| 5 | + |
| 6 | +namespace ebsdlib |
| 7 | +{ |
| 8 | + |
| 9 | +namespace |
| 10 | +{ |
| 11 | +constexpr double k_Pi = 3.14159265358979323846; |
| 12 | +constexpr double k_HalfPi = k_Pi / 2.0; |
| 13 | +constexpr double k_DegToRad = k_Pi / 180.0; |
| 14 | +} // namespace |
| 15 | + |
| 16 | +GriddedColorKey::GriddedColorKey(IColorKey::Pointer innerKey, double resolutionDeg) |
| 17 | +: m_InnerKey(std::move(innerKey)) |
| 18 | +, m_ResolutionDeg(resolutionDeg) |
| 19 | +, m_ResolutionRad(resolutionDeg * k_DegToRad) |
| 20 | +{ |
| 21 | + // Grid covers eta in [0, pi] (180 degrees) and chi in [0, pi/2] (90 degrees) |
| 22 | + // This covers all possible Laue group SSTs |
| 23 | + m_EtaSteps = static_cast<int>(std::ceil(180.0 / resolutionDeg)) + 1; |
| 24 | + m_ChiSteps = static_cast<int>(std::ceil(90.0 / resolutionDeg)) + 1; |
| 25 | + precomputeGrid(); |
| 26 | +} |
| 27 | + |
| 28 | +void GriddedColorKey::precomputeGrid() |
| 29 | +{ |
| 30 | + m_Grid.resize(m_EtaSteps); |
| 31 | + |
| 32 | + for(int ei = 0; ei < m_EtaSteps; ei++) |
| 33 | + { |
| 34 | + m_Grid[ei].resize(m_ChiSteps); |
| 35 | + double eta = static_cast<double>(ei) * m_ResolutionRad; |
| 36 | + |
| 37 | + for(int ci = 0; ci < m_ChiSteps; ci++) |
| 38 | + { |
| 39 | + double chi = static_cast<double>(ci) * m_ResolutionRad; |
| 40 | + |
| 41 | + // Convert spherical to Cartesian direction and compute color |
| 42 | + // via the inner key's Vec3 overload |
| 43 | + double sinChi = std::sin(chi); |
| 44 | + double cosChi = std::cos(chi); |
| 45 | + Vec3 dir = {sinChi * std::cos(eta), sinChi * std::sin(eta), cosChi}; |
| 46 | + |
| 47 | + m_Grid[ei][ci] = m_InnerKey->direction2Color(dir); |
| 48 | + } |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +GriddedColorKey::Vec3 GriddedColorKey::lookupGrid(double eta, double chi) const |
| 53 | +{ |
| 54 | + // Map to grid indices via nearest-neighbor snapping |
| 55 | + int ei = static_cast<int>(std::round(eta / m_ResolutionRad)); |
| 56 | + int ci = static_cast<int>(std::round(chi / m_ResolutionRad)); |
| 57 | + |
| 58 | + // Clamp to grid bounds |
| 59 | + ei = std::clamp(ei, 0, m_EtaSteps - 1); |
| 60 | + ci = std::clamp(ci, 0, m_ChiSteps - 1); |
| 61 | + |
| 62 | + return m_Grid[ei][ci]; |
| 63 | +} |
| 64 | + |
| 65 | +GriddedColorKey::Vec3 GriddedColorKey::direction2Color(const Vec3& direction) const |
| 66 | +{ |
| 67 | + // Convert direction to (eta, chi) and look up from grid |
| 68 | + double chi = std::acos(std::clamp(direction[2], -1.0, 1.0)); |
| 69 | + double eta = std::atan2(direction[1], direction[0]); |
| 70 | + if(eta < 0.0) |
| 71 | + { |
| 72 | + eta += 2.0 * k_Pi; |
| 73 | + } |
| 74 | + return lookupGrid(eta, chi); |
| 75 | +} |
| 76 | + |
| 77 | +GriddedColorKey::Vec3 GriddedColorKey::direction2Color(double eta, double chi, const Vec3& angleLimits) const |
| 78 | +{ |
| 79 | + // Snap eta and chi to nearest grid point (flat shading). |
| 80 | + // Instead of computing the exact color at (eta, chi), |
| 81 | + // we return the precomputed color at the nearest grid point. |
| 82 | + // This produces flat-colored patches like MTEX's surf() rendering. |
| 83 | + double etaPositive = eta; |
| 84 | + if(etaPositive < 0.0) |
| 85 | + { |
| 86 | + etaPositive += 2.0 * k_Pi; |
| 87 | + } |
| 88 | + return lookupGrid(etaPositive, chi); |
| 89 | +} |
| 90 | + |
| 91 | +std::string GriddedColorKey::name() const |
| 92 | +{ |
| 93 | + return m_InnerKey->name() + " (gridded)"; |
| 94 | +} |
| 95 | + |
| 96 | +IColorKey::Pointer GriddedColorKey::innerKey() const |
| 97 | +{ |
| 98 | + return m_InnerKey; |
| 99 | +} |
| 100 | + |
| 101 | +double GriddedColorKey::resolutionDeg() const |
| 102 | +{ |
| 103 | + return m_ResolutionDeg; |
| 104 | +} |
| 105 | + |
| 106 | +} // namespace ebsdlib |
0 commit comments