-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from jennyfothergill/feat/order_parameter
Add utilities to calculate Order Parameter
- Loading branch information
Showing
10 changed files
with
495 additions
and
27 deletions.
There are no files selected for viewing
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,87 @@ | ||
import numpy as np | ||
from numpy.linalg import svd | ||
|
||
|
||
def get_plane_normal(points): | ||
"""Calculate the plane which best fits a cloud of points. | ||
Best fit is calculated using numpy's Singular Value Decomposition. | ||
Example | ||
------- | ||
To visualize the plane fit in a Jupyter notebook:: | ||
%matplotlib notebook | ||
import matplotlib.pyplot as plt | ||
import numpy as np | ||
normal, ctr = get_plane_normal(points) | ||
plt.figure() | ||
ax = plt.subplot(111, projection='3d') | ||
ax.scatter(points[:,0], points[:,1], points[:,2], color='b') | ||
# plot plane | ||
xlim = ax.get_xlim() | ||
ylim = ax.get_ylim() | ||
xx, yy = np.meshgrid( | ||
np.linspace(xlim[0], xlim[1], 3), np.linspace(ylim[0], ylim[1], 3) | ||
) | ||
d = -ctr.dot(normal) | ||
ax.scatter(ctr[0], ctr[1], ctr[2], color='r') | ||
z = (-normal[0] * xx - normal[1] * yy - d) * 1 / normal[2] | ||
ax.plot_surface(xx, yy, z, alpha=0.5) | ||
ax.set_box_aspect(aspect = (1,1,1)) | ||
ax.set_xlabel('x') | ||
ax.set_ylabel('y') | ||
ax.set_zlabel('z') | ||
plt.show() | ||
Parameters | ||
---------- | ||
points : numpy.ndarray, shape (N,3) | ||
Coordinates (x,y,z) through which to fit a plane. Must be at least 3 | ||
points. | ||
Returns | ||
------- | ||
ctr : numpy.ndarray, shape (3,) | ||
The center of the point cloud. | ||
normal : numpy.ndarray, shape (3,) | ||
The plane normal vector. A unit vector from the origin. | ||
""" | ||
assert points.shape[0] >= 3, "Need at least 3 points to calculate a plane." | ||
ctr = points.mean(axis=0) | ||
shiftpoints = points - ctr | ||
U, S, Vt = svd(shiftpoints.T @ shiftpoints) | ||
normal = U[:, -1] | ||
return ctr, normal | ||
|
||
|
||
def angle_between_vectors(u, v, min_angle=True): | ||
"""Calculate the angle between two vectors in degrees. | ||
Parameters | ||
---------- | ||
u : np.ndarray, shape (3,) | ||
Vector | ||
v : np.ndarray, shape (3,) | ||
Vector | ||
min_angle : bool, default True | ||
Whether to return the supplement if the angle is greater than 90 | ||
degrees. Useful for calculating the minimum angle between the normal | ||
vectors of planes as direction doesn't matter. | ||
Returns | ||
------- | ||
angle: float | ||
Angle between u and v in degrees | ||
""" | ||
angle = np.rad2deg( | ||
np.arccos(u.dot(v) / (np.linalg.norm(u) * np.linalg.norm(v))) | ||
) | ||
if angle > 90 and min_angle: | ||
return 180 - angle | ||
return angle |
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
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
Oops, something went wrong.