3
\$\begingroup\$

Let's say I have a heightmap, but I want to create flat areas on it. The easiest solution would be to just say that every value between 0.2 and 0.4 should just become 0.3, but to keep the terrain continuous, I need to make sure that the values below 0.2 are mapped between 0 and 0.3 and the values above 0.4 get mapped between 0.3 and 1

So if we make a diagram, where the x axis is the value and the y is the outcome, then we get this:

enter image description here

This is fine when you have only 1 flat area, but if I'm trying to recreate something like a farmland in Peru, I have to create a lot of these:

enter image description here

So how can I create an efficient function, which given a value and an array containing the from and to values of the flattened areas transforms the value in the new system.

For instance, using the same example, a value of 0.25 would return 0.3 and a value of 0.5 would return 0.41666....

\$\endgroup\$
  • \$\begingroup\$ A cheap workaround would be to create a model with what you are going for. Or, create a function to set certain pieces of your terrain to the specific height you want. \$\endgroup\$ – The Mattbat999 May 10 '18 at 19:19
  • \$\begingroup\$ Possible duplicate of Terracing mountain features \$\endgroup\$ – Pikalek May 10 '18 at 20:43
  • \$\begingroup\$ @Pikalek The top post creates equally sized terraced areas, the accepted answer only creates one \$\endgroup\$ – Bálint May 10 '18 at 20:58
2
\$\begingroup\$

Does not seem to be as complicated as it looks like to implement the algorithm you suggested. The complexity of log(n) is also most likely an optimal one. In order to re-iterate:

  1. Find the lower bound of value
  2. If it is odd index, terrace was hit and average of two adjacent values is returned
  3. If it is even index, rescale the value onto interval between two adjacent pairs (terraces)

The input is just sorted pairs of the clamped intervals of terraces. As a bonus adding doubled bounds will catch edge cases:

std::vector<float> terrace { 0.f, 0.f, 0.2f, 0.4f, 1.0f, 1.0f };//example input
float map_terrace(std::vector<float>& intervals_bounds/*sorted!*/, float value)
{
    auto right = std::lower_bound(intervals_bounds.begin() + 1, intervals_bounds.end(), value);
    auto left = std::prev(right);
    if ((right - intervals_bounds.begin()) % 2 == 1)
        return (*left + *right) / 2.f; //hit terrace
    else //hit slope
        return remap_interval(value, *left, *right,
        (*std::prev(left) + *left) / 2.f, (*std::next(right) + *right) / 2.f);
}

where remap_interval is standard utility function remapping value from one interval onto another:

inline float remap_interval(float value, float min1, float max1, float min2, float max2)
{
  return ((value - min1)/(max1 - min1))*(max2 - min2) + min2;
}

The algorithm allows arbitrary and/or uneven spacing between terraces.

Try it online!

\$\endgroup\$
1
\$\begingroup\$

As an analogy to your graph, I submit

$$min(slope * mod(x, 1), 1) + \lfloor x\rfloor$$

enter image description here

http://www.wolframalpha.com/input/?i=plot+(min(5+*+mod(x,+1),+1)+%2B+floor(x))+from+x%3D-5+to+5

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.