1

I'm currently rotating xy points using the angle between a reference point and a single fixed point. I'm hoping to include two fixed points to rotate the points. I'm just not sure how this can be achieved.

Using below, the points that get rotated are displayed in x and y. The angle between X_Ref, Y_Ref and X_Fixed,Y_Fixed is used to rotate the points (displayed as the black vector in the initial distribution figure).

The current process is to transform the fixed/ref points to complex numbers to compute the rotation. I finally re-center the rotated points so X_Ref, Y_Ref is centred at 0,0 (shown in the after rotation figure).

However, I'm hoping to include the angle between X_Ref, Y_Ref and X2_Fixed,Y2_Fixed. The second figure below shows how only one angle is accounted for when rotating the points. I'm hoping to account for both to return the intended out. Any suggestions/advice is welcome.

df = pd.DataFrame({  
    'Period' : ['1','1','1','1'],        
    'Label' : ['A','B','C','D'],                             
    'x' : [0.0,-1.0,3.0,2.0],
    'y' : [0.0,-1.0,-1.0,0.0],     
    'X_Ref' : [1,1,1,1],
    'Y_Ref' : [1,1,1,1],        
    'X_Fixed' : [-2,-2,-2,-2],
    'Y_Fixed' : [-2,-2,-2,-2],      
    'X2_Fixed' : [4,4,4,4],
    'Y2_Fixed' : [-2,-2,-2,-2],           
    })

fig, ax = plt.subplots(figsize = (6,6))
ax.set_xlim(-5,5)
ax.set_ylim(-5,5)
ax.grid(False)

Initial distribution:

enter image description here

#transform fixed/ref points to complex numbers
for f in ['Ref', 'Fixed']:
    df[f] = df['X_'+f] + 1j*df['Y_'+f]
    df.drop(['X_'+f, 'Y_'+f], axis=1, inplace=True)

#compute the rotation  
df['angle'] = - np.angle(df['Ref'] - df['Fixed'])

#compute the rotation for every point 
df['rotated'] = (df['x'] + 1j*df["y"]) * np.exp(1j*df['angle'])
for f in ['Ref', 'Fixed']:
    df[f+'_Rotated'] = df[f] * np.exp(1j*df['angle'])

#center the dataset around the "reference" point 
df['translation'] = - df['Ref_Rotated']
df['NewPoint'] = df['rotated'] + df['translation']
for f in ['Ref', 'Fixed']:
    df[f+'_Transformed'] = df[f+'_Rotated'] + df['translation']

#revert to cartesian coordinates
df['x2'] = np.real(df['NewPoint'])
df['y2'] = np.imag(df['NewPoint'])
for f in ['Ref', 'Fixed']:
    df['NewX_'+f] = np.real(df[f+'_Transformed'])
    df['NewY_'+f] = np.imag(df[f+'_Transformed'])

After Rotation:

output = df[['Label', 'x2', 'y2', 'NewX_Ref', 'NewY_Ref', 'NewX_Fixed', 'NewY_Fixed']]

ax.scatter(output['NewX_Ref'], output['NewY_Ref'], marker = 'x', zorder = 5, color = 'black')
ax.scatter(output['NewX_Fixed'], output['NewY_Fixed'], marker = '+', zorder = 5, color = 'red')
ax.scatter(output['x2'], output['y2'], marker = 'o')

enter image description here

Intended rotation:

enter image description here

