I am working on an animation library and have a variable that is incrementing from 0 to 1 (X axis in the graphic) while a timer is running. I now want to apply easing to my function in a similar fashion as the CSS function cubic-bezier.
My goal is that the y value doesn't increment linearly but instead is converted by the easing function. The function should look something like this:
function cubic-bezier(x, p0x, p0y, p1x, p1y){
// Any suggestions for a formula or algorithm?
return y;
};
I am not using any libraries and can't use any third party libraries on this project.
Thanks!
EDIT: This is the code I came up with. It is an adaption of this code jsfiddle.net/fQYsU.
var p1 = {0.5, 0.5};
var p2 = {0.5, 0.5};
cubic-bezier(t, p1, p2){
var cX = 3 * (p1.x),
bX = 3 * (p2.x - p1.x) - cX,
aX = 1 - cX - bX;
var cY = 3 * (p1.y - 1),
bY = 3 * (p2.y - p1.y) - cY,
aY = -1 - cY - bY;
var x = (aX * Math.pow(t, 3)) + (bX * Math.pow(t, 2)) + (cX * t);
var y = (aY * Math.pow(t, 3)) + (bY * Math.pow(t, 2)) + (cY * t) + 1;
return {x: x, y: y};
}
var y = cubic-bezier(progress, p1, p2).y;
But the value it returns corresponds to x which might not be t. So I still don't know how to get the value at the correct position.
Related
So I have a solution to solving for location (point of intersection) of someone based on landmark angles (312.27) and (19.65) degrees and grid coordinates (1,5) and (9,7) of those landmarks. So the issue I'm having is how can I convert these formulas into something that I can dynamically plugin angles and grid coordinates and return the x and y intersection point for location?
Equations of the Lines based on land marks:
P1: y = cot(312.27)*x + 5 - cot(312.27)*1 ⇒ y = -0.91x + 5.91
P2: y = cot(19.65)*x + 7 - cot(19.65) * 9 ⇒ y = 2.80x - 18.21
solve point of intersection:
P1 = P2
-0.91x + 5.91 = 2.80x - 18.21
5.91 + 18.21 = 2.80x + 0.91x
24.12 = 3.71x
6.5 = x
y = -0.91(6.5) + 5.91
y = 0
Your position is (6.5,0).
So I'm looking at creating a function like:
function getLocation(angle1, angle2, coord1, coord2){}
but I just am having trouble trying to figure out how I can convert this solution into something that would output x and y. As I would have to pass around x or y which is unknown.
Any ideas would be appreciated.
note: angles are converted to radians.
You need to solve the system of equations in terms of the angles and the x,y coordinates:
// let phi1 be the first angle and phi2 be the second angle thus
// let P1 be the first point and P2 be the second point
y = x * cot(phi1) + P1.y - P1.x * cot(phi1)
Similarly
y = x * cot(phi2) + P2.y - P2.x * cot(phi2)
Equating both sides:
x * cot(phi1) + P1.y - P1.x * cot(phi1) = x * cot(phi2) + P2.y - P2.x * cot(phi2)
Solving for x
x * (cot(phi1) - cot(phi2)) = P2.y - P2.x * cot(phi2) - P1.y + P1.x * cot(phi1)
Thus:
x = (P2.y - P2.x * cot(phi2) - P1.y + P1.x * cot(phi1)) / (cot(phi1) - cot(phi2))
Once you get x you can plug x in any of the equations for y:
y = x * cot(phi1) + P1.y - P1.x * cot(phi1)
So to get x and y:
function getLocation(angle1, angle2, coord1, coord2) {
let num = coord2.y - coord2.x * cot(angle2) - coord1.y + coord1.x * cot(angle1)
let den = cot(angle1) - cot(angle2)
let x = num / den
let y = x * cot(angle1) + P1.y - P1.x * cot(angle1)
// do something awesome with x and y
}
The above expression was taken from the below method.
I know that to rotate a point around the center we have to
Move the point to the origin
Make the rotation and
Move the point back
But the pieces I don't get my head around are:
r[0] = x * Math.cos(angle) - y * Math.sin(angle);
^
|
why we use the minus sign here?
r[1] = x * Math.sin(angle) + y * Math.cos(angle);
^
|
And why here we use plus sign instead of minus?
Vec2.prototype.rotate = function (center, angle) {
//rotate in counterclockwise
var r = [];
var x = this.x - center.x;
var y = this.y - center.y;
r[0] = x * Math.cos(angle) - y * Math.sin(angle);
r[1] = x * Math.sin(angle) + y * Math.cos(angle);
r[0] += center.x;
r[1] += center.y;
return new Vec2(r[0], r[1]);
};
The book was to be great but it doesn't explain most of the code it simply spits out.
I got it! Just saw a video of Dr Peyam on transformation matrix.
To get x' and y' we multiply the transformation matrix by the current coordinate (x,y)
enter image description here
I am trying to get a function to calculate if a line, that is defined by 2 coordinates, intersects a circle, defined by a coordinate and a radius. I previously used these 2 functions
getLineEquation(point1, point2) {
var lineObj = {
gradient: (point1.latitude - point2.latitude) / (point1.longitude - point2.longitude)
};
lineObj.yIntercept = point1.latitude - lineObj.gradient * point1.longitude;
return lineObj;
}
checkIntercept(y, m, circle) {
// y: y intercept of line
// m: gradient of line
// get a,b,c values
var a = 1 + (m * m);
var b = -circle.longitude * 2 + (m * (y - circle.latitude)) * 2;
var c = (circle.longitude * circle.longitude) + ((y - circle.latitude)* (y - circle.latitude)) - (circle.range * circle.range);
// get discriminant
var d = (b * b) - 4 * a * c;
if (d >= 0) {
// insert into quadratic formula
var intersections = [
(-b + Math.sqrt((b * b) - 4 * a * c)) / (2 * a),
(-b - Math.sqrt((b * b) - 4 * a * c)) / (2 * a)
];
if (d == 0) {
// only 1 intersection
return [intersections[0]];
}
return intersections;
}
// no intersection
return false;
}
but this didn't work as it converted the 2 points into an infinite line, which I don't want as it would return false readings for circles that aren't actually between the 2 points.
How could I fix these functions to make the calculation into a finite line?
Here is a possible approach.
If one of the two vertices is inside the circle, and the other is outside, then there will be an intersection. (This means two distance calculations.)
If both vertices lie inside the circle, there is no intersection.
If the first two steps weren't decisive, find s, the projection of the circle center to the line (p,q).
If the distance of s to the circle center is larger than the radius, there is no intersection.
Otherwise, if s is between a and b (so s_x between p_x and q_x, and also s_y between p_y and q_y, to include the case of horizontal and vertical edges), then there is an intersection, otherwise not.
To project a point on a line, you basically make a line equation in the form (px,py) + t * (dx,dy) with dx = qx - px and dy = qy - py for two points (px,py) and (qx,qy). The perpendicular line through a point (cx,cy) is then (cx,cy) + t * (-dy,dx). Setting both equations equal finds the intersection.
Or, if you prefer straight formulas:
sx = (d*px + (px - qx)*((cx - px)*(px - qx) + (cy - py)*(py - qy)))/d
sy = (d*py + (py - qy)*((cx - px)*(px - qx) + (cy - py)*(py - qy)))/d
with
d = (px - qx)^2 + (py - qy)^2
So I have an object rotating around an origin point. Once I rotate and then change the origin point. My object seems to jump positions. After the jump it rotates fine... Need help finding the pattern/why it's jumping and what I need to do to stop it.
Here's the rotation code:
adjustMapTransform = function (_x, _y) {
var x = _x + (map.width/2);
var y = _y + (map.height/2);
//apply scale here
var originPoint = {
x:originXInt,
y:originYInt
};
var mapOrigin = {
x:map.x + (map.width/2),
y:map.y + (map.height/2)
};
//at scale 1
var difference = {
x:mapOrigin.x - originPoint.x,
y:mapOrigin.y - originPoint.y
};
x += (difference.x * scale) - difference.x;
y += (difference.y * scale) - difference.y;
var viewportMapCentre = {
x: originXInt,
y: originYInt
}
var rotatedPoint = {};
var angle = (rotation) * Math.PI / 180.0;
var s = Math.sin(angle);
var c = Math.cos(angle);
// translate point back to origin:
x -= viewportMapCentre.x;
y -= viewportMapCentre.y;
// rotate point
var xnew = x * c - y * s;
var ynew = x * s + y * c;
// translate point back:
x = xnew + viewportMapCentre.x - (map.width/2);
y = ynew + viewportMapCentre.y - (map.height/2);
var coords = {
x:x,
y:y
};
return coords;
}
Also here is a JS Fiddle project that you can play around in to give you a better idea of what's happening.
EDITED LINK - Got rid of the originY bug and scaling bug
https://jsfiddle.net/fionoble/6k8sfkdL/13/
Thanks!
The direction of rotation is a consequence of the sign you pick for the elements in your rotation matrix. [This is Rodrigues formula for rotation in two dimensions]. So to rotate in the opposite direction simply subtract your y cosine term rather than your y sine term.
Also you might try looking at different potential representations of your data.
If you use the symmetric representation of the line between your points you can avoid shifting and instead simply transform your coordinates.
Take your origin [with respect to your rotation], c_0, to be the constant offset in the symmetric form.
You have for a point p relative to c_0:
var A = (p.x - c_0.x);
var B = (p.y - c_0.y);
//This is the symmetric form.
(p.x - c_0.x)/A = (p.y - c_0.y)/B
which will be true under a change of coordinates and for any point on the line (which also takes care of scaling/dilation).
Then after the change of coordinates for rotation you have [noting that this rotation has the opposite sense, not the same as yours].
//This is the symmetric form of the line incident on your rotated point
//and on the center of its rotation
((p.x - c_0.x) * c + (p.y - c_0.y) * s)/A = ((p.x - c_0.x) * s - (p.y - c_0.y) * c)/B
so, multiplying out we get
(pn.x - c_0.x) * B * c + (pn.y - c_0.y) * B * s = (pn.x - c_0.x) * A * s - (pn.y - c_0.y) * A * c
rearrangement gives
(pn.x - c_0.x) * (B * c - A * s) = - (pn.y - c_0.y) * (B * s + A * c)
pn.y = -(pn.x - c_0.x) * (B * c - A * s) / (B * s + A * c) + c_0.y;
for any scaling.
I need to have all point coordinates for a given circle one after another, so I can make an object go in circles by hopping from one point to the next. I tried the Midpoint circle algorithm, but the version I found is meant to draw and the coordinates are not sequential. They are produced simultaneously for 8 quadrants and in opposing directions on top of that. If at least they were in the same direction, I could make a separate array for every quadrant and append them to one another at the end. This is the JavaScript adapted code I have now:
function calcCircle(centerCoordinates, radius) {
var coordinatesArray = new Array();
// Translate coordinates
var x0 = centerCoordinates.left;
var y0 = centerCoordinates.top;
// Define variables
var f = 1 - radius;
var ddFx = 1;
var ddFy = -radius << 1;
var x = 0;
var y = radius;
coordinatesArray.push(new Coordinates(x0, y0 + radius));
coordinatesArray.push(new Coordinates(x0, y0 - radius));
coordinatesArray.push(new Coordinates(x0 + radius, y0));
coordinatesArray.push(new Coordinates(x0 - radius, y0));
// Main loop
while (x < y) {
if (f >= 0) {
y--;
ddFy += 2;
f += ddFy;
}
x++;
ddFx += 2;
f += ddFx;
coordinatesArray.push(new Coordinates(x0 + x, y0 + y));
coordinatesArray.push(new Coordinates(x0 - x, y0 + y));
coordinatesArray.push(new Coordinates(x0 + x, y0 - y));
coordinatesArray.push(new Coordinates(x0 - x, y0 - y));
coordinatesArray.push(new Coordinates(x0 + y, y0 + x));
coordinatesArray.push(new Coordinates(x0 - y, y0 + x));
coordinatesArray.push(new Coordinates(x0 + y, y0 - x));
coordinatesArray.push(new Coordinates(x0 - y, y0 - x));
}
// Return the result
return coordinatesArray;
}
I prefer some fast algorithm without trigonometry, but any help is appreciated!
EDIT
This is the final solution. Thanks everybody!
function calcCircle(centerCoordinates, radius) {
var coordinatesArray = new Array();
var octantArrays =
{oct1: new Array(), oct2: new Array(), oct3: new Array(), oct4: new Array(),
oct5: new Array(), oct6: new Array(), oct7: new Array(), oct8: new Array()};
// Translate coordinates
var xp = centerCoordinates.left;
var yp = centerCoordinates.top;
// Define add coordinates to array
var setCrd =
function (targetArray, xC, yC) {
targetArray.push(new Coordinates(yC, xC));
};
// Define variables
var xoff = 0;
var yoff = radius;
var balance = -radius;
// Main loop
while (xoff <= yoff) {
// Quadrant 7 - Reverse
setCrd(octantArrays.oct7, xp + xoff, yp + yoff);
// Quadrant 6 - Straight
setCrd(octantArrays.oct6, xp - xoff, yp + yoff);
// Quadrant 3 - Reverse
setCrd(octantArrays.oct3, xp - xoff, yp - yoff);
// Quadrant 2 - Straight
setCrd(octantArrays.oct2, xp + xoff, yp - yoff);
// Avoid duplicates
if (xoff != yoff) {
// Quadrant 8 - Straight
setCrd(octantArrays.oct8, xp + yoff, yp + xoff);
// Quadrant 5 - Reverse
setCrd(octantArrays.oct5, xp - yoff, yp + xoff);
// Quadrant 4 - Straight
setCrd(octantArrays.oct4, xp - yoff, yp - xoff);
// Quadrant 1 - Reverse
setCrd(octantArrays.oct1, xp + yoff, yp - xoff);
}
// Some weird stuff
balance += xoff++ + xoff;
if (balance >= 0) {
balance -= --yoff + yoff;
}
}
// Reverse counter clockwise octant arrays
octantArrays.oct7.reverse();
octantArrays.oct3.reverse();
octantArrays.oct5.reverse();
octantArrays.oct1.reverse();
// Remove counter clockwise octant arrays last element (avoid duplicates)
octantArrays.oct7.pop();
octantArrays.oct3.pop();
octantArrays.oct5.pop();
octantArrays.oct1.pop();
// Append all arrays together
coordinatesArray =
octantArrays.oct4.concat(octantArrays.oct3).concat(octantArrays.oct2).concat(octantArrays.oct1).
concat(octantArrays.oct8).concat(octantArrays.oct7).concat(octantArrays.oct6).concat(octantArrays.oct5);
// Return the result
return coordinatesArray;
}
Use can try the following approach: use the algorithm you gave but push your coordinates to eight different coordinateArrays. Afterwards you have to reverse half of them (those with (x0+x,y0-y), (x0-x,y0+y), (x0+y,y0+x), (x0-y,y0-x)) and afterwards append all arrays in the correct order. Take care that you add the first four points to the correct arrays.
As far as I know, you cannot do it without trigonometry, but it works pretty fast for me. Sorry I'm not familiar with Java, so I write the code in VB:
Dim PointList As New List(Of PointF)
For angle = 0 To Math.PI * 2 Step 0.01
'the smaller the step, the more points you get
PointList.Add(New PointF(Math.Cos(angle) * r + x0, Math.Sin(angle) * r + y0))
Next
x0 and y0 are the center coordinates of the circle, r is the radius.
Hope I answered your question.
Here is a javascript implementation based on Dave's answer. A bit over-engineered, I wanted to avoid calling sin and cos more than necessary. Ironically making use of Dave's first answer without the radius :)
function calculateCircle(x,y,radius) {
var basicPoints = getBasicCircle();
var i = basicPoints.length;
var points = []; // don't change basicPoints: that would spoil the cache.
while (i--) {
points[i] = {
x: x + (radius * basicPoints[i].x),
y: y + (radius * basicPoints[i].y)
};
}
return points;
}
function getBasicCircle() {
if (!arguments.callee.points) {
var points = arguments.callee.points = [];
var end = Math.PI * 2;
for (var angle=0; angle < end; angle += 0.1) {
points.push({x: Math.sin(angle),
y: Math.cos(angle)
});
}
}
return arguments.callee.points
}