Having issues while getting the same horizontal range for angles 30 and 60 degees in projectile motion

I am trying to animate a simple Projectile motion. In this type of motion, the value of the horizontal range (R = v^2*sin(2 * theta)/g) is the same for angles 30 and 60 degrees. Although I was able to display this value in a dynamic text annotation in my code, I am not getting the same value in the plot title. I believe there is an issue with how I am updating the value of x in the title, but I am unable to figure it out.
I have attached the code and the figure that I received. Can anyone please assist me with this issue?

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
from math import *


fig = plt.figure(figsize=(10, 6))
ax = fig.add_subplot()

# Constants
g = 10
v = 40 # velocity of projection

# Function to animate projectile motion for given angles
def animate(angles):
    ax.clear()

    # Calculating time of flight, range, and maximum height for each angle
    tms = [(2*v*sin(np.deg2rad(angle))/g) for angle in angles]
    Rs = [v**2*sin(2*np.deg2rad(angle))/g for angle in angles]
    Hms = [(v*sin(np.deg2rad(angle)))**2/(2*g) for angle in angles]
    # Initializing lists to store x and y points for each angle
    x_points = [[] for _ in range(len(angles))]
    y_points = [[] for _ in range(len(angles))]

    # Creating lines for each angle
    lines = [ax.plot([], [], color='C'+str(i), ls='-', lw=1, label=f'Angle of projection = {angle}°')[0] for i, angle in enumerate(angles)]

    # Create markers for each angle
    markers = [ax.scatter([], [], color='C'+str(i), edgecolor='black', alpha=1) for i in range(len(angles))]

    # Creating vertical lines to indicate x position
    vertical_lines = [ax.plot([], [], color='black', ls='--', lw=0.5, alpha=0.5)[0] for _ in range(len(angles))]

    # Creating horizontal lines to indicate y position
    horizontal_lines = [ax.plot([], [], color='black', ls='--', lw=0.5, alpha=0.5)[0] for _ in range(len(angles))]

    # Creating text annotations for x and y positions
    texts1 = [ax.text(0, 0, '', ha='left', va='bottom', fontsize=8) for _ in range(len(angles))]
    texts2 = [ax.text(0, 0, '', ha='center', va='bottom', fontsize=8) for _ in range(len(angles))]


    def update(t):
        for i, angle in enumerate(angles):
            if t <= tms[i]:
                x = v * cos(np.deg2rad(angle)) * t
                y = v * sin(np.deg2rad(angle)) * t - 0.5 * g * t**2

                x_points[i].append(x)
                y_points[i].append(y)
                lines[i].set_data(x_points[i], y_points[i])
                markers[i].set_offsets([[x, y]])
                vertical_lines[i].set_data([x, x], [0, y])
                horizontal_lines[i].set_data([0, x], [y, y])

                texts1[i].set_text(f'y = {y:.2f}m')
                texts1[i].set_position((x+1, y+0.5))
                texts2[i].set_text(f'x = {x:.2f}m')
                texts2[i].set_position((x/2, y))

            else:
                x = v * cos(np.deg2rad(angle)) * tms[i]
                y = v * sin(np.deg2rad(angle)) * tms[i] - 0.5 * g * tms[i]**2

                lines[i].set_data(x_points[i] + [x], y_points[i] + [y])
                markers[i].set_offsets([[x, y]])
                markers[i].set_offsets([[x, y]])
                vertical_lines[i].set_data([x, x], [0, y])
                horizontal_lines[i].set_data([0, x], [y, y])

                texts1[i].set_text(f'y = {y:.2f}m')
                texts1[i].set_position((x+1, y+0.5))
                texts2[i].set_text(f'x = {x:.2f}m')
                texts2[i].set_position((x/2, y))

        ax.set_xlabel('x')
        ax.set_ylabel('y')
        ax.set_title(', '.join([f'R_{angle}= {x:.2f}m, T_{angle}= {t if t <= tm else tm:.2f}s'
                                for angle, x, tm in zip(angles, [x_points[i][-1] for i in range(len(angles))], tms)]),
                     fontsize=8)
        ax.set_xlim(0, max(Rs) + 5)
        ax.set_ylim(-5, max(Hms) + 5)
        ax.legend(loc='upper right', fontsize=7)
        return lines + markers + vertical_lines + horizontal_lines + texts1 + texts2

    anim = FuncAnimation(fig, update, frames=np.linspace(0, max(tms), 50), interval=100, repeat=True)
    return anim

fig.suptitle(f'Projectile Motion of particles projected with velocity {v}m/s')

# List of angles for which projectile motion will be animated
angles = [15, 30, 60]
anim = animate(angles)
plt.show()

projectile_prob

in the plot below, we can see the value of x at the end of the animation in texts2[i].set_position((x/2, y)) is the same, which ultimately indicates the horizontal range, but I am not getting this same value for the angles 30 and 60 degrees in the title

 ax.set_title(', '.join([f'R_{angle}= {x:.2f}m, T_{angle}= {t if t <= tm else tm:.2f}s' for angle, x, tm in zip(angles, [x_points[i][-1] for i in range(len(angles))], tms)]), fontsize=8)

how do I fix this issue?

Hi,

I think that you already have the answer. Per the range formula that you provided:

(R = v^2*sin(2 * theta)/g) 

You have three constants and one unknown:

  1. Range - this is given since this is the range that you want
  2. g - this is a constant
  3. Angle - this you determine as the angle that you wish to shoot the projectile beforehand

The only unknown is the velocity. Perform a little algebra to solve for v, and you will have your
answer as to the velocity to shoot the projectile.

One small note. Since the angle is smaller than the other two (15 deg < 30 deg < 60 deg), the velocity should be greater than the other two since it has two reach its target distance R before -g takes its toll on the projectile. So, v1 > v2 > v3, where v1 is the velocity for the projectile at 15 degrees.

You aren’t adding the last point of the arc (i.e. what you are calculating in the else branch inside the loop in update) to the x_points/y_points lists. This means that what is displayed at the end of the antimation is not necessarily accurate, depending on how close the point that is being sampled is to actual end point of the arc.

2 Likes

Yeah right! Thanks for pointing that out. Now it is working perfectly.