How can I find points at an equal distance between 2 objects? - math

I'm trying to find points at an equal distance between 2 other points in 3D space. For example, I have 2 cubes in my scene. I want to add 5 (or 3, or 80...) locators at an equal distance between these two spheres with Pymel.
I can easily find the midway point between the spheres like this:
import pymel.core as pm
import pymel.core.datatypes as dt
pos_1, pos_2 = pm.selected()
point_1 = dt.Vector(pos_1.getTranslation())
point_2 = dt.Vector(pos_2.getTranslation())
midway_point = (point_1 + point_2) / 2
However, I can't seem to figure out how to get multiple points on the line between the two spheres.
I tried something like this:
import pymel.core as pm
import pymel.core.datatypes as dt
pos_1, pos_2 = pm.selected()
point_1 = dt.Vector(pos_1.getTranslation())
point_2 = dt.Vector(pos_2.getTranslation())
distance = point_1.distanceTo(point_2)
divided_distance = distance / 5
for i in range (1, 5):
position = point_1 + (divided_distance * i)
pm.spaceLocator(position = position, absolute = True)
Which does add 5 locators between the two spheres, but they're not on the line connecting the two points in 3D space.
Can anyone point me in the right direction?

When you calculate the distance between the two points, you're getting a scalar, essentially a single number that is the number of units the points are away from each other. But what you're not getting is the direction from one to the other. That would be a vector. To get the vector, change this line:
distance = point_1.distanceTo(point_2)
to this:
difference = point_2 - point_1
Now instead of getting the single unit distance between the two points, you're getting a vector with the distance required for each of the three axes.
Almost miraculously, all the other code in your script will work if you just replace the variable distance with difference

Related

How to transform a distorted square, captured via xy camera coordinates, to a perfect square in cm coordinates?

I am very sorry for asking a question that is probably very easy if you know how to solve it, and where many versions of the same question has been asked before. However, I am creating a new post since I have not found an answer to this specific question.
Basically, I have a 200cm x 200cm square that I am recording with a camera above it. However, the camera distorts the square slightly, see example here.. I am wondering how I go from transforming the x,y coordinates in the camera to real-life x,y coordinates (e.g., between 0-200 cm for each side). I understand that I probably need to apply some kind of transformation matrix, but I do not know which one, nor how to determine the transformation matrix. I haven't done any serious linear-algebra in a long time, so I appreciate any pointers for what to read up on, or how to get it done. I am working in python, so if there is some ready code for doing the transformation that would also be useful to know.
Thanks a lot!
I will show this using python and numpy.
import numpy as np
First, you have to understand the projection model
def apply_homography(H, p1):
p = H # p1.T
return (p[:2] / p[2]).T
With some algebraic manipulation you can determine the points at the plane z=1 that produced the given points.
def revert_homography(H, p2):
Hb = np.linalg.inv(H)
# 1 figure out which z coordinate should be added to p2
# order to get z=1 for p1
z = 1/(Hb[2,2] + (Hb[2,0] * p2[:,0] + Hb[2,1]*p2[:,1]))
p2 = np.hstack([p2[:,:2] * z[:,None], z[:, None]])
return p2 # Hb.T
The projection is not invertible, but under the complanarity assumption it may be inverted successfully.
Now, let's see how to determine the H matrix from the given points (assuming they are coplanar).
If you have the four corners in order in order you can simply specify the (x,y) coordinates of the cornder, then you can use the projection equations to determine the homography matrix like here, or here.
This requires at least 5 points to be determined as there is 9 coefficients, but we can fix one element of the matrix and make it an inhomogeneous equation.
def find_homography(p1, p2):
A = np.zeros((8, 2*len(p1)))
# x2'*(H[2,0]*x1+H[2,1]*x2)
A[6,0::2] = p1[:,0] * p2[:,0]
A[7,0::2] = p1[:,1] * p2[:,0]
# - (H[0,0]*x1+H[0,1]*y1+H[0,2])
A[0,0::2] = -p1[:,0]
A[1,0::2] = -p1[:,1]
A[2,0::2] = -1
# y2'*(H[2,0]*x1+H[2,1]*x2)
A[6,1::2] = p1[:,0] * p2[:,1]
A[7,1::2] = p1[:,1] * p2[:,1]
# - (H[1,0]*x1+H[1,1]*y1+H[1,2])
A[3,1::2] = -p1[:,0]
A[4,1::2] = -p1[:,1]
A[5,1::2] = -1
# assuming H[2,2] = 1 we can pass its coefficient
# to the independent term making an inhomogeneous
# equation
b = np.zeros(2*len(p2))
b[0::2] = -p2[:,0]
b[1::2] = -p2[:,1]
h = np.ones(9)
h[:8] = np.linalg.lstsq(A.T, b, rcond=None)[0]
return h.reshape(3,3)
Here a complete usage example. I pick a random H and transform four random points, this is what you have, I show how to find the transformation matrix H_. Next I create a test set of points, and I show how to find the world coordinates from the image coordinates.
# Pick a random Homography
H = np.random.rand(3,3)
H[2,2] = 1
# Pick a set of random points
p1 = np.random.randn(4, 3);
p1[:,2] = 1;
# The coordinates of the points in the image
p2 = apply_homography(H, p1)
# testing
# Create a set of random points
p_test = np.random.randn(20, 3)
p_test[:,2] = 1;
p_test2 = apply_homography(H, p_test)
# Now using only the corners find the homography
# Find a homography transform
H_ = find_homography(p1, p2)
assert np.allclose(H, H_)
# Predict the plane points for the test points
p_test_predicted = revert_homography(H_, p_test2)
assert np.allclose(p_test_predicted, p_test)

