Search code examples
python-3.xrotationalignmentcoordinatescomputational-geometry

How to make code parametric to make geometry aligned in Python?


I am working on the task in which I have to align the geometry if that geometry is rotated at some angle. Otherwise rotation should not be done. (Basically I want to make rotation 0 such that geometry's principal axes are parallel to reference system axis X Y.)

enter image description here

I have written the code which makes rotated geometry without rotation. However, I want to make my code very parametric such that, if I will be given two sets of coordinates (X and Y), my code should make my geometry without rotation when there is any rotation. Otherwise, that process should not be done.

Let me give you detailed explanation.

I have two sets of coordinates like below,

X2_coordinate = [0.0, 0.87, 1.37, 1.87, 2.73, 3.6, 4.46, 4.96, 5.46, 4.6, 3.73, 2.87, 2.0, 1.5, 1.0, 0.5, 2.37, 3.23, 4.1]
Y2_coordinate = [0.0, 0.5, -0.37, -1.23, -0.73, -0.23, 0.27, -0.6, -1.46, -1.96, -2.46, -2.96, -3.46, -2.6, -1.73, -0.87, -2.1, -1.6, -1.1]

The code I have to make my geometry without rotation:

from sbNative.debugtools import log, cleanRepr
from numpy import arctan, degrees
import math


def rotate(origin, point, angle):
    """
    https://stackoverflow.com/questions/34372480/rotate-point-about-another-point-in-degrees-python
    Rotate a point counterclockwise by a given angle around a given origin.

    The angle should be given in radians.
    """

    qx = origin.x + math.cos(angle) * (point.x - origin.x) - math.sin(angle) * (point.y - origin.y)
    qy = origin.y + math.sin(angle) * (point.x - origin.x) + math.cos(angle) * (point.y - origin.y)
    return qx, qy
    
@cleanRepr()
class Point:
    def __init__(self, *pts):
        pt_names = "xyzw"
        for n, v in zip(pt_names[:len(pts)], pts):
            self.__setattr__(n, v)


coordinates = list(zip(X2_coordinate, Y2_coordinate))

points = [Point(*coors) for coors in coordinates]
point_pairs = [(points[i], points[i + 1]) for i in range(len(points) - 1)] + \
              [(points[-1], points[0])]

angles = [abs(arctan((p1.x - p2.x) / (p1.y - p2.y))) for p1, p2 in point_pairs]

angle_amounts = {}
for a in set(angles):
    angle_amounts[a] = angles.count(a)

final_rotation_angle = max(angle_amounts, key=angle_amounts.get)

new_points = [rotate(Point(0, 0), p2, final_rotation_angle) for p2 in points]

log(new_points)

X2_coordinate_aligned, Y2_coordinate_aligned = zip(*new_points)

X2_coordinate_aligned = [round(num,1) for num in X2_coordinate_aligned] 
Y2_coordinate_aligned = [round(num,1) for num in Y2_coordinate_aligned] 

df_2['X2_coordinate_aligned'] = X2_coordinate_aligned
df_2['Y2_coordinate_aligned'] = Y2_coordinate_aligned

plt.scatter(X2_coordinate_aligned, Y2_coordinate_aligned)
plt.show()

Above code runs and it made the geometry without rotation as below. enter image description here

Now I have different set of cooridnates as mentioned below. Well this geometry does not have rotation as it can be seen in the picture below.

Given sets of cooridnates

X2_coordinate = [0, 0, 1, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 3, 2, 1, 3, 3, 3]
Y2_coordinate = [0, 1, 1, 1, 2, 3, 4, 4, 4, 3, 2, 1, 0, 0, 0, 0, 1, 2, 3]

enter image description here

When I run the above code (that just mentioned above to make alignment), it gives error like, division by zero.

enter image description here

The purpose is: I want to make my code parametric, such that if there is rotated geometry, code should make geometry without rotation and if there is no rotation, code should do nothing and should give same coordinates.

Kindly let me know what should I do to make my code parametric and to remove the error division by zero.


Solution

  • Use the function atan2 instead of arctan.