function lerp(start, end, amt) {
return (1-amt)*start+amt*end
}
This lerp function works perfectly with coords. I can easily lerp X from 1 to 10.
But problems appear when it comes to rotation. The rotation of an object is in radians. It can be from -3.14 to 3.14. So let's rotate our object. Starting from 0, one moment the rotation will reach to 3.14, and then... -3.14. So when lerping from 3.14 to -3.14, the object makes one full 360º rotation, (3.14, 2, 1, 0, -1, -2, -3.14) which is not good. So, can anybody tell, how to lerp the rotation?
I am using JavaScript.
Honestly, I don't remember how this works. But, it works.
Its in my code to deal with lerping rotation of a player object to point towards the mouse, when the pointer angle traverses -3.14 to 3.14 this function correctly calculates the lerp across the gap. With, um, magic.
function rLerp (A, B, w){
let CS = (1-w)*Math.cos(A) + w*Math.cos(B);
let SN = (1-w)*Math.sin(A) + w*Math.sin(B);
return Math.atan2(SN,CS);
}
If end value is less than start one, increment it by 2*Pi, and make interpolation.
Map intermediate values into needed range.
Python code (JS one is close)
a0 = 3
a1 = - 3
if a1 < a0:
a1 += 2*math.pi
for i in range(11):
a = a0 * (1 - i/10) + a1 * i / 10
if a > math.pi:
a -= 2*math.pi
print(a)
3.0
3.028318530717959
3.0566370614359175
3.0849555921538756
3.113274122871834
3.141592653589793
-3.113274122871834
-3.084955592153876
-3.056637061435917
-3.028318530717959
-3.0
Related
I have two straight lines as <path> in SVG canvas. Using pixel coordinates of LineA (A1x, A1y) (A2x, A2y) and LineB (B1x, B1y) (B2x, B2y) how can I calculate the angle between these lines.
I have below code which works for THREE points (it works for green cases in below image). It does not work when (A2x, A2y) != (B1x, B1y).
How can I modify this formula to work even when lines are not joined.
function find_angle(p0,p1,c) {
var p0c = Math.sqrt(Math.pow(c.x-p0.x,2)+
Math.pow(c.y-p0.y,2));
var p1c = Math.sqrt(Math.pow(c.x-p1.x,2)+
Math.pow(c.y-p1.y,2));
var p0p1 = Math.sqrt(Math.pow(p1.x-p0.x,2)+
Math.pow(p1.y-p0.y,2));
var angle = Math.acos((p1c*p1c+p0c*p0c-p0p1*p0p1)/(2*p1c*p0c));
return angle * (180 / Math.PI);
}
You can exploit Math.atan2 function with cross product and dot product of direction vectors for these segments. Note the atan2 returns signed angle in range -Pi...Pi
//find vector components
var dAx = A2x - A1x;
var dAy = A2y - A1y;
var dBx = B2x - B1x;
var dBy = B2y - B1y;
var angle = Math.atan2(dAx * dBy - dAy * dBx, dAx * dBx + dAy * dBy);
if(angle < 0) {angle = angle * -1;}
var degree_angle = angle * (180 / Math.PI);
You want P0-C, P1-D instead of P0-C, P1-C: just translate one of the segments to let D and C coincide: P1' = P1 - D + C (then D' = C).
After submitting my answer, I realize this is the same solution as the one provided by #YvesDaoust. That answer is a more concise conceptual summary of the same approach I have fleshed out here with a JavaScript example.
The answer is fairly simple:
function find_disconnected_angle(p0,c0, p1,c1) {
return find_angle({x:p0.x-c0.x+c1.x,y:p0.y-c0.y+c1.y},p1,c1);
}
You can calculate the angle from scratch using trigonometry fundamentals. However, to make your life easier you can also just use the function you already have. First, just mathematically translate one line so that one of its end points coincides with one of the end points of the other line. There are four different ways of matching up one end point from each line, and each way will produce a potentially different angle measure. However, this is no bigger of a dilemma than you having to figure out which angle you want of the four angles you get when you take each of the original untranslated line segments, extend each into an infinite line, and examine the four angles where those two lines intersect.
You need a function that takes 4 points as inputs, i.e. p0, p1, p2 and p3. However, just to make clear which points are being made coincidental, I've instead labeled them as p0, c0, p1 and c1, such that p0 and c0 are both being moved in such a way as to make c0 and c1 coincide, resulting in three points: p0new, p1 and c, the latter of which equals both c1 and c0new.
Update: Upon examining your original function more closely, I realize my discussion above of the choice of four possible angles may not be relevant with the exact function implementation you have written, as the order of points p0 and p1 does not matter for your function. You could rewrite your original function, perhaps using some of the concepts from the other answers, to be able to more fully control which angle you get as a result, if that's really what you want. In any case, the general concept behind my answer holds: if you already have a function that calculates an angle between 3 points (with whatever limitations the algorithm has), you can use the same function on two disconnected line segments by simply translating one so that two end points coincide and then using the same function (again, with whatever limitations the algorithm still has).
function find_angle(p0,p1,c) {
var p0c = Math.sqrt(Math.pow(c.x-p0.x,2)+
Math.pow(c.y-p0.y,2));
var p1c = Math.sqrt(Math.pow(c.x-p1.x,2)+
Math.pow(c.y-p1.y,2));
var p0p1 = Math.sqrt(Math.pow(p1.x-p0.x,2)+
Math.pow(p1.y-p0.y,2));
var angle = Math.acos((p1c*p1c+p0c*p0c-p0p1*p0p1)/(2*p1c*p0c));
return angle * (180 / Math.PI);
}
function find_disconnected_angle(p0,c0, p1,c1) {
return find_angle({x:p0.x-c0.x+c1.x,y:p0.y-c0.y+c1.y},p1,c1);
}
console.log(
find_angle(
{x: 7, y: 2},
{x: 7, y: 7},
{x: 2, y: 2}
)
);
console.log(
find_disconnected_angle(
{x: 27, y: 42},
{x: 22, y: 42},
{x: 7, y: 7},
{x: 2, y: 2}
)
);
finding the angle (#) between two lines:
tan# = (m1-m2)/(1+m1.m2)
where m1 and m2 are the gradient of the lines respectively. In terms of JS:
var m1 = (A1y-A2y)/(A1x-A2x)
var m2 = (B1y-B2y)/(B1x-B2x)
var angle
if(m1*m2==-1){
angle = Math.PI/2
}else{
angle = Math.atan((m1-m2)/(1+m1*m2))
}
I want to make a Tetradecagon, a polygon with 14 sides, with Processing.JS.
(I want to make the Tetradecagon like the one shown in the Image below!)
Using the numbers given in the image, which I would like to replicate, I concluded that each piece (I don't know it's proper name), has an angle of 25.714285714°.....25 and 10/14 = 25 and 5/7 - 5/7 in decimal form = 0.714285714So, I arrived at 25.714285714°
Now, in Processing.JS, I was wanting to use a while loop:
var r = 0;
var draw = function() {
translate(200,200);
while(r < 361){
rotate(r);
r = r + 25.714285714;
line(200,0,200,200);
}
};
Here, I have set one variable, r. r will be the variable for the rotate() function. The while loop will keep going until r meets 360 - this will allow for the the change in r, the angle, to increase by 25.714285714°, while r < 361.
But, sadly, this is not happening. What I see on my canvas is the line being shot off the screen.
(edit) I added translate(200,200); just above the while() loop - this helped, but the lines are still not looking like the picture above.
The second point of the line is not staying in the center; the whole line is being shifted. I only want the first (top) point to be shifted by the given change in angles.
How do I change the code in order to achieve the goal that I am striving for? Any help would be appreciated - Thanks for your time!
P.S. This is my result with the current code -
Processing.js is just for running Processing code. This looks like a mix of Processing and Javascript code so my first advice would be "write real Processing code".
With that said, if you want to do this based on coordinate rotation, look at your 14-gon: it's 14 repeated triangles, so analyze one triangle and draw that 14 times. Any triangular slice is defined by a line from "the center" to "a vertex on the 14-gon" at a (necessary) distance r, the radius of the circumscribing circle. So, given a vertex (r,0) on the 14-gon where is the next vertex (nx,ny)?
Simple maths:
first vertex = (x, y) = (r,0)
next vertex = (nx,ny) = (r,0) rotated over (0,0) by (phi = tau/14)
(I'm using tau here because it's a far more convenient constant for programming purposes. It's simply equal to 2*pi, and as such represents an entire circle, rather than a half circle)
Now, computing that rotate coordinate using basic trigonometry:
nx = r * cos(phi) - 0 * sin(phi) = r * cos(phi)
ny = r * sin(phi) + 0 * cos(phi) = r * sin(phi)
Alright, done. And this nx,ny computation is clearly not specific to the number 14, it about arbitrary angles, so let's code the solution and make it work for any n-sided polygon:
void setup() {
size(400,400);
noLoop();
}
void draw() {
background(255);
// offset the coordinate system so that (0,0) is the sketch center
translate(width/2,height/2);
// then draw a polygon. In this case, radius width/2, and 14 sided
drawNgon(width/2, 14);
}
void drawNgon(float r, float n) {
// to draw (r,0)-(x',y') we need x' and y':
float phi = TAU/n;
float nx = r * cos(phi);
float ny = r * sin(phi);
// and then we just draw that line as many times as there are sides
for(int a=0; a<n; a++) {
// draw line...
line(r,0, nx,ny);
// rotate the entire coordinate system...
rotate(phi);
// repeat until done.
}
}
And now we can freely change both the polygon radius and the number of sides by changing the input to drawNgon(..., ...).
I have these conditions:
Point A, B and C are created.
Point A to Point B will create a line.
Parallel lines are created depending on the position of Point A, B and C (refer to the figure below).
If you move Point A, the lines will also move but Points B and C remain on their respective positions.
They can be moved at any position.
What I want is to create this:
Consider the figure 1 below (I'm sure you already know this basic 2D geometry but without this my answer would be incomplete):
Coordinates for points A and B are known and we want to find function that can be used to calculate y-coordinate whenever x-coordinate is known, in such a way that point (x,y) lies on the line. From the figure 1:
k = tan(alpha) = (y2 - y1) / (x2 - x1) - the slope of line
Putting coordinates of either A or B into well known line equation y = kx + m we can calculate m to make the equation complete. Having this equation, for any coordinate x we can calculate coordinate y using this equation. The good thing about it is that it doesn't depend on the position of point A and B or slop (angle) of the line - you will have to take care of the special case of vertical/horizontal lines where y/x will be infinite according to this equation.
Back to your question. Take a look at figure 2 below:
We have very similar situation here, there is a line between points A and C, and line between points B and D. I assumed that point A is at the center of the coordinate system! This generally won't be the case but this is really not a restriction as you can perform translation that will put A in the center, then make your calculations and then translate everything back.
Using the technique described at the beginning, you can find the line equation for the line that connects A and C points and for the line that connects B and D points (D coordinates can be easily calculated). Let's assume you did just that:
A-C: y = k1*x (m is zero as line goes through the center A)
B-D: y = k2*x + m2 (m2 is not zero as line doesn't go through the center A)
Finally the algorithm you could use to draw these parallel lines:
Choose a space with which you want to take x-coordinates between x1 and x3. For example, if you want 4 lines this space will be s = (x3 - x1) / 4 and so on.
Set value x_start = x1 + s (and later x_start += s), and calculate y-coordinate using the equation for A-C line y_end = k1*x_start. This will give you point that lies on the line A-C and this is the start of your line.
Similarly, calculate the end point that will lie on the line that connects B and D:
x_end = x2 + s (later x_end += s)
y_end = k2*x_end + m2
Using these equations calculate points (x_start,y_start) and (x_end,y_end) for all lines that you want to draw (there is |x3 - x1| / desired_num_of_lines of them).
You'll have to form new equations each time point A moves out of the current A-C line, as every time this happens the slop of the A-C (and B-D) line changes invalidating the current equations.
I'm not going to write any JS code, but having the logic behind the possible solution should give you more then enough information to move forward with you own implementation.
Always think, when using the Context2D, that using the transforms (translate, rotate, scale), can spare you some math.
With those transforms you can think of your drawing like you would do with a pen : where do you put the pen ? where do you move next (translate) ? do you rotate the page ? do you get closer or further from the page (scale) ?
Here you want to start at A, then move along AC.
Each step on the way, you want to draw the AB vector.
Here's how you could code it, as you see, just simple vector math here, so if you remember that AB vector has (B.x-A.x, B.y-A.y) coordinates, you know most of the math you'll need.
// boilerPlate
var ctx = document.getElementById('cv').getContext('2d');
ctx.strokeStyle = '#000';
// params : Points : {x,y}
var A, B, C;
A = { x: 20, y: 170 };
B = { x: 80, y: 60 };
C = { x: 140, y: 120 };
// param : number of lines to draw.
var stepCount = 5;
// ----------
// compute AB vector = B - A
var AB = { x: B.x - A.x, y: B.y - A.y };
// compute step : ( C - A ) / stepCount
var step = { x: (C.x - A.x) / stepCount, y: (C.y - A.y) / stepCount };
// -- start draw
ctx.save();
// Move pen to A
ctx.translate(A.x, A.y);
for (var i = 0; i <= stepCount; i++) {
// draw AB vector at current position
ctx.lineWidth= ( i==0 || i==stepCount ) ? 2 : 1 ;
drawVector(AB);
// move pen one step further
ctx.translate(step.x, step.y);
}
ctx.restore();
// --
// draws vector V at the current origin ((0,0)) of the context.
function drawVector(V) {
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(V.x, V.y);
ctx.stroke();
}
// ----------
// legend
drawPoint(A, 'A');
drawPoint(B, 'B');
drawPoint(C, 'C');
function drawPoint(P, name) {
ctx.beginPath();
ctx.arc(P.x, P.y, 3, 0, 6.28);
ctx.fill();
ctx.strokeText(name, P.x + 6, P.y + 6);
}
<canvas id='cv' width=300 height=200></canvas>
Džanan has it right, and in simple terms, you need the X and Y offsets between the starting points of the two lines, i'e' point A and point C. When drawing the line that starts at C, and assuming that it ends at D, you will need to add the same X and Y offsets, e.g., if you draw AB with starting coordinates (100, 150) as follows:
context.beginPath();
context.moveTo(100, 150);
context.lineTo(450, 50);
context.stroke();
And if C has to start at (150, 200), the offset here would be
X: 50, Y:50
so CD would be drawn as
context.beginPath();
context.moveTo(150, 200);
context.lineTo((450+50), (50+50));
context.stroke();
Now this assumes that the length of both the lines are going to be same. If they are to differ, the equation will be slightly more complex.
I am trying to rotate a vector [x,y] around the origin such that when the rotation is completed it lies on the X axis. In order to do this, I'm first computing the angle between [x,y] and [1,0], then applying a simple 2D rotation matrix to it. I'm using numericjs to work with the vectors.
math.angleBetween = function(A, B) {
var x = numeric.dot(A, B) / (numeric.norm2(A) * numeric.norm2(B));
if(Math.abs(x) <= 1) {
return Math.acos(x);
} else {
throw "Bad input to angleBetween";
}
};
math.alignToX = function(V) {
var theta = -math.angleBetween([1,0], V);
var R = [[Math.cos(theta), -Math.sin(theta)],
[Math.sin(theta), Math.cos(theta)]];
return numeric.dot(R, V);
};
(Note: math is a namespace object within my project. Math is ye olde math object.)
This code works sometimes, however there are occasions where no matter how many times I run math.alignToX the vector never even gets close to aligning with the X axis. I'm testing this by checking if the y coordinate is less than 1e-10.
I've also tried using Math.atan2 with an implicit z coordinate of 0, but the results have been the same. Errors are not being thrown. Some example results:
math.alignToX([152.44444444444434, -55.1111111111111])
// result: [124.62691466033475, -103.65652585400568]
// expected: [?, 0]
math.alignToX([372, 40])
// result: [374.14435716712336, -2.0605739337042905e-13]
// expected: [?, 0]
// this value has abs(y coordinate) < 1e-10, so its considered aligned
What am I doing wrong?
If you're rotating something other than your vector, then you'll need to use your R matrix. But if you just need to rotate your vector, the result will be [Math.sqrt(x*x+y*y),0].
Actually, the task of building a rotation matrix that aligns a known 2d vector with [1, 0] doesn't require any trigonometric functions at all.
In fact, if [x y] is your vector and s is its length (s = Sqrt(x*x + y*y)), then the transformation that maps [x y] to align with [1 0] (pure rotation, no scaling) is just:
[ x y]
T = 1/s^2 [-y x]
For example, suppose your vector is [Sqrt(3)/2, 1/2]. This is a unit vector as you can easily check so s = 1.
[Sqrt(3)/2 1/2 ]
T = [ -1/2 Sqrt(3)/2]
Multiplying T by our vector we get:
[Sqrt(3)/2 1/2 ][Sqrt(3)/2] [1]
T = [ -1/2 Sqrt(3)/2][ 1/2 ] = [0]
So in finding the rotation angle (which in this case is Pi/6) and then creating the rotation matrix, you've just come full circle back to what you started with. The rotation angle for [Sqrt(3)/2, 1/2] is Pi/2, and cos(Pi/2) is Sqrt(3)/2 = x, sin(pi/2) is 1/2 = y.
Put another way, if you know the vector, you ALREADY know the sine and cosine of it's angle with the x axis from the definition of sine and cosine:
cos a = x/s
sin a = y/s where s = || [x, y] ||, is the length of the vector.
My problem is so mind-bendingly obvious that I cannot believe I didn't see it. While I'm checking the domain of Math.acos, I'm not checking the range at all! The problem occurs when the vector lies outside of the range (which is [0,PI]). Here is what I did to fix it:
math.alignToX = function(V) {
var theta = -math.angleBetween([1,0], V);
var R = [[Math.cos(theta), -Math.sin(theta)],
[Math.sin(theta), Math.cos(theta)]];
var result = numeric.dot(R, V);
if(Math.abs(result[1]) < ZERO_THRESHOLD) {
return result;
} else {
V = numeric.dot([[-1, 0], [0, -1]], V); // rotate by PI
theta = -math.angleBetween([1,0], V);
R = [[Math.cos(theta), -Math.sin(theta)],
[Math.sin(theta), Math.cos(theta)]];
result = numeric.dot(R, V);
if(Math.abs(result[1]) < ZERO_THRESHOLD) {
return result;
} else {
throw "Unable to align " + V; // still don't trust it 100%
}
}
};
For the broken example I gave above, this produces:
[162.10041088743887, 2.842170943040401e-14]
The Y coordinate on this result is significantly less than my ZERO_THRESHOLD (1e-10). I almost feel bad that I solved it myself, but I don't think I would have done so nearly as quickly had I not posted here. I saw the problem when I was checking over my post for typos.
I've been trying to solve this problem for a number of days now but I must be missing something.
Known Variables:
vi = Initial Velocity
t = Animation Duration
d = Distance.
end velocity should always be zero
The function I'm trying to create: D(0...t) = the current distance for a given time
Using this information I want to be able to create a smooth animation curve with varying velocity (ease-in/ease-out).
The animation must be able ease-in from an initial velocity.
The animation must be exactly t seconds and must be travel exactly d units.
The curve should lean towards the average velocity with acceleration occurring at the beginning and the end portions of the curve.
I'm open to extra configuration variables.
The best I've been able to come up with is something that doesn't factor in the initial velocity. I'm hoping someone smarter can help me out. ;)
Thank you!
p.s. I'm working with an ECMAScript variant
Here is a different solution, where there isn't any time interval where the velocity is constant. Instead, velocity as a function of time is a second-order polynomial, and the acceleration is linear in time (positive at the beginning, and negative at the end). Maybe you can try it.
Let me rename your variables a bit. Let
T = final time = animation duration
V = initial velocity (>0)
D = total distance (>0)
We are searching for a smooth function v(t) (velocity as a function of time) such that:
v(0) = V
v(T) = 0
the integral from 0 to T of v(t)dt is D
With a (concave) second-order polynomial we can satisfy all the three constraints. Hence, let
v(t) := at^2 + bt + c
and let's solve for a, b, c. The first constraint v(0) = V gives immediately c = V.
The second constraint reads
aT^2 + bT + V = 0
On the other hand, the integral of v(t) is d(t) = 1/3 a t^3 + 1/2 b t^2 + Vt (this is the distance covered up to time t), hence the third constraint reads
d(T) = 1/3 a T^3 + 1/2 b T^2 + VT = D
The last two equations seem messy, but they are just two linear equations in the two unknowns a, b, and they should be easily solvable. If I did my computations correctly, the final result is
a = 3V/T^2 - 6D/T^3, b = 6D/T^2 - 4V/T
If you substitute a, b, c in the expression of d(t), you obtain the covered distance as a function of time.
I believe you want to solve your problem in 3 parts.
First, you need to solve for the minimum velocity necessary to complete the distance in time T.
This would be pretty simple (D/t) = v(min)
It assumes instantaneous acceleration from v(initial) to v(min) and again deceleration over a time period of 0s at the beginning and end.
so for example say your v(i) is 5px/s.
you want a 100px movement over 10 seconds.
v(min) = 100px/10s = 10px/s
Second, you want a smooth acceleration from v(initial) to v(min). this will take some period of time t(acc). Assuming the acceleration and deceleration will be equal, then you can just figure for one of them, then multiply by 2.
We can call the function that describes the distance travelled during accelleration D(accel).
Lets keep it easy to start and say we want the duration of accelleration to be 1s
so your equation for total distance travelled is going to be
D(total) = D(accel) + D(v(max) )
When you know that D(accel) is for 2s total, you can calculate
D(accel) = ( V(ini) + V(max) ) /2) * (2seconds)
and
D(v(max)) = V(max) * 8s
solving for V(max) we get
100px = D(accel) + D(v(max))
100px = ( 5px/s + VMax) /2 *(2s)) + VMax *8s
100px = 5px + (Vmax * 1s) + Vmax *8s
95px = 9Vmax *s
VMax = 95px/9s
VMax = 10.556px/s
You can now go back and replace your 1s acceleration window with a formula that defines the acceleration window as a % of the overall time period or some other thing.
Also note that for animation purposes, you will have to break 10.556px/s down into per frame px movements and account for time appropriately.
abustin, since you don't like the 3 segment solution, you may want to try looking at Bezier curves to solve this problem. Bezier curve can be utilized to interpolate time as well as distance, so you can define a few control points near the ends of your motion to generate acceleration, where the middle "segment" will be defined such that the velocity is close to constant. Utilizing splines is a possibility too.
Using constant accelerations:
Definitions:
Vi - Initial velocity
Va - Average velocity
Vo - Ending velocity
D - Total distance to be traveled
T - Total time for travel
t1 - Acceleration time from beginning to Va
t2 - Acceleration time from Va to Vo
The equation to solve is:
(Vi+Va)/2*t1 + Va*(T-t2-t1) + (Va+Vo)/2*t2 = D
You should decide how much time the initial acceleration (t1) and the final acceleration (t2) takes and then you are left with only one unknown -> Va, which can be easily solved.
EDIT: find the distance as a function of time:
Well, now that you know the velocities, it is easy to figure out the distance traveled:
D(t) = Vi*t + 0.5*t^2*(Va-Vi)/t1 {0<t<t1}
D(t) = Va*(t-t1) + D1 {t1<t<t3}
D(t) = Va*(t-t3)+0.5*(t-t3)^2*(Vo-Va)/t2 + D2 {t3<t<T}
where t3=(T-t2), D1 and D2 are the distance traveled at the end of the first and second segments, which can be found from the respective functions:
D1 = 0.5*(Va+Vi)*t1
D2 = D1 + Va*(t3-t1)
EDIT 2: Solving for Va:
(Vi+Va)/2*t1 + Va*(T-t2-t1) + (Va+Vo)/2*t2 = D
Remember that t1 and t2 are the problem parameters that you choose. You decide what fraction of the movement the object accelerates and decelerates. Let's say t1=t2=0.1*T. Substitution then gives:
(Vi+Va)/2*(0.1T) + Va*(T-(0.1T)-(0.1T)) + (Va+Vo)/2*(0.1T) = D
Va*(0.1T/2 + T-0.1T-0.1T + 0.1T/2) + Vi*(0.1T)/2 + Vo*(0.1T)/2 = D
Va*(0.9T) + Vi*(0.05T) + Vo*(0.05T) = D
Va*(0.9T) = D - Vi*(0.05T) + Vo*(0.05T)
Va = (D - (Vi + Vo)*(0.05T)) / (0.9T)
Got it?
Federico's solution is a nice one, but I found the acceleration of a linear acceleration solution a bit too abrupt, and I ended up forging ahead with a double parabola solution where the acceleration is constant, first in one direction and then in the other, until the object ends at 1 with a velocity 0. (I did try solving it with variable starts and ends, but it was too difficult. Instead, my implementation just scales the inputs and outputs before passing them through the function.)
The math was high school stuff, but I'll go over it for completeness' sake.
given v, the initial velocity, we have two parabolas, the left and the right
ly = m(t - ax)^2 + ay, where t is the time input, ranging from 0 to 1, and ax, ay, and m are constants we need to find, given v.
ry = -m(t - 1)^2 + 1
Note, they both take m as their steepness, because they accelerate at the same rate. ry uses -m because it accelerates in the other direction.
We have a number of constraints to solve for
ly(0) = 0, the value is 0 at t = 0
ly'(0) = v, the differential of the equation(the velocity) is v(the initial velocity, given) at t = 0
ly(h) = ry(h), the two parabolas connect at some given halfway point (which is not actually halfway unless v = 0)
ly'(h) = ry'(h), the velocity is the same at that halfway point, no sudden jerks
I went through a number of approaches from here on out, but in the end it seemed that the only way was to solve for m in terms of v. We arrive at the formula mm + m(v - 2) - (vv)/4. Applying the quadratic formula, we get m = ((2 - v) ± sqrt(2vv - 4v + 4))/2
Meaning that either
m = ((2 - v) + sqrt(2v*v - 4v + 4))/2
or
m = ((2 - v) - sqrt(2v*v - 4v + 4))/2
And we find that we can decide which it is by looking at the initial velocity,
let sqrt_part = Math.sqrt(2*sq(initial_velocity) - 4*initial_velocity + 4)
let m = (2 - initial_velocity + (initial_velocity < 2 ? sqrt_part : -sqrt_part))/2
from there, the rest of the variables (ax, ay and h) are quite easy to work out.
There is a rust implementation of the formulai here https://gist.github.com/makoConstruct/df173c3a4e0854b535869fdc2acdeeb1
Rust's syntax is pretty ordinary, so you wont have much trouble translating that to JS. Feel free to post your port in the comments.