Basic formula to calculate decimal between two numbers - javascript

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');
}

Related

How to calculate percentage of width from value between min and max numbers?

I am struggling with simple problem. What is elegant way of achieving this.
I have 2 values minimum and maximum which can be positive or negative and a third value which is between them. Now I want to calculate position(percentage) of other given number, which in my case is the width of element.
For example here I want to find what is position in the range 0-200 that represents value -10 in range -500 to 600 ?
var min = -500;
var max = 600;
var value = -10;
var width = 200;
var positionInWidth = ???;
You can one-liner this but let's do this in a few steps to see how it works:
// first we get the total range
var length = max - min; // 1100
// then we offset the position by the start position
var positionInLength = value - min; // 490
// now we get the percent through the value is in the position
var valuePercent = positionInLength / length;
// or a one liner (value - min) / (max - min);
// Then finally, apply `width percentage`
const positionInWidth = width * valuePercent;
Or as a one liner:
const positionInWidth = width * ((value - min) / (max - min));
So if I understand the question correctly, you are looking for the so called map function. If that's the case, the implementation is as given:
function map(input, inMin, inMax, outMin, outMax) {
return (input - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
}
In your scenario outMin = 0:
console.log(map(-10, -500, 600, 0, 200)); // === 89.0909090909091

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; }

Iterate through numbers between two set min and max values

I am making a javascript loop that makes certain elements change color very subtlely every time the loop runs. The value I am changing is the L value of an HSL color.
Right now I have this.
var h = 10,
s = 0.5,
l = 1 / ((i += 0.01) % 10);
color = hsl(h, s, l);
Right now this outputs colors going from light to dark, starting over whenever it hits black (because of the modulo operation).
The L value must be between 0 and 1, however I would like to cap the outputs of my operation to be between 0.1 and 0.9 to avoid the too dark and too bright colors.
Does anyone know how to tweak the operation to fit these requirements?
Also I would like to know if someone knows how to make the output reverse (instead of start over from the top) every time it hits min or max, meaning that it outputs numbers like this:
0.9...0.89...[...]...0.11...0..1...0.11...[...]...0.89...0.9...0.89 and so on
Thank you!
You can cap the output value between 0 and 1 to between 0.1 and 0.9 by:
cappedValue = 0.1 + 0.8 * uncappedValue
For bouncing the value between 0 and 1 you can use
boundcingValue = Math.abs(1 - 2 * valueBetween0and1)
so the resulting solution would be:
var h = 10,
s = 0.5,
l = 1 / ((i += 0.01) % 10);
lbouncing = Math.abs(1 - 2 * l);
lcapped = 0.1 + lbouncing * 0.8;
color = hsl(h, s, lcapped);
haven't tested it though...

How can I get the current amount of daylight given only the local date/time? (like flux does)

