Skip to main content
Glama

OpenSCAD MCP Server

by jhacksman
renderer.py6.98 kB
import os import subprocess import logging from typing import Dict, Any, Optional from PIL import Image, ImageDraw, ImageFont logger = logging.getLogger(__name__) class Renderer: """ Handles rendering of OpenSCAD models to preview images. Implements multi-angle views and fallback rendering when headless mode fails. """ def __init__(self, openscad_wrapper): """ Initialize the renderer. Args: openscad_wrapper: Instance of OpenSCADWrapper for generating previews """ self.openscad_wrapper = openscad_wrapper # Standard camera angles for multi-view rendering self.camera_angles = { 'front': "0,0,0,0,0,0,50", 'top': "0,0,0,90,0,0,50", 'right': "0,0,0,0,90,0,50", 'perspective': "20,20,20,55,0,25,100" } def generate_preview(self, scad_file: str, parameters: Optional[Dict[str, Any]] = None) -> str: """ Generate a preview image for a SCAD file. Args: scad_file: Path to the SCAD file parameters: Optional parameters to override in the SCAD file Returns: Path to the generated preview image """ try: # Try to generate a preview using OpenSCAD preview_file = self.openscad_wrapper.generate_preview( scad_file, parameters, camera_position=self.camera_angles['perspective'], image_size="800,600" ) # Check if the file exists and has content if os.path.exists(preview_file) and os.path.getsize(preview_file) > 0: return preview_file else: # If the file doesn't exist or is empty, create a placeholder return self._create_placeholder_image(preview_file) except Exception as e: logger.error(f"Error generating preview: {str(e)}") # Create a placeholder image model_id = os.path.basename(scad_file).split('.')[0] preview_file = os.path.join(self.openscad_wrapper.preview_dir, f"{model_id}.png") return self._create_placeholder_image(preview_file) def generate_multi_angle_previews(self, scad_file: str, parameters: Optional[Dict[str, Any]] = None) -> Dict[str, str]: """ Generate preview images from multiple angles. Args: scad_file: Path to the SCAD file parameters: Optional parameters to override in the SCAD file Returns: Dictionary mapping angle names to preview image paths """ previews = {} model_id = os.path.basename(scad_file).split('.')[0] for angle_name, camera_position in self.camera_angles.items(): preview_file = os.path.join( self.openscad_wrapper.preview_dir, f"{model_id}_{angle_name}.png" ) try: # Try to generate a preview using OpenSCAD preview_file = self.openscad_wrapper.generate_preview( scad_file, parameters, camera_position=camera_position, image_size="800,600" ) # Check if the file exists and has content if os.path.exists(preview_file) and os.path.getsize(preview_file) > 0: previews[angle_name] = preview_file else: # If the file doesn't exist or is empty, create a placeholder previews[angle_name] = self._create_placeholder_image(preview_file, angle_name) except Exception as e: logger.error(f"Error generating {angle_name} preview: {str(e)}") # Create a placeholder image previews[angle_name] = self._create_placeholder_image(preview_file, angle_name) return previews def _create_placeholder_image(self, output_path: str, angle_name: str = "perspective") -> str: """ Create a placeholder image when OpenSCAD rendering fails. Args: output_path: Path to save the placeholder image angle_name: Name of the camera angle for the placeholder Returns: Path to the created placeholder image """ try: # Create a blank image img = Image.new('RGB', (800, 600), color=(240, 240, 240)) draw = ImageDraw.Draw(img) # Add text draw.text((400, 280), f"Preview not available", fill=(0, 0, 0)) draw.text((400, 320), f"View: {angle_name}", fill=(0, 0, 0)) # Save the image img.save(output_path) logger.info(f"Created placeholder image: {output_path}") return output_path except Exception as e: logger.error(f"Error creating placeholder image: {str(e)}") # If all else fails, return the path anyway return output_path def create_composite_preview(self, previews: Dict[str, str], output_path: str) -> str: """ Create a composite image from multiple angle previews. Args: previews: Dictionary mapping angle names to preview image paths output_path: Path to save the composite image Returns: Path to the created composite image """ try: # Create a blank image img = Image.new('RGB', (1600, 1200), color=(240, 240, 240)) # Load and paste each preview positions = { 'perspective': (0, 0), 'front': (800, 0), 'top': (0, 600), 'right': (800, 600) } for angle_name, preview_path in previews.items(): if angle_name in positions and os.path.exists(preview_path): try: angle_img = Image.open(preview_path) # Resize if needed angle_img = angle_img.resize((800, 600)) # Paste into composite img.paste(angle_img, positions[angle_name]) except Exception as e: logger.error(f"Error processing {angle_name} preview: {str(e)}") # Save the composite image img.save(output_path) logger.info(f"Created composite preview: {output_path}") return output_path except Exception as e: logger.error(f"Error creating composite preview: {str(e)}") # If all else fails, return the path anyway return output_path

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jhacksman/OpenSCAD-MCP-Server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server