/*------------------------------------------------------------------------------ * Copyright (c) 2023 by Bai Bing (seread@163.com) * See COPYING file for copying and redistribution conditions. * * Alians IT Studio. *----------------------------------------------------------------------------*/ #pragma once #include #include #include #include #include #include #include "ASMatrix.h" #include "ASSlice.h" namespace ais { // Quick value access for ais::FMatrix while interpolating template > struct GridInterpolatorBlock { GridInterpolatorBlock() { clear(); } //============================================================================ // Method Description: /// Build a block of values from matrix, and center point at row and col /// /// @param value /// GridInterpolatorBlock(dtype &value) { clear(); for (int i = 0; i < 25; i++) { values[i] = value; } } //============================================================================ // Method Description: /// Build a block of values from matrix, and center point at row and col /// /// @param matrix, source extended matrix /// @param row, col block center point in extended matrix positions (uj, ui) = (j+2, i+2) /// GridInterpolatorBlock(const ais::Matrix &matrixExtended, size_t uj, size_t ui) { copy(matrixExtended, uj, ui); } //============================================================================ // Method Description: /// copy matrix values to block, and center point at row and col /// /// @param matrix, source extended matrix /// @param row, col block center point in extended matrix positions (uj, ui) = (j+2, i+2) /// GridInterpolatorBlock ©(const ais::Matrix &matrixExtended, size_t uj, size_t ui) { clear(); for (size_t j = uj - 2; j != uj + 3; ++j) { auto begin = j * matrixExtended.columns() + ui - 2; auto end = begin + 5; for (size_t pos = begin; pos < end; pos++) { size_t boxPos = (j - uj + 2) * 5 + pos - begin; values[boxPos] = matrixExtended[pos]; } } return *this; } inline void clear() { std::memset(values, 0, 25 * sizeof(dtype)); } //============================================================================ // Method Description: /// Get the value at the specified offset position, center point of this block is (0, 0) /// 2 top (2, 0) (row, col) /// 1 /// -2 -1 0 1 2 left (0, -2) center (0, 0) right (0, 2) /// -1 /// -2 bottom (-2, 0) /// /// @param row, col /// dtype &operator()(int8_t row, int8_t col) noexcept { size_t pos = (row + 2) * 5 + (col + 2); return values[pos]; } const dtype &operator()(int8_t row, int8_t col) const noexcept { size_t pos = (row + 2) * 5 + (col + 2); return values[pos]; } dtype &operator[](size_t pos) { return values[pos]; } const dtype &operator[](size_t pos) const { return values[pos]; } inline const size_t index(int8_t row, int8_t col) const { size_t index = (row + 2) * 5 + (col + 2); if (index > 24) { std::string msg = "Out of range"; THROW_INVALID_ARGUMENT(msg); } return index; } inline const std::pair &pos_pair(size_t index) const { if (index > 24) { std::string msg = "Out of range"; THROW_INVALID_ARGUMENT(msg); } auto row = int8_t(index / 5) - 2; auto col = int8_t(index % 5) - 2; std::pair ret = std::make_pair(row, col); return ret; } inline double avg() { double sum = 0.0; size_t count = 0; for (auto &d : values) { if ((!std::isnan(d)) && (!std::isinf(d))) { sum += d; count++; } } return count == 0 ? 0 : sum / count; } inline double mock_value(long mask) { double sum = 0.0; size_t count = 0; for (int i = 0; i < 25; i++) { if ((mask & (1 << i)) == 0) { sum += values[i]; count++; } } return count == 0 ? 0 : sum / count; } inline static const std::set> validPos = { {0, 0}, {0, 1}, {-1, 0}, {1, 0}, {0, -1}, {-1, 1}, {-1, -1}, {1, 1}, {1, -1}, {0, 2}, {-2, 0}, {2, 0}, {0, -2}}; inline static const std::set> validPos1 = { {0, 1}, {-1, 0}, {1, 0}, {0, -1}}; inline static const std::set> validPos2 = { {-1, 1}, {-1, -1}, {1, 1}, {1, -1}}; inline static const std::set> validPos3 = { {0, 2}, {-2, 0}, {2, 0}, {0, -2}}; inline double mock_value2(long mask, int pos, double originValue = 0.0) { double sum = 0.0; size_t count = 0; for (int index = 0; index < 25; index++) { int32_t j = int32_t(index / 5) - 2; int32_t i = index % 5 - 2; std::pair ppos = std::make_pair(i, j); if ((mask & (1 << pos)) == 0 && validPos.find(ppos) != validPos.end()) { sum += values[i]; count++; } } return count == 0 ? originValue : sum / count; } inline double mock_value3(long mask, int pos) { double origin = values[pos]; double temp = std::nan("1"); double weight = 0.0; for (int index = 0; index < 25; index++) { // use inverse distance weight to get mock value int32_t j = int32_t(index / 5) - 2; int32_t i = index % 5 - 2; std::pair ipos = std::make_pair(i, j); if (((mask & (1 << index)) == 0) && validPos.find(ipos) != validPos.end()) { // use valid block values to build the invalid node mock value. if (index == 12) { temp += 2 * values[index]; weight += 2; } else if (validPos1.find(ipos) != validPos1.end()) { temp += values[index]; weight += 1; } else if (validPos2.find(ipos) != validPos2.end()) { temp += .7071 * values[index]; weight += .7071; } else if (validPos3.find(ipos) != validPos3.end()) { temp += 0.5 * values[index]; weight += .5; } } } return std::isnan(temp) ? origin : (temp / weight); } inline double nodeInverseDistanceWeight(const std::pair &ppos, const std::pair &ipos) { double temp = std::nan("1"); if (ppos != ipos) temp = std::sqrt((ppos.first - ipos.first) * (ppos.first - ipos.first) + (ppos.second - ipos.second) * (ppos.second - ipos.second)); return std::isnan(temp) ? 2 : 1 / temp; } inline double mock_value4(long mask, int pos, double weightLimit = 0.2) { // use the trand between p(0,0) and mirror point to build the mock value for point at pos double origin = values[pos]; double temp = 0.0; double weight = 0.0; int32_t pj = int32_t(pos / 5) - 2; int32_t pi = pos % 5 - 2; std::pair ppos = std::make_pair(pi, pj); // use valid block values to build the invalid node mock value. for (int index = 0; index < 25; index++) { // use inverse distance weight to get mock value int32_t j = int32_t(index / 5) - 2; int32_t i = index % 5 - 2; std::pair ipos = std::make_pair(i, j); if (((mask & (1 << index)) == 0) && validPos.find(ipos) != validPos.end()) { auto pweight = nodeInverseDistanceWeight(ppos, ipos); if (pweight >= weightLimit) { temp += pweight * values[index]; weight += pweight; } } } return weight != 0.0 ? (temp / weight) : origin; } inline double mock_value5(long mask, int pos) { // use the trend between p(0,0) and mirror point to build the mock value for point at pos double origin = values[pos]; double temp = 0.0; double weight = 0.0; int32_t pj = int32_t(pos / 5) - 2; int32_t pi = pos % 5 - 2; std::pair ppos = std::make_pair(pi, pj); // build mock node by nearest valid point mean for (int i = -1; i < 2; i++) { int32_t ii = pi + i; if (std::abs(ii) > 2) continue; for (int j = -1; j < 2; j++) { // loop the nearest point int32_t ij = pj + j; if (std::abs(ij) > 2) continue; auto index = (ij + 2) * 5 + (ii + 2); if ((mask & (1 << index)) == 0) { temp += values[index]; weight++; } } } return weight != 0.0 ? (temp / weight) : origin; } inline double mock_value6(long mask, int pos) { // use the trend between p(0,0) and mirror point to build the mock value for point at pos double origin = values[pos]; double temp = 0.0; double weight = 0.0; int32_t pj = int32_t(pos / 5) - 2; int32_t pi = pos % 5 - 2; std::pair ppos = std::make_pair(pi, pj); double mock_p_0_0 = mock_value3(mask, 12); if (pos == 12) { // use mock 4 to build center value temp = mock_p_0_0; weight = 1; } else if (validPos3.find(ppos) != validPos3.end() || validPos1.find(ppos) != validPos1.end()) { // build mirror point index auto mi = pi, mj = pj; if (std::abs(pj) > 0) { // for pj = +/- 2 or +/- 1 mj = -pj; } else { // for pi = +/- 2 or +/- 1 mi = -pi; } auto index = (mj + 2) * 5 + (mi + 2); if ((mask & (1 << 12)) > 0 && (mask & (1 << index)) > 0) { // only make mock value while there p(0,0) and mirror point are valid temp = 2 * values[12] - values[index]; weight = 1; } else { // use mock 3 to build center value temp = mock_value3(mask, pos); weight = 1; } } else if (validPos2.find(ppos) != validPos2.end()) { auto mi = -pi, mj = -pj; auto index = (mj + 2) * 5 + (mi + 2); if ((mask & (1 << 12)) > 0 && (mask & (1 << index)) > 0) { // only make mock value while there p(0,0) and mirror point are valid temp = 2 * values[12] - values[index]; weight = 1; } else { // use mock 4 to build center value temp = mock_value3(mask, pos); weight = 1; } } return weight != 0.0 ? (temp / weight) : origin; } inline double mock_value7(long mask, int pos) { double origin = values[pos]; double med; std::vector validData; for (int i = 0; i < 25; i++) { if ((mask & (1 << i)) == 0) { validData.push_back(values[i]); } } auto s = validData.size(); if (s == 0) med = std::nan("-1"); else { auto m = validData.begin() + s / 2; std::partial_sort(std::execution::par, validData.begin(), m, validData.end()); med = validData[s / 2]; } return std::isnan(med) ? origin : med; } inline double mock_value8(long mask, int pos) { double origin = values[pos]; return mask & (1 << 12) ? origin : values[12]; } inline double mock_value9(long mask, int pos, double alpha) { // make the origin data as input value double temp = std::nan("1"); auto [row, col] = pos_pair(pos); std::pair ppos = std::make_pair(row, col); if (pos == index(0, 0)) { // for center point, use avg to smooth the isoline temp = mock_value(mask); } else if (validPos1.find(ppos) != validPos1.end()) { // inner extension edge (0, 1), (0, -1), (1, 0), (-1, 0) auto centerIndex = index(0, 0); auto mirrorIndex = (std::abs(row) != 0) ? index(-row, col) : index(row, -col); temp = 2 * values[centerIndex] - values[mirrorIndex]; } else if (validPos2.find(ppos) != validPos2.end()) { // inner corners (1, 1), (-1, -1), (1, -1), (-1, 1) auto supportIndex1 = index(-row, col); auto supportIndex2 = index(row, -col); auto mirrorIndex = index(-row, -col); temp = values[supportIndex1] + values[supportIndex2] - values[mirrorIndex]; } else if (validPos3.find(ppos) != validPos3.end()) { // exterior extension edge (2, 0), (-2, 0), (0, -2), (0, 2) double alpha1 = alpha * alpha; double alpha2 = -2 * (1 + alpha1); double beta = 1 / alpha; double beta1 = beta * beta; double beta2 = -2 * (1 + beta1); auto mirrorIndex = (std::abs(row) != 0) ? index(-row, col) : index(row, -col); double innerCornersValue = values[index(1, 1)] - values[index(-1, -1)]; double innerEdgeValue = 0.0; double adjustValue = 0; if (std::abs(row) != 0) { // top & bottom (2, 0), (-2, 0) innerCornersValue += values[index(-1, 1)] - values[index(1, -1)]; innerCornersValue *= alpha1; innerEdgeValue = alpha2 * (values[index(0, 1)] - values[index(0, -1)]); } else { // left & right (0, -2), (0, 2) innerCornersValue += values[index(1, -1)] - values[index(1, -1)]; innerCornersValue *= beta1; innerEdgeValue = beta2 * (values[index(1, 0)] - values[index(-1, 0)]); } adjustValue = innerCornersValue + innerEdgeValue; adjustValue = (row + col) > 0 ? -adjustValue : adjustValue; temp = values[mirrorIndex] + adjustValue; } else { temp = mock_value(mask); } return temp; } int get_fault_point_type(long mask, int pos) { // make the origin data as input value double temp = std::nan("1"); auto [row, col] = pos_pair(pos); if (std::abs(row) == std::abs(col)) { // corner points } else if (std::abs(row) < std::abs(col)) { // left or right } else if (std::abs(row) > std::abs(col)) { // top or bottom } return 0; } inline double mock_value10(long mask, int pos, double alpha) { // make the origin data as input value double temp = std::nan("1"); auto [row, col] = pos_pair(pos); std::pair ppos = std::make_pair(row, col); if (pos == index(0, 0)) { // for center point, use avg to smooth the isoline temp = mock_value(mask); } // else if (validPos1.find(ppos) != validPos1.end()) //{ // // inner extension edge (0, 1), (0, -1), (1, 0), (-1, 0) // auto centerIndex = index(0, 0); // auto mirrorIndex = (std::abs(row) != 0) ? index(-row, col) : index(row, -col); // temp = 2 * values[centerIndex] - values[mirrorIndex]; //} // else if (validPos2.find(ppos) != validPos2.end()) //{ // // inner corners (1, 1), (-1, -1), (1, -1), (-1, 1) // auto supportIndex1 = index(-row, col); // auto supportIndex2 = index(row, -col); // auto mirrorIndex = index(-row, -col); // temp = values[supportIndex1] + values[supportIndex2] - values[mirrorIndex]; //} // else if (validPos3.find(ppos) != validPos3.end()) //{ // // exterior extension edge (2, 0), (-2, 0), (0, -2), (0, 2) // double alpha1 = alpha * alpha; // double alpha2 = -2 * (1 + alpha1); // double beta = 1 / alpha; // double beta1 = beta * beta; // double beta2 = -2 * (1 + beta1); // auto mirrorIndex = (std::abs(row) != 0) ? index(-row, col) : index(row, -col); // double innerCornersValue = values[index(1, 1)] - values[index(-1, -1)]; // double innerEdgeValue = 0.0; // double adjustValue = 0; // if (std::abs(row) != 0) // { // // top & bottom (2, 0), (-2, 0) // innerCornersValue += values[index(-1, 1)] - values[index(1, -1)]; // innerCornersValue *= alpha1; // innerEdgeValue = alpha2 * (values[index(0, 1)] - values[index(0, -1)]); // } // else // { // // left & right (0, -2), (0, 2) // innerCornersValue += values[index(1, -1)] - values[index(1, -1)]; // innerCornersValue *= beta1; // innerEdgeValue = beta2 * (values[index(1, 0)] - values[index(-1, 0)]); // } // adjustValue = innerCornersValue + innerEdgeValue; // adjustValue = (row + col) > 0 ? -adjustValue : adjustValue; // temp = values[mirrorIndex] + adjustValue; //} // else //{ // temp = mock_value(mask); //} return temp; } std::string str() const { std::stringstream ss; for (size_t j = 0; j < 5; j++) { for (size_t i = 0; i < 5; i++) { ss << values[j * 5 + i] << " "; } ss << std::endl; } return ss.str(); } void print() const { std::cout << *this; } friend std::ostream &operator<<(std::ostream &stream, const GridInterpolatorBlock &block) { stream << block.str(); return stream; } private: dtype values[25]; }; } // namespace ais