codextangent-normal-vector-decomposition.py
tangent-normal-vector-decomposition.py
"""Orthogonal projections at (1,1) on a parabola; PNG to current directory."""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
point = np.array([1., 1.])
tangent = np.array([1., 2.]) / np.sqrt(5)
normal = np.array([-2., 1.]) / np.sqrt(5)
vector = np.array([3., 1.])
along = (vector @ tangent) * tangent
across = (vector @ normal) * normal
assert np.allclose(along, [1, 2])
assert np.allclose(across, [2, -1])
assert np.allclose(along + across, vector)
assert np.isclose(along @ across, 0)
assert np.isclose(np.linalg.norm(vector)**2,
np.linalg.norm(along)**2 + np.linalg.norm(across)**2)
fig, ax = plt.subplots(figsize=(9.2, 6.3), dpi=110)
fig.patch.set_facecolor("white")
x = np.linspace(-1.1, 2.3, 250)
ax.plot(x, x*x, color="#777777", lw=2, label=r"Curve $y=x^2$")
ax.scatter(*point, color="#343434", s=45, zorder=5)
for displacement, color in [(vector, "#343434"),
(along, "#245f9b"), (across, "#c56521")]:
ax.annotate("", point + displacement, point,
arrowprops=dict(arrowstyle="-|>", color=color,
lw=2.5, mutation_scale=18))
for endpoint in [point+along, point+across]:
ax.plot([endpoint[0], (point+vector)[0]],
[endpoint[1], (point+vector)[1]],
color="#aaaaaa", ls="--", lw=1.3)
ax.text(.5, .55, "P = (1, 1)", fontsize=11)
ax.text(2.08, 3.15, "Tangent part\n(1, 2)", color="#245f9b", fontsize=12)
ax.text(3.1, -.45, "Normal part\n(2, −1)", color="#c56521", fontsize=12)
ax.text(4.05, 2.13, "w = (3, 1)", fontsize=12)
ax.set_xlim(-1.3, 5.4)
ax.set_ylim(-.9, 5.2)
ax.set_aspect("equal")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_title("A vector splits along the local tangent and normal", fontsize=15)
ax.grid(alpha=.16)
ax.legend(loc="upper left", fontsize=10)
fig.text(.5, .035,
"Arrows share the base point P; dashed connectors show vector addition. "
"The two components are perpendicular.",
ha="center", fontsize=10.5)
fig.subplots_adjust(left=.07, right=.98, bottom=.13, top=.9)
fig.savefig(Path.cwd() / "tangent-normal-vector-decomposition.png",
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()