Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generate images and visualizations from Revit/IFC files without BIM software. Python-based noBIM tool for batch processing.
.claude/skills/datadrivenconstruction-nobim-image-generator/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 76% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 98% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 193% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 97% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 47% | 0% |
Creating visualizations from BIM models typically requires:
noBIM tool extracts data and generates visualizations using Python libraries, processing hundreds of projects without BIM software.
bashpip install pandas matplotlib seaborn plotly ifcopenshell
pythonimport pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import numpy as np from pathlib import Path from typing import List, Optional, Tuple class NoBIMVisualizer: def __init__(self): self.elements = None self.project_name = "" def load_from_excel(self, xlsx_path: str) -> int: """Load BIM data from converted Excel file.""" self.elements = pd.read_excel(xlsx_path, sheet_name="Elements") self.project_name = Path(xlsx_path).stem return len(self.elements) def generate_3d_scatter(self, output_path: str, color_by: str = "Category", size: Tuple[int, int] = (12, 10)) -> str: """Generate 3D scatter plot of elements.""" if not all(col in self.elements.columns for col in ['BBox_CenterX', 'BBox_CenterY', 'BBox_CenterZ']): raise ValueError("Bounding box data required. Export with 'bbox' option.") fig = plt.figure(figsize=size) ax = fig.add_subplot(111, projection='3d') # Get unique categories for coloring categories = self.elements[color_by].unique() colors = plt.cm.tab20(np.linspace(0, 1, len(categories))) color_map = dict(zip(categories, colors)) for cat in categories: subset = self.elements[self.elements[color_by] == cat] ax.scatter( subset['BBox_CenterX'], subset['BBox_CenterY'], subset['BBox_CenterZ'], c=[color_map[cat]], label=cat[:20], alpha=0.6, s=10 ) ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') ax.set_title(f'{self.project_name} - 3D Element Distribution') ax.legend(loc='upper left', fontsize=8, ncol=2) plt.savefig(output_path, dpi=150, bbox_inches='tight') plt.close() return output_path def generate_floor_plan(self, output_path: str, level: str, size: Tuple[int, int] = (14, 10)) -> str: """Generate floor plan visualization for specific level.""" level_elements = self.elements[self.elements['Level'] == level] if level_elements.empty: raise ValueError(f"No elements found for level: {level}") fig, ax = plt.subplots(figsize=size) # Draw walls walls = level_elements[level_elements['Category'] == 'Walls'] for _, wall in walls.iterrows(): rect = plt.Rectangle( (wall['BBox_MinX'], wall['BBox_MinY']), wall['BBox_MaxX'] - wall['BBox_MinX'], wall['BBox_MaxY'] - wall['BBox_MinY'], fill=True, facecolor='gray', edgecolor='black', alpha=0.7 ) ax.add_patch(rect) # Draw rooms rooms = level_elements[level_elements['Category'] == 'Rooms'] for _, room in rooms.iterrows(): center_x = (room['BBox_MinX'] + room['BBox_MaxX']) / 2 center_y = (room['BBox_MinY'] + room['BBox_MaxY']) / 2 ax.annotate(room.get('RoomName', 'Room'), (center_x, center_y), ha='center', fontsize=8) ax.set_aspect('equal') ax.set_title(f'{self.project_name} - {level}') ax.set_xlabel('X (m)') ax.set_ylabel('Y (m)') plt.savefig(output_path, dpi=150, bbox_inches='tight') plt.close() return output_path def generate_category_chart(self, output_path: str, size: Tuple[int, int] = (12, 8)) -> str: """Generate bar chart of element categories.""" cat_counts = self.elements['Category'].value_counts().head(20) fig, ax = plt.subplots(figsize=size) bars = ax.barh(cat_counts.index, cat_counts.values, color=plt.cm.viridis(np.linspace(0, 1, len(cat_counts)))) ax.set_xlabel('Element Count') ax.set_title(f'{self.project_name} - Element Categories') # Add count labels for bar, count in zip(bars, cat_counts.values): ax.text(bar.get_width() + 1, bar.get_y() + bar.get_height()/2, f'{count}', va='center', fontsize=9) plt.tight_layout() plt.savefig(output_path, dpi=150, bbox_inches='tight') plt.close() return output_path def generate_volume_treemap(self, output_path: str) -> str: """Generate treemap of volumes by category.""" import plotly.express as px vol_by_cat = self.elements.groupby('Category')['Volume'].sum().reset_index() vol_by_cat = vol_by_cat[vol_by_cat['Volume'] > 0].sort_values('Volume', ascending=False) fig = px.treemap( vol_by_cat.head(30), path=['Category'], values='Volume', title=f'{self.project_name} - Volume Distribution' ) fig.write_image(output_path) return output_path def batch_generate(self, xlsx_files: List[str], output_dir: str) -> List[str]: """Generate standard visualizations for multiple projects.""" output_dir = Path(output_dir) output_dir.mkdir(parents=True, exist_ok=True) generated = [] for xlsx in xlsx_files: try: self.load_from_excel(xlsx) base_name = Path(xlsx).stem # Generate all visualizations self.generate_3d_scatter(str(output_dir / f"{base_name}_3d.png")) self.generate_category_chart(str(output_dir / f"{base_name}_categories.png")) generated.append(base_name) print(f"Generated visualizations for: {base_name}") except Exception as e: print(f"Error processing {xlsx}: {e}") return generated
pythonviz = NoBIMVisualizer() viz.load_from_excel("C:/Projects/Office.xlsx") # Generate 3D view viz.generate_3d_scatter("office_3d.png", color_by="Category") # Generate floor plan viz.generate_floor_plan("office_level1.png", level="Level 1") # Generate category breakdown viz.generate_category_chart("office_categories.png")
pythonfrom pathlib import Path viz = NoBIMVisualizer() # Find all converted files xlsx_files = list(Path("C:/ConvertedProjects").glob("*.xlsx")) # Generate visualizations for all generated = viz.batch_generate( [str(f) for f in xlsx_files], output_dir="C:/Visualizations" ) print(f"Generated visualizations for {len(generated)} projects")
| Visualization | Use Case | |---------------|----------| | 3D Scatter | Overall project structure | | Floor Plan | Level-by-level layout | | Category Chart | Element distribution | | Volume Treemap | Material quantities | | Level Comparison | Multi-floor analysis |
pythonfrom reportlab.pdfgen import canvas from reportlab.lib.pagesizes import A4 def create_project_report(xlsx_path: str, output_pdf: str): """Generate PDF report with all visualizations.""" viz = NoBIMVisualizer() viz.load_from_excel(xlsx_path) # Generate images images = { '3D View': viz.generate_3d_scatter("temp_3d.png"), 'Categories': viz.generate_category_chart("temp_cat.png"), } # Create PDF c = canvas.Canvas(output_pdf, pagesize=A4) c.drawString(100, 800, f"Project Report: {viz.project_name}") y_pos = 700 for title, img_path in images.items(): c.drawString(100, y_pos, title) c.drawImage(img_path, 100, y_pos - 300, width=400, height=280) y_pos -= 350 c.save() return output_pdf
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-17 | pass→pass | 15,349 | 10,103 | -34% | 1 | 1 | 0% | 2,615 | 4,464 | +71% | 0 | 0 | — |
case-01 | fail→fail | 23,851 | 15,018 | -37% | 1 | 1 | 0% | 4,703 | 5,420 | +15% | 0 | 0 | — |
case-02 | fail→fail | 14,860 | 12,630 | -15% | 1 | 1 | 0% | 2,913 | 4,965 | +70% | 0 | 0 | — |
case-03 | fail→fail | 18,399 | 14,379 | -22% | 1 | 1 | 0% | 3,534 | 5,558 | +57% | 0 | 0 | — |
case-04 | fail→fail | 13,892 | 13,830 | -0% | 1 | 1 | 0% | 2,447 | 5,144 | +110% | 0 | 0 | — |
case-05 | fail→fail | 24,324 | 29,125 | +20% | 1 | 1 | 0% | 5,349 | 8,654 | +62% | 0 | 0 | — |
case-06 | fail→pass | 10,341 | 2,405 | -77% | 1 | 1 | 0% | 1,643 | 2,889 | +76% | 0 | 0 | — |
case-07 | fail→pass | 9,102 | 3,089 | -66% | 1 | 1 | 0% | 1,538 | 3,048 | +98% | 0 | 0 | — |
case-08 | pass→pass | 14,155 | 9,330 | -34% | 1 | 1 | 0% | 2,601 | 4,169 | +60% | 0 | 0 | — |
case-09 | pass→pass | 11,778 | 7,556 | -36% | 1 | 1 | 0% | 2,003 | 3,801 | +90% | 0 | 0 | — |
case-10 | fail→pass | 6,722 | 2,423 | -64% | 1 | 1 | 0% | 992 | 2,911 | +193% | 0 | 0 | — |
case-11 | fail→pass | 17,424 | 19,629 | +13% | 1 | 1 | 0% | 3,020 | 5,951 | +97% | 0 | 0 | — |
case-12 | fail→fail | 17,106 | 10,554 | -38% | 1 | 1 | 0% | 2,785 | 4,239 | +52% | 0 | 0 | — |
case-13 | fail→pass | 22,935 | 2,369 | -90% | 1 | 1 | 0% | 1,925 | 2,826 | +47% | 0 | 0 | — |
case-14 | fail→fail | 11,851 | 14,534 | +23% | 1 | 1 | 0% | 2,271 | 5,119 | +125% | 0 | 0 | — |
case-15 | pass→pass | 11,781 | 4,923 | -58% | 1 | 1 | 0% | 1,939 | 3,251 | +68% | 0 | 0 | — |
case-16 | fail→fail | 12,528 | 10,962 | -13% | 1 | 1 | 0% | 2,186 | 4,503 | +106% | 0 | 0 | — |
case-18 | fail→pass | 9,866 | 3,426 | -65% | 1 | 1 | 0% | 1,532 | 3,032 | +98% | 0 | 0 | — |
case-19 | pass→pass | 14,100 | 3,283 | -77% | 1 | 1 | 0% | 2,306 | 3,017 | +31% | 0 | 0 | — |
case-20 | pass→pass | 11,151 | 11,540 | +3% | 1 | 1 | 0% | 2,162 | 4,749 | +120% | 0 | 0 | — |
case-21 | pass→pass | 16,325 | 13,714 | -16% | 1 | 1 | 0% | 3,489 | 5,522 | +58% | 0 | 0 | — |
case-22 | pass→pass | 17,278 | 13,629 | -21% | 1 | 1 | 0% | 3,314 | 5,276 | +59% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +27 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.