codexdot-product-projection-and-angle.py
dot-product-projection-and-angle.py
"""Draw a real Euclidean projection; write an opaque PNG to the current directory."""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Arc
def main():
a = np.array([3.0, 4.0])
c = np.array([2.0, 0.0])
u = c / np.linalg.norm(c)
p = np.dot(a, u) * u
residual = a - p
angle = np.degrees(np.arccos(np.dot(a, c) / np.linalg.norm(a) / np.linalg.norm(c)))
np.testing.assert_allclose(p, [3, 0])
np.testing.assert_allclose(residual, [0, 4])
np.testing.assert_allclose(np.dot(residual, c), 0, atol=1e-14)
np.testing.assert_allclose(angle, 53.13010235415598)
plt.rcParams.update({"font.size": 12, "font.family": "DejaVu Sans"})
fig, ax = plt.subplots(figsize=(7.0, 5.7), facecolor="white")
fig.subplots_adjust(left=.10, right=.94, bottom=.24, top=.84)
blue, orange, green = "#245b96", "#b34e12", "#287446"
ax.set_facecolor("white")
ax.set_aspect("equal")
ax.set_xlim(-.55, 4.2)
ax.set_ylim(-.5, 4.9)
ax.set_xticks(range(5))
ax.set_yticks(range(5))
ax.grid(color="#e7e7e7", linewidth=.7)
ax.axhline(0, color="#999999", linewidth=.8)
ax.axvline(0, color="#999999", linewidth=.8)
ax.set_xlabel("x", labelpad=3)
ax.set_ylabel("y", rotation=0, labelpad=10)
for start, end, color in [(np.zeros(2), a, blue), (np.zeros(2), p, orange), (p, a, green)]:
ax.annotate("", xy=end, xytext=start,
arrowprops=dict(arrowstyle="-|>", color=color, lw=2.8, mutation_scale=17))
ax.plot([0, 0], [0, 4], ls="--", color=green, alpha=.5)
ax.plot([2.78, 2.78, 3], [0, .22, .22], color="#555555", lw=1.3)
ax.add_patch(Arc((0, 0), 1.9, 1.9, theta1=0, theta2=angle, color=blue, lw=1.6))
ax.text(1.05, .30, r"$53.1301^\circ$", fontsize=11, color=blue)
ax.text(1.00, 2.65, r"$a=(3,4)$", color=blue,
bbox=dict(facecolor="white", edgecolor="none", pad=2))
ax.text(3.16, 2.1, "$r=(0,4)$\nresidual", color=green, fontsize=11)
ax.text(1.8, -.35, r"$p=(3,0)$", color=orange, ha="center")
ax.plot(2, 0, "o", ms=6, color="#555555", zorder=4)
ax.text(1.50, .95, r"$c=(2,0)$", fontsize=11)
ax.text(-.11, -.15, "O", ha="right", va="top", fontsize=10)
fig.suptitle("The dot product measures the aligned component", y=.96, fontsize=16)
fig.text(.5, .88, r"$a=(3,4),\quad c=(2,0),\quad u=c/\|c\|=(1,0)$", ha="center")
fig.text(.5, .12, r"$a\cdot c=6,\qquad a\cdot u=3,\qquad p=(a\cdot u)u$", ha="center")
fig.text(.5, .045, r"$a=p+r,\qquad r\cdot c=0,\qquad \|a\|^2=3^2+4^2=25$", ha="center")
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=140,
facecolor="white", transparent=False)
plt.close(fig)
print(f"dot=6; scalar projection=3; vector projection={p.tolist()}; "
f"residual dot c=0; angle={angle:.10f} degrees")
if __name__ == "__main__":
main()