Skip to content

Commit

Permalink
Add README.md to describe how to build API Documentation + add method…
Browse files Browse the repository at this point in the history
…s in plot_tools and vector_tools
  • Loading branch information
TomLaclavere committed Dec 6, 2024
1 parent 3e3d411 commit 4496a60
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 168 deletions.
18 changes: 0 additions & 18 deletions .vscode/c_cpp_properties.json

This file was deleted.

24 changes: 0 additions & 24 deletions .vscode/launch.json

This file was deleted.

79 changes: 0 additions & 79 deletions .vscode/settings.json

This file was deleted.

1 change: 1 addition & 0 deletions README.md
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
178 changes: 131 additions & 47 deletions src/plot_tools.py
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>"
)
))
78 changes: 78 additions & 0 deletions src/vector_tools.py
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

0 comments on commit 4496a60

Please sign in to comment.