22import pandas as pd
33
44from .planar_geometry import PlanarGeometry
5- from .utils import rotation_matrix
5+ from .utils import rotation_matrix , rotation_matrices
66
77
88class SheetGeometry (PlanarGeometry ):
@@ -186,8 +186,8 @@ def face_rotation(sheet, face, psi=0):
186186 else :
187187 return np .dot (rotation_matrix (psi , [0 , 0 , 1 ]), r1 )
188188
189- @classmethod
190- def face_projected_pos (cls , sheet , face , psi = 0 ):
189+ @staticmethod
190+ def face_projected_pos (sheet , face , psi = 0 ):
191191 """Returns the position of a face vertices projected on a plane
192192 perpendicular to the face normal, and translated so that the face
193193 center is at the center of the coordinate system
@@ -216,7 +216,7 @@ def face_projected_pos(cls, sheet, face, psi=0):
216216 sheet .vert_df .loc [face_orbit .values , sheet .coords ].values
217217 - sheet .face_df .loc [face , sheet .coords ].values
218218 )
219- u , s , rotation = np .linalg .svd (rel_pos .astype (np .float ), full_matrices = False )
219+ _ , _ , rotation = np .linalg .svd (rel_pos .astype (np .float ), full_matrices = False )
220220 # rotation = cls.face_rotation(sheet, face, psi=psi)
221221 if psi != 0 :
222222 rotation = np .dot (rotation_matrix (psi , [0 , 0 , 1 ]), rotation )
@@ -225,6 +225,49 @@ def face_projected_pos(cls, sheet, face, psi=0):
225225 )
226226 return rot_pos
227227
228+ @staticmethod
229+ def face_rotations (sheet ):
230+ """Returns the (sheet.Ne, 3, 3) array of rotation matrices
231+ such that each rotation aligns the coordinate system along face normals
232+
233+ """
234+ normals = sheet .edge_df .groupby ("face" )[sheet .ncoords ].mean ()
235+ normals = normals / np .linalg .norm (normals , axis = 0 )
236+ normals = sheet .upcast_face (normals )
237+
238+ n_xy = np .linalg .norm (normals [["nx" , "ny" ]])
239+ theta = - np .arctan2 (n_xy , normals .nz )
240+
241+ direction = np .array (
242+ [normals .ny .to_numpy (), - normals .nx .to_numpy (), np .zeros (sheet .Ne )]
243+ ).T
244+ rots = rotation_matrices (theta , direction )
245+
246+ return rots
247+
248+ @classmethod
249+ def get_phis (cls , sheet ):
250+ """Returns the 'latitude' of the vertices in the plane perpendicular
251+ to each face normal. For not-too-deformed faces, sorting vertices by this
252+ gives clockwize orientation.
253+
254+ I think not-too-deformed means starconvex here.
255+
256+ """
257+ rots = cls .face_rotations (sheet )
258+
259+ rel_srce_pos = (
260+ sheet .edge_df [["sx" , "sy" , "sz" ]]
261+ - sheet .edge_df [["fx" , "fy" , "fz" ]].to_numpy ()
262+ ).to_numpy ()
263+ rotated = np .einsum ("ikj, ik -> ij" , rots , rel_srce_pos )
264+ return np .arctan2 (rotated [:, 1 ], rotated [:, 0 ])
265+
266+ @classmethod
267+ def sort_oriented_edges (cls , sheet ):
268+ phis = cls .get_phis (sheet )
269+ sheet .edge_df = sheet .edge_df .sort_values (["face" , phis ]).reset_index ()
270+
228271
229272class ClosedSheetGeometry (SheetGeometry ):
230273 """Geometry for a closed 2.5D sheet.
0 commit comments