유선 링크 문서의 그림 생성 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
# 유선 링크 문서의 그림을 만든다: 32_wired-links_fig1.svg
# 실행: ~/.venvs/vault-plots/bin/python 32_wired-links_plot.py (matplotlib, numpy 필요)
import math
import os
import matplotlib
matplotlib.use("svg")
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import font_manager
HERE = os.path.dirname(os.path.abspath(__file__))
PREFIX = "32_wired-links"
INK = "#888888" # 밝은 테마와 어두운 테마 모두에서 보이는 회색
C = ["#3b82c4", "#d9622b", "#3a9a5b", "#9b59b6", "#c49a1b"]
_fonts = {f.name for f in font_manager.fontManager.ttflist}
FONT = next((n for n in ["Apple SD Gothic Neo", "NanumGothic", "Noto Sans CJK KR"] if n in _fonts), "DejaVu Sans")
plt.rcParams.update({
"font.family": FONT, "mathtext.fontset": "dejavusans",
"font.size": 11, "axes.unicode_minus": False, "svg.fonttype": "path", "svg.hashsalt": PREFIX,
"axes.edgecolor": INK, "axes.labelcolor": INK, "xtick.color": INK, "ytick.color": INK,
"text.color": INK, "legend.frameon": False,
"axes.facecolor": "none", "figure.facecolor": "none", "savefig.transparent": True,
"axes.spines.top": False, "axes.spines.right": False,
})
def save(fig, k):
fig.savefig(os.path.join(HERE, f"{PREFIX}_fig{k}.svg"), bbox_inches="tight", metadata={"Date": None})
plt.close(fig)
N_CORE, N_CLAD = 1.50, 1.48 # 문서 본문의 임계각 예
THETA_C = math.degrees(math.asin(N_CLAD / N_CORE)) # 임계각 (경계의 수직선에서 잰 각) ≈ 80.6°
MAX_TILT = 90.0 - THETA_C # 축에서 잰 기울기가 이보다 작아야 전반사 ≈ 9.4°
LENGTH = 40.0 # 그림 속 광케이블 길이 (가로 비율은 보기 좋게)
MULTI_CORE = 1.0 # 멀티모드 코어 반지름 (그림 단위)
SINGLE_CORE = 0.18 # 싱글모드 코어 반지름 (그림 단위)
CLAD = 0.45 # 클래딩 두께 (그림 단위)
TILTS = [0.0, 4.0, 8.0] # 멀티모드에 그린 빛의 기울기 (도)
def zigzag(x, tilt_deg, a):
"""코어 반지름 a 안에서 경계에 닿을 때마다 반사되는 빛의 높이. 펼친 직선을 [-a, a]로 접는다"""
u = np.tan(np.radians(tilt_deg)) * x + a # 0..2a 범위로 옮겨 접기
u = np.mod(u, 4 * a)
return np.where(u <= 2 * a, u, 4 * a - u) - a
def path_ratio(tilt_deg):
"""기울어진 빛이 지나는 길이 ÷ 축을 따라 곧장 가는 길이"""
return 1.0 / math.cos(math.radians(tilt_deg))
def draw_fiber(ax, a, title):
ax.fill_between([0, LENGTH], a, a + CLAD, color=INK, alpha=0.18, lw=0)
ax.fill_between([0, LENGTH], -a - CLAD, -a, color=INK, alpha=0.18, lw=0)
ax.plot([0, LENGTH], [a, a], color=INK, lw=0.8)
ax.plot([0, LENGTH], [-a, -a], color=INK, lw=0.8)
ax.text(LENGTH + 0.6, a + CLAD / 2, "클래딩", va="center", fontsize=9)
ax.text(LENGTH + 0.6, 0, "코어", va="center", fontsize=9)
ax.text(LENGTH + 0.6, -a - CLAD / 2, "클래딩", va="center", fontsize=9)
ax.set_title(title, fontsize=10, loc="left", color=INK)
ax.set_xlim(-1.5, LENGTH + 4)
ax.set_yticks([])
ax.set_xticks([])
for side in ("left", "bottom"):
ax.spines[side].set_visible(False)
x = np.linspace(0, LENGTH, 4000)
fig, axes = plt.subplots(2, 1, figsize=(6.5, 4.0), gridspec_kw={"height_ratios": [1.0, 0.55]})
ax = axes[0]
draw_fiber(ax, MULTI_CORE, "멀티모드: 코어가 굵어 빛이 여러 각도의 길로 튕기며 간다")
for k, tilt in enumerate(TILTS):
ax.plot(x, zigzag(x, tilt, MULTI_CORE), color=C[k], lw=1.4)
ax.annotate("", xy=(0, 0), xytext=(-1.4, 0), arrowprops=dict(arrowstyle="->", color=INK, lw=1.0))
ax.text(LENGTH * 0.52, -MULTI_CORE - CLAD - 0.35, "축에서 0°, 4°, 8° 기울어진 빛: 기울수록 길이 길다",
ha="center", va="top", fontsize=9)
ax.set_ylim(-MULTI_CORE - CLAD - 0.9, MULTI_CORE + CLAD + 0.1)
ax = axes[1]
draw_fiber(ax, SINGLE_CORE, "싱글모드: 코어가 가늘어 빛이 거의 곧은 길 하나로 간다")
ax.plot(x, np.zeros_like(x), color=C[0], lw=1.4)
ax.annotate("", xy=(0, 0), xytext=(-1.4, 0), arrowprops=dict(arrowstyle="->", color=INK, lw=1.0))
ax.set_ylim(-SINGLE_CORE - CLAD - 0.1, SINGLE_CORE + CLAD + 0.1)
fig.subplots_adjust(hspace=0.55)
save(fig, 1)
if __name__ == "__main__":
# 임계각: n_core = 1.50, n_clad = 1.48이면 약 80.6° (문서의 값)
assert abs(THETA_C - 80.6) < 0.05
# 그린 빛은 모두 축에서 9.4° 안쪽이라 경계에서 전반사된다 (경계의 수직선에서 잰 각이 임계각보다 크다)
assert all(t < MAX_TILT for t in TILTS)
assert all(90.0 - t > THETA_C for t in TILTS)
# 접은 빛은 코어 밖으로 나가지 않는다
for t in TILTS:
assert np.all(np.abs(zigzag(x, t, MULTI_CORE)) <= MULTI_CORE + 1e-9)
# 8° 기울어진 빛의 길은 곧은 길보다 약 1% 길다 (모드마다 도착 시간이 달라지는 이유)
assert abs(path_ratio(8.0) - 1.0098) < 1e-4
assert path_ratio(0.0) == 1.0 and path_ratio(4.0) < path_ratio(8.0)
print(f"임계각 {THETA_C:.1f}°, 허용 기울기 {MAX_TILT:.1f}°, 8° 길이 비 {path_ratio(8.0):.4f}")
print("ALL CHECKS PASSED")