Spaces:
Running on Zero
Running on Zero
| # === PREVIEW IMAGES === | |
| if images: | |
| preview_header = doc.add_paragraph() | |
| run = preview_header.add_run("PREVIEW RENDERS") | |
| run.font.size = Pt(12) | |
| run.font.bold = True | |
| run.font.color.rgb = RGBColor(70, 70, 70) | |
| doc.add_paragraph() # spacing | |
| # Only include HDRI Forest textured renders (shaded_forest key) | |
| if 'shaded_forest' in images: | |
| forest_images = images['shaded_forest'] | |
| # Create a table to arrange images horizontally | |
| img_table = doc.add_table(rows=1, cols=len(forest_images)) | |
| img_table.alignment = WD_ALIGN_PARAGRAPH.CENTER | |
| for idx, img_array in enumerate(forest_images): | |
| # Convert numpy array to PIL Image | |
| img_pil = Image.fromarray(img_array.astype('uint8')) | |
| # Save to temp file | |
| temp_img = tempfile.NamedTemporaryFile(delete=False, suffix='.png') | |
| img_pil.save(temp_img.name) | |
| # Add to table cell | |
| cell = img_table.rows[0].cells[idx] | |
| cell_para = cell.paragraphs[0] | |
| cell_para.alignment = WD_ALIGN_PARAGRAPH.CENTER | |
| run = cell_para.add_run() | |
| run.add_picture(temp_img.name, width=Inches(6.5 / len(forest_images))) # Divide page width | |
| os.unlink(temp_img.name) | |
| doc.add_paragraph() # spacing | |
| doc.add_paragraph() # spacing | |
| # Model details | |
| doc.add_paragraph() | |
| details_header = doc.add_paragraph() | |
| run = details_header.add_run("MODEL DETAILS") | |
| run.font.size = Pt(12) | |
| run.font.bold = True | |
| run.font.color.rgb = RGBColor(70, 70, 70) | |
| doc.add_paragraph() # spacing | |
| doc.add_paragraph() # spacing | |
| details = doc.add_paragraph() | |
| details.add_run(f"File Size: {file_size:.1f} MB\n") | |
| # Handle mesh info with proper N/A formatting | |
| verts_before = mesh_info.get('vertices_before', 'N/A') | |
| faces_before = mesh_info.get('faces_before', 'N/A') | |
| verts_final = mesh_info.get('final_vertices', 'N/A') | |
| faces_final = mesh_info.get('final_faces', 'N/A') | |
| if verts_before != 'N/A': | |
| details.add_run(f"Vertices (before export): {verts_before:,}\n") | |
| else: | |
| details.add_run(f"Vertices (before export): N/A\n") | |
| if faces_before != 'N/A': | |
| details.add_run(f"Faces (before export): {faces_before:,}\n") | |
| else: | |
| details.add_run(f"Faces (before export): N/A\n") | |
| if verts_final != 'N/A': | |
| details.add_run(f"Final Vertices: {verts_final:,}\n") | |
| else: | |
| details.add_run(f"Final Vertices: N/A\n") | |
| if faces_final != 'N/A': | |
| details.add_run(f"Final Faces: {faces_final:,}") | |
| else: | |
| details.add_run(f"Final Faces: N/A") | |
| doc.add_paragraph() # spacing | |
| # === DETAILED TIMING BREAKDOWN === | |
| timing_header = doc.add_paragraph() | |
| run = timing_header.add_run("DETAILED TIMING BREAKDOWN") | |
| run.font.size = Pt(12) | |
| run.font.bold = True | |
| run.font.color.rgb = RGBColor(70, 70, 70) | |
| timing_details = doc.add_paragraph() | |
| run = timing_details.add_run(f"STEP 1 ({step1_time:.2f}s total)\n") | |
| run.font.bold = True | |
| run.font.size = Pt(11) | |
| timing_details.add_run(f" Pipeline Total: {timings.get('pipeline_total', 0):.2f}s\n") | |
| timing_details.add_run(f" • Sparse Structure: {timings.get('sparse_structure', 0):.2f}s\n") | |
| timing_details.add_run(f" • Shape SLat: {timings.get('shape_slat', 0):.2f}s\n") | |
| timing_details.add_run(f" • Texture SLat: {timings.get('texture_slat', 0):.2f}s\n") | |
| timing_details.add_run(f" Mesh Simplification: {timings.get('mesh_simplify', 0):.2f}s\n") | |
| timing_details.add_run(f" Preview Rendering: {timings.get('rendering', 0):.2f}s\n") | |
| if step2_time > 0: | |
| timing_details.add_run("\n") | |
| run = timing_details.add_run(f"STEP 2 ({step2_time:.2f}s total)\n") | |
| run.font.bold = True | |
| run.font.size = Pt(11) | |
| timing_details.add_run(f" Decode Latent: {timings.get('decode', 0):.2f}s\n") | |
| timing_details.add_run(f" Mesh Simplification: {timings.get('simplify', 0):.2f}s\n") | |
| timing_details.add_run(f" to_glb (remesh+texture baking): {timings.get('to_glb', 0):.2f}s\n") | |
| timing_details.add_run(f" Save to File: {timings.get('save_file', 0):.2f}s\n") | |
| doc.add_paragraph() # spacing | |
| doc.add_paragraph() # spacing | |
| # Quality rating section | |
| rating_header = doc.add_paragraph() | |
| run = rating_header.add_run("QUALITY RATING") | |
| run.font.size = Pt(12) | |
| run.font.bold = True | |
| run.font.color.rgb = RGBColor(70, 70, 70) | |
| doc.add_paragraph() # spacing | |
| rating_categories = [ | |
| ("Accuracy", "accuracy"), | |
| ("Realism", "realism"), | |
| ("Geometry Quality", "geometry_quality"), | |
| ("Texture Quality", "texture_quality"), | |
| ("Topology", "topology"), | |
| ("Detail Preservation", "detail_preservation") | |
| ] | |
| for category_name, category_key in rating_categories: | |
| rating_value = ratings.get(category_key, []) | |
| # rating_value is a list of checked numbers | |
| highest_rating = max(rating_value) if rating_value else 0 | |
| p = doc.add_paragraph() | |
| p.add_run(f"{category_name}: ") | |
| if highest_rating > 0: | |
| # Show checkboxes with X for selected | |
| checkbox_str = " ".join([f"[{'X' if i in rating_value else ' '}] {i}" for i in range(1, 6)]) | |
| run = p.add_run(checkbox_str) | |
| run.font.bold = True | |
| p.add_run(f" → Score: {highest_rating}/5") | |
| else: | |
| p.add_run("[ ] 1 [ ] 2 [ ] 3 [ ] 4 [ ] 5") | |
| p = doc.add_paragraph() | |
| p.add_run("\nFAVORITE: ") | |
| if ratings.get('favorite', False): | |
| p.add_run("[X]").font.bold = True | |
| else: | |
| p.add_run("[ ]") | |
| doc.add_paragraph("=" * 80) | |
| # Notes section | |
| notes_header = doc.add_paragraph() | |
| notes_header.add_run("NOTES").font.bold = True | |
| doc.add_paragraph("=" * 80) | |
| notes_p = doc.add_paragraph(notes if notes else "\n\n\n") | |
| doc.add_paragraph("=" * 80) | |
| # Save DOCX | |