Raycasting to calculate the volume of a space - python

I'm trying to use ray casting to gather all the surfaces in a room and determine it's volume.
I have a centroid location where the rays will be coming from, but I'm drawing a blank on how to get the rays in all 360 degrees (in 3D space).
I'm not getting any points on the floors or ceilings, it's like it's doing a 60 degree spread rotated about the Z axis.
I think I have the rest of it working, but this is stumping me.
for y in range(360):
for x in range(360):
vector = DB.XYZ(math.sin(math.radians(x)), math.cos(math.radians(x)), math.cos(math.radians(y))).Normalize()
prox = ri.FindNearest(origin, direction).Proximity
point = origin + (direction * prox)

Look at it this way: x and y of vector are created from angle x (-> a circle in the plane) and then you add a z component which lies between -1 and 1 (which cos does). So it's obvious that you end up with a cylindrical distribution.
What you might want are spherical coordinates. Modify your code like this:
for y in range(-90, 91):
for x in range(360):
vector = DB.XYZ(math.sin(math.radians(x)) * cos(math.radians(y)),
math.cos(math.radians(x)) * cos(math.radians(y)),
math.sin(math.radians(y))) # Normalize unnecessary, since vector² = sin² * cos² + cos² * cos² + sin² = 1
prox = ri.FindNearest(origin, direction).Proximity
point = origin + (direction * prox)
But be aware that the angle distribution of rays is not uniform using spherical coordinates. At the poles it's more dense than at the equator. You can mitigate this e.g. by scaling the density of x down, depending on y. The surface elements scale down by cos(y)², so I think you have to scale by cos(y).

Related

Avoid distortion when inserting a rectangle into a point cloud using plane equation

I have a set of different point clouds, from each of which I find the plane with the largest supponrt and get the plane equation (ax + by + cz + d = 0)
Using the plane equation, I am trying to insert a plane into each of the point clouds, which I manage to do. The problem is the distortion of said plane.
How can I insert a plane that has a rectangular form?
This is how I am inserting the plane on each of the point clouds:
I first find the min/max value for the x and y, so the plane is proportional to the point cloud size and intersects it.
I multiply the min by 10 and divide x and y by 10 to increase the density of the inserted plane
min = np.min(point_cloud, axis=0)
max = np.max(point_cloud, axis=0)
for i in range (int(min[0] * 10), int(max[0]* 10)):
for j in range (int(min[1]* 10), int(max[1]* 10)):
x = i/10
y = j/10
z = (-d1 - a1*x - b1*y)/c1
plane.append([x, y, z])
And here is a gif showing different angles of the different point clouds with the plane inserted
Any guidance or literature where I could understand/learn how to fix this?
I think the problem is the way I am calculating z, because when I set z to a fixed number (e.g. 10), the plane has a rectangle form, without distortion. But of course, it doesn't intersect the point cloud.
This is an example of that:
Thanks

Visualisation of proximity of points to 4d-sphere in Python

