Get the closest SpriteKit node in a certain direction - sprite-kit

I want to get the distance to the closest node (if there is a one) from another node that is "looking" at a specific direction.
In the image, the "P" is a node for the player, and the line is the direction to look at. The circe is another node, crossing the direction line.
Getting the distance between 2 nodes is not a problem, but how do I access the closest node exactly crossing my line?

I assume that your sprite has some radius, and crossing my line denotes that distance from line to sprite center is smaller than radius.
Let playur point is P, direction angle is Fi, so unit direction vector is
dx = cos(fi)
dy = sin(fi)
and line equation is
- x * dy + y * dx + q = 0
where
q = dy * p.x - dx * p.y
To find distance from sprite center to this line, substitute x and y with center.x and center.y
dist = - center.x * dy + center.y * dx + q
So walk through sprite list, check whether sprite is intersected (dist < spriteRadius) and choose the closest from intersected sprites

Related

Finding a point on a Vector3 in Unity?

So I have a Vector3 in Unity and a direction, and I also have a X and Z coordinates, and I would like to find the Y coordinate of this Vector3 at the continuation of this Vector3, with those X and Z that I already have. Is this possible to do?
So if I understand you correctly you have
one Vector3 let's call it startPoint
one Vector3 let's call it direction
and a third Vector3 let's call it targetPoint on the XZ plane
and all you need now is the y component of that targetPoint.
So assuming that both X and Z are valid and they are not 0 in the direction actually it doesn't matter if you are in 2D or 3D for this.
you actually only need one of the given axis and resolve it's function for Y like e.g.
targetPoint.y = startPoint.y + ((targetPoint.x - startPoint.x) / direction.x) * direction.y;
or
targetPoint.y = startPoint.y + ((targetPoint.z - startPoint.z) / direction.z) * direction.y;
which basically takes the distance that the given x already has from the start point x and divides it by the given direction x.
So we basically know by what factor the direction needed to be multiplied in order to reach the target x.
This factor we apply also to the direction y. This should result in the target Y.
Or to be sure that it also works if either the X or Z direction is 0 you could use something more generalized but also more expensive
var xzDirection = new Vector3(direction.x, 0, direction.z);
var xzDelta = targetPoint - startPoint;
startPoint.y = startPoint.y + xzDelta.magnitude / xzDirection.magnitude * direction.y;

predict car position after making a circular movement

