361 lines
13 KiB
Python
361 lines
13 KiB
Python
"""
|
|
Visualization Exporter for CAE Mesh Generator
|
|
|
|
This module handles mesh visualization export including image generation,
|
|
3D visualization, and mesh quality visualization for blade geometry.
|
|
"""
|
|
import logging
|
|
import os
|
|
import time
|
|
from typing import Dict, List, Any, Optional, Tuple
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from dataclasses import dataclass
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
@dataclass
|
|
class VisualizationSettings:
|
|
"""Settings for mesh visualization export"""
|
|
width: int = 1280
|
|
height: int = 720
|
|
background_color: str = "white"
|
|
show_edges: bool = True
|
|
show_nodes: bool = False
|
|
camera_view: str = "isometric" # isometric, front, side, top
|
|
image_format: str = "PNG"
|
|
quality: int = 95
|
|
anti_aliasing: bool = True
|
|
|
|
@dataclass
|
|
class VisualizationResult:
|
|
"""Result container for visualization export"""
|
|
success: bool = False
|
|
image_path: Optional[str] = None
|
|
image_size: Tuple[int, int] = (0, 0)
|
|
file_size: int = 0
|
|
export_time: float = 0.0
|
|
error_message: Optional[str] = None
|
|
warnings: List[str] = None
|
|
|
|
def __post_init__(self):
|
|
if self.warnings is None:
|
|
self.warnings = []
|
|
|
|
class VisualizationExporter:
|
|
"""
|
|
Mesh visualization exporter for ANSYS Mechanical
|
|
|
|
This class provides functionality to export mesh visualizations,
|
|
generate images, and create visual reports for blade geometry.
|
|
"""
|
|
|
|
def __init__(self, mechanical_session=None, output_dir: str = "output"):
|
|
"""
|
|
Initialize visualization exporter
|
|
|
|
Args:
|
|
mechanical_session: Active PyMechanical session
|
|
output_dir: Directory for output files
|
|
"""
|
|
self.mechanical = mechanical_session
|
|
self.output_dir = Path(output_dir)
|
|
self.output_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Store mechanical session for real visualization export
|
|
self.mechanical = mechanical_session
|
|
|
|
logger.info("Visualization Exporter initialized")
|
|
|
|
def export_mesh_image(self,
|
|
filename: str = None,
|
|
settings: VisualizationSettings = None) -> VisualizationResult:
|
|
"""
|
|
Export mesh visualization as image
|
|
|
|
Args:
|
|
filename: Output filename (auto-generated if None)
|
|
settings: Visualization settings
|
|
|
|
Returns:
|
|
VisualizationResult with export results
|
|
"""
|
|
try:
|
|
logger.info("Starting mesh image export...")
|
|
start_time = time.time()
|
|
|
|
# Use default settings if not provided
|
|
if settings is None:
|
|
settings = VisualizationSettings()
|
|
|
|
# Generate filename if not provided
|
|
if filename is None:
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
filename = f"mesh_visualization_{timestamp}.{settings.image_format.lower()}"
|
|
|
|
output_path = self.output_dir / filename
|
|
|
|
return self._export_real_image(output_path, settings, start_time)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Mesh image export failed: {str(e)}")
|
|
result = VisualizationResult()
|
|
result.success = False
|
|
result.error_message = str(e)
|
|
return result
|
|
|
|
|
|
def _export_real_image(self,
|
|
output_path: Path,
|
|
settings: VisualizationSettings,
|
|
start_time: float) -> VisualizationResult:
|
|
"""
|
|
Export real mesh image using PyMechanical
|
|
|
|
Args:
|
|
output_path: Output file path
|
|
settings: Visualization settings
|
|
start_time: Export start time
|
|
|
|
Returns:
|
|
VisualizationResult with export results
|
|
"""
|
|
try:
|
|
logger.info("Exporting real mesh image using PyMechanical...")
|
|
|
|
# Prepare visualization script based on design document recommendations
|
|
visualization_script = f'''
|
|
# Mesh visualization export using PyMechanical API
|
|
import Ansys.Mechanical.Graphics as Graphics
|
|
|
|
try:
|
|
print("=== Starting Mesh Visualization Export ===")
|
|
|
|
# Set up graphics export settings
|
|
graphics_settings = Ansys.Mechanical.Graphics.GraphicsImageExportSettings()
|
|
graphics_settings.Width = {settings.width}
|
|
graphics_settings.Height = {settings.height}
|
|
graphics_settings.Background = Ansys.Mechanical.DataModel.Enums.GraphicsBackgroundType.{settings.background_color.title()}
|
|
graphics_settings.CurrentGraphicsDisplay = False
|
|
|
|
# Set image format
|
|
if "{settings.image_format.upper()}" == "PNG":
|
|
image_format = Ansys.Mechanical.DataModel.Enums.GraphicsImageExportFormat.PNG
|
|
elif "{settings.image_format.upper()}" == "JPG" or "{settings.image_format.upper()}" == "JPEG":
|
|
image_format = Ansys.Mechanical.DataModel.Enums.GraphicsImageExportFormat.JPG
|
|
else:
|
|
image_format = Ansys.Mechanical.DataModel.Enums.GraphicsImageExportFormat.PNG
|
|
|
|
print("Graphics settings configured")
|
|
|
|
# Set camera view
|
|
camera_view = "{settings.camera_view.lower()}"
|
|
if camera_view == "isometric":
|
|
ExtAPI.Graphics.Camera.SetSpecificViewOrientation(Ansys.Mechanical.DataModel.Enums.ViewOrientationType.Iso)
|
|
elif camera_view == "front":
|
|
ExtAPI.Graphics.Camera.SetSpecificViewOrientation(Ansys.Mechanical.DataModel.Enums.ViewOrientationType.Front)
|
|
elif camera_view == "side":
|
|
ExtAPI.Graphics.Camera.SetSpecificViewOrientation(Ansys.Mechanical.DataModel.Enums.ViewOrientationType.Right)
|
|
elif camera_view == "top":
|
|
ExtAPI.Graphics.Camera.SetSpecificViewOrientation(Ansys.Mechanical.DataModel.Enums.ViewOrientationType.Top)
|
|
|
|
print("Camera view set to: " + camera_view)
|
|
|
|
# Ensure mesh is visible
|
|
mesh = Model.Mesh
|
|
if mesh:
|
|
# Make sure mesh is displayed
|
|
mesh.Activate()
|
|
print("Mesh activated for visualization")
|
|
|
|
# Set mesh display options
|
|
if {str(settings.show_edges).lower()}:
|
|
print("Showing mesh edges")
|
|
if {str(settings.show_nodes).lower()}:
|
|
print("Showing mesh nodes")
|
|
|
|
# Fit view to show entire mesh
|
|
ExtAPI.Graphics.Camera.FitToScreen()
|
|
print("Camera fitted to screen")
|
|
|
|
# Export the image
|
|
export_path = r"{str(output_path).replace(chr(92), chr(92)+chr(92))}"
|
|
ExtAPI.Graphics.ExportImage(export_path, image_format, graphics_settings)
|
|
|
|
print("SUCCESS: Mesh visualization exported to: " + export_path)
|
|
|
|
except Exception as export_error:
|
|
print("ERROR: Mesh visualization export failed: " + str(export_error))
|
|
print("Common causes:")
|
|
print("- No mesh generated yet")
|
|
print("- Graphics system not initialized")
|
|
print("- Invalid file path or permissions")
|
|
print("- Unsupported image format")
|
|
raise export_error
|
|
'''
|
|
|
|
# Execute visualization script
|
|
result_str = self.mechanical.run_python_script(visualization_script)
|
|
logger.info(f"Visualization script result: {result_str}")
|
|
|
|
export_time = time.time() - start_time
|
|
|
|
# Check if file was created successfully
|
|
if output_path.exists():
|
|
file_size = os.path.getsize(output_path)
|
|
|
|
result = VisualizationResult(
|
|
success=True,
|
|
image_path=str(output_path),
|
|
image_size=(settings.width, settings.height),
|
|
file_size=file_size,
|
|
export_time=export_time
|
|
)
|
|
|
|
logger.info(f"✓ Real mesh image export completed: {output_path} ({file_size} bytes)")
|
|
return result
|
|
else:
|
|
# Export failed, but try to provide useful feedback
|
|
result = VisualizationResult()
|
|
result.success = False
|
|
result.export_time = export_time
|
|
|
|
if "SUCCESS:" in result_str:
|
|
result.error_message = "Export script reported success but file not found"
|
|
result.warnings.append("File may have been created in different location")
|
|
elif "ERROR:" in result_str:
|
|
# Extract error message
|
|
error_lines = [line for line in result_str.split('\n') if line.startswith("ERROR:")]
|
|
if error_lines:
|
|
result.error_message = error_lines[0].replace("ERROR:", "").strip()
|
|
else:
|
|
result.error_message = "Visualization export failed"
|
|
else:
|
|
result.error_message = "No output from visualization script"
|
|
|
|
logger.error(f"✗ Mesh image export failed: {result.error_message}")
|
|
return result
|
|
|
|
except Exception as e:
|
|
logger.error(f"Real image export failed: {str(e)}")
|
|
result = VisualizationResult()
|
|
result.success = False
|
|
result.error_message = f"Real export failed: {str(e)}"
|
|
result.export_time = time.time() - start_time
|
|
return result
|
|
|
|
def export_quality_visualization(self,
|
|
filename: str = None,
|
|
quality_metric: str = "element_quality") -> VisualizationResult:
|
|
"""
|
|
Export mesh quality visualization
|
|
|
|
Args:
|
|
filename: Output filename
|
|
quality_metric: Quality metric to visualize (element_quality, aspect_ratio, skewness)
|
|
|
|
Returns:
|
|
VisualizationResult with export results
|
|
"""
|
|
try:
|
|
logger.info(f"Exporting mesh quality visualization: {quality_metric}")
|
|
|
|
# Generate filename if not provided
|
|
if filename is None:
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
filename = f"mesh_quality_{quality_metric}_{timestamp}.png"
|
|
|
|
# Use specialized settings for quality visualization
|
|
settings = VisualizationSettings(
|
|
width=1280,
|
|
height=720,
|
|
background_color="white",
|
|
show_edges=True,
|
|
show_nodes=False,
|
|
camera_view="isometric",
|
|
image_format="PNG"
|
|
)
|
|
|
|
# For real mode, we would need to implement quality contour visualization
|
|
# This would require additional PyMechanical commands for result visualization
|
|
logger.warning("Real quality visualization not yet implemented")
|
|
result = VisualizationResult()
|
|
result.success = False
|
|
result.error_message = "Real quality visualization not yet implemented"
|
|
return result
|
|
|
|
except Exception as e:
|
|
logger.error(f"Quality visualization export failed: {str(e)}")
|
|
result = VisualizationResult()
|
|
result.success = False
|
|
result.error_message = str(e)
|
|
return result
|
|
|
|
def get_available_views(self) -> List[str]:
|
|
"""
|
|
Get list of available camera views
|
|
|
|
Returns:
|
|
List of available view names
|
|
"""
|
|
return ["isometric", "front", "side", "top", "bottom", "back", "left"]
|
|
|
|
def get_available_formats(self) -> List[str]:
|
|
"""
|
|
Get list of available image formats
|
|
|
|
Returns:
|
|
List of supported image formats
|
|
"""
|
|
return ["PNG", "JPG", "JPEG", "BMP", "TIFF"]
|
|
|
|
def cleanup_temp_files(self) -> int:
|
|
"""
|
|
Clean up temporary visualization files
|
|
|
|
Returns:
|
|
Number of files cleaned up
|
|
"""
|
|
try:
|
|
temp_patterns = ["temp_*.png", "temp_*.jpg", "*_temp.*"]
|
|
cleaned_count = 0
|
|
|
|
for pattern in temp_patterns:
|
|
for temp_file in self.output_dir.glob(pattern):
|
|
try:
|
|
temp_file.unlink()
|
|
cleaned_count += 1
|
|
except Exception as e:
|
|
logger.warning(f"Could not delete temp file {temp_file}: {e}")
|
|
|
|
if cleaned_count > 0:
|
|
logger.info(f"✓ Cleaned up {cleaned_count} temporary visualization files")
|
|
|
|
return cleaned_count
|
|
|
|
except Exception as e:
|
|
logger.error(f"Cleanup failed: {str(e)}")
|
|
return 0
|
|
|
|
def get_export_summary(self) -> Dict[str, Any]:
|
|
"""
|
|
Get summary of visualization export capabilities
|
|
|
|
Returns:
|
|
Dictionary with export summary
|
|
"""
|
|
return {
|
|
'exporter_type': 'VisualizationExporter',
|
|
|
|
'output_directory': str(self.output_dir),
|
|
'available_views': self.get_available_views(),
|
|
'available_formats': self.get_available_formats(),
|
|
'supported_features': [
|
|
'mesh_visualization',
|
|
'quality_visualization',
|
|
'custom_camera_views',
|
|
'configurable_resolution',
|
|
'multiple_formats'
|
|
],
|
|
'created_at': datetime.now().isoformat()
|
|
} |