222 lines
6 KiB
C++
222 lines
6 KiB
C++
#pragma once
|
|
|
|
#include "topography_generator/marching_squares/contours_builder.hpp"
|
|
|
|
namespace topography_generator
|
|
{
|
|
template <typename ValueType>
|
|
class Square
|
|
{
|
|
public:
|
|
Square(ValueType minValue, ValueType valueStep, std::string const & debugId)
|
|
: m_minValue(minValue)
|
|
, m_valueStep(valueStep)
|
|
, m_debugId(debugId)
|
|
{
|
|
static_assert(std::is_integral<ValueType>::value && std::is_signed<ValueType>::value);
|
|
}
|
|
|
|
void Init(double left, double bottom, double right, double top, ValueType lb, ValueType rb, ValueType lt,
|
|
ValueType rt, ValueType invalid)
|
|
{
|
|
m_isValid = true;
|
|
|
|
m_left = left;
|
|
m_bottom = bottom;
|
|
m_right = right;
|
|
m_top = top;
|
|
|
|
m_valueLB = GetValue(lb, invalid);
|
|
m_valueRB = GetValue(rb, invalid);
|
|
m_valueLT = GetValue(lt, invalid);
|
|
m_valueRT = GetValue(rt, invalid);
|
|
}
|
|
|
|
void GenerateSegments(ContoursBuilder & builder) const
|
|
{
|
|
if (!m_isValid)
|
|
return;
|
|
|
|
ValueType minVal = std::min(m_valueLB, std::min(m_valueLT, std::min(m_valueRT, m_valueRB)));
|
|
ValueType maxVal = std::max(m_valueLB, std::max(m_valueLT, std::max(m_valueRT, m_valueRB)));
|
|
|
|
ToLevelsRange(m_valueStep, minVal, maxVal);
|
|
|
|
CHECK_GREATER_OR_EQUAL(minVal, m_minValue, (m_debugId));
|
|
|
|
for (auto val = minVal; val < maxVal; val += m_valueStep)
|
|
AddSegments(val, (val - m_minValue) / m_valueStep, builder);
|
|
}
|
|
|
|
static void ToLevelsRange(ValueType step, ValueType & minVal, ValueType & maxVal)
|
|
{
|
|
if (minVal > 0)
|
|
minVal = step * ((minVal + step - 1) / step);
|
|
else
|
|
minVal = step * (minVal / step);
|
|
|
|
if (maxVal > 0)
|
|
maxVal = step * ((maxVal + step) / step);
|
|
else
|
|
maxVal = step * ((maxVal + 1) / step);
|
|
}
|
|
|
|
private:
|
|
enum class Rib
|
|
{
|
|
None,
|
|
Left,
|
|
Top,
|
|
Right,
|
|
Bottom,
|
|
Unclear,
|
|
};
|
|
|
|
ValueType GetValue(ValueType val, ValueType invalid)
|
|
{
|
|
// If a contour goes right through the corner of the square false segments can be generated.
|
|
// Shift the value slightly from the corner.
|
|
if (val == invalid)
|
|
{
|
|
// LOG(LWARNING, ("Invalid value at the position", pos, m_debugId));
|
|
m_isValid = false;
|
|
return val;
|
|
}
|
|
|
|
if (abs(val) % m_valueStep == 0)
|
|
return val + 1;
|
|
return val;
|
|
}
|
|
|
|
void AddSegments(ValueType val, uint16_t ind, ContoursBuilder & builder) const
|
|
{
|
|
// Segment is a vector directed so that higher values is on the right.
|
|
static std::pair<Rib, Rib> const intersectedRibs[] = {
|
|
{Rib::None, Rib::None}, // 0000
|
|
{Rib::Left, Rib::Bottom}, // 0001
|
|
{Rib::Top, Rib::Left}, // 0010
|
|
{Rib::Top, Rib::Bottom}, // 0011
|
|
{Rib::Right, Rib::Top}, // 0100
|
|
{Rib::Unclear, Rib::Unclear}, // 0101
|
|
{Rib::Right, Rib::Left}, // 0110
|
|
{Rib::Right, Rib::Bottom}, // 0111
|
|
{Rib::Bottom, Rib::Right}, // 1000
|
|
{Rib::Left, Rib::Right}, // 1001
|
|
{Rib::Unclear, Rib::Unclear}, // 1010
|
|
{Rib::Top, Rib::Right}, // 1011
|
|
{Rib::Bottom, Rib::Top}, // 1100
|
|
{Rib::Left, Rib::Top}, // 1101
|
|
{Rib::Bottom, Rib::Left}, // 1110
|
|
{Rib::None, Rib::None}, // 1111
|
|
};
|
|
|
|
uint8_t const pattern = (m_valueLB > val ? 1u : 0u) | ((m_valueLT > val ? 1u : 0u) << 1u) |
|
|
((m_valueRT > val ? 1u : 0u) << 2u) | ((m_valueRB > val ? 1u : 0u) << 3u);
|
|
|
|
auto const ribs = intersectedRibs[pattern];
|
|
|
|
if (ribs.first == Rib::None)
|
|
return;
|
|
|
|
if (ribs.first != Rib::Unclear)
|
|
{
|
|
builder.AddSegment(ind, InterpolatePoint(ribs.first, val), InterpolatePoint(ribs.second, val));
|
|
}
|
|
else
|
|
{
|
|
auto const leftPos = InterpolatePoint(Rib::Left, val);
|
|
auto const rightPos = InterpolatePoint(Rib::Right, val);
|
|
auto const bottomPos = InterpolatePoint(Rib::Bottom, val);
|
|
auto const topPos = InterpolatePoint(Rib::Top, val);
|
|
|
|
ValueType const avVal = (m_valueLB + m_valueLT + m_valueRT + m_valueRB) / 4;
|
|
if (avVal > val)
|
|
{
|
|
if (m_valueLB > val)
|
|
{
|
|
builder.AddSegment(ind, leftPos, topPos);
|
|
builder.AddSegment(ind, rightPos, bottomPos);
|
|
}
|
|
else
|
|
{
|
|
builder.AddSegment(ind, bottomPos, leftPos);
|
|
builder.AddSegment(ind, topPos, rightPos);
|
|
}
|
|
}
|
|
else if (m_valueLB > val)
|
|
{
|
|
builder.AddSegment(ind, leftPos, bottomPos);
|
|
builder.AddSegment(ind, rightPos, topPos);
|
|
}
|
|
else
|
|
{
|
|
builder.AddSegment(ind, topPos, leftPos);
|
|
builder.AddSegment(ind, bottomPos, rightPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
ms::LatLon InterpolatePoint(Square::Rib rib, ValueType val) const
|
|
{
|
|
double val1;
|
|
double val2;
|
|
double lat;
|
|
double lon;
|
|
|
|
switch (rib)
|
|
{
|
|
case Rib::Left:
|
|
val1 = static_cast<double>(m_valueLB);
|
|
val2 = static_cast<double>(m_valueLT);
|
|
lon = m_left;
|
|
break;
|
|
case Rib::Right:
|
|
val1 = static_cast<double>(m_valueRB);
|
|
val2 = static_cast<double>(m_valueRT);
|
|
lon = m_right;
|
|
break;
|
|
case Rib::Top:
|
|
val1 = static_cast<double>(m_valueLT);
|
|
val2 = static_cast<double>(m_valueRT);
|
|
lat = m_top;
|
|
break;
|
|
case Rib::Bottom:
|
|
val1 = static_cast<double>(m_valueLB);
|
|
val2 = static_cast<double>(m_valueRB);
|
|
lat = m_bottom;
|
|
break;
|
|
default: UNREACHABLE();
|
|
}
|
|
|
|
CHECK_NOT_EQUAL(val, val2, (m_debugId));
|
|
double const coeff = (val1 - val) / (val - val2);
|
|
|
|
switch (rib)
|
|
{
|
|
case Rib::Left:
|
|
case Rib::Right: lat = (m_bottom + m_top * coeff) / (1 + coeff); break;
|
|
case Rib::Bottom:
|
|
case Rib::Top: lon = (m_left + m_right * coeff) / (1 + coeff); break;
|
|
default: UNREACHABLE();
|
|
}
|
|
|
|
return {lat, lon};
|
|
}
|
|
|
|
double m_left;
|
|
double m_right;
|
|
double m_bottom;
|
|
double m_top;
|
|
|
|
ValueType m_valueLB;
|
|
ValueType m_valueLT;
|
|
ValueType m_valueRT;
|
|
ValueType m_valueRB;
|
|
|
|
ValueType m_minValue;
|
|
ValueType m_valueStep;
|
|
std::string m_debugId;
|
|
|
|
bool m_isValid;
|
|
};
|
|
} // namespace topography_generator
|