I'm a beginner and I made a function to calculate the length of a semi-circular infinite snake figure. I took in two arguments; one of the radius the initial circle, and the next to be the precision (which is just the number of semi-circles).
Here's a diagram of the snake I'm talking about.
Here's what I wrote:
function snake(radius, precision) {
var pi = 3.14159265359
var exp = 1 - precision;
var sub = Math.pow(2, exp);
var product = 2 - sub;
var length = pi * radius * product
return length
}
I'm noticing that at one point the precision doesn't matter when I go really high as the value it return is the same. Is there a way to make it more precise?
You may use the constant Math.PI instead of your pi variable.
Feels like Number.toPrecision() is what you are looking for.
Below is slightly cleaned up version of your code snippet.
For the return value I'm using length.toPrecision(50):
function snake(radius, precision) {
const exp = 1 - precision;
const sub = Math.pow(2, exp);
const product = 2 - sub;
const length = Math.PI * radius * product;
return length.toPrecision(50);
}
console.log(snake(5, 55));
Yo can find out more about toPrecision() here.
The max precision value is 100.
This will be my method,
Length can be get by sum of the below series according to the screen shot,
PI X R (1 + 1/2 + 1/4 + ...)
S(n) = PIxR(1-0.5^n)/(1-0.5)
So the JavaScript function is,
function length(r, n) {
const ln = Math.PI * r * (1 - Math.pow(0.5, n))/(1 - 0.5);
return ln.toPrecision(50);
}
Related
I have one circle, which grows and shrinks by manipulating the radius in a loop.
While growing and shrinking, I draw a point on that circle. And within the same loop, increasing the angle for a next point.
The setup is like this:
let radius = 0;
let circleAngle = 0;
let radiusAngle = 0;
let speed = 0.02;
let radiusSpeed = 4;
let circleSpeed = 2;
And in the loop:
radius = Math.cos(radiusAngle) * 100;
// creating new point for line
let pointOnCircle = {
x: midX + Math.cos(circleAngle) * radius,
y: midY + Math.sin(circleAngle) * radius
};
circleAngle += speed * circleSpeed;
radiusAngle += speed * radiusSpeed;
This produces some kind of flower / pattern to be drawn.
After unknown rotations, the drawing line connects to the point from where it started, closing the path perfectly.
Now I would like to know how many rotations must occure, before the line is back to it's beginning.
A working example can be found here:
http://codepen.io/anon/pen/RGKOjP
The console logs the current rotations of both the circle and the line.
Full cycle is over, when both radius and point returns to the starting point. So
speed * circleSpeed * K = 360 * N
speed * radiusSpeed * K = 360 * M
Here K is unknown number of turns, N and M are integer numbers.
Divide the first equation by the second
circleSpeed / radiusSpeed = N / M
If speed values are integers, divide them by LCM to get minimal valid N and M values, if they are rational, multiply them to get integer proportion.
For your example minimal integers N=1,M=2, so we can get
K = 360 * 1 / (0.02 * 2) = 9000 loop turns
I was working on a fun project that implicates creating "imperfect" circles by drawing them with lines and animate their points to generate a pleasing effect.
The points should alternate between moving away and closer to the center of the circle, to illustrate:
I think I was able to accomplish that, the problem is when I try to render it in a canvas half the render jitters like crazy, you can see it in this demo.
You can see how it renders for me in this video. If you pay close attention the bottom right half of the render runs smoothly while the top left just..doesn't.
This is how I create the points:
for (var i = 0; i < q; i++) {
var a = toRad(aDiv * i);
var e = rand(this.e, 1);
var x = Math.cos(a) * (this.r * e) + this.x;
var y = Math.sin(a) * (this.r * e) + this.y;
this.points.push({
x: x,
y: y,
initX: x,
initY: y,
reverseX: false,
reverseY: false,
finalX: x + 5 * Math.cos(a),
finalY: y + 5 * Math.sin(a)
});
}
Each point in the imperfect circle is calculated using an angle and a random distance that it's not particularly relevant (it relies on a few parameters).
I think it's starts to mess up when I assign the final values (finalX,finalY), the animation is supposed to alternate between those and their initial values, but only half of the render accomplishes it.
Is the math wrong? Is the code wrong? Or is it just that my computer can't handle the rendering?
I can't figure it out, thanks in advance!
Is the math wrong? Is the code wrong? Or is it just that my computer can't handle the rendering?
I Think that your animation function has not care about the elapsed time. Simply the animation occurs very fast. The number of requestAnimationFrame callbacks is usually 60 times per second, So Happens just what is expected to happen.
I made some fixes in this fiddle. This animate function take care about timestamp. Also I made a gradient in the animation to alternate between their final and initial positions smoothly.
ImperfectCircle.prototype.animate = function (timestamp) {
var factor = 4;
var stepTime = 400;
for (var i = 0, l = this.points.length; i < l; i++) {
var point = this.points[i];
var direction = Math.floor(timestamp/stepTime)%2;
var stepProgress = timestamp % stepTime * 100 / stepTime;
stepProgress = (direction == 0 ? stepProgress: 100 -stepProgress);
point.x = point.initX + (Math.cos(point.angle) * stepProgress/100 * factor);
point.y = point.initY + (Math.sin(point.angle) * stepProgress/100 * factor);
}
}
Step by Step:
based on comments
// 1. Calculates the steps as int: Math.floor(timestamp/stepTime)
// 2. Modulo to know if even step or odd step: %2
var direction = Math.floor(timestamp/stepTime)%2;
// 1. Calculates the step progress: timestamp % stepTime
// 2. Convert it to a percentage: * 100 / stepTime
var stepProgress = timestamp % stepTime * 100 / stepTime;
// if odd invert the percentage.
stepProgress = (direction == 0 ? stepProgress: 100 -stepProgress);
// recompute position based on step percentage
// factor is for fine adjustment.
point.x = point.initX + (Math.cos(point.angle) * stepProgress/100 * factor);
point.y = point.initY + (Math.sin(point.angle) * stepProgress/100 * factor);
I'm struggling with creating a function, that interpolates one range to another. I already have a functioning linear interpolation, but getting more 'curved' interpolations, I am stumped.
F.ex. I want to interpolate the range [0,100] to [0,1000] in a way, so target-values near 0 is more likely than target-values near 1000 (or vice-versa).
One approach I got working was using logarithmic interpolation, which has some annoying drawbacks:
I used a crude hack to deal with negative numbers (f.ex. [-70,50]), by offsetting the range before calculating
The 'slope' is not adjustable (f.ex. if I want the target-value to be just a bit more likely or a lot more likely than a linear interpolation)
function logPol (value, s1, s2, t1, t2) {
var f = (value - s1) / ((value - s1) + (s2 - value));
var add = 0;
if(t1 <= 0 || t2 <= 0) {
add = t1 >= t2? 2 * Math.abs(t2) + 1 : 2 * Math.abs(t1) + 1;
t1 += add;
t2 += add;
}
var interpolated = Math.pow(t2,f) * Math.pow(t1, 1-f) - add;
return interpolated;
};
I've read a lot of articles on quadratic equations, thinking that would solve my problem, but never ended up with a working solution. If I need to explain something further, please let me know.
After reading a ton of different approaches, I finally solved my problem, and ended up using Bezier curves to handle my interpolation. Here's my resulting function:
/**
* Returns a bezier interpolated value, using the given ranges
* #param {number} value Value to be interpolated
* #param {number} s1 Source range start
* #param {number} s2 Source range end
* #param {number} t1 Target range start
* #param {number} t2 Target range end
* #param {number} [slope] Weight of the curve (0.5 = linear, 0.1 = weighted near target start, 0.9 = weighted near target end)
* #returns {number} Interpolated value
*/
var interpolate = function (value, s1, s2, t1, t2, slope) {
//Default to linear interpolation
slope = slope || 0.5;
//If the value is out of the source range, floor to min/max target values
if(value < Math.min(s1, s2)) {
return Math.min(s1, s2) === s1 ? t1 : t2;
}
if(value > Math.max(s1, s2)) {
return Math.max(s1, s2) === s1 ? t1 : t2;
}
//Reverse the value, to make it correspond to the target range (this is a side-effect of the bezier calculation)
value = s2-value;
var C1 = {x: s1, y:t1}; //Start of bezier curve
var C3 = {x: s2, y:t2}; //End of bezier curve
var C2 = { //Control point
x: C3.x,
y: C1.y + Math.abs(slope) * (C3.y - C1.y)
};
//Find out how far the value is on the curve
var percent = value / (C3.x-C1.x);
return C1.y*b1(percent) + C2.y*b2(percent) + C3.y*b3(percent);
function b1(t) { return t*t }
function b2(t) { return 2*t*(1 - t) }
function b3(t) { return (1 - t)*(1 - t) }
};
I have a set of locations that I want to display to a user in proximity order - closest to farthest - based on their current coordinates. Assume we have ~100 data points of locations which includes each one's latitude and longitude (in some kind of object or array), and we know the user's lat and long. The goal is to display an ordered list of locations -- and it is beneficial if we can get and display to the user the nearest 8-10 locations while we calculate then display the remaining distances.
I know the brute-force solution is to loop through all locations, calculate the distance from the user, put them in order, then display them all to the user. But this is too slow.
A better solution is this one: https://stackoverflow.com/a/2466908/1766230 where you check within a bounded box first, expanding if necessary, then do the rest.
I've also seen there are other algorithms out there - like FLANN and other methods - but I haven't seen any examples written in JavaScript.
So the question: What is the fastest way to calculate (and display in order) nearest points in JavaScript?
So, if you are starting out with that list of points, drawing a small bounding box won't cut down very much, because you still do an O(n) check against all points for their location.
I would advise using a max-length heap or some other form of partial sort while iterating through all of the points. This lets you keep track of a small subset of approximately maximum/minimal points (as described by the length), so you can render those quickly before dealing with the rest. If you need more explanation about what I'm saying precisely, let me know.
Also what are you making this for that has such stringent performance issues? Typically computation like this shouldn't be a stress point, barring that you have 100k+ points. DOM manipulation is usually the most expensive spot
var points = [];
for (i = 0; i < 100; i++) {
var point = [getRandomInt(0, 999), getRandomInt(0, 999)];
point.len = distanceBetweenPoints(point, [499,499]);
points.push(point);
}
console.log(Heap.nsmallest(points, 10, function (a, b) {
return a.len < b.len;
}));
Here is the performance for it compared to bruteforce
Heap Code
js fiddle
Using the method I described, and a prebuilt heap another person wrote, I compared our methods. I think you will be happy! It performed 8,586 ops/sec compared to the 566 in the brute force technique!
Well this is my attempt at sorting an array of points by distance to a given point. This is brute-force as far as I understand. Then I slice the array to give you the 10 closest points.
Javascript
function distanceBetweenPoints(p1, p2) {
return Math.abs(Math.sqrt((p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1])));
}
function sortByDistance(location, arrayOfPoints) {
arrayOfPoints.sort(function (a, b) {
a.distance = distanceBetweenPoints(location, a);
b.distance = distanceBetweenPoints(location, b);
return a.distance - b.distance;
});
return arrayOfPoints;
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var points = [];
for (i = 0; i < 100; i += 1) {
points.push([getRandomInt(-90, 90), getRandomInt(-180, 180)]);
}
console.log(sortByDistance([0, 0], points).slice(0, 10));
On jsFiddle
This will at least give you something to test algorithms against. And here is a jsPerf for the above, so you can add other routines to it and do some real performance comparisons.
Note: This does not take into consideration that the Earth is a sphere! This is calculating Euclidean distance and not Geodesic distance. This is fine if the points, are for example, in the same town (or close proximity) but not if they are in different countries/continents. It also assumes that you have converted your longitude and latitude to a decimal representation.
Otherwise you will need to look at things like Great-circle distance and Haversine formula
In fact, the earth is very slightly ellipsoidal; using a spherical model gives errors typically up to 0.3%
Javascript
function toRadians(degrees) {
return (degrees * Math.PI) / 180;
}
// Haversine formula
function distanceBetweenPoints(p1, p2) {
var R = 6371, // mean earth radius in km
lat1 = toRadians(p1[0]),
lon1 = toRadians(p1[1]),
lat2 = toRadians(p2[0]),
lon2 = toRadians(p2[1]),
dLat = lat2 - lat1,
dLon = lon2 - lon1,
a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2),
c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)),
d = R * c;
return d;
}
function sortByDistance(location, arrayOfPoints) {
arrayOfPoints.sort(function (a, b) {
a.distance = distanceBetweenPoints(location, a);
b.distance = distanceBetweenPoints(location, b);
return a.distance - b.distance;
});
return arrayOfPoints;
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var points = [];
for (i = 0; i < 100; i += 1) {
points.push([getRandomInt(-90, 90), getRandomInt(-180, 180)]);
}
console.log(sortByDistance([0, 0], points).slice(0, 10));
On jsFiddle
I am using the following code to generate a random number:
function getRandomInt (min, max) {
return Math.floor((Math.random() * (max - min + 1)) + min;
}
What I want to do is add a weighting that favours the numbers at the lower end of the range.
I thought about maybe trying to multiply the numbers by 1/cosine.
Would this work and does anyone know how I might go about it?
Many thanks!
First Solution
You need a function which contains the points (0, 0) and (1, 1). For instance: x^n when n > 0
Math.pow(1, n) === 1
And
Math.pow(0, n) === 0
Therefore, you would just change n depending on how you want the weighting to work.
When n = 1 : y === x
When n > 1 : y <= x
When 0 < n < 1 : y >= x
So, if you want lower values to be favored over higher values, simply use n > 1.
var weighted = Math.pow(Math.random(), 2);
Then you can scale the result as usual.
var scaled = Math.floor(weighted * (max - min + 1)) + min;
Other Functions
Likewise, you could use any continuous function which contains the points (0, 0), (1, 1), and has range and domain of [0, 1].
Sine
y = sin(xπ/2)
Cosine
y = 1 - cos(xπ/2)
EDIT: there was a type in the final formula, log(2+log(x)) is incorrect it should have been log(1+log(x))+1, its fixed now.
If you are using logarithmic weighting, using something like
var x = Math.random();
var weighted = x * Math.log(1+x);
would make 0.5 weigh in at around 0.2, but 1 would only weigh in at around 0.69.
Using this
var x = Math.random();
var weighted = x * Math.log(2 + Math.log(x));
would allow 1 to weigh in at 1. So combine them, and this
var x = Math.random();
var weighted = (x <= 0.5) ? x * Math.log(1 + x) : x * Math.log(1 + Math.log(x))+1;
should do the trick