javascript math formula. find inverse - javascript

I have some function:
function calc_degree(a,b,c,cnt) {
if(cnt==0) {
return a;
}
b = b+c;
a = a-b;
return calc_degree(a,b,c,cnt-1);
}
Shortly, it calcs degree of rotation some cicle, which rotation speed increase smoothly. Function returns summary degrees of the rotation. For example:
calc_degree(0,0,1.5,6*1000/time_out);
//a - start angle; b-value of increasing ratoation degree every tick.
//c-increase value; time_out - interval of rotation.
In this example, function returns summary degrees of rotation by 6 seconds.
So, how can I calc the "c" param, if I know the "a" and "cnt"? I need to get the increase value, knowing the summary degrees of rotation and time/tick. If my "a" value is 2790, I need to decrease it every time by "c" value and the last value of "a" must be zero.

Make it a proper recursion, with indices and all:
b[n] = b[n-1] + c => b[n] = b[0] + n*c
a[n] = a[n-1] - b[n] = a[n-1] - b[0] - n*c
results in
a[n] = a[0] - n*b[0] - n*(n+1)/2 * c
This shows you how to get c if a[0]=b[0]=0.
To get a[n]=b[n]=0, you would first need c=-b[0]/n and then c=-a[n]/(n*(n-1)/2). This only works if in the beginning 2*a[0] == (n-1)*b[0].

I cannot comment yet, so I'll add it here. LutzL answer is correct. The problem is that if you add more than one constraint to the problem (in your case requiring both a and b to go to 0) you are reducing the degrees of freedom of the problem. In your case you cannot make a go to zero smoothly (with both a and b 0) if there's not the relation stated by LutzL in the beginning. You can solve it by adding another degree of smoothness (ex: c[n] = c[n-1] + d.) But then you wont be able to make a, b and c tend to 0 without extra constraints.

Related

Smoothly snap a rotatable object to degree