how to calculate the coordinates on a polyline perpendicular to point (in 3D)?

I have two approximately parallel polylines representing railway tracks, consisting of hundreds (maybe thousands) of x, y, z coordinates. The two lines stay approximately 1.435m apart, but bend and curve as a railway would.
If I pick a point on one of the polylines, how do find the point which is perpendicular on the other parallel polyline?
I take it CAD programs use the cross product to find the distance / point and it chooses the line to snap to based on where your mouse is hovering.
I would like to achieve the same thing, but without hovering your mouse over the line.
Is there a way to simply compute the closest line segment on the parallel line? Or to see which segment of the polyline passes through a perpendicular plane at the selected point?
It isn't practical to loop through the segments as there are so many of them.
In python the input would be something like point x, y, z on rail1 and I would be looking to output point x, y, z on rail2.
Many thanks.
You want the point of the minimum distance to the other track.
If the other track is defined by line segments, each spanning two points with a parameter t going between 0 and 1
pos(t) => pos_1 + t * ( pos_2 - pos_1 )
You need to find the t value that produces the minimum distance to the point. Place a temporary coordinate system on the point and express the ends of each line segment pos_1 and pos_2 in relative coordinates to the point of interest.
The value of t is for the closest point is
dot(pos_1,pos_1) - dot(pos_1,pos_2)
t = ------------------------------------------------------
dot(pos_1,pos_1)-2*dot(pos_1,pos_2)+dot(pos_2,pos_2)
where dot(a,b)=ax*bx+ay*by+az*bz is the vector dot product.
Now if the resulting t is between 0 and 1, then the closest point is on this segment, and its coordinates are given by
pos(t) => pos_1 + t * ( pos_2 - pos_1 )

How to draw spokes from the center of a polygon to it's boundary using R