I am looking for a way to visualise the proximity of points to a 4-dimensional sphere. For a circle I can simply use a scatter plot and observe the distribution of points near the unit circle as shown below. For a 3D sphere I can do something similar. However, how would I go about visualising this for a 4-dimensional sphere?
Is there a way to reduce the dimensionality to project the entire space into 3D? Obviously I can just take the norm of the points and see how close it is to 1, but I would like to have a visual aid of some sort.
Here is one way to convert 4-dimensional coordinates into 3-dimenstional coordinates that will give you a visualization of the distances of the points from the 4D sphere. Since you show no code or equations of your own I'll just give an overview. If you give more details on your own work then you can ask me for more details.
Take a point in 4 dimensions, let's say (x, y, z, w). Then convert those Cartesian coordinates to 4D spherical coordinates
(r, t1, t2, t3), where r is the distance of the point to the origin and t1, t2, t3 are reference angles. Formulas for the conversion are in Wikipedia's entry for n-sphere, though in my preferred transformation I would reverse the order of the Cartesian coordinates. In other words, we get the relations
w = r * cos(t1)
z = r * sin(t1) * cos(t2)
y = r * sin(t1) * sin(t2) * cos(t3)
x = r * sin(t1) * sin(t2) * sin(t3)
We now map that point to a point in 3D space by changing angle t1 to 90° (or pi/2 radians). This has the effect of "rotating" the point away from the w axis down into 3D space in regular spherical coordinates. The distances from the origin and from any 4-sphere centered at the origin were not changed. Now convert to 3D Cartesian coordinates with
z = r * cos(t2)
y = r * sin(t2) * cos(t3)
x = r * sin(t2) * sin(t3)
Now graph those as usual. Since distances to the origin and to the 4-sphere were not changed, this should be a useful visualization.
Looking at those equations, we realize that the values of x, y, and z were all divided by sin(t1). That means you could optimize the calculations by finding only sin(t1) with the formula
sin(t1) = sqrt((x*x + y*y + z*z) / (x*x + y*y + z*z + w*w))
There is no need to find r, t2, or t3 or even t1 itself. You need to be careful for the special case sin(t1) == 0.0, which happens only when x == y == z == 0. I would then map the 4D point (0, 0, 0, w) to the 3D point (w, 0, 0) and the visualization should still work well.
There are other, similar transformations you could use that may be more useful, such as changing angle t3 to zero rather than changing t1. This slightly reduces the calculations but you would need to permute the coordinates and the visualization uses only half the 3-sphere, I believe.
Of course, one way to graph that 3D point to a computer graphing surface is to now set t2 to 90° to get
y = r * cos(t3)
x = r * sin(t3)
and you will get a graph very much like the one you show in your question.
(NOTE: I changed the formulas above, based on further consideration of the best visualization.)

Random point inside annulus with a shifted hole

