File size: 2,684 Bytes
3c25c17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
from __future__ import annotations

import io
import tempfile

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np


def plot_function(
    expression: str,
    x_range: tuple[float, float] = (-10, 10),
    title: str = "",
    xlabel: str = "x",
    ylabel: str = "y",
) -> str:
    """Plot a mathematical function and return the path to the saved image.

    Args:
        expression: A numpy-compatible expression string using 'x' as variable.
            Examples: "np.sin(x)", "x**2 - 3*x + 2", "np.exp(-x**2)"
        x_range: Tuple of (x_min, x_max).
        title: Plot title.
        xlabel: X-axis label.
        ylabel: Y-axis label.

    Returns:
        Path to the saved PNG image.
    """
    x = np.linspace(x_range[0], x_range[1], 500)
    try:
        y = eval(expression, {"x": x, "np": np, "pi": np.pi, "e": np.e})
    except Exception as e:
        return _error_plot(f"Cannot evaluate: {expression}\n{e}")

    fig, ax = plt.subplots(figsize=(8, 5))
    ax.plot(x, y, "b-", linewidth=2)
    ax.set_title(title or f"$y = {expression}$")
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.grid(True, alpha=0.3)
    ax.axhline(y=0, color="k", linewidth=0.5)
    ax.axvline(x=0, color="k", linewidth=0.5)

    return _save_fig(fig)


def plot_multiple(
    expressions: list[dict],
    x_range: tuple[float, float] = (-10, 10),
    title: str = "",
) -> str:
    """Plot multiple functions on the same axes.

    Args:
        expressions: List of {"expr": str, "label": str} dicts.
        x_range: Tuple of (x_min, x_max).
        title: Plot title.

    Returns:
        Path to the saved PNG image.
    """
    x = np.linspace(x_range[0], x_range[1], 500)
    fig, ax = plt.subplots(figsize=(8, 5))

    for item in expressions:
        expr = item["expr"]
        label = item.get("label", expr)
        try:
            y = eval(expr, {"x": x, "np": np, "pi": np.pi, "e": np.e})
            ax.plot(x, y, linewidth=2, label=label)
        except Exception:
            continue

    ax.set_title(title)
    ax.grid(True, alpha=0.3)
    ax.axhline(y=0, color="k", linewidth=0.5)
    ax.axvline(x=0, color="k", linewidth=0.5)
    ax.legend()

    return _save_fig(fig)


def _save_fig(fig) -> str:
    tmp = tempfile.NamedTemporaryFile(suffix=".png", delete=False)
    fig.savefig(tmp.name, dpi=100, bbox_inches="tight")
    plt.close(fig)
    return tmp.name


def _error_plot(message: str) -> str:
    fig, ax = plt.subplots(figsize=(8, 5))
    ax.text(0.5, 0.5, message, ha="center", va="center", fontsize=12, color="red")
    ax.set_xlim(0, 1)
    ax.set_ylim(0, 1)
    ax.axis("off")
    return _save_fig(fig)