Suppose we have a polygon with five vertices. The two coordinates of the vertices are-
>x=c(1,4,6,3,-2)
>y=c(1,1,5,9,4)
We define the centre of the polygon as the point (mean(x),mean(y)).
I am struggling to draw spokes from the centre of the polygon to the boundary of the polygon such that the spokes creates same angle in the centre (i.e., two neighbouring spokes create equal angle in the centre). I also want to have the all the points on the boundary of the polygon (red circle in the following plot) in orderly manner.
Here is a rough sample plot (convex) which I want to have:
Note: The polygon I am dealing with not necessarily convex.
Sample plot (non-convex)
The output I want: 1) The coordinates of the line (i.e., the intersection points of the line through the origin and boundary segments of the polygon).
2) For each equispaced angle (theta in fig.2) I want a to draw a spoke corresponding to each theta (as in figure 2). Note that, angle lies between 0 to 360 degree.
3) In case of my second polygon (non-convex) where the same line go through two boundary segments (creating three intersecting points), I want to have three coordinates corresponding to the same angle (theta).
Could anyone help me in doing that using R? Thanks in advance.
Here you go. You need the sp and rgeos packages:
spokey <- function(xy,n=20){
xcent = mean(xy[,1])
ycent = mean(xy[,2])
cent = sp::SpatialPoints(cbind(xcent, ycent))
pts = sp::SpatialPoints(xy)
## take the furthest distance from centre to vertex, times two!
r = 2 * max(sp::spDistsN1(pts, cent))
theta=seq(0,2*pi,length=n+1)[-(n+1)]
## construct a big wheel of spoke lines
sl = sp::SpatialLines(
lapply(1:length(theta),function(id){
t = theta[id]
sp::Lines(
list(
sp::Line(
rbind(
c(xcent, ycent),
c(xcent + r * cos(t),ycent + r * sin(t))
)
)
),ID=id)
}))
## construct the polygon as a SpatialPolygons object:
pol = sp::SpatialPolygons(list(sp::Polygons(list(sp::Polygon(rbind(xy,xy[1,]))),ID=1)))
## overlay spokes on polygon as "SpatialLines" so we do line-on-line
## intersect which gets us points
spokes = rgeos::gIntersection(sl, as(pol,"SpatialLines"), byid=TRUE)
spokes
}
It takes a matrix of coordinates where the first point is not the last point:
xy1 = structure(c(4.49425847117117, 4.9161781929536, 7.95751618746858,
7.92235621065338, 9.76825499345149, 9.9616348659351, 8.04541612950659,
7.83445626861537, 6.42805719600729, 0.644241009906543, 2.40223985066665,
1.24196061576498, 2.13854002455263, 7.935927470861, 9.41043173309254,
9.33179150577352, 6.50074332228897, 7.34612576596839, 2.76533252463575,
1.07456763727692, 3.88595576393172, 1.17286792142569, 2.745672467806,
5.20317957152522, 5.81264133324759, 8.21116826647756), .Dim = c(13L,
2L))
and then:
> plot(xy1,asp=1)
> polygon(xy1)
> spokes = spokey(xy1,20) # second arg is number of spokes
> points(spokes,pch=19,col="red")
gets you:
If you don't believe it, draw the segments from the centre to the points :)
segments(mean(xy1[,1]),mean(xy1[,2]), coordinates(spokes)[,1], coordinates(spokes)[,2])
The function coordinates(spokes) will get you a two-column matrix of the spoke points - its returned as a SpatialPoints object at present.
I modified this to handle the convex case illustrated.
You will have to write code that computes the intersection of a spoke from the center to each edge line segment. Not that hard, really, but have never seen it in R. Then you will have to loop over the angles that you are interested in drawing, loop over the segments, find the ones it intersects, sort those values, and then draw the line to the intersection you are interested in.
You would then to the furthest, or some combination (maybe a dotted line between the closest and the furthest).
In pseudo-code:
for each spoke you want to draw
calculate the spoke-line from the center to some point far outside
initialize edge intersection-point list to empty
for each edge-segment
calculate the intersection-point of spoke-line and edge-segment
if the intersection-point exists
add it to the intersection list
now go through the intersections and find the furthest
draw the spoke from the center to the furthest intersection point
continue with the next spoke
This would probably take several hours to research and write, unless you write this kind of graphics code constantly.

R Find the point of intersection of two lines connecting 4 coordinates