First of all, will appreciate if someone will give me a proper term for "annulus with a shifted hole", see exactly what kind of shape I mean on a picture below.
Back to main question: I want to pick a random point in the orange area, uniform distribution is not required. For a case of a usual annulus I would've picked random point in (r:R) range and a random angle, then transform those to x,y and it's done. But for this unusual shape... is there even a "simple" formula for that, or should I approach it by doing some kind of polygonal approximation of a shape?
I'm interested in a general approach but will appreciate an example in python, javascript or any coding language of your choice.
Here's a simple method that gives a uniform distribution with no resampling.
For simplicity assume that the center of the outer boundary circle (radius r_outer) is at (0, 0) and that the center of the inner circular boundary (radius r_inner) lies at (x_inner, y_inner).
Write D for the outer disk, H1 for the subset of the plane given by the off-center inner hole, and H2 for the central disk of radius r_inner, centered at (0, 0).
Now suppose that we ignore the fact that the inner circle is not central, and instead of sampling from D-H1 we sample from D-H2 (which is easy to do uniformly). Then we've made two mistakes:
there's a region A = H1 - H2 that we might sample from, even though those samples shouldn't be in the result.
there's a region B = H2 - H1 that we never sample from, even though we should
But here's the thing: the regions A and B are congruent: given any point (x, y) in the plane, (x, y) is in H2 if and only if (x_inner - x, y_inner - y) is in H1, and it follows that (x, y) is in A if and only if (x_inner - x, y_inner - y) is in B! The map (x, y) -> (x_inner - x, y_inner - y) represents a rotation by 180 degress around the point (0.5*x_inner, 0.5*y_inner). So there's a simple trick: generate from D - H2, and if we end up with something in H1 - H2, rotate to get the corresponding point of H2 - H1 instead.
Here's the code. Note the use of the square root of a uniform distribution to choose the radius: this is a standard trick. See this article, for example.
import math
import random
def sample(r_outer, r_inner, x_inner, y_inner):
"""
Sample uniformly from (x, y) satisfiying:
x**2 + y**2 <= r_outer**2
(x-x_inner)**2 + (y-y_inner)**2 > r_inner**2
Assumes that the inner circle lies inside the outer circle;
i.e., that hypot(x_inner, y_inner) <= r_outer - r_inner.
"""
# Sample from a normal annulus with radii r_inner and r_outer.
rad = math.sqrt(random.uniform(r_inner**2, r_outer**2))
angle = random.uniform(-math.pi, math.pi)
x, y = rad*math.cos(angle),rad*math.sin(angle)
# If we're inside the forbidden hole, reflect.
if math.hypot(x - x_inner, y - y_inner) < r_inner:
x, y = x_inner - x, y_inner - y
return x, y
And an example plot, generated by the following:
import matplotlib.pyplot as plt
samples = [sample(5, 2, 1.0, 2.0) for _ in range(10000)]
xs, ys = zip(*samples)
plt.scatter(xs, ys, s=0.1)
plt.axis("equal")
plt.show()
Do you really need exact sampling? Because with acceptance/rejection it should work just fine. I assume big orange circle is located at (0,0)
import math
import random
def sample_2_circles(xr, yr, r, R):
"""
R - big radius
r, xr, yr - small radius and its position
"""
x = xr
y = yr
cnd = True
while cnd:
# sample uniformly in whole orange circle
phi = 2.0 * math.pi * random.random()
rad = R * math.sqrt(random.random())
x = rad * math.cos(phi)
y = rad * math.sin(phi)
# check condition - if True we continue in the loop with sampling
cnd = ( (x-xr)**2 + (y-yr)**2 < r*r )
return (x,y)
Since you have shown no equation, algorithm, or code of your own, but just an outline of an algorithm for center-aligned circles, I'll also just give the outline of an algorithm here for the more general case.
The smaller circle is the image of the larger circle under a similarity transformation. I.e. there is a fixed point in the larger circle and a ratio (which is R/r, greater than one) such that you can take any point on the smaller circle, examine the vector from the fixed point to that point, and multiply that vector by the ratio, then the end of that vector when it starts from the fixed point is a point on the larger circle. This transformation is one-to-one.
So you can choose a random point on the smaller circle (choose the angle at random between 0 and two-pi) and choose a ratio at random between 1 and the proportionality ratio R/r between the circles. Then use that the similarity transformation with the same fixed point but using the random ratio to get the image point of the just-chosen point on the smaller circle. This is a random point in your desired region.
This method is fairly simple. In fact, the hardest mathematical part is finding the fixed point of the similarity transformation. But this is pretty easy, given the centers and radii of the two circles. Hint: the transformation takes the center of the smaller circle to the center of the larger circle.
Ask if you need more detail. My algorithm does not yield a uniform distribution: the points will be more tightly packed where the circles are closest together and less tightly packed where the circles are farthest apart.
Here is some untested Python 3.6.2 code that does the above. I'll test it and show a graphic for it when I can.
import math
import random
def rand_pt_between_circles(x_inner,
y_inner,
r_inner,
x_outer,
y_outer,
r_outer):
"""Return a random floating-point 2D point located between the
inner and the outer circles given by their center coordinates and
radii. No error checking is done on the parameters."""
# Find the fixed point of the similarity transformation from the
# inner circle to the outer circle.
x_fixed = x_inner - (x_outer - x_inner) / (r_outer - r_inner) * r_inner
y_fixed = y_inner - (y_outer - y_inner) / (r_outer - r_inner) * r_inner
# Find a a random transformation ratio between 1 and r_outer / r_inner
# and a random point on the inner circle
ratio = 1 + (r_outer - r_inner) * random.random()
theta = 2 * math.pi * random.random()
x_start = x_inner + r_inner * math.cos(theta)
y_start = y_inner + r_inner * math.sin(theta)
# Apply the similarity transformation to the random point.
x_result = x_fixed + (x_start - x_fixed) * ratio
y_result = y_fixed + (y_start - y_fixed) * ratio
return x_result, y_result
The acceptance/rejection method as described by Severin Pappadeux is probably the simplest.
For a direct approach, you can also work in polar coordinates, with the center of the hole as the pole.
The polar equation (Θ, σ) (sorry, no rho) of the external circle will be
(σ cosΘ - xc)² + (σ sinΘ - yc)² = σ² - 2(cosΘ xc + sinΘ yc)σ + xc² + yc² = R²
This is a quadratic equation in σ, that you can easily solve in terms of Θ. Then you can draw an angle in 0, 2π an draw a radius between r and σ.
This won't give you a uniform distribution, because the range of σ is a function of Θ and because of the polar bias. This might be fixed by computing a suitable transfer function, but this is a little technical and probably not tractable analytically.

Python program to rotate a line not working

