codexkinetic-friction-constant-speed-force-balance.py
kinetic-friction-constant-speed-force-balance.py
"""Draw an original horizontal sliding force balance, not measured data.
Writes a matching-basename opaque white PNG to CWD. Tested with Python
3.14.4, Matplotlib 3.10.7 and NumPy 2.3.5. Force arrows share a length scale;
the dashed velocity arrow has no force scale. The block already slides.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
def main():
mass, gravity, mu = 2.0, 9.8, 0.30
normal = mass * gravity
sliding_force = mu * normal
fig, ax = plt.subplots(figsize=(8.6, 5.6), dpi=120)
fig.patch.set_facecolor("white")
ax.set_facecolor("white")
ax.set(xlim=(-3.5, 3.5), ylim=(-2.7, 2.7), aspect="equal")
ax.axis("off")
ax.add_patch(Rectangle((-0.8, -0.38), 1.6, 0.76,
facecolor="#f1e0c3", edgecolor="#604c30", lw=1.5))
ax.text(0, 0.15, "2 kg block", ha="center", fontsize=12)
ax.plot([-3.25, 3.25], [-0.4, -0.4], color="#767676", lw=2, zorder=0)
ax.text(2.05, -0.69, "stationary horizontal surface", ha="center", fontsize=10)
scale = 0.077
arrows = [((0, 0), (0, normal * scale), "#2166ac"),
((0, 0), (0, -normal * scale), "#2166ac"),
((0.8, 0), (0.8 + sliding_force * scale, 0), "#a43b31"),
((-0.8, 0), (-0.8 - sliding_force * scale, 0), "#a43b31")]
for start, end, color in arrows:
ax.annotate("", xy=end, xytext=start,
arrowprops=dict(arrowstyle="-|>", color=color, lw=2.6,
mutation_scale=18))
ax.text(0.15, 1.1, f"N = {normal:.1f} N", color="#2166ac", fontsize=12)
ax.text(0.15, -1.35, f"mg = {normal:.1f} N", color="#2166ac", fontsize=12)
ax.text(1.65, 0.2, f"pull F = {sliding_force:.2f} N", ha="center",
color="#a43b31", fontsize=12)
ax.text(-1.7, 0.2, f"friction |fₖ| = {sliding_force:.2f} N", ha="center",
color="#a43b31", fontsize=12)
ax.annotate("", xy=(2.65, 1.62), xytext=(1.15, 1.62),
arrowprops=dict(arrowstyle="->", color="#35784f", lw=2,
linestyle="--"))
ax.text(1.9, 1.83, "constant velocity →", color="#35784f",
ha="center", fontsize=12)
ax.text(0, -2.04, "F − |fₖ| = 0 • N − mg = 0 • a = 0",
ha="center", fontsize=13)
ax.text(0, -2.46, "Stipulated model: μₖ = 0.30, g = 9.8 m/s²; already sliding",
ha="center", color="#555555", fontsize=11)
ax.set_title("Constant-speed sliding: balanced forces", fontsize=16, pad=13)
fig.subplots_adjust(left=0.035, right=0.965, top=0.9, bottom=0.04)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"),
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()