I have been hacking on this problem for a while and can't seem to find a solution (I am not friends with trigonometry).
I am trying to build an element that the user can "grab" and then rotate. I have it working except for one last feature I can't seem to figure out:
http://codepen.io/megakoresh/pen/WbLLzZ?editors=001
(it's long, but I am only asking about how the snapTo() function should work).
What I want is for the object snap to degrees based on the increments value. This means that if snap==true, the code should calculate the closest estimated targets to the point of release of the mouse and based on the direction of rotation smoothly rotate the object to that target rotation:
Since the object is 'grabbed', I calculate the offset at mousedown to object's current rotation, thats where it comes from, so it doesn't just snap to mouse.
So in this case the user rotates the object clockwise and releases the mouse when the objects rotation is between 90° and 45°. Since the direction (identified by the sign of angle variable) was positive, the target will be after the Current rotation.
The task is to calculate that target and then smoothly rotate the object to it.
The function I have written for it is based on by autoSpin() function (executes when spin==false), which takes a flipped time exponent multiplier delta (calculated from the time elapse since mouse was released). delta will decrease along a flipped exponent as time passes and so the angle slows down.
There is spinTo() function, please don't judge me I have a feeling it is very stupid:
function snapTo() {
var elapsed, delta;
increments = (typeof increments === 'number') ? Math.floor(increments) : 4;
var ia = 360 / increments; //increment angle - snapping points should occur "every ia degrees"
if (Math.abs(angle % ia) > 0) { //if user turned for more than 1 increment
var a = lastRot % ia; //check the distance from
if (angle > 0){ //positive direction
amplitude = 50; //if snapping is on, force amplitude
target = (lastRot - a) + ia;
}
if (angle < 0){ //negative direction
amplitude = -50;
target = lastRot - a;
}
} else { //cancel the rotation
target = rotation;
}
elapsed = Date.now() - timestamp; //time passed since mouse was released
delta = -amplitude * Math.exp(-elapsed / timeConstant); //the greater the time from mouse release, the smaller this value
if (delta > 0.5 || delta < -0.5) { //while delta is decreasing...
rotate(target - delta - offset);
snapFrame = requestAnimationFrame(snapTo); //keep rotation
} else {
rotate(target - offset); //when enough time passes (based on timeConstant), make one final rotation and stop
}
}
What am I doing wrong?
*le sigh
Ok well I had to figure it out on my own. If anyone wants to do this kind of thing, here is one way of doing it:
function snapTo() {
var elapsed, ease, diff;
increments = (typeof increments === 'number') ? Math.floor(increments) : 4;
var ia = 360 / increments; //increment angle - this is how large each increment will be
var a = last % ia; //check the distance from point of mouse release to the previous increment
if (movement>0) {
elapsed = Date.now() - timestamp; //time passed since mouse was released
ease = 1 - Math.exp((-3*elapsed)/timeConstant); //your easing function formula goes here
if(a > ia/2) { //if halfway through the increment...
target = (last - a) + ia; //target next increment
}
else { //else revert to previous increment to the point of mouse release
target = last - a;
}
diff = target - last; //difference between target and rotation at time of mouse release
if(ease < 0.95){ //this depends on your easing formula
rotate(last+diff * ease);
requestAnimationFrame(snapTo); //don't forget to RAF
} else { //and for your final frame...
rotate(last+diff); //make it snappy :P
}
console.log(last); //traces of debugging were found in this derelict question...
}
}
So in here
elapsed is the time passed since the mouse was released
increments is the argument provided by user: how many snapping points will be there
ia is the computed increment in degrees: 360/increments
last is the angle recorded on mouseup event - "Current rotation" in the diagram. It includes the offset (in my code, its just a "snapshot" of rotation at point of release, that is then also reversed in sign because in DOM the coordinate system is y-flipped and I don't like working with that).
a is how much bigger the last is than the previous nearest increment point.
diff is the "target" on the diagram - difference between final rotation and last
ease is just the value that changes according to your easing function, based on which you either continue calling RAF or finish the animation.
timeConstant is time in milliseconds for how long the animation will take. You can have this or not, depends on your easing formula
A good resource to read in general: Understanding Easing Functions
Also Desmos graphing calculator is quite good for developing easing formulas.
Oh and: in case anyone is wondering, it doesn't seem possible to pass any kind of non-time-related arguments to RAF callback functions. Seems to break it if I do. So the only solution is to define the increments value elsewhere in the code.
Codepen of the working function
Anyone got a better solution, I am all eyes.

The correct way to calculate Radians in Javascript?

I have this fiddle http://jsfiddle.net/pp7oby62/. The fiddle consists of a paddle game with rocks being hurdled at a paddle on a 80x10 sensor grid. The function notify_player has two parameters which give me the location of the rocks and the paddle. My code looks like this:
defender.start(
function notify_player(rocks, paddle_y) {
var rocks_x = Math.max(rocks);
var rocks_y = Math.min(rocks);
var paddle_x = Math.max(paddle_y);
var paddle_yp = Math.min(paddle_y);
var deltaY = rocks_y - paddle_yp;
var deltaX = rocks_x - paddle_x;
var angleInRadians = Math.atan2(deltaY, deltaX);
var angleRound=Math.round(angleInRadians);
// random plan
var moves = [];
for (var i = 0; i < 22; i++) {
// -1, 0, 1 to move the fiddle up and down on the grid
//moves.push( Math.floor(Math.random()*3) - 1 );
if (angleInRadians >0) moves.push(-1);
else if (angleInRadians < 0) moves.push(0, 1, -1);
}
return moves;
}
);
The moves array controls the paddle moving up and down. As you can see I am calculating the angle in radians using the the information I get from the two parameters. But when I run it there's no effect on the movement of paddle when I give conditional loops stating that the angle is >0 or <0. But suppose I say:
if(angleInRadians!==0)
The paddle moves.
How could this be possible?
there's no effect on the movement of paddle when I give conditional loops stating that the angle is >0 or <0.
This could simply be when anleInRadians is zero - you've got a blind spot there.
But suppose I say: if(angleInRadians!==0) The paddle moves. How could this be possible?
The variable angleInRadians contains the value NaN. It is neither smaller nor greater than zero, as it's not a number - but it is definitely not zero. Yes, NaNs equality relations are a bit odd (it's not even equal to itself).
The reason why you are getting NaN here is that you're doing invalid mathematical operations. rocks is an array of objects (you really should log and inspect it), which you cannot call Math.min or .max on.
Btw, it doesn't even make sense to call min or max on a single value. And you won't get the paddle_x position by calling Math.max(paddle_y).

Replace modulus by function

When I try to do 8067 % 80.67 I get 80.66999999999983, instead of 0 beacuse of known floating point javascript behaviour.
So I went and made a function for this, to avoid floating point javascript errors.
function math(a, b) {
var left = Math.abs(a),
times = 1,
abs = a >= 0 ? 1 : -1;
while (Math.abs(a) >= b * times) {
left -= b;
times++;
}
return (a - (b * (times - 1))) * abs;
}
http://jsfiddle.net/s5w3C/
So my question is: is this usefull, ie a good tool to use instead of %? is there cases where this will also give falsy results like the modulus % oprator.
I am looking for a tools to calculate % consistently.
I didn't really inspect the algorithm for correctness, but if you care about efficiency, this is a bad idea. Basically, the larger the input, the slower your code will execute.
I think any fix will only work to a certain level of accuracy and for certain sized numbers. Perhaps something like the following will be sufficient:
function nearlyMod(a, b) {
var precision = ('' + b).split('.').length;
var estimate = (a % b).toFixed(precision);
return estimate == b ? 0 : +estimate;
}
console.log(nearlyMod(8067, 80.66)); // 1
console.log(nearlyMod(8067, 80.67)); // 0
console.log(nearlyMod(8067, 80.68)); // 79.68
It tests if the result is an even divisor within the precision of the original number. If so, it returns 0, otherwise it returns a number to the same precision (which may or may not be what you want).
The result is always a number (the value returned from toFixed is a string, hence +estimate).
A better name might be "roundedMod" or similar.

How to determine if a collision is new or continued from last frame

I have this function which detects the collision between to objects. It is called within a Ticker (FPS 60). The if statement will run as long as the two objects are together. I think this has something to do with the ticker and it running the if every frame. What would be the best way to fix this so that for example when the two objects collided the person gets one point instead of four or ten.
function collDec(){
var minDistance = 10 + 10;
var xDist = circle.x - arrow.x;
var yDist = circle.y - arrow.y;
var distance = Math.sqrt(xDist*xDist + yDist*yDist);
if (distance < minDistance) {
Lpoints.text = "Points: " + ++pointsAm;
//console.log("HIT");
var dingSound = createjs.Sound.play("sound/ding.mp3");
//reset();
}
} // End of collDec
Have an int on each object, recently collided
If a collision occurs, set both recently collided on both objects to 2
At the start of every frame, decrement recently collided on all objects by 1 to a minimum of 0
If a collision occurs and recently collided is 1 or higher* on both objects, do not add points/play a sound but still increment recently collided.
*I think 'exactly 1' may be fine too. It seems to only matter in 'three balls collide simultaneously or near simultaneously' cases.

Elegant way to bias random boolean

I'd like to create a random boolean in JavaScript, but I want to take the previous value into account. If the previous value was true, I want it to be more likely for the next value to be true. At the moment I've got this (this is in the context of a closure - goUp and lastGoUp are locals to the containing scope):
function setGoUp() {
goUp = getRandomBoolean();
if(lastGoUp) {
goUp = getRandomBoolean() || goUp;
}
else {
goUp = getRandomBoolean() && goUp;
}
lastGoUp = goUp;
}
So, the algorithm goes:
Get a random boolean
If the random boolean from the previous call was True:
a) get another random boolean, and or these two together
b) else get another random boolean and and these together.
I'm sure this algorithm could be simplified. I wondered about doing:
if(lastGoUp && goUp) {
goUp = goUp * (getRandomBoolean() || goUp);
}
but that seems really dirty.
There's also a problem with this algorithm which means that I can only double the chance of getting the same boolean again - I can't tweak it easily. Any ideas?
You should define the distribution you want, but maybe you are looking for the following?
if (lastGoUp) {
goUp = Math.random() < 0.8;
} else {
goUp = Math.random() < 0.2;
}
Instead of getting a random boolean, get a random number, say between 0 and 99. Keep a threshold value instead of the last number, and adjust the threshold according to the result:
var threshold = 50;
function setGoUp() {
goUp = getRandomNumber() < threshold;
threshold += goUp ? -10 : 10;
}
This would keep a running tab, so if you get consecutive results that are the same, the probability would keep falling for that result.
If you only want to consider the last result, you would instead set the threshold to a specific value:
threshold = goUp ? 40 : 60;
If you only want the probability of the next event to depend on the current value, and not the history of values up til now, what you want is called a Markov process. Often these are implemented with a 2D table of probabilities that you look up (prob of each next outcome given current one), but for a simple bool-valued event, an if statement is sufficient (see meriton's answer; note that it corresponds to a table of probabilities [0.8 0.2; 0.2 0.8]).
If you want something that gets more likely, say, the more successes you get in a row, then you need to devise a sequence of probabilities for success that perhaps approaches, but does not exceed, 1. There are any number of formulas which can do this, depending on how strong you want the bias to become and how quickly you want it to get there.
I would just make the probability of getting value true be an explicit float variable p. Then I could tweak it easily, by increasing p in some way if I got true last time or by doing nothing with it if I got 'false'.
Can replace Math.random for a better randomizer.
var setGoUp = (function(){
var last;
return function(){
// if last 66% chance for true else 50% chance of true.
return !!(last ? Math.random()*3 : Math.random()*2);
}
}());
!! converts anything to a boolean, 0 = false.

Categories