I have a two segments AB and CD (in red). These two segments are facing each others. They are not completely parallel but will never be perpendicular to each others either.
From that, I need to find the two normals of these segments (in blue) that oppose each others (i.e. the two normals are outside ABCD). I know how to calculate the normals of the segments but obviously each segment has two normals, and I cannot figure out how to programmatically select the ones I need. Any suggestion?
Calculate the vector v between the midpoints of the two segments, pointing from AB to CD. Now the projection of the desired normal to AB onto v must be negative and the projection of the desired normal to CD onto v must be positive. So just calculate the normals, check against v, and negate the normals if needed to make them satisfy the condition.
Here it is in Python:
# use complex numbers to define minimal 2d vector datatype
def vec2d(x,y): return complex(x,y)
def rot90(v): return 1j * v
def inner_prod(u, v): return (u * v.conjugate()).real
def outward_normals(a, b, c, d):
n1 = rot90(b - a)
n2 = rot90(d - c)
mid = (c + d - a - b) / 2
if inner_prod(n1, mid) > 0:
n1 = -n1
if inner_prod(n2, mid) < 0:
n2 = -n2
return n1, n2
Note that I assume the endpoints define lines meeting the conditions in the problem. Nor do I check for the edge case when the lines have the same midpoint; the notion of "outside" doesn't apply in that case.
I think there are two cases to consider:
Case 1: Intersection between lines occurs outside the endpoints of either segment.
In this case the midpoint method suggested by #Michael J. Barber will work for sure. So form a vector between the midpoints of the segments, compute the dot product of your normal vectors with this midpoint vector and check the sign.
If you're computing the normal for lineA, the dot product of the normal with the vector midB -> midA should be +ve.
Case 2: Intersection between lines occurs inside the endpoints of one segment.
In this case form a vector between either one of the endpoints of the segment that does not enclose the intersection point and the intersection point itself.
The dot product of the normal for the segment that does enclose the intersection point and this new vector should be +ve.
You can find the outward normal for the other segment by requiring that the dot product between the two normals is -ve (which would only be ambiguous in the case of perpendicular segments).
I've assumed that the segments are not co-linear or actually intersecting.
Hope this helps.
You can reduce the four combinations for the signs as follows:
Calculate the dot product of the normals, a negative sign indicates that both show outside or inside.
As I suppose that your normals have unit lenght, you can detect parallelity if the dot product has magnitude one. A positive value indicates that both show in the same direction, a negative value says that both show in different directions.
It the normals are not parallel: parametrize lines as x(t) = x0 + t * n for a normal n and calculate the t for which both intersect. A negative t will indicate that both show outside. It is enough if you do this for one of the normals, as you reduced your combinations from 4 to 2 in step 1.
If both normals are parralel: Calculate the time t for which the normals hit the midpoint between of your segments. As in 2. is enough if you do this for one of the normals, as you reduced your combinations from 4 to 2 in step 1.
Related
I get the positions of 5 enemies in the game in vectors. Depending on the distance I choose, the number of enemies can vary from 0 to 5. I need to know their vectors each time to check whether it is possible to draw a straight line through a certain number of heroes (vectors).
After that, my hero will have to use his ability called wall. It consists of 2 start and end vectors. Thus, check whether my hero can put a wall on the enemies in the line to catch them
Let's say there are 3 enemy heroes whose positions I can get. I need to find out if I can pass through them directly, in order to use the ability on them.
Here's what using the ability looks like in the game
Here is getting the vector of one of the heroes
The ability itself can be twisted at a certain point. But anyway, it is necessary that the wall would touch several heroes
Wherever I move the mouse, I can put it in the desired position. But unfortunately it takes a lot of time, so I would like to automate
The coordinates of the wall itself, or rather its two edges, I can also get, but only after the ability has been used
If one prefers geometry to linear algebra...
Then One can compute the dot product of (unit-vector1. Unit-Vector2). That is equal to the SIN of the angle between them.
So if unit vector is the shooter position to target1, unit vector2 is the shooter to target2, etc... then when DOTPRODUCT(Vector1,vector2) = 1 and DOTPRODUCT(Vector1,vector3) = 1, then the three points are in syzygy.
And repeat from shooter to as many targets as you have to determine whether some or all of the points are in syzygy.
From your statement that there is a start and an endpoint I take that you select two enemys and want to trap anything in between.
So you're actually not looking for a straight line that can be drawn through your enemy positions but if they are withn a rectangle. It would be very unlikely and for more points nearly impossible that they are all collinear anyway.
So it becomes quite trivial. You draw a line through start and end enemy. Then you check the remaining enemies distance to that line vs the width of your AoE. Maye you want to also handle some body width in that calculation.
https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
You can describe all points belonging to line (x0, y0) + (dx, dy)t = (x1, y1). Chose any two points and t as 1 and you will get (dx, dy) for line connecting two dots. Now you will need to find distance between this line and (x2, y2). it is distance between (x2, y2) and (xd, yd), where on one hand (xd, yd) = (x0, y0) + t1(dx, dy) and on other hand (xd, yd) = (x2, y2) + t2*(-dy, dx). Solving this two equations you will find t1, t2, (xd, yd) and distance between (x2, y2) and (xd, yd), which is distance between (x2, y2) and line, connecting (x0, y0) and (x1, y1).
Knowing this, you select dots with min_x and max_x and calculate ditance between line, connecting said dots and rest of the dots. If distance is lesser then some threshold of your choice, then you can assume that you can have line passing through all dots.
Any line in the plane can be described by an equation a*x + b*y + c = 0 with (a, b) ≠ (0, 0). Note that if you have an equation of this form, then multiplying each coefficient a, b, c with the same number yields an equation describing the same line. That's the reason (a, b, c) is called a homogeneous coordinate vector for that line.
How do you find a, b, c? One simple approach would be treating this as three linear equations in three unknowns. You plug in the x and y coordinates for all your three points, and get tree equations for a thorough c. However, there is a catch. Since the right hand side of each equation is zero, a = b = c = 0 is always a solution. In those cases where there is only one solution, that will be it. So in order for there to be a line, you need more than one solution. The mathematical tool to determine whether a set of equations had more than one solution is the determinant. It is zero if the system has no single unique solution.
Long story short: three points are collinear (on a line) if
⎛x1 y1 1⎞
det ⎜x2 y2 1⎟ = 0
⎝x3 y3 1⎠
The homogeneous coordinate vector describing the line world correspond to the kernel of that matrix.
Of course, if your input coordinates are floating point numbers, exact zero is unlikely. Presumably that wall does allow for some error in some way, and you'd need to tell us about that in order to get an answer that models this aspect correctly. In the mean time, know that the absolute value of the determinant above is proportional to the area of the triangle created by these three points. So if your were to pick a constant threshold value, the farther your enemies are apart along the direction of the wall, the less they could deviate from the straight line without violating that threshold.
This question is a bit specific. Assume for simplicity that I have a unit circle, i.e. a circle centered on the origin (0,0) with radius 1. On this circle I have three points: A, B, and C. Assume that B and C are fixed, and that A can be moved along the circle's circumference.
Question: What conditions can I check efficiently to ensure that A is not moved outside the arc between B and C originally containing it?
As a point of clarification: I do not just wish to detect whether the point is on this arc or not (i.e., get a boolean), but to have a quantifiable measure (for example, of distance) so I can prevent it from being moved outside. You can imagine my setup like this, restricting point A to the red arc:
Here are some ideas I have pondered so far, but none were successful:
Limit the angle alpha of A = [cos(alpha),sin(alpha)] to the origin between the angles beta and gamma of B=[cos(beta),sin(beta)] and C=[cos(gamma),sin(gamma)], where alpha, beta, and gamma are angles in radians. Unfortunately, this naive case only works in scenario (a) above. If the arc to which A is restricted crosses the (in my case, western) discontinuity from +pi/-pi, a naive upper and lower bound won't do.
Calculate the length of the arcs between A-to-B and A-to-C, then ensure that as A is moved this sum does not change. Unfortunately, I have only found this solution to calculate the arcs between two points, and it always calculates the shorter arc. In scenario (c), however, the correct arc A-to-B is the larger one.
Given any two points on a circle, in your case B and C, there are of course two possible arcs. We use A to select between the two options. You say you want to prevent a point, say D, from moving outside this arc. I'm interpreting this to mean that we want a function which, if D lies on the arc defined by BC and A, returns D, otherwise it returns B or C, depending on which is nearer to D.
We can define the following function to implement this scheme:
def contstrainPoint(A, B, C, D)
M = midpoint(B, C)
If dot(MA, MD) >= 0
return D
else if(dot(MB, MD) >= 0
return B
else
return C
Where M is the midpoint of the chord BC and dot is the dot product function.
Take the cross product (A-B)x(A-C). This will have one non-zero component, normal to the plane. That component will vary smoothly, positive when A is on one arc, negative when it is on the other, and zero when it crosses B or C.
If I have 4 nodes on a line, for example:
Main line is formed by nodes A(0,0) and B(5,0)
and there is a line on this main line which is formed by C(2,0) and D(4,0)
It is clear that vector AB has the same direction with vector CD, or in other words, vector BA has the same direction with vector DC.
I know it is simple, that in this case we just calculate delta x and then divided by each length.
Is there any common technique and in a "straight-way fashion"?
For example by comparing the gradients (which is in this case, it would be still valid, since both gradients are zero, but if the coordinates are conversed, the computation will be error, since the gradients cannot be computed by dividing by zero).
Thanks in advance.
If you want to know whether AB has the same orientation as CD, compute the dot product (B-A)*(D-C). It will be positive if they point in the same direction, negative if they point in opposite directions, and zero if one of the vectors is zero (or otherwise perpendicular to the other, but you assumed collinear points so that can't happen).
I am trying to generate a 3d tube along a spline. I have the coördinates of the spline (x1,y1,z1 - x2,y2,z2 - etc) which you can see in the illustration in yellow. At those points I need to generate circles, whose vertices are to be connected at a later stadium. The circles need to be perpendicular to the 'corners' of two line segments of the spline to form a correct tube. Note that the segments are kept low for illustration purpose.
[apparently I'm not allowed to post images so please view the image at this link]
http://img191.imageshack.us/img191/6863/18720019.jpg
I am as far as being able to calculate the vertices of each ring at each point of the spline, but they are all on the same planar ie same angled. I need them to be rotated according to their 'legs' (which A & B are to C for instance).
I've been thinking this over and thought of the following:
two line segments can be seen as 2 vectors (in illustration A & B)
the corner (in illustraton C) is where a ring of vertices need to be calculated
I need to find the planar on which all of the vertices will reside
I then can use this planar (=vector?) to calculate new vectors from the center point, which is C
and find their x,y,z using radius * sin and cos
However, I'm really confused on the math part of this. I read about the dot product but that returns a scalar which I don't know how to apply in this case.
Can someone point me into the right direction?
[edit]
To give a bit more info on the situation:
I need to construct a buffer of floats, which -in groups of 3- describe vertex positions and will be connected by OpenGL ES, given another buffer with indices to form polygons.
To give shape to the tube, I first created an array of floats, which -in groups of 3- describe control points in 3d space.
Then along with a variable for segment density, I pass these control points to a function that uses these control points to create a CatmullRom spline and returns this in the form of another array of floats which -again in groups of 3- describe vertices of the catmull rom spline.
On each of these vertices, I want to create a ring of vertices which also can differ in density (amount of smoothness / vertices per ring).
All former vertices (control points and those that describe the catmull rom spline) are discarded.
Only the vertices that form the tube rings will be passed to OpenGL, which in turn will connect those to form the final tube.
I am as far as being able to create the catmullrom spline, and create rings at the position of its vertices, however, they are all on a planars that are in the same angle, instead of following the splines path.
[/edit]
Thanks!
Suppose you have a parametric curve such as:
xx[t_] := Sin[t];
yy[t_] := Cos[t];
zz[t_] := t;
Which gives:
The tangent vector to our curve is formed by the derivatives in each direction. In our case
Tg[t_]:= {Cos[t], -Sin[t], 1}
The orthogonal plane to that vector comes solving the implicit equation:
Tg[t].{x - xx[t], y - yy[t], z - zz[t]} == 0
In our case this is:
-t + z + Cos[t] (x - Sin[t]) - (y - Cos[t]) Sin[t] == 0
Now we find a circle in that plane, centered at the curve. i.e:
c[{x_, y_, z_, t_}] := (x - xx[t])^2 + (y - yy[t])^2 + (z - zz[t])^2 == r^2
Solving both equations, you get the equation for the circles:
HTH!
Edit
And by drawing a lot of circles, you may get a (not efficient) tube:
Or with a good Graphics 3D library:
Edit
Since you insist :) here is a program to calculate the circle at junctions.
a = {1, 2, 3}; b = {3, 2, 1}; c = {2, 3, 4};
l1 = Line[{a, b}];
l2 = Line[{b, c}];
k = Cross[(b - a), (c - b)] + b; (*Cross Product*)
angle = -ArcCos[(a - b).(c - b)/(Norm[(a - b)] Norm[(c - b)])]/2;
q = RotationMatrix[angle, k - b].(a - b);
circle[t_] := (k - b)/Norm[k - b] Sin#t + (q)/Norm[q] Cos#t + b;
Show[{Graphics3D[{
Red, l1,
Blue, l2,
Black, Line[{b, k}],
Green, Line[{b, q + b}]}, Axes -> True],
ParametricPlot3D[circle[t], {t, 0, 2 Pi}]}]
Edit
Here you have the mesh constructed by this method. It is not pretty, IMHO:
I don't know what your language of choice is, but if you speak MatLab there are already a few implementations available. Even if you are using another language, some of the code might be clear enough to inspire a reimplementation.
The key point is that if you don't want your tube to twist when you connect the vertices, you cannot determine the basis locally, but need to propagate it along the curve. The Frenet frame, as proposed by jalexiou, is one option but simpler stuff works fine as well.
I did a simple MatLab implementation called tubeplot.m in my formative years (based on a simple non-Frenet propagation), and googling it, I can see that Anders Sandberg from kth.se has done a (re?)implementation with the same name, available at http://www.nada.kth.se/~asa/Ray/Tubeplot/tubeplot.html.
Edit:
The following is pseudocode for the simple implementation in tubeplot.m. I have found it to be quite robust.
The plan is to propagate two normals a and b along the curve, so
that at each point on the curve a, b and the tangent to the curve
will form an orthogonal basis which is "as close as possible" to the
basis used in the previous point.
Using this basis we can find points on the circumference of the tube.
// *** Input/output ***
// v[0]..v[N-1]: Points on your curve as vectors
// No neighbours should overlap
// nvert: Number of vertices around tube, integer.
// rtube: Radius of tube, float.
// xyz: (N, nvert)-array with vertices of the tube as vectors
// *** Initialization ***
// 1: Tangent vectors
for i=1 to N-2:
dv[i]=v[i+1]-v[i-1]
dv[0]=v[1]-v[0], dv[N-1]=v[N-1]-v[N-2]
// 2: An initial value for a (must not be pararllel to dv[0]):
idx=<index of smallest component of abs(dv[0])>
a=[0,0,0], a[idx]=1.0
// *** Loop ***
for i = 0 to N-1:
b=normalize(cross(a,dv[i]));
a=normalize(cross(dv[i],b));
for j = 0 to nvert-1:
th=j*2*pi/nvert
xyz[i,j]=v[i] + cos(th)*rtube*a + sin(th)*rtube*b
Implementation details: You can probably speed up things by precalculating the cos and sin. Also, to get a robust performance, you should fuse input points closer than, say, 0.1*rtube, or a least test that all the dv vectors are non-zero.
HTH
You need to look at Fenet formulas in Differential Geometry. See figure 2.1 for an example with a helix.
Surfaces & Curves
Taking the cross product of the line segment and the up vector will give you a vector at right-angles to them both (unless the line segment points exactly up or down) which I'll call horizontal. Taking the cross product of horizontal and the line segment with give you another vector that's at right angles to the line segment and the other one (let's call it vertical). You can then get the circle coords by lineStart + cos theta * horizontal + sin theta * vertical for theta in 0 - 2Pi.
Edit: To get the points for the mid-point between two segments, use the sum of the two line segment vectors to find the average.
Given a list of vertices (v), and a list of edges connecting the vertices (e), and a list of surfaces that connect the edges (s), how to calculate the volume of the Polyhedron?
Take the polygons and break them into triangles.
Consider the tetrahedron formed by each triangle and an arbitrary point (the origin).
Sum the signed volumes of these tetrahedra.
Notes:
This will only work if you can keep a consistent CW or CCW order to the triangles as viewed from the outside.
The signed volume of the tetrahedron is equal to 1/6 the determinant of the following matrix:
[ x1 x2 x3 x4 ]
[ y1 y2 y3 y4 ]
[ z1 z2 z3 z4 ]
[ 1 1 1 1 ]
where the columns are the homogeneous coordinates of the verticies (x,y,z,1).
It works even if the shape does not enclose the origin by subracting off that volume as well as adding it in, but that depends on having a consistent ordering.
If you can't preserve the order you can still find some way to break it into tetrahedrons and sum 1/6 absolute value of the determinant of each one.
Edit:
I'd like to add that for triangle mesh where one vertex (say V4) of the tetrahedron is (0,0,0) the determinante of the 4x4 matrix can be simplified to the upper left 3x3 (expansion along the 0,0,0,1 column) and that can be simplified to Vol = V1xV2.V3 where "x" is cross product and "." is dot product. So compute that expression for every triangle, sum those volumes and divide by 6.
Similarly with a polygon where we can split it into triangles and sum the areas,
you could split a polyhedron into pyramids and sum their volumes. But I'm not sure how hard is to implement an algorithm for that.
(I believe there is a mathematical way/formula, like using vectors and matrices.
I suggest to post your question also on http://mathoverflow.net)
I have done this before, but the surface mesh I used always had triangular facets. If your mesh has non triangular facets, you can easily break them up into triangular facets first. Then I fed it to TetGen to obtain a tetrahedralization of the interior. Finally, I added up all the volumes of the tetrahedra. TetGen is reasonably easy to use, and is the only library other than CGAL I know of that can handle complicated meshes. CGAL is pretty easy to use if you don't mind installing a gigantic library and use templates like crazy.
First, break every face into triangles by drawing in new edges.
Now look at one triangle, and suppose it's on the "upper" surface (some of these details will turn out to be unimportant later). Look at the volume below the triangle, down to some horizontal plane below the polyhedron. If {h1, h2, h3} are the heights of the three points, and A is the area of the base, then the volume of the solid will be A(h1+h2+h3)/3. Now we have to add up the volumes of these solids for the upper faces, and subtract them for the lower faces to get the volume of the polyhedron.
Play with the algebra and you'll see that the height of the polyhedron above the horizontal plane doesn't matter. The plane can be above the polyhedron, or pass through it, and the result will still be correct.
So what we need is (1) a way to calculate the area of the base, and (2) a way to tell an "upper" face from a "lower" one. The first is easy if you have the Cartesian coordinates of the points, the second is easy if the points are ordered, and you can combine them and kill two birds with one stone. Suppose for each face you have a list of its corners, in counter-clockwise order. Then the projection of those points on the x-y plane will be counterclockwise for an upper face and clockwise for a lower one. If you use this method to calculate the area of the base, it will come up positive for an upper face and negative for a lower one, so you can add them all together and have the answer.
So how do you get the ordered lists of corners? Start with one triangle, pick an ordering, and for each edge the neighbor that shares that edge should list those two points in the opposite order. Move from neighbor to neighbor until you have a list for every triangle. If the volume of the polyhedron comes up negative, just multiply by -1 (it means you chose the wrong ordering for that first triangle, and the polyhedron was inside-out).
EDIT:
I forgot the best part! If you check the algebra for adding up these volumes, you'll see that a lot of terms cancel out, especially when combining triangles back into the original faces. I haven't worked this out in detail, but it looks as if the final result could be a surprisingly simple function.
Here's a potential implementation for that in Python.
Can anyone please check if it's correct?
I believe that I am missing permutations of the points because my second test (cube) gives 0.666 and not 1. Ideas anyone?
Cheers
EL
class Simplex(object):
'''
Simplex
'''
def __init__(self,coordinates):
'''
Constructor
'''
if not len(coordinates) == 4:
raise RuntimeError('You must provide only 4 coordinates!')
self.coordinates = coordinates
def volume(self):
'''
volume: Return volume of simplex. Formula from http://de.wikipedia.org/wiki/Tetraeder
'''
import numpy
vA = numpy.array(self.coordinates[1]) - numpy.array(self.coordinates[0])
vB = numpy.array(self.coordinates[2]) - numpy.array(self.coordinates[0])
vC = numpy.array(self.coordinates[3]) - numpy.array(self.coordinates[0])
return numpy.abs(numpy.dot(numpy.cross(vA,vB),vC)) / 6.0
class Polyeder(object):
def __init__(self,coordinates):
'''
Constructor
'''
if len(coordinates) < 4:
raise RuntimeError('You must provide at least 4 coordinates!')
self.coordinates = coordinates
def volume(self):
pivotCoordinate = self.coordinates[0]
volumeSum = 0
for i in xrange(1,len(self.coordinates)-3):
newCoordinates = [pivotCoordinate]
for j in xrange(i,i+3):
newCoordinates.append(self.coordinates[j])
simplex = Simplex(newCoordinates)
volumeSum += simplex.volume()
return volumeSum
coords = []
coords.append([0,0,0])
coords.append([1,0,0])
coords.append([0,1,0])
coords.append([0,0,1])
s = Simplex(coords)
print s.volume()
coords.append([0,1,1])
coords.append([1,0,1])
coords.append([1,1,0])
coords.append([1,1,1])
p = Polyeder(coords)
print p.volume()