I am trying to figure out direction vectors of the arrowheads of an arrow. Basically I'm given a normalized direction vector (u,v,w) and I need the normalized direction vectors of the its two arrow heads which make a 15 degree angle.
My plan is to first start off with a simple normalized vector (0,0,1). The direction vectors of its arrow heads are (-sin(15), 0, -cos(15)) and (sin(15), 0, -cos(15)), and then rotate (0,0,1) so its parallel to the given (u,v,w). I do this by projecting (u,v,w) on its x-axis, and getting its angle relative to (0,0,1), then projecting on the y-axis, and getting its angle relative to (0,0,1), then I use the 3d rotation matrices to use those found angles to rotate the arrow head direction vector.
I have this code below, but its not working properly. Does anyone see whats wrong?
Thanks
ra = 15
ca = math.cos(ra)
sa = math.sin(ra)
px = (0,v,w)
if u!=1:
px = [i/float(math.sqrt(v**2 + w**2)) for i in px]
py = (u,0,w)
if v!=1:
py = [i/float(math.sqrt(u**2 + w**2)) for i in py]
pxangle = math.acos(px[2])
pyangle = math.acos(py[2])
cpx = math.cos(pxangle)
spx = math.sin(pxangle)
cpy = math.cos(pyangle)
spy = math.sin(pyangle)
def rotatefunction(ah):
xr = (ah[0], -spx*ah[2], cpx*ah[2])
return (cpy*xr[0]+spy*xr[2], xr[1], -spy*xr[0]+cpy*xr[2])
lah = rotatefunction((-sa, 0, -ca))
rah = rotatefunction((sa, 0, -ca))
First of all, you need to convert degrees into radians (= degree * (pi / 180)) before passing to math.cos, math.sin, etc.
Suppose the normalized direction vector of the arrow is given by
The unit vector parallel to the z-axis is given by
The orthonormal vector perpendicular to both the unit vectors are given by the cross product
and the angle between the two vector is given by the scalar product
Basically the rotation is about this u (uhat) axis by angle theta. The corresponding rotation matrix is given by
so this is the matrix you need to multiply the arrowhead vectors with.

python: elegant way of finding the GPS coordinates of a circle around a certain GPS location

I have a set of GPS coordinates in decimal notation, and I'm looking for a way to find the coordinates in a circle with variable radius around each location.
Here is an example of what I need. It is a circle with 1km radius around the coordinate 47,11.
What I need is the algorithm for finding the coordinates of the circle, so I can use it in my kml file using a polygon. Ideally for python.
see also Adding distance to a GPS coordinate for simple relations between lat/lon and short-range distances.
this works:
import math
# inputs
radius = 1000.0 # m - the following code is an approximation that stays reasonably accurate for distances < 100km
centerLat = 30.0 # latitude of circle center, decimal degrees
centerLon = -100.0 # Longitude of circle center, decimal degrees
# parameters
N = 10 # number of discrete sample points to be generated along the circle
# generate points
circlePoints = []
for k in xrange(N):
# compute
angle = math.pi*2*k/N
dx = radius*math.cos(angle)
dy = radius*math.sin(angle)
point = {}
point['lat']=centerLat + (180/math.pi)*(dy/6378137)
point['lon']=centerLon + (180/math.pi)*(dx/6378137)/math.cos(centerLat*math.pi/180)
# add to list
circlePoints.append(point)
print circlePoints
Use the formula for "Destination point given distance and bearing from start point" here:
http://www.movable-type.co.uk/scripts/latlong.html
with your centre point as start point, your radius as distance, and loop over a number of bearings from 0 degrees to 360 degrees. That will give you the points on a circle, and will work at the poles because it uses great circles everywhere.
It is a simple trigonometry problem.
Set your coordinate system XOY at your circle centre. Start from y = 0 and find your x value with x = r. Then just rotate your radius around origin by angle a (in radians). You can find the coordinates of your next point on the circle with Xi = r * cos(a), Yi = r * sin(a). Repeat the last 2 * Pi / a times.
That's all.
UPDATE
Taking the comment of #poolie into account, the problem can be solved in the following way (assuming the Earth being the right sphere). Consider a cross section of the Earth with its largest diameter D through our point (call it L). The diameter of 1 km length of our circle then becomes a chord (call it AB) of the Earth cross section circle. So, the length of the arc AB becomes (AB) = D * Theta, where Theta = 2 * sin(|AB| / 2). Further, it is easy to find all other dimensions.

Categories

Resources