aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <raisersoft@proton.me>2026-04-19 14:04:27 +0200
committersalmonik <raisersoft@proton.me>2026-04-19 14:04:27 +0200
commitc65a2cb0b35982fac320744fb662456025ad42d0 (patch)
treec452f1849449d473cb98b39bf05d19bd795125d5
parenterror handling in plot.py (diff)
downloadsenator-sperma-c65a2cb0b35982fac320744fb662456025ad42d0.tar.gz
senator-sperma-c65a2cb0b35982fac320744fb662456025ad42d0.zip
discontinue plots; too complex.
Signed-off-by: salmonik <raisersoft@proton.me>
Diffstat (limited to '')
-rw-r--r--cogs/plot.py195
1 files changed, 0 insertions, 195 deletions
diff --git a/cogs/plot.py b/cogs/plot.py
deleted file mode 100644
index ee19de9..0000000
--- a/cogs/plot.py
+++ /dev/null
@@ -1,195 +0,0 @@
1# This file is part of Senator Sperma
2# Licensed under the GPL-3.0 License
3# Copyright (C) 2026 Salmonik @ salmonik@tuta.io
4# https://codeberg.org/salmonik/senator-sperma
5
6import asyncio
7import logging
8import math
9import os
10import re
11
12import discord
13from discord.ext import commands
14
15logger = logging.getLogger(__name__)
16
17_plt_module = None
18_plot_available = False
19
20
21def _ensure_plot():
22 global _plt_module, _plot_available
23
24 if _plt_module is not None:
25 return _plot_available
26
27 try:
28 import matplotlib
29 matplotlib.use("Agg")
30 import matplotlib.pyplot as _plt_module
31 _plot_available = True
32 except ImportError:
33 _plot_available = False
34 logger.warning("matplotlib not available, plot command will be disabled")
35 return _plot_available
36
37
38def parse_expression(expr: str) -> str:
39 expr = expr.replace("^", "**")
40 expr = re.sub(r"(\d)([x])", r"\1*\2", expr)
41 expr = re.sub(r"([x])(\d)", r"\1*\2", expr)
42 expr = re.sub(r"(\d)([y])", r"\1*\2", expr)
43 expr = re.sub(r"([y])(\d)", r"\1*\2", expr)
44 return expr
45
46
47def is_implicit(expr: str) -> bool:
48 return "=" in expr and "y" in expr and "x" in expr
49
50
51def parse_implicit(expr: str) -> tuple[str, str]:
52 lhs, rhs = expr.split("=", 1)
53 lhs = parse_expression(lhs.strip())
54 rhs = parse_expression(rhs.strip())
55 return lhs, rhs
56
57
58class Plot(commands.Cog):
59 def __init__(self, bot):
60 self.bot = bot
61
62 def _generate_plot(self, func_expr: str, output_path: str):
63 global _plt_module
64
65 if not _ensure_plot():
66 raise RuntimeError("matplotlib not available")
67
68 plt = _plt_module
69
70 if is_implicit(func_expr):
71 self._generate_implicit_plot(func_expr, output_path)
72 return
73
74 parsed_expr = parse_expression(func_expr)
75
76 x = [i * 0.1 for i in range(-100, 101)]
77 y = []
78
79 for xi in x:
80 try:
81 result = eval(
82 parsed_expr,
83 {
84 "x": xi,
85 "math": math,
86 "sin": math.sin,
87 "cos": math.cos,
88 "tan": math.tan,
89 "sqrt": math.sqrt,
90 "log": math.log,
91 "exp": math.exp,
92 "pi": math.pi,
93 "e": math.e,
94 },
95 )
96 if math.isfinite(result):
97 y.append(result)
98 else:
99 y.append(float("nan"))
100 except Exception:
101 y.append(float("nan"))
102
103 valid_y = [yi for yi in y if not math.isnan(yi)]
104 if not valid_y:
105 raise ValueError("Brak danych do wykreślenia dla podanej funkcji")
106
107 fig, ax = plt.subplots(figsize=(10, 6))
108 ax.plot(x, y, linewidth=2, color="#00ff00")
109 ax.set_title(f"y = {func_expr}", fontsize=14)
110 ax.set_xlabel("x", fontsize=12)
111 ax.set_ylabel("y", fontsize=12)
112 ax.grid(True, alpha=0.3)
113 ax.axhline(y=0, color="white", linewidth=0.5)
114 ax.axvline(x=0, color="white", linewidth=0.5)
115 ax.set_facecolor("#1a1a1a")
116 fig.patch.set_facecolor("#1a1a1a")
117
118 plt.savefig(output_path, dpi=100, bbox_inches="tight")
119 plt.close()
120
121 def _generate_implicit_plot(self, func_expr: str, output_path: str):
122 global _plt_module
123 plt = _plt_module
124
125 lhs, rhs = parse_implicit(func_expr)
126
127 x_range = 20
128 y_range = 20
129
130 x_vals = []
131 y_vals = []
132
133 for xi in range(-x_range * 10, x_range * 10 + 1):
134 x = xi / 10.0
135 for yi in range(-y_range * 10, y_range * 10 + 1):
136 y = yi / 10.0
137 try:
138 left_val = eval(lhs, {"x": x, "y": y, "math": math, "sqrt": math.sqrt, "pi": math.pi, "e": math.e})
139 right_val = eval(rhs, {"x": x, "y": y, "math": math, "sqrt": math.sqrt, "pi": math.pi, "e": math.e})
140 diff = abs(left_val - right_val)
141 if math.isfinite(diff) and diff < 0.3:
142 x_vals.append(x)
143 y_vals.append(y)
144 except Exception:
145 pass
146
147 if not x_vals or not y_vals:
148 raise ValueError("Brak danych do wykreślenia dla podanej funkcji")
149
150 fig, ax = plt.subplots(figsize=(10, 10))
151 ax.scatter(x_vals, y_vals, s=1, color="#00ff00", alpha=0.5)
152 ax.set_title(f"{func_expr}", fontsize=14)
153 ax.set_xlabel("x", fontsize=12)
154 ax.set_ylabel("y", fontsize=12)
155 ax.grid(True, alpha=0.3)
156 ax.axhline(y=0, color="white", linewidth=0.5)
157 ax.axvline(x=0, color="white", linewidth=0.5)
158 ax.set_xlim(-x_range, x_range)
159 ax.set_ylim(-y_range, y_range)
160 ax.set_aspect("equal")
161 ax.set_facecolor("#1a1a1a")
162 fig.patch.set_facecolor("#1a1a1a")
163
164 plt.savefig(output_path, dpi=100, bbox_inches="tight")
165 plt.close()
166
167 @commands.slash_command(name="funkcja", description="Generuje wykres funkcji matematycznej")
168 async def plot(self, ctx, funkcja: str = discord.Option(str, "Funkcja np. x^3, sin(x), x^2 + 2*x - 1", required=True)):
169 if not _ensure_plot():
170 await ctx.respond("Generowanie wykresów nie jest dostępne (brak matplotlib)", ephemeral=True)
171 return
172
173 await ctx.defer()
174
175 from cogs.utils.temp import get_temp_dir
176 temp_path = os.path.join(get_temp_dir(), f"plot_{os.getpid()}_{asyncio.get_event_loop().time()}.png")
177
178 try:
179 loop = asyncio.get_event_loop()
180 await loop.run_in_executor(None, self._generate_plot, funkcja, temp_path)
181 await ctx.respond(file=discord.File(temp_path, filename="plot.png"))
182 logger.info(f"Generated plot for function: {funkcja}")
183 except Exception as e:
184 logger.error(f"Plot generation error: {e}")
185 await ctx.respond(f"Błąd generowania wykresu: {str(e)}", ephemeral=True)
186 finally:
187 try:
188 if os.path.exists(temp_path):
189 os.unlink(temp_path)
190 except Exception as e:
191 logger.error(f"Error cleaning up temp file: {e}")
192
193
194def setup(bot):
195 bot.add_cog(Plot(bot))