Search code examples
pythonpython-3.xmathcurve

Moving single point from curve knowing distance


I have a set of x and y points (whatever the function behind) here in black. I would like to move the (x0, y0) point to the (x1,y1) knowing that there is 3 cm (whatever the unit) from (x0, y0) to (x1,y1) at 90° angle.

1

I would like to do it in Python, however obviously this is pretty bad.

fig = plt.figure()
from mpl_toolkits.mplot3d import Axes3D
ax = fig.add_subplot(111)

c = [55,  53, 54]
d = [29,  27,  27]

c = [55,  53 + 3, 54]
dd = [29,  27 + 3,  27 ]

ax.plot(c,d,'-o', c='g')
ax.plot(c,dd,'-o', c='b')

Partial final answer translated into Python (Thanks to picobit), however I would like to make the picobit function "orientation sensitive" :

fig = plt.figure()
from mpl_toolkits.mplot3d import Axes3D
ax = fig.add_subplot(111)
    
    
a = [0.22520001, 0.22140153, 0.21732369, 0.21258711, 0.20764232, 0.20515779,
0.20449048, 0.20467589, 0.20534733]
b = [0.21270538 ,0.21026637, 0.20749939, 0.20383899, 0.19925433, 0.19559762,
0.19440357, 0.19375025, 0.19344115]


dev = [0.0009969 , 0.00143304, 0.00174457, 0.00193148, 0.00199379, 0.00186918,
0.00149534, 0.00087228, 0.        ]

import math

def rotate_vector(x0, y0, angle, dev):
    magnitude = math.sqrt(x0**2 + y0**2)
    xhat = x0/magnitude
    yhat = y0/magnitude
    x_rot = -yhat * math.sin(angle) + xhat * math.cos(angle)
    y_rot = yhat * math.cos(angle) + xhat * math.sin(angle)
    x_rot = x_rot * dev
    y_rot = y_rot * dev

    anglee = 90 # Obviously different if 0 or 45, etc...

    x_rot = (x_rot * math.cos(np.radians(anglee))) - (y_rot * math.sin(np.radians(anglee)))
    y_rot = (x_rot * math.sin(np.radians(anglee))) + (y_rot * math.cos(np.radians(anglee))) 

    x_final = x_rot + x0
    y_final = y_rot + y0
    return x_final, y_final

Solution

  • Prerequisites:

    Your algorithm is:

    1. Find the unit vector that points from the origin to (x0,y0)
    2. Multiply the unit vector by the rotation matrix
    3. Multiply this new vector by your desired length (3cm in your example)
    4. Move the newly-scaled vector's starting point back to (x0,y0)

    Step 1: Let A be the vector between the origin and (x0,y0). We need to find |A|, magnitude of A, (aka the length of the line segment) using the Pythagorean Theorem.

    Find the unit vector by dividing (x0,y0) by |A|, giving us (x0/|A|,y0/|A|). This is the unit vector along A. Prove it to yourself by drawing a little, tiny right triangle with one end of the hypotenuse at the origin and the other end at (x0/|A|,y0/|A|).

    Just to make things easier to type, let xhat=x0/|A| and yhat=y0/|A|.

    Step 2:
    You can rotate the unit vector by any angle θ by multiplying by the rotation matrix. After shaking out the matrix multiplication, you get the new point (xhat',yhat') where

    xhat' = xhat*Cosθ - yhat*Sinθ  
    yhat' = xhat*Sinθ + yhat*Cosθ
    

    90 degrees is a friendly angle, so this simplifies to:

    xhat' = -yhat
    yhat' = xhat
    

    Step 3:
    Scale the new vector by 3 units (centimeters in your example):

    3*(-yhat,xhat) = (-3*yhat,3*xhat)
    

    Step 4:
    Move this new vector's starting point back to (x0,y0)

    (x1,y1) = (-3*yhat,3*xhat) + (x0,y0) 
            = (-3*yhat+x0,3*xhat+y0)
    

    Those are the coordinates for your new point.

    As a quick example, if you have the point (3,4), and you want to perform the same translation as in your example image, then you'd do this:

    1. |A| = 5, so (xhat, yhat) = (3/5, 4/5)
    2. (xhat', yhat') = (-4/5, 3/5)
    3. 3*(-4/5, 3/5) = (-12/5, 9/5)
    4. (-12/5+3, 9/5+4) = (0.6, 5.8)

    Now prove to yourself that the two points are 3 units apart, again using the Pythagorean Theorem. A right triangle with hypotenuse connecting the two points (3,4) and (0.6,5.8) has sides with lengths (3-0.6) and (5.8-3)

    enter image description here