Buckets:
| """ | |
| Geometry Visualizer | |
| تجسمساز هندسه | |
| This module provides visualization capabilities for geometric problems, | |
| solutions, and proofs. | |
| """ | |
| import os | |
| import logging | |
| import numpy as np | |
| import matplotlib.pyplot as plt | |
| import matplotlib.patches as patches | |
| from matplotlib.patches import Circle, Polygon, FancyBboxPatch | |
| from matplotlib.lines import Line2D | |
| import matplotlib.patches as mpatches | |
| from typing import Dict, List, Any, Optional, Tuple | |
| from datetime import datetime | |
| import json | |
| # Set matplotlib to use Persian fonts | |
| plt.rcParams["font.family"] = ["DejaVu Sans", "Arial Unicode MS", "Tahoma"] | |
| logger = logging.getLogger(__name__) | |
| class GeometryVisualizer: | |
| """ | |
| Visualizer for geometric problems and solutions | |
| تجسمساز مسائل و راهحلهای هندسی | |
| """ | |
| def __init__(self, config: Dict[str, Any] = None): | |
| """ | |
| Initialize the geometry visualizer | |
| Args: | |
| config: Visualization configuration | |
| """ | |
| self.config = config or {} | |
| # Visualization settings | |
| self.resolution = self.config.get("diagrams", {}).get("resolution", [1200, 900]) | |
| self.dpi = self.config.get("diagrams", {}).get("dpi", 150) | |
| self.style = self.config.get("diagrams", {}).get("style", "professional") | |
| # Colors | |
| self.colors = self.config.get("diagrams", {}).get( | |
| "colors", | |
| { | |
| "points": "#2E86AB", | |
| "lines": "#A23B72", | |
| "circles": "#F18F01", | |
| "triangles": "#C73E1D", | |
| "annotations": "#6C5CE7", | |
| }, | |
| ) | |
| # Create output directory | |
| self.output_dir = "visualizations" | |
| os.makedirs(self.output_dir, exist_ok=True) | |
| logger.info("Geometry Visualizer initialized") | |
| def create_triangle_diagram( | |
| self, | |
| vertices: List[Tuple[float, float]], | |
| labels: List[str] = None, | |
| angles: List[float] = None, | |
| title: str = "مثلث", | |
| ) -> str: | |
| """ | |
| Create a triangle diagram | |
| Args: | |
| vertices: List of triangle vertices (x, y) | |
| labels: Labels for vertices | |
| angles: Angles at each vertex | |
| title: Diagram title | |
| Returns: | |
| Path to saved diagram | |
| """ | |
| try: | |
| fig, ax = plt.subplots(1, 1, figsize=(10, 8)) | |
| # Draw triangle | |
| triangle = Polygon( | |
| vertices, | |
| closed=True, | |
| fill=False, | |
| edgecolor=self.colors["triangles"], | |
| linewidth=3, | |
| ) | |
| ax.add_patch(triangle) | |
| # Add vertex labels | |
| if labels is None: | |
| labels = ["A", "B", "C"] | |
| for i, (x, y) in enumerate(vertices): | |
| ax.plot(x, y, "o", color=self.colors["points"], markersize=8) | |
| ax.text( | |
| x, | |
| y + 0.2, | |
| labels[i], | |
| fontsize=14, | |
| ha="center", | |
| fontweight="bold", | |
| color=self.colors["annotations"], | |
| ) | |
| # Add angle labels | |
| if angles: | |
| for i, angle in enumerate(angles): | |
| x, y = vertices[i] | |
| ax.text( | |
| x - 0.3, | |
| y - 0.3, | |
| f"{angle}°", | |
| fontsize=12, | |
| ha="center", | |
| color=self.colors["annotations"], | |
| ) | |
| # Add side length labels | |
| for i in range(3): | |
| x1, y1 = vertices[i] | |
| x2, y2 = vertices[(i + 1) % 3] | |
| mid_x = (x1 + x2) / 2 | |
| mid_y = (y1 + y2) / 2 | |
| # Calculate side length | |
| length = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) | |
| ax.text( | |
| mid_x, | |
| mid_y, | |
| f"{length:.2f}", | |
| fontsize=10, | |
| ha="center", | |
| va="center", | |
| bbox=dict(boxstyle="round,pad=0.3", facecolor="white", alpha=0.8), | |
| ) | |
| # Set up the plot | |
| ax.set_xlim( | |
| min(x for x, y in vertices) - 1, max(x for x, y in vertices) + 1 | |
| ) | |
| ax.set_ylim( | |
| min(y for x, y in vertices) - 1, max(y for x, y in vertices) + 1 | |
| ) | |
| ax.set_aspect("equal") | |
| ax.grid(True, alpha=0.3) | |
| ax.set_title(title, fontsize=16, fontweight="bold", pad=20) | |
| # Save the diagram | |
| filename = f"triangle_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png" | |
| filepath = os.path.join(self.output_dir, filename) | |
| plt.savefig(filepath, dpi=self.dpi, bbox_inches="tight") | |
| plt.close() | |
| logger.info(f"Triangle diagram saved to {filepath}") | |
| return filepath | |
| except Exception as e: | |
| logger.error(f"Error creating triangle diagram: {e}") | |
| return "" | |
| def create_circle_diagram( | |
| self, | |
| center: Tuple[float, float], | |
| radius: float, | |
| points: List[Tuple[float, float]] = None, | |
| chords: List[List[Tuple[float, float]]] = None, | |
| tangents: List[List[Tuple[float, float]]] = None, | |
| title: str = "دایره", | |
| ) -> str: | |
| """ | |
| Create a circle diagram | |
| Args: | |
| center: Circle center (x, y) | |
| radius: Circle radius | |
| points: Points on the circle | |
| chords: List of chords | |
| tangents: List of tangent lines | |
| title: Diagram title | |
| Returns: | |
| Path to saved diagram | |
| """ | |
| try: | |
| fig, ax = plt.subplots(1, 1, figsize=(10, 8)) | |
| # Draw circle | |
| circle = Circle( | |
| center, | |
| radius, | |
| fill=False, | |
| edgecolor=self.colors["circles"], | |
| linewidth=3, | |
| ) | |
| ax.add_patch(circle) | |
| # Draw center point | |
| ax.plot( | |
| center[0], | |
| center[1], | |
| "o", | |
| color=self.colors["points"], | |
| markersize=8, | |
| label="مرکز", | |
| ) | |
| # Draw points on circle | |
| if points: | |
| for i, (x, y) in enumerate(points): | |
| ax.plot(x, y, "o", color=self.colors["points"], markersize=6) | |
| ax.text( | |
| x + 0.2, | |
| y + 0.2, | |
| f"P{i+1}", | |
| fontsize=10, | |
| ha="center", | |
| color=self.colors["annotations"], | |
| ) | |
| # Draw chords | |
| if chords: | |
| for chord in chords: | |
| if len(chord) == 2: | |
| x_coords = [chord[0][0], chord[1][0]] | |
| y_coords = [chord[0][1], chord[1][1]] | |
| ax.plot( | |
| x_coords, | |
| y_coords, | |
| color=self.colors["lines"], | |
| linewidth=2, | |
| linestyle="--", | |
| ) | |
| # Draw tangents | |
| if tangents: | |
| for tangent in tangents: | |
| if len(tangent) == 2: | |
| x_coords = [tangent[0][0], tangent[1][0]] | |
| y_coords = [tangent[0][1], tangent[1][1]] | |
| ax.plot( | |
| x_coords, | |
| y_coords, | |
| color=self.colors["lines"], | |
| linewidth=2, | |
| linestyle="-", | |
| ) | |
| # Set up the plot | |
| ax.set_xlim(center[0] - radius - 1, center[0] + radius + 1) | |
| ax.set_ylim(center[1] - radius - 1, center[1] + radius + 1) | |
| ax.set_aspect("equal") | |
| ax.grid(True, alpha=0.3) | |
| ax.set_title(title, fontsize=16, fontweight="bold", pad=20) | |
| ax.legend() | |
| # Save the diagram | |
| filename = f"circle_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png" | |
| filepath = os.path.join(self.output_dir, filename) | |
| plt.savefig(filepath, dpi=self.dpi, bbox_inches="tight") | |
| plt.close() | |
| logger.info(f"Circle diagram saved to {filepath}") | |
| return filepath | |
| except Exception as e: | |
| logger.error(f"Error creating circle diagram: {e}") | |
| return "" | |
| def create_polygon_diagram( | |
| self, | |
| vertices: List[Tuple[float, float]], | |
| polygon_type: str = "polygon", | |
| labels: List[str] = None, | |
| title: str = "چندضلعی", | |
| ) -> str: | |
| """ | |
| Create a polygon diagram | |
| Args: | |
| vertices: List of polygon vertices | |
| polygon_type: Type of polygon | |
| labels: Labels for vertices | |
| title: Diagram title | |
| Returns: | |
| Path to saved diagram | |
| """ | |
| try: | |
| fig, ax = plt.subplots(1, 1, figsize=(10, 8)) | |
| # Draw polygon | |
| polygon = Polygon( | |
| vertices, | |
| closed=True, | |
| fill=False, | |
| edgecolor=self.colors["triangles"], | |
| linewidth=3, | |
| ) | |
| ax.add_patch(polygon) | |
| # Add vertex labels | |
| if labels is None: | |
| labels = [chr(65 + i) for i in range(len(vertices))] | |
| for i, (x, y) in enumerate(vertices): | |
| ax.plot(x, y, "o", color=self.colors["points"], markersize=8) | |
| ax.text( | |
| x, | |
| y + 0.2, | |
| labels[i], | |
| fontsize=14, | |
| ha="center", | |
| fontweight="bold", | |
| color=self.colors["annotations"], | |
| ) | |
| # Set up the plot | |
| ax.set_xlim( | |
| min(x for x, y in vertices) - 1, max(x for x, y in vertices) + 1 | |
| ) | |
| ax.set_ylim( | |
| min(y for x, y in vertices) - 1, max(y for x, y in vertices) + 1 | |
| ) | |
| ax.set_aspect("equal") | |
| ax.grid(True, alpha=0.3) | |
| ax.set_title(title, fontsize=16, fontweight="bold", pad=20) | |
| # Save the diagram | |
| filename = f"{polygon_type}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png" | |
| filepath = os.path.join(self.output_dir, filename) | |
| plt.savefig(filepath, dpi=self.dpi, bbox_inches="tight") | |
| plt.close() | |
| logger.info(f"Polygon diagram saved to {filepath}") | |
| return filepath | |
| except Exception as e: | |
| logger.error(f"Error creating polygon diagram: {e}") | |
| return "" | |
| def create_proof_visualization( | |
| self, steps: List[Dict[str, Any]], title: str = "تجسم اثبات" | |
| ) -> str: | |
| """ | |
| Create a visualization of proof steps | |
| Args: | |
| steps: List of proof steps | |
| title: Visualization title | |
| Returns: | |
| Path to saved visualization | |
| """ | |
| try: | |
| fig, ax = plt.subplots(1, 1, figsize=(12, 8)) | |
| # Create a text-based visualization | |
| y_pos = 0.9 | |
| step_height = 0.1 | |
| for i, step in enumerate(steps): | |
| step_text = f"مرحله {i+1}: {step.get('description', '')}" | |
| # Add step text | |
| ax.text( | |
| 0.05, | |
| y_pos, | |
| step_text, | |
| fontsize=12, | |
| ha="left", | |
| va="top", | |
| transform=ax.transAxes, | |
| bbox=dict( | |
| boxstyle="round,pad=0.5", facecolor="lightblue", alpha=0.7 | |
| ), | |
| ) | |
| # Add result if available | |
| if "result" in step: | |
| result_text = f"نتیجه: {step['result'][:100]}..." | |
| ax.text( | |
| 0.05, | |
| y_pos - 0.03, | |
| result_text, | |
| fontsize=10, | |
| ha="left", | |
| va="top", | |
| transform=ax.transAxes, | |
| style="italic", | |
| color="darkgreen", | |
| ) | |
| y_pos -= step_height | |
| # Set up the plot | |
| ax.set_xlim(0, 1) | |
| ax.set_ylim(0, 1) | |
| ax.axis("off") | |
| ax.set_title(title, fontsize=16, fontweight="bold", pad=20) | |
| # Save the visualization | |
| filename = f"proof_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png" | |
| filepath = os.path.join(self.output_dir, filename) | |
| plt.savefig(filepath, dpi=self.dpi, bbox_inches="tight") | |
| plt.close() | |
| logger.info(f"Proof visualization saved to {filepath}") | |
| return filepath | |
| except Exception as e: | |
| logger.error(f"Error creating proof visualization: {e}") | |
| return "" | |
| def create_comparison_chart( | |
| self, results: Dict[str, Any], title: str = "مقایسه نتایج" | |
| ) -> str: | |
| """ | |
| Create a comparison chart of different solving methods | |
| Args: | |
| results: Results from different methods | |
| title: Chart title | |
| Returns: | |
| Path to saved chart | |
| """ | |
| try: | |
| fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6)) | |
| # Extract data | |
| methods = [] | |
| confidences = [] | |
| solution_lengths = [] | |
| for method, result in results.items(): | |
| if method == "comparison": | |
| continue | |
| if "error" not in result: | |
| methods.append(method) | |
| confidences.append(result.get("confidence", 0)) | |
| solution_lengths.append(len(result.get("solution", ""))) | |
| # Confidence chart | |
| if methods: | |
| bars1 = ax1.bar( | |
| methods, confidences, color=self.colors["points"], alpha=0.7 | |
| ) | |
| ax1.set_title("امتیاز اعتماد", fontsize=14, fontweight="bold") | |
| ax1.set_ylabel("اعتماد", fontsize=12) | |
| ax1.set_ylim(0, 1) | |
| # Add value labels on bars | |
| for bar, value in zip(bars1, confidences): | |
| ax1.text( | |
| bar.get_x() + bar.get_width() / 2, | |
| bar.get_height() + 0.01, | |
| f"{value:.2f}", | |
| ha="center", | |
| va="bottom", | |
| fontsize=10, | |
| ) | |
| # Solution length chart | |
| bars2 = ax2.bar( | |
| methods, solution_lengths, color=self.colors["lines"], alpha=0.7 | |
| ) | |
| ax2.set_title("طول راهحل", fontsize=14, fontweight="bold") | |
| ax2.set_ylabel("تعداد کاراکتر", fontsize=12) | |
| # Add value labels on bars | |
| for bar, value in zip(bars2, solution_lengths): | |
| ax2.text( | |
| bar.get_x() + bar.get_width() / 2, | |
| bar.get_height() + max(solution_lengths) * 0.01, | |
| f"{value}", | |
| ha="center", | |
| va="bottom", | |
| fontsize=10, | |
| ) | |
| # Set up the plot | |
| fig.suptitle(title, fontsize=16, fontweight="bold") | |
| plt.tight_layout() | |
| # Save the chart | |
| filename = f"comparison_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png" | |
| filepath = os.path.join(self.output_dir, filename) | |
| plt.savefig(filepath, dpi=self.dpi, bbox_inches="tight") | |
| plt.close() | |
| logger.info(f"Comparison chart saved to {filepath}") | |
| return filepath | |
| except Exception as e: | |
| logger.error(f"Error creating comparison chart: {e}") | |
| return "" | |
| def create_workflow_diagram( | |
| self, workflow_steps: List[Dict[str, Any]], title: str = "گردش کار حل مسئله" | |
| ) -> str: | |
| """ | |
| Create a workflow diagram | |
| Args: | |
| workflow_steps: List of workflow steps | |
| title: Diagram title | |
| Returns: | |
| Path to saved diagram | |
| """ | |
| try: | |
| fig, ax = plt.subplots(1, 1, figsize=(12, 8)) | |
| # Create flowchart-style visualization | |
| y_pos = 0.9 | |
| step_height = 0.12 | |
| for i, step in enumerate(workflow_steps): | |
| step_name = step.get("step", f"مرحله {i+1}") | |
| step_desc = step.get("description", "") | |
| # Create rounded rectangle for each step | |
| rect = FancyBboxPatch( | |
| (0.1, y_pos - 0.05), | |
| 0.8, | |
| 0.08, | |
| boxstyle="round,pad=0.02", | |
| facecolor="lightblue", | |
| edgecolor="navy", | |
| linewidth=2, | |
| ) | |
| ax.add_patch(rect) | |
| # Add step text | |
| ax.text( | |
| 0.5, | |
| y_pos, | |
| f"{step_name}: {step_desc}", | |
| fontsize=11, | |
| ha="center", | |
| va="center", | |
| transform=ax.transAxes, | |
| fontweight="bold", | |
| ) | |
| # Add arrow to next step | |
| if i < len(workflow_steps) - 1: | |
| ax.arrow( | |
| 0.5, | |
| y_pos - 0.05, | |
| 0, | |
| -0.02, | |
| head_width=0.02, | |
| head_length=0.01, | |
| fc="black", | |
| ec="black", | |
| transform=ax.transAxes, | |
| ) | |
| y_pos -= step_height | |
| # Set up the plot | |
| ax.set_xlim(0, 1) | |
| ax.set_ylim(0, 1) | |
| ax.axis("off") | |
| ax.set_title(title, fontsize=16, fontweight="bold", pad=20) | |
| # Save the diagram | |
| filename = f"workflow_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png" | |
| filepath = os.path.join(self.output_dir, filename) | |
| plt.savefig(filepath, dpi=self.dpi, bbox_inches="tight") | |
| plt.close() | |
| logger.info(f"Workflow diagram saved to {filepath}") | |
| return filepath | |
| except Exception as e: | |
| logger.error(f"Error creating workflow diagram: {e}") | |
| return "" | |
| def get_visualization_info(self) -> Dict[str, Any]: | |
| """ | |
| Get information about the visualizer | |
| Returns: | |
| Dictionary containing visualizer information | |
| """ | |
| return { | |
| "output_directory": self.output_dir, | |
| "resolution": self.resolution, | |
| "dpi": self.dpi, | |
| "style": self.style, | |
| "colors": self.colors, | |
| "available_diagrams": [ | |
| "triangle_diagram", | |
| "circle_diagram", | |
| "polygon_diagram", | |
| "proof_visualization", | |
| "comparison_chart", | |
| "workflow_diagram", | |
| ], | |
| } | |
| def cleanup_old_files(self, days_old: int = 7): | |
| """ | |
| Clean up old visualization files | |
| Args: | |
| days_old: Number of days old files to remove | |
| """ | |
| try: | |
| import time | |
| current_time = time.time() | |
| cutoff_time = current_time - (days_old * 24 * 60 * 60) | |
| removed_count = 0 | |
| for filename in os.listdir(self.output_dir): | |
| filepath = os.path.join(self.output_dir, filename) | |
| if os.path.isfile(filepath): | |
| file_time = os.path.getmtime(filepath) | |
| if file_time < cutoff_time: | |
| os.remove(filepath) | |
| removed_count += 1 | |
| logger.info(f"Cleaned up {removed_count} old visualization files") | |
| except Exception as e: | |
| logger.error(f"Error cleaning up old files: {e}") | |
Xet Storage Details
- Size:
- 20.8 kB
- Xet hash:
- e2c49c62a3882052cb56cfef3b556f9366ad0a9d115bad66fbb826c950ae9759
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.