I apologize in advance if my code looks very amateurish.
I'm trying to assign quadrants to 4 measurement stations approximately located on the edges of a town.
I have the coordinates of these 4 stations:
a <- c(13.2975,52.6556)
b <- c(14.0083,52.5583)
c <- c(13.3722,52.3997)
d <- c(12.7417,52.6917)
Now my idea was to create lines connecting the north-south and east-west stations:
line.1 <- matrix(c(d[1],b[1],d[2],b[2]),ncol=2)
line.2 <- matrix(c(a[1],c[1],a[2],c[2]),ncol=2)
Plotting all the stations the connecting lines looks allright, however not very helpful for analyzing it on a computer.
So I calculated the eucledian vectors for the two lines:
vec.1 <- as.vector(c((b[1]-d[1]),(b[2]-d[2])))
vec.2 <- as.vector(c((c[1]-a[1]),(c[2]-a[2])))
which allowed me to calculate the angle between the two lines in degrees:
alpha <- acos((vec.1%*%vec.2) / (sqrt(vec.1[1]^2+vec.1[2]^2)*
sqrt(vec.2[1]^2+vec.2[2]^2)))) * 180/pi
The angle I get for alpha is 67.7146°. This looks fairly good. From this angle I can easily calculate the other 3 angles of the intersection, however I need values relative to the grid so I can assign values from 0°-360° for the wind directions.
Now my next planned step was to find the point where the two lines intersect, add a horizontal and vertical abline through that point and then calculate the angle relative to the grid. However I can't find a proper example that does that and I don't think I have a nice linear equation system I could solve.
Is my code way off? Or maybe anyone knows of a package which could help me? It feels like my whole approach is a bit wrong.
Okay I managed to calculate the intersection point, using line equations. Here is how.
The basic equation for two points is like this:
y - y_1 = (y_2-y_1/x_2-x_1) * (x-x_1)
If you make one for each of the two lines, you can just substitute the fractions.
k.1 <- ((c[2]-a[2])/(c[1]-a[1]))
k.2 <- ((b[2]-d[2])/(b[1]-d[1]))
Reshaping the two functions you get a final form for y:
y <- (((-k.1/k.2)*d[2]+k.1*d[1]-k.1*c[1]+d[2])/(1-k.1/k.2))
This one you can now use to calculate the x-value:
x <- ((y-d[2])+d[1]*k.2)/k.2
In my case I get
y = 52.62319
x = 13.3922
I'm starting to really enjoy this program!
Wikipedia has a good article on finding the intersection between two line segments with an explicit formula. However, you don't need to know the point of intersection to calculate the angle to the grid (or axes of coordinate system.) Just compute the angles from your vec.1 and vec.2 to the basis vectors:
e1 <- c(1, 0)
e2 <- c(0, 1)
as you have done.

2D Line Normals

I have what I'm sure is a very simple problem but I can't work it out.
I have two 2d vectors that form a line and I am looking to find the normals of this line. example:
vector 1 = ( -10 , 10 ) vector 2 = ( -10, -10 )
How do I calculate the normals for the line defined by these vectors?
It's hard to tell which "normal" you want.
Do you mean out of the plane that the two vectors lie in? That's the cross-product of the two. In this case it's simple: (0, 0, 1) is the normal vector, because both lie in the xy-plane.
Do you mean one of the two normals in the plane for the line that runs from the head of vector 1 to the head of vector 2? All you need to do there is calculate the vector between them, exchange the values of the x- and y-components, and toggle the sign of either component.
In your case,
v2 - v1 = (-10-(-10))i + (-10-10)j = 0i - 20j
The normal vector is:
n1 = 20i + 0j (points in the positive x-direction)
n2 = -20i + 0j (points in the negative x-direction)
Obviously you should normalize these to be unit vectors.
There are two vectors perpendicular to any line in a plane; they point in opposite directions.
If I understand your problem correctly, your "line" is x = -10, and the normal is y = real number.

Resources