|
|
import streamlit as st |
|
|
import numpy as np |
|
|
import matplotlib.pyplot as plt |
|
|
import matplotlib.patches as patches |
|
|
|
|
|
def calculate_resultant_force(forces): |
|
|
""" |
|
|
Calculate the resultant force vector from multiple forces. |
|
|
|
|
|
Args: |
|
|
forces (list): List of tuples (magnitude, angle in degrees) |
|
|
|
|
|
Returns: |
|
|
tuple: Resultant force magnitude and angle |
|
|
""" |
|
|
|
|
|
fx = sum(force[0] * np.cos(np.radians(force[1])) for force in forces) |
|
|
fy = sum(force[0] * np.sin(np.radians(force[1])) for force in forces) |
|
|
|
|
|
|
|
|
resultant_magnitude = np.sqrt(fx**2 + fy**2) |
|
|
resultant_angle = np.degrees(np.arctan2(fy, fx)) |
|
|
|
|
|
return resultant_magnitude, resultant_angle |
|
|
|
|
|
def draw_force_diagram(forces): |
|
|
""" |
|
|
Create a force diagram using matplotlib. |
|
|
|
|
|
Args: |
|
|
forces (list): List of tuples (magnitude, angle in degrees) |
|
|
|
|
|
Returns: |
|
|
matplotlib figure |
|
|
""" |
|
|
fig, ax = plt.subplots(figsize=(8, 8)) |
|
|
ax.set_xlim(-10, 10) |
|
|
ax.set_ylim(-10, 10) |
|
|
ax.grid(True, linestyle='--', alpha=0.7) |
|
|
ax.set_title('Force Diagram') |
|
|
ax.set_xlabel('X-axis') |
|
|
ax.set_ylabel('Y-axis') |
|
|
|
|
|
|
|
|
ax.plot(0, 0, 'ro', markersize=10) |
|
|
|
|
|
|
|
|
colors = ['blue', 'green', 'red', 'purple', 'orange'] |
|
|
|
|
|
|
|
|
for i, (magnitude, angle) in enumerate(forces): |
|
|
color = colors[i % len(colors)] |
|
|
|
|
|
fx = magnitude * np.cos(np.radians(angle)) |
|
|
fy = magnitude * np.sin(np.radians(angle)) |
|
|
|
|
|
|
|
|
ax.arrow(0, 0, fx, fy, head_width=0.3, head_length=0.5, |
|
|
fc=color, ec=color, alpha=0.7, |
|
|
label=f'Force {i+1}: {magnitude} N at {angle}°') |
|
|
|
|
|
|
|
|
resultant_magnitude, resultant_angle = calculate_resultant_force(forces) |
|
|
ax.arrow(0, 0, |
|
|
resultant_magnitude * np.cos(np.radians(resultant_angle)), |
|
|
resultant_magnitude * np.sin(np.radians(resultant_angle)), |
|
|
head_width=0.5, head_length=0.8, |
|
|
fc='black', ec='black', alpha=1, |
|
|
label=f'Resultant: {resultant_magnitude:.2f} N at {resultant_angle:.2f}°') |
|
|
|
|
|
ax.legend() |
|
|
return fig |
|
|
|
|
|
def main(): |
|
|
st.title('Force Visualization Simulator') |
|
|
|
|
|
st.sidebar.header('Force Input') |
|
|
|
|
|
|
|
|
if 'forces' not in st.session_state: |
|
|
st.session_state.forces = [] |
|
|
|
|
|
|
|
|
magnitude = st.sidebar.number_input('Force Magnitude (N)', min_value=0.0, step=0.1, value=5.0) |
|
|
angle = st.sidebar.number_input('Force Angle (degrees)', min_value=0.0, max_value=360.0, step=1.0, value=0.0) |
|
|
|
|
|
|
|
|
if st.sidebar.button('Add Force'): |
|
|
st.session_state.forces.append((magnitude, angle)) |
|
|
|
|
|
|
|
|
if st.sidebar.button('Remove Last Force') and st.session_state.forces: |
|
|
st.session_state.forces.pop() |
|
|
|
|
|
|
|
|
if st.sidebar.button('Clear All Forces'): |
|
|
st.session_state.forces = [] |
|
|
|
|
|
|
|
|
st.sidebar.subheader('Current Forces:') |
|
|
for i, (mag, ang) in enumerate(st.session_state.forces, 1): |
|
|
st.sidebar.text(f'Force {i}: {mag} N at {ang}°') |
|
|
|
|
|
|
|
|
if st.session_state.forces: |
|
|
fig = draw_force_diagram(st.session_state.forces) |
|
|
st.pyplot(fig) |
|
|
|
|
|
|
|
|
resultant_magnitude, resultant_angle = calculate_resultant_force(st.session_state.forces) |
|
|
st.subheader('Resultant Force') |
|
|
st.write(f'Magnitude: {resultant_magnitude:.2f} N') |
|
|
st.write(f'Angle: {resultant_angle:.2f}°') |
|
|
else: |
|
|
st.info('Add forces to visualize the force diagram') |
|
|
|
|
|
|
|
|
st.sidebar.markdown(""" |
|
|
### How to Use |
|
|
1. Enter force magnitude and angle |
|
|
2. Click 'Add Force' to include in diagram |
|
|
3. Visualize vector forces and resultant |
|
|
|
|
|
### Force Visualization Concepts |
|
|
- **Magnitude**: Strength of the force (Newtons) |
|
|
- **Angle**: Direction of the force (0-360 degrees) |
|
|
- **Resultant Force**: Combined effect of all forces |
|
|
""") |
|
|
|
|
|
if __name__ == '__main__': |
|
|
main() |