x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

Blueprint MapRangedClamped inplementation in C++

Hi all,

I'm trying to convert a Blueprint to C++ (mainly as a learning exercise).... i'm at the MapRangeClamped node and am looking in the docs, searching Google and trawling //GenericPlatformMath.h etc but can't find an engine C++ equivalent.

Any assistance with a C++ equivalent function would be greatly received!!

alt text

Many thanks in advance, Matt.

Product Version: UE 4.19
Tags:
more ▼

asked Jul 11 '18 at 07:07 PM in C++ Programming

avatar image

fallingbrickwork
35 5 8 12

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

1 answer: sort voted first

As this is your learning exercise I will not give you any code. Just the way it is supposed to works and a little hint at the end.

Lets say you input:

Value = 150; InRangeA = 250; InRangeB = 50; OutRangeA = -5; OutRangeB = 5;

  • Your Value (150) is half way between InRangeA (250) and InRangeB (50) on the number line.

  • The ReturnValue should be halfway between OutRangeA (-5) and OutRangeB (5) on the number line which is exactly 0.0.

Notice that InRange numbers are in reverse order so closer to 250 your Value is the closer to -5 your ReturnValue should be. Your function though should work with ranges in any order.

"Clamped" just means that if your Value is outside of the InRange (say 2000) it will treat it as the maximum or minimum value (in this case max = 250) and return the corresponding OutRange value (-5 in this case).


The hint: It might be obvious by now that you should go through a intermediary normalized number line. One that is from 0.0 to 1.0. "0.0" means "return OutRangeA", "1.0" means "return OutRangeB" and 0.3 means return the number that is 30% of the way from OutRangeA to OutrangeB.


I really hope I managed to explain it and not make it more confusing.

more ▼

answered Jul 11 '18 at 09:27 PM

avatar image

dZh0
1.2k 6 2 7

avatar image fallingbrickwork Jul 12 '18 at 07:41 PM

Hi dZh0,

I can't thank you enough for firstly replying and secondly for not just giving me the code; that would not have helped my learning as you pointed out!

Your reply helped me so much, so many thanks once again. I've followed your lead and produced the following code. It seems to work, but I'm wondering if you could look it over and basically check it (I've tried as many tests as possible but I'm still mindful of edge cases i've not thought about) + any obvious optimizations that could be had.

 /* Return a value based on 2 inputted ranges (ratio)*/
 float AUniverse_StarGenerator::MapRangedClamped(float value, float inRangeA, float inRangeB, float outRangeA, float outRangeB)
 {
     float maprangedclamped = 0.0f;
 
     // Find the value as a ration of the inRangeA & B
     /*
     range = max - min
     correctedStartValue = input - min
     percentage = (correctedStartValue * 100) / range 
     */
     float clampedValue = FMath::Clamp(value, inRangeB, inRangeA);
 
     float inRange = inRangeA - inRangeB;
     float correctedInValue = clampedValue - inRangeB;
     float inPercentage = (correctedInValue) / inRange;
 
     // Find the inPercentage of the outRange
     /*
     val = ((percent * (max - min) / 100) + min
     */
     maprangedclamped = (inPercentage * (outRangeA - outRangeB)) + outRangeB;
 
     UE_LOG(LogTemp, Warning, TEXT("value: %f, inRangeA: %f, inRangeB: %f, inPercentage: %f :::: outRangeValue: %f"), value, inRangeA, inRangeB, inPercentage, maprangedclamped);
 
 
     return maprangedclamped;
 }
avatar image dZh0 Jul 12 '18 at 09:57 PM

This is trickier than it seems at glance :)

You are on the right path but your code "assumes" that inRangeB < inRangeA which will fail in this case:

(1, 0, 4, 10, 50) should return 20 because 1 is 1/4 (25%) the way from 0 to 4

Also, before you divide here float inPercentage = (correctedInValue) / inRange; you MUST make sure that inRange is not 0:

(4, 4, 4, 10, 50) = ? //You might show an error message or just default to some value but never, ever go through a division that might not be defined.

As for optimization - this is a very, very short code so it needs none. I would probably make a few "early returns" before going to the body of the method. Doing this would also let me get rid of the FMath::Clamp() call.

avatar image fallingbrickwork Jul 14 '18 at 06:39 PM

Thanks again dZd0, your help has been so invaluable.

I fear in this case you may have to, if you can, offer up some code. My current thoughts are around the idea of if statements (i.e if (inRangeB > inRangeA) then swapping the variables around A is always greater than B etc). I'm sure there is a far more elegant way than this?!

Kindest regards in advance, Matt.

avatar image dZh0 Jul 14 '18 at 10:03 PM

We will imagine that we have an input number line with the value, inRangeA and inRangeB. We will start to modify it until the value becomes what we need:

 float AUniverse_StarGenerator::MapRangedClamped(float value, float inRangeA, float inRangeB, float outRangeA, float outRangeB)
 {
 //First, the early returns to get the inconvenient edge cases out of the way
     if (outRangeA == outRangeB) return outRangeA; // The output range is a single value so return it. No matter what you input is it will produce this value.
     if (inRangeA == inRangeB) UE_LOG(YourLog, Fatal, TEXT("inRangeA == inRangeB which will produce one to many mapping")) ; // The inRange is a single value so it can't map to a range unless it is also a single value (which we covered in the line above)
     
 // MOVE: First we move all the input numbers until inRangeA reaches 0:
     // inRangeA -= inRangeA; We don't calculate this because we know that X-X=0
     value -= inRangeA;
     inRangeB -= inRangeA;
     // Note that we don't care if A is bigger or smaller than B or if it's negative or positive - it will always end up 0
 
 // SCALE: Now we scale all the input numbers until inRangeB is 1
     // We KNOW for fact that inRangeB is not 0 because inRangeA is zero and we made sure inRangeA and inRangeB are different numbers
     // inRangeA /= inRangeB; We don't calculate this as 0/X=0
     value /= inRangeB;
     // inRangeB /= inRangeB; We don't calculate this as X/X=1
     // Note that we don't care if B is negative or positive - it will always end up 1
 
 // Now  we make 2 more early returns if the value is out of the range
     if (value < 0.0f) return outRangeA; // 0 = inRangeA
     if (value > 1.0f) return outRangeB; // 1 = inRanbeB
 
 // With every case out of the way we can calculate the value:
     return outRangeA + value * (outRangeB - outRangeA);
 }
avatar image dZh0 Jul 14 '18 at 10:08 PM

Now the code above is mostly comments. As you have noticed we only calculate value and inRangeB but inRangeB is only used to modify value so we can get this in one line. Also it is considered "rude" by some programmers (not me!) to modify the input values. So if we remove all the comments and collapse the lines with value and inRangeB the code gets to just 6 lines:

 float AUniverse_StarGenerator::MapRangedClamped(float value, float inRangeA, float inRangeB, float outRangeA, float outRangeB)
 {
     if (outRangeA == outRangeB) return outRangeA;
     if (inRangeA == inRangeB) UE_LOG(YourLog,Fatal,TEXT("inRangeA == inRangeB which will produce one to many mapping"));
     float inPercentage = (value - inRangeA)/(inRangeB - inRangeA);
     if (inPercentage  < 0.0f ) return outRangeA;
     if (inPercentage  > 1.0f ) return outRangeB;
     return outRangeA + inPercentage * (outRangeB - outRangeA);
 }
avatar image fallingbrickwork Jul 16 '18 at 08:46 AM

Many thanks indeed. It was good to see your final code implementation; and really great as a learning exercise!

Cheers!, Matt.

avatar image dZh0 Jul 17 '18 at 07:16 PM

Note that I am NOT a professional programmer. There might be a better solution.

(comments are locked)
10|2000 characters needed characters left
Viewable by all users
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question