How to generate a line with exact number of points? - javascript

I have to generate a path from a dataset of n points.
I am plotting a cubic spline through the points in this dataset.
The generated path must contain an exact number of projected path-points.
My problem is not with the plotting of the curve but rather with the distribution of the path-points along the x-axis to yield a path that is made up of an exact number of path-points. This is why I have reduced the following example to a one dimensional array of points through which a straight line should be plotted. Each point in the dataset should represent the beginning of a curve segment (even though the curve is really a line because of the simplification).
My current naive approach is not exact ie it does not yield a path that contains the specified number of points (it's off by 4-5 points depending on the density of the dataset and the specified targetLength).
I think I'll have to use linear interpolation to get an exact result but I don't know how. Can anyone help or point me in the right direction?
Naive approach (javascript):
// Array with floating point values constrained between 0 - 1
// Think of each value as the beginning of a line segment.
const dataset = [0, 0.123, 0.3432, 0.454, 0.56, 0.8334, 0.987, 1];
// Path should have this many points
const targetLength = 1024;
// Step distance between points
const delta = 1 / targetLength;
// The path array we're generating
const path = [];
// For each point (segment)
for (let i = 0; i < dataset.length - 1; i++) {
const x1 = dataset[i]; // current point
const x2 = dataset[i + 1]; // next point
const xd = x2 - x1 - delta; // dist between current and next point(-delta)
// For each step in the segment generate a path-point
for (let k = 0; k <= xd; k += delta) {
// For this example we're only pushing the x-value onto the array.
// In the real implementation I'm calculating a y-value to plot a curve
// and push an array of [x, y] onto the dataset.
path.push(dataset[i] + k);
}
}
// expect: path.length === targetLength
console.log(path.length);
In the above example I expect path.length to equal targetLength (1024).
I could take the generated path as a whole and interpolate the entire array but I think I'm looking for a smarter way to generate the path in the first place. Any help is greatly appreciated!

It really looks like what you want is just this:
for (let i=0; i<targetLength; ++i) {
path.push(i / (targetLength-1));
}
... because this is what you are approximating, and it actually makes sense for some kinds of cubic spline interpolation. You wouldn't normally need to store these path points, however, because they are so easy to calculate.

Related

Figuring out the value for PI

Let's say I have a function called bars()
bars () {
const bars = []
for (let i = 0; i < this.numberOfBars; i++) {
bars.push(Math.sqrt(this.numberOfBars * this.numberOfBars - i * i))
}
return bars
}
If I'm reducing the bars array to approximate PI, what should be on the right side of the arrow function?
PI = bars().reduce((a, b) =>
I tried adding the values and dividing by the number of bars, but I'm not getting anywhere near the approximation of Pi. I feel like there's a simple trick that I'm missing.
Your funcion seems to list lengths of "bars" in a quarter of a circle, so we have to add them all up (to have the area of the quarter of a circle), then multiply by 4 (because there is 4 quarter) and the divide by this.numberOfBars ^ 2 because area = π * r^2, but like we have to know the radius, it is better using a pure function :
// Your function rewritten as a pure one
const bars = numberOfBars => {
const bars = []
for (let i = 0; i < numberOfBars; i++) {
bars.push(Math.sqrt(numberOfBars * numberOfBars - i * i))
}
return bars
}
// Here we take 1000 bars as an example but in your case you replace it by this.numberOfBars
// Sum them all up, multiply by 4, divide by the square of the radius
const PI = bars(1000).reduce((g, c) => g + c) * 4 / Math.pow(1000, 2)
console.log(PI)
/** Approximates PI using geometry
* You get a better approximation using more bars and a smaller step size
*/
function approximatePI(numberOfBars, stepSize) {
const radius = numberOfBars * stepSize;
// Generate bars (areas of points on quarter circle)
let bars = [];
// You can think of i as some point along the x-axis
for (let i = 0; i < radius; i += stepSize) {
let height = Math.sqrt(radius*radius - i*i)
bars.push(height * stepSize);
}
// Add up all the areas of the bars
// (This is approximately the area of a quarter circle if stepSize is small enough)
const quarterArea = bars.reduce((a, b) => a + b);
// Calculate PI using area of circle formula
const PI = 4 * quarterArea / (radius*radius)
return PI;
}
console.log(`PI is approximately ${approximatePI(100_000, 0.001)}`);
There is no reason to push all terms to an array, then to reduce the array by addition. Just use an accumulator variable and add all terms to it.
Notice that the computation becomes less and less accurate the closer you get to the end of the radius. If you sum to half of the radius, you obtain r²(3√3+π)/24, from which you can draw π.
(Though in any case, this is one of the worst methods to evaluate π.)

Javascript matrix check neighbor state to find edges

i am making a virtual Hama beads tool online. (its a kids toy where you put plastic beads on a matrix and iron them to make them stick together and form a solid shape)like this one below (real hama)
So far i managed to make this work (you can try it out there : https://static.club1.fr/alixturcq/hama%20dev/ ) but i would like to make it more elaborate and have the beads on the edges more realistic with round corners where there are no neighbors (virtual hama)
This would mean having a special image for each of theses neighbor cases like this (neighbor_cases)
So each bead is an object, and i have added a "neighbor" parameter to display the correct image.
Every object is automatically placed on the matrix at start but "isOn" is false, so nothing is displayed. When I click on the matrix, I change the 'isOn' boolean to true and the hama bead displays.
The objects are stored in a 1D array called hamas[], but are being displayed as a 2D array, so i can manipulate it as 2D.
class hama {
constructor(isOn, posx, posy, color, neighbor) {
this.posx = posx;
this.posy = posy;
this.color = color;
this.isOn = isOn;
this.neighbor = neighbor;
this.variation = int(random(0,6));
map
}
display() {
if (this.isOn == true) {
if (ironed == true) {
image(hamas_img[this.color][this.neighbor], this.posx, this.posy, CELL_SIZE, CELL_SIZE);
}
else {
image(ironed_hamas_img[this.color][this.neighbor], this.posx, this.posy, CELL_SIZE, CELL_SIZE);
}
}
}
}
Every time I place a bead i need the program to check every objects if they are On, and also what is the 'isOn' state of each of the neighbors in the matrix.
So far I've done this in pseudo-code but I hope there are smarter ways.
// creating an array of neighbor indexes (clockwise around the object)
checkAround[(+1); (width+1); (width); (width-1); (-1); (-width-1); (-width); (-width+1)]
// creating an empty string that is going to be the neighbor code
neighborCode= "";
// check around object to get neighborstate
for (int i=0; i<checkAround.count; i++){
if (hammas[hammaIndex+checkAround[i]].isOn==true){
neighborCode += "O"
}
else {
neighborCode += "X"
}
}
Then I get neighbourCode strings looking like "XOOXOXOO" and map all these code to appropriate neighbour state number and display the correct image.
So this is rather twisted, I was hoping someone could help me find a smarter solution !
And also I have to find a way to avoid checking non existing indexes on the edges of the matrix, but this should not be to hard to find.
Thanks in advance
Al
What you are looking for is edge detection on a pixel array and has been solved many times before. You can solve it yourself and it's fun to figure out yourself.
When a problem exists in 2D space, it often helps to think in 2D.
But your data is structured in 1D (which is fine, and the convention)
Each pixel/bead has a position in the 1D array -let's call that i- and a position in 2D space, defined by x and y.
The relationship between those is like this: i = x + y * w
( Where w is the width of the image)
With this information you can traverse the 1D array through x and y quite easily and decide for each pixel what its neighbours are doing.
example:
for(var x = 0; x < width; x++){
for(var y = 0; y < height; y++){
var i = x + y * width; // id of the current pixel being checked.
// Calculate neighbour ids:
var iNorth = x + (y - 1) * width;
var iSouth = x + (y + 1) * width;
var iEast = x + 1 + y * width;
var iWest = x - 1 + y * width;
var iNorthWest = (x - 1) + (y - 1) * width; // haha Kanye
// etc.. you get the point
// Now encode all those vars in one single byte
// Use that byte to choose the right image for the bead at position I
// Don't forget to deal with your edge cases
}
}
You can optimize later. (eg. You don't have to update the whole image each time you change one pixel, only its neighbors are affected.)
Your "OXOOXOOO" string is a perfectly acceptable way of storing the different states of a bead. A more nerdy approach would be to just store bits in a 8 bit byte.(because each pixel has only 8 neighbours..)
Those bytes can be stored in a buffer with the same exact structure as your actual pixel data.
I think this should get you started without spoiling too much of the fun?

Arranging rectangles in a circle with equal distance

I'm writing a program that renders mind maps. So far, I've succeeded in drawing the root node and the first level of child nodes around it, in a circle.
This is the code that renders the mind map nodes:
const root = data.find((node) => node.parent === undefined);
const level1 = data.filter((node) => node.parent === root.id);
root.x = 0;
root.y = 0;
root.level = 0;
await addMindMapNode(scene, root);
const radius = 2;
const slice = (2 * Math.PI) / level1.length;
for (let i = 0; i < level1.length; i++) {
const level1node = level1[i];
level1node.level = 1;
const angle = slice * i;
const x = root.x + radius * Math.cos(angle);
const y = root.y + radius * Math.sin(angle);
level1node.x = x;
level1node.y = y;
await addMindMapNode(scene, level1node);
}
You can find the whole program on CodeSandbox.
The output looks like this (with different number of child nodes, from 1 child to 9 children):
As you can see, depending on the number of child nodes, the distribution of the child nodes around the root node in the center looks more or less uneven and not pleasing to the eye, especially with 3, 7 or 9 child nodes.
This is because of the rectangular format of the child node boxes. The center points of the boxes have the exact same distance between them, but the length of the distance (shown as red line segments below) is different, depending on position:
I have to find a way to make it so that the red circle sections are equal in size.
So my question is:
How can I calculate the angle at which to render each purple box so that the distance between each box looks to be the same (i.e. take into account the width and height of each box)?
Okay, here's my attempt (with diagrams too!) 😅:
So I noticed that the problem was that the calculated angles being used to generate the co-ordinates of the rectangles needed to shift a little by some small angle β. Something like:
Somebody more math-savvy than I am can probably do better, but for my attempt I noticed that I could tinker with the angle generated by the rectangle itself, given its dimensions. Like:
So, plugging that in:
const angle = slice * i;
// == addition starts here
const opp_hyp = 60/120;
const b_prime_degrees = Math.atan(opp_hyp);
const b_prime = 180 * b_prime_degrees/Math.PI; // converting to radians
const x = root.x + (radius * Math.cos(angle-b_prime));
const y = root.y + (radius * Math.sin(angle-b_prime));
// == addition ends here
Using that, I got the following outcomes for 3, 7 and 9:
I wasn't too satisfied, so totally on a whim, I decided to try the other angle in that triangle i.e. 180-β
const angle = slice * i;
// == addition starts here
const opp_hyp = 60/120;
const b_prime_degrees = Math.atan(opp_hyp);
const new_guy = 180 * (180-b_prime_degrees)/Math.PI;
const x = root.x + (radius * Math.cos(angle-new_guy));
const y = root.y + (radius * Math.sin(angle-new_guy));
// == addition ends here
Using that, I got the following outcomes for 3, 7 and 9:
...which kinda looks better, sort of.
Hopefully somebody more knowledgeable than I am comes along and helps you with a definitive answer. Till then, I hope my attempts at least help nudge you in the right direction.

Calculate minimum value not between set of ranges

Given an array of circles (x,y,r values), I want to place a new point, such that it has a fixed/known Y-coordinate (shown as the horizontal line), and is as close as possible to the center BUT not within any of the existing circles. In the example images, the point in red would be the result.
Circles have a known radius and Y-axis attribute, so easy to calculate the points where they intersect the horizontal line at the known Y value. Efficiency is important, I don't want to have to try a bunch of X coords and test them all against each item in the circles array. Is there a way to work out this optimal X coordinate mathematically? Any help greatly appreciated. By the way, I'm writing it in javascript using the Raphael.js library (because its the only one that supports IE8) - but this is more of a logic problem so the language doesn't really matter.
I'd approach your problem as follows:
Initialize a set of intervals S, sorted by the X coordinate of the interval, to the empty set
For each circle c, calculate the interval of intersection Ic of c with with the X axis. If c does not intersect, go on to the next circle. Otherwise, test whether Ic overlaps with any interval(s) in S (this is quick because S is sorted); if so, remove all intersecting intervals from S, collapse Ic and all removed intervals into a new interval I'c and add I'c to S. If there are no intersections, add Ic to S.
Check whether any interval in S includes the center (again, fast because S is sorted). If so, select the interval endpoint closest to the center; if not, select the center as the closest point.
Basically the equation of a circle is (x - cx)2 + (y - cy)2 = r2. Therefore you can easily find the intersection points between the circle and X axis by substituting y with 0. After that you just have a simple quadratic equation to solve: x2 - 2cxx + cx2 + cy2 - r2 = 0 . For it you have 3 possible outcomes:
No intersection - the determinant will be irrational number (NaN in JavaScript), ignore this result;
One intersection - both solutions match, use [value, value];
Two intersections - both solutions are different, use [value1, value2].
Sort the newly calculated intersection intervals, than try merge them where it is possible. However take in mind that in every program language there approximation, therefore you need to define delta value for your dot approximation and take it into consideration when merging the intervals.
When the intervals are merged you can generate your x coordinates by subtracting/adding the same delta value to the beginning/end of every interval. And lastly from all points, the one closest to zero is your answer.
Here is an example with O(n log n) complexity that is oriented rather towards readability. I've used 1*10-10 for delta :
var circles = [
{x:0, y:0, r:1},
{x:2.5, y:0, r:1},
{x:-1, y:0.5, r:1},
{x:2, y:-0.5, r:1},
{x:-2, y:0, r:1},
{x:10, y:10, r:1}
];
console.log(getClosestPoint(circles, 1e-10));
function getClosestPoint(circles, delta)
{
var intervals = [],
len = circles.length,
i, result;
for (i = 0; i < len; i++)
{
result = getXIntersection(circles[i])
if (result)
{
intervals.push(result);
}
}
intervals = intervals.sort(function(a, b){
return a.from - b.from;
});
if (intervals.length <= 0) return 0;
intervals = mergeIntervals(intervals, delta);
var points = getClosestPoints(intervals, delta);
points = points.sort(function(a, b){
return Math.abs(a) - Math.abs(b);
});
return points[0];
}
function getXIntersection(circle)
{
var d = Math.sqrt(circle.r * circle.r - circle.y * circle.y);
return isNaN(d) ? null : {from: circle.x - d, to: circle.x + d};
}
function mergeIntervals(intervals, delta)
{
var curr = intervals[0],
result = [],
len = intervals.length, i;
for (i = 1 ; i < len ; i++)
{
if (intervals[i].from <= curr.to + delta)
{
curr.to = Math.max(curr.to, intervals[i].to);
} else {
result.push(curr);
curr = intervals[i];
}
}
result.push(curr);
return result;
}
function getClosestPoints(intervals, delta)
{
var result = [],
len = intervals.length, i;
for (i = 0 ; i < len ; i++)
{
result.push( intervals[i].from - delta );
result.push( intervals[i].to + delta );
}
return result;
}
create the intersect_segments array (normalizing at x=0 y=0)
sort intersectsegments by upperlimit and remove those with upperlimit<0
initialize point1 = 0 and segment = 0
loop while point1 is inside intersectsegment[segment]
4.1. increment point1 by uppper limit of intersectsegment[segment]
4.2. increment segment
sort intersectsegments by lowerlimit and remove those with loerlimit>0
initialize point2 = 0 and segment = 0
loop while point2 is inside intersectsegments[segment]
7.1. decrement point2 by lower limit of segment
7.2. decrement segment
the point is minimum absolute value of p1 and p2

How to draw a exponential function y=ab^x using d3.js axis functionality

I am trying to draw an exponential function (y=ab^x) using the d3.js (javascript) library. I understand how to draw the axes themselves. I just need the magic that draws the actual line. I have seen description for the linear and quadratic equations but nothing more custom.
Any help would be appreciated.
I think that you need to construct the data yourself. For an exponential function, you can generate the data:
var data = [],
n = 100,
a = 1,
b = 2;
for (var k = 0; k < 100; k++) {
data.push({x: 0.01 * k, y: a * Math.pow(b, 0.01 * k)});
}
and then, use the standard code to generate a line graph, for instance, see http://bl.ocks.org/3883245.

Categories