Interpolating coordinates by a 200ms time-step in javascript - javascript

Below is a 2D array that represents movement of an entity over a 2 second period:
[[10, 200, 0], [70, 170, 600], [110, 150, 1000], [155, 120, 1600], [155, 120, 2000]]
Each array elements contains x-coordinate, y-coordinate, and timestamp in such order.
I need to transform this data which shows location at sporadic moments in time to an array showing the location at a fixed rate of every 200ms. I need to interpolate the missing values.
I know the correct output is:
10 200 0
30 190 200
50 180 400
70 170 600
90 160 800
110 150 1000
125 140 1200
140 130 1400
155 120 1600
155 120 1800
155 120 2000
How can I achieve this?

FINAL EDIT HOPEFULLY
I think I understand the question now thanks to #j08691 and #ISAE
EDIT #Gershy pointed out that the 200ms step asked was hardcoded into my solution so I changed it to a variable.
Here is the NEW JSFiddle.
Here is the code:
const data = [
[10, 200, 0],
[70, 170, 600],
[110, 150, 1000],
[155, 120, 1600],
[155, 120, 2000]
];
const STEP = 200;
// log the starting position first
console.log("" + data[0][0] + " " + data[0][1] + " " + data[0][2] + "\n")
for (let i = 0; i < data.length - 1; i++) {
const array1 = data[i];
const array2 = data[i + 1];
//find how many updates you need to print for each comparison
const n = ((array2[2] - array1[2]) / STEP);
const xMovement = (array2[0] - array1[0]) / n;
const yMovement = (array2[1] - array1[1]) / n;
for (let j = 1; j <= n; j++) {
console.log("" + (array1[0] + xMovement * j) + " " +
(array1[1] + yMovement * j) + " " +
(array1[2] + STEP * j) + "\n");
}
}
EDIT
Ok I confused myself and others with this one a lot so I worked all of the way through it on JSFiddle.
I definitely works now.
___________
The problem just wants you to iterate through the 2D Array and log it to the console.
I don't want to do your homework for you but you'll need nested loops to iterate each and print. When you get to the end of an inner array, print a newline character as well. [this was rude...sry]
EDIT
Ok since someone downvoted me, here you go:
The iteration:
const data = [
[10, 200, 0],
[70, 170, 600],
[110, 150, 1000],
[155, 120, 1600],
[155, 120, 2000]
];
let n = 0
data.forEach(array => {
setTimeout(() => {
let line = ""
array.forEach(item => line += item + " ");
console.log(line + "\n");
}, n += 200);
})

The assessment is asking you to calculate the movement of elements for every 200ms period.
Taking the first and second elements for example, you have: [10, 200, 0], [70, 170, 600] which means that at the start (0ms) the element is in XY position of 10 & 200 respectively. after 600ms (2nd element) it is in XY position of 70 & 170. So the element has moved 60 X points (70 - 10) and -30 Y points (200 - 170).
You'll need to find the ms difference between each element and the next, find it's divisor to 200ms blocks, and calculate the position it should be at each 200ms.
To continue with the example, the difference between element 1 and 2 in ms (3rd number in the element) is 600ms which is 3 200ms blocks, so 60 X points divide to 3 (3 200ms steps) is 20. adding the start position of 10, you'll get 30, 50 and 70. -30 Y points divides into -10 movement in each 200ms timeframe, so it'll be 190, 180 and 170. And so on.

This is a matter of interpolation. We need to log the position every 200ms. Consider the 600th millisecond; it is very easy to log, because there is an entry for exactly the 600th millisecond - but the 800th millisecond is not as trivial, as it lands between the entries for 600ms and 1000ms. How do we log it? We take a weighted average of the two surrounding values. Note that this is called a "linear interpolation" (and is only one of several ways of filling in blanks in a dataset).
The following interpolate function allows any sequence to be interpolated using any timestep. The only restriction is that the data array must be sorted in ascending order by its millisecond value (already the case for your data).
let data = [
[10, 200, 0],
[70, 170, 600],
[110, 150, 1000],
[155, 120, 1600],
[155, 120, 2000]
];
let interpolate = (data, stepSize=200) => {
let result = [];
for (let ms = 0; true; ms += stepSize) {
// Find the two entries to either side of the current `ms`
// E.g. if current `ms` is 800, should return the 600ms and
// 1000ms entries. If `ms` is exactly equal to a particlar
// entry, `e1` and `e2` are *both* set to that entry.
// If we can't find both an entry before and after, return
// `null` for both entries.
let [ e1, e2 ] = (() => {
for (let i = 0; i < data.length; i++) {
// Can an exact match be found?
if (data[i][2] === ms)
return [ data[i], data[i] ];
// Do consecutive entries contain the current `ms`?
if (data[i + 1] && data[i][2] < ms && data[i + 1][2] > ms)
return [ data[i], data[i + 1] ];
}
return [ null, null ];
})();
// Stop once no more entries can be found
if (e1 === null) break;
// Get the distance between entries
let msDist = e2[2] - e1[2];
if (msDist === 0) {
// Handle an exact match by including the exact entry
result.push([ ...e1 ]);
} else {
// Handle moments between entries with an interpolated
// average:
// Get distance between `e1` and `ms`, and between `e2`
// and `ms`
let dist1 = ms - e1[2];
let dist2 = e2[2] - ms;
// Take the interpolated average. The bigger `dist2` is
// the more `e1` plays into the value. The bigger
// `dist1` is, the more `e2` plays into the value.
let mult1 = 1 - (dist1 / msDist);
let mult2 = 1 - (dist2 / msDist);
result.push([ e1[0] * mult1 + e2[0] * mult2, e1[1] * mult1 + e2[1] * mult2, ms ]);
}
}
return result;
};
// Do the interpolation
let interpolated = interpolate(data);
// Log the resulting items
console.log(interpolated.map(([ x, y, ms ]) =>
`${ms}ms: (${Math.round(x)}, ${Math.round(y)})`
));