8
  • 1
    Wouldn't matrix transformations be helpful here? Commented Mar 22, 2021 at 1:33
  • I'm confused -- you have vectors at a 90-degree angle. You want to rotate the entire figure, and end up with opposite vectors? Exactly how does that make sense in your system? Commented Mar 22, 2021 at 1:45
  • Please note that your code is not minimal: there is a large amount of graphing overhead that is mere visualization, not related to the problem. Please replace it with a simple output, such as the direction of each vector. Commented Mar 22, 2021 at 1:46
  • 2
    Please explain independently what you are trying to do; very few people are going to trouble to reverse-engineer your intent from you code. You have a "fixed" vector that is not fixed. You say that you're using the angle between them to rotate the vectors, but you say nothing about the rotation algorithm. Please repeat how to ask from the intro tour. Commented Mar 22, 2021 at 20:45
  • 1
    Defining an angle between 2 points does not make much sense. An angle can only be defined between 2 vectors, or 3 points. In polar coordinates, an angle can be expressed for a single point, but this angle is implicitly understood to be with respect to a reference direction (often the x-axis) and a reference point (the origin). According to - np.angle(df['Ref'] - df['Fixed']), you actually consider Ref and Fixed as vectors and thus compute the angle of the vector Ref - Fixed (with respect to the x-axis and the origin and with clockwise orientation). I guess that's not what you want. Commented Aug 10, 2021 at 18:05

1 Answer 1

3
+25

The problem to be solved is not clear. From what I understand the problem is to make a point rotate around a pivot point with a certain angle theta. The dataframes of pandas are not very suited to the problem, let's use numpy instead. For convenience we first define a class to represent points and vectors in the plane.

class Coord:
    def __init__(self, x, y):
        x, y = np.array(x), np.array(y)
        assert x.shape == y.shape
        self.x, self.y = x, y
        self.shape = x.shape

    @property
    def radius(self):
        return np.sqrt(self.x ** 2 + self.y ** 2)

    def __sub__(self, other):
        return Coord(self.x - other.x, self.y - other.y)

    def __add__(self, other):
        return Coord(self.x + other.x, self.y + other.y)

    def __repr__(self):
        return f"Coord({self.x}, {self.y})"

For instance Coord(2, 3) simply represents a point or a vector with coordinates (2, 3). Additions and subtractions can be performed: Coord(2, 3) + Coord(1, -2) returns Coord(3, 1). Note x and y attributes can be numpy arrays. In this case a Coord instance could be though as a set of points. For example Coord([1, 2, 0],[2, -1, 3]) represents the points/vectors (1, 2), (2, -1) and (0, 3).

Rotation around the origin

Let's add a method _rotate_from_origin to the class Coord that outputs the point after rotation. You can find here an explanation of how to rotate a point.

    def _rotate_from_origin(self, theta):
        return Coord(
            self.x * np.cos(theta) - self.y * np.sin(theta),
            self.x * np.sin(theta) + self.y * np.cos(theta))

Rotation around a given pivot

It seems that you defined the coordinates x_ref and y_ref, with the idea of performing the rotation not around the origin but around this reference point. We can simply achieve this using the following method:

    def rotate(self, theta, pivot=None):
        if not pivot:
            pivot = Coord(0, 0)
        return (self - pivot)._rotate_from_origin(theta) + pivot

That's it! Now you can use this method for any point or set of points.

Example

Let's try on your example (or at least on what I understood of it).

startA = Coord([0, -1], [ 0, -1])  # points that will be rotated with an angle of thetaA
startB = Coord([3,  2], [-1,  0])  # will be rotated with an angle of thetaB
pivot = Coord(1, 1)
thetaA = -np.pi / 4
thetaB = np.pi / 4

## Rotate each set of points with the corresponding angle
rotatedA = startA.rotate(thetaA, pivot=pivot)
rotatedB = startB.rotate(thetaB, pivot=pivot)

## Display the result
plt.plot(pivot.x, pivot.y, 'ko')
plt.plot(startA.x, startA.y, 'o', c='gold')
plt.plot(rotatedA.x, rotatedA.y, 'o', c='gold')
plt.plot(startB.x, startB.y, 'o', c='lightblue')
plt.plot(rotatedB.x, rotatedB.y, 'o', c='royalblue')

results

Note that here theta parameter is a single integer. In this case each points will be rotated using the same theta value. But several values of thetas can be used to make each point rotate differently. For instance we could have done:

starts = Coord([0, -1, 3, 2], [0, -1, -1, 0])
pivot = Coord(1, 1)
thetas = (np.pi / 4) * np.array([-1, -1, 1, 1])

rotated = starts.rotate(thetas, pivot=pivot)

And the same applies to the pivot parameter. Basically this is the expected behavior when using functions with numpy.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.