## MVP 1 FILE! import gradio as gr import os import json import tempfile import gmsh from mesh_service.run_interface import ( get_result, extract_faces_for_gui # <-- NEW helper from updated run_interface ) # Absolute path to example files inside Docker container os.chdir(os.path.dirname(__file__)) EXAMPLE_DIR = os.path.join(os.path.dirname(__file__), "example_steps") # ----------------------------------------- # Gmsh initialize only once # ----------------------------------------- try: gmsh.initialize() except: pass # ----------------------------------------- # STEP Upload → Extract Faces for GUI # ----------------------------------------- def on_step_upload(step_file): if step_file is None: return gr.update(choices=[], value=None), "[]" # Work in temp copy if needed if hasattr(step_file, "read"): with tempfile.NamedTemporaryFile(delete=False, suffix=".step") as f: f.write(step_file.read()) step_path = f.name else: step_path = step_file.name faces = extract_faces_for_gui(step_path) labels = [f["label"] for f in faces] faces_json = json.dumps(faces) #default = labels[0] if labels else None default = None return gr.update(choices=labels, value=default), faces_json # ----------------------------------------- # Main pipeline call # ----------------------------------------- def run_mesh(step_file, thickness, load_type, direction, scale, selected_face_label, faces_json, view_mode): # 1. Handle STEP file path if hasattr(step_file, "read"): # Cloud / HuggingFace mode with tempfile.NamedTemporaryFile(delete=False, suffix=".step") as f: f.write(step_file.read()) step_path = f.name else: step_path = step_file.name # 2. Convert direction text → number direction_val = 1 if direction == "positive" else -1 # 3. Run model pipeline sentence, global_png, refined_png, fig, msh_path = get_result( step_path=step_path, thickness=float(thickness), load_face=load_type, # still used as categorical feature load_direction=direction_val, load_scale=scale, selected_face_label=selected_face_label, # NEW faces_json=faces_json, # NEW view_mode=view_mode ) # 4. Convert PNG paths to absolute (for Gradio) refined_img = os.path.abspath(refined_png) global_img = os.path.abspath(global_png) msh_path = os.path.abspath(msh_path) return sentence, refined_img, global_img, fig, msh_path # ----------------------------------------- # Gradio Interface # ----------------------------------------- with gr.Blocks(title="Mesh Refinement Agent") as iface: gr.Markdown("## Mesh Refinement Prediction Demo") gr.Markdown( '''### 📘 How to Use This Mesh Refinement Model 1. **Upload a STEP file** (`.step` / `.stp`). OR: choose from one of the example STEP files at the bottom of the GUI and click *Run Mesh Prediction* 2. **Set basic parameters:** - *Thickness* (mm) - *Load Type* (bend from below or tension) - *Direction* (positive/negative) - *Load Scale* (low/medium/high) 3. After uploading, the system automatically detects all faces. Select the **Load Face** from the dropdown. 4. Choose a **3D visualization mode**: - **Wireframe** – full mesh structure - **Highlight** – locally refined regions in red - **Heatmap** – continuous refinement magnitude 5. Click **Run Mesh Prediction**. The model will generate: - A natural-language summary - PNGs of the global and refined meshes - An interactive 3D mesh viewer - A downloadable `.msh` file This workflow reproduces the full ML-driven refinement pipeline used during training.''' ) gr.HTML("""
How to Choose the Correct Load Face

Different load types correspond to different physical loading scenarios.
After uploading your STEP file, the face dropdown will list all detected faces.
To ensure the ML model receives inputs aligned with its training distribution, please choose the face following these guidelines:

Load Type: bend_bottom

Use this when the bracket or part is loaded from below, such as:

Choose the face that is physically on the bottom of the part, i.e.:

💡 Tip: In most STEP files this is a face with a downward normal (normal ≈ [0, 0, -1]).


Load Type: tension

Use this when the bracket or part is pulled backwards or outwards, such as:

Choose the face that is physically the rear/back face of the part, i.e.:

💡 Tip: Often the normal points along ±X (normal ≈ [±1, 0, 0]).


If You Are Unsure

""") gr.HTML("""
3D Visualization Mode Guidance

In the 3D visualization mode dropdown, there are three modes to choose from:

Heatmap color scale:

The heatmap provides a smooth, global visualization of refinement intensity across the mesh.

""") with gr.Row(): with gr.Column(scale=1): # STEP file upload step_file = gr.File( label="Upload STEP File", file_types=[".step", ".stp"] ) thickness = gr.Textbox( label="Thickness (mm)", value="3" ) load_type = gr.Dropdown( ["bend_bottom", "tension"], value="bend_bottom", label="Load Type (categorical feature)" ) direction = gr.Dropdown( ["positive", "negative"], value="positive", label="Direction" ) scale = gr.Dropdown( ["low", "medium", "high"], value="high", label="Load Scale" ) # toggle visualization mode view_mode = gr.Dropdown( ["wireframe", "highlight", "heatmap"], value="wireframe", label="3D Visualization Mode" ) # NEW: face selection & json hidden box selected_face_label = gr.Dropdown( label="Select Load Face (detected from STEP)", choices=[], value=None, interactive=True ) faces_json_box = gr.Textbox( visible=False ) examples_data = [ [f"{EXAMPLE_DIR}/example1.step", "3", "bend_bottom", "positive", "high", None, "[]", "wireframe"], [f"{EXAMPLE_DIR}/example2.step", "2", "tension", "negative", "medium", None, "[]", "highlight"], [f"{EXAMPLE_DIR}/example3.step", "3", "bend_bottom", "positive", "high", None, "[]", "heatmap"], [f"{EXAMPLE_DIR}/example4.step", "3.5", "tension", "negative", "medium", None, "[]", "wireframe"], ] # Populate face dropdown upon STEP upload step_file.change( on_step_upload, inputs=step_file, outputs=[selected_face_label, faces_json_box] ) run_button = gr.Button("Run Mesh Prediction") with gr.Column(scale=1): result_text = gr.Textbox( label="Result", lines=12, max_lines=20, interactive=False ) refined_img = gr.Image( label="Refined Mesh (PNG)" ) global_img = gr.Image( label="Global Mesh (PNG)" ) # NEW: 3D interactive refined mesh mesh_plot_3d = gr.Plot( label="Refined Mesh (3D Interactive)" ) mesh_download = gr.File( label="Download Generated Mesh (.msh)", interactive=False ) gr.Examples( examples=examples_data, inputs=[ step_file, thickness, load_type, direction, scale, selected_face_label, faces_json_box, view_mode ], # The `run_on_click` parameter automatically runs the main function (run_mesh) # when an example is selected, giving immediate results. # If you prefer users click the 'Run' button manually after selection, set this to False. fn=run_mesh, outputs=[ result_text, refined_img, global_img, mesh_plot_3d, mesh_download ], run_on_click=False ) # Connect compute button run_button.click( run_mesh, inputs=[ step_file, thickness, load_type, direction, scale, selected_face_label, faces_json_box, view_mode, ], outputs=[ result_text, refined_img, global_img, mesh_plot_3d, mesh_download ] ) iface.launch(server_name="0.0.0.0", server_port=7860)