Related

Create array with numbers +10/-10, where middle one is 0, depending on what length I want

I need to dynamically create array, knowing only how long I want it to be. So for example I need array to be 3 long so I need array = [-10, 0, 10], if I need array 9 long it will be [-40, -30, -20, -10, 0, 10, 20, 30, 40] etc. How can I do it automatically?
You can simply get the result using Array with fill
Since you only want 0 at the center that input should be odd.
const end = 9;
let start = Math.floor(end / 2);
const result = [
...Array(start).fill(0).map((_, i) => start * -10 + i * 10),
0,
...Array(start).fill(0).map((_, i) => (i + 1) * 10)
]
console.log(result)
Another solution with Array.from:
const createArray = (length) => Array.from({length}, (el, i) => Math.round(i - length / 2) * 10);
console.log(createArray(1))
console.log(createArray(3))
console.log(createArray(9))

How to generate array of n equidistant points along a line segment of length x?

I ported this function from a python answer to a similar question on this site. However, although the points are equidistant from each other, they are not centered on the line.
The first point is at 0 while the last is at 83 (the end of the line segment is 100).
How do I center this row of points?
Specifically, I want the first point to be the same distance from 0 that the last point is from 100.
'use strict';
function generatePoints(count, length) {
const points = []
for (let i = 0; i < count; i++) {
const a = i / count;
const x = a * length;
points.push(Math.round(x));
}
return points;
}
const points = generatePoints(6, 100);
console.log(points);
// [ 0, 17, 33, 50, 67, 83 ]
I tried this, but it doesn't appear to work as I expected:
for (let i = 0; i < points.length; i++) {
points[i] += (points[1] / 2);
points[i] = Math.round(points[i]);
}
console.log(points);
// [ 9, 26, 46, 63, 80, 96 ]
The first point is 9 away from 0, but the last point is 4 away from 100.
Change i / count to (i + 1) / (count + 1).

How to determine the fewest number of Y increments needed to plot a multiple of 5 on a graph?

Using JavaScript, an array of numbers is going to be charted on a graph (for my purposes it is a spline chart):
[12, 22, 25, 38, 47]
I want all the Y axis values to be multiples of 5. I have the Y axis capped at the next multiple of 5 that occurs after the highest number in the array. Since 47 is the highest number, the next multiple of 5 is 50 (call that value the "cap"), and that is the top value ("tick") on the chart's Y axis. After figuring that out, I know that the Y axis should be 0 a the bottom, and 50 at the top, but I want to override the default behavior and tell it exactly how many ticks to show in between, and what the values should be for those ticks.
This is where it gets tricky, because of the following restrictions:
Use the fewest number of ticks possible (0, the max value, and at least one tick in between)
Bottom value is always zero
All Y tick values are multiples of 5
Y ticks are evenly spaced on the axis
For the previous example, fifty is the cap, which is divisible by two, so the Y axis would only need one tick in between the bottom and top, resulting in three tick values of 0, 25, 50. The function I am trying to build would receive 50 as an argument, and output 3 as the result. Then I would know the chart needs 3 ticks, and I could generate it like so:
My question is, given that a charted value can be any multiple of 5, how can I calculate the fewest number of ticks needed on the Y axis, using only increments that are multiples of 5? It may be easiest to just show the first few scenarios to illustrate how the pattern is not (at least to me) obvious:
value = tick1, tick2, tick3, etc. >> return count of ticks
05 = 5, 4, 3, 2, 1, 0 >> return 6;// This case is an outlier and can be set manually
10 = 10, 5, 0 >> return 3;
15 = 15, 10, 5, 0 >> return 4;
20 = 20, 10, 0 >> return 3;
25 = 25, 20, 15, 10, 5, 0 >> return 6;
30 = 30, 15, 0 >> return 3;
35 = 35, 30, 25, 20, 15, 10, 5, 0 >> return 8;
40 = 40, 20, 0 >> return 3;
45 = 45, 30, 15, 0 >> return 4;
50 = 50, 25, 0 >> return 3;
55 = 55, 50, 45, 40, 35, 30, 25, 20, 15, 10, 5, 0 >> return 12;
It was at this point that I realized there is probably an equation or function that exists to address this dilemma, maybe even something to do with the Fibonacci sequence or Dan Brown. I couldn't find any related SO questions, and my use of "increments of 5" may make this use case too specific to return google results on the general principle, so any advice is appreciated.
You could take an iterative approach by using the fifth of the value an checke the abulity for the division by 2, 3, 5, 7 and so on and return this value incremented by one.
const
fn = v => {
v /= 5;
if (v === 1) return 6;
if (v % 2 === 0) return 3;
var i = 1;
while ((i += 2) < v) if (v % i === 0) return i + 1;
return v + 1;
},
format = s => s.toString().padStart(2);
var values = Array.from({ length: 12 }, (_, i) => (i + 1) * 5),
data = values.map(fn);
console.log(...values.map(format));
console.log(...data.map(format));

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

