-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ray casting, cast multiple rays at once #173
Comments
Sadly, at the moment this isn't something that |
FYI: #80 and pyvista/pyvista#478 |
You may be able to vectorize the |
Thanks for the feedback guys. @banesullivan do you mean something like with numpy.apply_along_axis. Actually I just found it, it seems interesting. I will give it a try. Also is it possible to draw all the rays at once, with import pyvista as pv
# Create source to ray trace
sphere = pv.Sphere(radius=0.85)
# Define line segment
start = [0, 0, 0]
stop = [0.25, 1, 0.5]
# Perform ray trace
points, ind = sphere.ray_trace(start, stop)
# Create geometry to represent ray trace
ray = pv.Line(start, stop) <---------- How to replace this line with plot_arrows()
intersection = pv.PolyData(points)
# Render the result
p = pv.Plotter()
p.add_mesh(sphere,
show_edges=True, opacity=0.5, color="w",
lighting=False, label="Test Mesh")
p.add_mesh(ray, color="blue", line_width=5, label="Ray Segment") <---------- How to replace this line with plot_arrows()
p.add_mesh(intersection, color="maroon",
point_size=25, label="Intersection Points")
p.add_legend()
p.show() |
I've tried to vectorize the
but it doesn't seem to work, I am getting Any idea how we might be able to make it work. |
As for vectorizing the ray tracing...
Yep! That's what I was getting after. However, Also, the Here is a solution to perform batch ray tracing (a for-loop) that collects the first intersected cell IDs for the rays: import numpy as np
import pyvista as pv
sphere = pv.Sphere(radius=0.85)
def ray_trace(start, stop):
"""Pass two same sized arrays of the start and stop coordinates for all rays"""
assert start.shape == stop.shape
assert start.ndim == 2
# Launch this for loop in parallel if needed
zeroth_cellids = []
for i in range(len(start)):
_, ids = sphere.ray_trace(start[i], stop[i])
if len(ids) < 1:
v = None
else:
v = ids[0]
zeroth_cellids.append(v)
return np.array(zeroth_cellids)
start = np.array([[0,0,0], [0,0,0]])
stop = np.array([[0.25, 1, 0.5], [0.5, 1, 0.25]])
cell_ids = ray_trace(start, stop)
cells = sphere.extract_cells(cell_ids)
p = pv.Plotter(notebook=0)
p.add_mesh(sphere,
show_edges=True, opacity=0.5, color="w",
lighting=False, label="Test Mesh")
p.add_arrows(start, stop, mag=1, color=True, opacity=0.5, )
p.add_mesh(cells, color="maroon",
label="Intersection Cells")
p.add_legend()
p.show() |
Thanks for the feedback. Indeed The solution with the multiple threads is an option, and then I was checking on the the
|
plotter = Plotter()
plotter.add_arrows(cent, direction)
plotter.show() there is nothing special about it. |
Has there been any further progress on this? I'm trying to create a 2D contour plot from the Elevation data of a 3D surface. The issue is that any [x, y] coordinate could have multiple points of the surface along the z-axis and I want to plot the contour of only the maximum elevation at any point on the plane. My solution so far, which is based on the distance between two surfaces example, is to create a 2D uniform grid above the surface then ray trace along -Z from every point and store the height of the first point found. The problem with this is that the python for loop can only get through around 200 ray traces per second. If I want my uniform grid to contain, say, 1 million points, then it's going to take hours to process, then times by a fairly large number of surfaces and I end up in months. The best I've been able to find on google is the suggestion that this sort of thing would be much much faster done in C++, but that doesn't really help me much when the rest of my project is in Python. I have tried threading this operation in the past but ran into weird errors that seemed to suggest the VTKOBBTree wasn't thread safe. |
There hasn't been any progress on this... 😞 Perhaps the note in #173 (comment) helps?
Otherwise, it may be a long time before any pyvista developers find the time to look into a way of doing this in batch with the VTK python bindings (I suspect it should be possible though) |
I personally went with trimesh and embree (it supports only until 2.17) were I was able to cast 18 million rays in less than >20 sec. Check here the discussion mikedh/trimesh#875 you might be able to use embree v.3 with this wrapper though https://github.com/sampotter/python-embree which might improve casting time even further (unfortunately I did not have the time to test it myself). |
I got this working with the trimesh library. Unfortunately trimesh only supports meshio formats and meshio doesn't support VTK polydata, so I had to convert my surface into a .ply file first, but the results are impressive nonetheless. Codefrom pathlib import Path
from time import time
import trimesh
import pyvista as pv
import numpy as np
vtk_file = Path("test_file.vtp")
vista_mesh = pv.read(vtk_file)
vista_mesh.clear_arrays()
xmin, xmax, ymin, ymax, zmin, zmax = vista_mesh.bounds
points_per_metre = 20
dims = [
int(round(xmax - xmin, 2) * points_per_metre) + 1,
int(round(ymax - ymin, 2) * points_per_metre) + 1,
1,
]
origin = [xmin, ymin, zmax + 1]
spacing = [1 / points_per_metre] * 3
ug = pv.UniformGrid(dims, spacing, origin)
ug["heights"] = np.empty(ug.n_points)
ug["heights"][:] = np.nan
vec = [0, 0, zmin - zmax - 1]
start = time()
for i in range(ug.n_points):
p = ug.points[i]
ip, ic = vista_mesh.ray_trace(p, p + vec, first_point=True)
if ip.any():
ug["heights"][i] = ip[2]
print(f"for-loop ray tracing with pyvista did {ug.n_points} points in {time()-start:.3f} s")
ug2 = pv.UniformGrid(dims, spacing, origin)
ug2["heights"] = np.empty(ug2.n_points)
ug2["heights"][:] = np.nan
vista_mesh.save("test_file.ply")
trimesh_mesh = trimesh.load("test_file.ply")
plane_points = ug2.points
vectors = np.array([[0, 0, -1]] * len(plane_points))
start = time()
loc, idr, idt = trimesh_mesh.ray.intersects_location(plane_points, vectors, multiple_hits=False)
print(f"\nvectorised ray tracing with trimesh did {ug2.n_points} points in {time()-start:.3f} s")
start = time()
for i, location in zip(idr, loc):
ug2["heights"][i] = location[2]
print(f"plus {time()-start:.3f} s to unpack the results")
points_per_metre = 200
dims = [
int(round(xmax - xmin, 2) * points_per_metre) + 1,
int(round(ymax - ymin, 2) * points_per_metre) + 1,
1,
]
origin = [xmin, ymin, zmax + 1]
spacing = [1 / points_per_metre] * 3
ug3 = pv.UniformGrid(dims, spacing, origin)
ug3["heights"] = np.empty(ug3.n_points)
ug3["heights"][:] = np.nan
plane_points = ug3.points
vectors = np.array([[0, 0, -1]] * len(plane_points))
start = time()
loc, idr, idt = trimesh_mesh.ray.intersects_location(plane_points, vectors, multiple_hits=False)
print(f"\nvectorised ray tracing with trimesh did {ug3.n_points} points in {time()-start:.3f} s")
start = time()
for i, location in zip(idr, loc):
ug3["heights"][i] = location[2]
print(f"plus {time()-start:.3f} s to unpack the results") and the output
interestingly the majority of the initial 7 s is loading the file, which is then cached for the second run with many more traces. |
This is cool, thanks for posting a working solution, @Keou0007 |
Hi guys,
I am trying to use the ray casting functionality for a
PolyData
, and I was looking at the corresponding example https://docs.pyvista.org/examples/01-filter/poly-ray-trace.html. However, it seems that at the moment you cover only the case of one ray each time, where you provide one origin and one direction. Thus I was wondering if it is possible to apply the casting procedure for multiple rays at once and ideally without a for loop, for example:Thanks.
The text was updated successfully, but these errors were encountered: