|
|
import os
|
|
|
from flask import Blueprint, render_template, request, jsonify
|
|
|
import numpy as np
|
|
|
import matplotlib
|
|
|
matplotlib.use('Agg')
|
|
|
import matplotlib.pyplot as plt
|
|
|
import base64
|
|
|
from io import BytesIO
|
|
|
import re
|
|
|
|
|
|
graph_bp = Blueprint('graph_bp', __name__)
|
|
|
|
|
|
UPLOAD_FOLDER = 'static/uploads'
|
|
|
GRAPHS_FOLDER = 'static/graphs'
|
|
|
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
|
|
os.makedirs(GRAPHS_FOLDER, exist_ok=True)
|
|
|
|
|
|
|
|
|
def parse_plot_command(command):
|
|
|
"""Parse plot command and extract function and range"""
|
|
|
|
|
|
command = command.strip()
|
|
|
|
|
|
|
|
|
pattern = r"plot\s+(.+?)\s+from\s+(-?[\d\*\.pi]+)\s+to\s+(-?[\d\*\.pi]+)"
|
|
|
match = re.match(pattern, command, re.IGNORECASE)
|
|
|
|
|
|
if match:
|
|
|
function = match.group(1)
|
|
|
x_min = match.group(2)
|
|
|
x_max = match.group(3)
|
|
|
|
|
|
|
|
|
x_min = x_min.replace('pi', 'np.pi')
|
|
|
x_max = x_max.replace('pi', 'np.pi')
|
|
|
|
|
|
try:
|
|
|
x_min_val = eval(x_min)
|
|
|
x_max_val = eval(x_max)
|
|
|
return function, x_min_val, x_max_val
|
|
|
except:
|
|
|
raise ValueError("Invalid range values")
|
|
|
|
|
|
|
|
|
simple_pattern = r"plot\s+(.+)"
|
|
|
simple_match = re.match(simple_pattern, command, re.IGNORECASE)
|
|
|
|
|
|
if simple_match:
|
|
|
function = simple_match.group(1)
|
|
|
return function, -10, 10
|
|
|
|
|
|
raise ValueError("Invalid plot command format")
|
|
|
|
|
|
|
|
|
def evaluate_function(func_str, x):
|
|
|
"""Safely evaluate mathematical function"""
|
|
|
|
|
|
func_str = func_str.replace('^', '**')
|
|
|
func_str = func_str.replace('sin', 'np.sin')
|
|
|
func_str = func_str.replace('cos', 'np.cos')
|
|
|
func_str = func_str.replace('tan', 'np.tan')
|
|
|
func_str = func_str.replace('log', 'np.log')
|
|
|
func_str = func_str.replace('exp', 'np.exp')
|
|
|
func_str = func_str.replace('sqrt', 'np.sqrt')
|
|
|
func_str = func_str.replace('abs', 'np.abs')
|
|
|
|
|
|
|
|
|
func_str = func_str.replace('x', f'({x})')
|
|
|
|
|
|
try:
|
|
|
return eval(func_str)
|
|
|
except:
|
|
|
raise ValueError(f"Error evaluating function: {func_str}")
|
|
|
|
|
|
|
|
|
def generate_tikz_latex(function, x_min, x_max):
|
|
|
"""Generate TikZ/pgfplots LaTeX code for the function"""
|
|
|
|
|
|
pgf_function = function.replace('^', '^')
|
|
|
pgf_function = pgf_function.replace('sqrt', 'sqrt')
|
|
|
|
|
|
|
|
|
latex_code = r'''\documentclass{article}
|
|
|
\usepackage{pgfplots}
|
|
|
\usepackage{amsmath}
|
|
|
\pgfplotsset{compat=1.18}
|
|
|
|
|
|
\begin{document}
|
|
|
|
|
|
\begin{figure}[h]
|
|
|
\centering
|
|
|
\begin{tikzpicture}
|
|
|
\begin{axis}[
|
|
|
xlabel={$x$},
|
|
|
ylabel={$y$},
|
|
|
grid=major,
|
|
|
width=12cm,
|
|
|
height=8cm,
|
|
|
samples=200,
|
|
|
domain=''' + f"{x_min}:{x_max}" + r''',
|
|
|
legend pos=north west,
|
|
|
axis lines=middle,
|
|
|
]
|
|
|
\addplot[blue, thick] {''' + pgf_function + r'''};
|
|
|
\legend{$f(x)=''' + function + r'''$}
|
|
|
\end{axis}
|
|
|
\end{tikzpicture}
|
|
|
\caption{Graph of $f(x) = ''' + function + r'''$}
|
|
|
\label{fig:graph}
|
|
|
\end{figure}
|
|
|
|
|
|
\end{document}'''
|
|
|
|
|
|
return latex_code
|
|
|
|
|
|
|
|
|
def generate_plot(function, x_min, x_max):
|
|
|
"""Generate plot and return base64 image"""
|
|
|
try:
|
|
|
|
|
|
x = np.linspace(x_min, x_max, 1000)
|
|
|
|
|
|
|
|
|
y = []
|
|
|
for xi in x:
|
|
|
try:
|
|
|
yi = evaluate_function(function, xi)
|
|
|
y.append(yi)
|
|
|
except:
|
|
|
y.append(np.nan)
|
|
|
|
|
|
y = np.array(y)
|
|
|
|
|
|
|
|
|
plt.figure(figsize=(10, 6))
|
|
|
plt.plot(x, y, linewidth=2, color='#667eea')
|
|
|
plt.grid(True, alpha=0.3)
|
|
|
plt.xlabel('x')
|
|
|
plt.ylabel('y')
|
|
|
plt.title(f'Graph of f(x) = {function}')
|
|
|
|
|
|
|
|
|
buffer = BytesIO()
|
|
|
plt.savefig(buffer, format='png', dpi=150, bbox_inches='tight')
|
|
|
buffer.seek(0)
|
|
|
image_base64 = base64.b64encode(buffer.getvalue()).decode()
|
|
|
plt.close()
|
|
|
|
|
|
return image_base64
|
|
|
except Exception as e:
|
|
|
raise ValueError(f"Error generating plot: {str(e)}")
|
|
|
|
|
|
|
|
|
@graph_bp.route("/graph")
|
|
|
def graph_page():
|
|
|
return render_template("graph.html")
|
|
|
|
|
|
|
|
|
@graph_bp.route("/graph/generate", methods=["POST"])
|
|
|
def generate_graph():
|
|
|
"""Generate graph from user input"""
|
|
|
try:
|
|
|
data = request.get_json()
|
|
|
if not data:
|
|
|
return jsonify({'error': 'No data provided'}), 400
|
|
|
|
|
|
command = data.get('command', '')
|
|
|
if not command:
|
|
|
return jsonify({'error': 'No command provided'}), 400
|
|
|
|
|
|
|
|
|
function, x_min, x_max = parse_plot_command(command)
|
|
|
|
|
|
|
|
|
image_base64 = generate_plot(function, x_min, x_max)
|
|
|
|
|
|
|
|
|
latex_code = generate_tikz_latex(function, x_min, x_max)
|
|
|
|
|
|
return jsonify({
|
|
|
'success': True,
|
|
|
'image': image_base64,
|
|
|
'function': function,
|
|
|
'range': f'[{x_min}, {x_max}]',
|
|
|
'latex': latex_code
|
|
|
})
|
|
|
|
|
|
except Exception as e:
|
|
|
return jsonify({'error': str(e)}), 500 |