-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add README.md to describe how to build API Documentation + add method…
…s in plot_tools and vector_tools
- Loading branch information
1 parent
3e3d411
commit 4496a60
Showing
6 changed files
with
210 additions
and
168 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
The tutorial to build a documentation using Sphinx can be found following : https://github.com/sfarrens/ecole-euclid-2023?tab=readme-ov-file |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,136 @@ | ||
import numpy as np | ||
import plotly.graph_objects as go | ||
|
||
def plot_angle_3d(self, ax, origin, v1, v2, angle, num_points=1000, radius=0.5, **kwargs): | ||
"""Plot angle 3d. | ||
General function to plot a 3D angle between two vectors v1 and v2. | ||
Parameters | ||
---------- | ||
ax : mpl_toolkits.mplot3d.axes3d.Axes3D | ||
Matplotlib 3D axis | ||
origin : array_like | ||
Position of the origin of the angle. | ||
v1 : array_like | ||
Vector 1. | ||
v2 : array_like | ||
Vector 2. | ||
angle : float | ||
Angle between v1 and v2, in radians. | ||
num_points : int, optional | ||
Number of points used to plot the angle, by default 100 | ||
radius : float, optional | ||
Radiius from the origin at which the angle is plotted, by default 0.5 | ||
Other Parameters | ||
---------------- | ||
kwargs : optional | ||
Any kwarg for plt.plot() | ||
""" | ||
|
||
#! This function can be moved in a more genral repostitory | ||
|
||
v1_norm = v1 / np.linalg.norm(v1) | ||
v2_norm = v2 / np.linalg.norm(v2) | ||
def plot_angle_3d(ax, origin, v1, v2, angle, num_points=1000, radius=0.5, **kwargs): | ||
"""Plot angle 3d. | ||
General function to plot a 3D angle between two vectors v1 and v2. | ||
Parameters | ||
---------- | ||
ax : mpl_toolkits.mplot3d.axes3d.Axes3D | ||
Matplotlib 3D axis | ||
origin : array_like | ||
Position of the origin of the angle. | ||
v1 : array_like | ||
Vector 1. | ||
v2 : array_like | ||
Vector 2. | ||
angle : float | ||
Angle between v1 and v2, in radians. | ||
num_points : int, optional | ||
Number of points used to plot the angle, by default 100 | ||
radius : float, optional | ||
Radiius from the origin at which the angle is plotted, by default 0.5 | ||
# Create orthonormal basis for the plane containing v1 and v2 | ||
normal = np.cross(v1_norm, v2_norm) | ||
if np.allclose(normal, 0): | ||
# Vectors are parallel, choose an arbitrary perpendicular vector | ||
normal = np.array([1, 0, 0]) if np.allclose(v1_norm, [0, 1, 0]) else np.cross(v1_norm, [0, 1, 0]) | ||
normal = normal / np.linalg.norm(normal) | ||
Other Parameters | ||
---------------- | ||
kwargs : optional | ||
Any kwarg for plt.plot() | ||
""" | ||
|
||
#! This function can be moved in a more genral repostitory | ||
|
||
v1_norm = v1 / np.linalg.norm(v1) | ||
v2_norm = v2 / np.linalg.norm(v2) | ||
|
||
# Create orthonormal basis for the plane containing v1 and v2 | ||
normal = np.cross(v1_norm, v2_norm) | ||
if np.allclose(normal, 0): | ||
# Vectors are parallel, choose an arbitrary perpendicular vector | ||
normal = np.array([1, 0, 0]) if np.allclose(v1_norm, [0, 1, 0]) else np.cross(v1_norm, [0, 1, 0]) | ||
normal = normal / np.linalg.norm(normal) | ||
|
||
angles = np.linspace(0, angle, num_points) | ||
arc_points = np.zeros((num_points, 3)) | ||
|
||
for i, theta in enumerate(angles): | ||
rotated = v1_norm * np.cos(theta) + \ | ||
np.cross(normal, v1_norm) * np.sin(theta) + \ | ||
normal * np.dot(normal, v1_norm) * (1 - np.cos(theta)) | ||
arc_points[i] = origin + radius * rotated | ||
|
||
angles = np.linspace(0, angle, num_points) | ||
arc_points = np.zeros((num_points, 3)) | ||
ax.plot(arc_points[:, 0], arc_points[:, 1], arc_points[:, 2], **kwargs) | ||
|
||
def plot_vector(fig, pos, vector, color='blue', name='vector', show_arrow=True, arrow_size = 0.2): | ||
"""Plot vector with plotly. | ||
General method to plot a vector with arrow using plotly. | ||
Parameters | ||
---------- | ||
fig : matplotlib.figure.Figure | ||
Matplotlib figure. | ||
pos : array_like | ||
Position of the vector. | ||
vector : array_like | ||
Vector to plot. | ||
color : str, optional | ||
Color of the vector, by default 'blue' | ||
name : str, optional | ||
Name of the vector, by default 'vector' | ||
show_arrow : bool, optional | ||
Show or not the arrow, by default True | ||
arrow_size : float, optional | ||
Vector's arrow size, by default 0.2 | ||
""" | ||
|
||
### Coordiantes of the two points defining the vector | ||
start = pos | ||
end = pos + vector | ||
|
||
### Build unitary vector | ||
vector_unit = vector / np.linalg.norm(vector) | ||
|
||
### Plot the segment between the two points | ||
fig.add_trace(go.Scatter3d( | ||
x=[start[0], end[0]], | ||
y=[start[1], end[1]], | ||
z=[start[2], end[2]], | ||
mode='lines', | ||
line=dict(color=color, width=2), | ||
name=name, | ||
text=[name, name], | ||
hovertemplate=( | ||
"<b>%{text}</b><br>" | ||
"X: %{x:.2f}<br>" | ||
"Y: %{y:.2f}<br>" | ||
"Z: %{z:.2f}<extra></extra>" | ||
) | ||
)) | ||
|
||
### Plot the arrowhead | ||
if show_arrow: | ||
if vector_unit[0] != 0 or vector_unit[1] != 0: | ||
# General case: construct perpendicular vectors | ||
ortho1 = np.cross(vector_unit, [0, 0, 1]) | ||
else: | ||
# Special case: vector is along z-axis | ||
ortho1 = np.cross(vector_unit, [1, 0, 0]) | ||
|
||
for i, theta in enumerate(angles): | ||
rotated = v1_norm * np.cos(theta) + \ | ||
np.cross(normal, v1_norm) * np.sin(theta) + \ | ||
normal * np.dot(normal, v1_norm) * (1 - np.cos(theta)) | ||
arc_points[i] = origin + radius * rotated | ||
|
||
ax.plot(arc_points[:, 0], arc_points[:, 1], arc_points[:, 2], **kwargs) | ||
ortho1 /= np.linalg.norm(ortho1) | ||
# Compute the second orthogonal vector | ||
ortho2 = np.cross(vector_unit, ortho1) | ||
ortho2 /= np.linalg.norm(ortho2) | ||
# Base of the arrowhead | ||
tip_base = np.array(end) - arrow_size * vector_unit | ||
|
||
# Compute the points for the arrowhead | ||
point1 = tip_base + arrow_size * 0.5 * ortho1 | ||
point2 = tip_base - arrow_size * 0.5 * ortho1 | ||
|
||
# Add the arrowhead segments | ||
for point in [point1, point2]: | ||
fig.add_trace(go.Scatter3d( | ||
x=[end[0], point[0]], | ||
y=[end[1], point[1]], | ||
z=[end[2], point[2]], | ||
mode='lines', | ||
line=dict(color=color, width=5), | ||
showlegend=False, | ||
hovertemplate=( | ||
"X: %{x:.2f}<br>" | ||
"Y: %{y:.2f}<br>" | ||
"Z: %{z:.2f}<extra></extra>" | ||
) | ||
)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import numpy as np | ||
from scipy.spatial.transform import Rotation as R | ||
|
||
def _compute_dot_product(v1, v2): | ||
v1_normalized = v1 / np.linalg.norm(v1, axis=0) | ||
v2_normalized = v2 / np.linalg.norm(v2, axis=0) | ||
dot_product = np.sum(v1_normalized * v2_normalized, axis=0) | ||
|
||
return dot_product | ||
|
||
def _compute_cross_product(v1, V2): | ||
v1_normalized = v1 / np.linalg.norm(v1, axis=0) | ||
v2_normalized = V2 / np.linalg.norm(V2, axis=0) | ||
cross_product = np.cross(v2_normalized.T, v1_normalized.T).T | ||
|
||
return cross_product | ||
|
||
def compute_angle(v1, v2): | ||
|
||
dot_product = _compute_dot_product(v1, v2) | ||
cross_product = _compute_cross_product(v1, v2) | ||
|
||
angle = np.arctan2(cross_product, dot_product) | ||
return angle | ||
|
||
def compute_rotation(v1, v2): | ||
"""Rotation. | ||
Compute the rotation instance from Spipy.spatial.transform.Rotation, that transforms v1 to v2. | ||
Parameters | ||
---------- | ||
v1 : array_like | ||
Fisrt vector. | ||
v2 : array_like | ||
Second vector. | ||
Returns | ||
------- | ||
rotationon_instance : Rotation | ||
Rotation instance from Spipy.spatial.transform.Rotation. | ||
""" | ||
|
||
cross_product = _compute_cross_product(v1, v2) | ||
|
||
### Define the rotation axis and angle between the vectors | ||
rotation_axis = cross_product / np.linalg.norm(cross_product, axis=0) | ||
angle = compute_angle(v1, v2) | ||
|
||
### Build the scipy Rotation instance | ||
print(angle.shape) | ||
print(rotation_axis.shape) | ||
print((angle * rotation_axis).shape ) | ||
rotation_instance = R.from_rotvec((angle * rotation_axis).T) | ||
|
||
return rotation_instance | ||
|
||
def apply_rotation(v, rotation_instance): | ||
"""Apply rotation. | ||
Apply the rotation instance to the vector v. | ||
Parameters | ||
---------- | ||
v : array_like | ||
Vector to rotate. | ||
rotation_instance : Rotation | ||
Rotation instance from Spipy.spatial.transform.Rotation. | ||
Returns | ||
------- | ||
rotated_vector : array_like | ||
Rotated vector. | ||
""" | ||
|
||
### Rotate the vector using the rotation instance | ||
rotated_vector = rotation_instance.apply(v) | ||
return rotated_vector.T |