ZLayout EDA Library v1.0.0
Advanced Electronic Design Automation Layout Library with Bilingual Documentation
Loading...
Searching...
No Matches
visualization.py
Go to the documentation of this file.
1"""
2Layout visualization and plotting utilities.
3"""
4
5try:
6 import matplotlib.pyplot as plt
7 import matplotlib.patches as patches
8 HAS_MATPLOTLIB = True
9except ImportError:
10 HAS_MATPLOTLIB = False
11 plt = None
12 patches = None
13
14from typing import List, Dict, Optional, Tuple
15from .geometry import Point, Rectangle, Polygon
16
17
19 """Visualizer for layout analysis results."""
20
21 def __init__(self, figsize: Tuple[float, float] = (12, 8)):
22 if not HAS_MATPLOTLIB:
23 raise ImportError("Matplotlib is required for visualization. Install with: pip install matplotlib")
24 self.figsize = figsize
25
26 def plot_layout(self,
27 polygons: List[Polygon],
28 rectangles: List[Rectangle] = None,
29 title: str = "Layout View"):
30 """Plot polygons and rectangles in a layout."""
31 fig, ax = plt.subplots(figsize=self.figsize)
32
33 # Plot polygons
34 for i, polygon in enumerate(polygons):
35 x_coords = [v.x for v in polygon.vertices] + [polygon.vertices[0].x]
36 y_coords = [v.y for v in polygon.vertices] + [polygon.vertices[0].y]
37
38 ax.plot(x_coords, y_coords, 'b-', linewidth=2, alpha=0.7)
39 ax.fill(x_coords, y_coords, alpha=0.3, color='lightblue')
40
41 # Add polygon ID label
42 centroid = self._calculate_centroid(polygon)
43 ax.text(centroid.x, centroid.y, f'P{i}',
44 ha='center', va='center', fontweight='bold')
45
46 # Plot rectangles
47 if rectangles:
48 for i, rect in enumerate(rectangles):
49 rect_patch = patches.Rectangle(
50 (rect.x, rect.y), rect.width, rect.height,
51 linewidth=2, edgecolor='red', facecolor='lightcoral', alpha=0.3
52 )
53 ax.add_patch(rect_patch)
54
55 # Add rectangle ID label
56 ax.text(rect.center.x, rect.center.y, f'R{i}',
57 ha='center', va='center', fontweight='bold')
58
59 ax.set_aspect('equal')
60 ax.grid(True, alpha=0.3)
61 ax.set_title(title)
62 ax.set_xlabel('X')
63 ax.set_ylabel('Y')
64
65 return fig
66
68 polygons: List[Polygon],
69 analysis_results: Dict,
70 title: str = "Layout Analysis"):
71 """Plot layout with analysis overlays."""
72 fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12))
73
74 # Plot 1: Original layout
75 self._plot_polygons_on_axis(ax1, polygons, "Original Layout")
76
77 # Plot 2: Sharp angles
78 self._plot_polygons_on_axis(ax2, polygons, "Sharp Angles")
79 sharp_angles = analysis_results.get('sharp_angles', {}).get('details', [])
80 for poly_id, vertex_idx, angle in sharp_angles:
81 if poly_id < len(polygons):
82 vertex = polygons[poly_id].vertices[vertex_idx]
83 ax2.plot(vertex.x, vertex.y, 'ro', markersize=8)
84 ax2.text(vertex.x, vertex.y, f'{angle:.1f}°',
85 xytext=(5, 5), textcoords='offset points',
86 fontsize=8, color='red')
87
88 # Plot 3: Narrow distances
89 self._plot_polygons_on_axis(ax3, polygons, "Narrow Distances")
90 narrow_regions = analysis_results.get('narrow_distances', {}).get('details', [])
91 for point1, point2, distance in narrow_regions:
92 ax3.plot([point1.x, point2.x], [point1.y, point2.y],
93 'r-', linewidth=3, alpha=0.7)
94 mid_x = (point1.x + point2.x) / 2
95 mid_y = (point1.y + point2.y) / 2
96 ax3.text(mid_x, mid_y, f'{distance:.2f}',
97 ha='center', va='center', fontsize=8,
98 bbox=dict(boxstyle='round,pad=0.2', facecolor='yellow', alpha=0.7))
99
100 # Plot 4: Intersections
101 self._plot_polygons_on_axis(ax4, polygons, "Edge Intersections")
102 intersection_points = analysis_results.get('intersections', {}).get('points', [])
103 for point in intersection_points:
104 ax4.plot(point.x, point.y, 'rx', markersize=10, markeredgewidth=3)
105
106 plt.tight_layout()
107 return fig
108
110 quadtree_node,
111 polygons: List[Polygon] = None,
112 title: str = "QuadTree Structure"):
113 """Visualize quadtree structure."""
114 fig, ax = plt.subplots(figsize=self.figsize)
115
116 # Plot polygons if provided
117 if polygons:
118 for polygon in polygons:
119 x_coords = [v.x for v in polygon.vertices] + [polygon.vertices[0].x]
120 y_coords = [v.y for v in polygon.vertices] + [polygon.vertices[0].y]
121 ax.plot(x_coords, y_coords, 'b-', linewidth=1, alpha=0.5)
122 ax.fill(x_coords, y_coords, alpha=0.2, color='lightblue')
123
124 # Recursively draw quadtree boundaries
125 self._draw_quadtree_boundaries(ax, quadtree_node, depth=0)
126
127 ax.set_aspect('equal')
128 ax.grid(True, alpha=0.3)
129 ax.set_title(title)
130 ax.set_xlabel('X')
131 ax.set_ylabel('Y')
132
133 return fig
134
135 def plot_optimization_summary(self, optimization_results: Dict):
136 """Create a summary visualization of optimization results."""
137 fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(14, 10))
138
139 analysis = optimization_results['analysis']
140
141 # Plot 1: Score gauge
142 score = optimization_results['optimization_score']
143 self._plot_score_gauge(ax1, score)
144
145 # Plot 2: Issue counts
146 issue_types = ['Sharp Angles', 'Narrow Distances', 'Intersections']
147 counts = [
148 analysis['sharp_angles']['count'],
149 analysis['narrow_distances']['count'],
150 analysis['intersections']['polygon_pairs']
151 ]
152 colors = ['orange', 'red', 'darkred']
153
154 bars = ax2.bar(issue_types, counts, color=colors, alpha=0.7)
155 ax2.set_title('Issues Found')
156 ax2.set_ylabel('Count')
157
158 # Add value labels on bars
159 for bar, count in zip(bars, counts):
160 height = bar.get_height()
161 ax2.text(bar.get_x() + bar.get_width()/2., height,
162 f'{count}', ha='center', va='bottom')
163
164 # Plot 3: Distance statistics
165 if analysis['narrow_distances']['count'] > 0:
166 min_dist = analysis['narrow_distances']['minimum']
167 max_dist = analysis['narrow_distances']['maximum']
168 avg_dist = analysis['narrow_distances']['average']
169
170 distances = ['Minimum', 'Average', 'Maximum']
171 values = [min_dist, avg_dist, max_dist]
172
173 ax3.bar(distances, values, color='skyblue', alpha=0.7)
174 ax3.set_title('Distance Statistics')
175 ax3.set_ylabel('Distance')
176
177 for bar, value in zip(ax3.patches, values):
178 height = bar.get_height()
179 ax3.text(bar.get_x() + bar.get_width()/2., height,
180 f'{value:.3f}', ha='center', va='bottom')
181 else:
182 ax3.text(0.5, 0.5, 'No narrow distances found',
183 ha='center', va='center', transform=ax3.transAxes)
184 ax3.set_title('Distance Statistics')
185
186 # Plot 4: Suggestions
187 suggestions = optimization_results['suggestions']
188 ax4.axis('off')
189 ax4.set_title('Optimization Suggestions')
190
191 if suggestions:
192 suggestion_text = '\n\n'.join([f"• {s}" for s in suggestions])
193 else:
194 suggestion_text = "✓ No issues found! Layout looks good."
195
196 ax4.text(0.05, 0.95, suggestion_text, transform=ax4.transAxes,
197 verticalalignment='top', fontsize=10, wrap=True)
198
199 plt.tight_layout()
200 return fig
201
202 def _plot_polygons_on_axis(self, ax, polygons: List[Polygon], title: str):
203 """Helper to plot polygons on a given axis."""
204 for polygon in polygons:
205 x_coords = [v.x for v in polygon.vertices] + [polygon.vertices[0].x]
206 y_coords = [v.y for v in polygon.vertices] + [polygon.vertices[0].y]
207
208 ax.plot(x_coords, y_coords, 'b-', linewidth=2, alpha=0.7)
209 ax.fill(x_coords, y_coords, alpha=0.3, color='lightblue')
210
211 ax.set_aspect('equal')
212 ax.grid(True, alpha=0.3)
213 ax.set_title(title)
214 ax.set_xlabel('X')
215 ax.set_ylabel('Y')
216
217 def _draw_quadtree_boundaries(self, ax, node, depth: int = 0):
218 """Recursively draw quadtree node boundaries."""
219 if depth > 6: # Prevent too deep recursion for visualization
220 return
221
222 # Draw this node's boundary
223 boundary = node.boundary
224 rect = patches.Rectangle(
225 (boundary.x, boundary.y), boundary.width, boundary.height,
226 linewidth=1, edgecolor='gray', facecolor='none', alpha=0.5
227 )
228 ax.add_patch(rect)
229
230 # Add object count text
231 object_count = len(node.objects)
232 if object_count > 0:
233 center = boundary.center
234 ax.text(center.x, center.y, str(object_count),
235 ha='center', va='center', fontsize=8,
236 bbox=dict(boxstyle='round,pad=0.2', facecolor='white', alpha=0.7))
237
238 # Recursively draw children
239 if node.divided:
240 for child in node.children:
241 self._draw_quadtree_boundaries(ax, child, depth + 1)
242
243 def _plot_score_gauge(self, ax, score: float):
244 """Plot a gauge showing optimization score."""
245 # Create a semicircle gauge
246 theta = (score / 100) * 180 # Convert score to degrees (0-180)
247
248 # Background arc
249 import math
250 theta_bg = list(range(0, 181, 5))
251 x_bg = [0.8 * math.cos(math.radians(t)) for t in theta_bg]
252 y_bg = [0.8 * math.sin(math.radians(t)) for t in theta_bg]
253 ax.plot(x_bg, y_bg, 'lightgray', linewidth=10)
254
255 # Score arc
256 import math
257 theta_score = list(range(0, int(theta) + 1, 5))
258 x_score = [0.8 * math.cos(math.radians(t)) for t in theta_score]
259 y_score = [0.8 * math.sin(math.radians(t)) for t in theta_score]
260
261 # Color based on score
262 if score >= 80:
263 color = 'green'
264 elif score >= 60:
265 color = 'orange'
266 else:
267 color = 'red'
268
269 if x_score: # Only plot if there are points
270 ax.plot(x_score, y_score, color, linewidth=10)
271
272 # Score text
273 ax.text(0, -0.3, f'{score:.1f}', ha='center', va='center',
274 fontsize=24, fontweight='bold')
275 ax.text(0, -0.5, 'Optimization Score', ha='center', va='center',
276 fontsize=12)
277
278 ax.set_xlim(-1, 1)
279 ax.set_ylim(-0.7, 1)
280 ax.set_aspect('equal')
281 ax.axis('off')
282 ax.set_title('Layout Quality Score')
283
284 def _calculate_centroid(self, polygon: Polygon) -> Point:
285 """Calculate polygon centroid."""
286 x_sum = sum(v.x for v in polygon.vertices)
287 y_sum = sum(v.y for v in polygon.vertices)
288 n = len(polygon.vertices)
289 return Point(x_sum / n, y_sum / n)
290
291 def save_plots(self, figures: List, base_filename: str,
292 format: str = 'png', dpi: int = 300):
293 """Save multiple figures to files."""
294 for i, fig in enumerate(figures):
295 filename = f"{base_filename}_{i+1}.{format}"
296 fig.savefig(filename, format=format, dpi=dpi, bbox_inches='tight')
297 print(f"Saved plot: {filename}")
2D point with high-precision coordinates and utility methods
Definition point.hpp:23
plot_optimization_summary(self, Dict optimization_results)
_draw_quadtree_boundaries(self, ax, node, int depth=0)
plot_quadtree_visualization(self, quadtree_node, List[Polygon] polygons=None, str title="QuadTree Structure")
Point _calculate_centroid(self, Polygon polygon)
__init__(self, Tuple[float, float] figsize=(12, 8))
plot_analysis_results(self, List[Polygon] polygons, Dict analysis_results, str title="Layout Analysis")
_plot_polygons_on_axis(self, ax, List[Polygon] polygons, str title)
plot_layout(self, List[Polygon] polygons, List[Rectangle] rectangles=None, str title="Layout View")
save_plots(self, List figures, str base_filename, str format='png', int dpi=300)