Начал тестировать 5.3 codex. Для меня эти инструменты новшество. Раньше поправлял латек в чатботе.
Код в питоне 5.3 codex написал приблизительно за 10 минут. Я сделал 4 запроса. Oколо 500 линий кода в Питоне и неплохо получилось. Можете протестить. Помоему очень даже хорошо. Я только 3-4 запроса сделал. Два последних для дизайна. Оценить качество кода не могу. Но по-моему очень даже неплохо. Это анимация и запускается в редакторе.
Код:
from dataclasses import dataclass
from itertools import count
from typing import Callable
import numpy as np
import matplotlib
# Ensure we have an interactive GUI backend.
if matplotlib.get_backend().lower().endswith("agg"):
for backend in ("TkAgg", "QtAgg"):
try:
matplotlib.use(backend, force=True)
break
except Exception:
pass
import matplotlib.pyplot as plt
import matplotlib.patheffects as pe
from matplotlib.animation import FuncAnimation
from matplotlib.colors import LinearSegmentedColormap
from matplotlib.lines import Line2D
from matplotlib.patches import ConnectionPatch
from matplotlib.widgets import Button, Slider
@dataclass(frozen=True)
class FunctionDef:
numerator: Callable[[np.ndarray], np.ndarray]
denominator: Callable[[np.ndarray], np.ndarray] | None = None
asymptote_offset: float | None = None
asymptote_step: float | None = None
@dataclass(frozen=True)
class RowSpec:
kind: str
color: str
label: str
@dataclass
class RowArtists:
spec: RowSpec
circle_ax: plt.Axes
graph_ax: plt.Axes
radius: Line2D
point: Line2D
trace: Line2D
end_dot: Line2D
connector: ConnectionPatch
class TrigGraphsReplica:
def __init__(self):
self.theta = 0.0
self.running = True
self.speed = 1.0
self.max_pi = 4
self.theta_max = self.max_pi * np.pi
self.base_omega = np.pi / 3.0 # 4pi sweep in ~12s when speed=1
self.dt = 1.0 / 60.0
self.y_lim = 1.45
self.singularity_eps = 0.035
self.bg = "#04050A"
self.axis_color = "#70717D"
self.tick_color = "#B9BAC7"
self.frame_glow = "#0B0D17"
self.asymptote_color = "#95A536"
self.graph_line_width = 0.72
self.graph_glow_extra = 0.35
self.graph_glow_alpha = 0.06
self.row_specs = self._build_row_specs()
self.function_defs = self._build_function_defs()
self.fig = plt.figure(figsize=(7.2, 12.8), facecolor=self.bg)
self.fig.subplots_adjust(0, 0, 1, 1)
self._draw_title()
self.rows = self._build_rows()
self._build_controls()
self.ani = FuncAnimation(
self.fig,
self.update,
frames=count(),
interval=int(self.dt * 1000),
blit=False,
cache_frame_data=False,
)
def _build_row_specs(self) -> list[RowSpec]:
return [
RowSpec(kind="sin", color="#22E8FF", label=r"sin $\theta$"),
RowSpec(kind="cos", color="#B58AFF", label=r"cos $\theta$"),
RowSpec(kind="tan", color="#FF0A88", label=r"tan $\theta$"),
RowSpec(kind="cot", color="#59E9C2", label=r"cot $\theta$"),
RowSpec(kind="sec", color="#7FFF9D", label=r"sec $\theta$"),
RowSpec(kind="cosec", color="#FFC857", label=r"cosec $\theta$"),
]
def _build_function_defs(self) -> dict[str, FunctionDef]:
ones = lambda x: np.ones_like(x)
return {
"sin": FunctionDef(numerator=np.sin),
"cos": FunctionDef(numerator=np.cos),
"tan": FunctionDef(
numerator=np.sin,
denominator=np.cos,
asymptote_offset=np.pi / 2.0,
asymptote_step=np.pi,
),
"cot": FunctionDef(
numerator=np.cos,
denominator=np.sin,
asymptote_offset=np.pi,
asymptote_step=np.pi,
),
"sec": FunctionDef(
numerator=ones,
denominator=np.cos,
asymptote_offset=np.pi / 2.0,
asymptote_step=np.pi,
),
"cosec": FunctionDef(
numerator=ones,
denominator=np.sin,
asymptote_offset=np.pi,
asymptote_step=np.pi,
),
}
def _draw_title(self):
title_text = "Trigonometric Graphs"
font_size = 31
y = 0.953
cmap = LinearSegmentedColormap.from_list(
"title_grad",
["#2EEBFF", "#9CA5FF", "#DF7FFF"],
)
widths_px = self._measure_char_widths(title_text, font_size)
fig_width_px = self.fig.bbox.width
total_w = sum(widths_px)
x = 0.5 - (total_w / fig_width_px) / 2.0
for i, (ch, w_px) in enumerate(zip(title_text, widths_px)):
if ch != " ":
color = cmap(i / max(len(title_text) - 1, 1))
txt = self.fig.text(
x,
y,
ch,
ha="left",
va="center",
fontsize=font_size,
family="STIXGeneral",
color=color,
zorder=3,
)
txt.set_path_effects(
[
pe.Stroke(linewidth=1.2, foreground="#090A0F", alpha=0.90),
pe.Normal(),
]
)
x += w_px / fig_width_px
def _measure_char_widths(self, text: str, font_size: int) -> list[float]:
probes = []
for ch in text:
probe = self.fig.text(
0.0,
0.0,
ch if ch != " " else "n n",
ha="left",
va="center",
fontsize=font_size,
family="STIXGeneral",
color=(0.0, 0.0, 0.0, 0.0),
)
probes.append(probe)
n_probe = self.fig.text(
0.0,
0.0,
"n",
ha="left",
va="center",
fontsize=font_size,
family="STIXGeneral",
color=(0.0, 0.0, 0.0, 0.0),
)
self.fig.canvas.draw()
renderer = self.fig.canvas.get_renderer()
n_width = n_probe.get_window_extent(renderer=renderer).width
n_probe.remove()
widths_px = []
for ch, probe in zip(text, probes):
bb = probe.get_window_extent(renderer=renderer)
if ch == " ":
widths_px.append(max(bb.width - 2.0 * n_width, n_width * 0.28))
else:
widths_px.append(bb.width)
probe.remove()
return widths_px
def _build_rows(self) -> list[RowArtists]:
y_top = 0.80
y_bottom = 0.11
row_h = 0.095
y_step = (y_top - y_bottom) / (len(self.row_specs) - 1)
rows: list[RowArtists] = []
for idx, spec in enumerate(self.row_specs):
y = y_top - idx * y_step
circle_ax = self.fig.add_axes([0.055, y, 0.205, row_h], facecolor=self.bg)
graph_ax = self.fig.add_axes([0.285, y, 0.66, row_h], facecolor=self.bg)
rows.append(self._build_row(spec, circle_ax, graph_ax))
return rows
def _glow(self, artist, color, extra=1.5, alpha=0.14):
lw = artist.get_linewidth() if hasattr(artist, "get_linewidth") else 1.0
artist.set_path_effects(
[
pe.Stroke(linewidth=lw + extra, foreground=color, alpha=alpha),
pe.Normal(),
]
)
def _setup_circle_axis(self, ax: plt.Axes, spec: RowSpec) -> tuple[Line2D, Line2D]:
ax.clear()
ax.set_facecolor(self.bg)
ax.set_aspect("equal", adjustable="box")
ax.set_xlim(-1.20, 1.20)
ax.set_ylim(-1.20, 1.20)
ax.set_xticks([])
ax.set_yticks([])
for spine in ax.spines.values():
spine.set_visible(False)
theta = np.linspace(0.0, 2.0 * np.pi, 400)
circle, = ax.plot(np.cos(theta), np.sin(theta), color=spec.color, lw=1.55)
self._glow(circle, spec.color, extra=1.6, alpha=0.14)
ax.plot([-1.0, 1.0], [0.0, 0.0], color="#E4E5EE", lw=0.88, alpha=0.9)
ax.plot([0.0, 0.0], [-1.0, 1.0], color="#E4E5EE", lw=0.88, alpha=0.9)
ax.text(
0.0,
-1.18,
spec.label,
color=spec.color,
fontsize=18,
family="STIXGeneral",
style="italic",
ha="center",
va="top",
)
radius_line, = ax.plot([], [], color="#F0F0F7", lw=1.28, alpha=0.94)
point, = ax.plot([], [], "o", color="#F5F6FB", ms=3.6)
return radius_line, point
def _asymptotes(self, kind: str) -> list[float]:
func = self.function_defs[kind]
if func.asymptote_offset is None or func.asymptote_step is None:
return []
xs = []
x = func.asymptote_offset
while x <= self.theta_max + 1e-9:
xs.append(x)
x += func.asymptote_step
return xs
def _setup_graph_axis(self, ax: plt.Axes, spec: RowSpec) -> tuple[Line2D, Line2D]:
ax.clear()
ax.set_facecolor(self.bg)
ax.set_xlim(-0.22, self.theta_max + 0.30)
ax.set_ylim(-self.y_lim - 0.03, self.y_lim + 0.03)
ax.set_xticks([])
ax.set_yticks([])
for spine in ax.spines.values():
spine.set_visible(False)
ax.patch.set_path_effects(
[
pe.Stroke(linewidth=2.0, foreground=self.frame_glow, alpha=0.25),
pe.Normal(),
]
)
ax.plot([-0.22, self.theta_max + 0.26], [0.0, 0.0], color=self.axis_color, lw=0.86, alpha=0.92)
ax.plot([0.0, 0.0], [-1.20, 1.20], color=self.axis_color, lw=0.86, alpha=0.92)
for y_tick in (-1.0, 1.0):
ax.plot([-0.09, 0.09], [y_tick, y_tick], color=self.axis_color, lw=0.78, alpha=0.82)
for k in range(1, self.max_pi + 1):
x_tick = k * np.pi
ax.plot([x_tick, x_tick], [-0.06, 0.06], color=self.axis_color, lw=0.76, alpha=0.80)
tick_text = r"$\pi$" if k == 1 else rf"${k}\pi$"
ax.text(
x_tick,
-0.155,
tick_text,
color=self.tick_color,
ha="center",
va="center",
fontsize=12,
family="DejaVu Serif",
)
for asym_x in self._asymptotes(spec.kind):
ax.plot(
[asym_x, asym_x],
[-self.y_lim, self.y_lim],
color=self.asymptote_color,
lw=0.85,
ls=(0, (5, 5)),
alpha=0.78,
)
trace, = ax.plot(
[],
[],
color=spec.color,
lw=self.graph_line_width,
alpha=0.99,
solid_capstyle="round",
)
self._glow(
trace,
spec.color,
extra=self.graph_glow_extra,
alpha=self.graph_glow_alpha,
)
end_dot, = ax.plot([], [], "o", color=spec.color, ms=3.1, alpha=0.96)
return trace, end_dot
def _build_row(self, spec: RowSpec, circle_ax: plt.Axes, graph_ax: plt.Axes) -> RowArtists:
radius_line, point = self._setup_circle_axis(circle_ax, spec)
trace, end_dot = self._setup_graph_axis(graph_ax, spec)
connector = ConnectionPatch(
(0.0, 0.0),
(0.0, 0.0),
coordsA="data",
coordsB="data",
axesA=circle_ax,
axesB=graph_ax,
color=spec.color,
lw=1.05,
ls=(0, (2.4, 2.8)),
alpha=0.70,
zorder=6,
)
self.fig.add_artist(connector)
return RowArtists(
spec=spec,
circle_ax=circle_ax,
graph_ax=graph_ax,
radius=radius_line,
point=point,
trace=trace,
end_dot=end_dot,
connector=connector,
)
def _build_controls(self):
self.ax_start = self.fig.add_axes([0.055, 0.02, 0.105, 0.04])
self.ax_stop = self.fig.add_axes([0.172, 0.02, 0.105, 0.04])
self.ax_reset = self.fig.add_axes([0.289, 0.02, 0.105, 0.04])
self.btn_start = Button(self.ax_start, "Start", color="#111525", hovercolor="#1A2032")
self.btn_stop = Button(self.ax_stop, "Stop", color="#111525", hovercolor="#1A2032")
self.btn_reset = Button(self.ax_reset, "Reset", color="#111525", hovercolor="#1A2032")
for button in (self.btn_start, self.btn_stop, self.btn_reset):
button.label.set_color("#ECECF2")
button.label.set_fontsize(10)
self.btn_start.on_clicked(self.on_start)
self.btn_stop.on_clicked(self.on_stop)
self.btn_reset.on_clicked(self.on_reset)
self.ax_speed = self.fig.add_axes([0.49, 0.042, 0.44, 0.020], facecolor=self.bg)
self.ax_maxpi = self.fig.add_axes([0.49, 0.013, 0.44, 0.020], facecolor=self.bg)
self.s_speed = Slider(
self.ax_speed,
"Speed",
0.2,
3.0,
valinit=self.speed,
valstep=0.1,
color="#3FC5FF",
)
self.s_maxpi = Slider(
self.ax_maxpi,
"Max $\\pi$",
2,
8,
valinit=self.max_pi,
valstep=1,
color="#D18FFF",
)
for text in (self.s_speed.label, self.s_speed.valtext, self.s_maxpi.label, self.s_maxpi.valtext):
text.set_color("#DADAE3")
text.set_fontsize(10)
self.s_speed.on_changed(self.on_speed)
self.s_maxpi.on_changed(self.on_maxpi)
def on_start(self, _event):
self.running = True
def on_stop(self, _event):
self.running = False
def on_reset(self, _event):
self.running = False
self.theta = 0.0
def on_speed(self, value):
self.speed = float(value)
def on_maxpi(self, value):
self.max_pi = int(value)
self.theta_max = self.max_pi * np.pi
if self.theta >= self.theta_max:
self.theta = np.mod(self.theta, self.theta_max)
for row in self.rows:
row.trace, row.end_dot = self._setup_graph_axis(row.graph_ax, row.spec)
def _evaluate(self, kind: str, x: np.ndarray) -> np.ndarray:
func = self.function_defs[kind]
x = np.asarray(x, dtype=float)
numerator = func.numerator(x)
if func.denominator is None:
return numerator
denominator = func.denominator(x)
return np.divide(
numerator,
denominator,
out=np.full_like(x, np.nan, dtype=float),
where=np.abs(denominator) >= self.singularity_eps,
)
def _series(self, kind: str, theta: float) -> tuple[np.ndarray, np.ndarray, float]:
x = np.linspace(0.0, max(theta, 1e-4), 1800)
y = self._evaluate(kind, x)
y[np.abs(y) > self.y_lim] = np.nan
y_now = float(self._evaluate(kind, np.array([theta]))[0])
if np.isfinite(y_now):
y_now = float(np.clip(y_now, -self.y_lim, self.y_lim))
return x, y, y_now
def _step_theta(self):
if self.running:
self.theta = (self.theta + self.base_omega * self.speed * self.dt) % self.theta_max
def _current_unit_circle_point(self) -> tuple[float, float]:
return float(np.cos(self.theta)), float(np.sin(self.theta))
def update(self, _frame):
self._step_theta()
px, py = self._current_unit_circle_point()
artists = []
for row in self.rows:
x, y, y_now = self._series(row.spec.kind, self.theta)
row.radius.set_data([0.0, px], [0.0, py])
row.point.set_data([px], [py])
row.trace.set_data(x, y)
if np.isfinite(y_now):
row.end_dot.set_data([self.theta], [y_now])
row.end_dot.set_visible(True)
row.connector.set_positions((px, py), (self.theta, y_now))
row.connector.set_visible(True)
else:
row.end_dot.set_visible(False)
row.connector.set_visible(False)
artists.extend([row.radius, row.point, row.trace, row.end_dot, row.connector])
return artists
if __name__ == "__main__":
app = TrigGraphsReplica()
plt.show()