I have a car in position p of a 2D world. The car look vector is a normalized vector d.
The radius of the circle is r = 2/π.
I want to create a function predict(p, d, clockwise, angle) that, given the initial position p, the look direction d, it returns the final position of the car after turning angle degrees. If it is clockwise, it rotates in the circle R, otherwise it rotates in L.
Example: if I call predict( (0,0), (0,1), true, 90 ), that means the car is in (0,0), looking up and it should rotate 90º clockwise, stopping in position (2/π, 2/π).
I tried to do everything supposing the car always looks up and then rotate the results according with its true angle but it didn't work properly.
The idea is to find the center of the circle, then rotate p around the center. Here is the pseudo-code:
d' = Rotate(d, clockwise ? -90 : 90)
circle_center = p + d' * circle_radius // Here, circle_radius is 2/pi
relative_p = p - circle_center
rotated_p = circle_center + Rotate(relative_p, clockwise ? -angle : angle)
This method will work for any p, d, and circle_radius.
I followed Gilles-Philippe Paillé idea of rotating around a point and defined a method for this task:
Vector2 RotateAround(Vector2 v, Vector2 pivot, float angle) { //angle in degrees
angle *= Mathf.Deg2Rad;
float x = Mathf.Cos(angle) * (v.x - pivot.x) - Mathf.Sin(angle) * (v.y - pivot.y) + pivot.x;
float x = Mathf.Sin(angle) * (v.x - pivot.x) + Mathf.Cos(angle) * (v.y - pivot.y) + pivot.y;
return new Vector2(x, y);
}
Then, to predict the final position, given position, lookDirection, angle and clockwise:
Vector2 PredictPosition(Vector2 position, Vector2 lookDirection, float angle, bool clockwise){
float radius = 2f / Mathf.PI;
Vector2 pivot = position + radius * (clockwise ? RIGHT : LEFT);
return RotateAround(transform.position, pivot, clockwise ? -angle : angle);
}
Where RIGHT and LEFT are the vectors corresponding to the car looking right and left (that is just lookDirection rotated 90º and -90º. Using RotateAround(lookDirection, Vector2.zero, 90) you get one of them, the latter is just the negation of the former.

Triangulate set of points on arbitrary plane in 3D space

I have a set of points in 3D space. With maximum error of 10^-5 i can place a plane through them (error is the distance from point to plane).
Is there a way to triangulate these points on this arbitrary plane? I have tried Bowyer-Watson but this only works when the error is 0. Anything else and it wont triangulate or i wont get a good triangulation(overlapping triangles).
Edit
I think i found the problem. At certain angles the bowyer watson algorithm wont work because my calculation of the circumcenter is off. How can i calculate the circumcenter of a triangle in 3D?
Since i know the points on the plane i can calculate a vector. This vector lies on the plane. Next i calculate the center of mass of the points.
Using the vector and center of mass i can create a large triangle on the plane
Vertex p1 = new Vertex(dir * 3000 + center);
Vertex p2 = new Vertex(Quaternion.AngleAxis(120, plane.normal) * dir * 3000 + center);
Vertex p3 = new Vertex(Quaternion.AngleAxis(240, plane.normal) * dir * 3000 + center);
Now that i have the enclosing triangle i can just use Bowyer-Watson.
For circumcenter in 3D i use:
Vector3 ac = p3 - p1;
Vector3 ab = p2 - p1;
Vector3 abXac = Vector3.Cross(ab, ac);
circumceter = p1 + (Vector3.Cross(abXac, ab) * ac.sqrMagnitude + Vector3.Cross(ac, abXac) * ab.sqrMagnitude) / (2 * abXac.sqrMagnitude);
And now i have a triangulated set of points on an arbitrary plane in 3D.

Distance from point to ellipse

I need to somehow compute the distance between a point and an Ellipse.
I describe the Ellipse in my program as coordinates x = a cos phi and y = b sin phi (where a,b are constants and phi the changing angle).
I want to compute the shortest distance between a point P and my ellipse.
My thought were to calculate the vector from the center of my ellipse and the point P and then find the vector that start from the center and reaches the end of the ellipse in the direction of the point P and at the end subtract both vectors to have the distance (thi may not give the shortest distance but it's still fine for what I need.
The problem is I don't know how to compute the second vector.
Does someone has a better Idea or can tell me how I can find the second vetor?
Thanks in advance!
EDIT1:
ISSUE:COMPUTED ANGLE DOESN'T SEEM TO GIVE RIGHT POINT ON ELLIPSE
Following the suggestion of MARTIN R, I get this result:
The white part is created by the program of how he calculates the distance. I compute the angle phi using the vector from the center P (of ellipse) to the center of the body. But as I use the angle in the equation of my ellipse to get the point that should stay on the ellipse BUT also having same direction of first calculated vector (if we consider that point as a vector) it actually gives the "delayed" vector shown above.
What could be the problem? I cannot really understand this behavior (could it have something to do with atan2??)
EDIT2:
I show also that in the other half of the ellipse it gives this result:
So we can see that the only case where this works is when we have phi = -+pi/2 and phi = -+pi
IMPLEMENTATION FAILED
I tried using the implementation of MARTIN R but I still get the things wrong.
At first I thought it could be the center (that is not always the same) and I changed the implementation this way:
func pointOnEllipse(ellipse: Ellipse, p: CGPoint) -> CGPoint {
let maxIterations = 10
let eps = CGFloat(0.1/max(ellipse.a, ellipse.b))
// Intersection of straight line from origin to p with ellipse
// as the first approximation:
var phi = atan2(ellipse.a*p.y, ellipse.b*p.x)
// Newton iteration to find solution of
// f(θ) := (a^2 − b^2) cos(phi) sin(phi) − x a sin(phi) + y b cos(phi) = 0:
for _ in 0..<maxIterations {
// function value and derivative at phi:
let (c, s) = (cos(phi), sin(phi))
let f = (ellipse.a*ellipse.a - ellipse.b*ellipse.b)*c*s - p.x*ellipse.a*s + p.y*ellipse.b*c - ellipse.center.x*ellipse.a*s + ellipse.center.y*ellipse.b*c
//for the second derivative
let f1 = (ellipse.a*ellipse.a - ellipse.b*ellipse.b)*(c*c - s*s) - p.x*ellipse.a*c - p.y*ellipse.b*s - ellipse.center.x*ellipse.a*c - ellipse.center.y*ellipse.b*s
let delta = f/f1
phi = phi - delta
if abs(delta) < eps { break }
}
return CGPoint(x: (ellipse.a * cos(phi)) + ellipse.center.x, y: (ellipse.b * sin(phi)) + ellipse.center.y)
}
We can see what happens here:
This is pretty strange, all points stay in that "quadrant". But I also noticed when I move the green box far far away from the ellipse it seems to get the right vector for the distance.
What could it be?
END RESULT
Using updated version of MARTIN R (with 3 iterations)
x = a cos(phi), y = b sin (phi) is an ellipse with the center at
the origin, and the approach described in your question can be realized like this:
// Point on ellipse in the direction of `p`:
let phi = atan2(a*p.y, b*p.x)
let p2 = CGPoint(x: a * cos(phi), y: b * sin(phi))
// Vector from `p2` to `p`:
let v = CGVector(dx: p.x - p2.x, dy: p.y - p2.y)
// Length of `v`:
let distance = hypot(v.dx, v.dy)
You are right that this does not give the shortest distance
of the point to the ellipse. That would require to solve 4th degree
polynomial equations, see for example distance from given point to given ellipse or
Calculating Distance of a Point from an Ellipse Border.
Here is a possible implementation of the algorithm
described in http://wwwf.imperial.ac.uk/~rn/distance2ellipse.pdf:
// From http://wwwf.imperial.ac.uk/~rn/distance2ellipse.pdf .
func pointOnEllipse(center: CGPoint, a: CGFloat, b: CGFloat, closestTo p: CGPoint) -> CGPoint {
let maxIterations = 10
let eps = CGFloat(0.1/max(a, b))
let p1 = CGPoint(x: p.x - center.x, y: p.y - center.y)
// Intersection of straight line from origin to p with ellipse
// as the first approximation:
var phi = atan2(a * p1.y, b * p1.x)
// Newton iteration to find solution of
// f(θ) := (a^2 − b^2) cos(phi) sin(phi) − x a sin(phi) + y b cos(phi) = 0:
for i in 0..<maxIterations {
// function value and derivative at phi:
let (c, s) = (cos(phi), sin(phi))
let f = (a*a - b*b)*c*s - p1.x*a*s + p1.y*b*c
let f1 = (a*a - b*b)*(c*c - s*s) - p1.x*a*c - p1.y*b*s
let delta = f/f1
phi = phi - delta
print(i)
if abs(delta) < eps { break }
}
return CGPoint(x: center.x + a * cos(phi), y: center.y + b * sin(phi))
}
You may have to adjust the maximum iterations and epsilon
according to your needs, but those values worked well for me.
For points outside of the ellipse, at most 3 iterations were required
to find a good approximation of the solution.
Using that you would calculate the distance as
let p2 = pointOnEllipse(a: a, b: b, closestTo: p)
let v = CGVector(dx: p.x - p2.x, dy: p.y - p2.y)
let distance = hypot(v.dx, v.dy)
Create new coordinate system, which transforms ellipse into circle https://math.stackexchange.com/questions/79842/is-an-ellipse-a-circle-transformed-by-a-simple-formula, then find distance of point to circle, and convert distance
I wrote up an explanation using Latex so it could be more readable and just took some screen shots. The approach I am sharing is one using a Newton step based optimization approach to the problem.
Note that for situations where you have an ellipse with a smaller ratio between the major and minor axis lengths, you only need a couple iterations, at most, to get pretty good accuracy. For smaller ratios, you could even probably get away with just the initial guess's result, which is essentially what Martin R shows. But if your ellipses can be any shape, you may want to add in some code to improve the approximation.
You have the Ellipsis center of (a, b) and an arbitrary point of P(Px, Py). The equation of the line defined by these two points looks like this:
(Y - Py) / (b - Py) = (X - Px) / (a - Px)
The other form you have is an ellipse. You need to find out which are the (X, Y) points which are both on the ellipse and on the line between the center and the point. There will be two such points and you need to calculate both their distance from P and choose the smaller distance.

Game programming difficult mathematical issue

The question I am about to ask could be somewhat challenging. I will try to make this as clear and cohesive as possible.
I am currently making a game, in which I have a 'laser ring,' as shown here:
This laser ring, when prompted, will fire a 'grappling hook' which is simply the image shown below. This image's frame.width property is adjusted to make it fire (lengthen) and retract (shorten.) It starts at a width of 0, and as the frames progress, it lengthens until reaching the desired point.
This grappling hook, when fired, should line up with the ring so that they appear to be one item. Refer to the image below for clarity:
*Note that the grappling hook's width changes almost every frame, so a constant width cannot be assumed.
Something else to note is that, for reasons that are difficult to explain, I can only access the frame.center property of the grappling hook and not the frame.origin property.
So, my question to you all is this: How can I, accessing only the frame.center.x and frame.center.y properties of the grappling hook, place it around the laser ring in such a way that it appears to be seamlessly extending from the ring as shown in the above image - presumably calculated based on the angle and width of the grappling hook at any given frame?
Any help is immensely appreciated.
OK, I've done this exact same thing in my own app.
The trick I did to make it easier was to have a function to calculate the "unitVector" of the line.
i.e. the vector change in the line based on a line length of 1.
It just uses simple pythagorus...
- (CGSize)unitVectorFromPoint:(CGPoint)start toPoint:(CGPoint)end
{
//distance between start an end
float dX = end.x - start.x;
float dY = end.y - start.y;
float distance = sqrtf(dX * dX + dY * dY); // simple pythagorus
//unit vector is just the difference divided by the distance
CGSize unitVector = CGSizeMake(dX/distance, dY/distance);
return unitVector;
}
Note... it doesn't matter which way round the start and end are as squaring the numbers will only give positive values.
Now you can use this vector to get to any point along the line between the two points (centre of the circle and target).
So, the start of the line is ...
CGPoint center = // center of circle
CGPoint target = // target
float radius = //radius of circle
float dX = center.x - target.x;
float dY = center.y - target.y;
float distance = sqrtf(dX * dX + dY * dY);
CGSize unitVector = [self unitVectorFromPoint:center toPoint:target];
CGPoint startOfLaser = CGPointMake(center.x + unitVector.x * radius, center.y + unitVector.y * radius).
CGPoint midPointOfLaser = CGPointMake(center.x + unitVecotr.x * distance * 0.5, center.y + unitVector.y * distance * 0.5);
This just multiplies the unit vector by how far you want to go (radius) to get to the point on the line at that distance.
Hope this helps :D
If you want the mid point between the two points then you just need to change "radius" to be the distance that you want to calculate and it will give you the mid point. (and so on).