How to get range from 0 - 1 based on two number range - javascript

In Javascript, when I am scrolling, I would like to get 0 - 1 when the scroll.x number is from 300 - 400.
So 300 would be 0, then 400 would be 1, and in between would be 0.1, 0.2.
I am attempting to try in the meantime and will post my attempt as well.

Yep, you'll just want a simple remapping function.
const remap = (
value,
sourceMin,
sourceMax,
destMin = 0,
destMax = 1,
) =>
destMin +
((value - sourceMin) / (sourceMax - sourceMin)) *
(destMax - destMin);
console.log(remap(300, 300, 400));
console.log(remap(400, 300, 400));
console.log(remap(350, 300, 400));
outputs
0
1
0.5
Note that this function does not clamp the output values to the range you specify; if you specify out-of-range inputs, you'll get out-of-range outputs.

You could subtract the start value and divide by 100 (the delta of the two values) for getting a value between zero and one.
function f(x) {
return (x - 300) / 100;
}
console.log(f(300)); // 0
console.log(f(350)); // 0.5
console.log(f(400)); // 1

Related

Represent a number with a color, difference not enough for percentage based rgb

I have an array (1200 values) of numbers
[123, 145, 158, 133...]
I'd like to have a div for each value with a background color from red to green, red being the smallest number and green the largest.
The base setup looks like this: (templating with vuejs but unrelated to the problem)
const values = [123, 145, 158, 133...]; // 1200 values inside
const total = values.length;
<div
v-for="(val, i) in values"
:key="i"
:style="{backgroundColor: `rgb(${(100 - (val*100/total)) * 256}, ${(val*100/total) * 256}, 0)`}">
{{val}}
</div>
I'm not a maths specialist but since all my numbers are around 100, the rgb generated is the same. (around 12% yellowish color)
How can I give more weight to the difference between 137 and 147?
EDIT: final formula:
:style="{backgroundColor: `rgb(${(256/(maxValue-minValue) * (boule-maxValue) - 255)}, ${(256/20 * (boule-maxValue) + 255)}, 0)`}"
Checkout this post: https://stats.stackexchange.com/questions/70801/how-to-normalize-data-to-0-1-range.
Basically you want to linearly rescale your values to another interval. You need your current min and max values from the array. Then define the new min' and max' which are the limits of the new interval. This would be [0, 255] in your case.
To do the transformation use the formula:
newvalue= (max'-min')/(max-min)*(value-max)+max'
As an example:
If your min value is 127 and max is 147, and you want to map 137. Then:
256/20 * (137-147) + 255 which results in 127.
If you want to map 130. Then:
256/20 * (130-147) + 255 = 37.4.
It really depends on what meaning those values actually have
However, you can try this: if your values are always bigger than 100 and always less than 150 (you can choose these number of course) you can "stretch" your values using the values as minimum and maximum. Let's take 137 and 147 as examples:
(val-min) : (max-min) = x : 255
(137-100):(150-100) = x:255 -> 37:50 = x:255 -> 188
(147-100):(150-100) = x:255 -> 47:50 = x:255 -> 239
That is for the math. In the end, this is the calculation:
newValue = (val-min)*255/(max-min)
where min and max are your chosen values.
You could take a kind of magnifier for a range of data. In this example, the values between 20 and 30 are mapped to a two times greater range than the outside values inside of an interval of 0 ... 100.
function magnifier(value, start, end, factor) {
var middle = (start + end) / 2,
size = (end - start) * factor / 2,
left = middle - size,
right = middle + size;
if (value <= start) return value * left / start;
if (value <= end) return (value - start) * factor + left;
return (value - end) * (100 - right) / (100 - end) + right;
}
var i;
for (i = 0; i <= 100; i += 5) {
console.log(i, magnifier(i, 20, 30, 2));
}
.as-console-wrapper { max-height: 100% !important; top: 0; }

Basic formula to calculate decimal between two numbers

