11

I am doing some 3D graphics and I have an open ocean. For this ocean, I have a matrix representing the sea state (i.e. wave heights) for a particular rectangular subsection of the sea. The rest of the ocean is flat. My problem is that my controlled sea, where there are waves, is positioned in the middle of open flat sea, and the discontinuity at the edges of my grid causes some bad artifacts. The reason I am only generating waves for a subsection and not the entire sea is because my noise function is prohibitively expensive to compute on the entire sea (and I know the easiest solution is to use a cheaper noise function like simplex noise, but that's not an option).

Having said that my question is really rather simple. If say I have a grid (aka matrix aka 2d array) of size 100x40, and I want to find the value for position 120x33, I simply want to take the nearest neighbour, which would be 100x33. So for any number that lies outside a given range, I want that number to saturate to lie within the given range. Is there a function in C or C++ that does this?

Edit: the position parameters are of type float

I know I can do this with some simple if statements, but it just seems like something that the standard libraries would include.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
lmirosevic
  • 15,787
  • 13
  • 70
  • 116
  • There are SSE instructions that supports saturation. – ruslik Dec 06 '10 at 20:10
  • does this entail coding in assembly? also can I specify the range or does that just saturate to the ranges of the primitive data types? – lmirosevic Dec 06 '10 at 20:11
  • I think any modern machine should be able to do an entire sea with no trouble at all. My software (http://www.orcina.com/SoftwareProducts/OrcaFlex) does so although it is somewhat different in nature in that it is attempting to model the physics realistically. – David Heffernan Dec 06 '10 at 20:13
  • @david: They can and they do, but I am getting my sea spectrum across the network from some MATLAB code, which ATM isn't compiled so runs about 100 times slower than native C would, and in MATLAB this is more difficult to do in realtime for an entire sea. If you have any thoughts on how to do it quickly (200Hz) in MATLAB in realtime for a grid with ~1k data points using 38 superimposed sin functions, I'd love to hear it. – lmirosevic Dec 06 '10 at 20:18
  • @codenoob the most obvious trick is only to evaluate the trig functions at t0 and then for t1, t2 etc. use addition formula. That way you just to FP multiply/add and no trig. – David Heffernan Dec 06 '10 at 20:28
  • @codenoob You could get the 38 sinusoidal wave components from MATLAB and to the wave elevation calcs in your C/C++ code. 38 is not very many, we routinely do hundreds or even thousands. – David Heffernan Dec 06 '10 at 20:29
  • explain why the neighbor of (120x33) is (100x33)? Also, if you propagate a wave around, and you have a boundary, you're going to need to do some very complicated damping at the edges, or you'll get weird reflections. – Dov Dec 06 '10 at 20:31
  • @david: im not quite sure what you mean by "just FP multiply/add", please could you elaborate a bit. @dov: I am just saturating each coordinate within its dimension. That way I will get no perceivable discontinuous edges. The wave model is just based on some trig functions, I am not doing any "realistic" CFD theory where the waves would actually reflect and refract off of edges. – lmirosevic Dec 06 '10 at 20:41
  • @codenoob: addition formulae: sin(x+y) = sin(x)cos(y) + sin(y)cos(x) and so on. Each wave elevation component will be of the form a*sin(omega*t-phi) where t is the time. Assuming you have equal time intervals then you can use the addition formulae to turn the very expensive trig evaluation into the much faster (order of magnitude typically) FP multiply/add. – David Heffernan Dec 06 '10 at 20:47

5 Answers5

13

And now there is, in the form of std::clamp. And I'm barely seven years late :)

Quentin
  • 62,093
  • 7
  • 131
  • 191
10
template<typename T>
T saturate(T val, T min, T max) {
    return std::min(std::max(val, min), max);
}
etarion
  • 16,935
  • 4
  • 43
  • 66
4

ISO/IEC JTC1 SC22 WG14 N1169 (Programming languages - C - Extensions to support embedded processors) specifies a _Sat type qualifier for saturating data types. I have never tried to use it in any compiler, but it is included in the GCC 4.x documentation.

VC++ 2003 onward supports MMX intrinsics that allow saturating arithmetic.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • MMX intrinsics link is dead – Eric Johnson Jan 17 '18 at 23:47
  • 1
    @EricJohnson : That should not be a surprise since the post is >6 years old. I am not about to maintain links in over 2000 answers posted! The key terms needed to search MSDN or Google were provided. Archived documentation for VS2010 [here](https://msdn.microsoft.com/en-us/library/8cs7e4zs(v=vs.100).aspx), and for VS2015 [here](https://msdn.microsoft.com/en-us/library/hh977023.aspx) - though no longer nicely categorised. I imagine in 2017 too. – Clifford Jan 18 '18 at 14:46
  • `_Sat _Accum` / `_Sat _Fract` fixed-point types aren't supported by gcc for x86, ARM, ARM64, or AVR. https://godbolt.org/z/RrMa8l :( I didn't try any other ISAs on Godbolt, but I assume MIPS / PowerPC / RISC-V don't have gcc support either. https://gcc.gnu.org/onlinedocs/gcc/Fixed-Point.html doesn't say which targets they are supported on. – Peter Cordes Jun 10 '19 at 17:15
  • Oh, clang supports them with `-ffixed-point`! But for x86, it just does addition without actual saturation, just wrapping. https://godbolt.org/z/akWV0N. But for RISC-V, there's a whole lot of instructions that do ... something(?) – Peter Cordes Jun 10 '19 at 17:20
  • Update: more recent `clang -ffixed-point` does compile them to do saturation, not just wrapping addition on x86: https://godbolt.org/z/5EdP1EnxT – Peter Cordes Oct 08 '22 at 21:35
1

Min and Max no?

x = std::min(100,x);
y = std::min(40,y);
OrangeDog
  • 36,653
  • 12
  • 122
  • 207
  • That's implemented in terms of if the conditional operator by default. –  Dec 06 '10 at 20:22
0

Or if you like complicated defines !

#define DERIVE_MAX (100)

//Saturate X at +/-DERIVE_MAX
#define SAT(x) ( ((x) > DERIVE_MAX) ? DERIVE_MAX : ( (-(x) > DERIVE_MAX) ? (-DERIVE_MAX) : (x) ) )