Can someone please show me an algorithm to write a function that returns true if 4 points form a quadrilateral, and false otherwise? The points do not come with any order.
I've tried to check all permutations of the 4 points and see if there's 3 points that forms a straight line. If there's 3 points that forms a straight line than it's not quadrilateral. But then I realize that there's no way to tell the order. And then I struggle for several hours of thinking and googling with no result :(
I've read these questions:
find if 4 points on a plane form a rectangle?
Determining ordering of vertices to form a quadrilateral
But still find no solution. In the case of 1, it can't detect another kind of quadrilateral, and in 2 it assumes that the points are quadirateral already. Are there any other way to find out if 4 points form a quadirateral?
Thanks before.
EDIT FOR CLARIFICATION:
I define quadrilateral as simple quadrilateral, basically all shapes shown in this picture:
except the shape with "quadrilateral" and "complex" caption.
As for problems with the "checking for collinear triplets" approach, I tried to check the vertical, horizontal, and diagonal lines with something like this:
def is_linear_line(pt1, pt2, pt3):
return (pt1[x] == pt2[x] == pt3[x] ||
pt1[y] == pt2[y] == pt3[y] ||
slope(pt1, pt2) == slope(pt2, pt3))
And realize that rectangle and square will count as linear line since the slope of the points will be all the same. Hope this clears things out.
This is for checking if a quadrilateral is convex. Not if it is a simple quadrilateral.
I did like this in objective-c https://github.com/hfossli/AGGeometryKit/
extern BOOL AGQuadIsConvex(AGQuad q)
{
BOOL isConvex = AGLineIntersection(AGLineMake(q.bl, q.tr), AGLineMake(q.br, q.tl), NULL);
return isConvex;
}
BOOL AGLineIntersection(AGLine l1, AGLine l2, AGPoint *out_pointOfIntersection)
{
// http://stackoverflow.com/a/565282/202451
AGPoint p = l1.start;
AGPoint q = l2.start;
AGPoint r = AGPointSubtract(l1.end, l1.start);
AGPoint s = AGPointSubtract(l2.end, l2.start);
double s_r_crossProduct = AGPointCrossProduct(r, s);
double t = AGPointCrossProduct(AGPointSubtract(q, p), s) / s_r_crossProduct;
double u = AGPointCrossProduct(AGPointSubtract(q, p), r) / s_r_crossProduct;
if(t < 0 || t > 1.0 || u < 0 || u > 1.0)
{
if(out_pointOfIntersection != NULL)
{
*out_pointOfIntersection = AGPointZero;
}
return NO;
}
else
{
if(out_pointOfIntersection != NULL)
{
AGPoint i = AGPointAdd(p, AGPointMultiply(r, t));
*out_pointOfIntersection = i;
}
return YES;
}
}
There is no way to determine both vertex order and presence of a quadrilateral in the same operation unless you use operations that are far more expensive than what you're already performing.
Checking for collinear triplets (like you did) will exclude cases where the four points form triangles or straight lines.
To exclude also the complex quadrilateral (with crossing edges):
A quadrilateral formed by the points A, B, C and D is complex, if the intersection of AB and CD (if any) lies between the points A and B, and the same applies for BC and DA.
Do you have any more inputs than the 4 points? because if 4 points succeed to your test, they can always form 3 different quadrilaterals, sometime of different family. For example, Take a square, add 2 diagonal and remove the side.
So with only 4 points as input, you cannot do better than what you are already doing.
Let A, B, C and D be the four points. You have to assume that the edges are A-B, B-C, C-D, and D-A. If you can't make that assumption, the four points will always form a quadrilateral.
if (A-B intersects C-D) return false
if (B-C intersects A-D) return false
return true
First, find all side and diagonal sizes using the distance formula:
This.
Next, find all angles using this formula:
This
reference from: https://algorithmdotcpp.blogspot.com/2022/01/find-type-of-quadrilateral-with-given-points.html
Code in python:
# array
# read 8 number
points = list(map(int,input().split()))
# create separate variable for coordinates
xa = points[0]
ya = points[1]
xb = points[2]
yb = points[3]
xc = points[4]
yc = points[5]
xd = points[6]
yd = points[7]
# edge of quadrilateral using edge formula
# finding edge using distance formula.
a = math.sqrt((xb - xa) * (xb - xa) + (yb - ya) * (yb - ya))
b = math.sqrt((xc - xb) * (xc - xb) + (yc - yb) * (yc - yb))
c = math.sqrt((xd - xc) * (xd - xc) + (yd - yc) * (yd - yc))
d = math.sqrt((xa - xd) * (xa - xd) + (ya - yd) * (ya - yd))
# diagonal of quadrilateral
# find diagonals.
diagonal_ac = math.sqrt((xc - xa) * (xc - xa) + (yc - ya) * (yc - ya))
diagonal_bd = math.sqrt((xd - xb) * (xd - xb) + (yd - yb) * (yd - yb))
# angles
# angles of quadrilateral
# find angle using angle formula.
A = math.acos((a * a + d * d - diagonal_bd * diagonal_bd) / (2 * a * d))
B = math.acos((b * b + a * a - diagonal_ac * diagonal_ac) / (2 * b * a))
C = math.acos((c * c + b * b - diagonal_bd * diagonal_bd) / (2 * c * b))
D = math.acos((d * d + c * c - diagonal_ac * diagonal_ac) / (2 * d * c))
Now we can determine whether the type of quadrilateral or quadrilateral is not found using if-else conditions.
# if angles are equal means(90*)
if (A == B and A == C and A == D):
# if edge size are equal
if (a == b and a == c and a == d):
# square
print("Quadrilateral is square...\n")
print("area of square :", a * a)
# else
else:
# rectangular
print("Quadrilateral is rectangular...\n")
print("area of square :", a * b)
# angles are not equal but edges are equal
elif (a == b and a == c and a == d):
# diamond
print("Quadrilateral is diamond(Rhombus)...\n")
# opposite edges(sides) are equal
elif (a == c and b == d):
# parallelogram
print("Quadrilateral is parallelogram...")
else:
print("Quadrilateral is just a quadrilateral...\n")
Related
I have been working on a Hackerearth Problem. Here is the problem statement:
We have three variables a, b and c. We need to convert a to b and following operations are allowed:
1. Can decrement by 1.
2. Can decrement by 2.
3. Can multiply by c.
Minimum steps required to convert a to b.
Here is the algorithm I came up with:
Increment count to 0.
Loop through till a === b:
1. Perform (x = a * c), (y = a - 1) and (z = a - 2).
2. Among x, y and z, choose the one whose absolute difference with b is the least.
3. Update the value of a to the value chosen among x, y and z.
4. Increment the count by 1.
I can get pass the basic test case but all my advance cases are failing. I guess my logic is correct but due to the complexity it seems to fail.
Can someone suggest a more optimized solution.
Edit 1
Sample Code
function findMinStep(arr) {
let a = parseInt(arr[0]);
let b = parseInt(arr[1]);
let c = parseInt(arr[2]);
let numOfSteps = 0;
while(a !== b) {
let multiply = Math.abs(b - (a * c));
let decrement = Math.abs(b - (a - 1));
let doubleDecrement = Math.abs(b - (a - 2));
let abs = Math.min(multiply, decrement, doubleDecrement);
if(abs === multiply) a = a * c;
else if(abs === decrement) a -= 1;
else a -= 2;
numOfSteps += 1;
}
return numOfSteps.toString()
}
Sample Input: a = 3, b = 10, c = 2
Explanation: Multiply 3 with 2 to get 6, subtract 1 from 6 to get 5, multiply 5 with 2 to get 10.
Reason for tagging both Python and JS: Comfortable with both but I am not looking for code, just an optimized algorithm and analytical thinking.
Edit 2:
function findMinStep(arr) {
let a = parseInt(arr[0]);
let b = parseInt(arr[1]);
let c = parseInt(arr[2]);
let depth = 0;
let queue = [a, 'flag'];
if(a === b ) return 0
if(a > b) {
let output = Math.floor((a - b) / 2);
if((a - b) % 2) return output + 1;
return output
}
while(true) {
let current = queue.shift();
if(current === 'flag') {
depth += 1;
queue.push('flag');
continue;
}
let multiple = current * c;
let decrement = current - 1;
let doubleDecrement = current -2;
if (multiple !== b) queue.push(multiple);
else return depth + 1
if (decrement !== b) queue.push(decrement);
else return depth + 1
if (doubleDecrement !== b) queue.push(doubleDecrement);
else return depth + 1
}
}
Still times out. Any more suggestions?
Link for the question for you reference.
BFS
A greedy approach won't work here.
However it is already on the right track. Consider the graph G, where each node represents a value and each edge represents one of the operations and connects two values that are related by that operation (e.g.: 4 and 3 are connected by "subtract 1"). Using this graph, we can easily perform a BFS-search to find the shortest path:
def a_to_b(a, b, c):
visited = set()
state = {a}
depth = 0
while b not in state:
visited |= state
state = {v - 1 for v in state if v - 1 not in visited} | \
{v - 2 for v in state if v - 2 not in visited} | \
{v * c for v in state if v * c not in visited}
depth += 1
return 1
This query systematically tests all possible combinations of operations until it reaches b by testing stepwise. I.e. generate all values that can be reached with a single operation from a, then test all values that can be reached with two operations, etc., until b is among the generated values.
In depth analysis
(Assuming c >= 0, but can be generalized)
So far for the standard-approach that works with little analysis. This approach has the advantage that it works for any problem of this kind and is easy to implement. However it isn't very efficient and will reach it's limits fairly fast, once the numbers grow. So instead I'll show a way to analyze the problem in depth and gain a (far) more performant solution:
In a first step this answer will analyze the problem:
We need operations -->op such that a -->op b and -->op is a sequence of
subtract 1
subtract 2
multiply by c
First of all, what happens if we first subtract and afterwards multiply?
(a - x) * c = a * c - x * c
Next what happens, if we first multiply and afterwards subtract?
a * c - x'
Positional systems
Well, there's no simplifying transformation for this. But we've got the basic pieces to analyze more complicated chains of operations. Let's see what happens when we chain subtractions and multiplications alternatingly:
(((a - x) * c - x') * c - x'') * c - x'''=
((a * c - x * c - x') * c - x'') * c - x''' =
(a * c^2 - x * c^2 - x' * c - x'') * c - x''' =
a * c^3 - x * c^3 - x' * c^2 - x'' * c - x'''
Looks familiar? We're one step away from defining the difference between a and b in a positional system base c:
a * c^3 - x * c^3 - x' * c^2 - x'' * c - x''' = b
x * c^3 + x' * c^2 + x'' * c + x''' = a * c^3 - b
Unfortunately the above is still not quite what we need. All we can tell is that the LHS of the equation will always be >=0. In general, we first need to derive the proper exponent n (3 in the above example), s.t. it is minimal, nonnegative and a * c^n - b >= 0. Solving this for the individual coefficients (x, x', ...), where all coefficients are non-negative is a fairly trivial task.
We can show two things from the above:
if a < b and a < 0, there is no solution
solving as above and transforming all coefficients into the appropriate operations leads to the optimal solution
Proof of optimality
The second statement above can be proven by induction over n.
n = 0: In this case a - b < c, so there is only one -->op
n + 1: let d = a * c^(n + 1) - b. Let d' = d - m * c^(n + 1), where m is chosen, such that d' is minimal and nonnegative. Per induction-hypothesis d' can be generated optimally via a positional system. Leaving a difference of exactly m * c^n. This difference can not be covered more efficiently via lower-order terms than by m / 2 subtractions.
Algorithm (The TLDR-part)
Consider a * c^n - b as a number base c and try to find it's digits. The final number should have n + 1 digits, where each digit represents a certain number of subtractions. Multiple subtractions are represented by a single digit by addition of the subtracted values. E.g. 5 means -2 -2 -1. Working from the most significant to the least significant digit, the algorithm operates as follows:
perform the subtractions as specified by the digit
if the current digit is was the last, terminate
multiply by c and repeat from 1. with the next digit
E.g.:
a = 3, b = 10, c = 2
choose n = 2
a * c^n - b = 3 * 4 - 10 = 2
2 in binary is 010
steps performed: 3 - 0 = 3, 3 * 2 = 6, 6 - 1 = 5, 5 * 2 = 10
or
a = 2, b = 25, c = 6
choose n = 2
a * c^n - b = 47
47 base 6 is 115
steps performed: 2 - 1 = 1, 1 * 6 = 6, 6 - 1 = 5, 5 * 6 = 30, 30 - 2 - 2 - 1 = 25
in python:
def a_to_b(a, b, c):
# calculate n
n = 0
pow_c = 1
while a * pow_c - b < 0:
n += 1
pow_c *= 1
# calculate coefficients
d = a * pow_c - b
coeff = []
for i in range(0, n + 1):
coeff.append(d // pow_c) # calculate x and append to terms
d %= pow_c # remainder after eliminating ith term
pow_c //= c
# sum up subtractions and multiplications as defined by the coefficients
return n + sum(c // 2 + c % 2 for c in coeff)
I'm looking for an enumeration algorithm to search through a 3D array "sphering" around a given starting point.
Given an array a of size NxNxN where each N is 2^k for some k, and a point p in that array. The algorithm I'm looking for should do the following: If a[p] satisfies a certain predicate, the algorithm stops and p is returned. Otherwise the next point q is checked, where q is another point in the array that is the closest to p and hasn't been visited yet. If that doesn't match either, the next q'is checked an so on until in the worst case the whole array has been searched.
By "closest" here the perfect solution would be the point q that has the smallest Euclidean distance to p. As only discrete points have to be considered, perhaps some clever enumeration algorithm woukd make that possible. However, if this gets too complicated, the smallest Manhattan distance would be fine too. If there are several nearest points, it doesn't matter which one should be considered next.
Is there already an algorithm that can be used for this task?
You can search for increasing squared distances, so you won't miss a point. This python code should make it clear:
import math
import itertools
# Calculates all points at a certain distance.
# Coordinate constraint: z <= y <= x
def get_points_at_squared_euclidean_distance(d):
result = []
x = int(math.floor(math.sqrt(d)))
while 0 <= x:
y = x
while 0 <= y:
target = d - x*x - y*y
lower = 0
upper = y + 1
while lower < upper:
middle = (lower + upper) / 2
current = middle * middle
if current == target:
result.append((x, y, middle))
break
if current < target:
lower = middle + 1
else:
upper = middle
y -= 1
x -= 1
return result
# Creates all possible reflections of a point
def get_point_reflections(point):
result = set()
for p in itertools.permutations(point):
for n in range(8):
result.add((
p[0] * (1 if n % 8 < 4 else -1),
p[1] * (1 if n % 4 < 2 else -1),
p[2] * (1 if n % 2 < 1 else -1),
))
return sorted(result)
# Enumerates all points around a center, in increasing distance
def get_next_point_near(center):
d = 0
points_at_d = []
while True:
while not points_at_d:
d += 1
points_at_d = get_points_at_squared_euclidean_distance(d)
point = points_at_d.pop()
for reflection in get_point_reflections(point):
yield (
center[0] + reflection[0],
center[1] + reflection[1],
center[2] + reflection[2],
)
# The function you asked for
def get_nearest_point(center, predicate):
for point in get_next_point_near(center):
if predicate(point):
return point
# Example usage
print get_nearest_point((1,2,3), lambda p: sum(p) == 10)
Basically you consume points from the generator until one of them fulfills your predicate.
This is pseudocode for a simple algorithm that will search in increasing-radius spherical husks until it either finds a point or it runs out of array. Let us assume that condition returns either true or false and has access to the x, y, z coordinates being tested and the array itself, returning false (instead of exploding) for out-of-bounds coordinates:
def find_from_center(center, max_radius, condition) returns a point
let radius = 0
while radius < max_radius,
let point = find_in_spherical_husk(center, radius, condition)
if (point != null) return point
radius ++
return null
the hard part is inside find_in_spherical_husk. We are interested in checking out points such that
dist(center, p) >= radius AND dist(center, p) < radius+1
which will be our operating definition of husk. We could iterate over the whole 3D array in O(n^3) looking for those, but that would be really expensive in terms of time. A better pseudocode is the following:
def find_in_spherical_husk(center, radius, condition)
let z = center.z - radius // current slice height
let r = 0 // current circle radius; maxes at equator, then decreases
while z <= center + radius,
let z_center = (z, center.x, point.y)
let point = find_in_z_circle(z_center, r)
if (point != null) return point
// prepare for next z-sliced cirle
z ++
r = sqrt(radius*radius - (z-center.z)*(z-center.z))
the idea here is to slice each husk into circles along the z-axis (any axis will do), and then look at each slice separately. If you were looking at the earth, and the poles were the z axis, you would be slicing from north to south. Finally, you would implement find_in_z_circle(z_center, r, condition) to look at the circumference of each of those circles. You can avoid some math there by using the Bresenham circle-drawing algorithm; but I assume that the savings are negligible compared with the cost of checking condition.
Given is a line (segment), defined by two vectors start(x,y) and end(x,y). I also have a point p(x,y), which is on either of the two areas separated by the line (i.e. it is not exactly on the line).
How can I calculate the normal to the line that is facing towards the side in which p is?
Let:
A = (a,b) and B = (c,d) define the line segment
P = (p,q) be the other point.
Define:
dot( (p,q), (r,s) ) == p*r + q*s
Then the vector:
v = ( c-a, d-b)
defines the direction along the line segment. Its perpendicular is:
u = (d-b, (-(c-a)) = (d-b,a-c)
This can be seen by taking the dot product with v. To get the normal from the perpendicular, just divide by its length:
n = u /|u|, |u| = sqrt( dot(u,u))
We now just need to know where P lies relative to the normal. If we take:
dir = dot( (P-A), n) )
Then dir > 0 means n is in the same direction as P, whilst dir < 0 means it is in the opposite direction. Should dir == 0, then P is in fact on the extended line (not necessarily the line segment itself).
First, determine which side of the line the point lies on, by taking the cross product of end-start and p-end:
z = (xend-xstart)(yp-yend) - (yend-ystart)(xp-xend)
If z>0, then the point is to the left of the line (as seen by a person standing at start and facing end). If z<0, then the point is to the right of the line.
Second, normalize the line segment:
S = end - start
k = S/|S|
Finally, if the point is to the left of the line, rotate k to the left:
(xk, yk) => (-yk, xk)
or if the point is to the right of the line, rotate k to the right:
(xk, yk) => (yk, -xk)
My math skills are a bit rusty, so I can't give you the exact calculations, but what you do is this (assuming 2D from your description):
First you calculate a normal n.
Then you calculate P' which is the perpendicular projection of your point P onto your line.
Basically, what you do is, you "create" another line and use your vector n from step 1 as the direction (y = p + x * n where y,p and n are vectors, p is actually your p(x,y) and x is a real number), then you intersect this line with the first one and the point where they intersect is P'.
Seeing you're from Austria, everyone else please forgive me for using one German word, I really don't know the English translation and couldn't find any. P' = Lotfußpunkt
Calculate P - P'. If it has the same sign as n in both components, n is the normal you're searching for. If it has the opposite sign, -n is the one you're searching for.
I hope the idea is clear even though I don't know all the technical terms in English.
For
start = (a,b)
end = (c,d)
p = (x,y)
Slope(startend) = (d - b) / (c - a)
Slope(norm) = -(c - a) / (d - b)
Norm line must include p = (x,y), so
ynorm = -((c - a) / (d - b)) * xnorm + (y + ((c - a) / (d - b)) * x)
y = mx + c
is the normal line equation where m is the slope and c is any constant.
You have start and end. Lets call them (x1,y1) and (x2,y2) and the line joining them L1.
The slope of this line, m1, is (y2-y1)/(x2-x1). This line is perpendicular to the line you need which we can call L2 with slope m2. The product of slopes of 2 mutuallu perpendicular lines is -1. Hence,
m1*m2=-1.
Hence you can calculate m2. Now, you need to find the equation of the line L2. You have 1 point in the line P (x,y). You can substitute in this manner:
y=m2*x+c.
This will give you c. Once you have the line equation, you can convert it to parametric form as shown below:
http://thejuniverse.org/PUBLIC/LinearAlgebra/LOLA/lines/index.html
The equation of the line is given as
A = start.y-end.y
B = end.x-start.x
C = start.x*end.y-start.y*end.x
A*x + B*y + C = 0
The minimum distance d to the line of a point p=(px,py) is
d = (A*px+B*py+C)/sqrt(A^2+B^2)
If the value is positive then the point is at a counter clockwise rotation from the vector (start->end). If negative then it is in clockwise rotation. So if (start->end) is pointing up, then a positive distance is to the left of the line.
Example
start = (8.04, -0.18)
end = (6.58, 1.72)
P = (2.82, 0.66)
A = (-0.18)-(1.72) = -1.9
B = (6.58)-(8.04) = -1.46
C = (8.04)*(1.72)-(-0.18)*(6.58) = 15.01
d = (A*(2.82)+B*(0.66)+C)/√(A^2+B^2) = 3.63
The calculation for d shows the identical value as the length of vector (near->P) in the sketch.
N = (Ey - Sy, Sx - Ex) is perpendicular to the line (it is SE rotated by 90°, not normalized).
Then compute the sign of the dot product
N . SP = (Ey - Sy)(Px - Sx) + (Sx - Ex)(Py - Sy),
it will tell you on what side the normal is pointing.
I need an example of fast algorithm allowing to calculate if a point lies over a triangle in 3D. I mean if the projection of this point on a plane containing given triangle is inside of this triangle.
I need to calculate distance between a point and a triangle (between a point and the face of this triangle if its projection lies inside the triangle or between a point and an edge of a triangle if its projection lays outside the triangle).
I hope I made it clear enough. I found some examples for 2D using barycentric coordinates but can't find any for 3D. Is there a faster way than calculating projection of a point, projecting this projected point and a given triangle to 2D and solving standard "point in triangle" problem?
If the triangle's vertices are A, B, C and the point is P, then begin by finding the triangle's normal N. For this just compute N = (B-A) X (C-A), where X is the vector cross product.
For the moment, assume P lies on the same side of ABC as its normal.
Consider the 3d pyramid with faces ABC, ABP, BCP, CAP. The projection of P onto ABC is inside it if and only if the dihedral angles between ABC and each of the other 3 triangles are all less than 90 degrees. In turn, these angles are equal to the angle between N and the respective outward-facing triangle normal! So our algorithm is this:
Let N = (B-A) X (C-A), N1 = (B-A) X (P-A), N2 = (C-B) X (P-B), N3 = (A-C) X (P-C)
return N1 * N >= 0 and N2 * N >= 0 and N3 * N >= 0;
The stars are dot products.
We still need to consider the case where P lies on the opposite side of ABC as its normal. Interestingly, in this case the vectors N1, N2, N3 now point into the pyramid, where in the above case they point outward. This cancels the opposing normal, and the algorithm above still provides the right answer. (Don't you love it when that happens?)
Cross products in 3d each require 6 multiplies and 3 subtractions. Dot products are 3 multiplies and 2 additions. On average (considering e.g. N2 and N3 need not be calculated if N1 * N < 0), the algorithm needs 2.5 cross products and 1.5 dot products. So this ought to be pretty fast.
If the triangles can be poorly formed, then you might want to use Newell's algorithm in place of the arbitrarily chosen cross products.
Note that edge cases where any triangle turns out to be degenerate (a line or point) are not handled here. You'd have to do this with special case code, which is not so bad because the zero normal says much about the geometry of ABC and P.
Here is C code, which uses a simple identity to reuse operands better than the math above:
#include <stdio.h>
void diff(double *r, double *a, double *b) {
r[0] = a[0] - b[0];
r[1] = a[1] - b[1];
r[2] = a[2] - b[2];
}
void cross(double *r, double *a, double *b) {
r[0] = a[1] * b[2] - a[2] * b[1];
r[1] = a[2] * b[0] - a[0] * b[2];
r[2] = a[0] * b[1] - a[1] * b[0];
}
double dot(double *a, double *b) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
int point_over_triangle(double *a, double *b, double *c, double *p) {
double ba[3], cb[3], ac[3], px[3], n[3], nx[3];
diff(ba, b, a);
diff(cb, c, b);
diff(ac, a, c);
cross(n, ac, ba); // Same as n = ba X ca
diff(px, p, a);
cross(nx, ba, px);
if (dot(nx, n) < 0) return 0;
diff(px, p, b);
cross(nx, cb, px);
if (dot(nx, n) < 0) return 0;
diff(px, p, c);
cross(nx, ac, px);
if (dot(nx, n) < 0) return 0;
return 1;
}
int main(void) {
double a[] = { 1, 1, 0 };
double b[] = { 0, 1, 1 };
double c[] = { 1, 0, 1 };
double p[] = { 0, 0, 0 };
printf("%s\n", point_over_triangle(a, b, c, p) ? "over" : "not over");
return 0;
}
I've tested it lightly and it seems to be working fine.
Let's assume that the vertices of the triangle are v, w, and the origin 0. Let's call the point p.
For the benefit of other readers, here's the barycentric approach for 2D point-in-triangle, to which you alluded. We solve the following system in variables beta:
[v.x w.x] [beta.v] [p.x]
[v.y w.y] [beta.w] = [p.y] .
Test whether 0 <= beta.v && 0 <= beta.w && beta.v + beta.w <= 1.
For 3D projected-point-in-triangle, we have a similar but overdetermined system:
[v.x w.x] [beta.v] [p.x]
[v.y w.y] [beta.w] = [p.y] .
[v.z w.z] [p.z]
The linear least squares solution gives coefficients beta for the point closest to p on the plane spanned by v and w, i.e., the projection. For your application, a solution via the following normal equations likely will suffice:
[v.x v.y v.z] [v.x w.x] [beta.v] [v.x v.y v.z] [p.x]
[w.x w.y w.z] [v.y w.y] [beta.w] = [w.x w.y w.z] [p.y] ,
[v.z w.z] [p.z]
from which we can reduce the problem to the 2D case using five dot products. This should be comparable in complexity to the method that Nico suggested but without the singularity.
I already googled for the problem but only found either 2D solutions or formulas that didn't work for me (found this formula that looks nice: http://www.ogre3d.org/forums/viewtopic.php?f=10&t=55796 but seems not to be correct).
I have given:
Vec3 cannonPos;
Vec3 targetPos;
Vec3 targetVelocityVec;
float bulletSpeed;
what i'm looking for is time t such that
targetPos+t*targetVelocityVec
is the intersectionpoint where to aim the cannon to and shoot.
I'm looking for a simple, inexpensive formula for t (by simple i just mean not making many unnecessary vectorspace transformations and the like)
thanks!
The real problem is finding out where in space that the bullet can intersect the targets path. The bullet speed is constant, so in a certain amount of time it will travel the same distance regardless of the direction in which we fire it. This means that it's position after time t will always lie on a sphere. Here's an ugly illustration in 2d:
This sphere can be expressed mathematically as:
(x-x_b0)^2 + (y-y_b0)^2 + (z-z_b0)^2 = (bulletSpeed * t)^2 (eq 1)
x_b0, y_b0 and z_b0 denote the position of the cannon. You can find the time t by solving this equation for t using the equation provided in your question:
targetPos+t*targetVelocityVec (eq 2)
(eq 2) is a vector equation and can be decomposed into three separate equations:
x = x_t0 + t * v_x
y = y_t0 + t * v_y
z = z_t0 + t * v_z
These three equations can be inserted into (eq 1):
(x_t0 + t * v_x - x_b0)^2 + (y_t0 + t * v_y - y_b0)^2 + (z_t0 + t * v_z - z_b0)^2 = (bulletSpeed * t)^2
This equation contains only known variables and can be solved for t. By assigning the constant part of the quadratic subexpressions to constants we can simplify the calculation:
c_1 = x_t0 - x_b0
c_2 = y_t0 - y_b0
c_3 = z_t0 - z_b0
(v_b = bulletSpeed)
(t * v_x + c_1)^2 + (t * v_y + c_2)^2 + (t * v_z + c_3)^2 = (v_b * t)^2
Rearrange it as a standard quadratic equation:
(v_x^2+v_y^2+v_z^2-v_b^2)t^2 + 2*(v_x*c_1+v_y*c_2+v_z*c_3)t + (c_1^2+c_2^2+c_3^2) = 0
This is easily solvable using the standard formula. It can result in zero, one or two solutions. Zero solutions (not counting complex solutions) means that there's no possible way for the bullet to reach the target. One solution will probably happen very rarely, when the target trajectory intersects with the very edge of the sphere. Two solutions will be the most common scenario. A negative solution means that you can't hit the target, since you would need to fire the bullet into the past. These are all conditions you'll have to check for.
When you've solved the equation you can find the position of t by putting it back into (eq 2). In pseudo code:
# setup all needed variables
c_1 = x_t0 - x_b0
c_2 = y_t0 - y_b0
c_3 = z_t0 - z_b0
v_b = bulletSpeed
# ... and so on
a = v_x^2+v_y^2+v_z^2-v_b^2
b = 2*(v_x*c_1+v_y*c_2+v_z*c_3)
c = c_1^2+c_2^2+c_3^2
if b^2 < 4*a*c:
# no real solutions
raise error
p = -b/(2*a)
q = sqrt(b^2 - 4*a*c)/(2*a)
t1 = p-q
t2 = p+q
if t1 < 0 and t2 < 0:
# no positive solutions, all possible trajectories are in the past
raise error
# we want to hit it at the earliest possible time
if t1 > t2: t = t2
else: t = t1
# calculate point of collision
x = x_t0 + t * v_x
y = y_t0 + t * v_y
z = z_t0 + t * v_z