Draw a line in Python with 2 points and 2 angles (it's a deformed beam)

429 Views Asked by At

I am trying to draw a line with 2 points and 2 angles corresponding to a deformed line previously straight (see the image below). Both the coordinates of the points and the angles come from functions. I have tried with matplotlib annotations (with angle3, but doesn't allow variables in AngleA and AngleB and doesn't permit opposite angles) and Bezier curves, but I can't find the way to specify the angles.

Frame made of the deformed lines that I want to draw:

Frame made of the deformed lines that I want to draw

1

There are 1 best solutions below

2
On

In the following I've assumed that the lines the OP is concerned with are beams loaded only in nodes, justifying the use of cubic splines.

The function displ below computes the displaced position of 31 points (possibly overdone) along the line in terms of the undeformed and deformed nodal positions and the nodal rotations, applying an optional scaling factor to the transversal displacements. It returns a (2, 31) array of displaced beam positions in the global coordinate system.

from numpy import linspace
_s = linspace(0, 1, 31) # non-dimensional abscissa
_s0, _s1 = (_s-1)**2*_s, (_s-1)*_s**2 # cubic splines for unit rotations

def displ(X0, Y0, X1, Y1,
          x0, y0, x1, y1,
          φ0, φ1,
          f=1.0,
          ξ=_s, ξ0=_s0, ξ1=_s1):
    
    from numpy import arctan2 as atan2, array, cos, sin, sqrt

    # rigid slopes before and after
    Θ, θ = atan2(Y1-Y0, X1-X0), atan2(y1-y0, x1-x0)
    # rigid rotation (difference in slopes, after - before)
    δ = θ - Θ
    # end rotations not explained by the rigid rotation
    dφ0, dφ1 = φ0-δ, φ1-δ
    # transversal displacements, ξ0 and ξ1 are the proper cubic splines
    ν = dφ0*ξ0 + dφ1*ξ1
    # deformed length, sine and cosine for projection
    l = sqrt((x1-x0)**2+(y1-y0)**2)
    cs, sn = cos(θ), sin(θ)

    # u, axial displacement, u = l*ξ
    # v, transversal disp.t, v = l*ν
    # dx, dy = T @ {u,v}
    x0y0 = array((x0, y0), dtype=float)[:,None]
    return x0y0+l*array(((cs,-sn),(sn,cs)))@array((ξ, f*ν)) # shape(2,31)

To check the correctness of the implementation and show the usage I've written a short program using displ to show 4 arbitrarily deformed shapes.

import matplotlib.pyplot as plt
X0, Y0, x0, y0 = 0.0, 0.0, 0.0, 0.0
#             X1    Y1    x1    y1    f0    f1
args_list = [( 0,    5,  0.2,  4.8, 0.17, 0.12),
             ( 5,    0,  5.2, -0.2, -.14, 0.18),
             ( 5,    5,  5.2,  4.7, 0.18, -.22),
             ( 5,   -5,  4.7, -5.3, -.20, 0.00)]

fig, axes = plt.subplots(2, 2, constrained_layout=1)
for ax, (X1, Y1, x1, y1, f0, f1) in zip(axes.flatten(), args_list):
    ax.set_title('φ₀ = %+4.2f, φ₁ = %+4.2f'%(f0, f1))
    ax.plot((X0, X1), (Y0, Y1), alpha=0.35)
    ax.plot(*displ(X0, Y0, X1, Y1, x0, y0, x1, y1, f0, f1))
    ax.set_aspect(1)
    ax.grid(1)
plt.show()

4 deformed lines