I have the coordinate of point A and another point (like B, C or D).
I also have the distance between A and the other point.
I know the maximum allowed distance between A and the other point (illustrated with the purple line and the imaginary circle).
Question: How do I find the coordinates of the red points (B1 or C1 or D1).
Example: A=(-1,1), E=(3,-8), Max allowed distance = 4. What is the coordinate of point E1?
Here is an image of the problem:
Note:
I found 2 other questions that are pretty similar or equal but I'm not able to work it out with those:
Finding coordinates of a point between two points?
How can I find a point placed between 2 points forming a segment using only the partial length of the segment?
P.S. This is not homework I need this for a programming problem but I forgot my Maths...
Assuming A is a position vector, B is a position vector, and maxLength is the max length you're allowing for.
A and B are Vector2's (as you tagged this question xna).
// Create a vector that describes going from A to B
var AtoB = (B - A);
// Make a vector going from A to B, but only one unit in length
var AtoBUnitLength = Vector2.Normalize(AtoB);
// Make a vector in the direction of B from A, of length maxLength
var AtoB1 = AtoBUnitLength * maxLength;
// B1 is the starting point (A) + the direction vector of the
// correct length we just created.
var B1 = A + AtoB1;
// One liner:
var B1 = A + Vector2.Normalize(B - A) * maxLength;
Related
I've been working on a small project for some days, everything was working fine until I changed my "map" implementation to be the same as in the game (Dofus) I'm based on (it's a little helper for the community).
Basically, I've a grid layout rotated at 45° (see image below), contructed from top left to bottom right. Every cell as an xIndex and zIndex to represent where it is (xIndex ; zIndex) on the image, and I just want to get the distance between two cells, without traveling diagonally.
As I tried to explain on the picture:
GetDistanceBetweenTiles(A, B) should be 3
GetDistanceBetweenTiles(A, C) should be 5
GetDistanceBetweenTiles(B, C) should be 2
I found the "Manhattan distance" which looks like it is what I want, but it's not giving me the values above.
Here is the code:
private int GetDistanceBetweenTiles(MovableObject a, MovableObject b)
{
//int dist = Mathf.Abs(a.xIndex - b.xIndex) + Mathf.Abs(a.zIndex - b.zIndex);
int minX = a.xIndex < b.xIndex ? a.xIndex : b.xIndex;
int maxX = a.xIndex > b.xIndex ? a.xIndex : b.xIndex;
int minZ = a.zIndex < b.zIndex ? a.zIndex : b.zIndex;
int maxZ = a.zIndex > b.zIndex ? a.zIndex : b.zIndex;
int distX = (maxX - minX);
int distZ = (maxZ - minZ);
int dist = Mathf.Abs(maxX - minX) + Mathf.Abs(maxZ - minZ);
print($"Distance between {a.name} and {b.name} is {dist}");
return dist;
}
Any help would be gladly appreciated.
If it can help, here is the project working with the first map implementation I did (but not translated yet).
Let make new coordinates in inclined rows with simple formulae:
row = z/2 - x ("/" for **integer division**)
col = z - row
Now we can just calculate Manhattan distance as
abs(row2 - row1) + abs(col2 - col1)
For your example
x z r c
4, 2 => -3, 5
1, 4 => 1, 4
distance = (1-(-3)) + (5-4) = 4 + 1 = 5
To explain: your grid rotated by 45 degrees:
0 1 2 3 4 5 6 7 8 \column
40|41 row -4
30|31|42|43 row -3
20|21|32|33|44|45 row -2
10|11|22|23|34|35|46|47 row -1
00|01|12|13|24|15|36|37|48 row 0
02|03|14|15|26|27|38 row 1
04|05|16|17|28 row 2
06|07|18 row 3
The "No-Maths" solution
I maybe have a workaround solution for you. I'm kind of a lazy person and very bad in maths ... so I usually let Unity do the maths for me in situations like yours ;)
For that you would need one dedicated GameObject that is rotated in the way that it represents the grid "rotation" so 0,45,0.
Then - since your tiles move always in steps of exactly 1 just in the rotated coordinate system - you could inetad of using an index based distance rather directly compare the absolute positions using Transform.InverseTransformPoint in order to get the positions relative to that rotated object.
InverseTransformPoint retuns as said the given world position in the local space of the used transform so that if the object was originally placed at e.g. x=1, z=1 in our rotated local space it will have the position z=1.1414..., x=0.
I simply attached this component to my rotated object .. actually I totate in Awake just to be sure ;)
public class PositionsManager : MonoBehaviour
{
// I know .. singleton pattern .. buuu
// but that's the fastest way to prototype ;)
public static PositionsManager Singleton;
private void Awake()
{
// just for making sure this object is at world origin
transform.position = Vector3.zero;
// rotate the object liek you need it
// possible that in your case you rather wanted -45°
transform.eulerAngles = new Vector3(0, 45, 0);
// since InverseTransformPoint is affacted by scale
// just make sure this object has the default scale
transform.localScale = Vector3.one;
// set the singleton so we can easily access this reference
Singleton = this;
}
public Vector2Int GetDistance(Transform from, Transform to)
{
var localPosFrom = transform.InverseTransformPoint(from.position);
var localPosTo = transform.InverseTransformPoint(to.position);
// Now you can simply get the actual position distance and return
// them as vector2 so you can even still see the components
// seperately
var difference = localPosTo - localPosFrom;
// since you are using X-Z not X-Y you have to convert the vector "manually"
return new Vector2Int(Mathf.RoundToInt(difference.x), Mathf.RoundToInt(difference.z));
}
public int GetAbsoluteDistance(Transform from, Trasnform to)
{
var difference = GetDistance(from, to);
return Mathf.Abs(difference.x) + Mathf.Abs(difference.y);
}
}
Now when you need to get the absolute distance you could simply do
var difference = PositionsManager.Singleton.GetDistance(objectA.transform, objectB.transform);
var absoluteDistance = PositionsManager.Singleton.GetAbsoluteDistance(objectA.transform, objectB.transform);
Little Demo (used a chess board drawer since I had that ^^)
The maths solution
It just came to me while writing the upper explenation:
You already know your steps between the tiles: It is allways Mathf.Sqrt(2)!
So again you could simply use the absolute positions in your world and compare them like
private float Sqrt2;
private void Awake()
{
Sqrt2 = Mathf.Sqrt(2);
}
...
// devide the actual difference by Sqrt(2)
var difference = (objectA.position - objectB.position) / Mathf.Sqrt(2);
// again set the Vector2 manually since we use Z not Y
// This step is optional if you anyway aren't interrested in the Vector2
// distance .. jsut added it for completeness
// You might need the rounding part though
var fixedDifference = new Vector2Int(Mathf.RoundToInt(difference.x), Mathf.RoundToInt(difference.z));
// get the absolute difference
var absoluteDistance = Mathf.Abs(fixedDifference.x) + Mathf.Abs(fixedDifference.y);
...
still completely without having to deal with the indexes at all.
I am trying to draw an arc between two points that represents a projectile's path. The angle that the projectile leaves point A at is known, and the X/Y coordinates of both points are known.
I'm trying to figure out the math behind this, and how to draw it up in c#.
Here is my failed attempt, based on some path examples I found
var g = new StreamGeometry();
var xDistance = Math.Abs(pointA.X - pointB.X);
var yDistance = Math.Abs(pointA.Y - pointB.Y);
var angle = 60;
var radiusX = xDistance / angle;
var radiusY = yDistance / angle;
using (var gc = g.Open())
{
gc.BeginFigure(
startPoint: pointA,
isFilled: false,
isClosed: false);
gc.ArcTo(
point: pointB,
size: new Size(radiusX, radiusY),
rotationAngle: 0d,
isLargeArc: false,
sweepDirection: SweepDirection.Clockwise,
isStroked: true,
isSmoothJoin: false);
}
Any help would be greatly appreciated!
Edit #2 (added clarity): For this problem assume that physics play no role (no gravity, velocity, or any outside forces). The projectile is guaranteed to land at point B and move along a parabolic path. The vertex will be halfway between point A and point B on the horizontal axis. The angle that it launches at is the angle up from the ground (horizontal).
So Point A (Ax, Ay) is known.
Point B (Bx, By) is known.
The angle of departure is known.
The X half of the vertex is known (Vx = Abs(Ax - Bx)).
Does this really boil down to needing to figure out the Y coordinate of the vertex?
Following on from the comments, we need a quadratic Bezier curve. This is defined by 3 points, the start, end, and a control point:
It is defined by the following equation:
We therefore need to find P1 using the given conditions (note that the gravity strength is determined implicitly). For a 2D coordinate we need two constraints / boundary conditions. They are given by:
The tangent vector at P0:
We need to match the angle to the horizontal:
The apex of the curve must be directly below the control point P1:
Therefore the vertical coordinate is given by:
[Please let me know if you would like some example code for the above]
Now for how to add a quadratic Bezier; thankfully, once you have done the above, it is not too difficult
The following method creates the parabolic geometry for the simple symmetric case. The angle is measured in degrees counterclockwise from the horizontal.
public Geometry CreateParabola(double x1, double x2, double y, double angle)
{
var startPoint = new Point(x1, y);
var endPoint = new Point(x2, y);
var controlPoint = new Point(
0.5 * (x1 + x2),
y - 0.5 * (x2 - x1) * Math.Tan(angle * Math.PI / 180));
var geometry = new StreamGeometry();
using (var context = geometry.Open())
{
context.BeginFigure(startPoint, false, false);
context.QuadraticBezierTo(controlPoint, endPoint, true, false);
}
return geometry;
}
A body movement subject only to the force of gravity (air resistance is ignored) can be evaluated with the following equations:
DistanceX(t) = dx0 + Vx0·t
DistanceY(t) = dy0 + Vy0·t - g/2·t^2
Where
g : gravity acceleration (9.8 m/s^2)
dx0 : initial position in the X axis
dy0 : initial position in the Y axis
Vy0 : initial X velocity component (muzzle speed)
Vy0 : initial Y velocity component (muzzle speed)
Well that doesn't seem very helpful, but lets investigate further. Your cannon has a muzzle speed V we can consider constant, so Vx0 and Vy0 can be written as:
Vx0 = V·cos(X)
Vy0 = V·sin(X)
Where X is the angle at which you are shooting. Ok, that seems interesting, we finally have an input that is useful to whoever is shooting the cannon: X. Lets go back to our equations and rewrite them:
DistanceX(t) = dx0 + V·cos(X)·t
DistanceY(t) = dy0 + V·sin(X)·t - g/2·t^2
And now, lets think through what we are trying to do. We want to figure out a way to hit a specific point P. Lets give it coordinates: (A, B). And in order to do that, the projectile has to reach that point in both projections at the same time. We'll call that time T. Ok, lets rewrite our equations again:
A = dx0 + V·cos(X)·T
B = dy0 + V·sin(X)·T - g/2·T^2
Lets get ourselves rid of some unnecessary constants here; if our cannon is located at (0, 0) our equations are now:
A = V·cos(X)·T [1]
B = V·sin(X)·T - g/2·T^2 [2]
From [1] we know that: T = A/(V·cos(X)), so we use that in [2]:
B = V·sin(X)·A/(V·cos(X)) - g/2·A^2/(V^2·cos^2(X))
Or
B = A·tan(X) - g/2·A^2/(V^2*cos^2(X))
And now some trigonometry will tell you that 1/cos^2 = 1 + tan^2 so:
B = A·tan(X) - g/2·A^2/V^2·(1+tan^2(X)) [3]
And now you have quadratic equation in tan(X) you can solve.
DISCLAIMER: typing math is kind of hard, I might have an error in there somewhere, but you should get the idea.
UPDATE The previous approach would allow you to solve the angle X that hits a target P given a muzzle speed V. Based on your comments, the angle X is given, so what you need to figure out is the muzzle speed that will make the projectile hit the target with the specified cannon angle. If it makes you more comfortable, don't think of V as muzzle speed, think of it as a form factor of the parabola you are trying to find.
Solve Vin [3]:
B = A·tan(X) - g/2·A^2/V^2·(1+tan^2(X))
This is a trivial quadratic equation, simply isolate V and take the square root. Obviously the negative root has no physical meaning but it will also do, you can take any of the two solutions. If there is no real number solution for V, it would mean that there is simply no possible shot (or parabola) that reaches P(angle X is too big; imagine you shoot straight up, you'll hit yourself, nothing else).
Now simply eliminate t in the parametrized equations:
x = V·cos(X)·t [4]
y = V·sin(X)·t - g/2·t^2 [5]
From [4] you have t = x/(V·cos(X)). Substitute in [5]:
y = tan(X)·x - g·x^2 /(2·V^2*cos^2(X))
And there is your parabola equation. Draw it and see your shot hit the mark.
I've given it a physical interpretation because I find it easier to follow, but you could change all the names I've written here to purely mathematical terms, it doesn't really matter, at the end of the day its all maths and the parabola is the same, any which way you want to think about it.
I am trying to code for a game I am working on a specific curve with a specific rotation. I am not a great mathematician... At all... Tried searching for solutions for a few hours, but I'm affraid I do not find any solution.
So, a small picture to illustrate first:
This is an eighth of a circle, radius of 9, beggining is (0,0)
The end is now at about 6.364, -2.636. But I need this same curve, with a 45° direction at the end, but ending at aexactly 6.0,-3.0.
Could any of you show me how to do this? I need to be able to calculate precisly any point on this curve & its exact length. I would suppose using some kind of eliptical math could be a solution? I admit my math class are reaaaly far now and have now good clue for now...
Thank for any possible help
I think I found a quadratic curve which sastisfies your requirement:
f(x) = -1/12 x^2 + 9
Copy the following into https://www.desmos.com/calculator to see it:
-\frac{1}{12}x^2+9
f'(x) would be -1/6x, so when x=6, the derivative would be -1, which corresponds to a -45° inclination. There are probably infinite curves that satisfy your requirement but if my calculus isn't too rusty this is one of them.
I tried to fit an ellipse with foci starting at y=6 here and starting at y=9 here to your points but the slope doesn't look like 45°.
Also starting at any height k, here doesn't seem to work.
I don't think you've fully understood the question I asked in the comments about the "inclination" angle. So I will give a general case solution, where you have an explicit tangent vector for the end of the curve. (You can calculate this using the inclination angle; if we clarify what you mean by it then I will be happy to edit with a formula to calculate the tangent vector if necessary)
Let's draw a diagram of how the setup can look:
(Not 100% accurate)
A and B are your fixed points. T is the unit tangent vector. r and C are the radius and center of the arc we need to calculate.
The angle θ is given by the angle between BA and T minus π/2 radians (90 degrees). We can calculate it using the dot product:
The (signed) distance from the center of AB to C is given by:
Note that this is negative for the case on the right, and positive for the left. The radius is given by:
(You can simplify by substituting and using a cosine addition rule, but I prefer to keep things in terms of variables in the diagram). To obtain the point C, we need the perpendicular vector to AB (call it n):
Now that we have the radius and center of the circular arc, we still need to determine which direction we are moving in, i.e. whether we are moving clockwise or anti-clockwise when going from A to B. This is a simple test, using the cross-product:
If this is negative, then T is as in the diagram, and we need to move clockwise, and vice versa. The length of the arc l, and the angular displacement γ when we move by a distance x along the arc:
Nearly there! Just one more step - we need to work out how to rotate the point A by angle γ around point C, to get the point we want (call it D):
(Adapted from this Wikipedia page)
Now for some code, in case the above was confusing (it probably was!):
public Vector2 getPointOnArc(Vector2 A, Vector2 B, Vector2 T, double x)
{
// calculate preliminaries
Vector2 BA = B - A;
double d = BA.Length();
double theta = Math.Acos(Vector2.DotProduct(BA, T) / d) - Math.PI * 0.5;
// calculate radius
double r = d / (2.0 * Math.Cos(theta));
// calculate center
Vector2 n = new Vector2(BA.y, -BA.x);
Vector2 C = 0.5 * (A + B + n * Math.Tan(theta));
// calculate displacement angle from point A
double l = (Math.PI - 2.0 * theta) * r;
double gamma = (2.0 * Math.PI * x) / l;
// sign change as discussed
double cross = T.x * BA.y - T.y * BA.x;
if (cross < 0.0) gamma = -gamma;
// finally return the point we want
Vector2 disp = A - C;
double c_g = Math.Cos(gamma), s_g = Math.Sin(gamma);
return new Vector2(disp.X * c_g + disp.Y * s_g + C.X,
disp.Y * c_g - disp.X * s_g + C.Y);
}
I get the bezier value from a path like this (note: If there is another/better way to do that let me know, please):
public static Vector3 PathCubic (float t, Vector3[] path)
{
if (t >= 1f)
return path[path.Length - 1];
// projects t in the path
var projT = path.Length / 3 * t;
// what interval t is (between P1 and P2 or between P2 and P3, etc)
var range = (int)projT;
// get the interval index
var i = range * 3;
var p0 = path[i + 0]; // first point
var c0 = path[i + 1]; // control 1
var c1 = path[i + 2]; // control 2
var p1 = path[i + 3]; // second point
// calculate bezier in the current interval
return Cubic(projT - range, p0, c0, c1, p1);
}
So, supposing points P1, P2, P3, P4, t = 0.0 is the first point P1 and t = 1.0 is the last point P4.
But, that doesn't give me a constant moviment over the bezier. Image bellow depicts what I mean. A t = 0.5 give me different positions depending on points location.
I found I have to calculate the length of the path in order to achieve that. And here comes the problem. Once calculated this length, how can I calculate the interval index, as I did before? Or I will need to walk through each interval (for instruction) in order to find that?
// get the ***interval index***
var i = range * 3;
var p0 = path[i + 0]; // first point
var c0 = path[i + 1]; // control 1
var c1 = path[i + 2]; // control 2
var p1 = path[i + 3]; // second point
There is no true solution but here is a usefull approximation:
First get the length of each segment. Let's say it's:
// |a b| is the operator of length between two points a and b
A=|P1 P2|=3.0
B=|P2 P3|=1.2
C=|P3 P4|=0.8
L=A+B+C=5.0 // length of the whole spline
Your t=0.5 and is between points P1 and P2 - we know that because t*L = 2.5 and length A=3 (and A is first, so the immaginary length before was 0).
We also know that the value of t that we want should be on 2.5/3.0 =~ 0.833 of the length between points P1 and P2.
Note that P1was calculated with t = 0 and P2 with t =~ 0.333.
We can now find the fixed value of t: Tfix=Mathf.Lerp(0, 0.333, 0.833) =~ 0.277, where:
0 - t of point P1
0.333 - t of point P2
0.833 - the approximated position of searched t relative to closest approximation points.
Finally to find the point you were searching for substitute Tfix for t: PathCubic(Tfix)
Also, think about using more approximation points. For more demanding uses I needed dividing the segment in 16 pieces.
Mathematical answer: in general you can't. There is no symbolic way to determine the length of your cubic curve (it'd require solving a 6th order polynomial, which cannot be with regular formula manipulations), so there's no way to straight-up compute which t value you need for distance d along your curve.
The fastest solution is typically to index your curve: at fixed t intervals, build a lookup table with the length of the curve up to that point. Then use that LUT to do estimation. If your curve is length 42, and your LUT is distance = [0,5,12,25,37,42] for corresponding t = [0,1/5,2/5,3/5,4/5,1] then now you can use that data at moment to quickly estimate which t values you need to highlight which fixed distances, using simple linear interpolation.
Lets Say I have a 3d Cartesian grid. Lets also assume that there are one or more log spirals emanating from the origin on the horizontal plane.
If I then have a point in the grid I want to test if that point is in one of the spirals. I acutally want to test if it within a certain range of the spirals but determining if it is on the point is a good start.
So I guess the question has a couple parts.
How to generate the arms from parameters (direction, tightness)
How to tell if a point in the grid is in one of the spiral arms
Any ideas? I have been googling all day and don't feel I am any closer to a solution than when I started.
Here is a bit more information that might help:
I don't actually need to render the spirals. I want to set the pitch and rotation and then pass a point to a method that can tell me if the point I passed is within the spiral (within a given range of any point on the spiral). Based on the value returned (true or false) my program will make a decision on whether or not something exists at the point in space.
How to parametrically define the log spirals (pitch and rotation and ??)
Test if a point (x, y, z) is withing a given range of any point on the spiral.
Note: Both of the above would be just on the horizontal plane
These are two functions defining an anti-clockwise spiral:
PolarPlot[{
Exp[(t + 10)/100],
Exp[t/100]},
{t, 0, 100 Pi}]
Output:
These are two functions defining a clockwise spiral:
PolarPlot[{
- Exp[(t + 10)/100],
- Exp[t/100]},
{t, 0, 100 Pi}]
Output:
Cartesian coordinates
The conversion Cartesian <-> Polar is
(1) Ro = Sqrt[x^2+y^2]
t = ArcTan[y/x]
(2) x = Ro Cos[t]
y = Ro Sin[t]
So, If you have a point in Cartesian Coords (x,y) you transform it to your equivalent polar coordinates using (1). Then you use the forula for the spiral function (any of the four mentinoned above the plots, or similar ones) putting in there the value for t, and obtaining Ro. The last step is to compare this Ro with the one we got from the coordinates converion. If they are equal, the point is on the spiral.
Edit Answering your comment
For a Log spiral is almost the same, but with multiple spirals you need to take care of the logs not going to negative values. That's why I used exponentials ...
Example:
PolarPlot[{
Log[t],
If[t > 3, Log[ t - 2], 0],
If[t > 5, Log[ t - 4], 0]
}, {t, 1, 10}]
Output:
Not sure this is what you want, but you can reverse the log function (or "any" other for that matter).
Say you have ln A = B, to get A from B you do e^B = A.
So you get your point and pass it as B, you'll get A. Then you just need to check if that A (with a certain +- range) is in the values you first passed on to ln to generate the spiral.
I think this might work...
Unfortunately, you will need to know some mathematics notation anyway - this is a good read about the logarithmic sprial.
http://en.wikipedia.org/wiki/Logarithmic_spiral
we will only need the top 4 equations.
For your question 1
- to control the tightness, you tune the parameter 'a' as in the wiki page.
- to control the direction, you offset theta by a certain amount.
For your question 2
In floating point arithmetic, you will never get absolute precision, which mean there will be no point falling exactly on the sprial. On the screen, however, you will know which pixel get rendered, and you can test whether you are hitting a point that is rendered.
To render a curve, you usually render it as a sequence of line segments, short enough so that overall it looks like a curve. If you want to know whether a point lies within certain distance from the spiral, you can render the curve (on a off-screen buffer if you wish) by having thicker lines.
here a C++ code drawing any spiral passing where the mouse here
(sorry for my English)
int cx = pWin->vue.right / 2;
int cy = pWin->vue.bottom / 2;
double theta_mouse = atan2((double)(pWin->y_mouse - cy),(double)(pWin->x_mouse - cx));
double square_d_mouse = (double)(pWin->y_mouse - cy)*(double)(pWin->y_mouse - cy)+
(double)(pWin->x_mouse - cx)*(double)(pWin->x_mouse - cx);
double d_mouse = sqrt(square_d_mouse);
double theta_t = log( d_mouse / 3.0 ) / log( 1.19 );
int x = cx + (3 * cos(theta_mouse));
int y = cy + (3 * sin(theta_mouse));
MoveToEx(hdc,x,y,NULL);
for(double theta=0.0;theta < PI2*5.0;theta+=0.1)
{
double d = pow( 1.19 , theta ) * 3.0;
x = cx + (d * cos(theta-theta_t+theta_mouse));
y = cy + (d * sin(theta-theta_t+theta_mouse));
LineTo(hdc,x,y);
}
Ok now the parameter of spiral is 1.19 (slope) and 3.0 (radius at center)
Just compare the points where theta is a mutiple of 2 PI = PI2 = 6,283185307179586476925286766559
if any points is near of a non rotated spiral like
x = cx + (d * cos(theta));
y = cy + (d * sin(theta));
then your mouse is ON the spiral... I searched this tonight and i googled your past question