'''
This module contains functions for drawing 3D objects using matplotlib.
'''
import numpy as np
import open3d as o3d
from rsaitehu import geometry as geom
import matplotlib.pyplot as plt
# from mpl_toolkits.mplot3d import Axes3D
def draw_plane_as_lines_open3d(A, B, C, D, size=10, line_color=[1, 0, 0]):
# Define the vertices of the plane
vertices = np.array([
[-size, -size, -(D + A * -size + B * -size) / C],
[size, -size, -(D + A * size + B * -size) / C],
[size, size, -(D + A * size + B * size) / C],
[-size, size, -(D + A * -size + B * size) / C]
])
# Define the lines
lines = np.array([
[0, 1],
[1, 2],
[2, 3],
[3, 0]
])
# Create the LineSet
lineset = o3d.geometry.LineSet()
lineset.points = o3d.utility.Vector3dVector(vertices)
lineset.lines = o3d.utility.Vector2iVector(lines)
# Set the color for each line
colors = [line_color for i in range(len(lines))]
lineset.colors = o3d.utility.Vector3dVector(colors)
return lineset
def draw_plane_as_lines_open3d(A, B, C, D, size=10, line_color=[1, 0, 0], grid_density=5, external_point=None):
vertices = []
lines = []
plane = np.array([A, B, C, D])
if external_point is not None:
center = geom.get_point_of_plane_closest_to_given_point(plane, external_point)
center_x, center_y, center_z = center[0], center[1], center[2]
else:
center_x, center_y, center_z = 0, 0, 0
# Add the corners of the grid
for i in np.linspace(-size, size, grid_density):
for j in np.linspace(-size, size, grid_density):
x = center_x + i
y = center_y + j
z = -(D + A * x + B * y) / C
vertices.append([x, y, z])
n = len(vertices)
vertices = np.array(vertices)
# Add the lines for the grid (horizontal and vertical)
for i in range(grid_density):
for j in range(grid_density - 1):
# horizontal lines
lines.append([i * grid_density + j, i * grid_density + j + 1])
# vertical lines
lines.append([j * grid_density + i, (j + 1) * grid_density + i])
lines = np.array(lines)
# Create the LineSet
lineset = o3d.geometry.LineSet()
lineset.points = o3d.utility.Vector3dVector(vertices)
lineset.lines = o3d.utility.Vector2iVector(lines)
# Set the color for each line
colors = [line_color for _ in range(len(lines))]
lineset.colors = o3d.utility.Vector3dVector(colors)
return lineset
[docs]def draw_face_of_cube(plane: np.ndarray, cube_min: np.ndarray, cube_max: np.ndarray, color: str = "red", alpha: float = 0.5, ax = None):
'''
Draws a plane in 3D space using matplotlib.
:param plane: A 4x1 numpy array containing the coefficients of the plane corresponding to the face.
:type plane: numpy.ndarray
:param cube_min: A 3x1 numpy array containing the minimum x, y, and z values of the cube.
:type cube_min: numpy.ndarray
:param cube_max: A 3x1 numpy array containing the maximum x, y, and z values of the cube.
:type cube_max: numpy.ndarray
:param color: The color of the plane.
:type color: str
:param alpha: The transparency of the plane.
:type alpha: float
:param ax: The matplotlib axis to draw on.
:type ax: matplotlib.axes.Axes3D
:return: None
:rtype: None
'''
plot_inside_function = False
if ax is None:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
plot_inside_function = True
# Define the plane's normal vector
normal = plane[:3]
# Create a grid of points on the plane for visualization
x_vals = np.linspace(cube_min[0], cube_max[0], 50)
y_vals = np.linspace(cube_min[1], cube_max[1], 50)
z_vals = np.linspace(cube_min[2], cube_max[2], 50)
x_grid, y_grid = np.meshgrid(x_vals, y_vals)
if normal[2] != 0:
z_grid = (-normal[0] * x_grid - normal[1] * y_grid - plane[3]) / normal[2]
elif normal[1] != 0:
x_grid, z_grid = np.meshgrid(x_vals, z_vals)
y_grid = (-normal[0] * x_grid - normal[2] * z_grid - plane[3]) / normal[1]
else:
y_grid, z_grid = np.meshgrid(y_vals, z_vals)
x_grid = (-normal[1] * y_grid - normal[2] * z_grid - plane[3]) / normal[0]
# Plot the plane
ax.plot_surface(x_grid, y_grid, z_grid, alpha=alpha, color=color)
if plot_inside_function:
# Set axis labels
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
# Show the plot
plt.show()
else:
return ax
[docs]def draw_cube(cube_min: np.ndarray, cube_max: np.ndarray, color: str = "red", alpha: float = 0.5, ax = None):
'''
Draws a cube in 3D space using matplotlib.
:param cube_min: A 3x1 numpy array containing the minimum x, y, and z values of the cube.
:type cube_min: numpy.ndarray
:param cube_max: A 3x1 numpy array containing the maximum x, y, and z values of the cube.
:type cube_max: numpy.ndarray
:param color: The color of the cube.
:type color: str
:param alpha: The transparency of the cube.
:type alpha: float
:return: None
:rtype: None
:Example:
::
>>> import rsaitehu.drawing as drawing
>>> import numpy as np
>>> cube_min = np.array([-2, -2, -1])
>>> cube_max = np.array([1, 2, 2])
>>> drawing.draw_cube(cube_min, cube_max, color="red", alpha=0.5)
|drawing_draw_cube_example|
.. |drawing_draw_cube_example| image:: ../../doc/source/_static/images/drawing_draw_cube_example.png
'''
plot_inside_function = False
if ax is None:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
plot_inside_function = True
# Plane equations for the cube faces (front, back, top, bottom, left, right).
planes = [
(1, 0, 0, -cube_min[0]), # Front face
(-1, 0, 0, cube_max[0]), # Back face
(0, 1, 0, -cube_min[1]), # Top face
(0, -1, 0, cube_max[1]), # Bottom face
(0, 0, 1, -cube_min[2]), # Left face
(0, 0, -1, cube_max[2]) # Right face
]
# Draw the cube faces
for plane in planes:
# draw a plane limited to the cube's bounds
draw_face_of_cube(plane, cube_min, cube_max, color=color, alpha=alpha, ax=ax)
if plot_inside_function:
# Set axis labels
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
# Show the plot
plt.show()
else:
return ax
[docs]def draw_line_extension_to_plane(line: np.ndarray, plane: np.ndarray, ax = None):
'''
Draws a line in 3D space, and the intersection point of the line and the plane, but this does not draw the plane.
:param line: A 2x3 numpy array containing the endpoints of the line.
:type line: numpy.ndarray
:param plane: A 4x1 numpy array containing the coefficients of the plane.
:type plane: numpy.ndarray
:return: None
:rtype: None
:Example:
::
>>> import rsaitehu.geometry as geom
>>> import numpy as np
>>> import rsaitehu.drawing as drawing
>>> import matplotlib.pyplot as plt
>>> line = np.array([[0, 0, 0], [1, 1, 1]])
>>> plane = np.array([0, 0, 1, -3])
>>> intersection_point = geom.get_intersection_point_of_line_with_plane(line, plane)
>>> intersection_point
array([3., 3., 3.])
>>> fig = plt.figure()
>>> ax = fig.add_subplot(111, projection='3d')
>>> drawing.draw_line_extension_to_plane(line, plane, ax)
>>>
>>> drawing.draw_line_extension_to_plane(line, plane)
|drawing_draw_line_extension_to_plane_example|
.. |drawing_draw_line_extension_to_plane_example| image:: ../../doc/source/_static/images/drawing_draw_line_extension_to_plane_example.png
'''
plot_inside_function = False
if ax is None:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
plot_inside_function = True
intersection_point = geom.get_intersection_point_of_line_with_plane(line, plane)
# Calculate the distances from line[0] and line[1] to the intersection point
distance_to_line0 = np.linalg.norm(intersection_point - line[0])
distance_to_line1 = np.linalg.norm(intersection_point - line[1])
# Determine which point is farther away
farther_point_index = 0 if distance_to_line0 > distance_to_line1 else 1
farther_point = line[farther_point_index]
# Plot the remaining portion of the line from the farther point to the intersection point
intersection_x = intersection_point[0]
intersection_y = intersection_point[1]
intersection_z = intersection_point[2]
farther_x = farther_point[0]
farther_y = farther_point[1]
farther_z = farther_point[2]
ax.plot([farther_x, intersection_x], [farther_y, intersection_y], [farther_z, intersection_z], linestyle='--', color='blue')
# Plot the intersection point
ax.scatter(intersection_point[0], intersection_point[1], intersection_point[2], c='green', label='Intersection Point', s=100)
if plot_inside_function:
# Set axis labels
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
# Show the plot
plt.show()
else:
return ax