I'm having a brain fart and need help with a basic formula.
if (d >= 0.1) {
scale = 0.1
} else if (d <= 0.004) {
scale = 1;
}
How can I set the value of scale to somewhere between 0.1 and 1 if the value of d is somewhere between 0.1 and 0.004?
You can add an else statement to handle the numbers between 0.004 and 0.1:
// ...
else {
scale = 1 - (d - 0.004) * (1 - 0.1) / (0.1 - 0.004);
}
Or you can do the whole thing in one go:
var scale = 1 - (Math.max(Math.min(d, 1), 0.004) - 0.004) * (1 - 0.1) / (0.1 - 0.004);
The scaling should look like this:
scale = 1 - (1 - 0.1)*(d - 0.004)/(0.1 - 0.004)
or more simply
scale = 1 - 9.375*(d - 0.004)
For example, if d = 0.05, then scale = 0.56875.
If I understand correctly from the comments, this is the pattern for your calculation. dRange is the range of d values. The input values are limited to also restrict the output.
Note: The input values d are first restricted, so that we have 2 absolute 'scales' that we can mirror: dRange and scaleRange, and their corresponding min- and max-values.
Note: The inverted scaleRange, where 1 (max value) is to the left, and 0.1 (min value) is to the right:
0.004 <-- dRange --> 0.1
~~~~~~~~~~~~|--------------------------------------------------------------|~~~~~~~~~~~~
In: ~~~d~~~~~d----------------------d---------------------------------------d-----d------
| | | | |
----->| | |<----
| | |
Limited: [d,d]--------------------d-------------------------------------[d,d]
| | |
Out: [x,x]--------------------x-------------------------------------[x,x]
~~~~~~~~~~~~|--------------------------------------------------------------|~~~~~~~~~~~~
1 <-- scaleRange --> 0.1
That's some of the more complex logic that deserves a slightly more declarative approach. I came up with the following to implement the scenario above. Further explanation is in the code comments:
// The minimum and maximum values for d - also the 'input'
const minDValue = 0.004;
const maxDValue = 0.1;
// The minimum and maximum values for scale - also the 'output'
const minScaleValue = 0.1;
const maxScaleValue = 1;
// Absolute ranges for d and scale:
const dRangeAbs = maxDValue - minDValue;
const scaleRangeAbs = maxScaleValue - minScaleValue;
// restricts the value of d if it exceeds the bounds:
// 'minDValue <= d <= maxDValue'
function restrictD(d) {
return Math.max(minDValue, Math.min(maxDValue, d));
}
// Calculate the absolute scale value for a certain d-value.
function scaleForDValue(d) {
// First, restrict the input. If not restricted the input 'scale' is
// not accurate, and we can not determine the corresponding scale.
d = restrictD(d);
// Determine how far (as fraction of 1) the d-value sits along
// the absolute d-range:
const dProgressFraction = (d - minDValue) / dRangeAbs;
// Use the d-progress-fraction to add a corresponding relative
// progress value for the scale-'scale'. Because the scale is
// inverted, we subtract the progress from the max-value this
// time.
const calculatedScale = maxScaleValue - (dProgressFraction * scaleRangeAbs);
// Restrict calculated value to prevent rouding error.
return Math.min(maxScaleValue, Math.max(minScaleValue, calculatedScale));
}
// Log some values!
const log = (val, info) => console.log({ d: val, scale: scaleForDValue(val), info });
log(minDValue, 'this is the min d-value');
log(minDValue - 1, 'this is a too low d-value');
log(maxDValue, 'this is the max d-value');
log(maxDValue + 1, 'this is a too high d-value');
for (let i = 0; i < 5; i++) {
log(minDValue + Math.random() * dRangeAbs, 'A random d-value');
}

What's the easiest way to convert a number to a percentage in JavaScript?

I'm calculating the difference in size of images after they've been resized by the user. I take the images new width and divide it by the natural width. This is the code:
Math.round( (img.width / naturalWidth) * 100) / 100
The numbers I get as a result can look like the following (and the numbers commented out are what I'd like to convert them to).
0 // 0%
1 // 100%
1.2 // 120%
1.39402 // 139%
1.39502 // 140%
21.56 // 216%
0.4 // 40%
0.44 // 44%
0.1 // 10%
0.01 // 1%
0.005 // 1%
0.0049 // 0%
Never negative numbers. I need to round these numbers and then convert them into strings represented as percentages. Is there an easy and straightforward way to accomplish this?
You can use Math.round like this:
Math.round((img.width/ naturalWidth) * 100));
A simple example:
var a = 1.2;
var b = 1;
alert(Math.round((a / b) * 100) + '%'); // 120%
This should do the trick:
const formatAsPercentage = x => `${Math.round(x * 100)}%`
You can use it as:
formatAsPercentage(.05) // => "5%"
First multiply the number by 100 then use Math.round() to round the result. Finally, add the percent sign:
Math.round(img.width / naturalWidth * 100) + "%";
I've used in own project
function calculatePercent(config){
var currentProgressPercent;
var totalRecords = Number.parseFloat(config.totalRecords);
var current = Number.parseFloat(config.current);
currentProgressPercent = 0;
if (!(isNaN(totalRecords) || isNaN(current))) {
currentProgressPercent = (totalRecords === 0 ? 100 : Math.round((current / totalRecords) * 100));
}
currentProgressPercent += '%';
return currentProgressPercent;
}
var input = [0, 1, 1.2, 2.156, 0.4, 0.44, 0.1, 0.01, 0.005, 0.0049];
input.forEach(function(value){
alert(calculatePercent({current:value, totalRecords: 1}));
});
You might do some refactoring for your needs in variable names.

Best way to generate random number between x and y but not between a and b

I have a canvas that is 1000x600px. I want to spawn sprites outside the canvas (but evenly distributed).
What is the best way to retrieve random values between (-500, -500) and (1500, 1100) but not between (0, 0) and (1000, 600)? I understand a while loop could be used to generate numbers until they are in range but that seems superfluous. Thanks.
If you want to generate a number between -500 and 1500, excluding 0 to 1000, you can just generate a number between 0 and 1000 ( 0 - -500 + 1500 - 1000).
If the number is less than 500, you subtract 500; if the number is greater or equal to 500, add 500.
Or, more generically:
function randomInt(outerMin, outerMax, innerMin, innerMax)
{
var usableRange = innerMin - outerMin + outerMax - innerMax,
threshold = innerMin - outerMin,
num = Math.floor(Math.random() * (usableRange + 1));
if (num < threshold) {
return num - threshold;
} else {
return num - threshold + innerMax;
}
}
randomInt(-500, 1500, 0, 1000);
For two-dimensional points you have to get more creative. First, you generate two points that ARE inside the forbidden area and then spread those values to the good areas:
function randomVector(outer, inner)
{
var innerWidth = inner.right - inner.left,
innerHeight = inner.bottom - inner.top,
x = Math.floor(Math.random() * (innerWidth + 1)),
y = Math.floor(Math.random() * (innerHeight + 1)),
midx = Math.floor(innerWidth / 2),
midy = Math.floor(innerHeight / 2);
if (x < midx) { // left side of forbidden area, spread left
x = x / midx * (inner.left - outer.left) - inner.left;
} else { // right side of forbidden area, spread right
x = (x - midx) / midx * (outer.right - inner.right) + inner.right;
}
if (y < midy) { // top side of forbidden area, spread top
y = y / midy * (inner.top - outer.top) - inner.top;
} else { // bottom side of forbidden area, spread bottom
y = (y - midy) / midy * (outer.bottom - inner.bottom) + inner.bottom;
}
// at this point I'm not sure how to round them
// but it probably should have happened one step above :)
return {
x: Math.floor(x),
y: Math.floor(y)
}
}
randomVector({
left: -500,
top: -500,
right: 1500,
bottom: 1100
}, {
left: 0,
top: 0,
right: 1000,
bottom: 600
});
Important
This works because the areas outside of your "forbidden" area are equal in their respective dimension, i.e. padding-top == padding-bottom && padding-left == padding-right.
If this will be different, the distribution is no longer uniform.
Generate a random number between 0 and 1000, if its over 500 add 500 (or 600 respectivly) if not negate it.
Instead of having a set of forbidden rectangles, you could calculate a set of allowed rectangles. To get a random position inside any allowed rectangle, you first choose a random rectangle and then a random position inside that chosen rectangle.
When the retangles don't have an equal size, you need to weight them by area, otherwise smaller rectangles will have a higher density than larger ones (a 200x100 rectangle needs to be 100 times as likely as a 10x20 rectangle).
Just generate those numbers between (0,0) and (1,1) and then use some linear function to do the mapping.
Otherwise, divide the area where you want the random coordinates to fall in rectangles. Let's say you obtain N such rectangles. Each of those rectangles may be populated through mapping the output of a random generator betweeen (0,0) and (1,1) to that rectangle (this is a linear mapping).

JavaScript - random multiple of 20 between 0 - 580

In Javascript, how do I create a random even number multiplied by 20 between 0 - 580?
E.g.: 220, 360, 180, 0 (min), 400, 200, 580 (max)
You want increments of 20, so what you really need is an integer in the range 0 to 29, and then multiply with 20. Example:
var max = (580/20) + 1;
var result = 20 * (Math.floor(Math.random())*max)
We are adding one to max, because Math.random() is a uniformly distributed number between (inclusive 0) and (exclusive 1), so since we use Math.floor, the maximum must be 1 larger.
This way creates a random number, then rounds it down to the nearest multiple:
When you need 0 <= randomMultiple <= max
var random = Math.random() * (580 + 20);
randomMultiple = random - (random % 20);
When you need 0 <= randomMultiple < max
var random = Math.random() * 580;
randomMultiple = random - (random % 20);
Use a principle like this: Generate random number between two numbers in JavaScript
Keeping in mind that if you want your max result to be 580, then the maximum integer you want to multiply by 20 would be 29 (or 580/20). Then just add some logic to make sure the integer is even.
Ta da!
Try use this:
var result = parseInt(Math.random()*30)*20;
29*20 = 580
Math.random() return [0..1)
result between 0..580, step by 20
Here is a generic javascript one-liner that can be used for any range, and any multiple.
Essentially, what we are trying to do here is figure out a range, 0 to N, which when multiplied by our given multiple stays within the range [0,max-min].
In this case N, is simply, (max - min)/multiple, or range/multiple.
Once we have N, we can use Math.random() to get a random number between 0-N, and multiply it with multiple. Next, we just add min.
We assume that min and max are already multiples of multiple.
Note the additional +1 to the input of Math.random() is because Math.random() returns a number between 0 (inclusive) and 1 (exclusive). So, Math.random() can never return 1. If we didn't account for that we would never be able to include the max number in our results.
/*
* Returns a random number within range [min,max]
*
* min and max must be multiples of multiple
* (note that 0 is a multiple of all integers)
*/
function randomMultiple (min, max, multiple) {
return Math.floor(Math.random() * (((max - min)/multiple)+1)) * multiple + min;
}
console.log(randomMultiple(0, 580, 20));

Categories