Heart Rate Monitor

Okay, so I followed a previous post on creating a hear rate monitor and tweaked it a bit to fit my web design. Instead of setting Var Data how would I randomize the numbers between 1 and 300 over and over again until it reaches 200 random numbers total and draws them? Thanks for your time. This is my code but I took out most of the Var Data as it's 200 numbers long!
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#dbbd7a";
ctx.fill();
var fps = 60;
var n = 1;
var data = [
110, 149, 89, 150, 117, 150, 143, 82, 12, 92, 144, 73, 82, 200,
177, 149, 199, 116, 88, 105, 123, 12, 82, 72, 66, 15, 177, 182,
199, 116, 159, 150, 100, 10, ];
drawWave();
function drawWave() {
setTimeout(function() {
requestAnimationFrame(drawWave);
ctx.lineWidth = "1";
ctx.strokeStyle = 'green';
n += 1;
if (n >= data.length) {
n = 1;
}
ctx.beginPath();
ctx.moveTo(n - 1, data[n - 1 ]);
ctx.lineTo(n, data[n]);
ctx.stroke();
ctx.clearRect(n+1, 0, 10, canvas.height);
}, 1000 / fps);
}
</script>
The essence would be something like this: an array of size 200, since you want 200 values, and using a loop randomly populate the values.
Math.random will generate a number between 0 (inclusive) and 1 (non-inclusive), multiplying by 300 will give you anything between 0 and 299.99... Math.floor() removes the decimal places (range becomes 0 and 299); so we add 1 in the end to get the 1 to 300 range you wanted.
Hope this helps
var data = [];
for(var i = 0; i < 200; ++i) {
data[i] = Math.floor(Math.random() * 300) + 1;
}
with an elegant one-liner:
var data = Array.apply(null, Array(200)).map(function(){
return Math.floor(Math.random() * 299) + 1;
});
the above will work with the sparse / falsy val els in the array.
One easy way to do this is the following:
// creating a new Array with a length of 20 (obviously
// adjust the '20' to '200' for your own use-case):
var arr = new Array(20);
// using Array.prototype.fill() to assign a value (an
// empty String) to each array element, in order that
// Array.prototype.forEach() can iterate over each array
// element:
arr.fill('').forEach(function(needle, i, haystack){
// the arguments are automagically available to the
// the anonymous function;
// needle: the current array-element of the Array,
// i: the index of the current array-element,
// haystack: the Array itself.
// here we set the array value using bracket notation:
haystack[i] = Math.floor(Math.random() * (300 - 1) + 1);
});
// outputting the array to the console:
console.log( arr );
var arr = new Array(20);
arr.fill('').forEach(function(needle, i, haystack) {
haystack[i] = Math.floor(Math.random() * (300 - 1) + 1);
});
// Setting the array as the text of the <body> element,
// given the lack of a Snippet console:
document.body.textContent = arr.join(', ') + '.';
JS Fiddle demo.
The portion of the posted solution that generates the random numbers within a range:
Math.floor(Math.random() * (300 - 1) + 1)
was effectively borrowed from an answer elsewhere on the site (https://stackoverflow.com/a/1527820/82548, written by Ionut G. Stan), and is explained in that answer.
However, briefly, it generates a random number between 0 and 1; multiplies that by 299 (300 - 1), so that the integer portion becomes a number between 0 and 299; and then 1 is added to ensure the integer is now between 1 and 300.
Afterwards we apply Math.float() to round that random number to the integer portion of the number. The answer I've linked to, above, explains it far more completely.
References:
JavaScript:
Array.
Array.prototype.fill().
Array.prototype.forEach().
Math.floor().
Math.random().
String.prototype.join().
Stack Overflow:
Generating random whole numbers in JavaScript in a specific range?

Categories