I'm trying to find a math function for animating, but so far without success. There must be some very simple solution for this, but I'm a bit confused. I need to get the value of a function like in the example image
.
[A1,B1] - range of possible argument value
[A,C] - range of function values
D - the value of the argument for which the function shows the minimum value
What type of functions is this? I would be grateful for any hints.
I see is piecewise curve: 2 quadratic + 2 line for example something like this in C++:
float x0,x1,x2,y0,y1,y2; // set these to your curve edge points
float fx(float x) // this returns y for any x
{
if (x<=x0) return y0;
if (x<=x1)
{
float a=(x-x1)/(x1-x0);
return y1+a*a*(y0-y1);
}
if (x<=x2)
{
float a=(x-x1)/(x2-x1);
return y1+a*a*(y2-y1);
}
return y2;
}
And here how it looks like:
Related
I realize this is more of a math question, but I don't have a math brain. I'm specifically interested in solving this problem in JavaScript, just for this specific case. If someone can do this for me with a simple function, I can generalize the solution myself as needed.
I have two lines on a graph. One is linear, just going straight from (0,0) to (x,x):
// Line 1
var f1 = function(x) {
return x;
};
The other line is curved, and can be drawn like this:
// Line 2
var f2 = function(x) {
var alpha = 0.3;
return (1 - alpha) *
(1.4 *
1.6 ** alpha) *
(x ** -alpha);
};
Given only these functions, can I write a function that gives me the co-ordinates of the point(s) at which these two lines intersect?
I've looked at things like algebra.js, but haven't been able to come up with the solution myself.
Derivating process:
So, for alpha= a= 0.3 we have:
k= (1-alpha) * 1.4 * 1.6**alpha = 1.12839738
x= k ** (1/(1 + alpha)) = 1.09737595
The desired intersection is {x, f(x)} = {1.0974, 1.0974}
My goal is to write an animation in JavaScript that performs an ease-in-out style bezier curve animation
(such as in http://cubic-bezier.com/#.42,0,.58,1)
I came up with the following script to calculate the y value given "x" value (time):
function CalculateBezierPoint(t, p0, p1, p2, p3) {
var y = ((1-t)*(1-t)*(1-t)*p0) + (3*(1-t)*(1-t)*t*p1) + (3*(1-t)*t*t*p2) + (t*t*t*p3);
return y;
}
Using the explicit formula from Wikipedia :
Demo - https://codepen.io/anon/pen/QpRzBg
However, the print statements show the Y value going down before going up, when it should ONLY be going up:
0.42
0.3228427793606603
0.3119941308275725
0.3025864871426283
0.29458762995005683
0.2879653408940873
0.28268740161894895
0.27872159376887096
0.27603569898808256
0.27459749892081287
0.2743747752112911
0.2753353095037466
0.27744688344240837
0.28067727867150566
0.2849942768352678
0.29675920854370297
0.3041427053768346
0.3124839317215477
0.3217506690862967
0.33191069937460593
0.34293180410763435
0.35478176492961133
I did manage to find someone else's code that seems to work, here is the output:
0
0.009480343767040133
0.0246451904411195
0.03199616010201068
0.040680303103589804
0.05080871722437687
0.062492500242891866
0.07584274993765482
0.0909705640871857
0.10798704047000454
0.12700327686463134
0.14813037104958607
0.17147942080338874
0.19716152390455938
0.225287778131618
0.25596928126308455
0.28931713107747903
0.3254424253533215
0.36445626186913194
0.4064697384034303
0.4515939527347367
0.499940002641571
Demo - https://codepen.io/anon/pen/evabrr
Both demos use the same input: p0 = .42, p1 = 0, p2 = .58, p3 = 1
I don't know why my attempt fails, and the code I found works. Did I implement the formula wrong? Did I choose the wrong formula? Something else?
So your mistake is neither passing a single value nor the power function. The mistake is that you are assuming that x = t.
I came up with the following script to calculate the y value given "x" value (time):
If x=t then what you have is an explicit bezier curve, not a parametric bezier curve. Explicit meaning that y is a function of x (i.e. y=f(x)). As opposed to a parametric equation where both x and y are functions of t (i.e. x=f(t) and y=f(t)).
One way to test that this is truly the case, set your x values to [0, 1/3, 2/3, 1]. Evenly spaced x-values ensures that x=t and will give you an explicit bezier curve. You can achieve this on http://cubic-bezier.com by setting the x values in the address bar to 0.333 and 0.666. But as soon as you move the control points to either the left or the right your results will again differ.
To get the same effect it is a bit more involved. You must solve for t at a given x and then calculate y from t. Solving for t is a little complex but can be approximated with the newton-raphson method. This link does a much better job explaining how to implement it: http://greweb.me/2012/02/bezier-curve-based-easing-functions-from-concept-to-implementation/
Late to the party, but: your comment "I think I'm actually making a big error -- the P are supposed to be point values.. which should have (x,y) and I'm only providing one digit" is correct.
A Bezier curve is a parametric function where both x and y (or x, y, and z in 3D) are functions of t. You're only calculating half of the curve, so you need to modify your CalculateBezierPoint function to return an x/y coordinate, not just a y coordinate:
calculateBezierPoint(t, xvalues, yvalues) {
return new Point(
x = calculateBezierDim(t, x1, x2, x3, x4),
y = calculateBezierDim(t, y1, y2, y3,y4)
);
}
calculateBezierDim(t, vals) {
a=vals[0], b=vals[1], c=vals[2], d=vals[3];
mt = 1-t;
t2 = t*t;
mt2 = mt*mt;
return a * mt*mt2 + 3 * b * mt2 * t + 3 * c * mt * t2 + d * t2 * t;
}
(Adapted to your programming language and data types of course).
Then you can draw that x/y coordinate instead.
step = some small value
S = calculateBezierPoint(0, xvals, yvals)
for(t=step; t<1+step; t+=step) {
E = calculateBezierPoint(t, xvals, yvals)
drawLine(S.x, S.y, E.x, E.y)
S = E
}
I think there could be an error in your formula, with operator precedence. I would try using the exponentiation function where appropriate, would make things easier to debug.
Math.pow(base, exponent)
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.
I'm not able to make function trajgen() to calculate x and y and after three days put in trying I still can't find the solution. The whole program is over 200 lines so I'll cross my fingers you to find something false in the following 'problematic' function. :)
Take a look...
function trajgen(){
$(".reddot").remove();
x=xo;
y=yo;
$(".reddot").css({"top": y, "left": x});
isin=true;
while(isin==true){
x = xo + v0*Math.cos(angle)*time;
y = yo + v0*Math.sin(angle)*time - 0.5*g*Math.pow(time,2);
alert("x= "+x);
alert("y= "+y);
$("#frames").append('<div class = "reddot" style = "top: '+ y + 'px; left: '+ x +'px;"><img src="red.png" height="10" width="10"></div>');
time+=1;
isin=inchecker();
}
return;
}
function inchecker(){
if(y<Dy||y>Ay||x<Dx||x>Bx){
isin = false;
return isin;
}
isin = true;
return isin;
}
Now, when I alert x and y this thing returns me NaNs (i know what it means), for the both coordinates. Another interesting aspect is that function inchecker doesn't stop the buggy reddot appending after they get out of the div I'm using as a element where to draw my trajectory. Instead of getting parabolic arc I get infinitely vertically clonning/appending red points. All other variables work properly, x, y, xo, yo, speed, time... and so on are globals. Xo and yo I get as (0;0) from where to launch the projectile and I found out that all other variables work well, the only problem is with those x and y which I haven't used anywhere else so far. I assigned them with zeros but by now this doesn't change the unwillingness of my script to calculate x and y. #frames is the div where all those things happen. Please, help me and thanks in advance.
At the line where you compute x and y, either angle, v0, xo, or vo is null or not a number.
run a debugger and step through those lines, and check the values in place.
I'm trying to implement inertia for drag using D3 version 2.
The latest version of d3 (3.3.10) from the download page does not have it, while pjanik has modified a version of d3 which does that - http://bl.ocks.org/pjanik/raw/5872514/
In any case, how can I implement this in D3 version 2?
A very primitive function to achieve the feel, but would really prefer an elegant formula if anyone has any suggestions, much appreciated:
function inertia(value) {
var remainder = value,
output,
drag = 0.3,
stepTime = 60
//var x = 0
var interval = setInterval(function() {
output = remainder * drag
//x += output
//console.log(x)
//jQuery('.logo').css('margin-left', x)
console.log(output)
remainder = remainder - output
/*a hack to clear when value is small*/
if (output < value * .005)
clearInterval(interval)
}, stepTime)
}
//Any integer to be split for steps, can be distance, time, any value!
inertia(100)