My plan is to give a slight tint to a web page based on the current local time (from javascript). I don't need something as specific as current lumens or anything, but I'd like to get the approximate time for peak sunlight, sunrise, sunset, and mid-night +-2 hours or so. I realize the exact times would vary greatly based on latitude & longitude and also timezone data, which I could potentially have access to. But to start off, I was wondering if there were just a formula for something like [northern] hemisphere and current local time.
How does f.lux do it?
Update 1: Most of my searches have just returned daylight savings related info, which isn't very helpful. I did find this JS: http://www.esrl.noaa.gov/gmd/grad/solcalc/main.js (from here http://www.esrl.noaa.gov/gmd/grad/solcalc/) but it is laden with unexplained magic constants. For example:
function calcGeomMeanLongSun(t)
{
var L0 = 280.46646 + t * (36000.76983 + t*(0.0003032))
while(L0 > 360.0)
{
L0 -= 360.0
}
while(L0 < 0.0)
{
L0 += 360.0
}
return L0 // in degrees
}
function calcGeomMeanAnomalySun(t)
{
var M = 357.52911 + t * (35999.05029 - 0.0001537 * t);
return M; // in degrees
}
function calcEccentricityEarthOrbit(t)
{
var e = 0.016708634 - t * (0.000042037 + 0.0000001267 * t);
return e; // unitless
}
Update 2: I think the "cost" of determining a locale of the user via gps or whatever is too great (especially since this is purely for cosmetic reasons and serves no other functional purpose), so I'm probably just going to stick with the 12am-6am-12pm-6pm cycle of whatever the local time is via javascript.
Update 3: I just went with a slightly modified sine-wave with a small preference towards day-time:
var x, fx, hour,
// starting hsl value for midnight:
h = 220,
s = 42,
l = 75;
for (hour = 0; hour < 24; hour++) {
// 0 for midnight, up to 12 for noon
x = 12 - Math.abs(hour - 12);
// 0.0 to 1.0
fx = x / 12;
// factor of pi, shift x axis by 1 half pi
fx = (Math.PI * (fx - (1 / 2)));
// sine function
fx = Math.sin(fx);
// +1 to start at 0, take half to max out at one
fx = (fx + 1) / 2;
// skew the values just slightly for daytime
fx = Math.pow(fx, 0.75);
// change range back to top out at 12
fx = fx * 12;
// base of 220 degrees, 18.25 provided a nice color rage from bluish to yellowish
h = Math.floor((220 + (18.25 * fx)));
// rotate on 360 degrees
while (h >= 360) {
h = h - 360;
}
// base of 42% saturated, multiplied x for a linear slope up to 100%
s = Math.floor(42 + (5.5 * x));
// 100 max
if (s > 100) {
s = 100;
}
// base of 75% lightness, 1.85 factor was a nice linear slope stopping short of 100%
l = Math.floor(75 + (1.85 * x));
// 100 max
if (l > 100) {
l = 100;
}
// "style='background-color: hsl(" + h + ", " + s + "%, " + l + "%);'"
}
Here it is on JSBin. I may play around with getting the actual amount in the future, but this gets me close enough for now.
Is not this equation from Wikipedia enough:
http://en.wikipedia.org/wiki/Sunrise_equation
There is no magic in it, just a bit of math.
Or do you need something more?

Generating labels for a chart

I'm trying to find an algorithm for generating a Y axis for a chart engine I'm writing and am at the pulling out hair stage.
Searching around yields various solutions however I'm struggling to find one that caters for all data ranges.
Here's what I've got so far:
// Raise the max and lower the min so that we get a prettier looking chart.
var tickRangeMinMax = maxValue - minValue;
var min = tickRangeMinMax * Math.round(minValue / tickRangeMinMax);
var max = tickRangeMinMax * Math.round(1 + (maxValue / tickRangeMinMax));
This gives me a new range for which I'd like to generate a Y axis.
I calculate the distance between each YAxis label as follows:
var ticks = tickRange(min, max, labelCount);
function tickRange(minVal, maxVal, tickCount) {
var range = maxVal - minVal;
var unRoundedTicksSize = range / (tickCount - 1);
var x = Math.ceil(log10(unRoundedTicksSize) - 1);
var pow10X = Math.pow(10, x);
var roundedTickRange = Math.ceil(unRoundedTicksSize / pow10X) * pow10X;
return roundedTickRange;
}
I've also tried calculating the ticks using the much simpler algorithm:
return (max - min) / labelCount
The former method works well with small ranges such as 23 -> 200 however neither of these methods work well for me when I've got a range of say, 0 -> 3000.
In the case of 0 -> 3000 I end up with negative values in some of my labels.
I add labels to the label collection by looping over the labelCount, in my case it's 5, and subtracting the tick range from the previous label value. I start with the max value.
For reference, same as yours only in Python (don't speak JS):
import math
def ticks (low, high, labels):
nlabels = len(labels)
tick = (high - low) / (nlabels - 1)
x = math.floor(math.log10(tick))
pow10x = math.pow(10,x)
tick = int(math.ceil(tick/pow10x)*pow10x)
return zip (range(low,high+tick,tick), labels)
print (ticks (0, 225, ['a', 'b', 'c', 'd', 'e']))
PS. maybe floor on the logarithm is better

Categories